Skip to content

Commit 36609f6

Browse files
committed
[crypto] add platform AES-CCM* one-shot hook
When OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_ONE_SHOT_ENABLE is set, AesCcm::Engine::ProcessOneShot() calls the new weak platform hook: otPlatCryptoAesCcmProcessOneShot() The hook operates in-place on a contiguous [payload|tag] buffer, mapping to a one-shot PSA AEAD call or a packet-oriented hardware engine. Default weak implementations: - PSA path: psa_aead_encrypt / psa_aead_decrypt (one-shot). - mbedTLS path: mbedtls_ccm_encrypt_and_tag / mbedtls_ccm_auth_decrypt, both support in-place (input == output).
1 parent 0fbee98 commit 36609f6

8 files changed

Lines changed: 275 additions & 19 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 (605)
55+
#define OPENTHREAD_API_VERSION (606)
5656

5757
/**
5858
* @addtogroup api-instance

include/openthread/platform/crypto.h

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

752+
/**
753+
* @struct otPlatCryptoAesCcmConfig
754+
*
755+
* Holds the parameters for a one-shot AES-CCM* operation passed to `otPlatCryptoAesCcmProcessOneShot`.
756+
*/
757+
typedef struct otPlatCryptoAesCcmConfig
758+
{
759+
otCryptoKey mKey; ///< The encryption key.
760+
const uint8_t *mNonce; ///< Pointer to the nonce buffer (IEEE 802.15.4 CCM* format, 13 bytes).
761+
uint8_t mNonceLength; ///< Length of @p mNonce in bytes.
762+
uint8_t mTagLength; ///< Authentication tag length in bytes (even; 4, 6, 8, 10, 12, 14, or 16).
763+
uint32_t mHeaderLength; ///< Length of the additional authenticated data (header) in bytes.
764+
uint32_t mPlainTextLength; ///< Payload length in bytes (excluding tag).
765+
} otPlatCryptoAesCcmConfig;
766+
767+
/**
768+
* Performs in-place AES-CCM* authenticated encryption or decryption in a single call.
769+
*
770+
* For encryption (@p aEncrypt == true):
771+
* - Plaintext at @p aData is replaced with ciphertext in-place.
772+
* - The authentication tag is written to @p aData + @p aConfig->mPlainTextLength.
773+
*
774+
* For decryption (@p aEncrypt == false):
775+
* - Ciphertext at @p aData is replaced with plaintext in-place.
776+
* - The tag to verify must be at @p aData + @p aConfig->mPlainTextLength.
777+
*
778+
* Requires `OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_ONE_SHOT_ENABLE`.
779+
* A default weak PSA implementation is provided.
780+
*
781+
* @param[in] aEncrypt True to encrypt and generate tag; false to decrypt and verify tag.
782+
* @param[in] aConfig CCM* parameters (key, nonce, lengths).
783+
* @param[in] aHeader Additional authenticated data (not encrypted). May be NULL if header length is 0.
784+
* @param[in,out] aData Payload buffer (plaintext on encrypt entry, ciphertext on decrypt entry).
785+
* The buffer must hold @p aConfig->mPlainTextLength + @p aConfig->mTagLength bytes.
786+
*
787+
* @retval OT_ERROR_NONE Success.
788+
* @retval OT_ERROR_SECURITY Tag mismatch (decrypt only).
789+
* @retval OT_ERROR_FAILED Operation failed.
790+
*/
791+
otError otPlatCryptoAesCcmProcessOneShot(bool aEncrypt,
792+
const otPlatCryptoAesCcmConfig *aConfig,
793+
const uint8_t *aHeader,
794+
uint8_t *aData);
795+
752796
/**
753797
* @}
754798
*/

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_ONE_SHOT_ENABLE
73+
*
74+
* Define to 1 to enable platform one-shot AES-CCM* acceleration.
75+
*
76+
* When enabled, `AesCcm::Engine::ProcessOneShot()` calls
77+
* `otPlatCryptoAesCcmProcessOneShot()` instead of the built-in
78+
* software CCM engine.
79+
*
80+
*/
81+
#ifndef OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_ONE_SHOT_ENABLE
82+
#define OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_ONE_SHOT_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: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -212,13 +212,11 @@ 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.
215+
Error error = kErrorNone;
220216

