Skip to content

Commit

Permalink
[PSA] bugfix for potential buffer overflow from AES_CCM Encrypt/Decry…
Browse files Browse the repository at this point in the history
…pt (#37337)

* [PSA] bugfix for potential buffer overflow from AES_CCM Encrypt/Decrypt

* Integrating Comments

* Integrating comments
  • Loading branch information
Alami-Amine authored Feb 6, 2025
1 parent 779221f commit 35781e5
Showing 1 changed file with 83 additions and 10 deletions.
93 changes: 83 additions & 10 deletions src/crypto/CHIPCryptoPALPSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c
const psa_algorithm_t algorithm = PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, tag_length);
psa_status_t status = PSA_SUCCESS;
psa_aead_operation_t operation = PSA_AEAD_OPERATION_INIT;
size_t out_length;
size_t tag_out_length;
size_t out_length = 0;
size_t tag_out_length = 0;

status = psa_aead_encrypt_setup(&operation, key.As<psa_key_id_t>(), algorithm);
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
Expand All @@ -94,19 +94,58 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c
ChipLogDetail(Crypto, "AES_CCM_encrypt: Using aad == null path");
}

if (plaintext_length != 0)
// psa_aead_update() requires use of the macro PSA_AEAD_UPDATE_OUTPUT_SIZE to determine the output buffer size.
// For AES-CCM, PSA_AEAD_UPDATE_OUTPUT_SIZE will round up the size to the next multiple of the block size (16).
// If the ciphertext length is not a multiple of the block size, we will encrypt in two steps, first with the
// block_aligned_length, and then with a rounded up partial_block_length, where a temporary buffer will be used for the output.
constexpr uint8_t kBlockSize = PSA_BLOCK_CIPHER_BLOCK_LENGTH(PSA_KEY_TYPE_AES);
size_t block_aligned_length = (plaintext_length / kBlockSize) * kBlockSize;
size_t partial_block_length = plaintext_length % kBlockSize;

// Make sure the calculated block_aligned_length is compliant with PSA's output size requirements.
VerifyOrReturnError(block_aligned_length == PSA_AEAD_UPDATE_OUTPUT_SIZE(PSA_KEY_TYPE_AES, algorithm, block_aligned_length),
CHIP_ERROR_INTERNAL);

if (block_aligned_length > 0)
{
status = psa_aead_update(&operation, plaintext, plaintext_length, ciphertext,
PSA_AEAD_UPDATE_OUTPUT_SIZE(PSA_KEY_TYPE_AES, algorithm, plaintext_length), &out_length);
status = psa_aead_update(&operation, plaintext, block_aligned_length, ciphertext, block_aligned_length, &out_length);
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);

ciphertext += out_length;
}

if (partial_block_length > 0)
{
uint8_t temp_buffer[kBlockSize];
size_t rounded_up_length = PSA_AEAD_UPDATE_OUTPUT_SIZE(PSA_KEY_TYPE_AES, algorithm, partial_block_length);

VerifyOrReturnError(rounded_up_length <= sizeof(temp_buffer), CHIP_ERROR_BUFFER_TOO_SMALL);

out_length = 0;
status = psa_aead_update(&operation, plaintext + block_aligned_length, partial_block_length, &temp_buffer[0],
rounded_up_length, &out_length);
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);

VerifyOrReturnError(partial_block_length == out_length, CHIP_ERROR_INTERNAL);
memcpy(ciphertext, temp_buffer, partial_block_length);

ciphertext += out_length;
}

if (plaintext_length != 0)
{
out_length = 0;
tag_out_length = 0;

status = psa_aead_finish(&operation, ciphertext, PSA_AEAD_FINISH_OUTPUT_SIZE(PSA_KEY_TYPE_AES, algorithm), &out_length, tag,
tag_length, &tag_out_length);
}

else
{
out_length = 0;
tag_out_length = 0;

status = psa_aead_finish(&operation, nullptr, 0, &out_length, tag, tag_length, &tag_out_length);
}
VerifyOrReturnError(status == PSA_SUCCESS && tag_length == tag_out_length, CHIP_ERROR_INTERNAL);
Expand All @@ -126,7 +165,7 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_length,
const psa_algorithm_t algorithm = PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, tag_length);
psa_status_t status = PSA_SUCCESS;
psa_aead_operation_t operation = PSA_AEAD_OPERATION_INIT;
size_t outLength;
size_t outLength = 0;

status = psa_aead_decrypt_setup(&operation, key.As<psa_key_id_t>(), algorithm);
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
Expand All @@ -147,20 +186,54 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_length,
ChipLogDetail(Crypto, "AES_CCM_decrypt: Using aad == null path");
}

if (ciphertext_length != 0)
// psa_aead_update() requires use of the macro PSA_AEAD_UPDATE_OUTPUT_SIZE to determine the output buffer size.
// For AES-CCM, PSA_AEAD_UPDATE_OUTPUT_SIZE will round up the size to the next multiple of the block size (16).
// If the ciphertext length is not a multiple of the block size, we will decrypt in two steps, first with the
// block_aligned_length, and then with a rounded up partial_block_length, where a temporary buffer will be used for the output.
constexpr uint8_t kBlockSize = PSA_BLOCK_CIPHER_BLOCK_LENGTH(PSA_KEY_TYPE_AES);
size_t block_aligned_length = (ciphertext_length / kBlockSize) * kBlockSize;
size_t partial_block_length = ciphertext_length % kBlockSize;

// Make sure the calculated block_aligned_length is compliant with PSA's output size requirements.
VerifyOrReturnError(block_aligned_length == PSA_AEAD_UPDATE_OUTPUT_SIZE(PSA_KEY_TYPE_AES, algorithm, block_aligned_length),
CHIP_ERROR_INTERNAL);

if (block_aligned_length > 0)
{
status = psa_aead_update(&operation, ciphertext, ciphertext_length, plaintext,
PSA_AEAD_UPDATE_OUTPUT_SIZE(PSA_KEY_TYPE_AES, algorithm, ciphertext_length), &outLength);
status = psa_aead_update(&operation, ciphertext, block_aligned_length, plaintext, block_aligned_length, &outLength);
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);

plaintext += outLength;
}

if (partial_block_length > 0)
{
uint8_t temp_buffer[kBlockSize];
size_t rounded_up_length = PSA_AEAD_UPDATE_OUTPUT_SIZE(PSA_KEY_TYPE_AES, algorithm, partial_block_length);

VerifyOrReturnError(rounded_up_length <= sizeof(temp_buffer), CHIP_ERROR_BUFFER_TOO_SMALL);

outLength = 0;
status = psa_aead_update(&operation, ciphertext + block_aligned_length, partial_block_length, &temp_buffer[0],
rounded_up_length, &outLength);
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);

VerifyOrReturnError(partial_block_length == outLength, CHIP_ERROR_INTERNAL);
memcpy(plaintext, &temp_buffer[0], partial_block_length);

plaintext += outLength;
}

if (ciphertext_length != 0)
{
outLength = 0;
status = psa_aead_verify(&operation, plaintext, PSA_AEAD_VERIFY_OUTPUT_SIZE(PSA_KEY_TYPE_AES, algorithm), &outLength, tag,
tag_length);
}
else
{
status = psa_aead_verify(&operation, nullptr, 0, &outLength, tag, tag_length);
outLength = 0;
status = psa_aead_verify(&operation, nullptr, 0, &outLength, tag, tag_length);
}

VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
Expand Down

0 comments on commit 35781e5

Please sign in to comment.