Skip to content

Commit fa3213e

Browse files
authored
[tcat] implement vendor policy for TLV support and automatic advertisement activation/deactivation (openthread#13038)
This commit implements additional vendor application or ecosystem policy settings for TCAT including: 1) Automatic deactivation of the TCAT agent / TCAT advertisement after the thread network has been started over TCAT 2) Automatic activation of the TCAT agent / TCAT advertisement after the thread network has been stopped over TCAT 3) Automatic activation of the TCAT agent / TCAT advertisement after decommissioning over TCAT 4) Blocking support of certain TCAT TLVs by the application / ecosystem The commit also fixes an issue with certificate storage after decommissioning.
1 parent 2a2d4be commit fa3213e

7 files changed

Lines changed: 226 additions & 79 deletions

File tree

include/openthread/ble_secure.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ otError otBleSecureSetTcatVendorInfo(otInstance *aInstance, const otTcatVendorIn
138138
/**
139139
* Enables the TCAT protocol over BLE Secure.
140140
*
141+
* Vendor info must be set before calling this function. Depending on the policy defined in the vendor info, TCAT may
142+
* start in standby mode if the device is commissioned and Thread is enabled.
143+
*
141144
* @param[in] aInstance A pointer to an OpenThread instance.
142145
* @param[in] aJoinHandler A pointer to a function that is called when a network join or leave
143146
* operation is requested under guidance of the TCAT Commissioner.

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 (599)
55+
#define OPENTHREAD_API_VERSION (600)
5656

5757
/**
5858
* @addtogroup api-instance

include/openthread/tcat.h

Lines changed: 122 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,58 @@ extern "C" {
7575
#define OT_TCAT_MAX_DEVICEID_SIZE 64 ///< TCAT max size of device ID.
7676
#define OT_TCAT_ENABLE_MAX 600 ///< TCAT_ENABLE_MAX, default max TMF TCAT enable time, in seconds.
7777

78+
/**
79+
* Represents TCAT command TLV type.
80+
*/
81+
typedef enum otTcatCommandTlvType
82+
{
83+
// Command Class General
84+
OT_TCAT_TLV_RESPONSE_WITH_STATUS = 0x01, ///< TCAT response with status value TLV
85+
OT_TCAT_TLV_RESPONSE_WITH_PAYLOAD = 0x02, ///< TCAT response with payload TLV
86+
OT_TCAT_TLV_RESPONSE_EVENT = 0x03, ///< TCAT response event TLV (reserved)
87+
OT_TCAT_TLV_GET_NETWORK_NAME = 0x08, ///< TCAT network name query TLV
88+
OT_TCAT_TLV_DISCONNECT = 0x09, ///< TCAT disconnect request TLV
89+
OT_TCAT_TLV_PING = 0x0A, ///< TCAT ping request TLV
90+
OT_TCAT_TLV_GET_DEVICE_ID = 0x0B, ///< TCAT device ID query TLV
91+
OT_TCAT_TLV_GET_EXTENDED_PAN_ID = 0x0C, ///< TCAT extended PAN ID query TLV
92+
OT_TCAT_TLV_GET_PROVISIONING_URL = 0x0D, ///< TCAT provisioning URL query TLV
93+
OT_TCAT_TLV_PRESENT_PSKD_HASH = 0x10, ///< TCAT rights elevation request TLV using PSKd hash
94+
OT_TCAT_TLV_PRESENT_PSKC_HASH = 0x11, ///< TCAT rights elevation request TLV using PSKc hash
95+
OT_TCAT_TLV_PRESENT_INSTALL_CODE_HASH = 0x12, ///< TCAT rights elevation TLV using install code
96+
OT_TCAT_TLV_REQUEST_RANDOM_CHALLENGE = 0x13, ///< TCAT random number challenge query TLV
97+
98+
// Command Class Commissioning
99+
OT_TCAT_TLV_SET_ACTIVE_OPERATIONAL_DATASET = 0x20, ///< TCAT active operational dataset TLV
100+
OT_TCAT_TLV_SET_ACTIVE_OPERATIONAL_DATASET_ALT = 0x21, ///< TCAT active dataset alt #1 TLV (reserved)
101+
OT_TCAT_TLV_GET_COMMISSIONER_CERTIFICATE = 0x25, ///< TCAT commissioner certificate query TLV
102+
OT_TCAT_TLV_GET_DIAGNOSTIC_TLVS = 0x26, ///< TCAT diagnostics TLVs query TLV
103+
OT_TCAT_TLV_START_THREAD_INTERFACE = 0x27, ///< TCAT start thread interface request TLV
104+
OT_TCAT_TLV_STOP_THREAD_INTERFACE = 0x28, ///< TCAT stop thread interface request TLV
105+
106+
// Command Class Extraction
107+
OT_TCAT_TLV_GET_ACTIVE_OPERATIONAL_DATASET = 0x40, ///< TCAT active operational dataset query TLV
108+
OT_TCAT_TLV_GET_ACTIVE_OPERATIONAL_DATASET_ALT = 0x41, ///< TCAT active dataset alt #1 query TLV (reserved)
109+
110+
// Command Class Decommissioning
111+
OT_TCAT_TLV_DECOMMISSION = 0x60, ///< TCAT decommission request TLV
112+
113+
// Command Class Application
114+
OT_TCAT_TLV_GET_APPLICATION_LAYERS = 0x80, ///< TCAT get application layers request TLV
115+
OT_TCAT_TLV_SEND_APPLICATION_DATA_1 = 0x81, ///< TCAT send application data 1 TLV
116+
OT_TCAT_TLV_SEND_APPLICATION_DATA_2 = 0x82, ///< TCAT send application data 2 TLV
117+
OT_TCAT_TLV_SEND_APPLICATION_DATA_3 = 0x83, ///< TCAT send application data 3 TLV
118+
OT_TCAT_TLV_SEND_APPLICATION_DATA_4 = 0x84, ///< TCAT send application data 4 TLV
119+
OT_TCAT_TLV_SERVICE_NAME_UDP = 0x89, ///< TCAT service name UDP sub-TLV (not used as a command)
120+
OT_TCAT_TLV_SERVICE_NAME_TCP = 0x8A, ///< TCAT service name TCP sub-TLV (not used as a command)
121+
OT_TCAT_TLV_SEND_VENDOR_SPECIFIC_DATA = 0x9F, ///< TCAT send vendor specific command or data TLV
122+
123+
// Command Class CCM
124+
OT_TCAT_TLV_SET_LDEV_ID_OPERATIONAL_CERT = 0xA0, ///< TCAT set LDevID certificate TLV (reserved)
125+
OT_TCAT_TLV_SET_LDEV_ID_PRIVATE_KEY = 0xA1, ///< TCAT set LDevID certificate private key TLV (reserved)
126+
OT_TCAT_TLV_SET_DOMAIN_CA_CERT = 0xA2, ///< TCAT set domain CA certificate TLV (reserved)
127+
128+
} otTcatCommandTlvType;
129+
78130
/**
79131
* Represents TCAT status code.
80132
*/
@@ -153,35 +205,6 @@ typedef struct otTcatGeneralDeviceId
153205
uint8_t mDeviceId[OT_TCAT_MAX_DEVICEID_SIZE];
154206
} otTcatGeneralDeviceId;
155207

