[ncp] add ephemeral key function in ncp (#11910)

The commit adds new spinel properties for ephemeral key feature:
* SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_STATE, for the NCP to update
  the ePSKc state to the host
* SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_ENABLE, for the host to
  enable/disable the ePSKc feature
* SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_ACTIVATE, for the host to
  start the ePSKc mode
* SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_DEACTIVATE, for the host to
  stop the ePSKc mode

The commit adds the NCP properties handler in NCP and also adds some
unit tests for the handlers.
This commit is contained in:
Li Cao
2025-09-12 14:33:28 -07:00
committed by GitHub
parent 48835fd143
commit bddd77d803
10 changed files with 336 additions and 2 deletions
+2 -1
View File
@@ -76,7 +76,8 @@
* Define to 1 to enable ephemeral key mechanism and its APIs in Border Agent.
*/
#ifndef OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
#define OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_4)
#define OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE \
(OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_4))
#endif
/**
+4
View File
@@ -1420,6 +1420,10 @@ const char *spinel_prop_key_to_cstr(spinel_prop_key_t prop_key)
{SPINEL_PROP_DNSSD_SERVICE, "DNSSD_SERVICE"},
{SPINEL_PROP_DNSSD_KEY_RECORD, "DNSSD_KEY_RECORD"},
{SPINEL_PROP_BORDER_AGENT_MESHCOP_SERVICE_STATE, "BORDER_AGENT_MESHCOP_SERVICE_STATE"},
{SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_STATE, "SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_STATE"},
{SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_ENABLE, "SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_ENABLE"},
{SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_ACTIVATE, "SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_ACTIVATE"},
{SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_DEACTIVATE, "SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_DEACTIVATE"},
{SPINEL_PROP_BACKBONE_ROUTER_STATE, "BACKBONE_ROUTER_STATE"},
{SPINEL_PROP_BACKBONE_ROUTER_ENABLE, "BACKBONE_ROUTER_ENABLE"},
{SPINEL_PROP_BACKBONE_ROUTER_MULTICAST_LISTENER, "BACKBONE_ROUTER_MULTICAST_LISTENER"},
+37
View File
@@ -4911,6 +4911,43 @@ enum
*/
SPINEL_PROP_BORDER_AGENT_MESHCOP_SERVICE_STATE = SPINEL_PROP_BORDER_AGENT__BEGIN + 1,
/// Border Agent Ephemeral Key State.
/**
* Format: `C` - Get and Unsolicited notifications.
*
* `C`: The Ephemeral Key state. The value corresponds to `otBorderAgentEphemeralKeyState`.
*/
SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_STATE = SPINEL_PROP_BORDER_AGENT__BEGIN + 2,
/// Enablement/Disablement of the Ephemeral Key feature.
/**
* Format: `b` - Write-Only
*
* `b`: true to enable the Ephemeral Key feature and false to disable the feature.
*
* Note that enabling the Ephemeral Key feature doesn't mean activating Ephemeral Key mode. If the feature
* is enabled, the corresponding bit in state bitmap in the meshcop service will be set.
*/
SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_ENABLE = SPINEL_PROP_BORDER_AGENT__BEGIN + 3,
/// Activation of the Ephemeral Key mode.
/**
* Format: `ULS` - Write-Only
*
* `U`: The ephemeral key to use.
* `L`: The timeout duration, in milliseconds, to use the ephemeral key.
* `S`: The UDP port for the MeshCop-e service. An ephemeral port will be used if this is 0.
*/
SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_ACTIVATE = SPINEL_PROP_BORDER_AGENT__BEGIN + 4,
/// Deactivation of the Ephemeral Key mode.
/**
* Format: `b` - Write-Only
*
* `b`: true to retain the current session if existed. false to force the disconnection for existing session.
*/
SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_DEACTIVATE = SPINEL_PROP_BORDER_AGENT__BEGIN + 5,
SPINEL_PROP_BORDER_AGENT__END = 0x970,
SPINEL_PROP_BACKBONE_ROUTER__BEGIN = 0x970,
+6 -1
View File
@@ -93,8 +93,13 @@ const ChangedPropsSet::Entry ChangedPropsSet::mSupportedProps[] = {
#endif
{SPINEL_PROP_PARENT_RESPONSE_INFO, SPINEL_STATUS_OK, true},
{SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS, SPINEL_STATUS_OK, false},
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
#if OPENTHREAD_FTD
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
{SPINEL_PROP_BORDER_AGENT_MESHCOP_SERVICE_STATE, SPINEL_STATUS_OK, false},
#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
{SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_STATE, SPINEL_STATUS_OK, false},
#endif
#endif
#endif
{SPINEL_PROP_BACKBONE_ROUTER_STATE, SPINEL_STATUS_OK, false},
};
+3
View File
@@ -375,6 +375,9 @@ NcpBase::NcpBase(Instance *aInstance)
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
otBorderAgentSetMeshCoPServiceChangedCallback(mInstance, HandleBorderAgentMeshCoPServiceChanged, this);
#endif
#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
otBorderAgentEphemeralKeySetCallback(mInstance, HandleBorderAgentEphemeralKeyStateChanged, this);
#endif
#endif // OPENTHREAD_FTD
#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
otSrpClientSetCallback(mInstance, HandleSrpClientCallback, this);
+5
View File
@@ -886,6 +886,11 @@ protected:
const otIp6Address *aAddress);
#endif
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
static void HandleBorderAgentEphemeralKeyStateChanged(void *aContext);
void HandleBorderAgentEphemeralKeyStateChanged(void);
#endif
#endif // OPENTHREAD_FTD
#if OPENTHREAD_CONFIG_DIAG_ENABLE
+8
View File
@@ -226,6 +226,9 @@ NcpBase::PropertyHandler NcpBase::FindGetPropertyHandler(spinel_prop_key_t aKey)
#endif
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_BORDER_AGENT_MESHCOP_SERVICE_STATE),
#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_STATE),
#endif
#endif
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_BACKBONE_ROUTER_STATE),
@@ -543,6 +546,11 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey)
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_DNSSD_STATE),
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_DNSSD_REQUEST_RESULT),
#endif
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_ENABLE),
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_ACTIVATE),
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_DEACTIVATE),
#endif
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_BACKBONE_ROUTER_ENABLE),
#endif
+89
View File
@@ -36,6 +36,9 @@
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
#include <openthread/backbone_router_ftd.h>
#endif
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
#include <openthread/border_agent.h>
#endif
#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE
#include <openthread/channel_manager.h>
#endif
@@ -205,6 +208,19 @@ void NcpBase::HandleBorderAgentMeshCoPServiceChanged(void)
#endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
void NcpBase::HandleBorderAgentEphemeralKeyStateChanged(void *aContext)
{
static_cast<NcpBase *>(aContext)->HandleBorderAgentEphemeralKeyStateChanged();
}
void NcpBase::HandleBorderAgentEphemeralKeyStateChanged(void)
{
mChangedPropsSet.AddProperty(SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_STATE);
mUpdateChangedPropsTask.Post();
}
#endif
// ----------------------------------------------------------------------------
// MARK: Individual Property Handlers
// ----------------------------------------------------------------------------
@@ -416,6 +432,70 @@ exit:
}
#endif // OPENTHREAD_CONFIG_DUA_ENABLE
#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_ENABLE>(void)
{
bool enabled;
otError error = OT_ERROR_NONE;
SuccessOrExit(error = mDecoder.ReadBool(enabled));
otBorderAgentEphemeralKeySetEnabled(mInstance, enabled);
exit:
return error;
}
template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_ACTIVATE>(void)
{
otError error = OT_ERROR_NONE;
const char *epskc;
uint32_t durationMilli;
uint16_t port;
SuccessOrExit(error = mDecoder.ReadUtf8(epskc));
SuccessOrExit(error = mDecoder.ReadUint32(durationMilli));
SuccessOrExit(error = mDecoder.ReadUint16(port));
VerifyOrExit(otBorderAgentEphemeralKeyGetState(mInstance) != OT_BORDER_AGENT_STATE_DISABLED,
error = OT_ERROR_NOT_CAPABLE);
error = otBorderAgentEphemeralKeyStart(mInstance, epskc, durationMilli, port);
exit:
return error;
}
template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_DEACTIVATE>(void)
{
otError error = OT_ERROR_NONE;
bool retainActiveSession;
SuccessOrExit(error = mDecoder.ReadBool(retainActiveSession));
switch (otBorderAgentEphemeralKeyGetState(mInstance))
{
case OT_BORDER_AGENT_STATE_STARTED:
break;
case OT_BORDER_AGENT_STATE_CONNECTED:
case OT_BORDER_AGENT_STATE_ACCEPTED:
VerifyOrExit(!retainActiveSession);
break;
case OT_BORDER_AGENT_STATE_DISABLED:
error = OT_ERROR_NOT_CAPABLE;
// Fall through
case OT_BORDER_AGENT_STATE_STOPPED:
ExitNow();
}
otBorderAgentEphemeralKeyStop(mInstance);
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_BACKBONE_ROUTER_LOCAL_STATE>(void)
{
@@ -1664,6 +1744,15 @@ exit:
return error;
}
#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_STATE>(void)
{
return mEncoder.WriteUint8(static_cast<uint8_t>(otBorderAgentEphemeralKeyGetState(mInstance)));
}
#endif
#endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
+1
View File
@@ -270,6 +270,7 @@ ot_unit_ncp_test(cli)
ot_unit_ncp_test(dnssd)
ot_unit_ncp_test(infra_if)
ot_unit_ncp_test(srp_server)
ot_unit_ncp_test(ephemeral_key)
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+181
View File
@@ -0,0 +1,181 @@
/*
* Copyright (c) 2025, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <openthread/border_agent.h>
#include "test_platform.h"
#include "test_util.h"
#include "common/code_utils.hpp"
#include "lib/spinel/spinel_buffer.hpp"
#include "lib/spinel/spinel_encoder.hpp"
#include "ncp/ncp_base.hpp"
#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
namespace ot {
constexpr uint16_t kMaxSpinelBufferSize = 2048;
static otError GenerateEphemeralKeySetEnabledFrame(bool aEnable, uint8_t *aBuf, uint16_t &aLen)
{
otError error = OT_ERROR_NONE;
uint8_t buf[kMaxSpinelBufferSize];
Spinel::Buffer ncpBuffer(buf, kMaxSpinelBufferSize);
Spinel::Encoder encoder(ncpBuffer);
uint8_t header = SPINEL_HEADER_FLAG | 0 /* Iid */ | 1 /* Tid */;
SuccessOrExit(
error = encoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_ENABLE));
SuccessOrExit(error = encoder.WriteBool(aEnable));
SuccessOrExit(error = encoder.EndFrame());
SuccessOrExit(ncpBuffer.OutFrameBegin());
aLen = ncpBuffer.OutFrameGetLength();
VerifyOrExit(ncpBuffer.OutFrameRead(aLen, aBuf) == aLen, error = OT_ERROR_FAILED);
exit:
return error;
}
static otError GenerateEphemeralKeyStartFrame(const char *aEpskc,
uint32_t aTimeout,
uint16_t aPort,
uint8_t *aBuf,
uint16_t &aLen)
{
otError error = OT_ERROR_NONE;
uint8_t buf[kMaxSpinelBufferSize];
Spinel::Buffer ncpBuffer(buf, kMaxSpinelBufferSize);
Spinel::Encoder encoder(ncpBuffer);
uint8_t header = SPINEL_HEADER_FLAG | 0 /* Iid */ | 1 /* Tid */;
SuccessOrExit(
error = encoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_ACTIVATE));
SuccessOrExit(error = encoder.WriteUtf8(aEpskc));
SuccessOrExit(error = encoder.WriteUint32(aTimeout));
SuccessOrExit(error = encoder.WriteUint16(aPort));
SuccessOrExit(error = encoder.EndFrame());
SuccessOrExit(ncpBuffer.OutFrameBegin());
aLen = ncpBuffer.OutFrameGetLength();
VerifyOrExit(ncpBuffer.OutFrameRead(aLen, aBuf) == aLen, error = OT_ERROR_FAILED);
exit:
return error;
}
static otError GenerateEphemeralKeyStopFrame(uint8_t *aBuf, uint16_t &aLen)
{
otError error = OT_ERROR_NONE;
uint8_t buf[kMaxSpinelBufferSize];
Spinel::Buffer ncpBuffer(buf, kMaxSpinelBufferSize);
Spinel::Encoder encoder(ncpBuffer);
uint8_t header = SPINEL_HEADER_FLAG | 0 /* Iid */ | 1 /* Tid */;
SuccessOrExit(error = encoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_SET,
SPINEL_PROP_BORDER_AGENT_EPHEMERAL_KEY_DEACTIVATE));
SuccessOrExit(error = encoder.WriteBool(false)); // Not retain active session. Doesn't matter here.
SuccessOrExit(error = encoder.EndFrame());
SuccessOrExit(ncpBuffer.OutFrameBegin());
aLen = ncpBuffer.OutFrameGetLength();
VerifyOrExit(ncpBuffer.OutFrameRead(aLen, aBuf) == aLen, error = OT_ERROR_FAILED);
exit:
return error;
}
void TestEphemeralKeySetEnabled(void)
{
Instance *instance = static_cast<Instance *>(testInitInstance());
Ncp::NcpBase ncpBase(instance);
uint8_t recvBuf[kMaxSpinelBufferSize];
uint16_t recvLen;
// Set Ephemeral Key feature to enabled.
SuccessOrQuit(GenerateEphemeralKeySetEnabledFrame(true, recvBuf, recvLen));
ncpBase.HandleReceive(recvBuf, recvLen);
// The state should be 'Stopped' after enabling it.
VerifyOrQuit(otBorderAgentEphemeralKeyGetState(instance) == OT_BORDER_AGENT_STATE_STOPPED);
// Set Ephemeral Key feature to disabled.
SuccessOrQuit(GenerateEphemeralKeySetEnabledFrame(false, recvBuf, recvLen));
ncpBase.HandleReceive(recvBuf, recvLen);
// The state should be 'Disabled' after disabling it.
VerifyOrQuit(otBorderAgentEphemeralKeyGetState(instance) == OT_BORDER_AGENT_STATE_DISABLED);
// Set Ephemeral Key feature to enabled again.
SuccessOrQuit(GenerateEphemeralKeySetEnabledFrame(true, recvBuf, recvLen));
ncpBase.HandleReceive(recvBuf, recvLen);
// The state should be 'Stopped' after enabling it.
VerifyOrQuit(otBorderAgentEphemeralKeyGetState(instance) == OT_BORDER_AGENT_STATE_STOPPED);
}
void TestEphemeralKeyStartAndStop(void)
{
Instance *instance = static_cast<Instance *>(testInitInstance());
Ncp::NcpBase ncpBase(instance);
uint8_t recvBuf[kMaxSpinelBufferSize];
uint16_t recvLen;
// Set Ephemeral Key feature to enabled.
SuccessOrQuit(GenerateEphemeralKeySetEnabledFrame(true, recvBuf, recvLen));
ncpBase.HandleReceive(recvBuf, recvLen);
VerifyOrQuit(otBorderAgentEphemeralKeyGetState(instance) == OT_BORDER_AGENT_STATE_STOPPED);
// Activate the Ephemeral Key mode.
SuccessOrQuit(GenerateEphemeralKeyStartFrame("123456789", 300000, 12345, recvBuf, recvLen));
ncpBase.HandleReceive(recvBuf, recvLen);
VerifyOrQuit(otBorderAgentEphemeralKeyGetState(instance) == OT_BORDER_AGENT_STATE_STARTED);
// Deactivate the Ephemeral Key mode.
SuccessOrQuit(GenerateEphemeralKeyStopFrame(recvBuf, recvLen));
ncpBase.HandleReceive(recvBuf, recvLen);
VerifyOrQuit(otBorderAgentEphemeralKeyGetState(instance) == OT_BORDER_AGENT_STATE_STOPPED);
}
} // namespace ot
#endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
int main(void)
{
#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
ot::TestEphemeralKeySetEnabled();
ot::TestEphemeralKeyStartAndStop();
#endif
printf("All tests passed\n");
return 0;
}