LCOV - code coverage report
Current view: top level - credentials - FabricTable.h (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 62 104 59.6 %
Date: 2024-02-15 08:20:41 Functions: 30 49 61.2 %

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

Generated by: LCOV version 1.14