Skip to content

Commit fe7d07a

Browse files
authored
psa: fix buffer overflow (project-chip#42015)
Signed-off-by: Doru-Cristian Gucea <[email protected]>
1 parent bf1f0e5 commit fe7d07a

File tree

1 file changed

+48
-28
lines changed

1 file changed

+48
-28
lines changed

src/crypto/CHIPCryptoPALPSA.cpp

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c
128128
constexpr uint8_t kBlockSize = PSA_BLOCK_CIPHER_BLOCK_LENGTH(PSA_KEY_TYPE_AES);
129129
size_t block_aligned_length = (plaintext_length / kBlockSize) * kBlockSize;
130130
size_t partial_block_length = plaintext_length % kBlockSize;
131+
size_t ciphertext_length = 0;
131132

132133
// Make sure the calculated block_aligned_length is compliant with PSA's output size requirements.
133134
VerifyOrReturnError(block_aligned_length == PSA_AEAD_UPDATE_OUTPUT_SIZE(PSA_KEY_TYPE_AES, algorithm, block_aligned_length),
@@ -137,37 +138,45 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c
137138
{
138139
status = psa_aead_update(&operation, plaintext, block_aligned_length, ciphertext, block_aligned_length, &out_length);
139140
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
140-
141-
ciphertext += out_length;
141+
VerifyOrReturnError(out_length <= block_aligned_length, CHIP_ERROR_INTERNAL);
142+
ciphertext_length += out_length;
142143
}
143144

144145
if (partial_block_length > 0)
145146
{
146-
uint8_t temp_buffer[kBlockSize];
147+
uint8_t temp[kBlockSize] = { 0 };
147148
size_t rounded_up_length = PSA_AEAD_UPDATE_OUTPUT_SIZE(PSA_KEY_TYPE_AES, algorithm, partial_block_length);
148149

149-
VerifyOrReturnError(rounded_up_length <= sizeof(temp_buffer), CHIP_ERROR_BUFFER_TOO_SMALL);
150+
VerifyOrReturnError(rounded_up_length <= sizeof(temp), CHIP_ERROR_INTERNAL);
150151

151152
out_length = 0;
152-
status = psa_aead_update(&operation, plaintext + block_aligned_length, partial_block_length, &temp_buffer[0],
153-
rounded_up_length, &out_length);
153+
status = psa_aead_update(&operation, plaintext + block_aligned_length, partial_block_length, temp, rounded_up_length,
154+
&out_length);
154155
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
155156

156-
VerifyOrReturnError(partial_block_length == out_length, CHIP_ERROR_INTERNAL);
157-
memcpy(ciphertext, temp_buffer, partial_block_length);
158-
159-
ciphertext += out_length;
157+
VerifyOrReturnError(ciphertext_length + out_length <= plaintext_length, CHIP_ERROR_INTERNAL);
158+
// Add the encrypted output, if any
159+
memcpy(&ciphertext[ciphertext_length], temp, out_length);
160+
ciphertext_length += out_length;
160161
}
161162

162163
if (plaintext_length != 0)
163164
{
164-
out_length = 0;
165-
tag_out_length = 0;
165+
uint8_t temp[kBlockSize] = { 0 };
166166

167-
status = psa_aead_finish(&operation, ciphertext, PSA_AEAD_FINISH_OUTPUT_SIZE(PSA_KEY_TYPE_AES, algorithm), &out_length, tag,
168-
tag_length, &tag_out_length);
169-
}
167+
// The finish output should fit in the temp buffer
168+
size_t max_finish = PSA_AEAD_FINISH_OUTPUT_SIZE(PSA_KEY_TYPE_AES, algorithm);
169+
VerifyOrReturnError(max_finish <= sizeof(temp), CHIP_ERROR_BUFFER_TOO_SMALL);
170170

171+
// The finish may return the last part of the ciphertext
172+
status = psa_aead_finish(&operation, temp, max_finish, &out_length, tag, tag_length, &tag_out_length);
173+
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
174+
// For CCM encryption, the ciphertext and plaintext length are equal
175+
VerifyOrReturnError((ciphertext_length + out_length) == plaintext_length, CHIP_ERROR_INTERNAL);
176+
// Add the encrypted output, if any
177+
memcpy(&ciphertext[ciphertext_length], temp, out_length);
178+
ciphertext_length += out_length;
179+
}
171180
else
172181
{
173182
out_length = 0;
@@ -242,6 +251,8 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_length,
242251
constexpr uint8_t kBlockSize = PSA_BLOCK_CIPHER_BLOCK_LENGTH(PSA_KEY_TYPE_AES);
243252
size_t block_aligned_length = (ciphertext_length / kBlockSize) * kBlockSize;
244253
size_t partial_block_length = ciphertext_length % kBlockSize;
254+
size_t plaintext_length = 0;
255+
uint8_t temp[kBlockSize] = { 0 };
245256

246257
// Make sure the calculated block_aligned_length is compliant with PSA's output size requirements.
247258
VerifyOrReturnError(block_aligned_length == PSA_AEAD_UPDATE_OUTPUT_SIZE(PSA_KEY_TYPE_AES, algorithm, block_aligned_length),
@@ -251,33 +262,42 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_length,
251262
{
252263
status = psa_aead_update(&operation, ciphertext, block_aligned_length, plaintext, block_aligned_length, &out_length);
253264
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
254-
255-
plaintext += out_length;
265+
VerifyOrReturnError(out_length <= block_aligned_length, CHIP_ERROR_INTERNAL);
266+
plaintext_length += out_length;
256267
}
257268

258269
if (partial_block_length > 0)
259270
{
260-
uint8_t temp_buffer[kBlockSize];
261271
size_t rounded_up_length = PSA_AEAD_UPDATE_OUTPUT_SIZE(PSA_KEY_TYPE_AES, algorithm, partial_block_length);
262-
263-
VerifyOrReturnError(rounded_up_length <= sizeof(temp_buffer), CHIP_ERROR_BUFFER_TOO_SMALL);
272+
VerifyOrReturnError(rounded_up_length <= sizeof(temp), CHIP_ERROR_BUFFER_TOO_SMALL);
264273

265274
out_length = 0;
266-
status = psa_aead_update(&operation, ciphertext + block_aligned_length, partial_block_length, &temp_buffer[0],
267-
rounded_up_length, &out_length);
275+
status = psa_aead_update(&operation, ciphertext + block_aligned_length, partial_block_length, temp, rounded_up_length,
276+
&out_length);
268277
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
269278

270-
VerifyOrReturnError(partial_block_length == out_length, CHIP_ERROR_INTERNAL);
271-
memcpy(plaintext, &temp_buffer[0], partial_block_length);
272-
273-
plaintext += out_length;
279+
VerifyOrReturnError(plaintext_length + out_length <= ciphertext_length, CHIP_ERROR_INTERNAL);
280+
// Add the decrypted output, if any
281+
memcpy(&plaintext[plaintext_length], temp, out_length);
282+
plaintext_length += out_length;
274283
}
275284

285+
// The finish output should fit in the temp buffer
286+
size_t max_verify = PSA_AEAD_VERIFY_OUTPUT_SIZE(PSA_KEY_TYPE_AES, algorithm);
287+
VerifyOrReturnError(max_verify <= sizeof(temp), CHIP_ERROR_BUFFER_TOO_SMALL);
288+
276289
if (ciphertext_length != 0)
277290
{
278291
out_length = 0;
279-
status = psa_aead_verify(&operation, plaintext, PSA_AEAD_VERIFY_OUTPUT_SIZE(PSA_KEY_TYPE_AES, algorithm), &out_length, tag,
280-
tag_length);
292+
memset(temp, 0, sizeof(temp));
293+
294+
status = psa_aead_verify(&operation, temp, max_verify, &out_length, tag, tag_length);
295+
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
296+
// For CCM decryption, the ciphertext and plaintext length are equal
297+
VerifyOrReturnError((plaintext_length + out_length) == ciphertext_length, CHIP_ERROR_INTERNAL);
298+
// Add the decrypted output, if any
299+
memcpy(&plaintext[plaintext_length], temp, out_length);
300+
plaintext_length += out_length;
281301
}
282302
else
283303
{

0 commit comments

Comments
 (0)