static int generate_enc_materials()

in source/caching_cmm.c [488:581]


static int generate_enc_materials(
    struct aws_cryptosdk_cmm *generic_cmm,
    struct aws_cryptosdk_enc_materials **output,
    struct aws_cryptosdk_enc_request *request) {
    struct caching_cmm *cmm = AWS_CONTAINER_OF(generic_cmm, struct caching_cmm, base);

    bool is_encrypt, should_invalidate = false;
    struct aws_cryptosdk_materials_cache_entry *entry = NULL;
    struct aws_cryptosdk_cache_usage_stats delta_usage;

    delta_usage.bytes_encrypted    = request->plaintext_size;
    delta_usage.messages_encrypted = 1;

    /*
     * If the (maximum) size of the plaintext is larger than our limit, there's no point
     * in doing any cache processing.
     *
     * Additionally, if an uncachable (non-KDF) algorithm is requested, we won't be able
     * to safely process the result, and should also bypass the cache.
     */

    if (delta_usage.bytes_encrypted > cmm->limit_bytes ||
        (request->requested_alg && !can_cache_algorithm(request->requested_alg))) {
        return aws_cryptosdk_cmm_generate_enc_materials(cmm->upstream, output, request);
    }

    uint8_t hash_arr[AWS_CRYPTOSDK_MD_MAX_SIZE];
    struct aws_byte_buf hash_buf = aws_byte_buf_from_array(hash_arr, sizeof(hash_arr));
    if (hash_enc_request(cmm->partition_id, &hash_buf, request)) {
        return AWS_OP_ERR;
    }

    if (aws_cryptosdk_materials_cache_find_entry(cmm->materials_cache, &entry, &is_encrypt, &hash_buf) || !entry ||
        !is_encrypt) {
        goto cache_miss;
    }

    if (!check_ttl(cmm, entry)) {
        goto cache_miss;
    }

    struct aws_cryptosdk_cache_usage_stats stats = delta_usage;
    if (aws_cryptosdk_materials_cache_update_usage_stats(cmm->materials_cache, entry, &stats)) {
        goto cache_miss;
    }

    if (stats.bytes_encrypted > cmm->limit_bytes || stats.messages_encrypted > cmm->limit_messages) {
        goto cache_miss;
    }

    /* If the current message exactly hits the message limit, reuse the data key this time but
     * immediately invalidate it from the cache. If the current message exactly hits the byte
     * limit, we do not invalidate the data key, because we are allowed to reuse it for zero
     * byte length messages.
     */
    if (stats.messages_encrypted == cmm->limit_messages) {
        should_invalidate = true;
    }

    if (aws_cryptosdk_materials_cache_get_enc_materials(
            cmm->materials_cache, request->alloc, output, request->enc_ctx, entry)) {
        goto cache_miss;
    }

    aws_cryptosdk_materials_cache_entry_release(cmm->materials_cache, entry, should_invalidate);

    return AWS_OP_SUCCESS;
cache_miss:
    if (entry) {
        /*
         * If we found the entry but then did a cache miss, it must have been unusable for some reason,
         * and we should invalidate.
         */
        aws_cryptosdk_materials_cache_entry_release(cmm->materials_cache, entry, true);
        entry = NULL;
    }

    if (aws_cryptosdk_cmm_generate_enc_materials(cmm->upstream, output, request)) {
        return AWS_OP_ERR;
    }

    if (can_cache_algorithm((*output)->alg)) {
        aws_cryptosdk_materials_cache_put_entry_for_encrypt(
            cmm->materials_cache, &entry, *output, delta_usage, request->enc_ctx, &hash_buf);

        set_ttl_on_miss(cmm, entry);

        if (entry) {
            aws_cryptosdk_materials_cache_entry_release(cmm->materials_cache, entry, false);
        }
    }

    return AWS_OP_SUCCESS;
}