• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "common/code_utils.hpp"
36 
37 #if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
38 
39 #include "instance/instance.hpp"
40 
41 namespace ot {
42 namespace MeshCoP {
43 
44 RegisterLogModule("TcatAgent");
45 
IsValid(void) const46 bool TcatAgent::VendorInfo::IsValid(void) const
47 {
48     return (mProvisioningUrl == nullptr ||
49             (IsValidUtf8String(mProvisioningUrl) &&
50              (static_cast<uint8_t>(StringLength(mProvisioningUrl, kProvisioningUrlMaxLength)) <
51               kProvisioningUrlMaxLength))) &&
52            mPskdString != nullptr;
53 }
54 
TcatAgent(Instance & aInstance)55 TcatAgent::TcatAgent(Instance &aInstance)
56     : InstanceLocator(aInstance)
57     , mVendorInfo(nullptr)
58     , mCurrentApplicationProtocol(kApplicationProtocolNone)
59     , mState(kStateDisabled)
60     , mCommissionerHasNetworkName(false)
61     , mCommissionerHasDomainName(false)
62     , mCommissionerHasExtendedPanId(false)
63     , mRandomChallenge(0)
64     , mPskdVerified(false)
65     , mPskcVerified(false)
66     , mInstallCodeVerified(false)
67 {
68     mJoinerPskd.Clear();
69     mCurrentServiceName[0] = 0;
70 }
71 
Start(AppDataReceiveCallback aAppDataReceiveCallback,JoinCallback aHandler,void * aContext)72 Error TcatAgent::Start(AppDataReceiveCallback aAppDataReceiveCallback, JoinCallback aHandler, void *aContext)
73 {
74     Error error = kErrorNone;
75 
76     LogInfo("Starting");
77     VerifyOrExit(mVendorInfo != nullptr, error = kErrorFailed);
78     mAppDataReceiveCallback.Set(aAppDataReceiveCallback, aContext);
79     mJoinCallback.Set(aHandler, aContext);
80     mRandomChallenge = 0;
81 
82     mCurrentApplicationProtocol = kApplicationProtocolNone;
83     mState                      = kStateEnabled;
84 
85 exit:
86     LogWarnOnError(error, "start TCAT agent");
87     return error;
88 }
89 
Stop(void)90 void TcatAgent::Stop(void)
91 {
92     mCurrentApplicationProtocol = kApplicationProtocolNone;
93     mState                      = kStateDisabled;
94     mAppDataReceiveCallback.Clear();
95     mJoinCallback.Clear();
96     mRandomChallenge     = 0;
97     mPskdVerified        = false;
98     mPskcVerified        = false;
99     mInstallCodeVerified = false;
100     LogInfo("TCAT agent stopped");
101 }
102 
SetTcatVendorInfo(const VendorInfo & aVendorInfo)103 Error TcatAgent::SetTcatVendorInfo(const VendorInfo &aVendorInfo)
104 {
105     Error error = kErrorNone;
106 
107     VerifyOrExit(aVendorInfo.IsValid(), error = kErrorInvalidArgs);
108     SuccessOrExit(error = mJoinerPskd.SetFrom(aVendorInfo.mPskdString));
109     mVendorInfo = &aVendorInfo;
110 
111 exit:
112     return error;
113 }
114 
Connected(MeshCoP::Tls::Extension & aTls)115 Error TcatAgent::Connected(MeshCoP::Tls::Extension &aTls)
116 {
117     size_t len;
118     Error  error;
119 
120     VerifyOrExit(IsEnabled(), error = kErrorInvalidState);
121     len = sizeof(mCommissionerAuthorizationField);
122     SuccessOrExit(
123         error = aTls.GetThreadAttributeFromPeerCertificate(
124             kCertificateAuthorizationField, reinterpret_cast<uint8_t *>(&mCommissionerAuthorizationField), &len));
125     VerifyOrExit(len == sizeof(mCommissionerAuthorizationField), error = kErrorParse);
126     VerifyOrExit((mCommissionerAuthorizationField.mHeader & kCommissionerFlag) == 1, error = kErrorParse);
127 
128     len = sizeof(mDeviceAuthorizationField);
129     SuccessOrExit(error = aTls.GetThreadAttributeFromOwnCertificate(
130                       kCertificateAuthorizationField, reinterpret_cast<uint8_t *>(&mDeviceAuthorizationField), &len));
131     VerifyOrExit(len == sizeof(mDeviceAuthorizationField), error = kErrorParse);
132     VerifyOrExit((mDeviceAuthorizationField.mHeader & kCommissionerFlag) == 0, error = kErrorParse);
133 
134     mCommissionerHasDomainName    = false;
135     mCommissionerHasNetworkName   = false;
136     mCommissionerHasExtendedPanId = false;
137 
138     len = sizeof(mCommissionerDomainName) - 1;
139     if (aTls.GetThreadAttributeFromPeerCertificate(
140             kCertificateDomainName, reinterpret_cast<uint8_t *>(&mCommissionerDomainName), &len) == kErrorNone)
141     {
142         mCommissionerDomainName.m8[len] = '\0';
143         mCommissionerHasDomainName      = true;
144     }
145 
146     len = sizeof(mCommissionerNetworkName) - 1;
147     if (aTls.GetThreadAttributeFromPeerCertificate(
148             kCertificateNetworkName, reinterpret_cast<uint8_t *>(&mCommissionerNetworkName), &len) == kErrorNone)
149     {
150         mCommissionerNetworkName.m8[len] = '\0';
151         mCommissionerHasNetworkName      = true;
152     }
153 
154     len = sizeof(mCommissionerExtendedPanId);
155     if (aTls.GetThreadAttributeFromPeerCertificate(
156             kCertificateExtendedPanId, reinterpret_cast<uint8_t *>(&mCommissionerExtendedPanId), &len) == kErrorNone)
157     {
158         if (len == sizeof(mCommissionerExtendedPanId))
159         {
160             mCommissionerHasExtendedPanId = true;
161         }
162     }
163 
164     mCurrentApplicationProtocol = kApplicationProtocolNone;
165     mCurrentServiceName[0]      = 0;
166     mState                      = kStateConnected;
167     LogInfo("TCAT agent connected");
168 
169 exit:
170     return error;
171 }
172 
Disconnected(void)173 void TcatAgent::Disconnected(void)
174 {
175     mCurrentApplicationProtocol = kApplicationProtocolNone;
176 
177     if (mState != kStateDisabled)
178     {
179         mState = kStateEnabled;
180     }
181 
182     mRandomChallenge     = 0;
183     mPskdVerified        = false;
184     mPskcVerified        = false;
185     mInstallCodeVerified = false;
186 
187     LogInfo("TCAT agent disconnected");
188 }
189 
CheckCommandClassAuthorizationFlags(CommandClassFlags aCommissionerCommandClassFlags,CommandClassFlags aDeviceCommandClassFlags,Dataset * aDataset) const190 bool TcatAgent::CheckCommandClassAuthorizationFlags(CommandClassFlags aCommissionerCommandClassFlags,
191                                                     CommandClassFlags aDeviceCommandClassFlags,
192                                                     Dataset          *aDataset) const
193 {
194     bool authorized                     = false;
195     bool additionalDeviceRequirementMet = false;
196     bool domainNamesMatch               = false;
197     bool networkNamesMatch              = false;
198     bool extendedPanIdsMatch            = false;
199 
200     VerifyOrExit(IsConnected());
201     VerifyOrExit(aCommissionerCommandClassFlags & kAccessFlag);
202 
203     if (aDeviceCommandClassFlags & kAccessFlag)
204     {
205         additionalDeviceRequirementMet = true;
206     }
207 
208     if (!additionalDeviceRequirementMet && (aDeviceCommandClassFlags & kPskdFlag))
209     {
210         additionalDeviceRequirementMet = mPskdVerified;
211     }
212 
213     if (!additionalDeviceRequirementMet && (aDeviceCommandClassFlags & kPskcFlag))
214     {
215         additionalDeviceRequirementMet = mPskcVerified;
216     }
217 
218     if (mCommissionerHasNetworkName || mCommissionerHasExtendedPanId)
219     {
220         Dataset::Info datasetInfo;
221         Error         datasetError = kErrorNone;
222 
223         if (aDataset == nullptr)
224         {
225             datasetError = Get<ActiveDatasetManager>().Read(datasetInfo);
226         }
227         else
228         {
229             aDataset->ConvertTo(datasetInfo);
230         }
231 
232         if (datasetError == kErrorNone)
233         {
234             if (datasetInfo.IsPresent<Dataset::kNetworkName>() && mCommissionerHasNetworkName &&
235                 (datasetInfo.Get<Dataset::kNetworkName>() == mCommissionerNetworkName))
236             {
237                 networkNamesMatch = true;
238             }
239 
240             if (datasetInfo.IsPresent<Dataset::kExtendedPanId>() && mCommissionerHasExtendedPanId &&
241                 (datasetInfo.Get<Dataset::kExtendedPanId>() == mCommissionerExtendedPanId))
242             {
243                 extendedPanIdsMatch = true;
244             }
245         }
246     }
247 
248     if (!networkNamesMatch)
249     {
250         VerifyOrExit((aCommissionerCommandClassFlags & kNetworkNameFlag) == 0);
251     }
252     else if (aDeviceCommandClassFlags & kNetworkNameFlag)
253     {
254         additionalDeviceRequirementMet = true;
255     }
256 
257     if (!extendedPanIdsMatch)
258     {
259         VerifyOrExit((aCommissionerCommandClassFlags & kExtendedPanIdFlag) == 0);
260     }
261     else if (aDeviceCommandClassFlags & kExtendedPanIdFlag)
262     {
263         additionalDeviceRequirementMet = true;
264     }
265 
266 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
267     VerifyOrExit((aCommissionerCommandClassFlags & kThreadDomainFlag) == 0);
268 #endif
269 
270     if (!domainNamesMatch)
271     {
272         VerifyOrExit((aCommissionerCommandClassFlags & kThreadDomainFlag) == 0);
273     }
274     else if (aDeviceCommandClassFlags & kThreadDomainFlag)
275     {
276         additionalDeviceRequirementMet = true;
277     }
278 
279     if (additionalDeviceRequirementMet)
280     {
281         authorized = true;
282     }
283 
284 exit:
285     return authorized;
286 }
287 
IsCommandClassAuthorized(CommandClass aCommandClass) const288 bool TcatAgent::IsCommandClassAuthorized(CommandClass aCommandClass) const
289 {
290     bool authorized = false;
291 
292     switch (aCommandClass)
293     {
294     case kGeneral:
295         authorized = true;
296         break;
297 
298     case kCommissioning:
299         authorized = CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mCommissioningFlags,
300                                                          mDeviceAuthorizationField.mCommissioningFlags, nullptr);
301         break;
302 
303     case kExtraction:
304         authorized = CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mExtractionFlags,
305                                                          mDeviceAuthorizationField.mExtractionFlags, nullptr);
306         break;
307 
308     case kTlvDecommissioning:
309         authorized = CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mDecommissioningFlags,
310                                                          mDeviceAuthorizationField.mDecommissioningFlags, nullptr);
311         break;
312 
313     case kApplication:
314         authorized = CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mApplicationFlags,
315                                                          mDeviceAuthorizationField.mApplicationFlags, nullptr);
316         break;
317 
318     case kInvalid:
319         authorized = false;
320         break;
321     }
322 
323     return authorized;
324 }
325 
GetCommandClass(uint8_t aTlvType) const326 TcatAgent::CommandClass TcatAgent::GetCommandClass(uint8_t aTlvType) const
327 {
328     static constexpr int kGeneralTlvs            = 0x1F;
329     static constexpr int kCommissioningTlvs      = 0x3F;
330     static constexpr int kExtractionTlvs         = 0x5F;
331     static constexpr int kTlvDecommissioningTlvs = 0x7F;
332     static constexpr int kApplicationTlvs        = 0x9F;
333 
334     if (aTlvType <= kGeneralTlvs)
335     {
336         return kGeneral;
337     }
338     else if (aTlvType <= kCommissioningTlvs)
339     {
340         return kCommissioning;
341     }
342     else if (aTlvType <= kExtractionTlvs)
343     {
344         return kExtraction;
345     }
346     else if (aTlvType <= kTlvDecommissioningTlvs)
347     {
348         return kTlvDecommissioning;
349     }
350     else if (aTlvType <= kApplicationTlvs)
351     {
352         return kApplication;
353     }
354     else
355     {
356         return kInvalid;
357     }
358 }
359 
CanProcessTlv(uint8_t aTlvType) const360 bool TcatAgent::CanProcessTlv(uint8_t aTlvType) const
361 {
362     CommandClass tlvCommandClass = GetCommandClass(aTlvType);
363     return IsCommandClassAuthorized(tlvCommandClass);
364 }
365 
HandleSingleTlv(const Message & aIncomingMessage,Message & aOutgoingMessage)366 Error TcatAgent::HandleSingleTlv(const Message &aIncomingMessage, Message &aOutgoingMessage)
367 {
368     Error    error = kErrorParse;
369     ot::Tlv  tlv;
370     uint16_t offset = aIncomingMessage.GetOffset();
371     uint16_t length;
372     bool     response = false;
373 
374     VerifyOrExit(IsConnected(), error = kErrorInvalidState);
375     SuccessOrExit(error = aIncomingMessage.Read(offset, tlv));
376 
377     if (tlv.IsExtended())
378     {
379         ot::ExtendedTlv extTlv;
380         SuccessOrExit(error = aIncomingMessage.Read(offset, extTlv));
381         length = extTlv.GetLength();
382         offset += sizeof(ot::ExtendedTlv);
383     }
384     else
385     {
386         length = tlv.GetLength();
387         offset += sizeof(ot::Tlv);
388     }
389 
390     if (!CanProcessTlv(tlv.GetType()))
391     {
392         error = kErrorRejected;
393     }
394     else
395     {
396         switch (tlv.GetType())
397         {
398         case kTlvDisconnect:
399             error    = kErrorAbort;
400             response = true; // true - to avoid response-with-status being sent.
401             break;
402 
403         case kTlvSetActiveOperationalDataset:
404             error = HandleSetActiveOperationalDataset(aIncomingMessage, offset, length);
405             break;
406 
407         case kTlvGetActiveOperationalDataset:
408             error = HandleGetActiveOperationalDataset(aOutgoingMessage, response);
409             break;
410 
411         case kTlvStartThreadInterface:
412             error = HandleStartThreadInterface();
413             break;
414 
415         case kTlvStopThreadInterface:
416             error = otThreadSetEnabled(&GetInstance(), false);
417             break;
418 
419         case kTlvSendApplicationData:
420             LogInfo("Application data len:%d, offset:%d", length, offset);
421             mAppDataReceiveCallback.InvokeIfSet(&GetInstance(), &aIncomingMessage, offset,
422                                                 MapEnum(mCurrentApplicationProtocol), mCurrentServiceName);
423             response = true;
424             error    = kErrorNone;
425             break;
426 
427         case kTlvDecommission:
428             error = HandleDecomission();
429             break;
430 
431         case kTlvPing:
432             error = HandlePing(aIncomingMessage, aOutgoingMessage, offset, length, response);
433             break;
434         case kTlvGetNetworkName:
435             error = HandleGetNetworkName(aOutgoingMessage, response);
436             break;
437         case kTlvGetDeviceId:
438             error = HandleGetDeviceId(aOutgoingMessage, response);
439             break;
440         case kTlvGetExtendedPanID:
441             error = HandleGetExtPanId(aOutgoingMessage, response);
442             break;
443         case kTlvGetProvisioningURL:
444             error = HandleGetProvisioningUrl(aOutgoingMessage, response);
445             break;
446         case kTlvPresentPskdHash:
447             error = HandlePresentPskdHash(aIncomingMessage, offset, length);
448             break;
449         case kTlvPresentPskcHash:
450             error = HandlePresentPskcHash(aIncomingMessage, offset, length);
451             break;
452         case kTlvPresentInstallCodeHash:
453             error = HandlePresentInstallCodeHash(aIncomingMessage, offset, length);
454             break;
455         case kTlvRequestRandomNumChallenge:
456             error = HandleRequestRandomNumberChallenge(aOutgoingMessage, response);
457             break;
458         case kTlvRequestPskdHash:
459             error = HandleRequestPskdHash(aIncomingMessage, aOutgoingMessage, offset, length, response);
460             break;
461         case kTlvGetCommissionerCertificate:
462             error = HandleGetCommissionerCertificate(aOutgoingMessage, response);
463             break;
464         default:
465             error = kErrorInvalidCommand;
466         }
467     }
468     if (!response)
469     {
470         StatusCode statusCode;
471 
472         switch (error)
473         {
474         case kErrorNone:
475             statusCode = kStatusSuccess;
476             break;
477 
478         case kErrorInvalidState:
479             statusCode = kStatusUndefined;
480             break;
481 
482         case kErrorParse:
483             statusCode = kStatusParseError;
484             break;
485 
486         case kErrorInvalidCommand:
487             statusCode = kStatusUnsupported;
488             break;
489 
490         case kErrorRejected:
491             statusCode = kStatusUnauthorized;
492             break;
493 
494         case kErrorNotImplemented:
495             statusCode = kStatusUnsupported;
496             break;
497 
498         case kErrorSecurity:
499             statusCode = kStatusHashError;
500             break;
501 
502         default:
503             statusCode = kStatusGeneralError;
504             break;
505         }
506 
507         SuccessOrExit(error = ot::Tlv::Append<ResponseWithStatusTlv>(aOutgoingMessage, statusCode));
508     }
509 
510 exit:
511     return error;
512 }
513 
HandleSetActiveOperationalDataset(const Message & aIncomingMessage,uint16_t aOffset,uint16_t aLength)514 Error TcatAgent::HandleSetActiveOperationalDataset(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength)
515 {
516     Dataset     dataset;
517     OffsetRange offsetRange;
518     Error       error;
519     uint8_t     buf[kCommissionerCertMaxLength];
520     size_t      bufLen = sizeof(buf);
521 
522     offsetRange.Init(aOffset, aLength);
523     SuccessOrExit(error = dataset.SetFrom(aIncomingMessage, offsetRange));
524     SuccessOrExit(error = dataset.ValidateTlvs());
525 
526     if (!CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mCommissioningFlags,
527                                              mDeviceAuthorizationField.mCommissioningFlags, &dataset))
528     {
529         error = kErrorRejected;
530         ExitNow();
531     }
532 
533     SuccessOrExit(error = Get<Ble::BleSecure>().GetPeerCertificateDer(buf, &bufLen, bufLen));
534     Get<Settings>().SaveTcatCommissionerCertificate(buf, static_cast<uint16_t>(bufLen));
535 
536     Get<ActiveDatasetManager>().SaveLocal(dataset);
537 
538 exit:
539     return error;
540 }
541 
HandleGetActiveOperationalDataset(Message & aOutgoingMessage,bool & aResponse)542 Error TcatAgent::HandleGetActiveOperationalDataset(Message &aOutgoingMessage, bool &aResponse)
543 {
544     Error         error = kErrorNone;
545     Dataset       dataset;
546     Dataset::Tlvs datasetTlvs;
547 
548     if (!CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mCommissioningFlags,
549                                              mDeviceAuthorizationField.mCommissioningFlags, &dataset))
550     {
551         error = kErrorRejected;
552         ExitNow();
553     }
554 
555     SuccessOrExit(error = Get<ActiveDatasetManager>().Read(datasetTlvs));
556     SuccessOrExit(
557         error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, datasetTlvs.mTlvs, datasetTlvs.mLength));
558     aResponse = true;
559 
560 exit:
561     return error;
562 }
563 
HandleGetCommissionerCertificate(Message & aOutgoingMessage,bool & aResponse)564 Error TcatAgent::HandleGetCommissionerCertificate(Message &aOutgoingMessage, bool &aResponse)
565 {
566     Error    error = kErrorNone;
567     Dataset  dataset;
568     uint8_t  buf[kCommissionerCertMaxLength];
569     uint16_t bufLen = sizeof(buf);
570 
571     if (!CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mCommissioningFlags,
572                                              mDeviceAuthorizationField.mCommissioningFlags, &dataset))
573     {
574         error = kErrorRejected;
575         ExitNow();
576     }
577 
578     VerifyOrExit(kErrorNone == Get<Settings>().ReadTcatCommissionerCertificate(buf, bufLen),
579                  error = kErrorInvalidState);
580     SuccessOrExit(error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, buf, bufLen));
581     aResponse = true;
582 
583 exit:
584     return error;
585 }
586 
HandleDecomission(void)587 Error TcatAgent::HandleDecomission(void)
588 {
589     Error         error = kErrorNone;
590     unsigned char buf[kCommissionerCertMaxLength];
591     size_t        bufLen = sizeof(buf);
592     Dataset       dataset;
593 
594     if (!CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mDecommissioningFlags,
595                                              mDeviceAuthorizationField.mDecommissioningFlags, &dataset))
596     {
597         error = kErrorRejected;
598         ExitNow();
599     }
600 
601     SuccessOrExit(error = Get<Ble::BleSecure>().GetPeerCertificateDer(buf, &bufLen, bufLen));
602     Get<Settings>().SaveTcatCommissionerCertificate(buf, static_cast<uint16_t>(bufLen));
603 
604     IgnoreReturnValue(otThreadSetEnabled(&GetInstance(), false));
605     Get<ActiveDatasetManager>().Clear();
606     Get<PendingDatasetManager>().Clear();
607 
608     IgnoreReturnValue(Get<Instance>().ErasePersistentInfo());
609 
610 #if !OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
611     {
612         NetworkKey networkKey;
613         networkKey.Clear();
614         Get<KeyManager>().SetNetworkKey(networkKey);
615     }
616 #endif
617 
618 exit:
619     return error;
620 }
621 
HandlePing(const Message & aIncomingMessage,Message & aOutgoingMessage,uint16_t aOffset,uint16_t aLength,bool & aResponse)622 Error TcatAgent::HandlePing(const Message &aIncomingMessage,
623                             Message       &aOutgoingMessage,
624                             uint16_t       aOffset,
625                             uint16_t       aLength,
626                             bool          &aResponse)
627 {
628     Error           error = kErrorNone;
629     ot::ExtendedTlv extTlv;
630     ot::Tlv         tlv;
631 
632     VerifyOrExit(aLength <= kPingPayloadMaxLength, error = kErrorParse);
633     if (aLength > ot::Tlv::kBaseTlvMaxLength)
634     {
635         extTlv.SetType(kTlvResponseWithPayload);
636         extTlv.SetLength(aLength);
637         SuccessOrExit(error = aOutgoingMessage.Append(extTlv));
638     }
639     else
640     {
641         tlv.SetType(kTlvResponseWithPayload);
642         tlv.SetLength(static_cast<uint8_t>(aLength));
643         SuccessOrExit(error = aOutgoingMessage.Append(tlv));
644     }
645 
646     SuccessOrExit(error = aOutgoingMessage.AppendBytesFromMessage(aIncomingMessage, aOffset, aLength));
647     aResponse = true;
648 
649 exit:
650     return error;
651 }
652 
HandleGetNetworkName(Message & aOutgoingMessage,bool & aResponse)653 Error TcatAgent::HandleGetNetworkName(Message &aOutgoingMessage, bool &aResponse)
654 {
655     Error             error    = kErrorNone;
656     MeshCoP::NameData nameData = Get<MeshCoP::NetworkNameManager>().GetNetworkName().GetAsData();
657 
658     VerifyOrExit(Get<ActiveDatasetManager>().IsCommissioned(), error = kErrorInvalidState);
659 #if !OPENTHREAD_CONFIG_ALLOW_EMPTY_NETWORK_NAME
660     VerifyOrExit(nameData.GetLength() > 0, error = kErrorInvalidState);
661 #endif
662 
663     SuccessOrExit(
664         error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, nameData.GetBuffer(), nameData.GetLength()));
665     aResponse = true;
666 
667 exit:
668     return error;
669 }
670 
HandleGetDeviceId(Message & aOutgoingMessage,bool & aResponse)671 Error TcatAgent::HandleGetDeviceId(Message &aOutgoingMessage, bool &aResponse)
672 {
673     const uint8_t  *deviceId;
674     uint16_t        length = 0;
675     Mac::ExtAddress eui64;
676     Error           error = kErrorNone;
677 
678     if (mVendorInfo->mGeneralDeviceId != nullptr)
679     {
680         length   = mVendorInfo->mGeneralDeviceId->mDeviceIdLen;
681         deviceId = mVendorInfo->mGeneralDeviceId->mDeviceId;
682     }
683 
684     if (length == 0)
685     {
686         Get<Radio>().GetIeeeEui64(eui64);
687 
688         length   = sizeof(Mac::ExtAddress);
689         deviceId = eui64.m8;
690     }
691 
692     SuccessOrExit(error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, deviceId, length));
693 
694     aResponse = true;
695 
696 exit:
697     return error;
698 }
699 
HandleGetExtPanId(Message & aOutgoingMessage,bool & aResponse)700 Error TcatAgent::HandleGetExtPanId(Message &aOutgoingMessage, bool &aResponse)
701 {
702     Error error;
703 
704     VerifyOrExit(Get<ActiveDatasetManager>().IsCommissioned(), error = kErrorInvalidState);
705 
706     SuccessOrExit(error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload,
707                                          &Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId(), sizeof(ExtendedPanId)));
708     aResponse = true;
709 
710 exit:
711     return error;
712 }
713 
HandleGetProvisioningUrl(Message & aOutgoingMessage,bool & aResponse)714 Error TcatAgent::HandleGetProvisioningUrl(Message &aOutgoingMessage, bool &aResponse)
715 {
716     Error    error = kErrorNone;
717     uint16_t length;
718 
719     VerifyOrExit(mVendorInfo->mProvisioningUrl != nullptr, error = kErrorInvalidState);
720 
721     length = StringLength(mVendorInfo->mProvisioningUrl, kProvisioningUrlMaxLength);
722     VerifyOrExit(length > 0 && length <= Tlv::kBaseTlvMaxLength, error = kErrorInvalidState);
723 
724     SuccessOrExit(error =
725                       Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, mVendorInfo->mProvisioningUrl, length));
726     aResponse = true;
727 
728 exit:
729     return error;
730 }
731 
HandlePresentPskdHash(const Message & aIncomingMessage,uint16_t aOffset,uint16_t aLength)732 Error TcatAgent::HandlePresentPskdHash(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength)
733 {
734     Error error = kErrorNone;
735 
736     VerifyOrExit(mVendorInfo->mPskdString != nullptr, error = kErrorSecurity);
737 
738     SuccessOrExit(error = VerifyHash(aIncomingMessage, aOffset, aLength, mVendorInfo->mPskdString,
739                                      StringLength(mVendorInfo->mPskdString, kMaxPskdLength)));
740     mPskdVerified = true;
741 
742 exit:
743     return error;
744 }
745 
HandlePresentPskcHash(const Message & aIncomingMessage,uint16_t aOffset,uint16_t aLength)746 Error TcatAgent::HandlePresentPskcHash(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength)
747 {
748     Error         error = kErrorNone;
749     Dataset::Info datasetInfo;
750     Pskc          pskc;
751 
752     VerifyOrExit(Get<ActiveDatasetManager>().Read(datasetInfo) == kErrorNone, error = kErrorSecurity);
753     VerifyOrExit(datasetInfo.IsPresent<Dataset::kPskc>(), error = kErrorSecurity);
754     pskc = datasetInfo.Get<Dataset::kPskc>();
755 
756     SuccessOrExit(error = VerifyHash(aIncomingMessage, aOffset, aLength, pskc.m8, Pskc::kSize));
757     mPskcVerified = true;
758 
759 exit:
760     return error;
761 }
762 
HandlePresentInstallCodeHash(const Message & aIncomingMessage,uint16_t aOffset,uint16_t aLength)763 Error TcatAgent::HandlePresentInstallCodeHash(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength)
764 {
765     Error error = kErrorNone;
766 
767     VerifyOrExit(mVendorInfo->mInstallCode != nullptr, error = kErrorSecurity);
768 
769     SuccessOrExit(error = VerifyHash(aIncomingMessage, aOffset, aLength, mVendorInfo->mInstallCode,
770                                      StringLength(mVendorInfo->mInstallCode, kInstallCodeMaxSize)));
771     mInstallCodeVerified = true;
772 
773 exit:
774     return error;
775 }
776 
HandleRequestRandomNumberChallenge(Message & aOutgoingMessage,bool & aResponse)777 Error TcatAgent::HandleRequestRandomNumberChallenge(Message &aOutgoingMessage, bool &aResponse)
778 {
779     Error error = kErrorNone;
780 
781     SuccessOrExit(error = Random::Crypto::Fill(mRandomChallenge));
782 
783     SuccessOrExit(
784         error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, &mRandomChallenge, sizeof(mRandomChallenge)));
785     aResponse = true;
786 exit:
787     return error;
788 }
789 
HandleRequestPskdHash(const Message & aIncomingMessage,Message & aOutgoingMessage,uint16_t aOffset,uint16_t aLength,bool & aResponse)790 Error TcatAgent::HandleRequestPskdHash(const Message &aIncomingMessage,
791                                        Message       &aOutgoingMessage,
792                                        uint16_t       aOffset,
793                                        uint16_t       aLength,
794                                        bool          &aResponse)
795 {
796     Error                    error             = kErrorNone;
797     uint64_t                 providedChallenge = 0;
798     Crypto::HmacSha256::Hash hash;
799 
800     VerifyOrExit(StringLength(mVendorInfo->mPskdString, kMaxPskdLength) != 0, error = kErrorFailed);
801     VerifyOrExit(aLength == sizeof(providedChallenge), error = kErrorParse);
802 
803     SuccessOrExit(error = aIncomingMessage.Read(aOffset, &providedChallenge, aLength));
804 
805     CalculateHash(providedChallenge, mVendorInfo->mPskdString, StringLength(mVendorInfo->mPskdString, kMaxPskdLength),
806                   hash);
807 
808     SuccessOrExit(error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, hash.GetBytes(),
809                                          Crypto::HmacSha256::Hash::kSize));
810     aResponse = true;
811 
812 exit:
813     return error;
814 }
815 
VerifyHash(const Message & aIncomingMessage,uint16_t aOffset,uint16_t aLength,const void * aBuf,size_t aBufLen)816 Error TcatAgent::VerifyHash(const Message &aIncomingMessage,
817                             uint16_t       aOffset,
818                             uint16_t       aLength,
819                             const void    *aBuf,
820                             size_t         aBufLen)
821 {
822     Error                    error = kErrorNone;
823     Crypto::HmacSha256::Hash hash;
824 
825     VerifyOrExit(aLength == Crypto::HmacSha256::Hash::kSize, error = kErrorSecurity);
826     VerifyOrExit(mRandomChallenge != 0, error = kErrorSecurity);
827 
828     CalculateHash(mRandomChallenge, reinterpret_cast<const char *>(aBuf), aBufLen, hash);
829 
830     VerifyOrExit(aIncomingMessage.Compare(aOffset, hash), error = kErrorSecurity);
831 
832 exit:
833     return error;
834 }
835 
CalculateHash(uint64_t aChallenge,const char * aBuf,size_t aBufLen,Crypto::HmacSha256::Hash & aHash)836 void TcatAgent::CalculateHash(uint64_t aChallenge, const char *aBuf, size_t aBufLen, Crypto::HmacSha256::Hash &aHash)
837 {
838     const mbedtls_asn1_buf &rawKey = Get<Ble::BleSecure>().GetOwnPublicKey();
839     Crypto::Key             cryptoKey;
840     Crypto::HmacSha256      hmac;
841 
842     cryptoKey.Set(reinterpret_cast<const uint8_t *>(aBuf), static_cast<uint16_t>(aBufLen));
843 
844     hmac.Start(cryptoKey);
845     hmac.Update(aChallenge);
846     hmac.Update(rawKey.p, static_cast<uint16_t>(rawKey.len));
847     hmac.Finish(aHash);
848 }
849 
HandleStartThreadInterface(void)850 Error TcatAgent::HandleStartThreadInterface(void)
851 {
852     Error         error;
853     Dataset::Info datasetInfo;
854 
855     VerifyOrExit(Get<ActiveDatasetManager>().Read(datasetInfo) == kErrorNone, error = kErrorInvalidState);
856     VerifyOrExit(datasetInfo.IsPresent<Dataset::kNetworkKey>(), error = kErrorInvalidState);
857 
858 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE
859     VerifyOrExit(!Get<Mac::LinkRaw>().IsEnabled(), error = kErrorInvalidState);
860 #endif
861 
862     Get<ThreadNetif>().Up();
863     error = Get<Mle::MleRouter>().Start();
864 
865 exit:
866     return error;
867 }
868 
SeralizeTcatAdvertisementTlv(uint8_t * aBuffer,uint16_t & aOffset,TcatAdvertisementTlvType aType,uint16_t aLength,const uint8_t * aValue)869 void SeralizeTcatAdvertisementTlv(uint8_t                 *aBuffer,
870                                   uint16_t                &aOffset,
871                                   TcatAdvertisementTlvType aType,
872                                   uint16_t                 aLength,
873                                   const uint8_t           *aValue)
874 {
875     aBuffer[aOffset++] = static_cast<uint8_t>(aType << 4 | (aLength & 0xf));
876     memcpy(aBuffer + aOffset, aValue, aLength);
877     aOffset += aLength;
878 }
879 
GetAdvertisementData(uint16_t & aLen,uint8_t * aAdvertisementData)880 Error TcatAgent::GetAdvertisementData(uint16_t &aLen, uint8_t *aAdvertisementData)
881 {
882     Error                 error = kErrorNone;
883     DeviceTypeAndStatus   tas;
884     otBleLinkCapabilities caps;
885 
886     VerifyOrExit(mVendorInfo != nullptr && aAdvertisementData != nullptr, error = kErrorInvalidArgs);
887 
888     aLen = 0;
889 
890     LittleEndian::WriteUint16(OT_TOBLE_SERVICE_UUID, aAdvertisementData);
891     aLen += sizeof(uint16_t);
892     aAdvertisementData[2] = OPENTHREAD_CONFIG_THREAD_VERSION << 4 | OT_TCAT_OPCODE;
893     aLen++;
894 
895     if (mVendorInfo->mAdvertisedDeviceIds != nullptr)
896     {
897         for (uint8_t i = 0; mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY; i++)
898         {
899             switch (MapEnum(mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdType))
900             {
901             case kTcatDeviceIdOui24:
902                 SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvVendorOui24,
903                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen,
904                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId);
905                 break;
906             case kTcatDeviceIdOui36:
907                 SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvVendorOui36,
908                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen,
909                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId);
910                 break;
911             case kTcatDeviceIdDiscriminator:
912                 SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvDeviceDiscriminator,
913                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen,
914                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId);
915                 break;
916             case kTcatDeviceIdIanaPen:
917                 SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvVendorIanaPen,
918                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen,
919                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId);
920                 break;
921             default:
922                 break;
923             }
924         }
925     }
926 
927     otPlatBleGetLinkCapabilities(&GetInstance(), &caps);
928 
929     if (caps.mGattNotifications || caps.mL2CapDirect)
930     {
931         SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvBleLinkCapabilities, kTlvBleLinkCapabilitiesLength,
932                                      reinterpret_cast<uint8_t *>(&caps));
933     }
934 
935     tas.mRsv                 = 0;
936     tas.mMultiradioSupport   = otPlatBleSupportsMultiRadio(&GetInstance());
937     tas.mIsCommisionned      = Get<ActiveDatasetManager>().IsCommissioned();
938     tas.mThreadNetworkActive = Get<Mle::Mle>().IsAttached();
939     tas.mDeviceType          = Get<Mle::Mle>().GetDeviceMode().IsFullThreadDevice();
940     tas.mRxOnWhenIdle        = Get<Mle::Mle>().GetDeviceMode().IsRxOnWhenIdle();
941 
942 #if OPENTHREAD_FTD && (OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE || OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE || \
943                        OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE)
944     tas.mIsBorderRouter = true;
945 #else
946     tas.mIsBorderRouter = false;
947 #endif
948 
949     SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvDeviceTypeAndStatus, kTlvDeviceTypeAndStatusLength,
950                                  reinterpret_cast<uint8_t *>(&tas));
951     OT_ASSERT(aLen <= OT_TCAT_ADVERTISEMENT_MAX_LEN);
952 
953 exit:
954     return error;
955 }
956 
957 } // namespace MeshCoP
958 } // namespace ot
959 
960 #endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
961