1 /*
2 * Copyright (c) 2017, 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 MeshCoP utility functions.
32 */
33
34 #include "meshcop.hpp"
35
36 #include "common/crc16.hpp"
37 #include "common/debug.hpp"
38 #include "common/locator_getters.hpp"
39 #include "common/logging.hpp"
40 #include "common/string.hpp"
41 #include "crypto/pbkdf2_cmac.hpp"
42 #include "crypto/sha256.hpp"
43 #include "mac/mac_types.hpp"
44 #include "thread/thread_netif.hpp"
45
46 namespace ot {
47 namespace MeshCoP {
48
SetFrom(const char * aPskdString)49 Error JoinerPskd::SetFrom(const char *aPskdString)
50 {
51 Error error = kErrorNone;
52
53 VerifyOrExit(IsPskdValid(aPskdString), error = kErrorInvalidArgs);
54
55 Clear();
56 memcpy(m8, aPskdString, StringLength(aPskdString, sizeof(m8)));
57
58 exit:
59 return error;
60 }
61
operator ==(const JoinerPskd & aOther) const62 bool JoinerPskd::operator==(const JoinerPskd &aOther) const
63 {
64 bool isEqual = true;
65
66 for (uint8_t i = 0; i < sizeof(m8); i++)
67 {
68 if (m8[i] != aOther.m8[i])
69 {
70 isEqual = false;
71 ExitNow();
72 }
73
74 if (m8[i] == '\0')
75 {
76 break;
77 }
78 }
79
80 exit:
81 return isEqual;
82 }
83
IsPskdValid(const char * aPskdString)84 bool JoinerPskd::IsPskdValid(const char *aPskdString)
85 {
86 bool valid = false;
87 uint16_t pskdLength = StringLength(aPskdString, kMaxLength + 1);
88
89 VerifyOrExit(pskdLength >= kMinLength && pskdLength <= kMaxLength);
90
91 for (uint16_t i = 0; i < pskdLength; i++)
92 {
93 char c = aPskdString[i];
94
95 VerifyOrExit(isdigit(c) || isupper(c));
96 VerifyOrExit(c != 'I' && c != 'O' && c != 'Q' && c != 'Z');
97 }
98
99 valid = true;
100
101 exit:
102 return valid;
103 }
104
GenerateJoinerId(Mac::ExtAddress & aJoinerId) const105 void JoinerDiscerner::GenerateJoinerId(Mac::ExtAddress &aJoinerId) const
106 {
107 aJoinerId.GenerateRandom();
108 CopyTo(aJoinerId);
109 aJoinerId.SetLocal(true);
110 }
111
Matches(const Mac::ExtAddress & aJoinerId) const112 bool JoinerDiscerner::Matches(const Mac::ExtAddress &aJoinerId) const
113 {
114 uint64_t mask;
115
116 OT_ASSERT(IsValid());
117
118 mask = GetMask();
119
120 return (Encoding::BigEndian::ReadUint64(aJoinerId.m8) & mask) == (mValue & mask);
121 }
122
CopyTo(Mac::ExtAddress & aExtAddress) const123 void JoinerDiscerner::CopyTo(Mac::ExtAddress &aExtAddress) const
124 {
125 // Copies the discerner value up to its bit length to `aExtAddress`
126 // array, assuming big-endian encoding (i.e., the discerner lowest bits
127 // are copied at end of `aExtAddress.m8[]` array). Any initial/remaining
128 // bits of `aExtAddress` array remain unchanged.
129
130 uint8_t *cur = &aExtAddress.m8[sizeof(Mac::ExtAddress) - 1];
131 uint8_t remaining = mLength;
132 uint64_t value = mValue;
133
134 OT_ASSERT(IsValid());
135
136 // Write full bytes
137 while (remaining >= CHAR_BIT)
138 {
139 *cur = static_cast<uint8_t>(value & 0xff);
140 value >>= CHAR_BIT;
141 cur--;
142 remaining -= CHAR_BIT;
143 }
144
145 // Write any remaining bits (not a full byte)
146 if (remaining != 0)
147 {
148 uint8_t mask = static_cast<uint8_t>((1U << remaining) - 1);
149
150 // `mask` has it lower (lsb) `remaining` bits as `1` and rest as `0`.
151 // Example with `remaining = 3` -> (1 << 3) - 1 = 0b1000 - 1 = 0b0111.
152
153 *cur &= ~mask;
154 *cur |= static_cast<uint8_t>(value & mask);
155 }
156 }
157
operator ==(const JoinerDiscerner & aOther) const158 bool JoinerDiscerner::operator==(const JoinerDiscerner &aOther) const
159 {
160 uint64_t mask = GetMask();
161
162 return IsValid() && (mLength == aOther.mLength) && ((mValue & mask) == (aOther.mValue & mask));
163 }
164
ToString(void) const165 JoinerDiscerner::InfoString JoinerDiscerner::ToString(void) const
166 {
167 InfoString string;
168
169 if (mLength <= sizeof(uint16_t) * CHAR_BIT)
170 {
171 string.Append("0x%04x", static_cast<uint16_t>(mValue));
172 }
173 else if (mLength <= sizeof(uint32_t) * CHAR_BIT)
174 {
175 string.Append("0x%08x", static_cast<uint32_t>(mValue));
176 }
177 else
178 {
179 string.Append("0x%x-%08x", static_cast<uint32_t>(mValue >> 32), static_cast<uint32_t>(mValue));
180 }
181
182 string.Append("/len:%d", mLength);
183
184 return string;
185 }
186
Init(uint8_t aLength)187 void SteeringData::Init(uint8_t aLength)
188 {
189 OT_ASSERT(aLength <= kMaxLength);
190 mLength = aLength;
191 memset(m8, 0, sizeof(m8));
192 }
193
SetToPermitAllJoiners(void)194 void SteeringData::SetToPermitAllJoiners(void)
195 {
196 Init(1);
197 m8[0] = kPermitAll;
198 }
199
UpdateBloomFilter(const Mac::ExtAddress & aJoinerId)200 void SteeringData::UpdateBloomFilter(const Mac::ExtAddress &aJoinerId)
201 {
202 HashBitIndexes indexes;
203
204 CalculateHashBitIndexes(aJoinerId, indexes);
205 UpdateBloomFilter(indexes);
206 }
207
UpdateBloomFilter(const JoinerDiscerner & aDiscerner)208 void SteeringData::UpdateBloomFilter(const JoinerDiscerner &aDiscerner)
209 {
210 HashBitIndexes indexes;
211
212 CalculateHashBitIndexes(aDiscerner, indexes);
213 UpdateBloomFilter(indexes);
214 }
215
UpdateBloomFilter(const HashBitIndexes & aIndexes)216 void SteeringData::UpdateBloomFilter(const HashBitIndexes &aIndexes)
217 {
218 OT_ASSERT((mLength > 0) && (mLength <= kMaxLength));
219
220 SetBit(aIndexes.mIndex[0] % GetNumBits());
221 SetBit(aIndexes.mIndex[1] % GetNumBits());
222 }
223
Contains(const Mac::ExtAddress & aJoinerId) const224 bool SteeringData::Contains(const Mac::ExtAddress &aJoinerId) const
225 {
226 HashBitIndexes indexes;
227
228 CalculateHashBitIndexes(aJoinerId, indexes);
229
230 return Contains(indexes);
231 }
232
Contains(const JoinerDiscerner & aDiscerner) const233 bool SteeringData::Contains(const JoinerDiscerner &aDiscerner) const
234 {
235 HashBitIndexes indexes;
236
237 CalculateHashBitIndexes(aDiscerner, indexes);
238
239 return Contains(indexes);
240 }
241
Contains(const HashBitIndexes & aIndexes) const242 bool SteeringData::Contains(const HashBitIndexes &aIndexes) const
243 {
244 return (mLength > 0) && GetBit(aIndexes.mIndex[0] % GetNumBits()) && GetBit(aIndexes.mIndex[1] % GetNumBits());
245 }
246
CalculateHashBitIndexes(const Mac::ExtAddress & aJoinerId,HashBitIndexes & aIndexes)247 void SteeringData::CalculateHashBitIndexes(const Mac::ExtAddress &aJoinerId, HashBitIndexes &aIndexes)
248 {
249 Crc16 ccitt(Crc16::kCcitt);
250 Crc16 ansi(Crc16::kAnsi);
251
252 for (uint8_t b : aJoinerId.m8)
253 {
254 ccitt.Update(b);
255 ansi.Update(b);
256 }
257
258 aIndexes.mIndex[0] = ccitt.Get();
259 aIndexes.mIndex[1] = ansi.Get();
260 }
261
CalculateHashBitIndexes(const JoinerDiscerner & aDiscerner,HashBitIndexes & aIndexes)262 void SteeringData::CalculateHashBitIndexes(const JoinerDiscerner &aDiscerner, HashBitIndexes &aIndexes)
263 {
264 Mac::ExtAddress address;
265
266 address.Clear();
267 aDiscerner.CopyTo(address);
268
269 CalculateHashBitIndexes(address, aIndexes);
270 }
271
DoesAllMatch(uint8_t aMatch) const272 bool SteeringData::DoesAllMatch(uint8_t aMatch) const
273 {
274 bool matches = true;
275
276 for (uint8_t i = 0; i < mLength; i++)
277 {
278 if (m8[i] != aMatch)
279 {
280 matches = false;
281 break;
282 }
283 }
284
285 return matches;
286 }
287
ComputeJoinerId(const Mac::ExtAddress & aEui64,Mac::ExtAddress & aJoinerId)288 void ComputeJoinerId(const Mac::ExtAddress &aEui64, Mac::ExtAddress &aJoinerId)
289 {
290 Crypto::Sha256 sha256;
291 Crypto::Sha256::Hash hash;
292
293 sha256.Start();
294 sha256.Update(aEui64);
295 sha256.Finish(hash);
296
297 memcpy(&aJoinerId, hash.GetBytes(), sizeof(aJoinerId));
298 aJoinerId.SetLocal(true);
299 }
300
GetBorderAgentRloc(ThreadNetif & aNetif,uint16_t & aRloc)301 Error GetBorderAgentRloc(ThreadNetif &aNetif, uint16_t &aRloc)
302 {
303 Error error = kErrorNone;
304 const BorderAgentLocatorTlv *borderAgentLocator;
305
306 borderAgentLocator = static_cast<const BorderAgentLocatorTlv *>(
307 aNetif.Get<NetworkData::Leader>().GetCommissioningDataSubTlv(Tlv::kBorderAgentLocator));
308 VerifyOrExit(borderAgentLocator != nullptr, error = kErrorNotFound);
309
310 aRloc = borderAgentLocator->GetBorderAgentLocator();
311
312 exit:
313 return error;
314 }
315
316 #if OPENTHREAD_FTD
GeneratePskc(const char * aPassPhrase,const Mac::NetworkName & aNetworkName,const Mac::ExtendedPanId & aExtPanId,Pskc & aPskc)317 Error GeneratePskc(const char * aPassPhrase,
318 const Mac::NetworkName & aNetworkName,
319 const Mac::ExtendedPanId &aExtPanId,
320 Pskc & aPskc)
321 {
322 Error error = kErrorNone;
323 const char saltPrefix[] = "Thread";
324 uint8_t salt[Crypto::Pbkdf2::kMaxSaltLength];
325 uint16_t saltLen = 0;
326 uint16_t passphraseLen;
327 uint8_t networkNameLen;
328
329 VerifyOrExit(IsValidUtf8String(aPassPhrase), error = kErrorInvalidArgs);
330
331 passphraseLen = static_cast<uint16_t>(StringLength(aPassPhrase, OT_COMMISSIONING_PASSPHRASE_MAX_SIZE + 1));
332 networkNameLen = static_cast<uint8_t>(StringLength(aNetworkName.GetAsCString(), OT_NETWORK_NAME_MAX_SIZE + 1));
333
334 VerifyOrExit((passphraseLen >= OT_COMMISSIONING_PASSPHRASE_MIN_SIZE) &&
335 (passphraseLen <= OT_COMMISSIONING_PASSPHRASE_MAX_SIZE) &&
336 (networkNameLen <= OT_NETWORK_NAME_MAX_SIZE),
337 error = kErrorInvalidArgs);
338
339 memset(salt, 0, sizeof(salt));
340 memcpy(salt, saltPrefix, sizeof(saltPrefix) - 1);
341 saltLen += static_cast<uint16_t>(sizeof(saltPrefix) - 1);
342
343 memcpy(salt + saltLen, aExtPanId.m8, sizeof(aExtPanId));
344 saltLen += OT_EXT_PAN_ID_SIZE;
345
346 memcpy(salt + saltLen, aNetworkName.GetAsCString(), networkNameLen);
347 saltLen += networkNameLen;
348
349 Crypto::Pbkdf2::GenerateKey(reinterpret_cast<const uint8_t *>(aPassPhrase), passphraseLen, salt, saltLen, 16384,
350 OT_PSKC_MAX_SIZE, aPskc.m8);
351
352 exit:
353 return error;
354 }
355 #endif // OPENTHREAD_FTD
356
357 #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_WARN) && (OPENTHREAD_CONFIG_LOG_MESHCOP == 1)
LogError(const char * aActionText,Error aError)358 void LogError(const char *aActionText, Error aError)
359 {
360 if (aError != kErrorNone)
361 {
362 otLogWarnMeshCoP("Failed to %s: %s", aActionText, ErrorToString(aError));
363 }
364 }
365 #endif
366
367 } // namespace MeshCoP
368 } // namespace ot
369