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