221-
Error error = kErrorNone;
217+
#if OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_ONE_SHOT_ENABLE
218+
error = otPlatCryptoAesCcmProcessOneShot(aOperation == kEncrypt, &aConfig, aHeader, aData);
219+
#else
222220
uint8_t tag[kMaxTagLength];
223221

224222
Start(aConfig);
@@ -236,6 +234,7 @@ Error AesCcm::Engine::ProcessOneShot(Operation aOperation,
236234
error = (memcmp(aData + aConfig.mPlainTextLength, tag, aConfig.mTagLength) == 0) ? kErrorNone : kErrorSecurity;
237235
break;
238236
}
237+
#endif
239238

240239
return error;
241240
}
@@ -249,7 +248,7 @@ void AesCcm::Engine::Start(const Config &aConfig)
249248

250249
OT_ASSERT(aConfig.IsValid());
251250

252-
mEcb.SetKey(aConfig.mKey);
251+
mEcb.SetKey(aConfig.GetKey());
253252

254253
mNonceLength = aConfig.mNonceLength;
255254
mTagLength = aConfig.mTagLength;

src/core/crypto/aes_ccm.hpp

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ class AesCcm
119119
* @param[in] aKey A pointer to the key.
120120
* @param[in] aKeyLength Length of the key in bytes.
121121
*/
122-
void SetKey(const uint8_t *aKey, uint16_t aKeyLength) { mConfig.mKey.Set(aKey, aKeyLength); }
122+
void SetKey(const uint8_t *aKey, uint16_t aKeyLength) { mConfig.GetKey().Set(aKey, aKeyLength); }
123123

124124
/**
125125
* Sets the key.
@@ -128,7 +128,7 @@ class AesCcm
128128
*
129129
* @param[in] aMacKey Key Material for AES operation.
130130
*/
131-
void SetKey(const Mac::KeyMaterial &aMacKey) { aMacKey.ConvertToCryptoKey(mConfig.mKey); }
131+
void SetKey(const Mac::KeyMaterial &aMacKey) { aMacKey.ConvertToCryptoKey(mConfig.GetKey()); }
132132

