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