mirror of
https://github.com/espressif/openthread.git
synced 2026-07-27 06:17:47 +00:00
[border-agent] add Thread Admin One-Time Passcodes (TAP) APIs (#12188)
This commit introduces new APIs to handle Thread Administration One-Time Passcodes (TAP). The new `otBorderAgentEphemeralKeyGenerateTap()` API generates a cryptographically secure 9-character TAP string. This consists of eight random numeric digits and a final check digit calculated using the Verhoeff algorithm for error detection. The corresponding `otBorderAgentEphemeralKeyValidateTap()` API validates a given TAP string by checking its length, ensuring it contains only digits, and verifying the Verhoeff checksum. A new test is added to ensure the correctness of both the generation and validation logic, covering success and failure scenarios.
This commit is contained in:
@@ -423,6 +423,11 @@ otError otBorderAgentEvictActiveCommissioner(otInstance *aInstance);
|
||||
*/
|
||||
#define OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_TIMEOUT (10 * 60 * 1000u)
|
||||
|
||||
/**
|
||||
* The string length of Thread Administration One-Time Passcode (TAP).
|
||||
*/
|
||||
#define OT_BORDER_AGENT_EPHEMERAL_KEY_TAP_STRING_LENGTH 9
|
||||
|
||||
/**
|
||||
* Represents Border Agent's Ephemeral Key Manager state.
|
||||
*/
|
||||
@@ -435,6 +440,14 @@ typedef enum otBorderAgentEphemeralKeyState
|
||||
OT_BORDER_AGENT_STATE_ACCEPTED = 4, ///< Session is established and candidate is accepted as full commissioner.
|
||||
} otBorderAgentEphemeralKeyState;
|
||||
|
||||
/**
|
||||
* Represents a Thread Administration One-Time Passcode (TAP).
|
||||
*/
|
||||
typedef struct otBorderAgentEphemeralKeyTap
|
||||
{
|
||||
char mTap[OT_BORDER_AGENT_EPHEMERAL_KEY_TAP_STRING_LENGTH + 1]; ///< TAP string buffer (including `\0` character).
|
||||
} otBorderAgentEphemeralKeyTap;
|
||||
|
||||
/**
|
||||
* Gets the state of Border Agent's Ephemeral Key Manager.
|
||||
*
|
||||
@@ -563,6 +576,37 @@ void otBorderAgentEphemeralKeySetCallback(otInstance *aIns
|
||||
*/
|
||||
const char *otBorderAgentEphemeralKeyStateToString(otBorderAgentEphemeralKeyState aState);
|
||||
|
||||
/**
|
||||
* Generates a cryptographically secure random Thread Administration One-Time Passcode (TAP) string.
|
||||
*
|
||||
* Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE` and `OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE`.
|
||||
*
|
||||
* The TAP is a string of 9 characters, generated as a sequence of eight cryptographically secure random
|
||||
* numeric digits [`0`-`9`] followed by a single check digit determined using the Verhoeff algorithm.
|
||||
*
|
||||
* @param[out] aTap A pointer to an `otBorderAgentEphemeralKeyTap` to output the generated TAP.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully generated a random TAP. @p aTap is updated.
|
||||
* @retval OT_ERROR_FAILED Failed to generate a random TAP.
|
||||
*/
|
||||
otError otBorderAgentEphemeralKeyGenerateTap(otBorderAgentEphemeralKeyTap *aTap);
|
||||
|
||||
/**
|
||||
* Validates a given Thread Administration One-Time Passcode (TAP) string.
|
||||
*
|
||||
* Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE` and `OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE`.
|
||||
*
|
||||
* Validates that the TAP string has the proper length, contains digit characters [`0`-`9`], and validates the
|
||||
* Verhoeff checksum.
|
||||
*
|
||||
* @param[in] aTap The `otBorderAgentEphemeralKeyTap` to validate.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully validated the @p aTap.
|
||||
* @retval OT_ERROR_INVALID_ARGS The @p aTap string has an invalid length or contains non-digit characters.
|
||||
* @retval OT_ERROR_FAILED Checksum validation failed.
|
||||
*/
|
||||
otError otBorderAgentEphemeralKeyValidateTap(const otBorderAgentEphemeralKeyTap *aTap);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
@@ -52,7 +52,7 @@ extern "C" {
|
||||
*
|
||||
* @note This number versions both OpenThread platform and user APIs.
|
||||
*/
|
||||
#define OPENTHREAD_API_VERSION (558)
|
||||
#define OPENTHREAD_API_VERSION (559)
|
||||
|
||||
/**
|
||||
* @addtogroup api-instance
|
||||
|
||||
@@ -172,6 +172,20 @@ const char *otBorderAgentEphemeralKeyStateToString(otBorderAgentEphemeralKeyStat
|
||||
return MeshCoP::BorderAgent::EphemeralKeyManager::StateToString(MapEnum(aState));
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE
|
||||
|
||||
otError otBorderAgentEphemeralKeyGenerateTap(otBorderAgentEphemeralKeyTap *aTap)
|
||||
{
|
||||
return AsCoreType(aTap).GenerateRandom();
|
||||
}
|
||||
|
||||
otError otBorderAgentEphemeralKeyValidateTap(const otBorderAgentEphemeralKeyTap *aTap)
|
||||
{
|
||||
return AsCoreType(aTap).Validate();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
|
||||
|
||||
#include "instance/instance.hpp"
|
||||
#include "utils/verhoeff_checksum.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace MeshCoP {
|
||||
@@ -428,6 +429,67 @@ const char *EphemeralKeyManager::DeactivationReasonToString(DeactivationReason a
|
||||
|
||||
#endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// EphemeralKeyManager::Tap
|
||||
|
||||
#if OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE
|
||||
|
||||
Error EphemeralKeyManager::Tap::GenerateRandom(void)
|
||||
{
|
||||
Error error;
|
||||
char checksum;
|
||||
|
||||
ClearAllBytes(mTap);
|
||||
|
||||
for (uint8_t index = 0; index < kLength - 1; index++)
|
||||
{
|
||||
SuccessOrExit(error = GenerateRandomDigit(mTap[index]));
|
||||
}
|
||||
|
||||
IgnoreError(Utils::VerhoeffChecksum::Calculate(mTap, checksum));
|
||||
mTap[kLength - 1] = checksum;
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
Error EphemeralKeyManager::Tap::GenerateRandomDigit(char &aChar)
|
||||
{
|
||||
static constexpr uint8_t kMaxValue = 250;
|
||||
|
||||
Error error;
|
||||
uint8_t byte;
|
||||
|
||||
// To ensure uniform random distribution and avoid bias toward
|
||||
// certain digit values, we ignore random `uint8` values of 250 or
|
||||
// larger (i.e., values in the range [250-255]). This ensures the
|
||||
// random `byte` is uniformly distributed in `[0-249]`, which,
|
||||
// when `% 10`, gives us a uniform probability of `[0-9]` values.
|
||||
|
||||
do
|
||||
{
|
||||
SuccessOrExit(error = Random::Crypto::Fill(byte));
|
||||
} while (byte >= kMaxValue);
|
||||
|
||||
aChar = '0' + (byte % 10);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
Error EphemeralKeyManager::Tap::Validate(void) const
|
||||
{
|
||||
Error error;
|
||||
|
||||
VerifyOrExit(StringLength(mTap, kLength + 1) == kLength, error = kErrorInvalidArgs);
|
||||
error = Utils::VerhoeffChecksum::Validate(mTap);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE
|
||||
|
||||
} // namespace BorderAgent
|
||||
} // namespace MeshCoP
|
||||
} // namespace ot
|
||||
|
||||
@@ -78,6 +78,43 @@ public:
|
||||
kStateAccepted = OT_BORDER_AGENT_STATE_ACCEPTED, ///< Session connected and accepted as full commissioner.
|
||||
};
|
||||
|
||||
#if OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE
|
||||
/**
|
||||
* Represents a Thread Administration One-Time Passcode (TAP).
|
||||
*/
|
||||
class Tap : public otBorderAgentEphemeralKeyTap
|
||||
{
|
||||
public:
|
||||
static constexpr uint8_t kLength = OT_BORDER_AGENT_EPHEMERAL_KEY_TAP_STRING_LENGTH; ///< TAP string length.
|
||||
|
||||
/**
|
||||
* Generates a cryptographically secure random Thread Administration One-Time Passcode (TAP) string.
|
||||
*
|
||||
* The TAP is a string of 9 characters, generated as a sequence of eight cryptographically secure random
|
||||
* numeric digits [`0`-`9`] followed by a single check digit determined using the Verhoeff algorithm.
|
||||
*
|
||||
* @retval kErrorNone Successfully generated a random TAP.
|
||||
* @retval kErrorFailed Failed to generate a random TAP.
|
||||
*/
|
||||
Error GenerateRandom(void);
|
||||
|
||||
/**
|
||||
* Validates a given Thread Administration One-Time Passcode (TAP) string.
|
||||
*
|
||||
* Validates that the TAP string has the proper length, contains digit characters [`0`-`9`], and validates the
|
||||
* Verhoeff checksum.
|
||||
*
|
||||
* @retval kErrorNone Successfully validated the TAP.
|
||||
* @retval kErrorInvalidArgs The TAP string has an invalid length or contains non-digit characters.
|
||||
* @retval kErrorFailed Checksum validation failed.
|
||||
*/
|
||||
Error Validate(void) const;
|
||||
|
||||
private:
|
||||
Error GenerateRandomDigit(char &aChar);
|
||||
};
|
||||
#endif // OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE
|
||||
|
||||
/**
|
||||
* Initializes the `EphemeralKeyManager`.
|
||||
*
|
||||
@@ -226,6 +263,9 @@ private:
|
||||
} // namespace MeshCoP
|
||||
|
||||
DefineMapEnum(otBorderAgentEphemeralKeyState, MeshCoP::BorderAgent::EphemeralKeyManager::State);
|
||||
#if OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE
|
||||
DefineCoreType(otBorderAgentEphemeralKeyTap, MeshCoP::BorderAgent::EphemeralKeyManager::Tap);
|
||||
#endif
|
||||
|
||||
} // namespace ot
|
||||
|
||||
|
||||
@@ -135,6 +135,7 @@
|
||||
#define OPENTHREAD_CONFIG_TREL_MANAGE_DNSSD_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_TREL_USE_HEAP_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_UPTIME_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE 1
|
||||
|
||||
// CLI configs
|
||||
#define OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH 800
|
||||
|
||||
@@ -757,6 +757,75 @@ void TestBorderAgentEphemeralKey(void)
|
||||
VerifyOrQuit(node0.Get<Manager>().GetCounters().mEpskcInvalidArgsErrors == 2);
|
||||
}
|
||||
|
||||
void TestBorderAgentEphemeralKeyTapGeneration(void)
|
||||
{
|
||||
static constexpr uint16_t kMaxRounds = 20;
|
||||
|
||||
using Tap = EphemeralKeyManager::Tap;
|
||||
|
||||
Core nexus;
|
||||
Node &node = nexus.CreateNode();
|
||||
Tap tap;
|
||||
uint8_t index;
|
||||
|
||||
Log("------------------------------------------------------------------------------------------------------");
|
||||
Log("TestBorderAgentEphemeralKeyTapGeneration");
|
||||
|
||||
nexus.AdvanceTime(0);
|
||||
|
||||
node.Form();
|
||||
nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
|
||||
VerifyOrQuit(node.Get<Mle::Mle>().IsLeader());
|
||||
|
||||
for (uint16_t round = 0; round < kMaxRounds; round++)
|
||||
{
|
||||
SuccessOrQuit(tap.GenerateRandom());
|
||||
SuccessOrQuit(tap.Validate());
|
||||
Log("Generated random TAP: %s", tap.mTap);
|
||||
|
||||
// Tamper with one of the digits and ensure checksum fails.
|
||||
|
||||
index = round % Tap::kLength;
|
||||
|
||||
tap.mTap[index]++;
|
||||
|
||||
if (tap.mTap[index] > '9')
|
||||
{
|
||||
tap.mTap[index] = '0';
|
||||
}
|
||||
|
||||
VerifyOrQuit(tap.Validate() == kErrorFailed);
|
||||
}
|
||||
|
||||
// CHeck valid TAP strings (Thread spec (1.4.1d3) - section 8.4.9.7)
|
||||
|
||||
SuccessOrQuit(StringCopy(tap.mTap, "903723159"));
|
||||
SuccessOrQuit(tap.Validate());
|
||||
|
||||
SuccessOrQuit(StringCopy(tap.mTap, "746351983"));
|
||||
SuccessOrQuit(tap.Validate());
|
||||
|
||||
// Check invalid TAP strings.
|
||||
|
||||
SuccessOrQuit(StringCopy(tap.mTap, ""));
|
||||
VerifyOrQuit(tap.Validate() == kErrorInvalidArgs);
|
||||
|
||||
SuccessOrQuit(StringCopy(tap.mTap, "1234"));
|
||||
VerifyOrQuit(tap.Validate() == kErrorInvalidArgs);
|
||||
|
||||
SuccessOrQuit(StringCopy(tap.mTap, "12345678"));
|
||||
VerifyOrQuit(tap.Validate() == kErrorInvalidArgs);
|
||||
|
||||
SuccessOrQuit(StringCopy(tap.mTap, "123456789"));
|
||||
VerifyOrQuit(tap.Validate() == kErrorFailed);
|
||||
|
||||
SuccessOrQuit(StringCopy(tap.mTap, "a23456789"));
|
||||
VerifyOrQuit(tap.Validate() == kErrorInvalidArgs);
|
||||
|
||||
SuccessOrQuit(StringCopy(tap.mTap, "12345678A"));
|
||||
VerifyOrQuit(tap.Validate() == kErrorInvalidArgs);
|
||||
}
|
||||
|
||||
EpskcEvent GetNewestEpskcEvent(Node &aNode)
|
||||
{
|
||||
const EpskcEvent *epskcEvent = nullptr;
|
||||
@@ -1979,6 +2048,7 @@ int main(void)
|
||||
{
|
||||
ot::Nexus::TestBorderAgent();
|
||||
ot::Nexus::TestBorderAgentEphemeralKey();
|
||||
ot::Nexus::TestBorderAgentEphemeralKeyTapGeneration();
|
||||
ot::Nexus::TestHistoryTrackerBorderAgentEpskcEvent();
|
||||
ot::Nexus::TestBorderAgentTxtDataCallback();
|
||||
ot::Nexus::TestBorderAgentServiceRegistration();
|
||||
|
||||
Reference in New Issue
Block a user