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 kTlvStartThreadInterface:
408             error = HandleStartThreadInterface();
409             break;
410 
411         case kTlvStopThreadInterface:
412             error = otThreadSetEnabled(&GetInstance(), false);
413             break;
414 
415         case kTlvSendApplicationData:
416             LogInfo("Application data len:%d, offset:%d", length, offset);
417             mAppDataReceiveCallback.InvokeIfSet(&GetInstance(), &aIncomingMessage, offset,
418                                                 MapEnum(mCurrentApplicationProtocol), mCurrentServiceName);
419             response = true;
420             error    = kErrorNone;
421             break;
422 
423         case kTlvDecommission:
424             error = HandleDecomission();
425             break;
426 
427         case kTlvPing:
428             error = HandlePing(aIncomingMessage, aOutgoingMessage, offset, length, response);
429             break;
430         case kTlvGetNetworkName:
431             error = HandleGetNetworkName(aOutgoingMessage, response);
432             break;
433         case kTlvGetDeviceId:
434             error = HandleGetDeviceId(aOutgoingMessage, response);
435             break;
436         case kTlvGetExtendedPanID:
437             error = HandleGetExtPanId(aOutgoingMessage, response);
438             break;
439         case kTlvGetProvisioningURL:
440             error = HandleGetProvisioningUrl(aOutgoingMessage, response);
441             break;
442         case kTlvPresentPskdHash:
443             error = HandlePresentPskdHash(aIncomingMessage, offset, length);
444             break;
445         case kTlvPresentPskcHash:
446             error = HandlePresentPskcHash(aIncomingMessage, offset, length);
447             break;
448         case kTlvPresentInstallCodeHash:
449             error = HandlePresentInstallCodeHash(aIncomingMessage, offset, length);
450             break;
451         case kTlvRequestRandomNumChallenge:
452             error = HandleRequestRandomNumberChallenge(aOutgoingMessage, response);
453             break;
454         case kTlvRequestPskdHash:
455             error = HandleRequestPskdHash(aIncomingMessage, aOutgoingMessage, offset, length, response);
456             break;
457         default:
458             error = kErrorInvalidCommand;
459         }
460     }
461     if (!response)
462     {
463         StatusCode statusCode;
464 
465         switch (error)
466         {
467         case kErrorNone:
468             statusCode = kStatusSuccess;
469             break;
470 
471         case kErrorInvalidState:
472             statusCode = kStatusUndefined;
473             break;
474 
475         case kErrorParse:
476             statusCode = kStatusParseError;
477             break;
478 
479         case kErrorInvalidCommand:
480             statusCode = kStatusUnsupported;
481             break;
482 
483         case kErrorRejected:
484             statusCode = kStatusUnauthorized;
485             break;
486 
487         case kErrorNotImplemented:
488             statusCode = kStatusUnsupported;
489             break;
490 
491         case kErrorSecurity:
492             statusCode = kStatusHashError;
493             break;
494 
495         default:
496             statusCode = kStatusGeneralError;
497             break;
498         }
499 
500         SuccessOrExit(error = ot::Tlv::Append<ResponseWithStatusTlv>(aOutgoingMessage, statusCode));
501     }
502 
503 exit:
504     return error;
505 }
506 
HandleSetActiveOperationalDataset(const Message & aIncomingMessage,uint16_t aOffset,uint16_t aLength)507 Error TcatAgent::HandleSetActiveOperationalDataset(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength)
508 {
509     Dataset     dataset;
510     OffsetRange offsetRange;
511     Error       error;
512 
513     offsetRange.Init(aOffset, aLength);
514     SuccessOrExit(error = dataset.SetFrom(aIncomingMessage, offsetRange));
515     SuccessOrExit(error = dataset.ValidateTlvs());
516 
517     if (!CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mApplicationFlags,
518                                              mDeviceAuthorizationField.mApplicationFlags, &dataset))
519     {
520         error = kErrorRejected;
521         ExitNow();
522     }
523 
524     Get<ActiveDatasetManager>().SaveLocal(dataset);
525 
526 exit:
527     return error;
528 }
529 
HandleDecomission(void)530 Error TcatAgent::HandleDecomission(void)
531 {
532     Error error = kErrorNone;
533 
534     IgnoreReturnValue(otThreadSetEnabled(&GetInstance(), false));
535     Get<ActiveDatasetManager>().Clear();
536     Get<PendingDatasetManager>().Clear();
537 
538     error = Get<Instance>().ErasePersistentInfo();
539 
540 #if !OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
541     {
542         NetworkKey networkKey;
543         networkKey.Clear();
544         Get<KeyManager>().SetNetworkKey(networkKey);
545     }
546 #endif
547 
548     return error;
549 }
550 
HandlePing(const Message & aIncomingMessage,Message & aOutgoingMessage,uint16_t aOffset,uint16_t aLength,bool & aResponse)551 Error TcatAgent::HandlePing(const Message &aIncomingMessage,
552                             Message       &aOutgoingMessage,
553                             uint16_t       aOffset,
554                             uint16_t       aLength,
555                             bool          &aResponse)
556 {
557     Error           error = kErrorNone;
558     ot::ExtendedTlv extTlv;
559     ot::Tlv         tlv;
560 
561     VerifyOrExit(aLength <= kPingPayloadMaxLength, error = kErrorParse);
562     if (aLength > ot::Tlv::kBaseTlvMaxLength)
563     {
564         extTlv.SetType(kTlvResponseWithPayload);
565         extTlv.SetLength(aLength);
566         SuccessOrExit(error = aOutgoingMessage.Append(extTlv));
567     }
568     else
569     {
570         tlv.SetType(kTlvResponseWithPayload);
571         tlv.SetLength(static_cast<uint8_t>(aLength));
572         SuccessOrExit(error = aOutgoingMessage.Append(tlv));
573     }
574 
575     SuccessOrExit(error = aOutgoingMessage.AppendBytesFromMessage(aIncomingMessage, aOffset, aLength));
576     aResponse = true;
577 
578 exit:
579     return error;
580 }
581 
HandleGetNetworkName(Message & aOutgoingMessage,bool & aResponse)582 Error TcatAgent::HandleGetNetworkName(Message &aOutgoingMessage, bool &aResponse)
583 {
584     Error             error    = kErrorNone;
585     MeshCoP::NameData nameData = Get<MeshCoP::NetworkNameManager>().GetNetworkName().GetAsData();
586 
587     VerifyOrExit(Get<ActiveDatasetManager>().IsCommissioned(), error = kErrorInvalidState);
588 #if !OPENTHREAD_CONFIG_ALLOW_EMPTY_NETWORK_NAME
589     VerifyOrExit(nameData.GetLength() > 0, error = kErrorInvalidState);
590 #endif
591 
592     SuccessOrExit(
593         error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, nameData.GetBuffer(), nameData.GetLength()));
594     aResponse = true;
595 
596 exit:
597     return error;
598 }
599 
HandleGetDeviceId(Message & aOutgoingMessage,bool & aResponse)600 Error TcatAgent::HandleGetDeviceId(Message &aOutgoingMessage, bool &aResponse)
601 {
602     const uint8_t  *deviceId;
603     uint16_t        length = 0;
604     Mac::ExtAddress eui64;
605     Error           error = kErrorNone;
606 
607     if (mVendorInfo->mGeneralDeviceId != nullptr)
608     {
609         length   = mVendorInfo->mGeneralDeviceId->mDeviceIdLen;
610         deviceId = mVendorInfo->mGeneralDeviceId->mDeviceId;
611     }
612 
613     if (length == 0)
614     {
615         Get<Radio>().GetIeeeEui64(eui64);
616 
617         length   = sizeof(Mac::ExtAddress);
618         deviceId = eui64.m8;
619     }
620 
621     SuccessOrExit(error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, deviceId, length));
622 
623     aResponse = true;
624 
625 exit:
626     return error;
627 }
628 
HandleGetExtPanId(Message & aOutgoingMessage,bool & aResponse)629 Error TcatAgent::HandleGetExtPanId(Message &aOutgoingMessage, bool &aResponse)
630 {
631     Error error;
632 
633     VerifyOrExit(Get<ActiveDatasetManager>().IsCommissioned(), error = kErrorInvalidState);
634 
635     SuccessOrExit(error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload,
636                                          &Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId(), sizeof(ExtendedPanId)));
637     aResponse = true;
638 
639 exit:
640     return error;
641 }
642 
HandleGetProvisioningUrl(Message & aOutgoingMessage,bool & aResponse)643 Error TcatAgent::HandleGetProvisioningUrl(Message &aOutgoingMessage, bool &aResponse)
644 {
645     Error    error = kErrorNone;
646     uint16_t length;
647 
648     VerifyOrExit(mVendorInfo->mProvisioningUrl != nullptr, error = kErrorInvalidState);
649 
650     length = StringLength(mVendorInfo->mProvisioningUrl, kProvisioningUrlMaxLength);
651     VerifyOrExit(length > 0 && length <= Tlv::kBaseTlvMaxLength, error = kErrorInvalidState);
652 
653     SuccessOrExit(error =
654                       Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, mVendorInfo->mProvisioningUrl, length));
655     aResponse = true;
656 
657 exit:
658     return error;
659 }
660 
HandlePresentPskdHash(const Message & aIncomingMessage,uint16_t aOffset,uint16_t aLength)661 Error TcatAgent::HandlePresentPskdHash(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength)
662 {
663     Error error = kErrorNone;
664 
665     VerifyOrExit(mVendorInfo->mPskdString != nullptr, error = kErrorSecurity);
666 
667     SuccessOrExit(error = VerifyHash(aIncomingMessage, aOffset, aLength, mVendorInfo->mPskdString,
668                                      StringLength(mVendorInfo->mPskdString, kMaxPskdLength)));
669     mPskdVerified = true;
670 
671 exit:
672     return error;
673 }
674 
HandlePresentPskcHash(const Message & aIncomingMessage,uint16_t aOffset,uint16_t aLength)675 Error TcatAgent::HandlePresentPskcHash(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength)
676 {
677     Error         error = kErrorNone;
678     Dataset::Info datasetInfo;
679     Pskc          pskc;
680 
681     VerifyOrExit(Get<ActiveDatasetManager>().Read(datasetInfo) == kErrorNone, error = kErrorSecurity);
682     VerifyOrExit(datasetInfo.IsPresent<Dataset::kPskc>(), error = kErrorSecurity);
683     pskc = datasetInfo.Get<Dataset::kPskc>();
684 
685     SuccessOrExit(error = VerifyHash(aIncomingMessage, aOffset, aLength, pskc.m8, Pskc::kSize));
686     mPskcVerified = true;
687 
688 exit:
689     return error;
690 }
691 
HandlePresentInstallCodeHash(const Message & aIncomingMessage,uint16_t aOffset,uint16_t aLength)692 Error TcatAgent::HandlePresentInstallCodeHash(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength)
693 {
694     Error error = kErrorNone;
695 
696     VerifyOrExit(mVendorInfo->mInstallCode != nullptr, error = kErrorSecurity);
697 
698     SuccessOrExit(error = VerifyHash(aIncomingMessage, aOffset, aLength, mVendorInfo->mInstallCode,
699                                      StringLength(mVendorInfo->mInstallCode, kInstallCodeMaxSize)));
700     mInstallCodeVerified = true;
701 
702 exit:
703     return error;
704 }
705 
HandleRequestRandomNumberChallenge(Message & aOutgoingMessage,bool & aResponse)706 Error TcatAgent::HandleRequestRandomNumberChallenge(Message &aOutgoingMessage, bool &aResponse)
707 {
708     Error error = kErrorNone;
709 
710     SuccessOrExit(error = Random::Crypto::Fill(mRandomChallenge));
711 
712     SuccessOrExit(
713         error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, &mRandomChallenge, sizeof(mRandomChallenge)));
714     aResponse = true;
715 exit:
716     return error;
717 }
718 
HandleRequestPskdHash(const Message & aIncomingMessage,Message & aOutgoingMessage,uint16_t aOffset,uint16_t aLength,bool & aResponse)719 Error TcatAgent::HandleRequestPskdHash(const Message &aIncomingMessage,
720                                        Message       &aOutgoingMessage,
721                                        uint16_t       aOffset,
722                                        uint16_t       aLength,
723                                        bool          &aResponse)
724 {
725     Error                    error             = kErrorNone;
726     uint64_t                 providedChallenge = 0;
727     Crypto::HmacSha256::Hash hash;
728 
729     VerifyOrExit(StringLength(mVendorInfo->mPskdString, kMaxPskdLength) != 0, error = kErrorFailed);
730     VerifyOrExit(aLength == sizeof(providedChallenge), error = kErrorParse);
731 
732     SuccessOrExit(error = aIncomingMessage.Read(aOffset, &providedChallenge, aLength));
733 
734     CalculateHash(providedChallenge, mVendorInfo->mPskdString, StringLength(mVendorInfo->mPskdString, kMaxPskdLength),
735                   hash);
736 
737     SuccessOrExit(error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, hash.GetBytes(),
738                                          Crypto::HmacSha256::Hash::kSize));
739     aResponse = true;
740 
741 exit:
742     return error;
743 }
744 
VerifyHash(const Message & aIncomingMessage,uint16_t aOffset,uint16_t aLength,const void * aBuf,size_t aBufLen)745 Error TcatAgent::VerifyHash(const Message &aIncomingMessage,
746                             uint16_t       aOffset,
747                             uint16_t       aLength,
748                             const void    *aBuf,
749                             size_t         aBufLen)
750 {
751     Error                    error = kErrorNone;
752     Crypto::HmacSha256::Hash hash;
753 
754     VerifyOrExit(aLength == Crypto::HmacSha256::Hash::kSize, error = kErrorSecurity);
755     VerifyOrExit(mRandomChallenge != 0, error = kErrorSecurity);
756 
757     CalculateHash(mRandomChallenge, reinterpret_cast<const char *>(aBuf), aBufLen, hash);
758 
759     VerifyOrExit(aIncomingMessage.Compare(aOffset, hash), error = kErrorSecurity);
760 
761 exit:
762     return error;
763 }
764 
CalculateHash(uint64_t aChallenge,const char * aBuf,size_t aBufLen,Crypto::HmacSha256::Hash & aHash)765 void TcatAgent::CalculateHash(uint64_t aChallenge, const char *aBuf, size_t aBufLen, Crypto::HmacSha256::Hash &aHash)
766 {
767     const mbedtls_asn1_buf &rawKey = Get<Ble::BleSecure>().GetOwnPublicKey();
768     Crypto::Key             cryptoKey;
769     Crypto::HmacSha256      hmac;
770 
771     cryptoKey.Set(reinterpret_cast<const uint8_t *>(aBuf), static_cast<uint16_t>(aBufLen));
772 
773     hmac.Start(cryptoKey);
774     hmac.Update(aChallenge);
775     hmac.Update(rawKey.p, static_cast<uint16_t>(rawKey.len));
776     hmac.Finish(aHash);
777 }
778 
HandleStartThreadInterface(void)779 Error TcatAgent::HandleStartThreadInterface(void)
780 {
781     Error         error;
782     Dataset::Info datasetInfo;
783 
784     VerifyOrExit(Get<ActiveDatasetManager>().Read(datasetInfo) == kErrorNone, error = kErrorInvalidState);
785     VerifyOrExit(datasetInfo.IsPresent<Dataset::kNetworkKey>(), error = kErrorInvalidState);
786 
787 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE
788     VerifyOrExit(!Get<Mac::LinkRaw>().IsEnabled(), error = kErrorInvalidState);
789 #endif
790 
791     Get<ThreadNetif>().Up();
792     error = Get<Mle::MleRouter>().Start();
793 
794 exit:
795     return error;
796 }
797 
SeralizeTcatAdvertisementTlv(uint8_t * aBuffer,uint16_t & aOffset,TcatAdvertisementTlvType aType,uint16_t aLength,const uint8_t * aValue)798 void SeralizeTcatAdvertisementTlv(uint8_t                 *aBuffer,
799                                   uint16_t                &aOffset,
800                                   TcatAdvertisementTlvType aType,
801                                   uint16_t                 aLength,
802                                   const uint8_t           *aValue)
803 {
804     aBuffer[aOffset++] = static_cast<uint8_t>(aType << 4 | (aLength & 0xf));
805     memcpy(aBuffer + aOffset, aValue, aLength);
806     aOffset += aLength;
807 }
808 
GetAdvertisementData(uint16_t & aLen,uint8_t * aAdvertisementData)809 Error TcatAgent::GetAdvertisementData(uint16_t &aLen, uint8_t *aAdvertisementData)
810 {
811     Error                 error = kErrorNone;
812     DeviceTypeAndStatus   tas;
813     otBleLinkCapabilities caps;
814 
815     VerifyOrExit(mVendorInfo != nullptr && aAdvertisementData != nullptr, error = kErrorInvalidArgs);
816 
817     aLen = 0;
818 
819     LittleEndian::WriteUint16(OT_TOBLE_SERVICE_UUID, aAdvertisementData);
820     aLen += sizeof(uint16_t);
821     aAdvertisementData[2] = OPENTHREAD_CONFIG_THREAD_VERSION << 4 | OT_TCAT_OPCODE;
822     aLen++;
823 
824     if (mVendorInfo->mAdvertisedDeviceIds != nullptr)
825     {
826         for (uint8_t i = 0; mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY; i++)
827         {
828             switch (MapEnum(mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdType))
829             {
830             case kTcatDeviceIdOui24:
831                 SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvVendorOui24,
832                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen,
833                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId);
834                 break;
835             case kTcatDeviceIdOui36:
836                 SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvVendorOui36,
837                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen,
838                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId);
839                 break;
840             case kTcatDeviceIdDiscriminator:
841                 SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvDeviceDiscriminator,
842                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen,
843                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId);
844                 break;
845             case kTcatDeviceIdIanaPen:
846                 SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvVendorIanaPen,
847                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen,
848                                              mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId);
849                 break;
850             default:
851                 break;
852             }
853         }
854     }
855 
856     otPlatBleGetLinkCapabilities(&GetInstance(), &caps);
857 
858     if (caps.mGattNotifications || caps.mL2CapDirect)
859     {
860         SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvBleLinkCapabilities, kTlvBleLinkCapabilitiesLength,
861                                      reinterpret_cast<uint8_t *>(&caps));
862     }
863 
864     tas.mRsv                 = 0;
865     tas.mMultiradioSupport   = otPlatBleSupportsMultiRadio(&GetInstance());
866     tas.mIsCommisionned      = Get<ActiveDatasetManager>().IsCommissioned();
867     tas.mThreadNetworkActive = Get<Mle::Mle>().IsAttached();
868     tas.mDeviceType          = Get<Mle::Mle>().GetDeviceMode().IsFullThreadDevice();
869     tas.mRxOnWhenIdle        = Get<Mle::Mle>().GetDeviceMode().IsRxOnWhenIdle();
870 
871 #if OPENTHREAD_FTD && (OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE || OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE || \
872                        OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE)
873     tas.mIsBorderRouter = true;
874 #else
875     tas.mIsBorderRouter = false;
876 #endif
877 
878     SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvDeviceTypeAndStatus, kTlvDeviceTypeAndStatusLength,
879                                  reinterpret_cast<uint8_t *>(&tas));
880     OT_ASSERT(aLen <= OT_TCAT_ADVERTISEMENT_MAX_LEN);
881 
882 exit:
883     return error;
884 }
885 
886 } // namespace MeshCoP
887 } // namespace ot
888 
889 #endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
890