[crypto] simplify AesCcm public APIs and introduce internal Engine (#13215)

This commit refactors the `AesCcm` class to simplify its public interface,
decoupling the high-level API from the underlying cryptographic execution.

Key improvements:
- Redesigned the API from a series of procedural method calls
  (`Init()`, `Header()`, `Payload()`, `Finalize()`) into a unified,
  stateful model. Callers now pre-configure the operation parameters using
  dedicated setters (`SetKey()`, `SetNonce()`, `SetAuthData()`,
  `SetTagLength()`) and execute the entire cryptographic operation in a
  single step via unified `Process()` methods.
- Introduced a nested `Engine` class to encapsulate the low-level AES-CCM
  mathematical and cryptographic state. The `Engine` provides clean internal
  interfaces for both optimized one-shot (single-part) and multi-part
  operations.
- This architectural separation allows the outer `AesCcm` class to focus on
  parameter validation, high-level buffer management, and complex `Message`
  chunk iterations, while the `Engine` remains focused purely on the
  cryptographic core. This also provides a clean extension point to easily
  route one-shot operations to platform-specific hardware acceleration APIs
  in the future.
- Updated `Mac` and `Mle` modules to use the simplified APIs, reducing
   boilerplate code.
- Retained a static `Perform()` wrapper to support the legacy public
  `otCrypto` API.
- Updated unit tests to validate the new stateful interfaces, including
  robust in-place message chunk processing and separate-buffer
  validations.
This commit is contained in:
Abtin Keshavarzian
2026-06-10 10:24:34 -07:00
committed by GitHub
parent a055c4b9b8
commit 0fbee98fd5
7 changed files with 573 additions and 285 deletions
+2 -13
View File
@@ -64,22 +64,11 @@ void otCryptoAesCcm(const otCryptoKey *aKey,
bool aEncrypt,
void *aTag)
{
AesCcm aesCcm;
AssertPointerIsNotNull(aNonce);
AssertPointerIsNotNull(aPlainText);
AssertPointerIsNotNull(aCipherText);
AssertPointerIsNotNull(aTag);
aesCcm.SetKey(AsCoreType(aKey));
aesCcm.Init(aHeaderLength, aLength, aTagLength, aNonce, aNonceLength);
if (aHeaderLength != 0)
{
OT_ASSERT(aHeader != nullptr);
aesCcm.Header(aHeader, aHeaderLength);
}
aesCcm.Payload(aPlainText, aCipherText, aLength, aEncrypt ? AesCcm::kEncrypt : AesCcm::kDecrypt);
aesCcm.Finalize(aTag);
AesCcm::Perform(aEncrypt ? AesCcm::kEncrypt : AesCcm::kDecrypt, AsCoreType(aKey), aTagLength, aNonce, aNonceLength,
aHeader, aHeaderLength, aPlainText, aCipherText, aLength, aTag);
}
+234 -72
View File
@@ -36,6 +36,7 @@
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/encoding.hpp"
#include "common/num_utils.hpp"
namespace ot {
namespace Crypto {
@@ -43,42 +44,221 @@ namespace Crypto {
//---------------------------------------------------------------------------------------------------------------------
// AesCcm
static_assert(sizeof(AesCcm::Nonce) == 13, "Nonce format is not valid");
void AesCcm::SetKey(const uint8_t *aKey, uint16_t aKeyLength)
AesCcm::AesCcm(void)
{
Key cryptoKey;
cryptoKey.Set(aKey, aKeyLength);
SetKey(cryptoKey);
mConfig.Clear();
mAuthData = nullptr;
}
void AesCcm::SetKey(const Mac::KeyMaterial &aMacKey)
void AesCcm::SetNonce(const void *aNonce, uint8_t aLength)
{
Key cryptoKey;
aMacKey.ConvertToCryptoKey(cryptoKey);
SetKey(cryptoKey);
mConfig.mNonce = reinterpret_cast<const uint8_t *>(aNonce);
mConfig.mNonceLength = aLength;
}
void AesCcm::Init(uint32_t aHeaderLength,
uint32_t aPlainTextLength,
uint8_t aTagLength,
const void *aNonce,
uint8_t aNonceLength)
void AesCcm::SetTagLength(uint8_t aTagLength)
{
const uint8_t *nonceBytes = reinterpret_cast<const uint8_t *>(aNonce);
uint8_t blockLength = 0;
uint32_t len;
uint8_t L;
uint8_t i;
OT_ASSERT(((aTagLength & 0x1) == 0) && IsValueInRange(aTagLength, kMinTagLength, kMaxTagLength));
// Tag length must be even and within [kMinTagLength, kMaxTagLength]
OT_ASSERT(((aTagLength & 0x1) == 0) && (kMinTagLength <= aTagLength) && (aTagLength <= kMaxTagLength));
mConfig.mTagLength = aTagLength;
}
void AesCcm::SetAuthData(const void *aAuthData, uint32_t aLength)
{
mAuthData = reinterpret_cast<const uint8_t *>(aAuthData);
mConfig.mHeaderLength = aLength;
}
Error AesCcm::Process(Operation aOperation, uint8_t *aData, uint32_t aLength)
{
Engine engine;
mConfig.mPlainTextLength = aLength;
return engine.ProcessOneShot(aOperation, mConfig, mAuthData, aData);
}
#if OPENTHREAD_FTD || OPENTHREAD_MTD
Error AesCcm::Process(Operation aOperation, Message &aMessage, uint16_t aOffset)
{
Error error = kErrorNone;
Engine engine;
Message::MutableChunk chunk;
uint16_t remainingLength;
uint8_t tag[kMaxTagLength];
VerifyOrExit(aOffset <= aMessage.GetLength(), error = kErrorInvalidArgs);
switch (aOperation)
{
case kEncrypt:
SuccessOrExit(error = aMessage.IncreaseLength(mConfig.mTagLength));
break;
case kDecrypt:
VerifyOrExit(aMessage.GetLength() - aOffset >= mConfig.mTagLength, error = kErrorSecurity);
break;
}
mConfig.mPlainTextLength = aMessage.GetLength() - aOffset - mConfig.mTagLength;
// First, check if the entire payload and tag are present in
// a single chunk (i.e., in one contiguous buffer).
remainingLength = aMessage.GetLength() - aOffset;
aMessage.GetFirstChunk(aOffset, remainingLength, chunk);
if (chunk.GetLength() == mConfig.mPlainTextLength + mConfig.mTagLength)
{
error = engine.ProcessOneShot(aOperation, mConfig, mAuthData, chunk.GetBytes());
ExitNow();
}
// The payload content spans multiple chunks. We need to process
// it iteratively using multi-part `engine` methods.
remainingLength = aMessage.GetLength() - aOffset - mConfig.mTagLength;
engine.Start(mConfig);
engine.AddHeader(mAuthData, mConfig.mHeaderLength);
aMessage.GetFirstChunk(aOffset, remainingLength, chunk);
while (chunk.GetLength() > 0)
{
engine.AddPayload(chunk.GetBytes(), chunk.GetBytes(), chunk.GetLength(), aOperation);
aMessage.GetNextChunk(remainingLength, chunk);
}
engine.Finalize(tag);
switch (aOperation)
{
case kEncrypt:
aMessage.WriteBytes(aMessage.GetLength() - mConfig.mTagLength, tag, mConfig.mTagLength);
break;
case kDecrypt:
VerifyOrExit(aMessage.CompareBytes(aMessage.GetLength() - mConfig.mTagLength, tag, mConfig.mTagLength),
error = kErrorSecurity);
break;
}
exit:
if ((aOperation == kDecrypt) && (error == kErrorNone))
{
aMessage.RemoveFooter(mConfig.mTagLength);
}
return error;
}
#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
void AesCcm::Perform(Operation aOperation,
const Key &aKey,
uint8_t aTagLength,
const void *aNonce,
uint8_t aNonceLength,
const void *aAuthData,
uint32_t aAuthDataLength,
void *aPlainText,
void *aCipherText,
uint32_t aLength,
void *aTag)
{
Config config;
Engine engine;
config.mKey = aKey;
config.mTagLength = aTagLength;
config.mNonce = reinterpret_cast<const uint8_t *>(aNonce);
config.mNonceLength = aNonceLength;
config.mHeaderLength = aAuthDataLength;
config.mPlainTextLength = aLength;
engine.Start(config);
engine.AddHeader(reinterpret_cast<const uint8_t *>(aAuthData), aAuthDataLength);
engine.AddPayload(aPlainText, aCipherText, aLength, aOperation);
engine.Finalize(aTag);
}
//---------------------------------------------------------------------------------------------------------------------
// AesCcm::Config
bool AesCcm::Config::IsValid(void) const
{
bool isValid = false;
if (mNonceLength > 0)
{
VerifyOrExit(mNonce != nullptr);
}
VerifyOrExit((mTagLength % 2) == 0);
VerifyOrExit(IsValueInRange(mTagLength, kMinTagLength, kMaxTagLength));
isValid = true;
exit:
return isValid;
}
//---------------------------------------------------------------------------------------------------------------------
// AesCcm::Engine
Error AesCcm::Engine::ProcessOneShot(Operation aOperation,
const Config &aConfig,
const uint8_t *aHeader,
uint8_t *aData)
{
// This method performs one-shot (single-part) AES-CCM processing.
// Currently, it is implemented by calling the multi-part
// streaming APIs sequentially. In the future, this can be
// optimized to directly call platform-specific one-shot hardware
// acceleration APIs if supported by the platform.
Error error = kErrorNone;
uint8_t tag[kMaxTagLength];
Start(aConfig);
AddHeader(aHeader, aConfig.mHeaderLength);
AddPayload(aData, aData, aConfig.mPlainTextLength, aOperation);
Finalize(tag);
switch (aOperation)
{
case kEncrypt:
memcpy(aData + aConfig.mPlainTextLength, tag, aConfig.mTagLength);
break;
case kDecrypt:
error = (memcmp(aData + aConfig.mPlainTextLength, tag, aConfig.mTagLength) == 0) ? kErrorNone : kErrorSecurity;
break;
}
return error;
}
void AesCcm::Engine::Start(const Config &aConfig)
{
uint8_t blockLength = 0;
uint32_t len;
uint8_t L;
uint8_t i;
OT_ASSERT(aConfig.IsValid());
mEcb.SetKey(aConfig.mKey);
mNonceLength = aConfig.mNonceLength;
mTagLength = aConfig.mTagLength;
mHeaderLength = aConfig.mHeaderLength;
mPlainTextLength = aConfig.mPlainTextLength;
L = 0;
for (len = aPlainTextLength; len; len >>= 8)
for (len = mPlainTextLength; len; len >>= 8)
{
L++;
}
@@ -88,36 +268,36 @@ void AesCcm::Init(uint32_t aHeaderLength,
L = 2;
}
if (aNonceLength > 13)
if (mNonceLength > 13)
{
aNonceLength = 13;
mNonceLength = 13;
}
// increase L to match nonce len
if (L < (15 - aNonceLength))
if (L < (15 - mNonceLength))
{
L = 15 - aNonceLength;
L = 15 - mNonceLength;
}
// decrease nonceLength to match L
if (aNonceLength > (15 - L))
if (mNonceLength > (15 - L))
{
aNonceLength = 15 - L;
mNonceLength = 15 - L;
}
// setup initial block
// write flags
mBlock[0] = (static_cast<uint8_t>((aHeaderLength != 0) << 6) | static_cast<uint8_t>(((aTagLength - 2) >> 1) << 3) |
mBlock[0] = (static_cast<uint8_t>((mHeaderLength != 0) << 6) | static_cast<uint8_t>(((mTagLength - 2) >> 1) << 3) |
static_cast<uint8_t>(L - 1));
// write nonce
memcpy(&mBlock[1], nonceBytes, aNonceLength);
memcpy(&mBlock[1], aConfig.mNonce, mNonceLength);
// write len
len = aPlainTextLength;
len = mPlainTextLength;
for (i = sizeof(mBlock) - 1; i > aNonceLength; i--)
for (i = sizeof(mBlock) - 1; i > mNonceLength; i--)
{
mBlock[i] = len & 0xff;
len >>= 8;
@@ -127,44 +307,41 @@ void AesCcm::Init(uint32_t aHeaderLength,
mEcb.Encrypt(mBlock, mBlock);
// process header
if (aHeaderLength > 0)
if (mHeaderLength > 0)
{
// process length
if (aHeaderLength < (65536U - 256U))
if (mHeaderLength < (65536U - 256U))
{
mBlock[blockLength++] ^= aHeaderLength >> 8;
mBlock[blockLength++] ^= aHeaderLength >> 0;
mBlock[blockLength++] ^= mHeaderLength >> 8;
mBlock[blockLength++] ^= mHeaderLength >> 0;
}
else
{
mBlock[blockLength++] ^= 0xff;
mBlock[blockLength++] ^= 0xfe;
mBlock[blockLength++] ^= aHeaderLength >> 24;
mBlock[blockLength++] ^= aHeaderLength >> 16;
mBlock[blockLength++] ^= aHeaderLength >> 8;
mBlock[blockLength++] ^= aHeaderLength >> 0;
mBlock[blockLength++] ^= mHeaderLength >> 24;
mBlock[blockLength++] ^= mHeaderLength >> 16;
mBlock[blockLength++] ^= mHeaderLength >> 8;
mBlock[blockLength++] ^= mHeaderLength >> 0;
}
}
// init counter
mCtr[0] = L - 1;
memcpy(&mCtr[1], nonceBytes, aNonceLength);
memset(&mCtr[aNonceLength + 1], 0, sizeof(mCtr) - aNonceLength - 1);
memcpy(&mCtr[1], aConfig.mNonce, mNonceLength);
memset(&mCtr[mNonceLength + 1], 0, sizeof(mCtr) - mNonceLength - 1);
mNonceLength = aNonceLength;
mHeaderLength = aHeaderLength;
mHeaderCur = 0;
mPlainTextLength = aPlainTextLength;
mPlainTextCur = 0;
mBlockLength = blockLength;
mCtrLength = sizeof(mCtrPad);
mTagLength = aTagLength;
mHeaderCur = 0;
mPlainTextCur = 0;
mBlockLength = blockLength;
mCtrLength = sizeof(mCtrPad);
}
void AesCcm::Header(const void *aHeader, uint32_t aHeaderLength)
void AesCcm::Engine::AddHeader(const void *aHeader, uint32_t aHeaderLength)
{
const uint8_t *headerBytes = reinterpret_cast<const uint8_t *>(aHeader);
OT_ASSERT((aHeaderLength == 0) || aHeader != nullptr);
OT_ASSERT(mHeaderCur + aHeaderLength <= mHeaderLength);
// process header
@@ -193,7 +370,7 @@ void AesCcm::Header(const void *aHeader, uint32_t aHeaderLength)
}
}
void AesCcm::Payload(void *aPlainText, void *aCipherText, uint32_t aLength, Mode aMode)
void AesCcm::Engine::AddPayload(void *aPlainText, void *aCipherText, uint32_t aLength, Operation aOperation)
{
uint8_t *plaintextBytes = reinterpret_cast<uint8_t *>(aPlainText);
uint8_t *ciphertextBytes = reinterpret_cast<uint8_t *>(aCipherText);
@@ -217,7 +394,7 @@ void AesCcm::Payload(void *aPlainText, void *aCipherText, uint32_t aLength, Mode
mCtrLength = 0;
}
if (aMode == kEncrypt)
if (aOperation == kEncrypt)
{
byte = plaintextBytes[i];
@@ -259,22 +436,7 @@ void AesCcm::Payload(void *aPlainText, void *aCipherText, uint32_t aLength, Mode
}
}
#if OPENTHREAD_FTD || OPENTHREAD_MTD
void AesCcm::Payload(Message &aMessage, uint16_t aOffset, uint16_t aLength, Mode aMode)
{
Message::MutableChunk chunk;
aMessage.GetFirstChunk(aOffset, aLength, chunk);
while (chunk.GetLength() > 0)
{
Payload(chunk.GetBytes(), chunk.GetBytes(), chunk.GetLength(), aMode);
aMessage.GetNextChunk(aLength, chunk);
}
}
#endif
void AesCcm::Finalize(void *aTag)
void AesCcm::Engine::Finalize(void *aTag)
{
uint8_t *tagBytes = reinterpret_cast<uint8_t *>(aTag);
+148 -59
View File
@@ -65,12 +65,17 @@ public:
static constexpr uint8_t kMaxTagLength = AesEcb::kBlockSize; ///< Maximum tag length (in bytes).
/**
* Type represent the encryption vs decryption mode.
* Initializes the AES-CCM object.
*/
enum Mode : uint8_t
AesCcm(void);
/**
* Represents the operation to perform (encryption or decryption)
*/
enum Operation : uint8_t
{
kEncrypt, // Encryption mode.
kDecrypt, // Decryption mode.
kEncrypt, ///< Encrypt.
kDecrypt, ///< Decrypt.
};
/**
@@ -100,102 +105,186 @@ public:
/**
* Sets the key.
*
* @param[in] aKey Crypto Key used in AES operation
* The passed-in @p aKey and its underlying buffer must remain valid during the lifecycle of the `AesCcm` object.
*
* @param[in] aKey Crypto Key used in AES operation.
*/
void SetKey(const Key &aKey) { mEcb.SetKey(aKey); }
void SetKey(const Key &aKey) { mConfig.mKey = aKey; }
/**
* Sets the key.
*
* The passed-in @p aKey buffer must remain valid during the lifecycle of the `AesCcm` object.
*
* @param[in] aKey A pointer to the key.
* @param[in] aKeyLength Length of the key in bytes.
*/
void SetKey(const uint8_t *aKey, uint16_t aKeyLength);
void SetKey(const uint8_t *aKey, uint16_t aKeyLength) { mConfig.mKey.Set(aKey, aKeyLength); }
/**
* Sets the key.
*
* The passed-in @p aMacKey and its underlying buffer must remain valid during the lifecycle of the `AesCcm` object.
*
* @param[in] aMacKey Key Material for AES operation.
*/
void SetKey(const Mac::KeyMaterial &aMacKey);
void SetKey(const Mac::KeyMaterial &aMacKey) { aMacKey.ConvertToCryptoKey(mConfig.mKey); }
/**
* Initializes the AES CCM computation.
* Sets the Nonce.
*
* @param[in] aHeaderLength Length of header in bytes.
* @param[in] aPlainTextLength Length of plaintext in bytes.
* @param[in] aTagLength Length of tag in bytes (must be even and in `[kMinTagLength, kMaxTagLength]`).
* @param[in] aNonce A pointer to the nonce.
* @param[in] aNonceLength Length of nonce in bytes.
* The passed-in @p aNonce must remain valid during the lifecycle of the `AesCcm` object.
*
* @param[in] aNonce A reference to the Nonce object.
*/
void Init(uint32_t aHeaderLength,
uint32_t aPlainTextLength,
uint8_t aTagLength,
const void *aNonce,
uint8_t aNonceLength);
void SetNonce(const Nonce &aNonce) { SetNonce(&aNonce, sizeof(Nonce)); }
/**
* Processes the header.
* Sets the Nonce.
*
* @param[in] aHeader A pointer to the header.
* @param[in] aHeaderLength Length of header in bytes.
* The passed-in @p aNonce buffer must remain valid during the lifecycle of the `AesCcm` object.
*
* @param[in] aNonce A pointer to the buffer containing the nonce.
* @param[in] aLength The length of the nonce in bytes.
*/
void Header(const void *aHeader, uint32_t aHeaderLength);
void SetNonce(const void *aNonce, uint8_t aLength);
/**
* Processes the payload.
* Sets the Additional Authenticated Data.
*
* When decrypting (`kDecrypt`), @p aPlainText can be `nullptr` if the decrypted plaintext is not needed.
* Similarly, when encrypting (`kEncrypt`), @p aCipherText can be `nullptr` if the ciphertext is not needed.
* The passed-in @p aAuthData buffer must remain valid during the lifecycle of the `AesCcm` object.
*
* @param[in,out] aPlainText A pointer to the plaintext.
* @param[in,out] aCipherText A pointer to the ciphertext.
* @param[in] aLength Payload length in bytes.
* @param[in] aMode Mode to indicate whether to encrypt (`kEncrypt`) or decrypt (`kDecrypt`).
* @param[in] aAuthData A pointer to the buffer containing the data.
* @param[in] aLength The length of data in bytes.
*/
void Payload(void *aPlainText, void *aCipherText, uint32_t aLength, Mode aMode);
void SetAuthData(const void *aAuthData, uint32_t aLength);
/**
* Sets the AES-CCM tag (MIC) length.
*
* @param[in] aTagLength The tag length in bytes (must be even and in [kMinTagLength, kMaxTagLength]).
*/
void SetTagLength(uint8_t aTagLength);
/**
* Performs in-place AES-CCM computation (encryption or decryption) on a contiguous buffer.
*
* Before calling this method, the `AesCcm` object must be fully configured by calling `SetKey()`, `SetNonce()`,
* `SetAuthData()`, and `SetTagLength()`. Otherwise, the behavior of this method is undefined.
*
* The buffer @p aData must have sufficient space. For encryption, it must be large enough to hold the plaintext
* plus the tag. The tag will be appended immediately after the payload bytes. For decryption, the tag must be
* present immediately after the ciphertext bytes.
*
* @param[in] aOperation The operation (kEncrypt or kDecrypt).
* @param[in,out] aData A pointer to the data buffer.
* @param[in] aLength The length of the payload in bytes (excluding the tag).
*
* @retval kErrorNone Operation succeeded (for decryption, this means the tag matched).
* @retval kErrorSecurity Decryption failed because the tag did not match.
*/
Error Process(Operation aOperation, uint8_t *aData, uint32_t aLength);
#if OPENTHREAD_FTD || OPENTHREAD_MTD
/**
* Processes the payload within a given message.
* Performs in-place AES-CCM computation (encryption or decryption) on a `Message`
*
* Encrypts/decrypts the payload content in place within the @p aMessage.
* Before calling this method, the `AesCcm` object must be fully configured by calling `SetKey()`, `SetNonce()`,
* `SetAuthData()`, and `SetTagLength()`. Otherwise, the behavior of this method is undefined.
*
* @param[in,out] aMessage The message to read from and update.
* @param[in] aOffset The offset in @p aMessage to start of payload.
* @param[in] aLength Payload length in bytes.
* @param[in] aMode Mode to indicate whether to encrypt (`kEncrypt`) or decrypt (`kDecrypt`).
* The operation starts at @p aOffset in the message.
*
* For encryption, the payload is from @p aOffset to the end of the message. The calculated tag is appended to the
* end of the message.
*
* For decryption, the tag is assumed to be the last bytes of the message. The payload is from @p aOffset up to the
* tag (tag length bytes before the end). If decryption succeeds (tag matches), the tag is removed from the
* message.
*
* @param[in] aOperation The operation (kEncrypt or kDecrypt).
* @param[in,out] aMessage The Message.
* @param[in] aOffset The offset in the message where the payload starts.
*
* @retval kErrorNone Operation succeeded.
* @retval kErrorSecurity Decryption failed because the tag did not match.
* @retval kErrorNoBufs Failed to grow the message to append the tag (during encryption).
* @retval kErrorInvalidArgs The @p aOffset is invalid (larger than message length).
*/
void Payload(Message &aMessage, uint16_t aOffset, uint16_t aLength, Mode aMode);
Error Process(Operation aOperation, Message &aMessage, uint16_t aOffset);
#endif
/**
* Returns the tag length in bytes.
* Performs a complete AES-CCM computation (encryption or decryption).
*
* @returns The tag length in bytes.
*/
uint8_t GetTagLength(void) const { return mTagLength; }
/**
* Generates the tag.
* @deprecated This method is provided to support the public `otCrypto` API and should not be used
* within the OpenThread core. Core modules should instantiate an `AesCcm` object and use
* the `Process()` methods instead.
*
* @param[out] aTag A pointer to the tag (must have `GetTagLength()` bytes).
* @param[in] aOperation The operation (kEncrypt or kDecrypt).
* @param[in] aKey The crypto key to use.
* @param[in] aTagLength The tag length in bytes.
* @param[in] aNonce A pointer to the nonce.
* @param[in] aNonceLength The length of the nonce in bytes.
* @param[in] aAuthData A pointer to the additional authentication data.
* @param[in] aAuthDataLength The length of the authentication data in bytes.
* @param[in,out] aPlainText A pointer to the plaintext buffer.
* @param[in,out] aCipherText A pointer to the ciphertext buffer.
* @param[in] aLength The length of the payload (plaintext/ciphertext) in bytes.
* @param[out] aTag A pointer to a buffer to output the calculated tag.
*/
void Finalize(void *aTag);
static void Perform(Operation aOperation,
const Key &aKey,
uint8_t aTagLength,
const void *aNonce,
uint8_t aNonceLength,
const void *aAuthData,
uint32_t aAuthDataLength,
void *aPlainText,
void *aCipherText,
uint32_t aLength,
void *aTag);
private:
AesEcb mEcb;
uint8_t mBlock[AesEcb::kBlockSize];
uint8_t mCtr[AesEcb::kBlockSize];
uint8_t mCtrPad[AesEcb::kBlockSize];
uint32_t mHeaderLength;
uint32_t mHeaderCur;
uint32_t mPlainTextLength;
uint32_t mPlainTextCur;
uint16_t mBlockLength;
uint16_t mCtrLength;
uint8_t mNonceLength;
uint8_t mTagLength;
struct Config : public Clearable<Config>
{
bool IsValid(void) const;
Key mKey;
uint8_t mNonceLength;
uint8_t mTagLength;
uint32_t mHeaderLength;
uint32_t mPlainTextLength;
const uint8_t *mNonce;
};
class Engine
{
public:
Error ProcessOneShot(Operation aOperation, const Config &aConfig, const uint8_t *aHeader, uint8_t *aData);
// Multi-part
void Start(const Config &aConfig);
void AddHeader(const void *aHeader, uint32_t aHeaderLength);
void AddPayload(void *aPlainText, void *aCipherText, uint32_t aLength, Operation aOperation);
void Finalize(void *aTag);
private:
AesEcb mEcb;
uint8_t mBlock[AesEcb::kBlockSize];
uint8_t mCtr[AesEcb::kBlockSize];
uint8_t mCtrPad[AesEcb::kBlockSize];
uint32_t mHeaderLength;
uint32_t mHeaderCur;
uint32_t mPlainTextLength;
uint32_t mPlainTextCur;
uint16_t mBlockLength;
uint16_t mCtrLength;
uint8_t mNonceLength;
uint8_t mTagLength;
};
Config mConfig;
const uint8_t *mAuthData;
};
/**
+16 -28
View File
@@ -1390,7 +1390,6 @@ void TxFrame::ProcessTransmitAesCcm(const ExtAddress &aExtAddress)
#if OPENTHREAD_FTD || OPENTHREAD_MTD || OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE
uint32_t frameCounter = 0;
uint8_t securityLevel;
uint8_t tagLength;
Crypto::AesCcm aesCcm;
Crypto::AesCcm::Nonce nonce;
@@ -1402,12 +1401,11 @@ void TxFrame::ProcessTransmitAesCcm(const ExtAddress &aExtAddress)
nonce.InitFrom(aExtAddress, frameCounter, securityLevel);
aesCcm.SetKey(GetAesKey());
tagLength = GetFooterLength() - GetFcsSize();
aesCcm.SetNonce(nonce);
aesCcm.SetAuthData(GetHeader(), GetHeaderLength());
aesCcm.SetTagLength(GetFooterLength() - GetFcsSize());
aesCcm.Init(GetHeaderLength(), GetPayloadLength(), tagLength, &nonce, sizeof(nonce));
aesCcm.Header(GetHeader(), GetHeaderLength());
aesCcm.Payload(GetPayload(), GetPayload(), GetPayloadLength(), Crypto::AesCcm::kEncrypt);
aesCcm.Finalize(GetFooter());
SuccessOrExit(aesCcm.Process(Crypto::AesCcm::kEncrypt, GetPayload(), GetPayloadLength()));
SetIsSecurityProcessed(true);
@@ -1423,7 +1421,6 @@ void TxFrame::DecryptTransmitAesCcm(const ExtAddress &aExtAddress)
{
uint32_t frameCounter = 0;
uint8_t securityLevel;
uint8_t tagLength;
Crypto::AesCcm aesCcm;
Crypto::AesCcm::Nonce nonce;
@@ -1435,13 +1432,13 @@ void TxFrame::DecryptTransmitAesCcm(const ExtAddress &aExtAddress)
nonce.InitFrom(aExtAddress, frameCounter, securityLevel);
aesCcm.SetKey(GetAesKey());
tagLength = GetFooterLength() - GetFcsSize();
aesCcm.SetNonce(nonce);
aesCcm.SetAuthData(GetHeader(), GetHeaderLength());
aesCcm.SetTagLength(GetFooterLength() - GetFcsSize());
aesCcm.Init(GetHeaderLength(), GetPayloadLength(), tagLength, &nonce, sizeof(nonce));
aesCcm.Header(GetHeader(), GetHeaderLength());
aesCcm.Payload(GetPayload(), GetPayload(), GetPayloadLength(), Crypto::AesCcm::kDecrypt);
// Note: We skip aesCcm.Finalize() checking because we are only decrypting back to plaintext,
// We expect success because we are only decrypting back to plaintext,
// and we know the ciphertext was generated correctly by us previously.
IgnoreError(aesCcm.Process(Crypto::AesCcm::kDecrypt, GetPayload(), GetPayloadLength()));
SetIsSecurityProcessed(false);
SetIsHeaderUpdated(false);
@@ -1640,8 +1637,6 @@ Error RxFrame::ProcessReceiveAesCcm(const ExtAddress &aExtAddress, const KeyMate
Error error = kErrorSecurity;
uint32_t frameCounter = 0;
uint8_t securityLevel;
uint8_t tag[kMaxMicSize];
uint8_t tagLength;
Crypto::AesCcm aesCcm;
Crypto::AesCcm::Nonce nonce;
@@ -1653,23 +1648,16 @@ Error RxFrame::ProcessReceiveAesCcm(const ExtAddress &aExtAddress, const KeyMate
nonce.InitFrom(aExtAddress, frameCounter, securityLevel);
aesCcm.SetKey(aMacKey);
tagLength = GetFooterLength() - GetFcsSize();
aesCcm.SetNonce(nonce);
aesCcm.SetAuthData(GetHeader(), GetHeaderLength());
aesCcm.SetTagLength(GetFooterLength() - GetFcsSize());
aesCcm.Init(GetHeaderLength(), GetPayloadLength(), tagLength, &nonce, sizeof(nonce));
aesCcm.Header(GetHeader(), GetHeaderLength());
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
aesCcm.Payload(GetPayload(), GetPayload(), GetPayloadLength(), Crypto::AesCcm::kDecrypt);
#else
// For fuzz tests, execute AES but do not alter the payload. A large
aesCcm.Payload(nullptr, GetPayload(), GetPayloadLength(), Crypto::AesCcm::kDecrypt);
#endif
aesCcm.Finalize(tag);
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
VerifyOrExit(memcmp(tag, GetFooter(), tagLength) == 0);
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
// Do not decrypt when fuzzing
ExitNow(error = kErrorNone);
#endif
error = kErrorNone;
error = aesCcm.Process(Crypto::AesCcm::kDecrypt, GetPayload(), GetPayloadLength());
exit:
return error;
+13 -38
View File
@@ -1499,13 +1499,13 @@ exit:
}
#endif
Error Mle::ProcessMessageSecurity(Crypto::AesCcm::Mode aMode,
Message &aMessage,
uint16_t aCmdOffset,
const AesCcmAuthData &aAuthData)
Error Mle::ProcessMessageSecurity(Crypto::AesCcm::Operation aOperation,
Message &aMessage,
uint16_t aCmdOffset,
const AesCcmAuthData &aAuthData)
{
// This method performs MLE message security. Based on `aMode` it
// can be used to encrypt and append tag to `aMessage` or to
// This method performs MLE message security. Based on `aOperation`
// it can be used to encrypt and append tag to `aMessage` or to
// decrypt and validate the tag in a received `aMessage` (which is
// then removed from `aMessage`).
//
@@ -1524,23 +1524,8 @@ Error Mle::ProcessMessageSecurity(Crypto::AesCcm::Mode aMode,
Error error = kErrorNone;
Crypto::AesCcm aesCcm;
Crypto::AesCcm::Nonce nonce;
uint8_t tag[kMleSecurityTagSize];
Mac::ExtAddress extAddress;
uint32_t keySequence;
uint16_t payloadLength = aMessage.GetLength() - aCmdOffset;
switch (aMode)
{
case Crypto::AesCcm::kEncrypt:
break;
case Crypto::AesCcm::kDecrypt:
// Ensure message contains command field (uint8_t) and
// tag. Then exclude the tag from payload to decrypt.
VerifyOrExit(aCmdOffset + sizeof(uint8_t) + kMleSecurityTagSize <= aMessage.GetLength(), error = kErrorParse);
payloadLength -= kMleSecurityTagSize;
break;
}
extAddress.SetFromIid(aAuthData.mSenderAddr.GetIid());
nonce.InitFrom(extAddress, aAuthData.mSecurityHeader.GetFrameCounter(), Mac::Frame::kSecurityEncMic32);
@@ -1551,12 +1536,12 @@ Error Mle::ProcessMessageSecurity(Crypto::AesCcm::Mode aMode,
? Get<KeyManager>().GetCurrentMleKey()
: Get<KeyManager>().GetTemporaryMleKey(keySequence));
aesCcm.Init(sizeof(AesCcmAuthData), payloadLength, kMleSecurityTagSize, &nonce, sizeof(nonce));
aesCcm.Header(&aAuthData, sizeof(AesCcmAuthData));
aesCcm.SetNonce(nonce);
aesCcm.SetAuthData(&aAuthData, sizeof(AesCcmAuthData));
aesCcm.SetTagLength(kMleSecurityTagSize);
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if (aMode == Crypto::AesCcm::kDecrypt)
if (aOperation == Crypto::AesCcm::kDecrypt)
{
// Skip decrypting the message under fuzz build mode
aMessage.RemoveFooter(kMleSecurityTagSize);
@@ -1564,18 +1549,8 @@ Error Mle::ProcessMessageSecurity(Crypto::AesCcm::Mode aMode,
}
#endif
aesCcm.Payload(aMessage, aCmdOffset, payloadLength, aMode);
aesCcm.Finalize(tag);
if (aMode == Crypto::AesCcm::kEncrypt)
{
SuccessOrExit(error = aMessage.Append(tag));
}
else
{
VerifyOrExit(aMessage.Compare(aMessage.GetLength() - kMleSecurityTagSize, tag), error = kErrorSecurity);
aMessage.RemoveFooter(kMleSecurityTagSize);
}
error = aesCcm.Process(aOperation, aMessage, aCmdOffset);
ExitNow();
exit:
return error;
@@ -1643,7 +1618,7 @@ void Mle::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageIn
SuccessOrExit(error = ProcessMessageSecurity(Crypto::AesCcm::kDecrypt, aMessage, aMessage.GetOffset(), authData));
IgnoreError(aMessage.ReadAtAndAdvanceOffset(command));
SuccessOrExit(error = aMessage.ReadAtAndAdvanceOffset(command));
extAddr.SetFromIid(aMessageInfo.GetPeerAddr().GetIid());
neighbor = (command == kCommandChildIdResponse) ? mNeighborTable.FindParent(extAddr)
+4 -4
View File
@@ -2403,10 +2403,10 @@ private:
bool HasUnregisteredAddress(void) const;
bool ShouldRegisterUnicastAddrWithParent(const Ip6::Netif::UnicastAddress &aUnicastAddress) const;
bool ShouldRegisterMulticastAddrsWithParent(void) const;
Error ProcessMessageSecurity(Crypto::AesCcm::Mode aMode,
Message &aMessage,
uint16_t aCmdOffset,
const AesCcmAuthData &aAuthData);
Error ProcessMessageSecurity(Crypto::AesCcm::Operation aOperation,
Message &aMessage,
uint16_t aCmdOffset,
const AesCcmAuthData &aAuthData);
#if OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH
void InformPreviousParent(void);
+156 -71
View File
@@ -47,7 +47,7 @@ void TestMacBeaconFrame(void)
uint8_t test[] = {0x08, 0xD0, 0x84, 0x21, 0x43, 0x01, 0x00, 0x00, 0x00, 0x00, 0x48, 0xDE,
0xAC, 0x02, 0x05, 0x00, 0x00, 0x00, 0x55, 0xCF, 0x00, 0x00, 0x51, 0x52,
0x53, 0x54, 0x22, 0x3B, 0xC1, 0xEC, 0x84, 0x1A, 0xB5, 0x53};
0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t encrypted[] = {0x08, 0xD0, 0x84, 0x21, 0x43, 0x01, 0x00, 0x00, 0x00, 0x00, 0x48, 0xDE,
0xAC, 0x02, 0x05, 0x00, 0x00, 0x00, 0x55, 0xCF, 0x00, 0x00, 0x51, 0x52,
@@ -57,34 +57,39 @@ void TestMacBeaconFrame(void)
0xAC, 0x02, 0x05, 0x00, 0x00, 0x00, 0x55, 0xCF, 0x00, 0x00, 0x51, 0x52,
0x53, 0x54, 0x22, 0x3B, 0xC1, 0xEC, 0x84, 0x1A, 0xB5, 0x53};
otInstance *instance = testInitInstance();
Crypto::AesCcm aesCcm;
otInstance *instance = testInitInstance();
uint32_t headerLength = sizeof(test) - 8;
uint32_t payloadLength = 0;
uint8_t tagLength = 8;
Crypto::AesCcm aesCcm;
uint8_t nonce[] = {
0xAC, 0xDE, 0x48, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x02,
};
printf("TestMacBeaconFrame\n");
VerifyOrQuit(instance != nullptr);
aesCcm.SetKey(key, sizeof(key));
aesCcm.Init(headerLength, payloadLength, tagLength, nonce, sizeof(nonce));
aesCcm.Header(test, headerLength);
VerifyOrQuit(aesCcm.GetTagLength() == tagLength);
aesCcm.Finalize(test + headerLength);
aesCcm.SetNonce(nonce, sizeof(nonce));
aesCcm.SetAuthData(test, headerLength);
aesCcm.SetTagLength(tagLength);
SuccessOrQuit(aesCcm.Process(Crypto::AesCcm::kEncrypt, &test[headerLength], payloadLength));
DumpBuffer("encrypted", test, sizeof(test));
VerifyOrQuit(memcmp(test, encrypted, sizeof(encrypted)) == 0);
aesCcm.Init(headerLength, payloadLength, tagLength, nonce, sizeof(nonce));
aesCcm.Header(test, headerLength);
VerifyOrQuit(aesCcm.GetTagLength() == tagLength);
aesCcm.Finalize(test + headerLength);
aesCcm.SetKey(key, sizeof(key));
aesCcm.SetNonce(nonce, sizeof(nonce));
aesCcm.SetAuthData(test, headerLength);
aesCcm.SetTagLength(tagLength);
SuccessOrQuit(aesCcm.Process(Crypto::AesCcm::kDecrypt, &test[headerLength], payloadLength));
DumpBuffer("decrypted", test, sizeof(test));
VerifyOrQuit(memcmp(test, decrypted, sizeof(decrypted)) == 0);
testFreeInstance(instance);
printf("\nTestMacBeaconFrame PASSED\n\n");
}
/**
@@ -122,28 +127,34 @@ void TestMacCommandFrame(void)
0xAC, 0xDE, 0x48, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x06,
};
uint8_t tag[kTagLength];
Instance *instance = testInitInstance();
Message *message;
Crypto::AesCcm aesCcm;
uint8_t plaintext[kPayloadLength] = {0xCE};
uint8_t ciphertext[kPayloadLength];
uint8_t tag[kTagLength];
Crypto::Key cryptoKey;
printf("TestMacBeaconCommand\n");
VerifyOrQuit(instance != nullptr);
// Encrypt
aesCcm.SetKey(key, sizeof(key));
aesCcm.Init(kHeaderLength, kPayloadLength, kTagLength, nonce, sizeof(nonce));
aesCcm.Header(test, kHeaderLength);
aesCcm.Payload(test + kHeaderLength, test + kHeaderLength, kPayloadLength, Crypto::AesCcm::kEncrypt);
VerifyOrQuit(aesCcm.GetTagLength() == kTagLength);
aesCcm.Finalize(test + kHeaderLength + kPayloadLength);
aesCcm.SetNonce(nonce, sizeof(nonce));
aesCcm.SetAuthData(test, kHeaderLength);
aesCcm.SetTagLength(kTagLength);
SuccessOrQuit(aesCcm.Process(Crypto::AesCcm::kEncrypt, test + kHeaderLength, kPayloadLength));
DumpBuffer("encrypted", test, sizeof(test));
VerifyOrQuit(memcmp(test, encrypted, sizeof(encrypted)) == 0);
aesCcm.Init(kHeaderLength, kPayloadLength, kTagLength, nonce, sizeof(nonce));
aesCcm.Header(test, kHeaderLength);
aesCcm.Payload(test + kHeaderLength, test + kHeaderLength, kPayloadLength, Crypto::AesCcm::kDecrypt);
VerifyOrQuit(aesCcm.GetTagLength() == kTagLength);
aesCcm.Finalize(test + kHeaderLength + kPayloadLength);
// Decrypt
aesCcm.SetKey(key, sizeof(key));
aesCcm.SetNonce(nonce, sizeof(nonce));
aesCcm.SetAuthData(test, kHeaderLength);
aesCcm.SetTagLength(kTagLength);
SuccessOrQuit(aesCcm.Process(Crypto::AesCcm::kDecrypt, test + kHeaderLength, kPayloadLength));
DumpBuffer("decrypted", test, sizeof(test));
VerifyOrQuit(memcmp(test, decrypted, sizeof(decrypted)) == 0);
// Verify encryption/decryption in place within a message.
@@ -153,34 +164,53 @@ void TestMacCommandFrame(void)
SuccessOrQuit(message->AppendBytes(test, kHeaderLength + kPayloadLength));
aesCcm.Init(kHeaderLength, kPayloadLength, kTagLength, nonce, sizeof(nonce));
aesCcm.Header(test, kHeaderLength);
aesCcm.Payload(*message, kHeaderLength, kPayloadLength, Crypto::AesCcm::kEncrypt);
VerifyOrQuit(aesCcm.GetTagLength() == kTagLength);
aesCcm.Finalize(tag);
SuccessOrQuit(message->Append(tag));
aesCcm.SetKey(key, sizeof(key));
aesCcm.SetNonce(nonce, sizeof(nonce));
aesCcm.SetAuthData(test, kHeaderLength);
aesCcm.SetTagLength(kTagLength);
SuccessOrQuit(aesCcm.Process(Crypto::AesCcm::kEncrypt, *message, kHeaderLength));
VerifyOrQuit(message->GetLength() == sizeof(encrypted));
VerifyOrQuit(message->Compare(0, encrypted));
aesCcm.Init(kHeaderLength, kPayloadLength, kTagLength, nonce, sizeof(nonce));
aesCcm.Header(test, kHeaderLength);
aesCcm.Payload(*message, kHeaderLength, kPayloadLength, Crypto::AesCcm::kDecrypt);
VerifyOrQuit(message->GetLength() == sizeof(encrypted));
VerifyOrQuit(message->Compare(0, decrypted));
aesCcm.SetKey(key, sizeof(key));
aesCcm.SetNonce(nonce, sizeof(nonce));
aesCcm.SetAuthData(test, kHeaderLength);
aesCcm.SetTagLength(kTagLength);
SuccessOrQuit(aesCcm.Process(Crypto::AesCcm::kDecrypt, *message, kHeaderLength));
VerifyOrQuit(message->GetLength() == sizeof(decrypted) - kTagLength);
VerifyOrQuit(message->CompareBytes(0, decrypted, sizeof(decrypted) - kTagLength));
message->Free();
// Verify static `Perform()` with separate buffers
cryptoKey.Set(key, sizeof(key));
// Encrypt
Crypto::AesCcm::Perform(Crypto::AesCcm::kEncrypt, cryptoKey, kTagLength, nonce, sizeof(nonce), test, kHeaderLength,
plaintext, ciphertext, kPayloadLength, tag);
VerifyOrQuit(memcmp(ciphertext, encrypted + kHeaderLength, kPayloadLength) == 0);
VerifyOrQuit(memcmp(tag, encrypted + kHeaderLength + kPayloadLength, kTagLength) == 0);
// Decrypt
memset(plaintext, 0, sizeof(plaintext));
Crypto::AesCcm::Perform(Crypto::AesCcm::kDecrypt, cryptoKey, kTagLength, nonce, sizeof(nonce), test, kHeaderLength,
plaintext, ciphertext, kPayloadLength, tag);
VerifyOrQuit(memcmp(plaintext, decrypted + kHeaderLength, kPayloadLength) == 0);
VerifyOrQuit(memcmp(tag, decrypted + kHeaderLength + kPayloadLength, kTagLength) == 0);
testFreeInstance(instance);
printf("\nTestMacBeaconCommand PASSED\n\n");
}
/**
* Verifies in-place encryption/decryption.
*/
void TestInPlaceAesCcmProcessing(void)
void TestAesCcmMessageProcessing(void)
{
static constexpr uint16_t kTagLength = 4;
static constexpr uint32_t kHeaderLength = 19;
static constexpr uint16_t kTagLength = 4;
static constexpr uint32_t kAuthDataLength = 19;
static constexpr uint16_t kMaxBufferSize = 1000;
static constexpr uint16_t kNumIters = 16;
static const uint8_t kKey[] = {
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
@@ -192,13 +222,14 @@ void TestInPlaceAesCcmProcessing(void)
static uint16_t kMessageLengths[] = {30, 400, 800};
uint8_t tag[kTagLength];
uint8_t header[kHeaderLength];
Crypto::AesCcm aesCcm;
Instance *instance = testInitInstance();
uint16_t payloadOffset;
Message *message;
Message *messageClone;
uint8_t buffer[kMaxBufferSize];
Crypto::AesCcm aesCcm;
printf("\nTestAesCcmMessageProcessing");
VerifyOrQuit(instance != nullptr);
@@ -206,45 +237,97 @@ void TestInPlaceAesCcmProcessing(void)
VerifyOrQuit(message != nullptr);
aesCcm.SetKey(kKey, sizeof(kKey));
aesCcm.SetNonce(kNonce, sizeof(kNonce));
aesCcm.SetTagLength(kTagLength);
for (uint16_t msgLength : kMessageLengths)
{
printf("msgLength %d\n", msgLength);
printf("\n\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
printf("\nmsgLength %u\n", msgLength);
SuccessOrQuit(message->SetLength(0));
for (uint16_t i = msgLength; i != 0; i--)
for (uint16_t i = 0; i < msgLength; i++)
{
SuccessOrQuit(message->Append<uint8_t>(i & 0xff));
SuccessOrQuit(message->Append<uint8_t>(static_cast<uint8_t>(i & 0xff) ^ static_cast<uint8_t>(i >> 8)));
}
messageClone = message->Clone<kNoReservedHeader>();
VerifyOrQuit(messageClone != nullptr);
VerifyOrQuit(messageClone->GetLength() == msgLength);
SuccessOrQuit(message->Read(0, header));
// Encrypt in place
aesCcm.Init(kHeaderLength, msgLength - kHeaderLength, kTagLength, kNonce, sizeof(kNonce));
aesCcm.Header(&header, sizeof(header));
aesCcm.Payload(*message, kHeaderLength, msgLength - kHeaderLength, Crypto::AesCcm::kEncrypt);
// Append the tag
aesCcm.Finalize(tag);
SuccessOrQuit(message->Append(tag));
SuccessOrQuit(message->Read(0, buffer, msgLength));
payloadOffset = kAuthDataLength;
printf("\nEncrypt `message`");
aesCcm.SetAuthData(buffer, kAuthDataLength);
SuccessOrQuit(aesCcm.Process(Crypto::AesCcm::kEncrypt, *message, payloadOffset));
VerifyOrQuit(message->GetLength() == msgLength + kTagLength);
// Decrypt in place
aesCcm.Init(kHeaderLength, msgLength - kHeaderLength, kTagLength, kNonce, sizeof(kNonce));
aesCcm.Header(&header, sizeof(header));
aesCcm.Payload(*message, kHeaderLength, msgLength - kHeaderLength, Crypto::AesCcm::kDecrypt);
printf("\nEncrypt same content in `buffer`");
SuccessOrQuit(aesCcm.Process(Crypto::AesCcm::kEncrypt, buffer + payloadOffset, msgLength - payloadOffset));
// Check the tag against what is the message
aesCcm.Finalize(tag);
VerifyOrQuit(message->Compare(msgLength, tag));
printf("\nValidate the two ways result in the same encrypted content and tag\n");
VerifyOrQuit(message->CompareBytes(0, buffer, msgLength + kTagLength));
// Check that decrypted message is the same as original (cloned) message
// Corrupt the message by modifying a byte at some offset
// (random offset or start/end of the tag)and
// validate the decryption fails
for (uint16_t iter = 0; iter < kNumIters; iter++)
{
Message *corruptedMsg = message->Clone<kNoReservedHeader>();
uint16_t offset;
uint8_t byte;
uint8_t randomByte;
VerifyOrQuit(corruptedMsg != nullptr);
VerifyOrQuit(corruptedMsg->GetLength() == message->GetLength());
switch (iter)
{
case 0:
offset = msgLength; // Start of the tag
break;
case 1:
offset = msgLength + kTagLength - 1; // End of the tag
break;
case 2:
offset = msgLength + kTagLength / 2; // Middle of the tag
break;
default:
offset = Random::NonCrypto::GenerateFromMinUpToExcluding(payloadOffset, msgLength);
break;
}
SuccessOrQuit(corruptedMsg->Read<uint8_t>(offset, byte));
do
{
randomByte = Random::NonCrypto::Generate<uint8_t>();
} while (randomByte == byte);
corruptedMsg->Write<uint8_t>(offset, randomByte);
printf("\nCorrupt the message content - modify byte at offset %3u from 0x%02x to 0x%02x", offset, byte,
randomByte);
VerifyOrQuit(aesCcm.Process(Crypto::AesCcm::kDecrypt, *corruptedMsg, payloadOffset) == kErrorSecurity);
corruptedMsg->Free();
}
printf("\n\nDecrypt `message`");
SuccessOrQuit(aesCcm.Process(Crypto::AesCcm::kDecrypt, *message, payloadOffset));
VerifyOrQuit(message->GetLength() == msgLength);
printf("\nDecrypt same content in `buffer`");
SuccessOrQuit(aesCcm.Process(Crypto::AesCcm::kDecrypt, buffer + payloadOffset, msgLength - payloadOffset));
printf("\nValidate the two ways result in the same decrypted content");
VerifyOrQuit(message->CompareBytes(0, buffer, msgLength));
printf("\nCheck that decrypted message is the same as original (cloned) message");
VerifyOrQuit(message->CompareBytes(0, *messageClone, 0, msgLength));
messageClone->Free();
@@ -252,6 +335,8 @@ void TestInPlaceAesCcmProcessing(void)
message->Free();
testFreeInstance(instance);
printf("\nTestAesCcmMessageProcessing PASSED\n\n");
}
} // namespace ot
@@ -260,7 +345,7 @@ int main(void)
{
ot::TestMacBeaconFrame();
ot::TestMacCommandFrame();
ot::TestInPlaceAesCcmProcessing();
ot::TestAesCcmMessageProcessing();
printf("All tests passed\n");
return 0;
}