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