Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021-2022 Project CHIP Authors
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : /**
19 : * @brief Defines a table of fabrics that have provisioned the device.
20 : */
21 :
22 : #pragma once
23 :
24 : #include <algorithm>
25 :
26 : #include <app/util/basic-types.h>
27 : #include <credentials/CHIPCert.h>
28 : #include <credentials/CHIPCertificateSet.h>
29 : #include <credentials/CertificateValidityPolicy.h>
30 : #include <credentials/LastKnownGoodTime.h>
31 : #include <credentials/OperationalCertificateStore.h>
32 : #include <crypto/CHIPCryptoPAL.h>
33 : #include <crypto/OperationalKeystore.h>
34 : #include <lib/core/CHIPEncoding.h>
35 : #include <lib/core/CHIPPersistentStorageDelegate.h>
36 : #include <lib/core/CHIPSafeCasts.h>
37 : #include <lib/core/Optional.h>
38 : #include <lib/core/ScopedNodeId.h>
39 : #include <lib/core/TLV.h>
40 : #include <lib/support/BitFlags.h>
41 : #include <lib/support/CHIPMem.h>
42 : #include <lib/support/DLLUtil.h>
43 : #include <lib/support/Span.h>
44 :
45 : namespace chip {
46 :
47 : static constexpr uint8_t kFabricLabelMaxLengthInBytes = 32;
48 :
49 : static_assert(kUndefinedFabricIndex < chip::kMinValidFabricIndex, "Undefined fabric index should not be valid");
50 :
51 : /**
52 : * Provides access to the core metadata for a given fabric to which a node is joined.
53 : *
54 : * This metadata includes:
55 : *
56 : * - FabricIndex within the local set of fabrics
57 : * - Operational Identity
58 : * - NodeId
59 : * - Fabric Id
60 : * - Public key of operational root CA (to avoid keeping/reloading RCAC (Root CA Certificate) too often)
61 : * - Pre-computed "Compressed Fabric ID" used for discovery
62 : * - Operational public key (if externally injected as opposed to present in an OperationalKeystore)
63 : * - Fabric Label
64 : * - VendorID allocated at fabric joining by commissioner
65 : *
66 : * NOTE: All the setters of this class are private and only accessible by FabricTable, the
67 : * friend class that owns these. The reason is that there are data dependencies between
68 : * fabrics that require FabricTable to be the single entrypoint for all mutations, rather
69 : * than directly on a FabricInfo instance.
70 : */
71 : class DLL_EXPORT FabricInfo
72 : {
73 : public:
74 0 : FabricInfo() { Reset(); }
75 0 : ~FabricInfo() { Reset(); }
76 :
77 : // Non-copyable
78 : FabricInfo(FabricInfo const &) = delete;
79 : void operator=(FabricInfo const &) = delete;
80 :
81 : // Returns a span into our internal storage.
82 0 : CharSpan GetFabricLabel() const { return CharSpan(mFabricLabel, strnlen(mFabricLabel, kFabricLabelMaxLengthInBytes)); }
83 : CHIP_ERROR SetFabricLabel(const CharSpan & fabricLabel);
84 :
85 1657 : NodeId GetNodeId() const { return mNodeId; }
86 : ScopedNodeId GetScopedNodeId() const { return ScopedNodeId(mNodeId, mFabricIndex); }
87 0 : ScopedNodeId GetScopedNodeIdForNode(const NodeId node) const { return ScopedNodeId(node, mFabricIndex); }
88 :
89 : // TODO(#15049): Refactor/rename PeerId to OperationalId or OpId throughout source
90 0 : PeerId GetPeerId() const { return PeerId(mCompressedFabricId, mNodeId); }
91 : PeerId GetPeerIdForNode(const NodeId node) const { return PeerId(mCompressedFabricId, node); }
92 :
93 37 : FabricId GetFabricId() const { return mFabricId; }
94 34880 : FabricIndex GetFabricIndex() const { return mFabricIndex; }
95 :
96 19919 : CompressedFabricId GetCompressedFabricId() const { return mCompressedFabricId; }
97 0 : CHIP_ERROR GetCompressedFabricIdBytes(MutableByteSpan & compressedFabricId) const
98 : {
99 0 : ReturnErrorCodeIf(compressedFabricId.size() != sizeof(uint64_t), CHIP_ERROR_INVALID_ARGUMENT);
100 0 : Encoding::BigEndian::Put64(compressedFabricId.data(), GetCompressedFabricId());
101 0 : return CHIP_NO_ERROR;
102 : }
103 :
104 : CHIP_ERROR FetchRootPubkey(Crypto::P256PublicKey & outPublicKey) const;
105 :
106 0 : VendorId GetVendorId() const { return mVendorId; }
107 :
108 75159 : bool IsInitialized() const { return (mFabricIndex != kUndefinedFabricIndex) && IsOperationalNodeId(mNodeId); }
109 :
110 666 : bool HasOperationalKey() const { return mOperationalKey != nullptr; }
111 :
112 0 : bool ShouldAdvertiseIdentity() const { return mShouldAdvertiseIdentity; }
113 :
114 : friend class FabricTable;
115 :
116 : private:
117 : struct InitParams
118 : {
119 : CompressedFabricId compressedFabricId = kUndefinedCompressedFabricId;
120 : NodeId nodeId = kUndefinedNodeId;
121 : FabricIndex fabricIndex = kUndefinedFabricIndex;
122 : Crypto::P256Keypair * operationalKeypair = nullptr;
123 : FabricId fabricId = kUndefinedFabricId;
124 : Crypto::P256PublicKey rootPublicKey;
125 : VendorId vendorId = VendorId::NotSpecified; /**< Vendor ID for commissioner of fabric */
126 : bool hasExternallyOwnedKeypair = false;
127 : bool advertiseIdentity = false;
128 :
129 640 : CHIP_ERROR AreValid() const
130 : {
131 640 : VerifyOrReturnError((fabricId != kUndefinedFabricId) && (fabricIndex != kUndefinedFabricIndex),
132 : CHIP_ERROR_INVALID_ARGUMENT);
133 640 : VerifyOrReturnError(IsOperationalNodeId(nodeId), CHIP_ERROR_INVALID_ARGUMENT);
134 : // We don't check the root public key validity or the compressed fabric ID, since in the
135 : // very small usage that exists in private use, the rest should be OK.
136 640 : return CHIP_NO_ERROR;
137 : }
138 : };
139 :
140 : // Move assignment operator to support setting from pending on fabric table commit
141 : void operator=(FabricInfo && other);
142 :
143 : /**
144 : * @brief Initialize a FabricInfo object's metadata given init parameters.
145 : *
146 : * Note that certificates are never owned by this object and are assumed pre-validated
147 : *
148 : * @param initParams Init parameters to use to initialize the given fabric.
149 : * @return CHIP_NO_ERROR on success or another internal CHIP_ERROR_* value on failure
150 : */
151 : CHIP_ERROR Init(const InitParams & initParams);
152 :
153 : /**
154 : * Sets the P256Keypair used for this fabric. This will make a copy of the keypair
155 : * via the P256Keypair::Serialize and P256Keypair::Deserialize methods.
156 : *
157 : * The keyPair argument is safe to deallocate once this method returns.
158 : *
159 : * If your P256Keypair does not support serialization, use the
160 : * `SetExternallyOwnedOperationalKeypair` method instead.
161 : */
162 : CHIP_ERROR SetOperationalKeypair(const Crypto::P256Keypair * keyPair);
163 :
164 : /**
165 : * Sets the P256Keypair used for this fabric, delegating ownership of the
166 : * key to the caller. The P256Keypair provided here must be freed later by
167 : * the caller of this method if it was allocated dynamically.
168 : *
169 : * This should be used if your P256Keypair does not support serialization
170 : * and deserialization (e.g. your private key is held in a secure element
171 : * and cannot be accessed directly), or if you back your operational
172 : * private keys by external implementation of the cryptographic interfaces.
173 : *
174 : * To have the ownership of the key managed for you, use
175 : * SetOperationalKeypair instead.
176 : */
177 : CHIP_ERROR SetExternallyOwnedOperationalKeypair(Crypto::P256Keypair * keyPair);
178 :
179 : /**
180 : * @brief Sign a message with the fabric's operational private key. This ONLY
181 : * works if `SetOperationalKeypair` or `SetExternallyOwnedOperationalKeypair`
182 : * had been called and is an API that is present ONLY to be called by FabricTable.
183 : *
184 : * @param message - message to sign
185 : * @param outSignature - buffer to hold the signature
186 : * @return CHIP_NO_ERROR on success or another CHIP_ERROR on crypto internal errors
187 : */
188 : CHIP_ERROR SignWithOpKeypair(ByteSpan message, Crypto::P256ECDSASignature & outSignature) const;
189 :
190 : /**
191 : * Reset the state to a completely uninitialized status.
192 : */
193 0 : void Reset()
194 : {
195 0 : mNodeId = kUndefinedNodeId;
196 0 : mFabricId = kUndefinedFabricId;
197 0 : mFabricIndex = kUndefinedFabricIndex;
198 0 : mCompressedFabricId = kUndefinedCompressedFabricId;
199 :
200 0 : mVendorId = VendorId::NotSpecified;
201 0 : mFabricLabel[0] = '\0';
202 :
203 0 : if (!mHasExternallyOwnedOperationalKey && mOperationalKey != nullptr)
204 : {
205 0 : chip::Platform::Delete(mOperationalKey);
206 : }
207 0 : mOperationalKey = nullptr;
208 0 : mHasExternallyOwnedOperationalKey = false;
209 0 : mShouldAdvertiseIdentity = true;
210 :
211 0 : mFabricIndex = kUndefinedFabricIndex;
212 0 : mNodeId = kUndefinedNodeId;
213 0 : }
214 :
215 : static constexpr size_t MetadataTLVMaxSize()
216 : {
217 : return TLV::EstimateStructOverhead(sizeof(uint16_t), kFabricLabelMaxLengthInBytes);
218 : }
219 :
220 : static constexpr size_t OpKeyTLVMaxSize()
221 : {
222 : return TLV::EstimateStructOverhead(sizeof(uint16_t), Crypto::P256SerializedKeypair::Capacity());
223 : }
224 :
225 : NodeId mNodeId = kUndefinedNodeId;
226 : FabricId mFabricId = kUndefinedFabricId;
227 : // We cache the compressed fabric id since it's used so often and costly to get.
228 : CompressedFabricId mCompressedFabricId = kUndefinedCompressedFabricId;
229 : // We cache the root public key since it's used so often and costly to get.
230 : Crypto::P256PublicKey mRootPublicKey;
231 :
232 : // mFabricLabel is 33 bytes, so ends on a 1 mod 4 byte boundary.
233 : char mFabricLabel[kFabricLabelMaxLengthInBytes + 1] = { '\0' };
234 :
235 : // mFabricIndex, mVendorId, mHasExternallyOwnedOperationalKey,
236 : // mShouldAdvertiseIdentity are 5 bytes and do not include any padding if
237 : // they come after the 33-byte mFabricLabel, so end on a 2 mod 4 byte
238 : // boundary.
239 : FabricIndex mFabricIndex = kUndefinedFabricIndex;
240 : VendorId mVendorId = VendorId::NotSpecified;
241 : bool mHasExternallyOwnedOperationalKey = false;
242 : bool mShouldAdvertiseIdentity = true;
243 :
244 : // 2 bytes of padding here, since mOperationalKey needs to be void*-aligned,
245 : // so has to be at a 0 mod 4 byte location.
246 :
247 : mutable Crypto::P256Keypair * mOperationalKey = nullptr;
248 :
249 : CHIP_ERROR CommitToStorage(PersistentStorageDelegate * storage) const;
250 : CHIP_ERROR LoadFromStorage(PersistentStorageDelegate * storage, FabricIndex newFabricIndex, const ByteSpan & rcac,
251 : const ByteSpan & noc);
252 : };
253 :
254 : /**
255 : * Iterates over valid fabrics within a list
256 : */
257 :
258 : class ConstFabricIterator
259 : {
260 : public:
261 : using value_type = FabricInfo;
262 : using pointer = FabricInfo *;
263 : using reference = FabricInfo &;
264 :
265 1308 : ConstFabricIterator(const FabricInfo * start, const FabricInfo * pending, size_t index, size_t maxSize) :
266 1308 : mStart(start), mPending(pending), mIndex(index), mMaxSize(maxSize)
267 : {
268 1308 : if (mIndex >= maxSize)
269 : {
270 654 : mIndex = maxSize;
271 : }
272 654 : else if (!mStart[mIndex].IsInitialized())
273 : {
274 26 : Advance();
275 : }
276 1308 : }
277 : ConstFabricIterator(const ConstFabricIterator &) = default;
278 : ConstFabricIterator & operator=(const ConstFabricIterator &) = default;
279 :
280 912 : ConstFabricIterator & operator++() { return Advance(); }
281 : ConstFabricIterator operator++(int)
282 : {
283 : ConstFabricIterator other(*this);
284 : Advance();
285 : return other;
286 : }
287 :
288 938 : const FabricInfo & operator*() const
289 : {
290 938 : VerifyOrDie(!IsAtEnd());
291 :
292 938 : return *GetCurrent();
293 : }
294 0 : const FabricInfo * operator->() const
295 : {
296 0 : VerifyOrDie(!IsAtEnd());
297 :
298 0 : return GetCurrent();
299 : }
300 :
301 1566 : bool operator==(const ConstFabricIterator & other)
302 : {
303 1566 : if (IsAtEnd())
304 : {
305 628 : return other.IsAtEnd();
306 : }
307 :
308 : // Pending entry does not participate in finding this.
309 938 : return (mStart == other.mStart) && (mIndex == other.mIndex) && (mMaxSize == other.mMaxSize);
310 : }
311 1566 : bool operator!=(const ConstFabricIterator & other) { return !(*this == other); }
312 :
313 13187 : bool IsAtEnd() const { return (mIndex == mMaxSize); }
314 :
315 : private:
316 : const FabricInfo * mStart;
317 : const FabricInfo * mPending; ///< Pointer to the shadow pending entry, nullptr if none
318 : size_t mIndex;
319 : size_t mMaxSize;
320 :
321 : // Helper to get either a given entry of the fabric table, or its pending shadow if
322 : // a fabric update is currently pending.
323 938 : const FabricInfo * GetCurrent() const
324 : {
325 938 : const auto * current = mStart + mIndex;
326 :
327 : // If we reached the pending entry, return that instead of the underlying entry from the mStates.
328 938 : if ((mPending != nullptr) && mPending->IsInitialized() && (current->GetFabricIndex() == mPending->GetFabricIndex()))
329 : {
330 0 : current = mPending;
331 : }
332 :
333 938 : return current;
334 : }
335 :
336 10055 : ConstFabricIterator & Advance()
337 : {
338 : do
339 : {
340 10055 : if (mIndex < mMaxSize)
341 : {
342 10055 : mIndex++;
343 : }
344 10055 : } while (!IsAtEnd() && !mStart[mIndex].IsInitialized());
345 :
346 938 : return *this;
347 : }
348 : };
349 :
350 : class DLL_EXPORT FabricTable
351 : {
352 : public:
353 : struct DLL_EXPORT InitParams
354 : {
355 : // PersistentStorageDelegate for Fabric Info metadata storage and Fabric Table index (MANDATORY).
356 : PersistentStorageDelegate * storage = nullptr;
357 : // Operational Keystore to abstract access to key. Mandatory for commissionable devices (e.g.
358 : // chip::Server-based things) and recommended for controllers. With this set to false, FabricInfo
359 : // added as new fabrics need to have directly injected operational keys with FabricInfo::Set*OperationalKey.
360 : Crypto::OperationalKeystore * operationalKeystore = nullptr;
361 : // Operational Certificate store to hold the NOC/ICAC/RCAC chains (MANDATORY).
362 : Credentials::OperationalCertificateStore * opCertStore = nullptr;
363 : };
364 :
365 : class DLL_EXPORT Delegate
366 : {
367 : public:
368 123 : Delegate() {}
369 123 : virtual ~Delegate() {}
370 :
371 : /**
372 : * Gets called when a fabric is about to be deleted, such as on
373 : * FabricTable::Delete(). This allows actions to be taken that need the
374 : * fabric to still be around before we delete it.
375 : **/
376 10 : virtual void FabricWillBeRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) {}
377 :
378 : /**
379 : * Gets called when a fabric is deleted, such as on FabricTable::Delete().
380 : **/
381 0 : virtual void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) {}
382 :
383 : /**
384 : * Gets called when a fabric in Fabric Table is persisted to storage, by CommitPendingFabricData.
385 : **/
386 1062 : virtual void OnFabricCommitted(const FabricTable & fabricTable, FabricIndex fabricIndex){};
387 :
388 : /**
389 : * Gets called when operational credentials are changed, which may not be persistent.
390 : *
391 : * Can be used to affect what is needed for UpdateNOC prior to commit.
392 : **/
393 1062 : virtual void OnFabricUpdated(const FabricTable & fabricTable, FabricIndex fabricIndex){};
394 :
395 : // Intrusive list pointer for FabricTable to manage the entries.
396 : Delegate * next = nullptr;
397 : };
398 :
399 : public:
400 0 : FabricTable() = default;
401 0 : ~FabricTable() = default;
402 :
403 : // Non-copyable
404 : FabricTable(FabricTable const &) = delete;
405 : void operator=(FabricTable const &) = delete;
406 :
407 : enum class AdvertiseIdentity : uint8_t
408 : {
409 : Yes,
410 : No
411 : };
412 :
413 : // Returns CHIP_ERROR_NOT_FOUND if there is no fabric for that index.
414 : CHIP_ERROR Delete(FabricIndex fabricIndex);
415 : void DeleteAllFabrics();
416 :
417 : // TODO this #if CONFIG_BUILD_FOR_HOST_UNIT_TEST is temporary. There is a change incoming soon
418 : // that will allow triggering NOC update directly.
419 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
420 : void SendUpdateFabricNotificationForTest(FabricIndex fabricIndex) { NotifyFabricUpdated(fabricIndex); }
421 : #endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
422 :
423 : /**
424 : * Collection of methods to help find a matching FabricInfo instance given a set of query criteria
425 : *
426 : */
427 :
428 : /**
429 : * Finds a matching FabricInfo instance given a root public key and fabric ID that uniquely identifies the fabric in any scope.
430 : *
431 : * Returns nullptr if no matching instance is found.
432 : *
433 : */
434 : const FabricInfo * FindFabric(const Crypto::P256PublicKey & rootPubKey, FabricId fabricId) const;
435 :
436 : /**
437 : * Finds a matching FabricInfo instance given a locally-scoped fabric index.
438 : *
439 : * Returns nullptr if no matching instance is found.
440 : *
441 : */
442 : const FabricInfo * FindFabricWithIndex(FabricIndex fabricIndex) const;
443 :
444 : /**
445 : * Finds a matching FabricInfo instance given a root public key, fabric ID AND a matching NodeId. This variant of find
446 : * is only to be used when it is possible to have colliding fabrics in the table that are on the same logical fabric
447 : * but may be associated with different node identities.
448 : *
449 : * Returns nullptr if no matching instance is found.
450 : *
451 : */
452 : const FabricInfo * FindIdentity(const Crypto::P256PublicKey & rootPubKey, FabricId fabricId, NodeId nodeId) const;
453 :
454 : /**
455 : * Finds a matching FabricInfo instance given a compressed fabric ID. If there are multiple
456 : * matching FabricInfo instances given the low but non-zero probability of collision, there is no guarantee
457 : * on which instance will be returned.
458 : *
459 : * Returns nullptr if no matching instance is found.
460 : */
461 : const FabricInfo * FindFabricWithCompressedId(CompressedFabricId compressedFabricId) const;
462 :
463 : CHIP_ERROR Init(const FabricTable::InitParams & initParams);
464 : void Shutdown();
465 :
466 : /**
467 : * @brief If `Init()` caused a Delete due to partial commit, the fabric index at play is returned.
468 : *
469 : * Allows caller to schedule more clean-up. This is because at Init() time, none of the delegates
470 : * are registered yet, so no other modules would learn of the removal.
471 : *
472 : * The value is auto-reset to `kUndefinedFabricIndex` on being returned, so that subsequent
473 : * `GetDeletedFabricFromCommitMarker()` after one that has a fabric index to give will provide
474 : * `kUndefinedFabricIndex`.
475 : *
476 : * @return the fabric index of a just-deleted fabric, or kUndefinedFabricIndex if none were deleted.
477 : */
478 : FabricIndex GetDeletedFabricFromCommitMarker();
479 :
480 : /**
481 : * @brief Clear the commit marker when we are sure we have proceeded with any remaining clean-up
482 : */
483 : void ClearCommitMarker();
484 :
485 : // Forget a fabric in memory: doesn't delete any persistent state, just
486 : // reverts any pending state (blindly) and then resets the fabric table
487 : // entry.
488 : //
489 : // TODO: We have to determine if we should remove this call.
490 : void Forget(FabricIndex fabricIndex);
491 :
492 : CHIP_ERROR AddFabricDelegate(FabricTable::Delegate * delegate);
493 : void RemoveFabricDelegate(FabricTable::Delegate * delegate);
494 :
495 : /**
496 : * @brief Set the Fabric Label for the fabric referred by `fabricIndex`.
497 : *
498 : * If a fabric add/update is pending, only the pending version will be updated,
499 : * so that on fail-safe expiry, you would actually see the only fabric label if
500 : * Update fails. If the fabric label is set before UpdateNOC, then the change is immediate.
501 : *
502 : * @param fabricIndex - Fabric Index for which to set the label
503 : * @param fabricLabel - Label to set on the fabric
504 : * @retval CHIP_NO_ERROR on success
505 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if fabricIndex does not refer to an fabric in the table
506 : * @retval CHIP_ERROR_INVALID_ARGUMENT on fabric label error (e.g. too large)
507 : * @retval other CHIP_ERROR on internal errors
508 : */
509 : CHIP_ERROR SetFabricLabel(FabricIndex fabricIndex, const CharSpan & fabricLabel);
510 :
511 : /**
512 : * @brief Get the Fabric Label for a given fabric
513 : *
514 : * NOTE: The outFabricLabel argument points to internal memory of the fabric info.
515 : * It may become invalid on the next FabricTable API call due to shadow
516 : * storage of data.
517 : *
518 : * @param fabricIndex - Fabric index for which to get the label
519 : * @param outFabricLabel - char span that will be set to the label value
520 : * @retval CHIP_NO_ERROR on success
521 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX on error
522 : * @retval other CHIP_ERROR on internal errors
523 : */
524 : CHIP_ERROR GetFabricLabel(FabricIndex fabricIndex, CharSpan & outFabricLabel);
525 :
526 : /**
527 : * Get the current Last Known Good Time.
528 : *
529 : * @param lastKnownGoodChipEpochTime (out) the current last known good time, if any is known
530 : * @return CHIP_NO_ERROR on success, else an appropriate CHIP_ERROR
531 : */
532 0 : CHIP_ERROR GetLastKnownGoodChipEpochTime(System::Clock::Seconds32 & lastKnownGoodChipEpochTime) const
533 : {
534 0 : return mLastKnownGoodTime.GetLastKnownGoodChipEpochTime(lastKnownGoodChipEpochTime);
535 : }
536 :
537 : /**
538 : * Validate that the passed Last Known Good Time is within bounds and then
539 : * store this and write back to storage. Legal values are those which are
540 : * not earlier than firmware build time or any of our stored certificates'
541 : * NotBefore times:
542 : *
543 : * 3.5.6.1. Last Known Good UTC Time
544 : *
545 : * A Node MAY adjust the Last Known Good UTC Time backwards if it
546 : * believes the current Last Known Good UTC Time is incorrect and it has
547 : * a good time value from a trusted source. The Node SHOULD NOT adjust
548 : * the Last Known Good UTC to a time before the later of:
549 : * • The build timestamp of its currently running software image
550 : * • The not-before timestamp of any of its operational certificates
551 : *
552 : * @param lastKnownGoodChipEpochTime Last Known Good Time in seconds since CHIP epoch
553 : * @return CHIP_NO_ERROR on success, else an appopriate CHIP_ERROR
554 : */
555 : CHIP_ERROR SetLastKnownGoodChipEpochTime(System::Clock::Seconds32 lastKnownGoodChipEpochTime);
556 :
557 : /**
558 : * @return the number of fabrics currently accessible/usable/iterable.
559 : */
560 9 : uint8_t FabricCount() const { return mFabricCount; }
561 :
562 654 : ConstFabricIterator cbegin() const
563 : {
564 654 : const FabricInfo * pending = GetShadowPendingFabricEntry();
565 654 : return ConstFabricIterator(mStates, pending, 0, CHIP_CONFIG_MAX_FABRICS);
566 : }
567 654 : ConstFabricIterator cend() const
568 : {
569 654 : return ConstFabricIterator(mStates, nullptr, CHIP_CONFIG_MAX_FABRICS, CHIP_CONFIG_MAX_FABRICS);
570 : }
571 654 : ConstFabricIterator begin() const { return cbegin(); }
572 654 : ConstFabricIterator end() const { return cend(); }
573 :
574 : /**
575 : * @brief Get the RCAC (operational root certificate) associated with a fabric.
576 : *
577 : * If a root is pending for `fabricIndex` from `AddNewPendingTrustedRootCert`, it is returned.
578 : *
579 : * @param fabricIndex - Fabric for which to get the RCAC
580 : * @param outCert - MutableByteSpan to receive the certificate. Resized to actual size.
581 : * @retval CHIP_NO_ERROR on success
582 : * @retval CHIP_ERROR_BUFFER_TOO_SMALL if `outCert` is too small
583 : * @retval CHIP_ERROR_NOT_FOUND if not found/available
584 : * @retval other CHIP_ERROR values on invalid arguments or internal errors.
585 : */
586 : CHIP_ERROR FetchRootCert(FabricIndex fabricIndex, MutableByteSpan & outCert) const;
587 :
588 : /**
589 : * @brief Get the pending root certificate which is not associated with a fabric, if there is one.
590 : *
591 : * If a root is pending from `AddNewPendingTrustedRootCert`, and there is no
592 : * fabric associated with the corresponding fabric index yet
593 : * (i.e. `AddNewPendingFabric*` has not been called yet) it is returned.
594 : *
595 : * @param outCert - MutableByteSpan to receive the certificate. Resized to actual size.
596 : * @retval CHIP_NO_ERROR on success
597 : * @retval CHIP_ERROR_BUFFER_TOO_SMALL if `outCert` is too small.
598 : * @retval CHIP_ERROR_NOT_FOUND if there is no pending root certificate
599 : * that's not yet associated with a fabric.
600 : * @retval other CHIP_ERROR values on invalid arguments or internal errors.
601 : */
602 : CHIP_ERROR FetchPendingNonFabricAssociatedRootCert(MutableByteSpan & outCert) const;
603 :
604 : /**
605 : * @brief Get the ICAC (operational intermediate certificate) associated with a fabric.
606 : *
607 : * If a fabric is pending from add/update operation for the given `fabricIndex`, its
608 : * ICAC is returned.
609 : *
610 : * If an NOC exists, but the ICAC is not present in the chain, CHIP_NO_ERROR is
611 : * returned and `outCert` is resized to 0 length so that its `empty()` method returns true.
612 : *
613 : * @param fabricIndex - Fabric for which to get the ICAC
614 : * @param outCert - MutableByteSpan to receive the certificate. Resized to actual size.
615 : * @retval CHIP_NO_ERROR on success, including if absent within an existing chain
616 : * @retval CHIP_ERROR_BUFFER_TOO_SMALL if `outCert` is too small
617 : * @retval CHIP_ERROR_NOT_FOUND if not found/available
618 : * @retval other CHIP_ERROR values on invalid arguments or internal errors.
619 : */
620 : CHIP_ERROR FetchICACert(FabricIndex fabricIndex, MutableByteSpan & outCert) const;
621 :
622 : /**
623 : * @brief Get the NOC (Node Operational Certificate) associated with a fabric.
624 : *
625 : * If a fabric is pending from add/update operation for the given `fabricIndex`, its
626 : * NOC is returned.
627 : *
628 : * @param fabricIndex - Fabric for which to get the NOC
629 : * @param outCert - MutableByteSpan to receive the certificate. Resized to actual size.
630 : * @retval CHIP_NO_ERROR on success
631 : * @retval CHIP_ERROR_BUFFER_TOO_SMALL if `outCert` is too small
632 : * @retval CHIP_ERROR_NOT_FOUND if not found/available
633 : * @retval other CHIP_ERROR values on invalid arguments or internal errors.
634 : */
635 : CHIP_ERROR FetchNOCCert(FabricIndex fabricIndex, MutableByteSpan & outCert) const;
636 :
637 : /**
638 : * @brief Get the root public key by value for the given `fabricIndex`.
639 : *
640 : * @param fabricIndex - Fabric for which to get the root public key (subject public key of RCAC)
641 : * @param outPublicKey - PublicKey instance to receive the public key contents
642 : * @retval CHIP_NO_ERROR on success
643 : * @retval CHIP_ERROR_BUFFER_TOO_SMALL if `outCert` is too small
644 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if not found/available, or `fabricIndex` has a bad value
645 : * @retval other CHIP_ERROR values on other invalid arguments or internal errors.
646 : */
647 : CHIP_ERROR FetchRootPubkey(FabricIndex fabricIndex, Crypto::P256PublicKey & outPublicKey) const;
648 :
649 : /**
650 : * @brief Get the CASE Authenticated Tags from the NOC for the given `fabricIndex`.
651 : *
652 : * @param fabricIndex - Fabric for which to get the root public key (subject public key of RCAC)
653 : * @param cats - CATValues struct to write the NOC CATs for the given fabric index
654 : * @retval CHIP_NO_ERROR on success
655 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if not found/available, or `fabricIndex` has a bad value
656 : * @retval other CHIP_ERROR values on other invalid arguments or internal errors.
657 : */
658 : CHIP_ERROR FetchCATs(const FabricIndex fabricIndex, CATValues & cats) const;
659 :
660 : /**
661 : * @brief Sign a message with a given fabric's operational keypair. This is used for
662 : * CASE and the only way the key should be used.
663 : *
664 : * This will use a pending key activated with `ActivatePendingOperationalKey` but
665 : * not yet persisted, if one is available for the fabric.
666 : *
667 : * @param fabricIndex - Fabric index whose operational key touse
668 : * @param message - Message to sign
669 : * @param outSignature - Signature object to receive the signature
670 : *
671 : * @retval CHIP_NO_ERROR on success
672 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if no active key is found for the given `fabricIndex` or if
673 : * `fabricIndex` is invalid.
674 : * @retval other CHIP_ERROR value on internal errors
675 : */
676 : CHIP_ERROR SignWithOpKeypair(FabricIndex fabricIndex, ByteSpan message, Crypto::P256ECDSASignature & outSignature) const;
677 :
678 : /**
679 : * @brief Create an ephemeral keypair for use in session establishment.
680 : *
681 : * WARNING: The return value MUST be released by `ReleaseEphemeralKeypair`. This is because
682 : * Matter CHIPMem.h does not properly support UniquePtr in a way that would
683 : * safely allow classes derived from Crypto::P256Keypair to be released properly.
684 : *
685 : * This delegates to the OperationalKeystore if one exists, otherwise it directly allocates a base
686 : * Crypto::P256Keypair instance
687 : *
688 : * @return a pointer to a dynamically P256Keypair (or derived class thereof), which may evaluate to nullptr
689 : * if running out of memory.
690 : */
691 : Crypto::P256Keypair * AllocateEphemeralKeypairForCASE();
692 :
693 : /**
694 : * @brief Release an ephemeral keypair previously created by `AllocateEphemeralKeypairForCASE()`
695 : */
696 : void ReleaseEphemeralKeypair(Crypto::P256Keypair * keypair);
697 :
698 : /**
699 : * This initializes a new keypair for the given fabric and generates a CSR for it,
700 : * so that it can be passed in a CSRResponse.
701 : *
702 : * The keypair is temporary and becomes usable for `SignWithOpKeypair` only after either
703 : * `ActivatePendingOperationalKey` is called. It is destroyed if
704 : * `RevertPendingFabricData` is called before `CommitPendingFabricData`.
705 : * If a pending keypair for the provided fabricIndex (if present) already existed, it is replaced by this call.
706 : *
707 : * Only one pending operational keypair is supported at a time.
708 : *
709 : * @param fabricIndex - Existing FabricIndex for which a new keypair must be made available. If it
710 : * doesn't have a value, the key will be marked pending for the next available
711 : * fabric index that would apply for `AddNewFabric`.
712 : * @param outputCsr - Buffer to contain the CSR. Must be at least `kMIN_CSR_Buffer_Size` large.
713 : *
714 : * @retval CHIP_NO_ERROR on success
715 : * @retval CHIP_ERROR_BUFFER_TOO_SMALL if `outputCsr` buffer is too small
716 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there is already a pending keypair for another `fabricIndex` value
717 : * or if fabricIndex is an invalid value.
718 : * @retval other CHIP_ERROR value on internal errors
719 : */
720 : CHIP_ERROR AllocatePendingOperationalKey(Optional<FabricIndex> fabricIndex, MutableByteSpan & outputCsr);
721 :
722 : /**
723 : * @brief Returns whether an operational key is pending (true if `AllocatePendingOperationalKey` was
724 : * previously successfully called, false otherwise).
725 : *
726 : * @param outIsPendingKeyForUpdateNoc this is set to true if the `AllocatePendingOperationalKey` had an
727 : * associated fabric index attached, indicating it's for UpdateNoc
728 : */
729 : bool HasPendingOperationalKey(bool & outIsPendingKeyForUpdateNoc) const;
730 :
731 : /**
732 : * @brief Returns whether an operational key can be used to sign for given FabricIndex
733 : *
734 : * @param fabricIndex - Fabric index for which an operational key must be found
735 : * @return true if a pending fabric or committed fabric for fabricIndex has an operational key, false otherwise.
736 : */
737 : bool HasOperationalKeyForFabric(FabricIndex fabricIndex) const;
738 :
739 : /**
740 : * @brief Returns the operational keystore. This is used for
741 : * CASE and the only way the keystore should be used.
742 : *
743 : * @return The operational keystore, nullptr otherwise.
744 : */
745 7 : const Crypto::OperationalKeystore * GetOperationalKeystore() { return mOperationalKeystore; }
746 :
747 : /**
748 : * @brief Add a pending trusted root certificate for the next fabric created with `AddNewPendingFabric*` methods.
749 : *
750 : * The root only becomes actually pending when the `AddNewPendingFabric*` is called afterwards. It is reverted
751 : * if `RevertPendingFabricData` is called.
752 : *
753 : * This method with fail with CHIP_ERROR_INCORRECT_STATE in a variety of illogical/inconsistent conditions,
754 : * which always can be cleared with `RevertPendingFabricData`. Such a situation is calling this method after
755 : * `UpdatePendingFabric` which would mean logical collision of an addition and an update.
756 : *
757 : * @param rcac - Root certificate in Matter Operational Certificate Encoding (TLV) format
758 : * @retval CHIP_NO_ERROR on success
759 : * @retval CHIP_ERROR_INCORRECT_STATE if this is called in an inconsistent order
760 : * @retval CHIP_ERROR_NO_MEMORY if there is insufficient memory to make the root pending
761 : * @retval CHIP_ERROR_INVALID_ARGUMENT if the RCAC is too large (further checks are done on `AddNewPendingFabric*`)
762 : * @retval other CHIP_ERROR on internal errors.
763 : */
764 : CHIP_ERROR AddNewPendingTrustedRootCert(const ByteSpan & rcac);
765 :
766 : /**
767 : * @brief Use an NOC and optional ICAC chaining back to the pending RCAC to activate a new fabric
768 : *
769 : * Operational key is assumed to be pending or committed in the associated mOperationalKeystore.
770 : *
771 : * The fabric becomes temporarily active for purposes of `Fetch*` and `SignWithOpKeyPair`, etc.
772 : * The new fabric becomes permanent/persisted on successful `CommitPendingFabricData`. It disappears
773 : * on `RevertPendingFabricData` or `RevertPendingOpCertsExceptRoot`.
774 : *
775 : * This method with fail with CHIP_ERROR_INCORRECT_STATE in a variety of illogical/inconsistent conditions,
776 : * which always can be cleared with `RevertPendingFabricData`. Such a situation is calling this method after
777 : * `UpdatePendingFabric*` which would mean logical collision of an addition and an update.
778 : *
779 : * If a pending key was present in the OperationalKeystore associated with this FabricTable,
780 : * it is activated on success.
781 : *
782 : *
783 : * @param noc - NOC for the fabric. Must match an existing or pending operational keypair in the mOperationalKeystore.
784 : * @param icac - ICAC for the fabric. Can be empty if absent from the chain.
785 : * @param vendorId - VendorID to use for the new fabric
786 : * @param outNewFabricIndex - Pointer where the new fabric index for the fabric just added will be set. Cannot be nullptr.
787 : *
788 : * @retval CHIP_NO_ERROR on success.
789 : * @retval CHIP_ERROR_INCORRECT_STATE if this is called in an inconsistent order.
790 : * @retval CHIP_ERROR_NO_MEMORY if there is insufficient memory to make the fabric pending.
791 : * @retval CHIP_ERROR_INVALID_ARGUMENT if any of the arguments are invalid such as too large or out of bounds.
792 : * @retval CHIP_ERROR_FABRIC_EXISTS if operational identity collides with one already present.
793 : * @retval other CHIP_ERROR_* on internal errors or certificate validation errors.
794 : */
795 0 : CHIP_ERROR AddNewPendingFabricWithOperationalKeystore(const ByteSpan & noc, const ByteSpan & icac, uint16_t vendorId,
796 : FabricIndex * outNewFabricIndex,
797 : AdvertiseIdentity advertiseIdentity = AdvertiseIdentity::Yes)
798 : {
799 0 : return AddNewPendingFabricCommon(noc, icac, vendorId, nullptr, false, advertiseIdentity, outNewFabricIndex);
800 : };
801 :
802 : /**
803 : * @brief Use an NOC and optional ICAC chaining back to the pending RCAC to activate a new fabric
804 : *
805 : * Operational key is injected, and then owned by the fabric (!isExistingOpKeyExternallyOwned) or
806 : * owned externally if `isExistingOpKeyExternallyOwned` is true).
807 : *
808 : * WARNING: Copying keypairs is unsafe and not recommended. Consider using
809 : * AddNewPendingFabricWithOperationalKeystore and an associated OperationalKeystore
810 : * or always using `isExistingOpKeyExternallyOwned`, with `existingOpKey` being a safe
811 : * class derived from P256Keypair that avoids the true private key persisting in memory.
812 : *
813 : * For rest of semantics outside of operational key, @see AddNewPendingFabricWithOperationalKeystore
814 : *
815 : * @param noc - NOC for the fabric. Public key must match the `existingOpKey`'s public key
816 : * @param icac - ICAC for the fabric. Can be empty if absent from the chain.
817 : * @param vendorId - VendorID to use for the new fabric
818 : * @param existingOpKey - Existing operational key to ingest for use in the fabric. Cannot be nullptr.
819 : * @param isExistingOpKeyExternallyOwned - if true, operational key must outlive the fabric. If false, the key is
820 : * copied using P256Keypair::Serialize/Deserialize and owned in heap of a FabricInfo.
821 : * @param outNewFabricIndex - Pointer where the new fabric index for the fabric just added will be set. Cannot be nullptr.
822 : *
823 : * @retval CHIP_NO_ERROR on success.
824 : * @retval CHIP_ERROR_INCORRECT_STATE if this is called in an inconsistent order.
825 : * @retval CHIP_ERROR_NO_MEMORY if there is insufficient memory to make the fabric pending.
826 : * @retval CHIP_ERROR_INVALID_ARGUMENT if any of the arguments are invalid such as too large or out of bounds.
827 : * @retval CHIP_ERROR_FABRIC_EXISTS if operational identity collides with one already present.
828 : * @retval other CHIP_ERROR_* on internal errors or certificate validation errors.
829 : */
830 597 : CHIP_ERROR AddNewPendingFabricWithProvidedOpKey(const ByteSpan & noc, const ByteSpan & icac, uint16_t vendorId,
831 : Crypto::P256Keypair * existingOpKey, bool isExistingOpKeyExternallyOwned,
832 : FabricIndex * outNewFabricIndex,
833 : AdvertiseIdentity advertiseIdentity = AdvertiseIdentity::Yes)
834 : {
835 597 : return AddNewPendingFabricCommon(noc, icac, vendorId, existingOpKey, isExistingOpKeyExternallyOwned, advertiseIdentity,
836 597 : outNewFabricIndex);
837 : };
838 :
839 : /**
840 : * @brief Use an NOC and optional ICAC to update an existing fabric
841 : *
842 : * Operational key is assumed to be pending or committed in the associated mOperationalKeystore.
843 : *
844 : * The new NOC chain becomes temporarily active for purposes of `Fetch*` and `SignWithOpKeyPair`, etc.
845 : * The RCAC remains as before. For this method call to succeed, NOC chain must chain back to the existing RCAC.
846 : * The update fabric becomes permanent/persisted on successful `CommitPendingFabricData`. Changes revert
847 : * on `RevertPendingFabricData` or `RevertPendingOpCertsExceptRoot`. FabricId CANNOT be updated, but
848 : * CAT tags and Node ID in NOC can change between previous and new NOC for a given FabricId.
849 : *
850 : * This method with fail with CHIP_ERROR_INCORRECT_STATE in a variety of illogical/inconsistent conditions,
851 : * which always can be cleared with `RevertPendingFabricData`. Such a situation is calling this method after
852 : * `AddNewPending*` which would mean logical collision of an addition and an update.
853 : *
854 : * If a pending key was present in the OperationalKeystore associated with this FabricTable,
855 : * it is activated on success.
856 : *
857 : * @param fabricIndex - fabricIndex of the existing fabric to update
858 : * @param noc - Updated NOC for the fabric. Must match an existing or pending operational keypair in the mOperationalKeystore.
859 : * @param icac - Update ICAC for the fabric. Can be empty if absent from the chain.
860 : *
861 : * @retval CHIP_NO_ERROR on success
862 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if the `fabricIndex` is not an existing fabric
863 : * @retval CHIP_ERROR_INCORRECT_STATE if this is called in an inconsistent order
864 : * @retval CHIP_ERROR_NO_MEMORY if there is insufficient memory to store the pending updates
865 : * @retval CHIP_ERROR_INVALID_ARGUMENT if any of the arguments are invalid such as too large or out of bounds.
866 : * @retval other CHIP_ERROR_* on internal errors or certificate validation errors.
867 : */
868 0 : CHIP_ERROR UpdatePendingFabricWithOperationalKeystore(FabricIndex fabricIndex, const ByteSpan & noc, const ByteSpan & icac,
869 : AdvertiseIdentity advertiseIdentity = AdvertiseIdentity::Yes)
870 : {
871 0 : return UpdatePendingFabricCommon(fabricIndex, noc, icac, nullptr, false, advertiseIdentity);
872 : }
873 :
874 : /**
875 : * @brief Use an NOC and optional ICAC to update an existing fabric
876 : *
877 : * Operational key is injected, and then owned by the fabric (!isExistingOpKeyExternallyOwned) or
878 : * owned externally if `isExistingOpKeyExternallyOwned` is true).
879 : *
880 : * WARNING: Copying keypairs is unsafe and not recommended. Consider using
881 : * AddNewPendingFabricWithOperationalKeystore and an associated OperationalKeystore
882 : * or always using `isExistingOpKeyExternallyOwned`, with `existingOpKey` being a safe
883 : * class derived from P256Keypair that avoids the true private key persisting in memory.
884 : *
885 : * For rest of semantics outside of operational key, @see UpdatePendingFabricWithOperationalKeystore
886 : *
887 : * @param fabricIndex - fabricIndex of the existing fabric to update
888 : * @param noc - Updated NOC for the fabric. Must match an existing or pending operational keypair in the mOperationalKeystore.
889 : * @param icac - Update ICAC for the fabric. Can be empty if absent from the chain.
890 : * @param existingOpKey - Existing operational key to ingest for use in the fabric with new NOC. Cannot be nullptr.
891 : * @param isExistingOpKeyExternallyOwned - if true, operational key must outlive the fabric. If false, the key is
892 : * copied using P256Keypair::Serialize/Deserialize and owned in heap of a FabricInfo.
893 : *
894 : * @retval CHIP_NO_ERROR on success
895 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if the `fabricIndex` is not an existing fabric
896 : * @retval CHIP_ERROR_INCORRECT_STATE if this is called in an inconsistent order
897 : * @retval CHIP_ERROR_NO_MEMORY if there is insufficient memory to store the pending updates
898 : * @retval CHIP_ERROR_INVALID_ARGUMENT if any of the arguments are invalid such as too large or out of bounds.
899 : * @retval other CHIP_ERROR_* on internal errors or certificate validation errors.
900 : */
901 :
902 0 : CHIP_ERROR UpdatePendingFabricWithProvidedOpKey(FabricIndex fabricIndex, const ByteSpan & noc, const ByteSpan & icac,
903 : Crypto::P256Keypair * existingOpKey, bool isExistingOpKeyExternallyOwned,
904 : AdvertiseIdentity advertiseIdentity = AdvertiseIdentity::Yes)
905 : {
906 0 : return UpdatePendingFabricCommon(fabricIndex, noc, icac, existingOpKey, isExistingOpKeyExternallyOwned, advertiseIdentity);
907 : }
908 :
909 : /**
910 : * @brief Commit any pending temporary FabricTable state. This is used mostly for affecting
911 : * CommissioningComplete.
912 : *
913 : * On success, any pending information is committed such that after a restart, it would
914 : * be found to be the same in persistent storage.
915 : *
916 : * If no changes were pending and state is internally consistent, this appears as a no-op and returns
917 : * CHIP_NO_ERROR.
918 : *
919 : * If there is any internally inconsistent state, this methods acts the same as RevertPendingFabricData(),
920 : * and all state is lost.
921 : *
922 : * In rare circumstances, and depending on the storage backend for opcerts and operational keys,
923 : * an inconsistent state could be left, such as if restarting during storage writes of
924 : * CommitPendingFabricData(). If this happens, the next FabricTable::Init() will attempt
925 : * to clean-up the pieces.
926 : *
927 : * @return CHIP_NO_ERROR on success or any other CHIP_ERROR value on internal errors
928 : */
929 : CHIP_ERROR CommitPendingFabricData();
930 :
931 : /**
932 : * @brief Revert any pending state.
933 : *
934 : * This is used to handle fail-safe expiry of partially configured fabrics, or to recover
935 : * from situations where partial state was written and configuration cannot continue properly.
936 : *
937 : * All pending certificates and operational keys and pending fabric metadata are cleared.
938 : */
939 : void RevertPendingFabricData();
940 :
941 : /**
942 : * @brief Revert only the pending NOC/ICAC and pending added fabric, not RCAC. Used for error handling
943 : * during commissioning.
944 : */
945 : void RevertPendingOpCertsExceptRoot();
946 :
947 : // Verifies credentials, using the root certificate of the provided fabric index.
948 : CHIP_ERROR VerifyCredentials(FabricIndex fabricIndex, const ByteSpan & noc, const ByteSpan & icac,
949 : Credentials::ValidationContext & context, CompressedFabricId & outCompressedFabricId,
950 : FabricId & outFabricId, NodeId & outNodeId, Crypto::P256PublicKey & outNocPubkey,
951 : Crypto::P256PublicKey * outRootPublicKey = nullptr) const;
952 :
953 : // Verifies credentials, using the provided root certificate.
954 : static CHIP_ERROR VerifyCredentials(const ByteSpan & noc, const ByteSpan & icac, const ByteSpan & rcac,
955 : Credentials::ValidationContext & context, CompressedFabricId & outCompressedFabricId,
956 : FabricId & outFabricId, NodeId & outNodeId, Crypto::P256PublicKey & outNocPubkey,
957 : Crypto::P256PublicKey * outRootPublicKey = nullptr);
958 : /**
959 : * @brief Enables FabricInfo instances to collide and reference the same logical fabric (i.e Root Public Key + FabricId).
960 : *
961 : * *WARNING* This is ONLY to be used when creating multiple controllers on the same fabric OR for test.
962 : *
963 : */
964 0 : void PermitCollidingFabrics() { mStateFlags.Set(StateFlags::kAreCollidingFabricsIgnored); }
965 :
966 : // Add a new fabric for testing. The Operational Key is a raw P256Keypair (public key and private key raw bits) that will
967 : // get copied (directly) into the fabric table.
968 : CHIP_ERROR AddNewFabricForTest(const ByteSpan & rootCert, const ByteSpan & icacCert, const ByteSpan & nocCert,
969 : const ByteSpan & opKeySpan, FabricIndex * outFabricIndex);
970 :
971 : // Same as AddNewFabricForTest, but ignore if we are colliding with same <Root Public Key, Fabric Id>, so
972 : // that a single fabric table can have N nodes for same fabric. This usually works, but is bad form.
973 : CHIP_ERROR AddNewFabricForTestIgnoringCollisions(const ByteSpan & rootCert, const ByteSpan & icacCert, const ByteSpan & nocCert,
974 : const ByteSpan & opKeySpan, FabricIndex * outFabricIndex)
975 : {
976 : PermitCollidingFabrics();
977 : CHIP_ERROR err = AddNewFabricForTest(rootCert, icacCert, nocCert, opKeySpan, outFabricIndex);
978 : mStateFlags.Clear(StateFlags::kAreCollidingFabricsIgnored);
979 : return err;
980 : }
981 :
982 : // For test only. See definition of `StateFlags::kAbortCommitForTest`.
983 : void SetForceAbortCommitForTest(bool abortCommitForTest)
984 : {
985 : (void) abortCommitForTest;
986 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
987 : if (abortCommitForTest)
988 : {
989 : mStateFlags.Set(StateFlags::kAbortCommitForTest);
990 : }
991 : else
992 : {
993 : mStateFlags.Clear(StateFlags::kAbortCommitForTest);
994 : }
995 : #endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
996 : }
997 :
998 : /**
999 : * Get the fabric index that will be used for the next fabric that will be
1000 : * added. Returns error if no more fabrics can be added, otherwise writes
1001 : * the fabric index that will be used for the next addition into the
1002 : * outparam.
1003 : */
1004 : CHIP_ERROR PeekFabricIndexForNextAddition(FabricIndex & outIndex);
1005 :
1006 : private:
1007 : enum class StateFlags : uint16_t
1008 : {
1009 : // If true, we are in the process of a fail-safe and there was at least one
1010 : // operation that caused partial data in the fabric table.
1011 : kIsPendingFabricDataPresent = (1u << 0),
1012 : kIsTrustedRootPending = (1u << 1),
1013 : kIsUpdatePending = (1u << 2),
1014 : kIsAddPending = (1u << 3),
1015 :
1016 : // Only true when `AllocatePendingOperationalKey` has been called
1017 : kIsOperationalKeyPending = (1u << 4),
1018 : // True if `AllocatePendingOperationalKey` was for an existing fabric
1019 : kIsPendingKeyForUpdateNoc = (1u << 5),
1020 :
1021 : // True if we allow more than one fabric with same root and fabricId in the fabric table
1022 : // for test purposes. This disables a collision check.
1023 : kAreCollidingFabricsIgnored = (1u << 6),
1024 :
1025 : // If set to true (only possible on test builds), will cause `CommitPendingFabricData()` to early
1026 : // return during commit, skipping clean-ups, so that we can validate commit marker fabric removal.
1027 : kAbortCommitForTest = (1u << 7),
1028 : };
1029 :
1030 : // Stored to indicate a commit is in progress, so that it can be cleaned-up on next boot
1031 : // if stopped in the middle.
1032 : struct CommitMarker
1033 : {
1034 : CommitMarker() = default;
1035 630 : CommitMarker(FabricIndex fabricIndex_, bool isAddition_)
1036 630 : {
1037 630 : this->fabricIndex = fabricIndex_;
1038 630 : this->isAddition = isAddition_;
1039 630 : }
1040 : FabricIndex fabricIndex = kUndefinedFabricIndex;
1041 : bool isAddition = false;
1042 : };
1043 :
1044 : /**
1045 : * @brief Get a mutable FabricInfo entry from the table by FabricIndex.
1046 : *
1047 : * NOTE: This is private for use within the FabricTable itself. All mutations have to go through the
1048 : * FabricTable public methods that take a FabricIndex so that there are no mutations about which
1049 : * the FabricTable is unaware, since this would break expectations regarding shadow/pending
1050 : * entries used during fail-safe.
1051 : *
1052 : * @param fabricIndex - fabric index for which to get a mutable FabricInfo entry
1053 : * @return the FabricInfo entry for the fabricIndex if found, or nullptr if not found
1054 : */
1055 : FabricInfo * GetMutableFabricByIndex(FabricIndex fabricIndex);
1056 :
1057 : // Load a FabricInfo metatada item from storage for a given new fabric index. Returns internal error on failure.
1058 : CHIP_ERROR LoadFromStorage(FabricInfo * fabric, FabricIndex newFabricIndex);
1059 :
1060 : // Store a given fabric metadata directly/immediately. Used by internal operations.
1061 : CHIP_ERROR StoreFabricMetadata(const FabricInfo * fabricInfo) const;
1062 :
1063 : // Tries to set `mFabricIndexWithPendingState` and returns false if there's a clash.
1064 : bool SetPendingDataFabricIndex(FabricIndex fabricIndex);
1065 :
1066 : // Core validation logic for fabric additions/updates
1067 : CHIP_ERROR AddOrUpdateInner(FabricIndex fabricIndex, bool isAddition, Crypto::P256Keypair * existingOpKey,
1068 : bool isExistingOpKeyExternallyOwned, uint16_t vendorId, AdvertiseIdentity advertiseIdentity);
1069 :
1070 : // Common code for fabric addition, for either OperationalKeystore or injected key scenarios.
1071 : CHIP_ERROR AddNewPendingFabricCommon(const ByteSpan & noc, const ByteSpan & icac, uint16_t vendorId,
1072 : Crypto::P256Keypair * existingOpKey, bool isExistingOpKeyExternallyOwned,
1073 : AdvertiseIdentity advertiseIdentity, FabricIndex * outNewFabricIndex);
1074 :
1075 : // Common code for fabric updates, for either OperationalKeystore or injected key scenarios.
1076 : CHIP_ERROR UpdatePendingFabricCommon(FabricIndex fabricIndex, const ByteSpan & noc, const ByteSpan & icac,
1077 : Crypto::P256Keypair * existingOpKey, bool isExistingOpKeyExternallyOwned,
1078 : AdvertiseIdentity advertiseIdentity);
1079 :
1080 : // Common code for looking up a fabric given a root public key, a fabric ID and an optional node id scoped to that fabric.
1081 : const FabricInfo * FindFabricCommon(const Crypto::P256PublicKey & rootPubKey, FabricId fabricId,
1082 : NodeId nodeId = kUndefinedNodeId) const;
1083 :
1084 : /**
1085 : * UpdateNextAvailableFabricIndex should only be called when
1086 : * mNextAvailableFabricIndex has a value and that value stops being
1087 : * available. It will set mNextAvailableFabricIndex to the next available
1088 : * value, or no value if there is none available.
1089 : */
1090 : void UpdateNextAvailableFabricIndex();
1091 :
1092 : /**
1093 : * Ensure that we have a valid next available fabric index, if that's at all possible. This covers
1094 : * some FabricIndex allocation corner cases. After this is called, the only way we can fail to have
1095 : * a next available fabric index is if our fabric table is max-sized (254 entries) and full.
1096 : */
1097 : void EnsureNextAvailableFabricIndexUpdated();
1098 :
1099 : /**
1100 : * Store our current fabric index state: what our next available index is
1101 : * and what indices we're using right now.
1102 : */
1103 : CHIP_ERROR StoreFabricIndexInfo() const;
1104 :
1105 : /**
1106 : * @brief Delete all metadata from storage for the given fabric
1107 : *
1108 : * @param fabricIndex FabricIndex for which to delete the metadadata
1109 : * @return CHIP_NO_ERROR on success or another CHIP_ERROR on failure
1110 : */
1111 : CHIP_ERROR DeleteMetadataFromStorage(FabricIndex fabricIndex);
1112 :
1113 : /**
1114 : * @brief Determine if a collision (undesired on AddNOC, necessary on UpdateNOC) exists
1115 : * between the FabricID in the given noc, and the RCAC found for `currentFabricIndex`
1116 : * in the op cert store, against an existing fabric in the FabricTable (which could be pending)
1117 : *
1118 : * @param currentFabricIndex - pending fabricIndex for which we are trying to Add/Update a NOC
1119 : * @param noc - NOC cert received that contains FabricID whose collision we care to validate
1120 : * @param outMatchingFabricIndex - set to the FabricIndex matching the collision or kUndefinedFabricIndex on no collision found
1121 : * @return CHIP_NO_ERROR on successful update of outMatchingFabricIndex or other CHIP_ERROR on internal errors
1122 : */
1123 : CHIP_ERROR FindExistingFabricByNocChaining(FabricIndex currentFabricIndex, const ByteSpan & noc,
1124 : FabricIndex & outMatchingFabricIndex) const;
1125 :
1126 : /**
1127 : * @brief Get the shadow FabricInfo entry that is pending for updates, if an
1128 : * update is in progress.
1129 : *
1130 : * @return a pointer to the shadow pending fabric or nullptr if none is active.
1131 : */
1132 654 : const FabricInfo * GetShadowPendingFabricEntry() const { return HasPendingFabricUpdate() ? &mPendingFabric : nullptr; }
1133 :
1134 : // Returns true if we have a shadow entry pending for a fabric update.
1135 22624 : bool HasPendingFabricUpdate() const
1136 : {
1137 22624 : return mPendingFabric.IsInitialized() &&
1138 22624 : mStateFlags.HasAll(StateFlags::kIsPendingFabricDataPresent, StateFlags::kIsUpdatePending);
1139 : }
1140 :
1141 : // Validate an NOC chain at time of adding/updating a fabric (uses VerifyCredentials with additional checks).
1142 : // The `existingFabricId` is passed for UpdateNOC, and must match the Fabric, to make sure that we are
1143 : // not trying to change FabricID with UpdateNOC. If set to kUndefinedFabricId, we are doing AddNOC and
1144 : // we don't need to check match to pre-existing fabric.
1145 : static CHIP_ERROR ValidateIncomingNOCChain(const ByteSpan & noc, const ByteSpan & icac, const ByteSpan & rcac,
1146 : FabricId existingFabricId, Credentials::CertificateValidityPolicy * policy,
1147 : CompressedFabricId & outCompressedFabricId, FabricId & outFabricId,
1148 : NodeId & outNodeId, Crypto::P256PublicKey & outNocPubkey,
1149 : Crypto::P256PublicKey & outRootPubkey);
1150 :
1151 : /**
1152 : * Read our fabric index info from the given TLV reader and set up the
1153 : * fabric table accordingly.
1154 : */
1155 : CHIP_ERROR ReadFabricInfo(TLV::ContiguousBufferTLVReader & reader);
1156 :
1157 : CHIP_ERROR NotifyFabricUpdated(FabricIndex fabricIndex);
1158 : CHIP_ERROR NotifyFabricCommitted(FabricIndex fabricIndex);
1159 :
1160 : // Commit management clean-up APIs
1161 : CHIP_ERROR StoreCommitMarker(const CommitMarker & commitMarker);
1162 : CHIP_ERROR GetCommitMarker(CommitMarker & outCommitMarker);
1163 :
1164 : FabricInfo mStates[CHIP_CONFIG_MAX_FABRICS];
1165 : // Used for UpdateNOC pending fabric updates
1166 : FabricInfo mPendingFabric;
1167 : PersistentStorageDelegate * mStorage = nullptr;
1168 : Crypto::OperationalKeystore * mOperationalKeystore = nullptr;
1169 : Credentials::OperationalCertificateStore * mOpCertStore = nullptr;
1170 :
1171 : // FabricTable::Delegate link to first node, since FabricTable::Delegate is a form
1172 : // of intrusive linked-list item.
1173 : FabricTable::Delegate * mDelegateListRoot = nullptr;
1174 :
1175 : // When mStateFlags.Has(kIsPendingFabricDataPresent) is true, this holds the index of the fabric
1176 : // for which there is currently pending data.
1177 : FabricIndex mFabricIndexWithPendingState = kUndefinedFabricIndex;
1178 :
1179 : // For when a revert occurs during init, so that more clean-up can be scheduled by caller.
1180 : FabricIndex mDeletedFabricIndexFromInit = kUndefinedFabricIndex;
1181 :
1182 : LastKnownGoodTime mLastKnownGoodTime;
1183 :
1184 : // We may not have an mNextAvailableFabricIndex if our table is as large as
1185 : // it can go and is full.
1186 : Optional<FabricIndex> mNextAvailableFabricIndex;
1187 : uint8_t mFabricCount = 0;
1188 :
1189 : BitFlags<StateFlags> mStateFlags;
1190 : };
1191 :
1192 : } // namespace chip
|