1 /*
2  *  Copyright (c) 2023, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file implements the TCAT Agent service.
32  */
33 
34 #include "tcat_agent.hpp"
35 #include <openthread/tcat.h>
36 #include "meshcop/network_name.hpp"
37 
38 #if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
39 
40 #include "common/array.hpp"
41 #include "common/code_utils.hpp"
42 #include "common/debug.hpp"
43 #include "common/encoding.hpp"
44 #include "common/locator_getters.hpp"
45 #include "common/string.hpp"
46 #include "instance/instance.hpp"
47 #include "radio/radio.hpp"
48 #include "thread/thread_netif.hpp"
49 #include "thread/uri_paths.hpp"
50 #include "utils/otns.hpp"
51 
52 namespace ot {
53 namespace MeshCoP {
54 
55 RegisterLogModule("TcatAgent");
56 
IsValid(void) const57 bool TcatAgent::VendorInfo::IsValid(void) const
58 {
59     return (mProvisioningUrl == nullptr ||
60             (IsValidUtf8String(mProvisioningUrl) &&
61              (static_cast<uint8_t>(StringLength(mProvisioningUrl, kProvisioningUrlMaxLength)) <
62               kProvisioningUrlMaxLength))) &&
63            mPskdString != nullptr;
64 }
65 
TcatAgent(Instance & aInstance)66 TcatAgent::TcatAgent(Instance &aInstance)
67     : InstanceLocator(aInstance)
68     , mVendorInfo(nullptr)
69     , mCurrentApplicationProtocol(kApplicationProtocolNone)
70     , mState(kStateDisabled)
71     , mCommissionerHasNetworkName(false)
72     , mCommissionerHasDomainName(false)
73     , mCommissionerHasExtendedPanId(false)
74 {
75     mJoinerPskd.Clear();
76     mCurrentServiceName[0] = 0;
77 }
78 
Start(AppDataReceiveCallback aAppDataReceiveCallback,JoinCallback aHandler,void * aContext)79 Error TcatAgent::Start(AppDataReceiveCallback aAppDataReceiveCallback, JoinCallback aHandler, void *aContext)
80 {
81     Error error = kErrorNone;
82 
83     LogInfo("Starting");
84     VerifyOrExit(mVendorInfo != nullptr, error = kErrorFailed);
85     mAppDataReceiveCallback.Set(aAppDataReceiveCallback, aContext);
86     mJoinCallback.Set(aHandler, aContext);
87 
88     mCurrentApplicationProtocol = kApplicationProtocolNone;
89     mState                      = kStateEnabled;
90 
91 exit:
92     LogWarnOnError(error, "start TCAT agent");
93     return error;
94 }
95 
Stop(void)96 void TcatAgent::Stop(void)
97 {
98     mCurrentApplicationProtocol = kApplicationProtocolNone;
99     mState                      = kStateDisabled;
100     mAppDataReceiveCallback.Clear();
101     mJoinCallback.Clear();
102     LogInfo("TCAT agent stopped");
103 }
104 
SetTcatVendorInfo(const VendorInfo & aVendorInfo)105 Error TcatAgent::SetTcatVendorInfo(const VendorInfo &aVendorInfo)
106 {
107     Error error = kErrorNone;
108 
109     VerifyOrExit(aVendorInfo.IsValid(), error = kErrorInvalidArgs);
110     SuccessOrExit(error = mJoinerPskd.SetFrom(aVendorInfo.mPskdString));
111     mVendorInfo = &aVendorInfo;
112 
113 exit:
114     return error;
115 }
116 
Connected(MeshCoP::SecureTransport & aTlsContext)117 Error TcatAgent::Connected(MeshCoP::SecureTransport &aTlsContext)
118 {
119     size_t len;
120     Error  error;
121 
122     VerifyOrExit(IsEnabled(), error = kErrorInvalidState);
123     len = sizeof(mCommissionerAuthorizationField);
124     SuccessOrExit(
125         error = aTlsContext.GetThreadAttributeFromPeerCertificate(
126             kCertificateAuthorizationField, reinterpret_cast<uint8_t *>(&mCommissionerAuthorizationField), &len));
127     VerifyOrExit(len == sizeof(mCommissionerAuthorizationField), error = kErrorParse);
128     VerifyOrExit((mCommissionerAuthorizationField.mHeader & kCommissionerFlag) == 1, error = kErrorParse);
129 
130     len = sizeof(mDeviceAuthorizationField);
131     SuccessOrExit(error = aTlsContext.GetThreadAttributeFromOwnCertificate(
132                       kCertificateAuthorizationField, reinterpret_cast<uint8_t *>(&mDeviceAuthorizationField), &len));
133     VerifyOrExit(len == sizeof(mDeviceAuthorizationField), error = kErrorParse);
134     VerifyOrExit((mDeviceAuthorizationField.mHeader & kCommissionerFlag) == 0, error = kErrorParse);
135 
136     mCommissionerHasDomainName    = false;
137     mCommissionerHasNetworkName   = false;
138     mCommissionerHasExtendedPanId = false;
139 
140     len = sizeof(mCommissionerDomainName) - 1;
141     if (aTlsContext.GetThreadAttributeFromPeerCertificate(
142             kCertificateDomainName, reinterpret_cast<uint8_t *>(&mCommissionerDomainName), &len) == kErrorNone)
143     {
144         mCommissionerDomainName.m8[len] = '\0';
145         mCommissionerHasDomainName      = true;
146     }
147 
148     len = sizeof(mCommissionerNetworkName) - 1;
149     if (aTlsContext.GetThreadAttributeFromPeerCertificate(
150             kCertificateNetworkName, reinterpret_cast<uint8_t *>(&mCommissionerNetworkName), &len) == kErrorNone)
151     {
152         mCommissionerNetworkName.m8[len] = '\0';
153         mCommissionerHasNetworkName      = true;
154     }
155 
156     len = sizeof(mCommissionerExtendedPanId);
157     if (aTlsContext.GetThreadAttributeFromPeerCertificate(
158             kCertificateExtendedPanId, reinterpret_cast<uint8_t *>(&mCommissionerExtendedPanId), &len) == kErrorNone)
159     {
160         if (len == sizeof(mCommissionerExtendedPanId))
161         {
162             mCommissionerHasExtendedPanId = true;
163         }
164     }
165 
166     mCurrentApplicationProtocol = kApplicationProtocolNone;
167     mCurrentServiceName[0]      = 0;
168     mState                      = kStateConnected;
169     LogInfo("TCAT agent connected");
170 
171 exit:
172     return error;
173 }
174 
Disconnected(void)175 void TcatAgent::Disconnected(void)
176 {
177     mCurrentApplicationProtocol = kApplicationProtocolNone;
178 
179     if (mState != kStateDisabled)
180     {
181         mState = kStateEnabled;
182     }
183 
184     LogInfo("TCAT agent disconnected");
185 }
186 
CheckCommandClassAuthorizationFlags(CommandClassFlags aCommissionerCommandClassFlags,CommandClassFlags aDeviceCommandClassFlags,Dataset * aDataset) const187 bool TcatAgent::CheckCommandClassAuthorizationFlags(CommandClassFlags aCommissionerCommandClassFlags,
188                                                     CommandClassFlags aDeviceCommandClassFlags,
189                                                     Dataset          *aDataset) const
190 {
191     bool authorized                     = false;
192     bool additionalDeviceRequirementMet = false;
193     bool domainNamesMatch               = false;
194     bool networkNamesMatch              = false;
195     bool extendedPanIdsMatch            = false;
196 
197     VerifyOrExit(IsConnected());
198     VerifyOrExit(aCommissionerCommandClassFlags & kAccessFlag);
199 
200     if (aDeviceCommandClassFlags & kAccessFlag)
201     {
202         additionalDeviceRequirementMet = true;
203     }
204 
205     if (aDeviceCommandClassFlags & kPskdFlag)
206     {
207         additionalDeviceRequirementMet = true;
208     }
209 
210     if (aDeviceCommandClassFlags & kPskcFlag)
211     {
212         additionalDeviceRequirementMet = true;
213     }
214 
215     if (mCommissionerHasNetworkName || mCommissionerHasExtendedPanId)
216     {
217         Dataset::Info datasetInfo;
218         Error         datasetError = kErrorNone;
219 
220         if (aDataset == nullptr)
221         {
222             datasetError = Get<ActiveDatasetManager>().Read(datasetInfo);
223         }
224         else
225         {
226             aDataset->ConvertTo(datasetInfo);
227         }
228 
229         if (datasetError == kErrorNone)
230         {
231             if (datasetInfo.IsPresent<Dataset::kNetworkName>() && mCommissionerHasNetworkName &&
232                 (datasetInfo.Get<Dataset::kNetworkName>() == mCommissionerNetworkName))
233             {
234                 networkNamesMatch = true;
235             }
236 
237             if (datasetInfo.IsPresent<Dataset::kExtendedPanId>() && mCommissionerHasExtendedPanId &&
238                 (datasetInfo.Get<Dataset::kExtendedPanId>() == mCommissionerExtendedPanId))
239             {
240                 extendedPanIdsMatch = true;
241             }
242         }
243     }
244 
245     if (!networkNamesMatch)
246     {
247         VerifyOrExit((aCommissionerCommandClassFlags & kNetworkNameFlag) == 0);
248     }
249     else if (aDeviceCommandClassFlags & kNetworkNameFlag)
250     {
251         additionalDeviceRequirementMet = true;
252     }
253 
254     if (!extendedPanIdsMatch)
255     {
256         VerifyOrExit((aCommissionerCommandClassFlags & kExtendedPanIdFlag) == 0);
257     }
258     else if (aDeviceCommandClassFlags & kExtendedPanIdFlag)
259     {
260         additionalDeviceRequirementMet = true;
261     }
262 
263 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
264     VerifyOrExit((aCommissionerCommandClassFlags & kThreadDomainFlag) == 0);
265 #endif
266 
267     if (!domainNamesMatch)
268     {
269         VerifyOrExit((aCommissionerCommandClassFlags & kThreadDomainFlag) == 0);
270     }
271     else if (aDeviceCommandClassFlags & kThreadDomainFlag)
272     {
273         additionalDeviceRequirementMet = true;
274     }
275 
276     if (additionalDeviceRequirementMet)
277     {
278         authorized = true;
279     }
280 
281 exit:
282     return authorized;
283 }
284 
IsCommandClassAuthorized(CommandClass aCommandClass) const285 bool TcatAgent::IsCommandClassAuthorized(CommandClass aCommandClass) const
286 {
287     bool authorized = false;
288 
289     switch (aCommandClass)
290     {
291     case kGeneral:
292         authorized = true;
293         break;
294 
295     case kCommissioning:
296         authorized = CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mCommissioningFlags,
297                                                          mDeviceAuthorizationField.mCommissioningFlags, nullptr);
298         break;
299 
300     case kExtraction:
301         authorized = CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mExtractionFlags,
302                                                          mDeviceAuthorizationField.mExtractionFlags, nullptr);
303         break;
304 
305     case kTlvDecommissioning:
306         authorized = CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mDecommissioningFlags,
307                                                          mDeviceAuthorizationField.mDecommissioningFlags, nullptr);
308         break;
309 
310     case kApplication:
311         authorized = CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mApplicationFlags,
312                                                          mDeviceAuthorizationField.mApplicationFlags, nullptr);
313         break;
314 
315     case kInvalid:
316         authorized = false;
317         break;
318     }
319 
320     return authorized;
321 }
322 
GetCommandClass(uint8_t aTlvType) const323 TcatAgent::CommandClass TcatAgent::GetCommandClass(uint8_t aTlvType) const
324 {
325     static constexpr int kGeneralTlvs            = 0x1F;
326     static constexpr int kCommissioningTlvs      = 0x3F;
327     static constexpr int kExtractionTlvs         = 0x5F;
328     static constexpr int kTlvDecommissioningTlvs = 0x7F;
329     static constexpr int kApplicationTlvs        = 0x9F;
330 
331     if (aTlvType <= kGeneralTlvs)
332     {
333         return kGeneral;
334     }
335     else if (aTlvType <= kCommissioningTlvs)
336     {
337         return kCommissioning;
338     }
339     else if (aTlvType <= kExtractionTlvs)
340     {
341         return kExtraction;
342     }
343     else if (aTlvType <= kTlvDecommissioningTlvs)
344     {
345         return kTlvDecommissioning;
346     }
347     else if (aTlvType <= kApplicationTlvs)
348     {
349         return kApplication;
350     }
351     else
352     {
353         return kInvalid;
354     }
355 }
356 
CanProcessTlv(uint8_t aTlvType) const357 bool TcatAgent::CanProcessTlv(uint8_t aTlvType) const
358 {
359     CommandClass tlvCommandClass = GetCommandClass(aTlvType);
360     return IsCommandClassAuthorized(tlvCommandClass);
361 }
362 
HandleSingleTlv(const Message & aIncomingMessage,Message & aOutgoingMessage)363 Error TcatAgent::HandleSingleTlv(const Message &aIncomingMessage, Message &aOutgoingMessage)
364 {
365     Error    error = kErrorParse;
366     ot::Tlv  tlv;
367     uint16_t offset = aIncomingMessage.GetOffset();
368     uint16_t length;
369     bool     response = false;
370 
371     VerifyOrExit(IsConnected(), error = kErrorInvalidState);
372     SuccessOrExit(error = aIncomingMessage.Read(offset, tlv));
373 
374     if (tlv.IsExtended())
375     {
376         ot::ExtendedTlv extTlv;
377         SuccessOrExit(error = aIncomingMessage.Read(offset, extTlv));
378         length = extTlv.GetLength();
379         offset += sizeof(ot::ExtendedTlv);
380     }
381     else
382     {
383         length = tlv.GetLength();
384         offset += sizeof(ot::Tlv);
385     }
386 
387     if (!CanProcessTlv(tlv.GetType()))
388     {
389         error = kErrorRejected;
390     }
391     else
392     {
393         switch (tlv.GetType())
394         {
395         case kTlvDisconnect:
396             error    = kErrorAbort;
397             response = true; // true - to avoid response-with-status being sent.
398             break;
399 
400         case kTlvSetActiveOperationalDataset:
401             error = HandleSetActiveOperationalDataset(aIncomingMessage, offset, length);
402             break;
403 
404         case kTlvStartThreadInterface:
405             error = HandleStartThreadInterface();
406             break;
407 
408         case kTlvStopThreadInterface:
409             error = otThreadSetEnabled(&GetInstance(), false);
410             break;
411 
412         case kTlvSendApplicationData:
413             LogInfo("Application data len:%d, offset:%d", length, offset);
414             mAppDataReceiveCallback.InvokeIfSet(&GetInstance(), &aIncomingMessage, offset,
415                                                 MapEnum(mCurrentApplicationProtocol), mCurrentServiceName);
416             response = true;
417             error    = kErrorNone;
418             break;
419 
420         case kTlvDecommission:
421             error = HandleDecomission();
422             break;
423 
424         case kTlvPing:
425             error = HandlePing(aIncomingMessage, aOutgoingMessage, offset, length, response);
426             break;
427         case kTlvGetNetworkName:
428             error = HandleGetNetworkName(aOutgoingMessage, response);
429             break;
430         case kTlvGetDeviceId:
431             error = HandleGetDeviceId(aOutgoingMessage, response);
432             break;
433         case kTlvGetExtendedPanID:
434             error = HandleGetExtPanId(aOutgoingMessage, response);
435             break;
436         case kTlvGetProvisioningURL:
437             error = HandleGetProvisioningUrl(aOutgoingMessage, response);
438             break;
439 
440         default:
441             error = kErrorInvalidCommand;
442         }
443     }
444     if (!response)
445     {
446         StatusCode statusCode;
447 
448         switch (error)
449         {
450         case kErrorNone:
451             statusCode = kStatusSuccess;
452             break;
453 
454         case kErrorInvalidState:
455             statusCode = kStatusUndefined;
456             break;
457 
458         case kErrorParse:
459             statusCode = kStatusParseError;
460             break;
461 
462         case kErrorInvalidCommand:
463             statusCode = kStatusUnsupported;
464             break;
465 
466         case kErrorRejected:
467             statusCode = kStatusUnauthorized;
468             break;
469 
470         case kErrorNotImplemented:
471             statusCode = kStatusUnsupported;
472             break;
473 
474         default:
475             statusCode = kStatusGeneralError;
476             break;
477         }
478 
479         SuccessOrExit(error = ot::Tlv::Append<ResponseWithStatusTlv>(aOutgoingMessage, statusCode));
480     }
481 
482 exit:
483     return error;
484 }
485 
HandleSetActiveOperationalDataset(const Message & aIncomingMessage,uint16_t aOffset,uint16_t aLength)486 Error TcatAgent::HandleSetActiveOperationalDataset(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength)
487 {
488     Dataset     dataset;
489     OffsetRange offsetRange;
490     Error       error;
491 
492     offsetRange.Init(aOffset, aLength);
493     SuccessOrExit(error = dataset.SetFrom(aIncomingMessage, offsetRange));
494     SuccessOrExit(error = dataset.ValidateTlvs());
495 
496     if (!CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mApplicationFlags,
497                                              mDeviceAuthorizationField.mApplicationFlags, &dataset))
498     {
499         error = kErrorRejected;
500         ExitNow();
501     }
502 
503     Get<ActiveDatasetManager>().SaveLocal(dataset);
504 
505 exit:
506     return error;
507 }
508 
HandleDecomission(void)509 Error TcatAgent::HandleDecomission(void)
510 {
511     Error error = kErrorNone;
512 
513     IgnoreReturnValue(otThreadSetEnabled(&GetInstance(), false));
514     Get<ActiveDatasetManager>().Clear();
515     Get<PendingDatasetManager>().Clear();
516 
517     error = Get<Instance>().ErasePersistentInfo();
518 
519 #if !OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
520     {
521         NetworkKey networkKey;
522         networkKey.Clear();
523         Get<KeyManager>().SetNetworkKey(networkKey);
524     }
525 #endif
526 
527     return error;
528 }
529 
HandlePing(const Message & aIncomingMessage,Message & aOutgoingMessage,uint16_t aOffset,uint16_t aLength,bool & response)530 Error TcatAgent::HandlePing(const Message &aIncomingMessage,
531                             Message       &aOutgoingMessage,
532                             uint16_t       aOffset,
533                             uint16_t       aLength,
534                             bool          &response)
535 {
536     Error           error = kErrorNone;
537     ot::ExtendedTlv extTlv;
538     ot::Tlv         tlv;
539 
540     VerifyOrExit(aLength <= kPingPayloadMaxLength, error = kErrorParse);
541     if (aLength > ot::Tlv::kBaseTlvMaxLength)
542     {
543         extTlv.SetType(kTlvResponseWithPayload);
544         extTlv.SetLength(aLength);
545         SuccessOrExit(error = aOutgoingMessage.Append(extTlv));
546     }
547     else
548     {
549         tlv.SetType(kTlvResponseWithPayload);
550         tlv.SetLength(static_cast<uint8_t>(aLength));
551         SuccessOrExit(error = aOutgoingMessage.Append(tlv));
552     }
553 
554     SuccessOrExit(error = aOutgoingMessage.AppendBytesFromMessage(aIncomingMessage, aOffset, aLength));
555     response = true;
556 
557 exit:
558     return error;
559 }
560 
HandleGetNetworkName(Message & aOutgoingMessage,bool & response)561 Error TcatAgent::HandleGetNetworkName(Message &aOutgoingMessage, bool &response)
562 {
563     Error             error    = kErrorNone;
564     MeshCoP::NameData nameData = Get<MeshCoP::NetworkNameManager>().GetNetworkName().GetAsData();
565 
566     VerifyOrExit(Get<ActiveDatasetManager>().IsCommissioned(), error = kErrorInvalidState);
567 #if !OPENTHREAD_CONFIG_ALLOW_EMPTY_NETWORK_NAME
568     VerifyOrExit(nameData.GetLength() > 0, error = kErrorInvalidState);
569 #endif
570 
571     SuccessOrExit(
572         error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, nameData.GetBuffer(), nameData.GetLength()));
573     response = true;
574 
575 exit:
576     return error;
577 }
578 
HandleGetDeviceId(Message & aOutgoingMessage,bool & response)579 Error TcatAgent::HandleGetDeviceId(Message &aOutgoingMessage, bool &response)
580 {
581     const uint8_t  *deviceId;
582     uint16_t        length = 0;
583     Mac::ExtAddress eui64;
584     Error           error = kErrorNone;
585 
586     if (mVendorInfo->mGeneralDeviceId != nullptr)
587     {
588         length   = mVendorInfo->mGeneralDeviceId->mDeviceIdLen;
589         deviceId = mVendorInfo->mGeneralDeviceId->mDeviceId;
590     }
591 
592     if (length == 0)
593     {
594         Get<Radio>().GetIeeeEui64(eui64);
595 
596         length   = sizeof(Mac::ExtAddress);
597         deviceId = eui64.m8;
598     }
599 
600     SuccessOrExit(error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, deviceId, length));
601 
602     response = true;
603 
604 exit:
605     return error;
606 }
607 
HandleGetExtPanId(Message & aOutgoingMessage,bool & response)608 Error TcatAgent::HandleGetExtPanId(Message &aOutgoingMessage, bool &response)
609 {
610     Error error;
611 
612     VerifyOrExit(Get<ActiveDatasetManager>().IsCommissioned(), error = kErrorInvalidState);
613 
614     SuccessOrExit(error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload,
615                                          &Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId(), sizeof(ExtendedPanId)));
616     response = true;
617 
618 exit:
619     return error;
620 }
621 
HandleGetProvisioningUrl(Message & aOutgoingMessage,bool & response)622 Error TcatAgent::HandleGetProvisioningUrl(Message &aOutgoingMessage, bool &response)
623 {
624     Error    error = kErrorNone;
625     uint16_t length;
626 
627     VerifyOrExit(mVendorInfo->mProvisioningUrl != nullptr, error = kErrorInvalidState);
628 
629     length = StringLength(mVendorInfo->mProvisioningUrl, kProvisioningUrlMaxLength);
630     VerifyOrExit(length > 0 && length <= Tlv::kBaseTlvMaxLength, error = kErrorInvalidState);
631 
632     error    = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, mVendorInfo->mProvisioningUrl, length);
633     response = true;
634 
635 exit:
636     return error;
637 }
638 
HandleStartThreadInterface(void)639 Error TcatAgent::HandleStartThreadInterface(void)
640 {
641     Error         error;
642     Dataset::Info datasetInfo;
643 
644     VerifyOrExit(Get<ActiveDatasetManager>().Read(datasetInfo) == kErrorNone, error = kErrorInvalidState);
645     VerifyOrExit(datasetInfo.IsPresent<Dataset::kNetworkKey>(), error = kErrorInvalidState);
646 
647 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE
648     VerifyOrExit(!Get<Mac::LinkRaw>().IsEnabled(), error = kErrorInvalidState);
649 #endif
650 
651     Get<ThreadNetif>().Up();
652     error = Get<Mle::MleRouter>().Start();
653 
654 exit:
655     return error;
656 }
657 
SeralizeTcatAdvertisementTlv(uint8_t * aBuffer,uint16_t & aOffset,TcatAdvertisementTlvType aType,uint16_t aLength,const uint8_t * aValue)658 void SeralizeTcatAdvertisementTlv(uint8_t                 *aBuffer,
659                                   uint16_t                &aOffset,
660                                   TcatAdvertisementTlvType aType,
661                                   uint16_t                 aLength,
662                                   const uint8_t           *aValue)
663 {
664     aBuffer[aOffset++] = static_cast<uint8_t>(aType << 4 | (aLength & 0xf));
665     memcpy(aBuffer + aOffset, aValue, aLength);
666     aOffset += aLength;
667 }
668 
GetAdvertisementData(uint16_t & aLen,uint8_t * aAdvertisementData)669 Error TcatAgent::GetAdvertisementData(uint16_t &aLen, uint8_t *aAdvertisementData)
670 {
671     Error                 error = kErrorNone;
672     DeviceTypeAndStatus   tas;
673     otBleLinkCapabilities caps;
674 
675     VerifyOrExit(mVendorInfo != nullptr && aAdvertisementData != nullptr, error = kErrorInvalidArgs);
676 
677     aLen = 0;
678 
679     LittleEndian::WriteUint16(OT_TOBLE_SERVICE_UUID, aAdvertisementData);
680     aLen += sizeof(uint16_t);
681     aAdvertisementData[2] = OPENTHREAD_CONFIG_THREAD_VERSION << 4 | OT_TCAT_OPCODE;
682     aLen++;
683 
684     if (mVendorInfo->mAdvertisedDeviceIds != nullptr)
685     {
686         for (uint8_t i = 0; mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY; i++)
687         {
688             switch (MapEnum(mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdType))
689             {
690             case kTcatDeviceIdOui24:
691                 SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvVendorOui24,
692                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen,
693                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId);
694                 break;
695             case kTcatDeviceIdOui36:
696                 SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvVendorOui36,
697                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen,
698                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId);
699                 break;
700             case kTcatDeviceIdDiscriminator:
701                 SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvDeviceDiscriminator,
702                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen,
703                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId);
704                 break;
705             case kTcatDeviceIdIanaPen:
706                 SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvVendorIanaPen,
707                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen,
708                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId);
709                 break;
710             default:
711                 break;
712             }
713         }
714     }
715 
716     otPlatBleGetLinkCapabilities(&GetInstance(), &caps);
717 
718     if (caps.mGattNotifications || caps.mL2CapDirect)
719     {
720         SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvBleLinkCapabilities, kTlvBleLinkCapabilitiesLength,
721                                      reinterpret_cast<uint8_t *>(&caps));
722     }
723 
724     tas.mRsv                 = 0;
725     tas.mMultiradioSupport   = otPlatBleSupportsMultiRadio(&GetInstance());
726     tas.mIsCommisionned      = Get<ActiveDatasetManager>().IsCommissioned();
727     tas.mThreadNetworkActive = Get<Mle::Mle>().IsAttached();
728     tas.mDeviceType          = Get<Mle::Mle>().GetDeviceMode().IsFullThreadDevice();
729     tas.mRxOnWhenIdle        = Get<Mle::Mle>().GetDeviceMode().IsRxOnWhenIdle();
730 
731 #if OPENTHREAD_FTD && (OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE || OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE || \
732                        OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE)
733     tas.mIsBorderRouter = true;
734 #else
735     tas.mIsBorderRouter = false;
736 #endif
737 
738     SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvDeviceTypeAndStatus, kTlvDeviceTypeAndStatusLength,
739                                  reinterpret_cast<uint8_t *>(&tas));
740     OT_ASSERT(aLen <= OT_TCAT_ADVERTISEMENT_MAX_LEN);
741 
742 exit:
743     return error;
744 }
745 
746 } // namespace MeshCoP
747 } // namespace ot
748 
749 #endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
750