mirror of
https://github.com/espressif/openthread.git
synced 2026-10-04 06:47:36 +00:00
[security] verify received Enh-ACK integrity (#5393)
With Thread 1.2, it is possible to receive an Enh-ACK with security enabled. This commit: - Move receive AES CCM related code to Frame class. - Verify received Enh-ACK in Mac layer. If MIC check failed, the transmitted frame will be marked as OT_ERROR_NO_ACK.
This commit is contained in:
+101
-24
@@ -1369,6 +1369,15 @@ void Mac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, otError aError
|
|||||||
|
|
||||||
mBroadcastTransmitCount = 0;
|
mBroadcastTransmitCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
|
||||||
|
// Verify Enh-ACK integrity by checking its MIC
|
||||||
|
if ((aError == OT_ERROR_NONE) && (aAckFrame != nullptr) &&
|
||||||
|
(ProcessEnhAckSecurity(aFrame, *aAckFrame) != OT_ERROR_NONE))
|
||||||
|
{
|
||||||
|
aError = OT_ERROR_NO_ACK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine next action based on current operation.
|
// Determine next action based on current operation.
|
||||||
@@ -1537,14 +1546,10 @@ otError Mac::ProcessReceiveSecurity(RxFrame &aFrame, const Address &aSrcAddr, Ne
|
|||||||
uint8_t securityLevel;
|
uint8_t securityLevel;
|
||||||
uint8_t keyIdMode;
|
uint8_t keyIdMode;
|
||||||
uint32_t frameCounter;
|
uint32_t frameCounter;
|
||||||
uint8_t nonce[Crypto::AesCcm::kNonceSize];
|
|
||||||
uint8_t tag[Frame::kMaxMicSize];
|
|
||||||
uint8_t tagLength;
|
|
||||||
uint8_t keyid;
|
uint8_t keyid;
|
||||||
uint32_t keySequence = 0;
|
uint32_t keySequence = 0;
|
||||||
const Key * macKey;
|
const Key * macKey;
|
||||||
const ExtAddress *extAddress;
|
const ExtAddress *extAddress;
|
||||||
Crypto::AesCcm aesCcm;
|
|
||||||
|
|
||||||
VerifyOrExit(aFrame.GetSecurityEnabled(), error = OT_ERROR_NONE);
|
VerifyOrExit(aFrame.GetSecurityEnabled(), error = OT_ERROR_NONE);
|
||||||
|
|
||||||
@@ -1621,26 +1626,7 @@ otError Mac::ProcessReceiveSecurity(RxFrame &aFrame, const Address &aSrcAddr, Ne
|
|||||||
OT_UNREACHABLE_CODE(break);
|
OT_UNREACHABLE_CODE(break);
|
||||||
}
|
}
|
||||||
|
|
||||||
Crypto::AesCcm::GenerateNonce(*extAddress, frameCounter, securityLevel, nonce);
|
SuccessOrExit(aFrame.ProcessReceiveAesCcm(*extAddress, *macKey));
|
||||||
tagLength = aFrame.GetFooterLength() - Frame::kFcsSize;
|
|
||||||
|
|
||||||
aesCcm.SetKey(*macKey);
|
|
||||||
|
|
||||||
aesCcm.Init(aFrame.GetHeaderLength(), aFrame.GetPayloadLength(), tagLength, nonce, sizeof(nonce));
|
|
||||||
aesCcm.Header(aFrame.GetHeader(), aFrame.GetHeaderLength());
|
|
||||||
|
|
||||||
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
||||||
aesCcm.Payload(aFrame.GetPayload(), aFrame.GetPayload(), aFrame.GetPayloadLength(), Crypto::AesCcm::kDecrypt);
|
|
||||||
#else
|
|
||||||
// For fuzz tests, execute AES but do not alter the payload
|
|
||||||
uint8_t fuzz[OT_RADIO_FRAME_MAX_SIZE];
|
|
||||||
aesCcm.Payload(fuzz, aFrame.GetPayload(), aFrame.GetPayloadLength(), Crypto::AesCcm::kDecrypt);
|
|
||||||
#endif
|
|
||||||
aesCcm.Finalize(tag);
|
|
||||||
|
|
||||||
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
||||||
VerifyOrExit(memcmp(tag, aFrame.GetFooter(), tagLength) == 0, OT_NOOP);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if ((keyIdMode == Frame::kKeyIdMode1) && aNeighbor->IsStateValid())
|
if ((keyIdMode == Frame::kKeyIdMode1) && aNeighbor->IsStateValid())
|
||||||
{
|
{
|
||||||
@@ -1664,6 +1650,97 @@ exit:
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
|
||||||
|
otError Mac::ProcessEnhAckSecurity(TxFrame &aTxFrame, RxFrame &aAckFrame)
|
||||||
|
{
|
||||||
|
otError error = OT_ERROR_SECURITY;
|
||||||
|
uint8_t securityLevel;
|
||||||
|
uint8_t txKeyId;
|
||||||
|
uint8_t ackKeyId;
|
||||||
|
uint8_t keyIdMode;
|
||||||
|
Address srcAddr;
|
||||||
|
Neighbor * neighbor;
|
||||||
|
KeyManager &keyManager = Get<KeyManager>();
|
||||||
|
const Key * macKey;
|
||||||
|
|
||||||
|
VerifyOrExit(aAckFrame.GetSecurityEnabled(), error = OT_ERROR_NONE);
|
||||||
|
VerifyOrExit(aAckFrame.IsVersion2015(), OT_NOOP);
|
||||||
|
|
||||||
|
IgnoreError(aAckFrame.GetSecurityLevel(securityLevel));
|
||||||
|
VerifyOrExit(securityLevel == Frame::kSecEncMic32, OT_NOOP);
|
||||||
|
|
||||||
|
IgnoreError(aAckFrame.GetKeyIdMode(keyIdMode));
|
||||||
|
VerifyOrExit(keyIdMode == Frame::kKeyIdMode1, error = OT_ERROR_NONE);
|
||||||
|
|
||||||
|
IgnoreError(aTxFrame.GetKeyId(txKeyId));
|
||||||
|
IgnoreError(aAckFrame.GetKeyId(ackKeyId));
|
||||||
|
|
||||||
|
VerifyOrExit(txKeyId == ackKeyId, OT_NOOP);
|
||||||
|
|
||||||
|
IgnoreError(aAckFrame.GetSrcAddr(srcAddr));
|
||||||
|
|
||||||
|
if (srcAddr.IsShort())
|
||||||
|
{
|
||||||
|
neighbor = Get<NeighborTable>().FindNeighbor(srcAddr);
|
||||||
|
|
||||||
|
if (neighbor != nullptr)
|
||||||
|
{
|
||||||
|
srcAddr.SetExtended(neighbor->GetExtAddress());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!srcAddr.IsExtended())
|
||||||
|
{
|
||||||
|
// Get Enh-ACK source address from transmitted frame destination address
|
||||||
|
IgnoreError(aTxFrame.GetDstAddr(srcAddr));
|
||||||
|
|
||||||
|
if (srcAddr.IsShort())
|
||||||
|
{
|
||||||
|
neighbor = Get<NeighborTable>().FindNeighbor(srcAddr);
|
||||||
|
|
||||||
|
if (neighbor != nullptr)
|
||||||
|
{
|
||||||
|
srcAddr.SetExtended(neighbor->GetExtAddress());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VerifyOrExit(srcAddr.IsExtended(), OT_NOOP);
|
||||||
|
|
||||||
|
ackKeyId--;
|
||||||
|
|
||||||
|
if (ackKeyId == (keyManager.GetCurrentKeySequence() & 0x7f))
|
||||||
|
{
|
||||||
|
macKey = &mSubMac.GetCurrentMacKey();
|
||||||
|
}
|
||||||
|
else if (ackKeyId == ((keyManager.GetCurrentKeySequence() - 1) & 0x7f))
|
||||||
|
{
|
||||||
|
macKey = &mSubMac.GetPreviousMacKey();
|
||||||
|
}
|
||||||
|
else if (ackKeyId == ((keyManager.GetCurrentKeySequence() + 1) & 0x7f))
|
||||||
|
{
|
||||||
|
macKey = &mSubMac.GetNextMacKey();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ExitNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aAckFrame.ProcessReceiveAesCcm(srcAddr.GetExtended(), *macKey) == OT_ERROR_NONE)
|
||||||
|
{
|
||||||
|
error = OT_ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if (error != OT_ERROR_NONE)
|
||||||
|
{
|
||||||
|
otLogInfoMac("Frame tx attempt failed, error: Enh-ACK security check fail");
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
#endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
|
||||||
|
|
||||||
void Mac::HandleReceivedFrame(RxFrame *aFrame, otError aError)
|
void Mac::HandleReceivedFrame(RxFrame *aFrame, otError aError)
|
||||||
{
|
{
|
||||||
Address srcaddr;
|
Address srcaddr;
|
||||||
|
|||||||
@@ -815,6 +815,9 @@ private:
|
|||||||
|
|
||||||
otError ProcessReceiveSecurity(RxFrame &aFrame, const Address &aSrcAddr, Neighbor *aNeighbor);
|
otError ProcessReceiveSecurity(RxFrame &aFrame, const Address &aSrcAddr, Neighbor *aNeighbor);
|
||||||
void ProcessTransmitSecurity(TxFrame &aFrame);
|
void ProcessTransmitSecurity(TxFrame &aFrame);
|
||||||
|
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
|
||||||
|
otError ProcessEnhAckSecurity(TxFrame &aTxFrame, RxFrame &aAckFrame);
|
||||||
|
#endif
|
||||||
void UpdateIdleMode(void);
|
void UpdateIdleMode(void);
|
||||||
void StartOperation(Operation aOperation);
|
void StartOperation(Operation aOperation);
|
||||||
void FinishOperation(void);
|
void FinishOperation(void);
|
||||||
|
|||||||
@@ -1154,6 +1154,54 @@ exit:
|
|||||||
}
|
}
|
||||||
#endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
|
#endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
|
||||||
|
|
||||||
|
otError RxFrame::ProcessReceiveAesCcm(const ExtAddress &aExtAddress, const Key &aMacKey)
|
||||||
|
{
|
||||||
|
#if OPENTHREAD_RADIO
|
||||||
|
OT_UNUSED_VARIABLE(aExtAddress);
|
||||||
|
OT_UNUSED_VARIABLE(aMacKey);
|
||||||
|
|
||||||
|
return OT_ERROR_NONE;
|
||||||
|
#else
|
||||||
|
otError error = OT_ERROR_SECURITY;
|
||||||
|
uint32_t frameCounter = 0;
|
||||||
|
uint8_t securityLevel;
|
||||||
|
uint8_t nonce[Crypto::AesCcm::kNonceSize];
|
||||||
|
uint8_t tag[kMaxMicSize];
|
||||||
|
uint8_t tagLength;
|
||||||
|
Crypto::AesCcm aesCcm;
|
||||||
|
|
||||||
|
VerifyOrExit(GetSecurityEnabled(), error = OT_ERROR_NONE);
|
||||||
|
|
||||||
|
SuccessOrExit(GetSecurityLevel(securityLevel));
|
||||||
|
SuccessOrExit(GetFrameCounter(frameCounter));
|
||||||
|
|
||||||
|
Crypto::AesCcm::GenerateNonce(aExtAddress, frameCounter, securityLevel, nonce);
|
||||||
|
|
||||||
|
aesCcm.SetKey(aMacKey);
|
||||||
|
tagLength = GetFooterLength() - Frame::kFcsSize;
|
||||||
|
|
||||||
|
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
|
||||||
|
uint8_t fuzz[OT_RADIO_FRAME_MAX_SIZE];
|
||||||
|
aesCcm.Payload(fuzz, GetPayload(), GetPayloadLength(), Crypto::AesCcm::kDecrypt);
|
||||||
|
#endif
|
||||||
|
aesCcm.Finalize(tag);
|
||||||
|
|
||||||
|
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
||||||
|
VerifyOrExit(memcmp(tag, GetFooter(), tagLength) == 0, OT_NOOP);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
error = OT_ERROR_NONE;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return error;
|
||||||
|
#endif // OPENTHREAD_RADIO
|
||||||
|
}
|
||||||
|
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
|
|
||||||
#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
|
#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
|
||||||
|
|||||||
@@ -1074,6 +1074,19 @@ public:
|
|||||||
*/
|
*/
|
||||||
const uint64_t &GetTimestamp(void) const { return mInfo.mRxInfo.mTimestamp; }
|
const uint64_t &GetTimestamp(void) const { return mInfo.mRxInfo.mTimestamp; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method performs AES CCM on the frame which is received.
|
||||||
|
*
|
||||||
|
* @param[in] aExtAddress A reference to the extended address, which will be used to generate nonce
|
||||||
|
* for AES CCM computation.
|
||||||
|
* @param[in] aMacKey A reference to the MAC key to decrypt the received frame.
|
||||||
|
*
|
||||||
|
* @retval OT_ERROR_NONE Process of received frame AES CCM succeeded.
|
||||||
|
* @retval OT_ERROR_SECURITY Received frame MIC check failed.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
otError ProcessReceiveAesCcm(const ExtAddress &aExtAddress, const Key &aMacKey);
|
||||||
|
|
||||||
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
|
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
|
||||||
/**
|
/**
|
||||||
* This method gets the offset to network time.
|
* This method gets the offset to network time.
|
||||||
|
|||||||
Reference in New Issue
Block a user