Skip to content

Commit 9ea8ed3

Browse files
committed
[thread-direct] add guest Wake Key support
Extends the wake key model to support pre-provisioned guest keys (indices 130-192) for WI devices that do not hold the Thread Network Key. `KeyManager` gains a fixed `GuestWakeKeyEntry` table for storing and looking up guest keys. `otThreadDirectSetGuestWakeKey` / `otThreadDirectRemoveGuestWakeKey` expose the table to applications. `otThreadDirectWakeup` rejects a burst with an unprovisioned guest key index before the first frame is sent.
1 parent 5d3173d commit 9ea8ed3

9 files changed

Lines changed: 201 additions & 5 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 (609)
55+
#define OPENTHREAD_API_VERSION (610)
5656

5757
/**
5858
* @addtogroup api-instance

include/openthread/thread_direct.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,37 @@ bool otThreadDirectIsWakeListenerEnabled(otInstance *aInstance);
228228
*/
229229
bool otThreadDirectIsWakeBurstActive(otInstance *aInstance);
230230

231+
/**
232+
* Adds or replaces a guest Wake Key at the given key index.
233+
*
234+
* Guest Wake Keys are raw 16-byte keys provisioned out-of-band and used by WI devices
235+
* that do not hold the Thread Network Key. Valid key indices: [130, 192].
236+
* Key Index 129 is reserved for the default (network-derived) Wake Key.
237+
*
238+
* @param[in] aInstance The OpenThread instance.
239+
* @param[in] aKeyIndex Key Index in [130, 192].
240+
* @param[in] aKey 16-byte key material.
241+
*
242+
* @retval OT_ERROR_NONE Key stored.
243+
* @retval OT_ERROR_INVALID_ARGS @p aKeyIndex is outside [130, 192].
244+
* @retval OT_ERROR_NO_BUFS Guest key table is full.
245+
* @retval OT_ERROR_DISABLED_FEATURE OPENTHREAD_CONFIG_THREAD_DIRECT_GUEST_WAKE_KEY_ENABLE = 0.
246+
*/
247+
otError otThreadDirectSetGuestWakeKey(otInstance *aInstance, uint8_t aKeyIndex, const otThreadDirectWakeKey *aKey);
248+
249+
/**
250+
* Removes a previously configured guest Wake Key.
251+
*
252+
* No-op if no key is registered at @p aKeyIndex.
253+
*
254+
* @param[in] aInstance The OpenThread instance.
255+
* @param[in] aKeyIndex Key Index of the guest key to remove.
256+
*
257+
* @retval OT_ERROR_NONE Key removed (or was not present).
258+
* @retval OT_ERROR_INVALID_ARGS @p aKeyIndex is outside [130, 192].
259+
*/
260+
otError otThreadDirectRemoveGuestWakeKey(otInstance *aInstance, uint8_t aKeyIndex);
261+
231262
/**
232263
* @}
233264
*

src/core/api/thread_direct_api.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ otError otThreadDirectWakeup(otInstance *aInstance,
8787
IgnoreError(AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().ApplyConfiguration());
8888
}
8989

90+
if (effectiveKeyIndex >= OT_MAC_FRAME_GUEST_WAKE_KEY_INDEX_MIN &&
91+
effectiveKeyIndex <= OT_MAC_FRAME_GUEST_WAKE_KEY_INDEX_MAX)
92+
{
93+
VerifyOrExit(AsCoreType(aInstance).Get<ot::Mac::SubMac>().IsGuestWakeKeyRegistered(effectiveKeyIndex),
94+
error = OT_ERROR_INVALID_STATE);
95+
}
96+
9097
error = AsCoreType(aInstance).Get<WakeupTxScheduler>().StartWakeup(
9198
AsCoreType(aExtAddress), static_cast<Mac::Frame::WakeFrameType>(aWakeType), intervalUs, durationMs,
9299
effectiveKeyIndex);
@@ -125,4 +132,39 @@ bool otThreadDirectIsWakeListenerEnabled(otInstance *aInstance)
125132

126133
#endif // OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_LISTENER_ENABLE
127134

135+
otError otThreadDirectSetGuestWakeKey(otInstance *aInstance, uint8_t aKeyIndex, const otThreadDirectWakeKey *aKey)
136+
{
137+
otError error = OT_ERROR_NONE;
138+
ot::Mac::KeyMaterial material;
139+
140+
VerifyOrExit(aKey != nullptr, error = OT_ERROR_INVALID_ARGS);
141+
VerifyOrExit(aKeyIndex >= OT_MAC_FRAME_GUEST_WAKE_KEY_INDEX_MIN &&
142+
aKeyIndex <= OT_MAC_FRAME_GUEST_WAKE_KEY_INDEX_MAX,
143+
error = OT_ERROR_INVALID_ARGS);
144+
145+
// Import the raw 16-byte key into a PSA-aware KeyMaterial. kExportable ensures
146+
// the platform security layer can export the key bytes for hardware AES-CCM use.
147+
material.SetFrom(*reinterpret_cast<const ot::Mac::Key *>(aKey),
148+
OPENTHREAD_CONFIG_PLATFORM_MAC_KEYS_EXPORTABLE_ENABLE);
149+
150+
error = AsCoreType(aInstance).Get<ot::Mac::SubMac>().SetWakeKey(aKeyIndex, &material);
151+
152+
exit:
153+
return error;
154+
}
155+
156+
otError otThreadDirectRemoveGuestWakeKey(otInstance *aInstance, uint8_t aKeyIndex)
157+
{
158+
otError error = OT_ERROR_NONE;
159+
160+
VerifyOrExit(aKeyIndex >= OT_MAC_FRAME_GUEST_WAKE_KEY_INDEX_MIN &&
161+
aKeyIndex <= OT_MAC_FRAME_GUEST_WAKE_KEY_INDEX_MAX,
162+
error = OT_ERROR_INVALID_ARGS);
163+
164+
IgnoreError(AsCoreType(aInstance).Get<ot::Mac::SubMac>().SetWakeKey(aKeyIndex, nullptr));
165+
166+
exit:
167+
return error;
168+
}
169+
128170
#endif // OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_INITIATOR_ENABLE || OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_LISTENER_ENABLE

src/core/mac/mac.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1555,7 +1555,8 @@ Error Mac::ProcessReceiveSecurity(RxFrame &aFrame, const Address &aSrcAddr, Neig
15551555
}
15561556
else
15571557
{
1558-
ExitNow();
1558+
macKey = keyManager.FindGuestWakeKey(maybeWakeKeyId);
1559+
VerifyOrExit(macKey != nullptr); // drop frame if key not provisioned
15591560
}
15601561

15611562
extAddress = &aSrcAddr.GetExtended();

src/core/mac/sub_mac.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,17 @@ void SubMac::ProcessTransmitSecurity(void)
437437
{
438438
extAddress = &GetExtAddress();
439439

440-
mTransmitFrame.SetAesKey(Get<KeyManager>().GetDefaultWakeKey());
440+
if (mActiveBurstWakeKeyIndex == Frame::kWakeKeyIndex)
441+
{
442+
mTransmitFrame.SetAesKey(Get<KeyManager>().GetDefaultWakeKey());
443+
}
444+
else
445+
{
446+
const KeyMaterial *guestKey = Get<KeyManager>().FindGuestWakeKey(mActiveBurstWakeKeyIndex);
447+
448+
VerifyOrExit(guestKey != nullptr); // no key registered for this index; drop frame
449+
mTransmitFrame.SetAesKey(*guestKey);
450+
}
441451

442452
mTransmitFrame.ProcessTransmitAesCcm(*extAddress);
443453
ExitNow();
@@ -1024,6 +1034,12 @@ Error SubMac::SetWakeKey(uint8_t aKeyIndex, const KeyMaterial *aWakeKey)
10241034
{
10251035
Error error = kErrorNone;
10261036

1037+
// Guest keys (130-192) are stored in KeyManager for RX decryption on all platforms.
1038+
if (aKeyIndex >= OT_MAC_FRAME_GUEST_WAKE_KEY_INDEX_MIN && aKeyIndex <= OT_MAC_FRAME_GUEST_WAKE_KEY_INDEX_MAX)
1039+
{
1040+
SuccessOrExit(error = Get<KeyManager>().SetGuestWakeKey(aKeyIndex, aWakeKey));
1041+
}
1042+
10271043
if (!ShouldHandleTransmitSecurity())
10281044
{
10291045
Get<Radio>().SetWakeKey(aKeyIndex, aWakeKey);
@@ -1036,9 +1052,17 @@ Error SubMac::SetWakeKey(uint8_t aKeyIndex, const KeyMaterial *aWakeKey)
10361052
}
10371053
#endif
10381054

1055+
exit:
10391056
return error;
10401057
}
10411058

1059+
#if OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_INITIATOR_ENABLE
1060+
bool SubMac::IsGuestWakeKeyRegistered(uint8_t aKeyIndex) const
1061+
{
1062+
return Get<KeyManager>().FindGuestWakeKey(aKeyIndex) != nullptr;
1063+
}
1064+
#endif
1065+
10421066
#endif
10431067

10441068
void SubMac::SignalFrameCounterUsed(uint32_t aFrameCounter, uint8_t aKeyId)

src/core/mac/sub_mac.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,13 @@ class SubMac : public InstanceLocator, private NonCopyable
442442
void SetActiveBurstWakeKeyIndex(uint8_t aKeyIndex) { mActiveBurstWakeKeyIndex = aKeyIndex; }
443443
uint8_t GetActiveBurstWakeKeyIndex(void) const { return mActiveBurstWakeKeyIndex; }
444444

445+
/**
446+
* Returns true if a guest Wake Key has been provisioned at @p aKeyIndex via SetWakeKey().
447+
*
448+
* Used by the wake API to return OT_ERROR_INVALID_STATE before starting a burst with an
449+
* unprovisioned guest key index, rather than silently dropping every frame in the burst.
450+
*/
451+
bool IsGuestWakeKeyRegistered(uint8_t aKeyIndex) const;
445452
#endif
446453

