Matter SDK Coverage Report
Current view: top level - credentials - FabricTable.h (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 60.0 % 105 63
Test Date: 2025-01-17 19:00:11 Functions: 62.0 % 50 31

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

Generated by: LCOV version 2.0-1