@@ -106,6 +106,7 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c
106
106
const Aes128KeyHandle & key, const uint8_t * nonce, size_t nonce_length, uint8_t * ciphertext,
107
107
uint8_t * tag, size_t tag_length)
108
108
{
109
+ ChipLogDetail (Crypto, " ~~~ AES_CCM_encrypt, pl:%u" , (unsigned )plaintext_length);
109
110
VerifyOrReturnError (IsBufferNonEmpty (nonce, nonce_length), CHIP_ERROR_INVALID_ARGUMENT);
110
111
VerifyOrReturnError (IsValidTag (tag, tag_length), CHIP_ERROR_INVALID_ARGUMENT);
111
112
VerifyOrReturnError ((ciphertext != nullptr && plaintext != nullptr ) || plaintext_length == 0 , CHIP_ERROR_INVALID_ARGUMENT);
@@ -114,8 +115,8 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c
114
115
const psa_algorithm_t algorithm = PSA_ALG_AEAD_WITH_SHORTENED_TAG (PSA_ALG_CCM, tag_length);
115
116
psa_status_t status = PSA_SUCCESS;
116
117
psa_aead_operation_t operation = PSA_AEAD_OPERATION_INIT;
117
- size_t out_length;
118
- size_t tag_out_length;
118
+ size_t out_length = 0 ;
119
+ size_t tag_out_length = 0 ;
119
120
120
121
status = psa_aead_encrypt_setup (&operation, key.As <psa_key_id_t >(), algorithm);
121
122
VerifyOrReturnError (status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
@@ -136,20 +137,58 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c
136
137
ChipLogDetail (Crypto, " AES_CCM_encrypt: Using aad == null path" );
137
138
}
138
139
139
- if (plaintext_length != 0 )
140
+ if (0 == plaintext_length )
140
141
{
141
- status = psa_aead_update (&operation, plaintext, plaintext_length, ciphertext,
142
- PSA_AEAD_UPDATE_OUTPUT_SIZE (PSA_KEY_TYPE_AES, algorithm, plaintext_length), &out_length);
143
- VerifyOrReturnError (status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
144
-
145
- ciphertext += out_length;
146
-
147
- status = psa_aead_finish (&operation, ciphertext, PSA_AEAD_FINISH_OUTPUT_SIZE (PSA_KEY_TYPE_AES, algorithm), &out_length, tag,
148
- tag_length, &tag_out_length);
142
+ // Empty plaintext
143
+ status = psa_aead_finish (&operation, nullptr , 0 , &out_length, tag, tag_length, &tag_out_length);
149
144
}
150
145
else
151
146
{
152
- status = psa_aead_finish (&operation, nullptr , 0 , &out_length, tag, tag_length, &tag_out_length);
147
+ // psa_aead_update() requires use of the macro PSA_AEAD_UPDATE_OUTPUT_SIZE to determine the output buffer size.
148
+ // For AES-CCM, PSA_AEAD_UPDATE_OUTPUT_SIZE will round up the size to the next multiple of the block size (16).
149
+ // If the ciphertext length is not a multiple of the block size, we will encrypt in two steps, first with the
150
+ // block_aligned_length, and then with a rounded up partial_block_length, where a temporary buffer will be used for the output.
151
+ constexpr uint8_t kBlockSize = PSA_BLOCK_CIPHER_BLOCK_LENGTH (PSA_KEY_TYPE_AES);
152
+ size_t block_aligned_length = (plaintext_length / kBlockSize ) * kBlockSize ;
153
+ size_t partial_block_length = plaintext_length % kBlockSize ;
154
+ size_t ciphertext_length = 0 ;
155
+ uint8_t temp[kBlockSize ];
156
+
157
+ // Make sure the calculated block_aligned_length is compliant with PSA's output size requirements.
158
+ VerifyOrReturnError (block_aligned_length == PSA_AEAD_UPDATE_OUTPUT_SIZE (PSA_KEY_TYPE_AES, algorithm, block_aligned_length),
159
+ CHIP_ERROR_INTERNAL);
160
+
161
+ // Add the aligned part of the plaintext
162
+ status = psa_aead_update (&operation, plaintext, block_aligned_length, ciphertext, block_aligned_length, &out_length);
163
+ VerifyOrReturnError (status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
164
+ VerifyOrReturnError (out_length == block_aligned_length, CHIP_ERROR_INTERNAL);
165
+ ciphertext_length += out_length;
166
+
167
+ if (partial_block_length > 0 )
168
+ {
169
+ // The update output should fit in the temp buffer
170
+ size_t max_output = PSA_AEAD_UPDATE_OUTPUT_SIZE (PSA_KEY_TYPE_AES, algorithm, partial_block_length);
171
+ VerifyOrReturnError (max_output <= sizeof (temp), CHIP_ERROR_BUFFER_TOO_SMALL);
172
+
173
+ // Add the non-aligned end of the plaintext
174
+ status = psa_aead_update (&operation, &plaintext[block_aligned_length], partial_block_length, temp, max_output, &out_length);
175
+ VerifyOrReturnError (status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
176
+ // Add the encrypted output, if any
177
+ memcpy (&ciphertext[ciphertext_length], temp, out_length);
178
+ ciphertext_length += out_length;
179
+ }
180
+
181
+ // The finish output should fit in the temp buffer
182
+ size_t max_finish = PSA_AEAD_FINISH_OUTPUT_SIZE (PSA_KEY_TYPE_AES, algorithm);
183
+ VerifyOrReturnError (max_finish <= sizeof (temp), CHIP_ERROR_BUFFER_TOO_SMALL);
184
+
185
+ // The finish may return the last part of the ciphertext
186
+ status = psa_aead_finish (&operation, temp, max_finish, &out_length, tag, tag_length, &tag_out_length);
187
+ VerifyOrReturnError (ciphertext_length + out_length <= plaintext_length, CHIP_ERROR_INTERNAL);
188
+ // Add the encrypted output, if any
189
+ memcpy (&ciphertext[ciphertext_length], temp, out_length);
190
+ ciphertext_length += out_length;
191
+ VerifyOrReturnError (ciphertext_length == plaintext_length, CHIP_ERROR_INTERNAL);
153
192
}
154
193
VerifyOrReturnError (status == PSA_SUCCESS && tag_length == tag_out_length, CHIP_ERROR_INTERNAL);
155
194
0 commit comments