static int OnDecrypt()

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;
}