in aws-encryption-sdk-cpp/source/kms_keyring.cpp [161:301]
static int OnEncrypt(
struct aws_cryptosdk_keyring *keyring,
struct aws_allocator *request_alloc,
struct aws_byte_buf *unencrypted_data_key,
struct aws_array_list *keyring_trace,
struct aws_array_list *edk_list,
const struct aws_hash_table *enc_ctx,
enum aws_cryptosdk_alg_id alg) {
if (!keyring || !request_alloc || !unencrypted_data_key || !edk_list || !enc_ctx) {
abort();
}
auto self = static_cast<Aws::Cryptosdk::Private::KmsKeyringImpl *>(keyring);
/* When no key IDs are configured, i.e. "discovery mode", this function is a no-op. */
if (!self->key_ids.size()) {
return AWS_OP_SUCCESS;
}
Private::ListRaii my_edks(aws_cryptosdk_edk_list_init, aws_cryptosdk_edk_list_clean_up);
Private::ListRaii my_keyring_trace(aws_cryptosdk_keyring_trace_init, aws_cryptosdk_keyring_trace_clean_up);
int rv = my_edks.Create(request_alloc);
if (rv) return rv;
rv = my_keyring_trace.Create(request_alloc);
if (rv) return rv;
const auto enc_ctx_cpp = aws_map_from_c_aws_hash_table(enc_ctx);
bool generated_new_data_key = false;
if (!unencrypted_data_key->buffer) {
const struct aws_cryptosdk_alg_properties *alg_prop = aws_cryptosdk_alg_props(alg);
if (alg_prop == NULL) {
AWS_LOGSTREAM_ERROR(AWS_CRYPTO_SDK_KMS_CLASS_TAG, "Invalid encryption materials algorithm properties");
return aws_raise_error(AWS_CRYPTOSDK_ERR_CRYPTO_UNKNOWN);
}
Aws::String key_id = self->key_ids.front();
// Already checked on keyring build that this will succeed.
Aws::String kms_region = Private::parse_region_from_kms_key_arn(key_id);
std::function<void()> report_success;
auto kms_client = self->kms_client_supplier->GetClient(kms_region, report_success);
if (!kms_client) {
/* Client supplier is allowed to return NULL if, for example, user wants to exclude particular
* regions. But if we are here it means that user configured keyring with a KMS key that was
* incompatible with the client supplier in use.
*/
return aws_raise_error(AWS_CRYPTOSDK_ERR_BAD_STATE);
}
Aws::KMS::Model::GenerateDataKeyRequest kms_request;
kms_request.WithKeyId(key_id)
.WithGrantTokens(self->grant_tokens)
.WithNumberOfBytes((int)alg_prop->data_key_len)
.WithEncryptionContext(enc_ctx_cpp);
Aws::KMS::Model::GenerateDataKeyOutcome outcome = kms_client->GenerateDataKey(kms_request);
if (!outcome.IsSuccess()) {
AWS_LOGSTREAM_ERROR(AWS_CRYPTO_SDK_KMS_CLASS_TAG, "Invalid encryption materials algorithm properties");
return aws_raise_error(AWS_CRYPTOSDK_ERR_KMS_FAILURE);
}
report_success();
rv = append_key_dup_to_edks(
request_alloc,
&my_edks.list,
&outcome.GetResult().GetCiphertextBlob(),
&outcome.GetResult().GetKeyId(),
&self->key_provider);
if (rv != AWS_OP_SUCCESS) return rv;
rv = aws_byte_buf_dup_from_aws_utils(request_alloc, unencrypted_data_key, outcome.GetResult().GetPlaintext());
if (rv != AWS_OP_SUCCESS) return rv;
generated_new_data_key = true;
aws_cryptosdk_keyring_trace_add_record_c_str(
request_alloc,
&my_keyring_trace.list,
KEY_PROVIDER_STR,
key_id.c_str(),
AWS_CRYPTOSDK_WRAPPING_KEY_GENERATED_DATA_KEY | AWS_CRYPTOSDK_WRAPPING_KEY_ENCRYPTED_DATA_KEY |
AWS_CRYPTOSDK_WRAPPING_KEY_SIGNED_ENC_CTX);
}
const auto unencrypted_data_key_cpp = aws_utils_byte_buffer_from_c_aws_byte_buf(unencrypted_data_key);
size_t num_key_ids = self->key_ids.size();
for (size_t key_id_idx = generated_new_data_key ? 1 : 0; key_id_idx < num_key_ids; ++key_id_idx) {
Aws::String key_id = self->key_ids[key_id_idx];
// Already checked on keyring build that this will succeed.
Aws::String kms_region = Private::parse_region_from_kms_key_arn(key_id);
std::function<void()> report_success;
auto kms_client = self->kms_client_supplier->GetClient(kms_region, report_success);
if (!kms_client) {
/* Client supplier is allowed to return NULL if, for example, user wants to exclude particular
* regions. But if we are here it means that user configured keyring with a KMS key that was
* incompatible with the client supplier in use.
*/
rv = aws_raise_error(AWS_CRYPTOSDK_ERR_BAD_STATE);
goto out;
}
Aws::KMS::Model::EncryptRequest kms_request;
kms_request.WithKeyId(key_id)
.WithGrantTokens(self->grant_tokens)
.WithPlaintext(unencrypted_data_key_cpp)
.WithEncryptionContext(enc_ctx_cpp);
Aws::KMS::Model::EncryptOutcome outcome = kms_client->Encrypt(kms_request);
if (!outcome.IsSuccess()) {
AWS_LOGSTREAM_ERROR(
AWS_CRYPTO_SDK_KMS_CLASS_TAG,
"KMS encryption error : " << outcome.GetError().GetExceptionName()
<< " Message: " << outcome.GetError().GetMessage());
rv = aws_raise_error(AWS_CRYPTOSDK_ERR_KMS_FAILURE);
goto out;
}
report_success();
rv = append_key_dup_to_edks(
request_alloc,
&my_edks.list,
&outcome.GetResult().GetCiphertextBlob(),
&outcome.GetResult().GetKeyId(),
&self->key_provider);
if (rv != AWS_OP_SUCCESS) {
goto out;
}
aws_cryptosdk_keyring_trace_add_record_c_str(
request_alloc,
&my_keyring_trace.list,
KEY_PROVIDER_STR,
key_id.c_str(),
AWS_CRYPTOSDK_WRAPPING_KEY_ENCRYPTED_DATA_KEY | AWS_CRYPTOSDK_WRAPPING_KEY_SIGNED_ENC_CTX);
}
rv = aws_cryptosdk_transfer_list(edk_list, &my_edks.list);
if (rv == AWS_OP_SUCCESS) {
aws_cryptosdk_transfer_list(keyring_trace, &my_keyring_trace.list);
}
out:
if (rv != AWS_OP_SUCCESS && generated_new_data_key) {
aws_byte_buf_clean_up(unencrypted_data_key);
}
return rv;
}