Skip to content

Commit 4815521

Browse files
committed
[crypto] add platform AES-CCM* single-part hook
When OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_SINGLE_PART_ENABLE is set, AesCcm::Engine::ProcessOneShot() calls two new weak platform APIs: otPlatCryptoAesCcmEncryptAndTag() otPlatCryptoAesCcmDecryptAndVerify() Both operate in-place on a contiguous [payload|tag] buffer, mapping to single-part PSA AEAD or a packet-oriented hardware engine. Default weak implementations: - PSA path: psa_aead_encrypt_setup/update/finish and verify. CCM may buffer the entire payload until Finish/Verify, so the same pointer is passed for input and output to avoid a copy. - mbedTLS path: mbedtls_ccm_encrypt_and_tag / mbedtls_ccm_auth_decrypt, both support in-place (input == output).
1 parent 6f7fc46 commit 4815521

7 files changed

Lines changed: 393 additions & 7 deletions

File tree

include/openthread/instance.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ extern "C" {
5252
*
5353
* @note This number versions both OpenThread platform and user APIs.
5454
*/
55-
#define OPENTHREAD_API_VERSION (604)
55+
#define OPENTHREAD_API_VERSION (605)
5656

5757
/**
5858
* @addtogroup api-instance

include/openthread/platform/crypto.h

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,68 @@ otError otPlatCryptoPbkdf2GenerateKey(const uint8_t *aPassword,
749749
uint16_t aKeyLen,
750750
uint8_t *aKey);
751751

752+
/**
753+
* Performs in-place AES-CCM* authenticated encryption in a single call.
754+
*
755+
* On success, plaintext at @p aData is replaced with ciphertext in-place and the
756+
* authentication tag is written to @p aData + @p aPayloadLength.
757+
*
758+
* Requires `OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_SINGLE_PART_ENABLE`.
759+
* A default weak PSA implementation is provided.
760+
*
761+
* @param[in] aKey The key.
762+
* @param[in] aNonce The nonce (IEEE 802.15.4 CCM* format, typically 13 bytes).
763+
* @param[in] aNonceLength Length of @p aNonce in bytes.
764+
* @param[in] aHeader Additional authenticated data (not encrypted).
765+
* @param[in] aHeaderLength Length of @p aHeader in bytes.
766+
* @param[in,out] aData Plaintext on entry, ciphertext on return. Tag written at @p aData + @p
767+
* aPayloadLength.
768+
* @param[in] aPayloadLength Payload length in bytes (not including tag).
769+
* @param[in] aTagLength Tag length in bytes.
770+
*
771+
* @retval OT_ERROR_NONE Success.
772+
* @retval OT_ERROR_FAILED Operation failed.
773+
*/
774+
otError otPlatCryptoAesCcmEncryptAndTag(const otCryptoKey *aKey,
775+
const uint8_t *aNonce,
776+
uint8_t aNonceLength,
777+
const void *aHeader,
778+
uint32_t aHeaderLength,
779+
uint8_t *aData,
780+
uint32_t aPayloadLength,
781+
uint8_t aTagLength);
782+
783+
/**
784+
* Performs in-place AES-CCM* authenticated decryption and tag verification in a single call.
785+
*
786+
* On success, ciphertext at @p aData is replaced with plaintext in-place.
787+
* The tag to verify must be at @p aData + @p aPayloadLength.
788+
*
789+
* Requires `OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_SINGLE_PART_ENABLE`.
790+
* A default weak PSA implementation is provided.
791+
*
792+
* @param[in] aKey The key.
793+
* @param[in] aNonce The nonce (IEEE 802.15.4 CCM* format, typically 13 bytes).
794+
* @param[in] aNonceLength Length of @p aNonce in bytes.
795+
* @param[in] aHeader Additional authenticated data.
796+
* @param[in] aHeaderLength Length of @p aHeader in bytes.
797+
* @param[in,out] aData Ciphertext on entry, plaintext on return. Tag read from @p aData + @p aPayloadLength.
798+
* @param[in] aPayloadLength Payload length in bytes (not including tag).
799+
* @param[in] aTagLength Tag length in bytes.
800+
*
801+
* @retval OT_ERROR_NONE Success.
802+
* @retval OT_ERROR_SECURITY Tag mismatch.
803+
* @retval OT_ERROR_FAILED Operation failed.
804+
*/
805+
otError otPlatCryptoAesCcmDecryptAndVerify(const otCryptoKey *aKey,
806+
const uint8_t *aNonce,
807+
uint8_t aNonceLength,
808+
const void *aHeader,
809+
uint32_t aHeaderLength,
810+
uint8_t *aData,
811+
uint32_t aPayloadLength,
812+
uint8_t aTagLength);
813+
752814
/**
753815
* @}
754816
*/

src/core/config/crypto.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,20 @@
6868
#define OPENTHREAD_CONFIG_CRYPTO_PLATFORM_ALLOCS_CONTEXT 0
6969
#endif
7070

71+
/**
72+
* @def OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_SINGLE_PART_ENABLE
73+
*
74+
* Define to 1 to enable platform single-part AES-CCM* acceleration.
75+
*
76+
* When enabled, `AesCcm::Engine::ProcessOneShot()` calls
77+
* `otPlatCryptoAesCcmEncryptAndTag()` / `otPlatCryptoAesCcmDecryptAndVerify()`
78+
* instead of the built-in software CCM engine.
79+
*
80+
*/
81+
#ifndef OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_SINGLE_PART_ENABLE
82+
#define OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_SINGLE_PART_ENABLE 0
83+
#endif
84+
7185
#if OPENTHREAD_CONFIG_CRYPTO_LIB == OPENTHREAD_CONFIG_CRYPTO_LIB_PLATFORM
7286

7387
/**

src/core/crypto/aes_ccm.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -212,15 +212,27 @@ Error AesCcm::Engine::ProcessOneShot(Operation aOperation,
212212
const uint8_t *aHeader,
213213
uint8_t *aData)
214214
{
215-
// This method performs one-shot (single-part) AES-CCM processing.
216-
// Currently, it is implemented by calling the multi-part
217-
// streaming APIs sequentially. In the future, this can be
218-
// optimized to directly call platform-specific one-shot hardware
219-
// acceleration APIs if supported by the platform.
220-
221215
Error error = kErrorNone;
222216
uint8_t tag[kMaxTagLength];
223217

218+
#if OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_SINGLE_PART_ENABLE
219+
switch (aOperation)
220+
{
221+
case kEncrypt:
222+
error =
223+
otPlatCryptoAesCcmEncryptAndTag(&aConfig.mKey, aConfig.mNonce, aConfig.mNonceLength, aHeader,
224+
aConfig.mHeaderLength, aData, aConfig.mPlainTextLength, aConfig.mTagLength);
225+
break;
226+
case kDecrypt:
227+
error = otPlatCryptoAesCcmDecryptAndVerify(&aConfig.mKey, aConfig.mNonce, aConfig.mNonceLength, aHeader,
228+
aConfig.mHeaderLength, aData, aConfig.mPlainTextLength,
229+
aConfig.mTagLength);
230+
break;
231+
}
232+
233+
return error;
234+
#endif
235+
224236
Start(aConfig);
225237
AddHeader(aHeader, aConfig.mHeaderLength);
226238
AddPayload(aData, aData, aConfig.mPlainTextLength, aOperation);

src/core/crypto/crypto_platform_mbedtls.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include <string.h>
3838

3939
#include <mbedtls/aes.h>
40+
#include <mbedtls/ccm.h>
4041
#include <mbedtls/cmac.h>
4142
#include <mbedtls/ctr_drbg.h>
4243
#include <mbedtls/ecdsa.h>
@@ -152,6 +153,79 @@ OT_TOOL_WEAK otError otPlatCryptoAesFree(otCryptoContext *aContext)
152153
return error;
153154
}
154155

156+
#if OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_SINGLE_PART_ENABLE
157+
158+
OT_TOOL_WEAK otError otPlatCryptoAesCcmEncryptAndTag(const otCryptoKey *aKey,
159+
const uint8_t *aNonce,
160+
uint8_t aNonceLength,
161+
const void *aHeader,
162+
uint32_t aHeaderLength,
163+
uint8_t *aData,
164+
uint32_t aPayloadLength,
165+
uint8_t aTagLength)
166+
{
167+
Error error = kErrorNone;
168+
mbedtls_ccm_context ctx;
169+
int ret;
170+
171+
VerifyOrExit(aKey != nullptr && aKey->mKey != nullptr && aNonce != nullptr && aData != nullptr,
172+
error = kErrorInvalidArgs);
173+
174+
mbedtls_ccm_init(&ctx);
175+
176+
ret = mbedtls_ccm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, aKey->mKey, aKey->mKeyLength * 8);
177+
VerifyOrExit(ret == 0, error = kErrorFailed);
178+
179+
ret = mbedtls_ccm_encrypt_and_tag(&ctx, aPayloadLength, aNonce, aNonceLength, static_cast<const uint8_t *>(aHeader),
180+
aHeaderLength, aData, aData, aData + aPayloadLength, aTagLength);
181+
VerifyOrExit(ret == 0, error = kErrorFailed);
182+
183+
exit:
184+
mbedtls_ccm_free(&ctx);
185+
return error;
186+
}
187+
188+
OT_TOOL_WEAK otError otPlatCryptoAesCcmDecryptAndVerify(const otCryptoKey *aKey,
189+
const uint8_t *aNonce,
190+
uint8_t aNonceLength,
191+
const void *aHeader,
192+
uint32_t aHeaderLength,
193+
uint8_t *aData,
194+
uint32_t aPayloadLength,
195+
uint8_t aTagLength)
196+
{
197+
Error error = kErrorNone;
198+
mbedtls_ccm_context ctx;
199+
int ret;
200+
201+
VerifyOrExit(aKey != nullptr && aKey->mKey != nullptr && aNonce != nullptr && aData != nullptr,
202+
error = kErrorInvalidArgs);
203+
204+
mbedtls_ccm_init(&ctx);
205+
206+
ret = mbedtls_ccm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, aKey->mKey, aKey->mKeyLength * 8);
207+
VerifyOrExit(ret == 0, error = kErrorFailed);
208+
209+
// MBEDTLS_ERR_CCM_AUTH_FAILED is the expected return on tag mismatch; map to kErrorSecurity.
210+
ret = mbedtls_ccm_auth_decrypt(&ctx, aPayloadLength, aNonce, aNonceLength, static_cast<const uint8_t *>(aHeader),
211+
aHeaderLength, aData, aData, aData + aPayloadLength, aTagLength);
212+
213+
if (ret == MBEDTLS_ERR_CCM_AUTH_FAILED)
214+
{
215+
error = kErrorSecurity;
216+
}
217+
else
218+
{
219+
VerifyOrExit(ret == 0, error = kErrorFailed);
220+
}
221+
222+
exit:
223+
mbedtls_ccm_free(&ctx);
224+
return error;
225+
}
226+
227+
#endif // OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_SINGLE_PART_ENABLE
228+
155229
#if OPENTHREAD_FTD || OPENTHREAD_MTD
156230

157231
// HMAC implementations

src/core/crypto/crypto_platform_psa.cpp

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,113 @@ OT_TOOL_WEAK otError otPlatCryptoAesFree(otCryptoContext *aContext)
389389
return kErrorNone;
390390
}
391391

392+
#if OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_SINGLE_PART_ENABLE
393+
394+
OT_TOOL_WEAK otError otPlatCryptoAesCcmEncryptAndTag(const otCryptoKey *aKey,
395+
const uint8_t *aNonce,
396+
uint8_t aNonceLength,
397+
const void *aHeader,
398+
uint32_t aHeaderLength,
399+
uint8_t *aData,
400+
uint32_t aPayloadLength,
401+
uint8_t aTagLength)
402+
{
403+
Error error = kErrorNone;
404+
psa_status_t status;
405+
psa_aead_operation_t operation = PSA_AEAD_OPERATION_INIT;
406+
psa_algorithm_t algorithm = PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, aTagLength);
407+
uint8_t trailing[AesCcm::kMaxTagLength];
408+
size_t trailingLen = 0;
409+
size_t tagLen = 0;
410+
size_t outputLen = 0;
411+
412+
VerifyOrExit(aKey != nullptr && aNonce != nullptr && aData != nullptr, error = kErrorInvalidArgs);
413+
414+
status = psa_aead_encrypt_setup(&operation, aKey->mKeyRef, algorithm);
415+
SuccessOrExit(error = PsaToOtError(status));
416+
417+
status = psa_aead_set_lengths(&operation, aHeaderLength, aPayloadLength);
418+
SuccessOrExit(error = PsaToOtError(status));
419+
420+
status = psa_aead_set_nonce(&operation, aNonce, aNonceLength);
421+
SuccessOrExit(error = PsaToOtError(status));
422+
423+
if (aHeaderLength > 0)
424+
{
425+
status = psa_aead_update_ad(&operation, static_cast<const uint8_t *>(aHeader), aHeaderLength);
426+
SuccessOrExit(error = PsaToOtError(status));
427+
}
428+
429+
// CCM may buffer the entire payload until Finish; pass in-place to avoid a copy.
430+
status = psa_aead_update(&operation, aData, aPayloadLength, aData, aPayloadLength, &outputLen);
431+
SuccessOrExit(error = PsaToOtError(status));
432+
433+
status = psa_aead_finish(&operation, trailing, sizeof(trailing), &trailingLen, aData + aPayloadLength, aTagLength,
434+
&tagLen);
435+
SuccessOrExit(error = PsaToOtError(status));
436+
VerifyOrExit(tagLen == aTagLength, error = kErrorFailed);
437+
438+
exit:
439+
if (error != kErrorNone)
440+
{
441+
psa_aead_abort(&operation);
442+
}
443+
444+
return error;
445+
}
446+
447+
OT_TOOL_WEAK otError otPlatCryptoAesCcmDecryptAndVerify(const otCryptoKey *aKey,
448+
const uint8_t *aNonce,
449+
uint8_t aNonceLength,
450+
const void *aHeader,
451+
uint32_t aHeaderLength,
452+
uint8_t *aData,
453+
uint32_t aPayloadLength,
454+
uint8_t aTagLength)
455+
{
456+
Error error = kErrorNone;
457+
psa_status_t status;
458+
psa_aead_operation_t operation = PSA_AEAD_OPERATION_INIT;
459+
psa_algorithm_t algorithm = PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, aTagLength);
460+
uint8_t trailing[AesCcm::kMaxTagLength];
461+
size_t trailingLen = 0;
462+
size_t outputLen = 0;
463+
464+
VerifyOrExit(aKey != nullptr && aNonce != nullptr && aData != nullptr, error = kErrorInvalidArgs);
465+
466+
status = psa_aead_decrypt_setup(&operation, aKey->mKeyRef, algorithm);
467+
SuccessOrExit(error = PsaToOtError(status));
468+
469+
status = psa_aead_set_lengths(&operation, aHeaderLength, aPayloadLength);
470+
SuccessOrExit(error = PsaToOtError(status));
471+
472+
status = psa_aead_set_nonce(&operation, aNonce, aNonceLength);
473+
SuccessOrExit(error = PsaToOtError(status));
474+
475+
if (aHeaderLength > 0)
476+
{
477+
status = psa_aead_update_ad(&operation, static_cast<const uint8_t *>(aHeader), aHeaderLength);
478+
SuccessOrExit(error = PsaToOtError(status));
479+
}
480+
481+
// CCM may buffer the entire payload until Verify; pass in-place to avoid a copy.
482+
status = psa_aead_update(&operation, aData, aPayloadLength, aData, aPayloadLength, &outputLen);
483+
SuccessOrExit(error = PsaToOtError(status));
484+
485+
status = psa_aead_verify(&operation, trailing, sizeof(trailing), &trailingLen, aData + aPayloadLength, aTagLength);
486+
error = (status == PSA_ERROR_INVALID_SIGNATURE) ? kErrorSecurity : PsaToOtError(status);
487+
488+
exit:
489+
if (error != kErrorNone)
490+
{
491+
psa_aead_abort(&operation);
492+
}
493+
494+
return error;
495+
}
496+
497+
#endif // OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_SINGLE_PART_ENABLE
498+
392499
#if OPENTHREAD_FTD || OPENTHREAD_MTD
393500

394501
OT_TOOL_WEAK otError otPlatCryptoHmacSha256Init(otCryptoContext *aContext)

0 commit comments

Comments
 (0)