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 includes definitions for MeshCoP.
32 *
33 */
34
35 #ifndef MESHCOP_HPP_
36 #define MESHCOP_HPP_
37
38 #include "openthread-core-config.h"
39
40 #include <openthread/commissioner.h>
41 #include <openthread/instance.h>
42 #include <openthread/joiner.h>
43
44 #include "coap/coap.hpp"
45 #include "common/as_core_type.hpp"
46 #include "common/clearable.hpp"
47 #include "common/equatable.hpp"
48 #include "common/log.hpp"
49 #include "common/message.hpp"
50 #include "common/numeric_limits.hpp"
51 #include "common/string.hpp"
52 #include "mac/mac_types.hpp"
53 #include "meshcop/meshcop_tlvs.hpp"
54
55 namespace ot {
56
57 class ThreadNetif;
58
59 namespace MeshCoP {
60
61 /**
62 * Represents a Joiner PSKd.
63 *
64 */
65 class JoinerPskd : public otJoinerPskd, public Clearable<JoinerPskd>, public Unequatable<JoinerPskd>
66 {
67 public:
68 static constexpr uint8_t kMinLength = 6; ///< Min PSKd string length (excludes null char)
69 static constexpr uint8_t kMaxLength = OT_JOINER_MAX_PSKD_LENGTH; ///< Max PSKd string length (excludes null char)
70
71 /**
72 * Indicates whether the PSKd if well-formed and valid.
73 *
74 * Per Thread specification, a Joining Device Credential is encoded as uppercase alphanumeric characters
75 * (base32-thread: 0-9, A-Z excluding I, O, Q, and Z for readability) with a minimum length of 6 such characters
76 * and a maximum length of 32 such characters.
77 *
78 * @returns TRUE if the PSKd is valid, FALSE otherwise.
79 *
80 */
IsValid(void) const81 bool IsValid(void) const { return IsPskdValid(m8); }
82
83 /**
84 * Sets the joiner PSKd from a given C string.
85 *
86 * @param[in] aPskdString A pointer to the PSKd C string array.
87 *
88 * @retval kErrorNone The PSKd was updated successfully.
89 * @retval kErrorInvalidArgs The given PSKd C string is not valid.
90 *
91 */
92 Error SetFrom(const char *aPskdString);
93
94 /**
95 * Gets the PSKd as a null terminated C string.
96 *
97 * Must be used after the PSKd is validated, otherwise its behavior is undefined.
98 *
99 * @returns The PSKd as a C string.
100 *
101 */
GetAsCString(void) const102 const char *GetAsCString(void) const { return m8; }
103
104 /**
105 * Gets the PSKd string length.
106 *
107 * Must be used after the PSKd is validated, otherwise its behavior is undefined.
108 *
109 * @returns The PSKd string length.
110 *
111 */
GetLength(void) const112 uint8_t GetLength(void) const { return static_cast<uint8_t>(StringLength(m8, kMaxLength + 1)); }
113
114 /**
115 * Overloads operator `==` to evaluate whether or not two PSKds are equal.
116 *
117 * @param[in] aOther The other PSKd to compare with.
118 *
119 * @retval TRUE If the two are equal.
120 * @retval FALSE If the two are not equal.
121 *
122 */
123 bool operator==(const JoinerPskd &aOther) const;
124
125 /**
126 * Indicates whether a given PSKd string if well-formed and valid.
127 *
128 * @param[in] aPskdString A pointer to a PSKd string array.
129 *
130 * @sa IsValid()
131 *
132 * @returns TRUE if @p aPskdString is valid, FALSE otherwise.
133 *
134 */
135 static bool IsPskdValid(const char *aPskdString);
136 };
137
138 /**
139 * Represents a Joiner Discerner.
140 *
141 */
142 class JoinerDiscerner : public otJoinerDiscerner, public Unequatable<JoinerDiscerner>
143 {
144 friend class SteeringData;
145
146 public:
147 static constexpr uint8_t kMaxLength = OT_JOINER_MAX_DISCERNER_LENGTH; ///< Max length of a Discerner in bits.
148
149 static constexpr uint16_t kInfoStringSize = 45; ///< Size of `InfoString` to use with `ToString()
150
151 /**
152 * Defines the fixed-length `String` object returned from `ToString()`.
153 *
154 */
155 typedef String<kInfoStringSize> InfoString;
156
157 /**
158 * Clears the Joiner Discerner.
159 *
160 */
Clear(void)161 void Clear(void) { mLength = 0; }
162
163 /**
164 * Indicates whether the Joiner Discerner is empty (no value set).
165 *
166 * @returns TRUE if empty, FALSE otherwise.
167 *
168 */
IsEmpty(void) const169 bool IsEmpty(void) const { return mLength == 0; }
170
171 /**
172 * Gets the Joiner Discerner's value.
173 *
174 * @returns The Joiner Discerner value.
175 *
176 */
GetValue(void) const177 uint64_t GetValue(void) const { return mValue; }
178
179 /**
180 * Gets the Joiner Discerner's length (in bits).
181 *
182 * @return The Joiner Discerner length.
183 *
184 */
GetLength(void) const185 uint8_t GetLength(void) const { return mLength; }
186
187 /**
188 * Indicates whether the Joiner Discerner is valid (i.e. it not empty and its length is within
189 * valid range).
190 *
191 * @returns TRUE if Joiner Discerner is valid, FALSE otherwise.
192 *
193 */
IsValid(void) const194 bool IsValid(void) const { return (0 < mLength) && (mLength <= kMaxLength); }
195
196 /**
197 * Generates a Joiner ID from the Discerner.
198 *
199 * @param[out] aJoinerId A reference to `Mac::ExtAddress` to output the generated Joiner ID.
200 *
201 */
202 void GenerateJoinerId(Mac::ExtAddress &aJoinerId) const;
203
204 /**
205 * Indicates whether a given Joiner ID matches the Discerner.
206 *
207 * @param[in] aJoinerId A Joiner ID to match with the Discerner.
208 *
209 * @returns TRUE if the Joiner ID matches the Discerner, FALSE otherwise.
210 *
211 */
212 bool Matches(const Mac::ExtAddress &aJoinerId) const;
213
214 /**
215 * Overloads operator `==` to evaluate whether or not two Joiner Discerner instances are equal.
216 *
217 * @param[in] aOther The other Joiner Discerner to compare with.
218 *
219 * @retval TRUE If the two are equal.
220 * @retval FALSE If the two are not equal.
221 *
222 */
223 bool operator==(const JoinerDiscerner &aOther) const;
224
225 /**
226 * Converts the Joiner Discerner to a string.
227 *
228 * @returns An `InfoString` representation of Joiner Discerner.
229 *
230 */
231 InfoString ToString(void) const;
232
233 private:
GetMask(void) const234 uint64_t GetMask(void) const { return (static_cast<uint64_t>(1ULL) << mLength) - 1; }
235 void CopyTo(Mac::ExtAddress &aExtAddress) const;
236 };
237
238 /**
239 * Represents Steering Data (bloom filter).
240 *
241 */
242 class SteeringData : public otSteeringData
243 {
244 public:
245 static constexpr uint8_t kMaxLength = OT_STEERING_DATA_MAX_LENGTH; ///< Maximum Steering Data length (in bytes).
246
247 /**
248 * Represents the hash bit index values for the bloom filter calculated from a Joiner ID.
249 *
250 * The first hash bit index is derived using CRC16-CCITT and second one using CRC16-ANSI.
251 *
252 */
253 struct HashBitIndexes
254 {
255 static constexpr uint8_t kNumIndexes = 2; ///< Number of hash bit indexes.
256
257 uint16_t mIndex[kNumIndexes]; ///< The hash bit index array.
258 };
259
260 /**
261 * Initializes the Steering Data and clears the bloom filter.
262 *
263 * @param[in] aLength The Steering Data length (in bytes) - MUST be smaller than or equal to `kMaxLength`.
264 *
265 */
266 void Init(uint8_t aLength = kMaxLength);
267
268 /**
269 * Clears the bloom filter (all bits are cleared and no Joiner Id is accepted)..
270 *
271 * The Steering Data length (bloom filter length) is set to one byte with all bits cleared.
272 *
273 */
Clear(void)274 void Clear(void) { Init(1); }
275
276 /**
277 * Sets the bloom filter to permit all Joiner IDs.
278 *
279 * To permit all Joiner IDs, The Steering Data length (bloom filter length) is set to one byte with all bits set.
280 *
281 */
282 void SetToPermitAllJoiners(void);
283
284 /**
285 * Returns the Steering Data length (in bytes).
286 *
287 * @returns The Steering Data length (in bytes).
288 *
289 */
GetLength(void) const290 uint8_t GetLength(void) const { return mLength; }
291
292 /**
293 * Gets the Steering Data buffer (bloom filter).
294 *
295 * @returns A pointer to the Steering Data buffer.
296 *
297 */
GetData(void) const298 const uint8_t *GetData(void) const { return m8; }
299
300 /**
301 * Gets the Steering Data buffer (bloom filter).
302 *
303 * @returns A pointer to the Steering Data buffer.
304 *
305 */
GetData(void)306 uint8_t *GetData(void) { return m8; }
307
308 /**
309 * Updates the bloom filter adding the given Joiner ID.
310 *
311 * @param[in] aJoinerId The Joiner ID to add to bloom filter.
312 *
313 */
314 void UpdateBloomFilter(const Mac::ExtAddress &aJoinerId);
315
316 /**
317 * Updates the bloom filter adding a given Joiner Discerner.
318 *
319 * @param[in] aDiscerner The Joiner Discerner to add to bloom filter.
320 *
321 */
322 void UpdateBloomFilter(const JoinerDiscerner &aDiscerner);
323
324 /**
325 * Indicates whether the bloom filter is empty (all the bits are cleared).
326 *
327 * @returns TRUE if the bloom filter is empty, FALSE otherwise.
328 *
329 */
IsEmpty(void) const330 bool IsEmpty(void) const { return DoesAllMatch(0); }
331
332 /**
333 * Indicates whether the bloom filter permits all Joiner IDs (all the bits are set).
334 *
335 * @returns TRUE if the bloom filter permits all Joiners IDs, FALSE otherwise.
336 *
337 */
PermitsAllJoiners(void) const338 bool PermitsAllJoiners(void) const { return (mLength > 0) && DoesAllMatch(kPermitAll); }
339
340 /**
341 * Indicates whether the bloom filter contains a given Joiner ID.
342 *
343 * @param[in] aJoinerId A Joiner ID.
344 *
345 * @returns TRUE if the bloom filter contains @p aJoinerId, FALSE otherwise.
346 *
347 */
348 bool Contains(const Mac::ExtAddress &aJoinerId) const;
349
350 /**
351 * Indicates whether the bloom filter contains a given Joiner Discerner.
352 *
353 * @param[in] aDiscerner A Joiner Discerner.
354 *
355 * @returns TRUE if the bloom filter contains @p aDiscerner, FALSE otherwise.
356 *
357 */
358 bool Contains(const JoinerDiscerner &aDiscerner) const;
359
360 /**
361 * Indicates whether the bloom filter contains the hash bit indexes (derived from a Joiner ID).
362 *
363 * @param[in] aIndexes A hash bit index structure (derived from a Joiner ID).
364 *
365 * @returns TRUE if the bloom filter contains the Joiner ID mapping to @p aIndexes, FALSE otherwise.
366 *
367 */
368 bool Contains(const HashBitIndexes &aIndexes) const;
369
370 /**
371 * Calculates the bloom filter hash bit indexes from a given Joiner ID.
372 *
373 * The first hash bit index is derived using CRC16-CCITT and second one using CRC16-ANSI.
374 *
375 * @param[in] aJoinerId The Joiner ID to calculate the hash bit indexes.
376 * @param[out] aIndexes A reference to a `HashBitIndexes` structure to output the calculated index values.
377 *
378 */
379 static void CalculateHashBitIndexes(const Mac::ExtAddress &aJoinerId, HashBitIndexes &aIndexes);
380
381 /**
382 * Calculates the bloom filter hash bit indexes from a given Joiner Discerner.
383 *
384 * The first hash bit index is derived using CRC16-CCITT and second one using CRC16-ANSI.
385 *
386 * @param[in] aDiscerner The Joiner Discerner to calculate the hash bit indexes.
387 * @param[out] aIndexes A reference to a `HashBitIndexes` structure to output the calculated index values.
388 *
389 */
390 static void CalculateHashBitIndexes(const JoinerDiscerner &aDiscerner, HashBitIndexes &aIndexes);
391
392 private:
393 static constexpr uint8_t kPermitAll = 0xff;
394
GetNumBits(void) const395 uint8_t GetNumBits(void) const { return (mLength * kBitsPerByte); }
396
BitIndex(uint8_t aBit) const397 uint8_t BitIndex(uint8_t aBit) const { return (mLength - 1 - (aBit / kBitsPerByte)); }
BitFlag(uint8_t aBit) const398 uint8_t BitFlag(uint8_t aBit) const { return static_cast<uint8_t>(1U << (aBit % kBitsPerByte)); }
399
GetBit(uint8_t aBit) const400 bool GetBit(uint8_t aBit) const { return (m8[BitIndex(aBit)] & BitFlag(aBit)) != 0; }
SetBit(uint8_t aBit)401 void SetBit(uint8_t aBit) { m8[BitIndex(aBit)] |= BitFlag(aBit); }
ClearBit(uint8_t aBit)402 void ClearBit(uint8_t aBit) { m8[BitIndex(aBit)] &= ~BitFlag(aBit); }
403
404 bool DoesAllMatch(uint8_t aMatch) const;
405 void UpdateBloomFilter(const HashBitIndexes &aIndexes);
406 };
407
408 /**
409 * Represents a Commissioning Dataset.
410 *
411 */
412 class CommissioningDataset : public otCommissioningDataset, public Clearable<CommissioningDataset>
413 {
414 public:
415 /**
416 * Indicates whether or not the Border Router RLOC16 Locator is set in the Dataset.
417 *
418 * @returns TRUE if Border Router RLOC16 Locator is set, FALSE otherwise.
419 *
420 */
IsLocatorSet(void) const421 bool IsLocatorSet(void) const { return mIsLocatorSet; }
422
423 /**
424 * Gets the Border Router RLOC16 Locator in the Dataset.
425 *
426 * MUST be used when Locator is set in the Dataset, otherwise its behavior is undefined.
427 *
428 * @returns The Border Router RLOC16 Locator in the Dataset.
429 *
430 */
GetLocator(void) const431 uint16_t GetLocator(void) const { return mLocator; }
432
433 /**
434 * Sets the Border Router RLOCG16 Locator in the Dataset.
435 *
436 * @param[in] aLocator A Locator.
437 *
438 */
SetLocator(uint16_t aLocator)439 void SetLocator(uint16_t aLocator)
440 {
441 mIsLocatorSet = true;
442 mLocator = aLocator;
443 }
444
445 /**
446 * Indicates whether or not the Session ID is set in the Dataset.
447 *
448 * @returns TRUE if Session ID is set, FALSE otherwise.
449 *
450 */
IsSessionIdSet(void) const451 bool IsSessionIdSet(void) const { return mIsSessionIdSet; }
452
453 /**
454 * Gets the Session ID in the Dataset.
455 *
456 * MUST be used when Session ID is set in the Dataset, otherwise its behavior is undefined.
457 *
458 * @returns The Session ID in the Dataset.
459 *
460 */
GetSessionId(void) const461 uint16_t GetSessionId(void) const { return mSessionId; }
462
463 /**
464 * Sets the Session ID in the Dataset.
465 *
466 * @param[in] aSessionId The Session ID.
467 *
468 */
SetSessionId(uint16_t aSessionId)469 void SetSessionId(uint16_t aSessionId)
470 {
471 mIsSessionIdSet = true;
472 mSessionId = aSessionId;
473 }
474
475 /**
476 * Indicates whether or not the Steering Data is set in the Dataset.
477 *
478 * @returns TRUE if Steering Data is set, FALSE otherwise.
479 *
480 */
IsSteeringDataSet(void) const481 bool IsSteeringDataSet(void) const { return mIsSteeringDataSet; }
482
483 /**
484 * Gets the Steering Data in the Dataset.
485 *
486 * MUST be used when Steering Data is set in the Dataset, otherwise its behavior is undefined.
487 *
488 * @returns The Steering Data in the Dataset.
489 *
490 */
GetSteeringData(void) const491 const SteeringData &GetSteeringData(void) const { return static_cast<const SteeringData &>(mSteeringData); }
492
493 /**
494 * Returns a reference to the Steering Data in the Dataset to be updated by caller.
495 *
496 * @returns A reference to the Steering Data in the Dataset.
497 *
498 */
UpdateSteeringData(void)499 SteeringData &UpdateSteeringData(void)
500 {
501 mIsSteeringDataSet = true;
502 return static_cast<SteeringData &>(mSteeringData);
503 }
504
505 /**
506 * Indicates whether or not the Joiner UDP port is set in the Dataset.
507 *
508 * @returns TRUE if Joiner UDP port is set, FALSE otherwise.
509 *
510 */
IsJoinerUdpPortSet(void) const511 bool IsJoinerUdpPortSet(void) const { return mIsJoinerUdpPortSet; }
512
513 /**
514 * Gets the Joiner UDP port in the Dataset.
515 *
516 * MUST be used when Joiner UDP port is set in the Dataset, otherwise its behavior is undefined.
517 *
518 * @returns The Joiner UDP port in the Dataset.
519 *
520 */
GetJoinerUdpPort(void) const521 uint16_t GetJoinerUdpPort(void) const { return mJoinerUdpPort; }
522
523 /**
524 * Sets the Joiner UDP Port in the Dataset.
525 *
526 * @param[in] aJoinerUdpPort The Joiner UDP Port.
527 *
528 */
SetJoinerUdpPort(uint16_t aJoinerUdpPort)529 void SetJoinerUdpPort(uint16_t aJoinerUdpPort)
530 {
531 mIsJoinerUdpPortSet = true;
532 mJoinerUdpPort = aJoinerUdpPort;
533 }
534 };
535
536 /**
537 * Generates PSKc.
538 *
539 * PSKc is used to establish the Commissioner Session.
540 *
541 * @param[in] aPassPhrase The commissioning passphrase.
542 * @param[in] aNetworkName The network name for PSKc computation.
543 * @param[in] aExtPanId The extended PAN ID for PSKc computation.
544 * @param[out] aPskc A reference to a PSKc where the generated PSKc will be placed.
545 *
546 * @retval kErrorNone Successfully generate PSKc.
547 * @retval kErrorInvalidArgs If the length of passphrase is out of range.
548 *
549 */
550 Error GeneratePskc(const char *aPassPhrase,
551 const NetworkName &aNetworkName,
552 const ExtendedPanId &aExtPanId,
553 Pskc &aPskc);
554
555 /**
556 * Computes the Joiner ID from a factory-assigned IEEE EUI-64.
557 *
558 * @param[in] aEui64 The factory-assigned IEEE EUI-64.
559 * @param[out] aJoinerId The Joiner ID.
560 *
561 */
562 void ComputeJoinerId(const Mac::ExtAddress &aEui64, Mac::ExtAddress &aJoinerId);
563
564 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
565 /**
566 * Emits a log message indicating an error during a MeshCoP action.
567 *
568 * Note that log message is emitted only if there is an error, i.e. @p aError is not `kErrorNone`. The log
569 * message will have the format "Failed to {aActionText} : {ErrorString}".
570 *
571 * @param[in] aActionText A string representing the failed action.
572 * @param[in] aError The error in sending the message.
573 *
574 */
575 void LogError(const char *aActionText, Error aError);
576 #else
LogError(const char *,Error)577 inline void LogError(const char *, Error) {}
578 #endif
579
580 } // namespace MeshCoP
581
582 DefineCoreType(otJoinerPskd, MeshCoP::JoinerPskd);
583 DefineCoreType(otJoinerDiscerner, MeshCoP::JoinerDiscerner);
584 DefineCoreType(otSteeringData, MeshCoP::SteeringData);
585 DefineCoreType(otCommissioningDataset, MeshCoP::CommissioningDataset);
586
587 } // namespace ot
588
589 #endif // MESHCOP_HPP_
590