EaR: Helper routines to support configurable encryption (#9368)
* EaR: Helper routines to support configurable encryption Description Add helper methods to BlobCipherEncryptHeaderRef enabling: 1. Extract 'IV' abstracting out underlying algorithm header 1. Extract 'cipherDetails' abstracting out underlying algorithm header Testing BlobCipherUnitTest & EncryptionOps are updated - 100K loop * EaR: Helper routines to support configurable encryption Description Add helper methods to BlobCipherEncryptHeaderRef enabling: 1. Extract 'IV' abstracting out underlying algorithm header 1. Extract 'cipherDetails' abstracting out underlying algorithm header Testing BlobCipherUnitTest & EncryptionOps are updated - 100K loop
This commit is contained in:
parent
05e73b7836
commit
401b9c8918
|
@ -60,6 +60,58 @@
|
|||
#define BLOB_CIPHER_DEBUG false
|
||||
#define BLOB_CIPHER_SERIALIZATION_CHECKS false
|
||||
|
||||
namespace {
|
||||
void validateEncryptHeaderFlagVersion(const int flagsVersion) {
|
||||
ASSERT(CLIENT_KNOBS->ENABLE_CONFIGURABLE_ENCRYPTION);
|
||||
|
||||
if (flagsVersion > CLIENT_KNOBS->ENCRYPT_HEADER_FLAGS_VERSION) {
|
||||
TraceEvent("EncryptHeaderUnsupportedFlagVersion")
|
||||
.detail("MaxSupportedVersion", CLIENT_KNOBS->ENCRYPT_HEADER_FLAGS_VERSION)
|
||||
.detail("Version", flagsVersion);
|
||||
throw not_implemented();
|
||||
}
|
||||
}
|
||||
|
||||
void validateEncryptHeaderAlgoHeaderVersion(const EncryptCipherMode cipherMode,
|
||||
const EncryptAuthTokenMode authMode,
|
||||
const EncryptAuthTokenAlgo authAlgo,
|
||||
const int version) {
|
||||
ASSERT(CLIENT_KNOBS->ENABLE_CONFIGURABLE_ENCRYPTION);
|
||||
|
||||
if (cipherMode != ENCRYPT_CIPHER_MODE_AES_256_CTR) {
|
||||
TraceEvent("EncryptHeaderUnsupportedEncryptCipherMode")
|
||||
.detail("MaxSupportedVersion", CLIENT_KNOBS->ENCRYPT_HEADER_FLAGS_VERSION)
|
||||
.detail("CipherMode", cipherMode);
|
||||
throw not_implemented();
|
||||
}
|
||||
|
||||
int maxSupportedVersion = -1;
|
||||
if (authMode == ENCRYPT_HEADER_AUTH_TOKEN_MODE_NONE) {
|
||||
maxSupportedVersion = CLIENT_KNOBS->ENCRYPT_HEADER_AES_CTR_NO_AUTH_VERSION;
|
||||
} else {
|
||||
ASSERT_EQ(authMode, ENCRYPT_HEADER_AUTH_TOKEN_MODE_SINGLE);
|
||||
|
||||
if (authAlgo == ENCRYPT_HEADER_AUTH_TOKEN_ALGO_HMAC_SHA) {
|
||||
maxSupportedVersion = CLIENT_KNOBS->ENCRYPT_HEADER_AES_CTR_HMAC_SHA_AUTH_VERSION;
|
||||
} else if (authAlgo == ENCRYPT_HEADER_AUTH_TOKEN_ALGO_AES_CMAC) {
|
||||
maxSupportedVersion = CLIENT_KNOBS->ENCRYPT_HEADER_AES_CTR_AES_CMAC_AUTH_VERSION;
|
||||
} else {
|
||||
// Unknown encryption authentication algo
|
||||
}
|
||||
}
|
||||
|
||||
if (version > maxSupportedVersion || maxSupportedVersion == -1) {
|
||||
TraceEvent("EncryptHeaderUnsupportedEncryptAuthToken")
|
||||
.detail("CipherMode", cipherMode)
|
||||
.detail("AuthMode", authMode)
|
||||
.detail("AuthAlgo", authAlgo)
|
||||
.detail("AlgoHeaderVersion", version)
|
||||
.detail("MaxSsupportedVersion", maxSupportedVersion);
|
||||
throw not_implemented();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// BlobCipherEncryptHeaderRef
|
||||
|
||||
uint32_t BlobCipherEncryptHeaderRef::getHeaderSize(const int flagVersion,
|
||||
|
@ -91,62 +143,93 @@ uint32_t BlobCipherEncryptHeaderRef::getHeaderSize(const int flagVersion,
|
|||
return total;
|
||||
}
|
||||
|
||||
const uint8_t* BlobCipherEncryptHeaderRef::getIV() const {
|
||||
ASSERT(CLIENT_KNOBS->ENABLE_CONFIGURABLE_ENCRYPTION);
|
||||
|
||||
validateEncryptHeaderFlagVersion(flagsVersion);
|
||||
ASSERT_EQ(flagsVersion, 1);
|
||||
|
||||
BlobCipherEncryptHeaderFlagsV1 flags = std::get<BlobCipherEncryptHeaderFlagsV1>(this->flags);
|
||||
|
||||
validateEncryptHeaderAlgoHeaderVersion((EncryptCipherMode)flags.encryptMode,
|
||||
(EncryptAuthTokenMode)flags.authTokenMode,
|
||||
(EncryptAuthTokenAlgo)flags.authTokenAlgo,
|
||||
algoHeaderVersion);
|
||||
ASSERT_EQ(algoHeaderVersion, 1);
|
||||
|
||||
return std::visit([](auto& h) { return h.iv; }, algoHeader);
|
||||
}
|
||||
|
||||
template <class>
|
||||
inline constexpr bool always_false_v = false;
|
||||
|
||||
const EncryptHeaderCipherDetails BlobCipherEncryptHeaderRef::getCipherDetails() const {
|
||||
ASSERT(CLIENT_KNOBS->ENABLE_CONFIGURABLE_ENCRYPTION);
|
||||
|
||||
validateEncryptHeaderFlagVersion(flagsVersion);
|
||||
ASSERT_EQ(flagsVersion, 1);
|
||||
|
||||
BlobCipherEncryptHeaderFlagsV1 flags = std::get<BlobCipherEncryptHeaderFlagsV1>(this->flags);
|
||||
|
||||
validateEncryptHeaderAlgoHeaderVersion((EncryptCipherMode)flags.encryptMode,
|
||||
(EncryptAuthTokenMode)flags.authTokenMode,
|
||||
(EncryptAuthTokenAlgo)flags.authTokenAlgo,
|
||||
algoHeaderVersion);
|
||||
ASSERT_EQ(algoHeaderVersion, 1);
|
||||
|
||||
// TODO: Replace with "Overload visitor pattern" someday.
|
||||
return std::visit(
|
||||
[](auto&& h) {
|
||||
using T = std::decay_t<decltype(h)>;
|
||||
if constexpr (std::is_same_v<T, AesCtrNoAuthV1>) {
|
||||
return EncryptHeaderCipherDetails(h.cipherTextDetails);
|
||||
} else if constexpr (std::is_same_v<T, AesCtrWithAuthV1<AUTH_TOKEN_HMAC_SHA_SIZE>> ||
|
||||
std::is_same_v<T, AesCtrWithAuthV1<AUTH_TOKEN_AES_CMAC_SIZE>>) {
|
||||
return EncryptHeaderCipherDetails(h.cipherTextDetails, h.cipherHeaderDetails);
|
||||
} else {
|
||||
static_assert(always_false_v<T>, "Unknown encryption authentication");
|
||||
}
|
||||
},
|
||||
algoHeader);
|
||||
}
|
||||
|
||||
void BlobCipherEncryptHeaderRef::validateEncryptionHeaderDetails(const BlobCipherDetails& textCipherDetails,
|
||||
const BlobCipherDetails& headerCipherDetails,
|
||||
const StringRef& ivRef) const {
|
||||
ASSERT(CLIENT_KNOBS->ENABLE_CONFIGURABLE_ENCRYPTION);
|
||||
|
||||
if (flagsVersion > CLIENT_KNOBS->ENCRYPT_HEADER_FLAGS_VERSION) {
|
||||
TraceEvent("ValidateEncryptHeaderUnsupportedFlagVersion")
|
||||
.detail("MaxSupportedVersion", CLIENT_KNOBS->ENCRYPT_HEADER_FLAGS_VERSION)
|
||||
.detail("Version", flagsVersion);
|
||||
throw not_implemented();
|
||||
}
|
||||
validateEncryptHeaderFlagVersion(flagsVersion);
|
||||
ASSERT_EQ(flagsVersion, 1);
|
||||
|
||||
BlobCipherEncryptHeaderFlagsV1 flags = std::get<BlobCipherEncryptHeaderFlagsV1>(this->flags);
|
||||
|
||||
validateEncryptHeaderAlgoHeaderVersion((EncryptCipherMode)flags.encryptMode,
|
||||
(EncryptAuthTokenMode)flags.authTokenMode,
|
||||
(EncryptAuthTokenAlgo)flags.authTokenAlgo,
|
||||
algoHeaderVersion);
|
||||
ASSERT_EQ(algoHeaderVersion, 1);
|
||||
|
||||
BlobCipherDetails persistedTextCipherDetails;
|
||||
BlobCipherDetails persistedHeaderCipherDetails;
|
||||
uint8_t* persistedIV = nullptr;
|
||||
|
||||
if (flags.authTokenMode == ENCRYPT_HEADER_AUTH_TOKEN_MODE_NONE) {
|
||||
if (algoHeaderVersion > CLIENT_KNOBS->ENCRYPT_HEADER_AES_CTR_NO_AUTH_VERSION) {
|
||||
TraceEvent("ValidateEncryptHeaderUnsupportedAlgoHeaderVersion")
|
||||
.detail("AuthMode", "No-Auth")
|
||||
.detail("MaxSupportedVersion", CLIENT_KNOBS->ENCRYPT_HEADER_AES_CTR_NO_AUTH_VERSION)
|
||||
.detail("Version", algoHeaderVersion);
|
||||
throw not_implemented();
|
||||
}
|
||||
persistedTextCipherDetails = std::get<AesCtrNoAuthV1>(this->algoHeader).cipherTextDetails;
|
||||
persistedIV = (uint8_t*)(&std::get<AesCtrNoAuthV1>(this->algoHeader).iv[0]);
|
||||
} else {
|
||||
if (flags.authTokenAlgo == ENCRYPT_HEADER_AUTH_TOKEN_ALGO_HMAC_SHA) {
|
||||
if (algoHeaderVersion > CLIENT_KNOBS->ENCRYPT_HEADER_AES_CTR_NO_AUTH_VERSION) {
|
||||
TraceEvent("ValidateEncryptHeaderUnsupportedAlgoHeaderVersion")
|
||||
.detail("AuthMode", "Hmac-Sha")
|
||||
.detail("MaxSupportedVersion", CLIENT_KNOBS->ENCRYPT_HEADER_AES_CTR_HMAC_SHA_AUTH_VERSION)
|
||||
.detail("Version", algoHeaderVersion);
|
||||
}
|
||||
persistedTextCipherDetails =
|
||||
std::get<AesCtrWithAuthV1<AUTH_TOKEN_HMAC_SHA_SIZE>>(this->algoHeader).cipherTextDetails;
|
||||
persistedHeaderCipherDetails =
|
||||
std::get<AesCtrWithAuthV1<AUTH_TOKEN_HMAC_SHA_SIZE>>(this->algoHeader).cipherHeaderDetails;
|
||||
persistedIV = (uint8_t*)(&std::get<AesCtrWithAuthV1<AUTH_TOKEN_HMAC_SHA_SIZE>>(this->algoHeader).iv[0]);
|
||||
} else if (flags.authTokenAlgo == ENCRYPT_HEADER_AUTH_TOKEN_ALGO_AES_CMAC) {
|
||||
if (algoHeaderVersion > CLIENT_KNOBS->ENCRYPT_HEADER_AES_CTR_AES_CMAC_AUTH_VERSION) {
|
||||
TraceEvent("ValidateEncryptHeaderUnsupportedAlgoHeaderVersion")
|
||||
.detail("AuthMode", "Aes-Cmac")
|
||||
.detail("MaxSupportedVersion", CLIENT_KNOBS->ENCRYPT_HEADER_AES_CTR_AES_CMAC_AUTH_VERSION)
|
||||
.detail("Version", algoHeaderVersion);
|
||||
}
|
||||
persistedTextCipherDetails =
|
||||
std::get<AesCtrWithAuthV1<AUTH_TOKEN_AES_CMAC_SIZE>>(this->algoHeader).cipherTextDetails;
|
||||
persistedHeaderCipherDetails =
|
||||
std::get<AesCtrWithAuthV1<AUTH_TOKEN_AES_CMAC_SIZE>>(this->algoHeader).cipherHeaderDetails;
|
||||
persistedIV = (uint8_t*)(&std::get<AesCtrWithAuthV1<AUTH_TOKEN_AES_CMAC_SIZE>>(this->algoHeader).iv[0]);
|
||||
} else {
|
||||
throw not_implemented();
|
||||
}
|
||||
}
|
||||
// TODO: Replace with "Overload visitor pattern" someday.
|
||||
return std::visit(
|
||||
[&persistedTextCipherDetails, &persistedHeaderCipherDetails, &persistedIV](auto&& h) {
|
||||
using T = std::decay_t<decltype(h)>;
|
||||
if constexpr (std::is_same_v<T, AesCtrNoAuthV1>) {
|
||||
persistedTextCipherDetails = h.cipherTextDetails;
|
||||
persistedIV = (uint8_t*)&h.iv[0];
|
||||
} else if constexpr (std::is_same_v<T, AesCtrWithAuthV1<AUTH_TOKEN_HMAC_SHA_SIZE>> ||
|
||||
std::is_same_v<T, AesCtrWithAuthV1<AUTH_TOKEN_AES_CMAC_SIZE>>) {
|
||||
persistedTextCipherDetails = h.cipherTextDetails;
|
||||
persistedHeaderCipherDetails = h.cipherHeaderDetails;
|
||||
persistedIV = (uint8_t*)&h.iv[0];
|
||||
} else {
|
||||
static_assert(always_false_v<T>, "Unknown encryption authentication");
|
||||
}
|
||||
},
|
||||
algoHeader);
|
||||
|
||||
// Validate encryption header 'cipherHeader' details sanity
|
||||
if (flags.authTokenMode != ENCRYPT_HEADER_AUTH_TOKEN_MODE_NONE &&
|
||||
|
@ -1808,8 +1891,17 @@ void testConfigurableEncryptionHeaderNoAuthMode(const int minDomainId) {
|
|||
|
||||
BlobCipherEncryptHeaderFlagsV1 flags = std::get<BlobCipherEncryptHeaderFlagsV1>(headerRef.flags);
|
||||
AesCtrNoAuthV1 noAuth = std::get<AesCtrNoAuthV1>(headerRef.algoHeader);
|
||||
Standalone<StringRef> serHeaderRef = BlobCipherEncryptHeaderRef::toStringRef(headerRef);
|
||||
|
||||
const uint8_t* headerIV = headerRef.getIV();
|
||||
ASSERT_EQ(memcmp(&headerIV[0], &iv[0], AES_256_IV_LENGTH), 0);
|
||||
|
||||
EncryptHeaderCipherDetails validateDetails = headerRef.getCipherDetails();
|
||||
ASSERT(validateDetails.textCipherDetails.isValid() &&
|
||||
validateDetails.textCipherDetails ==
|
||||
BlobCipherDetails(cipherKey->getDomainId(), cipherKey->getBaseCipherId(), cipherKey->getSalt()));
|
||||
ASSERT(!validateDetails.headerCipherDetails.present());
|
||||
|
||||
Standalone<StringRef> serHeaderRef = BlobCipherEncryptHeaderRef::toStringRef(headerRef);
|
||||
BlobCipherEncryptHeaderRef validateHeader = BlobCipherEncryptHeaderRef::fromStringRef(serHeaderRef);
|
||||
BlobCipherEncryptHeaderFlagsV1 validateFlags = std::get<BlobCipherEncryptHeaderFlagsV1>(validateHeader.flags);
|
||||
ASSERT(validateFlags == flags);
|
||||
|
@ -2093,8 +2185,20 @@ void testConfigurableEncryptionHeaderSingleAuthMode(int minDomainId) {
|
|||
|
||||
BlobCipherEncryptHeaderFlagsV1 flags = std::get<BlobCipherEncryptHeaderFlagsV1>(headerRef.flags);
|
||||
AesCtrWithAuthV1<AuthTokenSize> algoHeader = std::get<AesCtrWithAuthV1<AuthTokenSize>>(headerRef.algoHeader);
|
||||
Standalone<StringRef> serHeaderRef = BlobCipherEncryptHeaderRef::toStringRef(headerRef);
|
||||
|
||||
const uint8_t* headerIV = headerRef.getIV();
|
||||
ASSERT_EQ(memcmp(&headerIV[0], &iv[0], AES_256_IV_LENGTH), 0);
|
||||
|
||||
EncryptHeaderCipherDetails validateDetails = headerRef.getCipherDetails();
|
||||
ASSERT(validateDetails.textCipherDetails.isValid() &&
|
||||
validateDetails.textCipherDetails ==
|
||||
BlobCipherDetails(cipherKey->getDomainId(), cipherKey->getBaseCipherId(), cipherKey->getSalt()));
|
||||
ASSERT(validateDetails.headerCipherDetails.present() && validateDetails.headerCipherDetails.get().isValid() &&
|
||||
validateDetails.headerCipherDetails.get() == BlobCipherDetails(headerCipherKey->getDomainId(),
|
||||
headerCipherKey->getBaseCipherId(),
|
||||
headerCipherKey->getSalt()));
|
||||
|
||||
Standalone<StringRef> serHeaderRef = BlobCipherEncryptHeaderRef::toStringRef(headerRef);
|
||||
BlobCipherEncryptHeaderRef validateHeader = BlobCipherEncryptHeaderRef::fromStringRef(serHeaderRef);
|
||||
BlobCipherEncryptHeaderFlagsV1 validateFlags = std::get<BlobCipherEncryptHeaderFlagsV1>(validateHeader.flags);
|
||||
ASSERT(validateFlags == flags);
|
||||
|
@ -2105,7 +2209,7 @@ void testConfigurableEncryptionHeaderSingleAuthMode(int minDomainId) {
|
|||
ASSERT_EQ(memcmp(&iv[0], &validateAlgo.iv[0], AES_256_IV_LENGTH), 0);
|
||||
ASSERT_EQ(memcmp(&algoHeader.authToken[0], &validateAlgo.authToken[0], AuthTokenSize), 0);
|
||||
|
||||
TraceEvent("HmacShaHeaderSize")
|
||||
TraceEvent("HeaderSize")
|
||||
.detail("Flags", sizeof(flags))
|
||||
.detail("AlgoHeader", sizeof(algoHeader))
|
||||
.detail("TotalHeader", serHeaderRef.size());
|
||||
|
|
|
@ -174,6 +174,11 @@ struct BlobCipherDetails {
|
|||
}
|
||||
bool operator!=(const BlobCipherDetails& o) const { return !(*this == o); }
|
||||
|
||||
bool isValid() const {
|
||||
return this->encryptDomainId != INVALID_ENCRYPT_DOMAIN_ID &&
|
||||
this->baseCipherId != INVALID_ENCRYPT_CIPHER_KEY_ID && this->salt != INVALID_ENCRYPT_RANDOM_SALT;
|
||||
}
|
||||
|
||||
template <class Ar>
|
||||
void serialize(Ar& ar) {
|
||||
serializer(ar, encryptDomainId, baseCipherId, salt);
|
||||
|
@ -333,6 +338,15 @@ struct AesCtrNoAuthV1 {
|
|||
}
|
||||
};
|
||||
|
||||
struct EncryptHeaderCipherDetails {
|
||||
BlobCipherDetails textCipherDetails;
|
||||
Optional<BlobCipherDetails> headerCipherDetails;
|
||||
|
||||
EncryptHeaderCipherDetails(const BlobCipherDetails& tCipherDetails) : textCipherDetails(tCipherDetails) {}
|
||||
EncryptHeaderCipherDetails(const BlobCipherDetails& tCipherDetails, const BlobCipherDetails& hCipherDetails)
|
||||
: textCipherDetails(tCipherDetails), headerCipherDetails(hCipherDetails) {}
|
||||
};
|
||||
|
||||
struct BlobCipherEncryptHeaderRef {
|
||||
// Serializable fields
|
||||
|
||||
|
@ -460,6 +474,9 @@ struct BlobCipherEncryptHeaderRef {
|
|||
}
|
||||
}
|
||||
|
||||
const uint8_t* getIV() const;
|
||||
const EncryptHeaderCipherDetails getCipherDetails() const;
|
||||
|
||||
void validateEncryptionHeaderDetails(const BlobCipherDetails& textCipherDetails,
|
||||
const BlobCipherDetails& headerCipherDetails,
|
||||
const StringRef& ivRef) const;
|
||||
|
|
|
@ -315,6 +315,24 @@ struct EncryptionOpsWorkload : TestWorkload {
|
|||
auto end = std::chrono::high_resolution_clock::now();
|
||||
|
||||
// validate encrypted buffer size and contents (not matching with plaintext)
|
||||
const uint8_t* headerIV = headerRef->getIV();
|
||||
ASSERT_EQ(memcmp(&headerIV[0], &iv[0], AES_256_IV_LENGTH), 0);
|
||||
|
||||
EncryptHeaderCipherDetails validateDetails = headerRef->getCipherDetails();
|
||||
ASSERT(validateDetails.textCipherDetails.isValid() &&
|
||||
validateDetails.textCipherDetails == BlobCipherDetails(textCipherKey->getDomainId(),
|
||||
textCipherKey->getBaseCipherId(),
|
||||
textCipherKey->getSalt()));
|
||||
if (authMode == ENCRYPT_HEADER_AUTH_TOKEN_MODE_NONE) {
|
||||
ASSERT(!validateDetails.headerCipherDetails.present());
|
||||
} else {
|
||||
ASSERT(validateDetails.headerCipherDetails.present() &&
|
||||
validateDetails.headerCipherDetails.get().isValid() &&
|
||||
validateDetails.headerCipherDetails.get() == BlobCipherDetails(headerCipherKey->getDomainId(),
|
||||
headerCipherKey->getBaseCipherId(),
|
||||
headerCipherKey->getSalt()));
|
||||
}
|
||||
|
||||
ASSERT_EQ(encrypted.size(), len);
|
||||
ASSERT_EQ(headerRef->flagsVersion, CLIENT_KNOBS->ENCRYPT_HEADER_FLAGS_VERSION);
|
||||
ASSERT_NE(memcmp(encrypted.begin(), payload, len), 0);
|
||||
|
|
Loading…
Reference in New Issue