in aws-encryption-sdk-cpp/source/kms_keyring.cpp [49:159]
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;
auto self = static_cast<Aws::Cryptosdk::Private::KmsKeyringImpl *>(keyring);
if (!self || !request_alloc || !unencrypted_data_key || !edks || !enc_ctx) {
abort();
}
Aws::StringStream error_buf;
const auto enc_ctx_cpp = aws_map_from_c_aws_hash_table(enc_ctx);
size_t num_elems = aws_array_list_length(edks);
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;
}
if (!aws_byte_buf_eq(&edk->provider_id, &self->key_provider)) {
// EDK belongs to a different non KMS keyring. Skip.
continue;
}
const Aws::String key_arn = Private::aws_string_from_c_aws_byte_buf(&edk->provider_info);
/* If there are no key IDs in the list, keyring is in "discovery" mode and will attempt KMS calls with
* every key ARN it comes across in the message, so long as the key ARN is authorized by the
* DiscoveryFilter (matches the partition and an account ID).
*
* If there are key IDs in the list, it will cross check the ARN it reads with that list
* before attempting KMS calls. Note that if caller provided key IDs in anything other than
* a CMK ARN format, the SDK will not attempt to decrypt those data keys, because the EDK
* data format always specifies the CMK with the full (non-alias) ARN.
*/
if (self->key_ids.size() &&
std::find(self->key_ids.begin(), self->key_ids.end(), key_arn) == self->key_ids.end()) {
// This keyring does not have access to the CMK used to encrypt this data key. Skip.
continue;
}
// self->discovery_filter is non-null only if self was constructed via BuildDiscovery, which
// in turn implies discovery mode
if (self->discovery_filter && !self->discovery_filter->IsAuthorized(key_arn)) {
// The DiscoveryFilter blocks the CMK used to encrypt this data key. Skip.
continue;
}
Aws::String kms_region = Private::parse_region_from_kms_key_arn(key_arn);
if (kms_region.empty()) {
error_buf << "Error: Malformed ciphertext. Provider ID field of KMS EDK is invalid KMS CMK ARN: " << key_arn
<< " ";
continue;
}
std::function<void()> report_success;
auto kms_client = self->kms_client_supplier->GetClient(kms_region, report_success);
if (!kms_client) {
// Client supplier does not serve this region. Skip.
continue;
}
Aws::KMS::Model::DecryptRequest kms_request;
kms_request.WithGrantTokens(self->grant_tokens)
.WithKeyId(key_arn)
.WithCiphertextBlob(aws_utils_byte_buffer_from_c_aws_byte_buf(&edk->ciphertext))
.WithEncryptionContext(enc_ctx_cpp);
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 standard mode.
if (self->key_ids.size()) {
error_buf << "Error: " << outcome.GetError().GetExceptionName()
<< " Message:" << outcome.GetError().GetMessage() << " ";
}
continue;
}
report_success();
const Aws::String &outcome_key_id = outcome.GetResult().GetKeyId();
if (outcome_key_id == key_arn) {
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,
key_arn.c_str(),
AWS_CRYPTOSDK_WRAPPING_KEY_DECRYPTED_DATA_KEY | AWS_CRYPTOSDK_WRAPPING_KEY_VERIFIED_ENC_CTX);
}
return ret;
} else {
// Since we specified the key ARN explicitly in the request,
// KMS had better use that key to decrypt
return aws_raise_error(AWS_ERROR_INVALID_STATE);
}
}
AWS_LOGSTREAM_ERROR(
AWS_CRYPTO_SDK_KMS_CLASS_TAG,
"Could not find any data key that can be decrypted by KMS. Errors:" << error_buf.str());
// According to materials.h we should return success when no key was found
return AWS_OP_SUCCESS;
}