This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
pxz-hos-client-cpp-module/support/aws-sdk-cpp-master/aws-cpp-sdk-s3-encryption/source/s3-encryption/materials/KMSEncryptionMaterials.cpp

183 lines
9.8 KiB
C++

#include <aws/s3-encryption/materials/KMSEncryptionMaterials.h>
#include <aws/kms/KMSClient.h>
#include <aws/kms/model/GenerateDataKeyRequest.h>
#include <aws/kms/model/GenerateDataKeyResult.h>
#include <aws/kms/model/EncryptRequest.h>
#include <aws/kms/model/EncryptResult.h>
#include <aws/kms/model/DecryptRequest.h>
#include <aws/kms/model/DecryptResult.h>
#include <aws/core/utils/Outcome.h>
#include <aws/core/utils/HashingUtils.h>
#include <aws/core/utils/logging/LogMacros.h>
using namespace Aws;
using namespace Aws::Utils;
using namespace Aws::Utils::Crypto;
using namespace Aws::KMS;
using namespace Aws::KMS::Model;
using namespace Aws::Client;
using namespace Aws::S3Encryption;
namespace Aws
{
namespace S3Encryption
{
namespace Materials
{
static const char* const ALLOCATION_TAG = "KMSEncryptionMaterials";
const char* cmkID_Identifier = "kms_cmk_id";
const char* kmsEncryptionContextKey = "aws:x-amz-cek-alg";
KMSEncryptionMaterialsBase::KMSEncryptionMaterialsBase(const String& customerMasterKeyID, const ClientConfiguration& clientConfig) :
m_customerMasterKeyID(customerMasterKeyID), m_kmsClient(Aws::MakeShared<KMSClient>(ALLOCATION_TAG, clientConfig)), m_allowDecryptWithAnyCMK(true)
{
}
KMSEncryptionMaterialsBase::KMSEncryptionMaterialsBase(const String & customerMasterKeyID, const std::shared_ptr<KMSClient>& kmsClient) :
m_customerMasterKeyID(customerMasterKeyID), m_kmsClient(kmsClient), m_allowDecryptWithAnyCMK(true)
{
}
CryptoOutcome KMSEncryptionMaterialsBase::EncryptCEK(ContentCryptoMaterial & contentCryptoMaterial)
{
// non empty context map passed in.
if (contentCryptoMaterial.GetMaterialsDescription().size())
{
AWS_LOGSTREAM_ERROR(ALLOCATION_TAG, "Customized encryption context map is not allowed for KMS Key wrap algorithm.");
return CryptoOutcome(AWSError<CryptoErrors>(CryptoErrors::ENCRYPT_CONTENT_ENCRYPTION_KEY_FAILED, "EncryptContentEncryptionKeyFailed", "Customized encryption context map is not allowed for KMS Key wrap algorithm.", false/*not retryable*/));
}
EncryptRequest request;
request.SetKeyId(m_customerMasterKeyID);
contentCryptoMaterial.AddMaterialsDescription(cmkID_Identifier, m_customerMasterKeyID);
request.SetEncryptionContext(contentCryptoMaterial.GetMaterialsDescription());
request.SetPlaintext(contentCryptoMaterial.GetContentEncryptionKey());
EncryptOutcome outcome = m_kmsClient->Encrypt(request);
if (!outcome.IsSuccess())
{
AWS_LOGSTREAM_ERROR(ALLOCATION_TAG, "KMS encryption call not successful: "
<< outcome.GetError().GetExceptionName() << " : " << outcome.GetError().GetMessage());
//return without changing the encrypted content encryption key
return CryptoOutcome(AWSError<CryptoErrors>(CryptoErrors::ENCRYPT_CONTENT_ENCRYPTION_KEY_FAILED, "EncryptContentEncryptionKeyFailed", "Failed to encrypt content encryption key(CEK)", false/*not retryable*/));
}
EncryptResult result = outcome.GetResult();
contentCryptoMaterial.SetKeyWrapAlgorithm(KeyWrapAlgorithm::KMS);
contentCryptoMaterial.SetEncryptedContentEncryptionKey(result.GetCiphertextBlob());
contentCryptoMaterial.SetFinalCEK(result.GetCiphertextBlob());
return CryptoOutcome(Aws::NoResult());
}
CryptoOutcome KMSEncryptionMaterialsBase::DecryptCEK(ContentCryptoMaterial &contentCryptoMaterial)
{
auto errorOutcome = CryptoOutcome(AWSError<CryptoErrors>(CryptoErrors::DECRYPT_CONTENT_ENCRYPTION_KEY_FAILED, "DecryptContentEncryptionKeyFailed", "Failed to decrypt content encryption key(CEK)", false/*not retryable*/));
if (m_customerMasterKeyID.empty() && IsKMSDecryptWithAnyCMKAllowed() == false)
{
AWS_LOGSTREAM_ERROR(ALLOCATION_TAG, "Failed to decrypt content encryption key(CEK): KMS CMK is not provided and CMK Any is not allowed.");
return errorOutcome;
}
if (!ValidateDecryptCEKMaterials(contentCryptoMaterial))
{
AWS_LOGSTREAM_ERROR(ALLOCATION_TAG, "Materials Description does not match encryption context.");
return errorOutcome;
}
auto encryptedContentEncryptionKey = contentCryptoMaterial.GetEncryptedContentEncryptionKey();
if (encryptedContentEncryptionKey.GetLength() == 0)
{
AWS_LOGSTREAM_ERROR(ALLOCATION_TAG, "Encrypted content encryption key does not exist.");
return errorOutcome;
}
DecryptRequest request;
if (!m_customerMasterKeyID.empty())
{
request.SetKeyId(m_customerMasterKeyID);
}
request.SetEncryptionContext(contentCryptoMaterial.GetMaterialsDescription());
request.SetCiphertextBlob(encryptedContentEncryptionKey);
DecryptOutcome outcome = m_kmsClient->Decrypt(request);
if (!outcome.IsSuccess())
{
AWS_LOGSTREAM_ERROR(ALLOCATION_TAG, "KMS decryption not successful: "
<< outcome.GetError().GetExceptionName() << outcome.GetError().GetMessage());
return errorOutcome;
}
DecryptResult result = outcome.GetResult();
contentCryptoMaterial.SetContentEncryptionKey(CryptoBuffer(result.GetPlaintext()));
if (contentCryptoMaterial.GetContentEncryptionKey().GetLength() == 0u)
{
AWS_LOGSTREAM_ERROR(ALLOCATION_TAG, "Content Encryption Key could not be decrypted.");
return errorOutcome;
}
return CryptoOutcome(Aws::NoResult());
}
bool KMSEncryptionMaterialsBase::ValidateDecryptCEKMaterials(const ContentCryptoMaterial& contentCryptoMaterial) const
{
switch(contentCryptoMaterial.GetKeyWrapAlgorithm())
{
case KeyWrapAlgorithm::KMS:
{
auto materials = contentCryptoMaterial.GetMaterialsDescription();
auto iterator = materials.find(cmkID_Identifier);
return (iterator == materials.end() || iterator->second == m_customerMasterKeyID);
}
case KeyWrapAlgorithm::KMS_CONTEXT:
{
Aws::String cekAlg = ContentCryptoSchemeMapper::GetNameForContentCryptoScheme(contentCryptoMaterial.GetContentCryptoScheme());
auto materials = contentCryptoMaterial.GetMaterialsDescription();
auto iterator = materials.find(kmsEncryptionContextKey);
return (iterator != materials.end() && iterator->second == cekAlg);
}
default:
return false;
}
}
CryptoOutcome KMSWithContextEncryptionMaterials::EncryptCEK(ContentCryptoMaterial& contentCryptoMaterial)
{
if (contentCryptoMaterial.GetMaterialsDescription().count(kmsEncryptionContextKey))
{
AWS_LOGSTREAM_ERROR(ALLOCATION_TAG, "Conflict in reserved KMS Encryption Context key aws:x-amz-cek-alg. This value is reserved for the S3 Encryption Client and cannot be set by the user.");
return CryptoOutcome(AWSError<CryptoErrors>(CryptoErrors::GENERATE_CONTENT_ENCRYPTION_KEY_FAILED, "GenerateContentEncryptionKeyFailed", "Failed to generate content encryption key(CEK)", false/*not retryable*/));
}
GenerateDataKeyRequest request;
request.SetKeyId(m_customerMasterKeyID);
// Should be "AES/GCM/NoPadding" by default
Aws::String cekAlg = ContentCryptoSchemeMapper::GetNameForContentCryptoScheme(contentCryptoMaterial.GetContentCryptoScheme());
contentCryptoMaterial.AddMaterialsDescription(kmsEncryptionContextKey, cekAlg);
request.SetEncryptionContext(contentCryptoMaterial.GetMaterialsDescription());
request.SetKeySpec(DataKeySpec::AES_256);
GenerateDataKeyOutcome outcome = m_kmsClient->GenerateDataKey(request);
if (!outcome.IsSuccess())
{
AWS_LOGSTREAM_ERROR(ALLOCATION_TAG, "Failed to call KMS GenerateDataKey API with error: "
<< outcome.GetError().GetExceptionName() << " : " << outcome.GetError().GetMessage());
//return without changing the encrypted content encryption key
return CryptoOutcome(AWSError<CryptoErrors>(CryptoErrors::GENERATE_CONTENT_ENCRYPTION_KEY_FAILED, "GenerateContentEncryptionKeyFailed", "Failed to generate content encryption key(CEK)", false/*not retryable*/));
}
GenerateDataKeyResult result = outcome.GetResult();
contentCryptoMaterial.SetKeyWrapAlgorithm(KeyWrapAlgorithm::KMS_CONTEXT);
contentCryptoMaterial.SetContentEncryptionKey(result.GetPlaintext());
contentCryptoMaterial.SetEncryptedContentEncryptionKey(result.GetCiphertextBlob());
contentCryptoMaterial.SetFinalCEK(result.GetCiphertextBlob());
return CryptoOutcome(Aws::NoResult());
}
}//namespace Materials
}//namespace S3Encryption
}//namespace Aws