447454
/**

src/core/mac/wakeup_tx_scheduler.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ Error WakeupTxScheduler::StartWakeup(const Mac::ExtAddress &aWlExtAddress,
7171
aKeyIndex <= OT_MAC_FRAME_GUEST_WAKE_KEY_INDEX_MAX),
7272
error = kErrorInvalidArgs);
7373

74+
if (aKeyIndex >= OT_MAC_FRAME_GUEST_WAKE_KEY_INDEX_MIN && aKeyIndex <= OT_MAC_FRAME_GUEST_WAKE_KEY_INDEX_MAX)
75+
{
76+
VerifyOrExit(Get<Mac::SubMac>().IsGuestWakeKeyRegistered(aKeyIndex), error = kErrorInvalidState);
77+
}
78+
7479
mKeyIndex = aKeyIndex;
7580

7681
// Inform SubMac of the key index so ProcessTransmitSecurity stamps the correct key ID.

src/core/thread/key_manager.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ KeyManager::KeyManager(Instance &aInstance)
176176
, mKeySequence(0)
177177
#if OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_INITIATOR_ENABLE || OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_LISTENER_ENABLE
178178
, mWakeKeyValid(false)
179+
, mGuestWakeKeys()
179180
#endif
180181
, mMleFrameCounter(0)
181182
, mStoredMacFrameCounter(0)
@@ -351,6 +352,60 @@ const Mac::KeyMaterial &KeyManager::GetDefaultWakeKey(void)
351352
return mWakeKeyMaterial;
352353
}
353354

355+
Error KeyManager::SetGuestWakeKey(uint8_t aKeyIndex, const Mac::KeyMaterial *aKey)
356+
{
357+
Error error = kErrorNone;
358+
359+
for (GuestWakeKeyEntry &entry : mGuestWakeKeys)
360+
{
361+
if (entry.mKeyIndex == aKeyIndex)
362+
{
363+
if (aKey != nullptr)
364+
{
365+
entry.mKey = *aKey;
366+
}
367+
else
368+
{
369+
entry.mKeyIndex = 0;
370+
entry.mKey.Clear();
371+
}
372+
373+
ExitNow();
374+
}
375+
}
376+
377+
if (aKey != nullptr)
378+
{
379+
for (GuestWakeKeyEntry &entry : mGuestWakeKeys)
380+
{
381+
if (entry.mKeyIndex == 0)
382+
{
383+
entry.mKeyIndex = aKeyIndex;
384+
entry.mKey = *aKey;
385+
ExitNow();
386+
}
387+
}
388+
389+
error = kErrorNoBufs;
390+
}
391+
392+
exit:
393+
return error;
394+
}
395+
396+
const Mac::KeyMaterial *KeyManager::FindGuestWakeKey(uint8_t aKeyIndex) const
397+
{
398+
for (const GuestWakeKeyEntry &entry : mGuestWakeKeys)
399+
{
400+
if (entry.mKeyIndex == aKeyIndex)
401+
{
402+
return &entry.mKey;
403+
}
404+
}
405+
406+
return nullptr;
407+
}
408+
354409
#endif // OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_INITIATOR_ENABLE || OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_LISTENER_ENABLE
355410

356411
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE

src/core/thread/key_manager.hpp

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,26 @@ class KeyManager : public InstanceLocator, private NonCopyable
451451
*/
452452
const Mac::KeyMaterial &GetDefaultWakeKey(void);
453453