156-
/**
157-
* This structure represents a TCAT vendor information.
158-
*
159-
* The content of this structure MUST persist and remain unchanged while a TCAT session is running.
160-
*/
161-
typedef struct otTcatVendorInfo
162-
{
163-
const char *mProvisioningUrl; ///< Provisioning URL path string
164-
const char *mVendorName; ///< Vendor name string
165-
const char *mVendorModel; ///< Vendor model string
166-
const char *mVendorSwVersion; ///< Vendor software version string
167-
const char *mVendorData; ///< Vendor specific data string
168-
const char *mPskdString; ///< Vendor managed pre-shared key for device
169-
const char *mInstallCode; ///< Vendor managed install code string
170-
const otTcatAdvertisedDeviceId
171-
*mAdvertisedDeviceIds; /** Vendor managed advertised device ID array.
172-
Array is terminated like C string with OT_TCAT_DEVICE_ID_EMPTY */
173-
const otTcatGeneralDeviceId *mGeneralDeviceId; /** Vendor managed general device ID array.
174-
(if NULL: device ID is set to EUI-64 in binary format) */
175-
const char *mApplicationServiceName[OT_TCAT_APPLICATION_LAYER_MAX_COUNT]; /** Array with application service names
176-
as C string with maximum length
177-
OT_TCAT_SERVICE_NAME_MAX_LENGTH or
178-
NULL if not supported */
179-
bool mApplicationServiceIsTcp[OT_TCAT_APPLICATION_LAYER_MAX_COUNT]; /** Array with boolean values indicating
180-
if the service is of TCP type (otherwise
181-
UDP) */
182-
183-
} otTcatVendorInfo;
184-
185208
/**
186209
* Pointer to call when application data or vendor-specific data was received over a TCAT TLS connection.
187210
* The application may generate a response to an incoming TCAT application data packet. The TCAT agent
@@ -201,9 +224,12 @@ typedef void (*otHandleTcatApplicationDataReceive)(otInstance *aIn
201224
void *aContext);
202225

203226
/**
204-
* Pointer to call to notify the completion of a network join/leave operation performed under
205-
* guidance of a TCAT Commissioner.
227+
* Pointer to call to notify of a network join/leave operation initiated under guidance of a TCAT Commissioner.
206228
*
229+
* @param[in] aInstance A pointer to an OpenThread instance.
230+
* @param[in] aIsJoin True if the operation was a network join (OT_TCAT_TLV_START_THREAD_INTERFACE),
231+
* false if it was a network leave (OT_TCAT_TLV_STOP_THREAD_INTERFACE or
232+
* OT_TCAT_TLV_DECOMMISSION).
207233
* @param[in] aError OT_ERROR_NONE if the network join/leave operation was successfully started.
208234
* OT_ERROR_INVALID_STATE if network join was requested but network credentials
209235
* were missing or incomplete.
@@ -213,7 +239,71 @@ typedef void (*otHandleTcatApplicationDataReceive)(otInstance *aIn
213239
* credential mismatch.
214240
* @param[in] aContext A pointer to arbitrary context information.
215241
*/
216-
typedef void (*otHandleTcatJoin)(otError aError, void *aContext);
242+
typedef void (*otHandleTcatJoin)(otInstance *aInstance, bool aIsJoin, otError aError, void *aContext);
243+
244+
/**
245+
* Pointer to call to control if a TCAT TLV of a specific type is supported. The application may allow
246+
* or reject processing of a received TCAT command based on an application defined policy.
247+
* If no handler is defined, all received TCAT commands will be allowed if the respective command class
248+
* is authorized. If a handler is defined and returns false, the TCAT command will be rejected with status
249+
* OT_TCAT_STATUS_UNSUPPORTED. If the handler returns true, the TCAT command will be allowed if the respective
250+
* command class is authorized.
251+
*
252+
* @param[in] aInstance A pointer to an OpenThread instance.
253+
* @param[in] aTlvType A TLV type to be authorized.
254+
* @param[in] aContext A pointer to arbitrary context information.
255+
*
256+
* @returns a boolean value indicating whether the TLV type is supported, based on current policy.
257+
*/
258+
typedef bool (*otHandleTcatTlvSupport)(otInstance *aInstance, otTcatCommandTlvType aTlvType, void *aContext);
259+
260+
/**
261+
* This structure represents a TCAT vendor information.
262+
*
263+
* The content of this structure MUST persist and remain unchanged while a TCAT session is running.
264+
*/
265+
typedef struct otTcatVendorInfo
266+
{
267+
const char *mProvisioningUrl; ///< Provisioning URL path string
268+
const char *mVendorName; ///< Vendor name string
269+
const char *mVendorModel; ///< Vendor model string
270+
const char *mVendorSwVersion; ///< Vendor software version string
271+
const char *mVendorData; ///< Vendor specific data string
272+
const char *mPskdString; ///< Vendor managed pre-shared key for device
273+
const char *mInstallCode; ///< Vendor managed install code string
274+
275+
/**
276+
* Vendor managed advertised device ID array. Array is terminated like C string with OT_TCAT_DEVICE_ID_EMPTY.
277+
*/
278+
const otTcatAdvertisedDeviceId *mAdvertisedDeviceIds;
279+
280+
/**
281+
* Vendor managed general device ID array (if NULL: device ID is set to EUI-64 in binary format)
282+
*/
283+
const otTcatGeneralDeviceId *mGeneralDeviceId;
284+
285+
/**
286+
* Array with application service names as C string with maximum length OT_TCAT_SERVICE_NAME_MAX_LENGTH or NULL if
287+
* not supported.
288+
*/
289+
const char *mApplicationServiceName[OT_TCAT_APPLICATION_LAYER_MAX_COUNT];
290+
291+
/**
292+
* Array with boolean values indicating if the service is of TCP type (otherwise UDP).
293+
*/
294+
bool mApplicationServiceIsTcp[OT_TCAT_APPLICATION_LAYER_MAX_COUNT];
295+
296+
bool mKeepActiveAfterJoining; ///< Continue advertising after thread interface has joined a network
297+
298+
/**
299+
* Prevent activating advertising indefinitely after the TCAT command OT_TCAT_TLV_STOP_THREAD_INTERFACE or
300+
* OT_TCAT_TLV_DECOMMISSION has been received.
301+
*/
302+
bool mDoNotActivateAfterLeaving;
303+
304+
otHandleTcatTlvSupport mTlvSupportHandler; ///< Optional pointer to a function to control TCAT TLV support
305+
306+
} otTcatVendorInfo;
217307

