static int OnEncrypt()

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