133133
/**
134134
* Sets the Nonce.
@@ -245,16 +245,11 @@ class AesCcm
245245
void *aTag);
246246

247247
private:
248-
struct Config : public Clearable<Config>
248+
struct Config : public otPlatCryptoAesCcmConfig, public Clearable<Config>
249249
{
250-
bool IsValid(void) const;
251-
252-
Key mKey;
253-
uint8_t mNonceLength;
254-
uint8_t mTagLength;
255-
uint32_t mHeaderLength;
256-
uint32_t mPlainTextLength;
257-
const uint8_t *mNonce;
250+
bool IsValid(void) const;
251+
Key &GetKey(void) { return AsCoreType(&mKey); }
252+
const Key &GetKey(void) const { return AsCoreType(&mKey); }
258253
};
259254

260255
class Engine

src/core/crypto/crypto_platform_mbedtls.cpp

Lines changed: 51 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,56 @@ OT_TOOL_WEAK otError otPlatCryptoAesFree(otCryptoContext *aContext)
152153
return error;
153154
}
154155

156+
#if OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_ONE_SHOT_ENABLE
157+
158+
OT_TOOL_WEAK otError otPlatCryptoAesCcmProcessOneShot(bool aEncrypt,
159+
const otPlatCryptoAesCcmConfig *aConfig,
160+
const uint8_t *aHeader,
161+
uint8_t *aData)
162+
{
163+
Error error = kErrorNone;
164+
mbedtls_ccm_context ctx;
165+
int ret;
166+
167+
mbedtls_ccm_init(&ctx);
168+
169+
VerifyOrExit(aConfig != nullptr && aConfig->mKey.mKey != nullptr && aConfig->mNonce != nullptr && aData != nullptr,
170+
error = kErrorInvalidArgs);
171+
172+
ret = mbedtls_ccm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, aConfig->mKey.mKey, aConfig->mKey.mKeyLength * 8);
173+
VerifyOrExit(ret == 0, error = kErrorFailed);
174+
175+
if (aEncrypt)
176+
{
177+
ret = mbedtls_ccm_encrypt_and_tag(&ctx, aConfig->mPlainTextLength, aConfig->mNonce, aConfig->mNonceLength,
178+
aHeader, aConfig->mHeaderLength, aData, aData,
179+
aData + aConfig->mPlainTextLength, aConfig->mTagLength);
180+
VerifyOrExit(ret == 0, error = kErrorFailed);
181+
}
182+
else
183+
{
184+
// MBEDTLS_ERR_CCM_AUTH_FAILED is the expected return on tag mismatch; map to kErrorSecurity.
185+
ret = mbedtls_ccm_auth_decrypt(&ctx, aConfig->mPlainTextLength, aConfig->mNonce, aConfig->mNonceLength, aHeader,
186+
aConfig->mHeaderLength, aData, aData, aData + aConfig->mPlainTextLength,
187+
aConfig->mTagLength);
188+
189+
if (ret == MBEDTLS_ERR_CCM_AUTH_FAILED)
190+
{
191+
error = kErrorSecurity;
192+
}
193+
else
194+
{
195+
VerifyOrExit(ret == 0, error = kErrorFailed);
196+
}
197+
}
198+
199+
exit:
200+
mbedtls_ccm_free(&ctx);
201+
return error;
202+
}
203+
204+
#endif // OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_ONE_SHOT_ENABLE
205+
155206
#if OPENTHREAD_FTD || OPENTHREAD_MTD
156207

157208
// HMAC implementations

src/core/crypto/crypto_platform_psa.cpp

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

392+
#if OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_ONE_SHOT_ENABLE
393+
394+
OT_TOOL_WEAK otError otPlatCryptoAesCcmProcessOneShot(bool aEncrypt,
395+
const otPlatCryptoAesCcmConfig *aConfig,
396+
const uint8_t *aHeader,
397+
uint8_t *aData)
398+
{
399+
Error error = kErrorNone;
400+
psa_status_t status;
401+
psa_algorithm_t algorithm;
402+
size_t outputLen = 0;
403+
404+
VerifyOrExit(aConfig != nullptr && aConfig->mNonce != nullptr && aData != nullptr, error = kErrorInvalidArgs);
405+
406+
algorithm = PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, aConfig->mTagLength);
407+
408+
if (aEncrypt)
409+
{
410+
// Output layout: ciphertext || tag, written in-place over the plaintext buffer.
411+
status = psa_aead_encrypt(aConfig->mKey.mKeyRef, algorithm, aConfig->mNonce, aConfig->mNonceLength, aHeader,
412+
aConfig->mHeaderLength, aData, aConfig->mPlainTextLength, aData,
413+
aConfig->mPlainTextLength + aConfig->mTagLength, &outputLen);
414+
SuccessOrExit(error = PsaToOtError(status));
415+
VerifyOrExit(outputLen == aConfig->mPlainTextLength + aConfig->mTagLength, error = kErrorFailed);
416+
}
417+
else
418+
{
419+
// Input layout: ciphertext || tag contiguous at aData. Output plaintext written in-place.
420+
status = psa_aead_decrypt(aConfig->mKey.mKeyRef, algorithm, aConfig->mNonce, aConfig->mNonceLength, aHeader,
421+
aConfig->mHeaderLength, aData, aConfig->mPlainTextLength + aConfig->mTagLength, aData,
422+
aConfig->mPlainTextLength, &outputLen);
423+
error = (status == PSA_ERROR_INVALID_SIGNATURE) ? kErrorSecurity : PsaToOtError(status);
424+
}
425+
426+
exit:
427+
return error;
428+
}
429+
430+
#endif // OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_ONE_SHOT_ENABLE
431+
392432
#if OPENTHREAD_FTD || OPENTHREAD_MTD
393433

394434
OT_TOOL_WEAK otError otPlatCryptoHmacSha256Init(otCryptoContext *aContext)

0 commit comments

Comments
 (0)