Skip to content

Commit 20c782f

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 c34311f commit 20c782f

10 files changed

Lines changed: 285 additions & 19 deletions

File tree

etc/cmake/options.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,7 @@ endif()
307307
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
308308
set(OT_CRYPTO_LIB_VALUES "MBEDTLS" "PSA" "PLATFORM")
309309
ot_multi_option(OT_CRYPTO_LIB OT_CRYPTO_LIB_VALUES OPENTHREAD_CONFIG_CRYPTO_LIB OPENTHREAD_CONFIG_CRYPTO_LIB_ "set Crypto backend library")
310+
ot_option(OT_CRYPTO_CCM_ONE_SHOT OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_ONE_SHOT_ENABLE "platform one-shot AES-CCM* hook")
310311

311312
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
312313
set(OT_THREAD_VERSION_VALUES "1.1" "1.2" "1.3" "1.3.1" "1.4")

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 (608)
55+
#define OPENTHREAD_API_VERSION (609)
5656

5757
/**
5858
* @addtogroup api-instance

include/openthread/platform/crypto.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,51 @@ 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)
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+
*
780+
* Default weak mbedTLS and PSA implementations are provided.
781+
*
782+
* @param[in] aEncrypt True to encrypt and generate tag; false to decrypt and verify tag.
783+
* @param[in] aConfig CCM* parameters (key, nonce, lengths).
784+
* @param[in] aHeader Additional authenticated data (not encrypted). May be NULL if header length is 0.
785+
* @param[in,out] aData Payload buffer (plaintext on encrypt entry, ciphertext on decrypt entry).
786+
* The buffer must hold @p aConfig->mPlainTextLength + @p aConfig->mTagLength bytes.
787+
*
788+
* @retval OT_ERROR_NONE Success.
789+
* @retval OT_ERROR_SECURITY Tag mismatch (decrypt only).
790+
* @retval OT_ERROR_FAILED Operation failed.
791+
*/
792+
otError otPlatCryptoAesCcmProcessOneShot(bool aEncrypt,
793+
const otPlatCryptoAesCcmConfig *aConfig,
794+
const uint8_t *aHeader,
795+
uint8_t *aData);
796+
752797
/**
753798
* @}
754799
*/

script/check-simulation-build-cmake

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,10 @@ build_all_features()
196196

197197
reset_source
198198
"$(dirname "$0")"/cmake-build simulation -DOT_BLE_TCAT=ON
199+
200+
# Build with platform CCM one-shot enabled
201+
reset_source
202+
"$(dirname "$0")"/cmake-build simulation -DOT_CRYPTO_CCM_ONE_SHOT=ON
199203
}
200204

201205
build_nest_common()

src/core/config/crypto.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,19 @@
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+
#ifndef OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_ONE_SHOT_ENABLE
81+
#define OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_ONE_SHOT_ENABLE 0
82+
#endif
83+
7184
#if OPENTHREAD_CONFIG_CRYPTO_LIB == OPENTHREAD_CONFIG_CRYPTO_LIB_PLATFORM
7285

7386
/**

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: 54 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,59 @@ 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->mNonce != nullptr && aData != nullptr, error = kErrorInvalidArgs);
170+
171+
{
172+
const LiteralKey key(*static_cast<const Key *>(&aConfig->mKey));
173+
174+
ret = mbedtls_ccm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, key.GetBytes(), key.GetLength() * kBitsPerByte);
175+
VerifyOrExit(ret == 0, error = kErrorFailed);
176+
177+
if (aEncrypt)
178+
{
179+
ret = mbedtls_ccm_encrypt_and_tag(&ctx, aConfig->mPlainTextLength, aConfig->mNonce, aConfig->mNonceLength,
180+
aHeader, aConfig->mHeaderLength, aData, aData,
181+
aData + aConfig->mPlainTextLength, aConfig->mTagLength);
182+
VerifyOrExit(ret == 0, error = kErrorFailed);
183+
}
184+
else
185+
{
186+
// MBEDTLS_ERR_CCM_AUTH_FAILED is the expected return on tag mismatch; map to kErrorSecurity.
187+
ret = mbedtls_ccm_auth_decrypt(&ctx, aConfig->mPlainTextLength, aConfig->mNonce, aConfig->mNonceLength,
188+
aHeader, aConfig->mHeaderLength, aData, aData,
189+
aData + aConfig->mPlainTextLength, aConfig->mTagLength);
190+
191+
if (ret == MBEDTLS_ERR_CCM_AUTH_FAILED)
192+
{
193+
error = kErrorSecurity;
194+
}
195+
else
196+
{
197+
VerifyOrExit(ret == 0, error = kErrorFailed);
198+
}
199+
}
200+
}
201+
202+
exit:
203+
mbedtls_ccm_free(&ctx);
204+
return error;
205+
}
206+
207+
#endif // OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_ONE_SHOT_ENABLE
208+
155209
#if OPENTHREAD_FTD || OPENTHREAD_MTD
156210

157211
// HMAC implementations

src/core/crypto/crypto_platform_psa.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,49 @@ 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+
VerifyOrExit(aConfig->mKey.mKey == nullptr, error = kErrorInvalidArgs);
406+
407+
algorithm = PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, aConfig->mTagLength);
408+
409+
if (aEncrypt)
410+
{
411+
// Output layout: ciphertext || tag, written in-place over the plaintext buffer.
412+
status = psa_aead_encrypt(aConfig->mKey.mKeyRef, algorithm, aConfig->mNonce, aConfig->mNonceLength, aHeader,
413+
aConfig->mHeaderLength, aData, aConfig->mPlainTextLength, aData,
414+
aConfig->mPlainTextLength + aConfig->mTagLength, &outputLen);
415+
SuccessOrExit(error = PsaToOtError(status));
416+
VerifyOrExit(outputLen == aConfig->mPlainTextLength + aConfig->mTagLength, error = kErrorFailed);
417+
}
418+
else
419+
{
420+
// Input layout: ciphertext || tag contiguous at aData. Output plaintext written in-place.
421+
status = psa_aead_decrypt(aConfig->mKey.mKeyRef, algorithm, aConfig->mNonce, aConfig->mNonceLength, aHeader,
422+
aConfig->mHeaderLength, aData, aConfig->mPlainTextLength + aConfig->mTagLength, aData,
423+
aConfig->mPlainTextLength, &outputLen);
424+
error = (status == PSA_ERROR_INVALID_SIGNATURE) ? kErrorSecurity : PsaToOtError(status);
425+
SuccessOrExit(error);
426+
VerifyOrExit(outputLen == aConfig->mPlainTextLength, error = kErrorFailed);
427+
}
428+
429+
exit:
430+
return error;
431+
}
432+
433+
#endif // OPENTHREAD_CONFIG_CRYPTO_PLATFORM_CCM_ONE_SHOT_ENABLE
434+
392435
#if OPENTHREAD_FTD || OPENTHREAD_MTD
393436

394437
OT_TOOL_WEAK otError otPlatCryptoHmacSha256Init(otCryptoContext *aContext)

0 commit comments

Comments
 (0)