218308
/**
219309
* @}

src/core/common/notifier.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ void Notifier::EmitEvents(void)
193193
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
194194
Get<Utils::LinkMetricsManager>().HandleNotifierEvents(events);
195195
#endif
196+
#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
197+
Get<MeshCoP::TcatAgent>().HandleNotifierEvents(events);
198+
#endif
196199

197200
for (ExternalCallback &callback : mExternalCallbacks)
198201
{

src/core/meshcop/tcat_agent.cpp

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ TcatAgent::TcatAgent(Instance &aInstance)
6161
, mActiveOrStandbyTimer(aInstance)
6262
, mTcatActiveDurationMs(0)
6363
, mHashVerificationAttempts(1)
64+
, mLastDeviceRole(Mle::kRoleDisabled)
6465
{
6566
ClearCommissionerState();
6667
mLastHashVerificationTimestamp = Get<UptimeTracker>().GetUptimeInSeconds();
@@ -95,8 +96,16 @@ Error TcatAgent::Start(AppDataReceiveCallback aAppDataReceiveCallback, JoinCallb
9596
mState = kStateActive;
9697
mNextState = kStateActive;
9798
mTcatActiveDurationMs = 0;
99+
mLastDeviceRole = Get<Mle::Mle>().GetRole();
100+
98101
LogInfo("Start");
99102

103+
if (!mVendorInfo->mKeepActiveAfterJoining && mLastDeviceRole != Mle::kRoleDisabled &&
104+
Get<ActiveDatasetManager>().IsCommissioned())
105+
{
106+
IgnoreError(Standby());
107+
}
108+
100109
exit:
101110
LogWarnOnError(error, "Start");
102111
return error;
@@ -400,10 +409,19 @@ Error TcatAgent::HandleSingleTlv(const Message &aIncomingMessage, Message &aOutg
400409
const uint16_t initialOutgoingMsgLength = aOutgoingMessage.GetLength();
401410
bool response = false;
402411

412+
VerifyOrExit(mVendorInfo != nullptr, error = kErrorInvalidState);
403413
VerifyOrExit(IsConnected(), error = kErrorInvalidState);
404414

405415
SuccessOrExit(error = tlvInfo.ParseFrom(aIncomingMessage, aIncomingMessage.GetOffset()));
406416

417+
if (mVendorInfo->mTlvSupportHandler != nullptr &&
418+
!mVendorInfo->mTlvSupportHandler(&GetInstance(), static_cast<otTcatCommandTlvType>(tlvInfo.GetType()),
419+
mJoinCallback.GetContext()))
420+
{
421+
statusCode = kStatusUnsupported;
422+
ExitNow();
423+
}
424+
407425
switch (tlvInfo.GetType())
408426
{
409427
case kTlvDisconnect:
@@ -665,13 +683,19 @@ Error TcatAgent::HandleDecommission(void)
665683

666684
VerifyOrExit(IsCommandClassAuthorized(kDecommissioning), error = kErrorRejected);
667685
SuccessOrExit(error = Get<Ble::BleSecure>().GetPeerCertificateDer(buf, &bufLen, bufLen));
668-
Get<Settings>().SaveTcatCommissionerCertificate(buf, static_cast<uint16_t>(bufLen));
669686

670-
IgnoreReturnValue(otThreadSetEnabled(&GetInstance(), false));
687+
Get<Mle::Mle>().Stop();
688+
689+
if (!mVendorInfo->mDoNotActivateAfterLeaving)
690+
{
691+
IgnoreError(Activate(0, 0));
692+
}
693+
671694
Get<ActiveDatasetManager>().Clear();
672695
Get<PendingDatasetManager>().Clear();
673696

674697
IgnoreReturnValue(Get<Instance>().ErasePersistentInfo());
698+
Get<Settings>().SaveTcatCommissionerCertificate(buf, static_cast<uint16_t>(bufLen));
675699

676700
#if !OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
677701
{
@@ -681,6 +705,7 @@ Error TcatAgent::HandleDecommission(void)
681705
}
682706
#endif
683707

708+
mJoinCallback.InvokeIfSet(&GetInstance(), /* aIsJoin */ false, error);
684709
mIsCommissioned = false; // enable repeated commissioning/decommissioning in a session
685710

