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