454+
/**
455+
* Stores or removes a pre-provisioned guest Wake Key.
456+
*
457+
* Guest keys use key indices in [OT_MAC_FRAME_GUEST_WAKE_KEY_INDEX_MIN,
458+
* OT_MAC_FRAME_GUEST_WAKE_KEY_INDEX_MAX] (130-192). Passing @p aKey as nullptr
459+
* removes any previously stored key at @p aKeyIndex.
460+
*
461+
* @param[in] aKeyIndex Guest key index (130-192).
462+
* @param[in] aKey Key material to store, or nullptr to remove.
463+
*/
464+
Error SetGuestWakeKey(uint8_t aKeyIndex, const Mac::KeyMaterial *aKey);
465+
466+
/**
467+
* Looks up a pre-provisioned guest Wake Key by index.
468+
*
469+
* @param[in] aKeyIndex Guest key index (130-192).
470+
*
471+
* @returns A pointer to the stored key material, or nullptr if not found.
472+
*/
473+
const Mac::KeyMaterial *FindGuestWakeKey(uint8_t aKeyIndex) const;
454474
#endif
455475

456476
/**
@@ -638,8 +658,19 @@ class KeyManager : public InstanceLocator, private NonCopyable
638658
Mle::KeyMaterial mTemporaryMleKey;
639659

640660
#if OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_INITIATOR_ENABLE || OPENTHREAD_CONFIG_THREAD_DIRECT_WAKE_LISTENER_ENABLE
641-
Mac::KeyMaterial mWakeKeyMaterial;
642-
bool mWakeKeyValid : 1;
661+
// Guest wake keys keyed by Aux Security Header index (130-192).
662+
// mKeyIndex == 0 marks an empty slot.
663+
struct GuestWakeKeyEntry
664+
{
665+
uint8_t mKeyIndex;
666+
Mac::KeyMaterial mKey;
667+
};
668+
669+
static constexpr uint8_t kMaxGuestWakeKeys = OPENTHREAD_CONFIG_THREAD_DIRECT_MAX_DIRECT_PEERS;
670+
671+
Mac::KeyMaterial mWakeKeyMaterial;
672+
bool mWakeKeyValid : 1;
673+
GuestWakeKeyEntry mGuestWakeKeys[kMaxGuestWakeKeys];
643674
#endif
644675

645676
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE

0 commit comments

Comments
 (0)