686711
exit:
@@ -979,24 +1004,29 @@ Error TcatAgent::HandleStartThreadInterface(void)
9791004
#endif
9801005

9811006
Get<ThreadNetif>().Up();
982-
error = Get<Mle::Mle>().Start();
1007+
SuccessOrExit(error = Get<Mle::Mle>().Start());
9831008

9841009
exit:
9851010
// error values for callback MUST be limited to the allowed set, see #JoinCallback
986-
mJoinCallback.InvokeIfSet(error);
1011+
mJoinCallback.InvokeIfSet(&GetInstance(), /* aIsJoin */ true, error);
9871012
return error;
9881013
}
9891014

9901015
Error TcatAgent::HandleStopThreadInterface(void)
9911016
{
992-
Error error;
1017+
Error error = kErrorNone;
9931018

9941019
VerifyOrExit(IsCommandClassAuthorized(kCommissioning), error = kErrorRejected);
9951020

996-
error = otThreadSetEnabled(&GetInstance(), false);
1021+
Get<Mle::Mle>().Stop();
1022+
1023+
if (!mVendorInfo->mDoNotActivateAfterLeaving)
1024+
{
1025+
IgnoreError(Activate(0, 0));
1026+
}
9971027

9981028
exit:
999-
mJoinCallback.InvokeIfSet(error);
1029+
mJoinCallback.InvokeIfSet(&GetInstance(), /* aIsJoin */ false, error);
10001030
return error;
10011031
}
10021032

@@ -1043,6 +1073,26 @@ void TcatAgent::NotifyStateChange(void)
10431073
mState == kStateConnected);
10441074
}
10451075

1076+
void TcatAgent::HandleNotifierEvents(Events aEvents)
1077+
{
1078+
VerifyOrExit(IsStarted());
1079+
VerifyOrExit(mVendorInfo != nullptr);
1080+
1081+
if (aEvents.ContainsAny(kEventThreadRoleChanged))
1082+
{
1083+
if (!mVendorInfo->mKeepActiveAfterJoining && Get<Mle::Mle>().IsAttached() &&
1084+
(mLastDeviceRole == Mle::kRoleDisabled || mLastDeviceRole == Mle::kRoleDetached))
1085+
{
1086+
IgnoreError(Standby());
1087+
}
1088+
1089+
mLastDeviceRole = Get<Mle::Mle>().GetRole();
1090+
}
1091+
1092+
exit:
1093+
return;
1094+
}
1095+
10461096
template <> void TcatAgent::HandleTmf<kUriTcatEnable>(Coap::Msg &aMsg)
10471097
{
10481098
Error error = kErrorNone;

0 commit comments

Comments
 (0)