1 /*
2 * Copyright (c) 2016, 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 common methods for manipulating MeshCoP Datasets.
32 *
33 */
34
35 #include "dataset.hpp"
36
37 #include <stdio.h>
38
39 #include "common/code_utils.hpp"
40 #include "common/encoding.hpp"
41 #include "common/locator_getters.hpp"
42 #include "common/log.hpp"
43 #include "instance/instance.hpp"
44 #include "mac/mac_types.hpp"
45 #include "meshcop/meshcop_tlvs.hpp"
46 #include "meshcop/timestamp.hpp"
47 #include "thread/mle_tlvs.hpp"
48
49 namespace ot {
50 namespace MeshCoP {
51
52 RegisterLogModule("Dataset");
53
GenerateRandom(Instance & aInstance)54 Error Dataset::Info::GenerateRandom(Instance &aInstance)
55 {
56 Error error;
57 Mac::ChannelMask supportedChannels = aInstance.Get<Mac::Mac>().GetSupportedChannelMask();
58 Mac::ChannelMask preferredChannels(aInstance.Get<Radio>().GetPreferredChannelMask());
59
60 // If the preferred channel mask is not empty, select a random
61 // channel from it, otherwise choose one from the supported
62 // channel mask.
63
64 preferredChannels.Intersect(supportedChannels);
65
66 if (preferredChannels.IsEmpty())
67 {
68 preferredChannels = supportedChannels;
69 }
70
71 Clear();
72
73 mActiveTimestamp.mSeconds = 1;
74 mActiveTimestamp.mTicks = 0;
75 mActiveTimestamp.mAuthoritative = false;
76 mChannel = preferredChannels.ChooseRandomChannel();
77 mChannelMask = supportedChannels.GetMask();
78 mPanId = Mac::GenerateRandomPanId();
79 AsCoreType(&mSecurityPolicy).SetToDefault();
80
81 SuccessOrExit(error = AsCoreType(&mNetworkKey).GenerateRandom());
82 SuccessOrExit(error = AsCoreType(&mPskc).GenerateRandom());
83 SuccessOrExit(error = Random::Crypto::Fill(mExtendedPanId));
84 SuccessOrExit(error = AsCoreType(&mMeshLocalPrefix).GenerateRandomUla());
85
86 snprintf(mNetworkName.m8, sizeof(mNetworkName), "%s-%04x", NetworkName::kNetworkNameInit, mPanId);
87
88 mComponents.mIsActiveTimestampPresent = true;
89 mComponents.mIsNetworkKeyPresent = true;
90 mComponents.mIsNetworkNamePresent = true;
91 mComponents.mIsExtendedPanIdPresent = true;
92 mComponents.mIsMeshLocalPrefixPresent = true;
93 mComponents.mIsPanIdPresent = true;
94 mComponents.mIsChannelPresent = true;
95 mComponents.mIsPskcPresent = true;
96 mComponents.mIsSecurityPolicyPresent = true;
97 mComponents.mIsChannelMaskPresent = true;
98
99 exit:
100 return error;
101 }
102
IsSubsetOf(const Info & aOther) const103 bool Dataset::Info::IsSubsetOf(const Info &aOther) const
104 {
105 bool isSubset = false;
106
107 if (IsNetworkKeyPresent())
108 {
109 VerifyOrExit(aOther.IsNetworkKeyPresent() && GetNetworkKey() == aOther.GetNetworkKey());
110 }
111
112 if (IsNetworkNamePresent())
113 {
114 VerifyOrExit(aOther.IsNetworkNamePresent() && GetNetworkName() == aOther.GetNetworkName());
115 }
116
117 if (IsExtendedPanIdPresent())
118 {
119 VerifyOrExit(aOther.IsExtendedPanIdPresent() && GetExtendedPanId() == aOther.GetExtendedPanId());
120 }
121
122 if (IsMeshLocalPrefixPresent())
123 {
124 VerifyOrExit(aOther.IsMeshLocalPrefixPresent() && GetMeshLocalPrefix() == aOther.GetMeshLocalPrefix());
125 }
126
127 if (IsPanIdPresent())
128 {
129 VerifyOrExit(aOther.IsPanIdPresent() && GetPanId() == aOther.GetPanId());
130 }
131
132 if (IsChannelPresent())
133 {
134 VerifyOrExit(aOther.IsChannelPresent() && GetChannel() == aOther.GetChannel());
135 }
136
137 if (IsPskcPresent())
138 {
139 VerifyOrExit(aOther.IsPskcPresent() && GetPskc() == aOther.GetPskc());
140 }
141
142 if (IsSecurityPolicyPresent())
143 {
144 VerifyOrExit(aOther.IsSecurityPolicyPresent() && GetSecurityPolicy() == aOther.GetSecurityPolicy());
145 }
146
147 if (IsChannelMaskPresent())
148 {
149 VerifyOrExit(aOther.IsChannelMaskPresent() && GetChannelMask() == aOther.GetChannelMask());
150 }
151
152 isSubset = true;
153
154 exit:
155 return isSubset;
156 }
157
Dataset(void)158 Dataset::Dataset(void)
159 : mUpdateTime(0)
160 , mLength(0)
161 {
162 memset(mTlvs, 0, sizeof(mTlvs));
163 }
164
Clear(void)165 void Dataset::Clear(void) { mLength = 0; }
166
IsValid(void) const167 bool Dataset::IsValid(void) const
168 {
169 bool rval = true;
170 const Tlv *end = GetTlvsEnd();
171
172 for (const Tlv *cur = GetTlvsStart(); cur < end; cur = cur->GetNext())
173 {
174 VerifyOrExit(!cur->IsExtended() && (cur + 1) <= end && cur->GetNext() <= end && Tlv::IsValid(*cur),
175 rval = false);
176 }
177
178 exit:
179 return rval;
180 }
181
FindTlv(Tlv::Type aType) const182 const Tlv *Dataset::FindTlv(Tlv::Type aType) const { return As<Tlv>(Tlv::FindTlv(mTlvs, mLength, aType)); }
183
ConvertTo(Info & aDatasetInfo) const184 void Dataset::ConvertTo(Info &aDatasetInfo) const
185 {
186 aDatasetInfo.Clear();
187
188 for (const Tlv *cur = GetTlvsStart(); cur < GetTlvsEnd(); cur = cur->GetNext())
189 {
190 switch (cur->GetType())
191 {
192 case Tlv::kActiveTimestamp:
193 aDatasetInfo.SetActiveTimestamp(cur->ReadValueAs<ActiveTimestampTlv>());
194 break;
195
196 case Tlv::kChannel:
197 aDatasetInfo.SetChannel(cur->ReadValueAs<ChannelTlv>().GetChannel());
198 break;
199
200 case Tlv::kChannelMask:
201 {
202 uint32_t mask;
203
204 if (As<ChannelMaskTlv>(cur)->ReadChannelMask(mask) == kErrorNone)
205 {
206 aDatasetInfo.SetChannelMask(mask);
207 }
208
209 break;
210 }
211
212 case Tlv::kDelayTimer:
213 aDatasetInfo.SetDelay(cur->ReadValueAs<DelayTimerTlv>());
214 break;
215
216 case Tlv::kExtendedPanId:
217 aDatasetInfo.SetExtendedPanId(cur->ReadValueAs<ExtendedPanIdTlv>());
218 break;
219
220 case Tlv::kMeshLocalPrefix:
221 aDatasetInfo.SetMeshLocalPrefix(cur->ReadValueAs<MeshLocalPrefixTlv>());
222 break;
223
224 case Tlv::kNetworkKey:
225 aDatasetInfo.SetNetworkKey(cur->ReadValueAs<NetworkKeyTlv>());
226 break;
227
228 case Tlv::kNetworkName:
229 aDatasetInfo.SetNetworkName(As<NetworkNameTlv>(cur)->GetNetworkName());
230 break;
231
232 case Tlv::kPanId:
233 aDatasetInfo.SetPanId(cur->ReadValueAs<PanIdTlv>());
234 break;
235
236 case Tlv::kPendingTimestamp:
237 aDatasetInfo.SetPendingTimestamp(cur->ReadValueAs<PendingTimestampTlv>());
238 break;
239
240 case Tlv::kPskc:
241 aDatasetInfo.SetPskc(cur->ReadValueAs<PskcTlv>());
242 break;
243
244 case Tlv::kSecurityPolicy:
245 aDatasetInfo.SetSecurityPolicy(As<SecurityPolicyTlv>(cur)->GetSecurityPolicy());
246 break;
247
248 default:
249 break;
250 }
251 }
252 }
253
ConvertTo(otOperationalDatasetTlvs & aDataset) const254 void Dataset::ConvertTo(otOperationalDatasetTlvs &aDataset) const
255 {
256 memcpy(aDataset.mTlvs, mTlvs, mLength);
257 aDataset.mLength = static_cast<uint8_t>(mLength);
258 }
259
Set(Type aType,const Dataset & aDataset)260 void Dataset::Set(Type aType, const Dataset &aDataset)
261 {
262 memcpy(mTlvs, aDataset.mTlvs, aDataset.mLength);
263 mLength = aDataset.mLength;
264
265 if (aType == kActive)
266 {
267 RemoveTlv(Tlv::kPendingTimestamp);
268 RemoveTlv(Tlv::kDelayTimer);
269 }
270
271 mUpdateTime = aDataset.GetUpdateTime();
272 }
273
SetFrom(const otOperationalDatasetTlvs & aDataset)274 void Dataset::SetFrom(const otOperationalDatasetTlvs &aDataset)
275 {
276 mLength = aDataset.mLength;
277 memcpy(mTlvs, aDataset.mTlvs, mLength);
278 }
279
SetFrom(const Info & aDatasetInfo)280 Error Dataset::SetFrom(const Info &aDatasetInfo)
281 {
282 Error error = kErrorNone;
283
284 if (aDatasetInfo.IsActiveTimestampPresent())
285 {
286 Timestamp activeTimestamp;
287
288 aDatasetInfo.GetActiveTimestamp(activeTimestamp);
289 IgnoreError(Write<ActiveTimestampTlv>(activeTimestamp));
290 }
291
292 if (aDatasetInfo.IsPendingTimestampPresent())
293 {
294 Timestamp pendingTimestamp;
295
296 aDatasetInfo.GetPendingTimestamp(pendingTimestamp);
297 IgnoreError(Write<PendingTimestampTlv>(pendingTimestamp));
298 }
299
300 if (aDatasetInfo.IsDelayPresent())
301 {
302 IgnoreError(Write<DelayTimerTlv>(aDatasetInfo.GetDelay()));
303 }
304
305 if (aDatasetInfo.IsChannelPresent())
306 {
307 ChannelTlvValue channelValue;
308
309 channelValue.SetChannelAndPage(aDatasetInfo.GetChannel());
310 IgnoreError(Write<ChannelTlv>(channelValue));
311 }
312
313 if (aDatasetInfo.IsChannelMaskPresent())
314 {
315 ChannelMaskTlv::Value value;
316
317 ChannelMaskTlv::PrepareValue(value, aDatasetInfo.GetChannelMask());
318 IgnoreError(WriteTlv(Tlv::kChannelMask, value.mData, value.mLength));
319 }
320
321 if (aDatasetInfo.IsExtendedPanIdPresent())
322 {
323 IgnoreError(Write<ExtendedPanIdTlv>(aDatasetInfo.GetExtendedPanId()));
324 }
325
326 if (aDatasetInfo.IsMeshLocalPrefixPresent())
327 {
328 IgnoreError(Write<MeshLocalPrefixTlv>(aDatasetInfo.GetMeshLocalPrefix()));
329 }
330
331 if (aDatasetInfo.IsNetworkKeyPresent())
332 {
333 IgnoreError(Write<NetworkKeyTlv>(aDatasetInfo.GetNetworkKey()));
334 }
335
336 if (aDatasetInfo.IsNetworkNamePresent())
337 {
338 NameData nameData = aDatasetInfo.GetNetworkName().GetAsData();
339
340 IgnoreError(WriteTlv(Tlv::kNetworkName, nameData.GetBuffer(), nameData.GetLength()));
341 }
342
343 if (aDatasetInfo.IsPanIdPresent())
344 {
345 IgnoreError(Write<PanIdTlv>(aDatasetInfo.GetPanId()));
346 }
347
348 if (aDatasetInfo.IsPskcPresent())
349 {
350 IgnoreError(Write<PskcTlv>(aDatasetInfo.GetPskc()));
351 }
352
353 if (aDatasetInfo.IsSecurityPolicyPresent())
354 {
355 SecurityPolicyTlv tlv;
356
357 tlv.Init();
358 tlv.SetSecurityPolicy(aDatasetInfo.GetSecurityPolicy());
359 IgnoreError(WriteTlv(tlv));
360 }
361
362 mUpdateTime = TimerMilli::GetNow();
363
364 return error;
365 }
366
GetTimestamp(Type aType,Timestamp & aTimestamp) const367 Error Dataset::GetTimestamp(Type aType, Timestamp &aTimestamp) const
368 {
369 Error error = kErrorNone;
370 const Tlv *tlv;
371
372 if (aType == kActive)
373 {
374 tlv = FindTlv(Tlv::kActiveTimestamp);
375 VerifyOrExit(tlv != nullptr, error = kErrorNotFound);
376 aTimestamp = tlv->ReadValueAs<ActiveTimestampTlv>();
377 }
378 else
379 {
380 tlv = FindTlv(Tlv::kPendingTimestamp);
381 VerifyOrExit(tlv != nullptr, error = kErrorNotFound);
382 aTimestamp = tlv->ReadValueAs<PendingTimestampTlv>();
383 }
384
385 exit:
386 return error;
387 }
388
SetTimestamp(Type aType,const Timestamp & aTimestamp)389 void Dataset::SetTimestamp(Type aType, const Timestamp &aTimestamp)
390 {
391 if (aType == kActive)
392 {
393 IgnoreError(Write<ActiveTimestampTlv>(aTimestamp));
394 }
395 else
396 {
397 IgnoreError(Write<PendingTimestampTlv>(aTimestamp));
398 }
399 }
400
WriteTlv(Tlv::Type aType,const void * aValue,uint8_t aLength)401 Error Dataset::WriteTlv(Tlv::Type aType, const void *aValue, uint8_t aLength)
402 {
403 Error error = kErrorNone;
404 uint16_t bytesAvailable = sizeof(mTlvs) - mLength;
405 Tlv *oldTlv = FindTlv(aType);
406 Tlv *newTlv;
407
408 if (oldTlv != nullptr)
409 {
410 bytesAvailable += sizeof(Tlv) + oldTlv->GetLength();
411 }
412
413 VerifyOrExit(sizeof(Tlv) + aLength <= bytesAvailable, error = kErrorNoBufs);
414
415 RemoveTlv(oldTlv);
416
417 newTlv = GetTlvsEnd();
418 mLength += sizeof(Tlv) + aLength;
419
420 newTlv->SetType(aType);
421 newTlv->SetLength(aLength);
422 memcpy(newTlv->GetValue(), aValue, aLength);
423
424 mUpdateTime = TimerMilli::GetNow();
425
426 exit:
427 return error;
428 }
429
WriteTlv(const Tlv & aTlv)430 Error Dataset::WriteTlv(const Tlv &aTlv) { return WriteTlv(aTlv.GetType(), aTlv.GetValue(), aTlv.GetLength()); }
431
ReadFromMessage(const Message & aMessage,uint16_t aOffset,uint16_t aLength)432 Error Dataset::ReadFromMessage(const Message &aMessage, uint16_t aOffset, uint16_t aLength)
433 {
434 Error error = kErrorParse;
435
436 VerifyOrExit(aLength <= kMaxSize);
437
438 SuccessOrExit(aMessage.Read(aOffset, mTlvs, aLength));
439 mLength = aLength;
440
441 VerifyOrExit(IsValid(), error = kErrorParse);
442
443 mUpdateTime = TimerMilli::GetNow();
444 error = kErrorNone;
445
446 exit:
447 return error;
448 }
449
RemoveTlv(Tlv::Type aType)450 void Dataset::RemoveTlv(Tlv::Type aType) { RemoveTlv(FindTlv(aType)); }
451
AppendMleDatasetTlv(Type aType,Message & aMessage) const452 Error Dataset::AppendMleDatasetTlv(Type aType, Message &aMessage) const
453 {
454 Error error = kErrorNone;
455 Mle::Tlv tlv;
456 Mle::Tlv::Type type;
457
458 VerifyOrExit(mLength > 0);
459
460 type = (aType == kActive ? Mle::Tlv::kActiveDataset : Mle::Tlv::kPendingDataset);
461
462 tlv.SetType(type);
463 tlv.SetLength(static_cast<uint8_t>(mLength) - sizeof(Tlv) - sizeof(Timestamp));
464 SuccessOrExit(error = aMessage.Append(tlv));
465
466 for (const Tlv *cur = GetTlvsStart(); cur < GetTlvsEnd(); cur = cur->GetNext())
467 {
468 if (((aType == kActive) && (cur->GetType() == Tlv::kActiveTimestamp)) ||
469 ((aType == kPending) && (cur->GetType() == Tlv::kPendingTimestamp)))
470 {
471 ; // skip Active or Pending Timestamp TLV
472 }
473 else if (cur->GetType() == Tlv::kDelayTimer)
474 {
475 uint32_t elapsed = TimerMilli::GetNow() - mUpdateTime;
476 uint32_t delayTimer = cur->ReadValueAs<DelayTimerTlv>();
477
478 if (delayTimer > elapsed)
479 {
480 delayTimer -= elapsed;
481 }
482 else
483 {
484 delayTimer = 0;
485 }
486
487 SuccessOrExit(error = Tlv::Append<DelayTimerTlv>(aMessage, delayTimer));
488 }
489 else
490 {
491 SuccessOrExit(error = cur->AppendTo(aMessage));
492 }
493 }
494
495 exit:
496 return error;
497 }
498
RemoveTlv(Tlv * aTlv)499 void Dataset::RemoveTlv(Tlv *aTlv)
500 {
501 if (aTlv != nullptr)
502 {
503 uint8_t *start = reinterpret_cast<uint8_t *>(aTlv);
504 uint16_t length = sizeof(Tlv) + aTlv->GetLength();
505
506 memmove(start, start + length, mLength - (static_cast<uint8_t>(start - mTlvs) + length));
507 mLength -= length;
508 }
509 }
510
ApplyConfiguration(Instance & aInstance,bool * aIsNetworkKeyUpdated) const511 Error Dataset::ApplyConfiguration(Instance &aInstance, bool *aIsNetworkKeyUpdated) const
512 {
513 Mac::Mac &mac = aInstance.Get<Mac::Mac>();
514 KeyManager &keyManager = aInstance.Get<KeyManager>();
515 Error error = kErrorNone;
516
517 VerifyOrExit(IsValid(), error = kErrorParse);
518
519 if (aIsNetworkKeyUpdated)
520 {
521 *aIsNetworkKeyUpdated = false;
522 }
523
524 for (const Tlv *cur = GetTlvsStart(); cur < GetTlvsEnd(); cur = cur->GetNext())
525 {
526 switch (cur->GetType())
527 {
528 case Tlv::kChannel:
529 {
530 uint8_t channel = static_cast<uint8_t>(cur->ReadValueAs<ChannelTlv>().GetChannel());
531
532 error = mac.SetPanChannel(channel);
533
534 if (error != kErrorNone)
535 {
536 LogWarn("ApplyConfiguration() Failed to set channel to %d (%s)", channel, ErrorToString(error));
537 ExitNow();
538 }
539
540 break;
541 }
542
543 case Tlv::kPanId:
544 mac.SetPanId(cur->ReadValueAs<PanIdTlv>());
545 break;
546
547 case Tlv::kExtendedPanId:
548 aInstance.Get<ExtendedPanIdManager>().SetExtPanId(cur->ReadValueAs<ExtendedPanIdTlv>());
549 break;
550
551 case Tlv::kNetworkName:
552 IgnoreError(aInstance.Get<NetworkNameManager>().SetNetworkName(As<NetworkNameTlv>(cur)->GetNetworkName()));
553 break;
554
555 case Tlv::kNetworkKey:
556 {
557 NetworkKey networkKey;
558
559 keyManager.GetNetworkKey(networkKey);
560
561 if (aIsNetworkKeyUpdated && (cur->ReadValueAs<NetworkKeyTlv>() != networkKey))
562 {
563 *aIsNetworkKeyUpdated = true;
564 }
565
566 keyManager.SetNetworkKey(cur->ReadValueAs<NetworkKeyTlv>());
567 break;
568 }
569
570 #if OPENTHREAD_FTD
571
572 case Tlv::kPskc:
573 keyManager.SetPskc(cur->ReadValueAs<PskcTlv>());
574 break;
575
576 #endif
577
578 case Tlv::kMeshLocalPrefix:
579 aInstance.Get<Mle::MleRouter>().SetMeshLocalPrefix(cur->ReadValueAs<MeshLocalPrefixTlv>());
580 break;
581
582 case Tlv::kSecurityPolicy:
583 keyManager.SetSecurityPolicy(As<SecurityPolicyTlv>(cur)->GetSecurityPolicy());
584 break;
585
586 default:
587 break;
588 }
589 }
590
591 exit:
592 return error;
593 }
594
ConvertToActive(void)595 void Dataset::ConvertToActive(void)
596 {
597 RemoveTlv(Tlv::kPendingTimestamp);
598 RemoveTlv(Tlv::kDelayTimer);
599 }
600
TypeToString(Type aType)601 const char *Dataset::TypeToString(Type aType) { return (aType == kActive) ? "Active" : "Pending"; }
602
603 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
604
SaveTlvInSecureStorageAndClearValue(Tlv::Type aTlvType,Crypto::Storage::KeyRef aKeyRef)605 void Dataset::SaveTlvInSecureStorageAndClearValue(Tlv::Type aTlvType, Crypto::Storage::KeyRef aKeyRef)
606 {
607 using namespace ot::Crypto::Storage;
608
609 Tlv *tlv = FindTlv(aTlvType);
610
611 VerifyOrExit(tlv != nullptr);
612 VerifyOrExit(tlv->GetLength() > 0);
613
614 SuccessOrAssert(ImportKey(aKeyRef, kKeyTypeRaw, kKeyAlgorithmVendor, kUsageExport, kTypePersistent, tlv->GetValue(),
615 tlv->GetLength()));
616
617 memset(tlv->GetValue(), 0, tlv->GetLength());
618
619 exit:
620 return;
621 }
622
ReadTlvFromSecureStorage(Tlv::Type aTlvType,Crypto::Storage::KeyRef aKeyRef)623 Error Dataset::ReadTlvFromSecureStorage(Tlv::Type aTlvType, Crypto::Storage::KeyRef aKeyRef)
624 {
625 using namespace ot::Crypto::Storage;
626
627 Error error = kErrorNone;
628 Tlv *tlv = FindTlv(aTlvType);
629 size_t readLength;
630
631 VerifyOrExit(tlv != nullptr);
632 VerifyOrExit(tlv->GetLength() > 0);
633
634 SuccessOrExit(error = ExportKey(aKeyRef, tlv->GetValue(), tlv->GetLength(), readLength));
635 VerifyOrExit(readLength == tlv->GetLength(), error = OT_ERROR_FAILED);
636
637 exit:
638 return error;
639 }
640
641 #endif // OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
642
643 } // namespace MeshCoP
644 } // namespace ot
645