in aws-encryption-sdk-cpp/source/kms_mrk_keyring.cpp [83:372]
static int OnDecrypt(
struct aws_cryptosdk_keyring *keyring,
struct aws_allocator *request_alloc,
struct aws_byte_buf *unencrypted_data_key,
struct aws_array_list *keyring_trace,
const struct aws_array_list *edks,
const struct aws_hash_table *enc_ctx,
enum aws_cryptosdk_alg_id alg) {
(void)alg;
const struct aws_cryptosdk_alg_properties *props = aws_cryptosdk_alg_props(alg);
auto self = static_cast<KmsMrkAwareSymmetricKeyringImpl *>(keyring);
if (!self || !request_alloc || !unencrypted_data_key || !edks || !enc_ctx) {
abort();
}
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-keyring.txt#2.8
//# If the decryption materials (structures.md#decryption-materials)
//# already contained a valid plaintext data key OnDecrypt MUST
//# immediately return the unmodified decryption materials
//# (structures.md#decryption-materials).
//
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
//# If the decryption materials (structures.md#decryption-materials)
//# already contained a valid plaintext data key OnDecrypt MUST
//# immediately return the unmodified decryption materials
//# (structures.md#decryption-materials).
if (unencrypted_data_key->len) {
return AWS_OP_SUCCESS;
}
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-keyring.txt#2.8
//# If this attempt
//# results in an error, then these errors MUST be collected.
Aws::StringStream error_buf;
const auto enc_ctx_cpp = aws_map_from_c_aws_hash_table(enc_ctx);
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-keyring.txt#2.8
//# The set of encrypted data keys MUST first be filtered to match this
//# keyring's configuration.
//
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-keyring.txt#2.8
//# For each encrypted data key in the filtered set, one at a time, the
//# OnDecrypt MUST attempt to decrypt the data key.
//
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
//# The set of encrypted data keys MUST first be filtered to match this
//# keyring's configuration.
//
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
//# For each encrypted data key in the filtered set, one at a time, the
//# OnDecrypt MUST attempt to decrypt the data key.
size_t num_elems = aws_array_list_length(edks);
bool failed_decrypt_attempt = false;
for (unsigned int idx = 0; idx < num_elems; idx++) {
struct aws_cryptosdk_edk *edk;
int rv = aws_array_list_get_at_ptr(edks, (void **)&edk, idx);
if (rv != AWS_OP_SUCCESS) {
continue;
}
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-keyring.txt#2.8
//# * Its provider ID MUST exactly match the value "aws-kms".
//
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
//# * Its provider ID MUST exactly match the value "aws-kms".
if (!aws_byte_buf_eq(&edk->provider_id, &self->key_provider)) {
continue;
}
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-keyring.txt#2.8
//# * The provider info MUST be a valid AWS KMS ARN (aws-kms-key-
//# arn.md#a-valid-aws-kms-arn) with a resource type of "key" or
//# OnDecrypt MUST fail.
//
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
//# * The provider info MUST be a valid AWS KMS ARN (aws-kms-key-
//# arn.md#a-valid-aws-kms-arn) with a resource type of "key" or
//# OnDecrypt MUST fail.
const Aws::String provider_info_key = Private::aws_string_from_c_aws_byte_buf(&edk->provider_info);
const Aws::Utils::ARN provider_info_key_arn(provider_info_key);
// Precondition: The provider info MUST be a well formed AWS KMS ARN.
if (!Private::is_valid_kms_key_arn(provider_info_key_arn) ||
!Private::starts_with(provider_info_key_arn.GetResource(), "key")) {
error_buf << "Error: Malformed ciphertext. Provider ID field of KMS EDK is invalid KMS CMK ARN: "
<< provider_info_key_arn.GetARNString() << " ";
continue;
}
bool is_mrk = Private::is_kms_mrk_identifier(provider_info_key_arn.GetARNString());
Aws::String key_kms_region = provider_info_key_arn.GetRegion();
Aws::String decrypting_key_kms_region;
if (is_discovery(self)) {
decrypting_key_kms_region = self->region;
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
//# * If the provider info is not identified as a multi-Region key (aws-
//# kms-key-arn.md#identifying-an-aws-kms-multi-region-key), then the
//# provider info's Region MUST match the AWS KMS client region.
if (!is_mrk && self->region != key_kms_region) {
continue;
}
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
//# * If a discovery filter is configured, its partition and the
//# provider info partition MUST match.
//
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
//# * If a discovery filter is configured, its set of accounts MUST
//# contain the provider info account.
if (self->discovery_filter && !self->discovery_filter->IsAuthorized(provider_info_key_arn.GetARNString())) {
continue;
}
} else {
const Aws::Utils::ARN key_arn(self->key_id);
decrypting_key_kms_region = key_arn.GetRegion();
if (!is_mrk) {
if (self->key_id != provider_info_key) {
continue;
}
} else {
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-keyring.txt#2.8
//# * The the function AWS KMS MRK Match for Decrypt (aws-kms-mrk-match-
//# for-decrypt.md#implementation) called with the configured AWS KMS
//# key identifier and the provider info MUST return "true".
if (!Private::kms_mrk_match_for_decrypt(self->key_id, provider_info_key_arn.GetARNString())) {
continue;
}
}
}
std::function<void()> report_success;
auto kms_client = self->kms_client_supplier->GetClient(decrypting_key_kms_region, report_success);
if (!kms_client) {
// Client supplier does not serve this region. Skip.
continue;
}
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
//# * "KeyId": If the provider info's resource type is "key" and its
//# resource is a multi-Region key then a new ARN MUST be created
//# where the region part MUST equal the AWS KMS client region and
//# every other part MUST equal the provider info.
Aws::String decrypt_key_arn;
if (is_mrk) {
if (is_discovery(self)) {
if (Private::starts_with(provider_info_key_arn.GetResource(), "key")) {
decrypt_key_arn = replace_arn_region(provider_info_key_arn, self->region);
} else {
decrypt_key_arn = provider_info_key;
}
} else {
decrypt_key_arn = self->key_id;
}
} else {
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
//# Otherwise it MUST
//# be the provider info.
decrypt_key_arn = provider_info_key;
}
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-keyring.txt#2.8
//# When calling AWS KMS Decrypt
//# (https://docs.aws.amazon.com/kms/latest/APIReference/
//# API_Decrypt.html), the keyring MUST call with a request constructed
//# as follows:
//
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
//# When calling AWS KMS Decrypt
//# (https://docs.aws.amazon.com/kms/latest/APIReference/
//# API_Decrypt.html), the keyring MUST call with a request constructed
//# as follows:
Aws::KMS::Model::DecryptRequest kms_request;
kms_request.WithKeyId(decrypt_key_arn)
.WithCiphertextBlob(aws_utils_byte_buffer_from_c_aws_byte_buf(&edk->ciphertext))
.WithEncryptionContext(enc_ctx_cpp)
.WithGrantTokens(self->grant_tokens);
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-keyring.txt#2.8
//# To attempt to decrypt a particular encrypted data key
//# (structures.md#encrypted-data-key), OnDecrypt MUST call AWS KMS
//# Decrypt (https://docs.aws.amazon.com/kms/latest/APIReference/
//# API_Decrypt.html) with the configured AWS KMS client.
//
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
//# To attempt to decrypt a particular encrypted data key
//# (structures.md#encrypted-data-key), OnDecrypt MUST call AWS KMS
//# Decrypt (https://docs.aws.amazon.com/kms/latest/APIReference/
//# API_Decrypt.html) with the configured AWS KMS client.
Aws::KMS::Model::DecryptOutcome outcome = kms_client->Decrypt(kms_request);
if (!outcome.IsSuccess()) {
// Failing on this call is normal behavior in discovery mode, but not in strict mode.
if (!is_discovery(self)) {
error_buf << "Error: " << outcome.GetError().GetExceptionName()
<< " Message:" << outcome.GetError().GetMessage() << " ";
failed_decrypt_attempt = true;
}
continue;
}
report_success();
const Aws::String &outcome_key_id = outcome.GetResult().GetKeyId();
// NOTE: these citations appear duplicated, but in fact some are for
// the strict keyring, and the others are for the discovery keyring
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-keyring.txt#2.8
//# * The length of the response's "Plaintext" MUST equal the key
//# derivation input length (algorithm-suites.md#key-derivation-input-
//# length) specified by the algorithm suite (algorithm-suites.md)
//# included in the input decryption materials
//# (structures.md#decryption-materials).
//
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
//# * The length of the response's "Plaintext" MUST equal the key
//# derivation input length (algorithm-suites.md#key-derivation-input-
//# length) specified by the algorithm suite (algorithm-suites.md)
//# included in the input decryption materials
//# (structures.md#decryption-materials).
//
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-keyring.txt#2.8
//# * The "KeyId" field in the response MUST equal the configured AWS
//# KMS key identifier.
//
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
//# * The "KeyId" field in the response MUST equal the requested "KeyId"
//
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-keyring.txt#2.8
//# If the response does not satisfies these requirements then an error
//# MUST be collected and the next encrypted data key in the filtered set
//# MUST be attempted.
//
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
//# If the response does not satisfies these requirements then an error
//# is collected and the next encrypted data key in the filtered set MUST
//# be attempted.
if (outcome.GetResult().GetPlaintext().GetLength() != props->content_key_len) {
failed_decrypt_attempt = true;
error_buf << "Malformed plaintext in response. ";
continue;
}
if (outcome_key_id != decrypt_key_arn) {
failed_decrypt_attempt = true;
error_buf << "Incorrect key used for KMS Decrypt. ";
continue;
}
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-keyring.txt#2.8
//# If the response does satisfies these requirements then OnDecrypt MUST
//# do the following with the response:
//
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
//# Since the response does satisfies these requirements then OnDecrypt
//# MUST do the following with the response:
//
// - set the plaintext data key on the [decryption materials](structures.md#decryption-materials) as the
// response Plaintext.
// - immediately return the modified [decryption materials](structures.md#decryption-materials).
int ret =
aws_byte_buf_dup_from_aws_utils(request_alloc, unencrypted_data_key, outcome.GetResult().GetPlaintext());
if (ret == AWS_OP_SUCCESS) {
aws_cryptosdk_keyring_trace_add_record_c_str(
request_alloc,
keyring_trace,
KEY_PROVIDER_STR,
provider_info_key_arn.GetARNString().c_str(),
AWS_CRYPTOSDK_WRAPPING_KEY_DECRYPTED_DATA_KEY | AWS_CRYPTOSDK_WRAPPING_KEY_VERIFIED_ENC_CTX);
}
return ret;
}
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-keyring.txt#2.8
//# If OnDecrypt fails to successfully decrypt any encrypted data key
//# (structures.md#encrypted-data-key), then it MUST yield an error that
//# includes all the collected errors.
//
//= compliance/framework/aws-kms/aws-kms-mrk-aware-symmetric-region-discovery-keyring.txt#2.8
//# If OnDecrypt fails to successfully decrypt any encrypted data key
//# (structures.md#encrypted-data-key), then it MUST yield an error that
//# includes all collected errors.
AWS_LOGSTREAM_ERROR(
AWS_CRYPTO_SDK_KMS_MRK_CLASS_TAG,
"Could not find any data key that can be decrypted by KMS. Errors:" << error_buf.str());
if (failed_decrypt_attempt) {
return aws_raise_error(AWS_CRYPTOSDK_ERR_KMS_FAILURE);
}
// According to materials.h we should return success when no key was found
return AWS_OP_SUCCESS;
}