LCOV - code coverage report
Current view: top level - credentials - FabricTable.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 798 884 90.3 %
Date: 2024-02-15 08:20:41 Functions: 64 67 95.5 %

          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             : #include "FabricTable.h"
      23             : 
      24             : #include <lib/core/CHIPEncoding.h>
      25             : #include <lib/support/BufferWriter.h>
      26             : #include <lib/support/CHIPMem.h>
      27             : #include <lib/support/CHIPMemString.h>
      28             : #include <lib/support/DefaultStorageKeyAllocator.h>
      29             : #include <lib/support/SafeInt.h>
      30             : #include <lib/support/ScopedBuffer.h>
      31             : #include <platform/LockTracker.h>
      32             : #include <tracing/macros.h>
      33             : 
      34             : namespace chip {
      35             : using namespace Credentials;
      36             : using namespace Crypto;
      37             : 
      38             : using CertChainElement = chip::Credentials::OperationalCertificateStore::CertChainElement;
      39             : 
      40             : namespace {
      41             : 
      42             : static_assert(kMinValidFabricIndex <= CHIP_CONFIG_MAX_FABRICS, "Must support some fabrics.");
      43             : static_assert(CHIP_CONFIG_MAX_FABRICS <= kMaxValidFabricIndex, "Max fabric count out of range.");
      44             : 
      45             : // Tags for our metadata storage.
      46             : constexpr TLV::Tag kVendorIdTag    = TLV::ContextTag(0);
      47             : constexpr TLV::Tag kFabricLabelTag = TLV::ContextTag(1);
      48             : 
      49             : // Tags for our index list storage.
      50             : constexpr TLV::Tag kNextAvailableFabricIndexTag = TLV::ContextTag(0);
      51             : constexpr TLV::Tag kFabricIndicesTag            = TLV::ContextTag(1);
      52             : 
      53             : // Tags for commit marker storage
      54             : constexpr TLV::Tag kMarkerFabricIndexTag = TLV::ContextTag(0);
      55             : constexpr TLV::Tag kMarkerIsAdditionTag  = TLV::ContextTag(1);
      56             : 
      57             : constexpr size_t CommitMarkerContextTLVMaxSize()
      58             : {
      59             :     // Add 2x uncommitted uint64_t to leave space for backwards/forwards
      60             :     // versioning for this critical feature that runs at boot.
      61             :     return TLV::EstimateStructOverhead(sizeof(FabricIndex), sizeof(bool), sizeof(uint64_t), sizeof(uint64_t));
      62             : }
      63             : 
      64             : constexpr size_t IndexInfoTLVMaxSize()
      65             : {
      66             :     // We have a single next-available index and an array of anonymous-tagged
      67             :     // fabric indices.
      68             :     //
      69             :     // The max size of the list is (1 byte control + bytes for actual value)
      70             :     // times max number of list items, plus one byte for the list terminator.
      71             :     return TLV::EstimateStructOverhead(sizeof(FabricIndex), CHIP_CONFIG_MAX_FABRICS * (1 + sizeof(FabricIndex)) + 1);
      72             : }
      73             : 
      74             : } // anonymous namespace
      75             : 
      76         640 : CHIP_ERROR FabricInfo::Init(const FabricInfo::InitParams & initParams)
      77             : {
      78         640 :     ReturnErrorOnFailure(initParams.AreValid());
      79             : 
      80         640 :     Reset();
      81             : 
      82         640 :     mNodeId                  = initParams.nodeId;
      83         640 :     mFabricId                = initParams.fabricId;
      84         640 :     mFabricIndex             = initParams.fabricIndex;
      85         640 :     mCompressedFabricId      = initParams.compressedFabricId;
      86         640 :     mRootPublicKey           = initParams.rootPublicKey;
      87         640 :     mVendorId                = static_cast<VendorId>(initParams.vendorId);
      88         640 :     mShouldAdvertiseIdentity = initParams.advertiseIdentity;
      89             : 
      90             :     // Deal with externally injected keys
      91         640 :     if (initParams.operationalKeypair != nullptr)
      92             :     {
      93         619 :         if (initParams.hasExternallyOwnedKeypair)
      94             :         {
      95          23 :             ReturnErrorOnFailure(SetExternallyOwnedOperationalKeypair(initParams.operationalKeypair));
      96             :         }
      97             :         else
      98             :         {
      99         596 :             ReturnErrorOnFailure(SetOperationalKeypair(initParams.operationalKeypair));
     100             :         }
     101             :     }
     102             : 
     103         640 :     return CHIP_NO_ERROR;
     104             : }
     105             : 
     106           2 : void FabricInfo::operator=(FabricInfo && other)
     107             : {
     108           2 :     Reset();
     109             : 
     110           2 :     mNodeId                  = other.mNodeId;
     111           2 :     mFabricId                = other.mFabricId;
     112           2 :     mFabricIndex             = other.mFabricIndex;
     113           2 :     mCompressedFabricId      = other.mCompressedFabricId;
     114           2 :     mRootPublicKey           = other.mRootPublicKey;
     115           2 :     mVendorId                = other.mVendorId;
     116           2 :     mShouldAdvertiseIdentity = other.mShouldAdvertiseIdentity;
     117             : 
     118           2 :     SetFabricLabel(other.GetFabricLabel());
     119             : 
     120             :     // Transfer ownership of operational keypair (if it was nullptr, it stays that way).
     121           2 :     mOperationalKey                         = other.mOperationalKey;
     122           2 :     mHasExternallyOwnedOperationalKey       = other.mHasExternallyOwnedOperationalKey;
     123           2 :     other.mOperationalKey                   = nullptr;
     124           2 :     other.mHasExternallyOwnedOperationalKey = false;
     125             : 
     126           2 :     other.Reset();
     127           2 : }
     128             : 
     129         632 : CHIP_ERROR FabricInfo::CommitToStorage(PersistentStorageDelegate * storage) const
     130             : {
     131             :     {
     132             :         uint8_t buf[MetadataTLVMaxSize()];
     133         632 :         TLV::TLVWriter writer;
     134         632 :         writer.Init(buf);
     135             : 
     136             :         TLV::TLVType outerType;
     137         632 :         ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerType));
     138             : 
     139         632 :         ReturnErrorOnFailure(writer.Put(kVendorIdTag, mVendorId));
     140             : 
     141         632 :         ReturnErrorOnFailure(writer.PutString(kFabricLabelTag, CharSpan::fromCharString(mFabricLabel)));
     142             : 
     143         632 :         ReturnErrorOnFailure(writer.EndContainer(outerType));
     144             : 
     145         632 :         const auto metadataLength = writer.GetLengthWritten();
     146         632 :         VerifyOrReturnError(CanCastTo<uint16_t>(metadataLength), CHIP_ERROR_BUFFER_TOO_SMALL);
     147         632 :         ReturnErrorOnFailure(storage->SyncSetKeyValue(DefaultStorageKeyAllocator::FabricMetadata(mFabricIndex).KeyName(), buf,
     148             :                                                       static_cast<uint16_t>(metadataLength)));
     149             :     }
     150             : 
     151             :     // NOTE: Operational Key is never saved to storage here. See OperationalKeystore interface for how it is accessed
     152             : 
     153         632 :     return CHIP_NO_ERROR;
     154             : }
     155             : 
     156          12 : CHIP_ERROR FabricInfo::LoadFromStorage(PersistentStorageDelegate * storage, FabricIndex newFabricIndex, const ByteSpan & rcac,
     157             :                                        const ByteSpan & noc)
     158             : {
     159          12 :     mFabricIndex = newFabricIndex;
     160             : 
     161             :     // Regenerate operational metadata from NOC/RCAC
     162             :     {
     163          12 :         ReturnErrorOnFailure(ExtractNodeIdFabricIdFromOpCert(noc, &mNodeId, &mFabricId));
     164             : 
     165          12 :         P256PublicKeySpan rootPubKeySpan;
     166          12 :         ReturnErrorOnFailure(ExtractPublicKeyFromChipCert(rcac, rootPubKeySpan));
     167          12 :         mRootPublicKey = rootPubKeySpan;
     168             : 
     169             :         uint8_t compressedFabricIdBuf[sizeof(uint64_t)];
     170          12 :         MutableByteSpan compressedFabricIdSpan(compressedFabricIdBuf);
     171          12 :         ReturnErrorOnFailure(GenerateCompressedFabricId(mRootPublicKey, mFabricId, compressedFabricIdSpan));
     172             : 
     173             :         // Decode compressed fabric ID accounting for endianness, as GenerateCompressedFabricId()
     174             :         // returns a binary buffer and is agnostic of usage of the output as an integer type.
     175          12 :         mCompressedFabricId = Encoding::BigEndian::Get64(compressedFabricIdBuf);
     176             :     }
     177             : 
     178             :     // Load other storable metadata (label, vendorId, etc)
     179             :     {
     180             :         uint8_t buf[MetadataTLVMaxSize()];
     181          12 :         uint16_t size = sizeof(buf);
     182          12 :         ReturnErrorOnFailure(
     183             :             storage->SyncGetKeyValue(DefaultStorageKeyAllocator::FabricMetadata(mFabricIndex).KeyName(), buf, size));
     184          12 :         TLV::ContiguousBufferTLVReader reader;
     185          12 :         reader.Init(buf, size);
     186             : 
     187          12 :         ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
     188             :         TLV::TLVType containerType;
     189          12 :         ReturnErrorOnFailure(reader.EnterContainer(containerType));
     190             : 
     191          12 :         ReturnErrorOnFailure(reader.Next(kVendorIdTag));
     192          12 :         ReturnErrorOnFailure(reader.Get(mVendorId));
     193             : 
     194          12 :         ReturnErrorOnFailure(reader.Next(kFabricLabelTag));
     195          12 :         CharSpan label;
     196          12 :         ReturnErrorOnFailure(reader.Get(label));
     197             : 
     198          12 :         VerifyOrReturnError(label.size() <= kFabricLabelMaxLengthInBytes, CHIP_ERROR_BUFFER_TOO_SMALL);
     199          12 :         Platform::CopyString(mFabricLabel, label);
     200             : 
     201          12 :         ReturnErrorOnFailure(reader.ExitContainer(containerType));
     202          12 :         ReturnErrorOnFailure(reader.VerifyEndOfContainer());
     203             :     }
     204             : 
     205             :     // NOTE: Operational Key is never loaded here. See OperationalKeystore interface for how it is accessed
     206             : 
     207          12 :     return CHIP_NO_ERROR;
     208             : }
     209             : 
     210         645 : CHIP_ERROR FabricInfo::SetFabricLabel(const CharSpan & fabricLabel)
     211             : {
     212         645 :     Platform::CopyString(mFabricLabel, fabricLabel);
     213             : 
     214         645 :     return CHIP_NO_ERROR;
     215             : }
     216             : 
     217          17 : CHIP_ERROR FabricTable::DeleteMetadataFromStorage(FabricIndex fabricIndex)
     218             : {
     219          17 :     VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
     220          17 :     VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
     221             : 
     222          17 :     CHIP_ERROR deleteErr = mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::FabricMetadata(fabricIndex).KeyName());
     223             : 
     224          17 :     if (deleteErr != CHIP_NO_ERROR)
     225             :     {
     226          10 :         if (deleteErr == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
     227             :         {
     228          10 :             ChipLogError(FabricProvisioning, "Warning: metadata not found during delete of fabric 0x%x",
     229             :                          static_cast<unsigned>(fabricIndex));
     230             :         }
     231             :         else
     232             :         {
     233           0 :             ChipLogError(FabricProvisioning, "Error deleting metadata for fabric fabric 0x%x: %" CHIP_ERROR_FORMAT,
     234             :                          static_cast<unsigned>(fabricIndex), deleteErr.Format());
     235             :         }
     236             :     }
     237             : 
     238          17 :     return deleteErr;
     239             : }
     240             : 
     241         596 : CHIP_ERROR FabricInfo::SetOperationalKeypair(const P256Keypair * keyPair)
     242             : {
     243         596 :     VerifyOrReturnError(keyPair != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     244             : 
     245         596 :     P256SerializedKeypair serialized;
     246         596 :     ReturnErrorOnFailure(keyPair->Serialize(serialized));
     247             : 
     248         596 :     if (mHasExternallyOwnedOperationalKey)
     249             :     {
     250             :         // Drop it, so we will allocate an internally owned one.
     251           0 :         mOperationalKey                   = nullptr;
     252           0 :         mHasExternallyOwnedOperationalKey = false;
     253             :     }
     254             : 
     255         596 :     if (mOperationalKey == nullptr)
     256             :     {
     257         596 :         mOperationalKey = chip::Platform::New<P256Keypair>();
     258             :     }
     259         596 :     VerifyOrReturnError(mOperationalKey != nullptr, CHIP_ERROR_NO_MEMORY);
     260         596 :     return mOperationalKey->Deserialize(serialized);
     261         596 : }
     262             : 
     263          23 : CHIP_ERROR FabricInfo::SetExternallyOwnedOperationalKeypair(P256Keypair * keyPair)
     264             : {
     265          23 :     VerifyOrReturnError(keyPair != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     266          23 :     if (!mHasExternallyOwnedOperationalKey && mOperationalKey != nullptr)
     267             :     {
     268           0 :         chip::Platform::Delete(mOperationalKey);
     269           0 :         mOperationalKey = nullptr;
     270             :     }
     271             : 
     272          23 :     mHasExternallyOwnedOperationalKey = true;
     273          23 :     mOperationalKey                   = keyPair;
     274          23 :     return CHIP_NO_ERROR;
     275             : }
     276             : 
     277         644 : CHIP_ERROR FabricTable::ValidateIncomingNOCChain(const ByteSpan & noc, const ByteSpan & icac, const ByteSpan & rcac,
     278             :                                                  FabricId existingFabricId, Credentials::CertificateValidityPolicy * policy,
     279             :                                                  CompressedFabricId & outCompressedFabricId, FabricId & outFabricId,
     280             :                                                  NodeId & outNodeId, Crypto::P256PublicKey & outNocPubkey,
     281             :                                                  Crypto::P256PublicKey & outRootPubkey)
     282             : {
     283             :     MATTER_TRACE_SCOPE("ValidateIncomingNOCChain", "Fabric");
     284         644 :     Credentials::ValidationContext validContext;
     285             : 
     286             :     // Note that we do NOT set a time in the validation context.  This will
     287             :     // cause the certificate chain NotBefore / NotAfter time validation logic
     288             :     // to report CertificateValidityResult::kTimeUnknown.
     289             :     //
     290             :     // The default CHIPCert policy passes NotBefore / NotAfter validation for
     291             :     // this case where time is unknown.  If an override policy is passed, it
     292             :     // will be up to the passed policy to decide how to handle this.
     293             :     //
     294             :     // In the FabricTable::AddNewFabric and FabricTable::UpdateFabric calls,
     295             :     // the passed policy always passes for all questions of time validity.  The
     296             :     // rationale is that installed certificates should be valid at the time of
     297             :     // installation by definition.  If they are not and the commissionee and
     298             :     // commissioner disagree enough on current time, CASE will fail and our
     299             :     // fail-safe timer will expire.
     300             :     //
     301             :     // This then is ultimately how we validate that NotBefore / NotAfter in
     302             :     // newly installed certificates is workable.
     303         644 :     validContext.Reset();
     304         644 :     validContext.mRequiredKeyUsages.Set(KeyUsageFlags::kDigitalSignature);
     305         644 :     validContext.mRequiredKeyPurposes.Set(KeyPurposeFlags::kServerAuth);
     306         644 :     validContext.mValidityPolicy = policy;
     307             : 
     308         644 :     ChipLogProgress(FabricProvisioning, "Validating NOC chain");
     309         644 :     CHIP_ERROR err = FabricTable::VerifyCredentials(noc, icac, rcac, validContext, outCompressedFabricId, outFabricId, outNodeId,
     310             :                                                     outNocPubkey, &outRootPubkey);
     311         644 :     if (err != CHIP_NO_ERROR && err != CHIP_ERROR_WRONG_NODE_ID)
     312             :     {
     313           4 :         err = CHIP_ERROR_UNSUPPORTED_CERT_FORMAT;
     314             :     }
     315         644 :     if (err != CHIP_NO_ERROR)
     316             :     {
     317           4 :         ChipLogError(FabricProvisioning, "Failed NOC chain validation: %" CHIP_ERROR_FORMAT, err.Format());
     318             :     }
     319         644 :     ReturnErrorOnFailure(err);
     320             : 
     321             :     // Validate fabric ID match for cases like UpdateNOC.
     322         640 :     if (existingFabricId != kUndefinedFabricId)
     323             :     {
     324           4 :         VerifyOrReturnError(existingFabricId == outFabricId, CHIP_ERROR_UNSUPPORTED_CERT_FORMAT);
     325             :     }
     326             : 
     327         640 :     ChipLogProgress(FabricProvisioning, "NOC chain validation successful");
     328         640 :     return CHIP_NO_ERROR;
     329         644 : }
     330             : 
     331           8 : CHIP_ERROR FabricInfo::SignWithOpKeypair(ByteSpan message, P256ECDSASignature & outSignature) const
     332             : {
     333             :     MATTER_TRACE_SCOPE("SignWithOpKeypair", "Fabric");
     334           8 :     VerifyOrReturnError(mOperationalKey != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
     335             : 
     336           8 :     return mOperationalKey->ECDSA_sign_msg(message.data(), message.size(), outSignature);
     337             : }
     338             : 
     339          53 : CHIP_ERROR FabricInfo::FetchRootPubkey(Crypto::P256PublicKey & outPublicKey) const
     340             : {
     341             :     MATTER_TRACE_SCOPE("FetchRootPubKey", "Fabric");
     342          53 :     VerifyOrReturnError(IsInitialized(), CHIP_ERROR_KEY_NOT_FOUND);
     343          53 :     outPublicKey = mRootPublicKey;
     344          53 :     return CHIP_NO_ERROR;
     345             : }
     346             : 
     347           7 : CHIP_ERROR FabricTable::VerifyCredentials(FabricIndex fabricIndex, const ByteSpan & noc, const ByteSpan & icac,
     348             :                                           ValidationContext & context, CompressedFabricId & outCompressedFabricId,
     349             :                                           FabricId & outFabricId, NodeId & outNodeId, Crypto::P256PublicKey & outNocPubkey,
     350             :                                           Crypto::P256PublicKey * outRootPublicKey) const
     351             : {
     352             :     MATTER_TRACE_SCOPE("VerifyCredentials", "Fabric");
     353           7 :     assertChipStackLockedByCurrentThread();
     354             :     uint8_t rootCertBuf[kMaxCHIPCertLength];
     355           7 :     MutableByteSpan rootCertSpan{ rootCertBuf };
     356           7 :     ReturnErrorOnFailure(FetchRootCert(fabricIndex, rootCertSpan));
     357          14 :     return VerifyCredentials(noc, icac, rootCertSpan, context, outCompressedFabricId, outFabricId, outNodeId, outNocPubkey,
     358           7 :                              outRootPublicKey);
     359             : }
     360             : 
     361         658 : CHIP_ERROR FabricTable::VerifyCredentials(const ByteSpan & noc, const ByteSpan & icac, const ByteSpan & rcac,
     362             :                                           ValidationContext & context, CompressedFabricId & outCompressedFabricId,
     363             :                                           FabricId & outFabricId, NodeId & outNodeId, Crypto::P256PublicKey & outNocPubkey,
     364             :                                           Crypto::P256PublicKey * outRootPublicKey)
     365             : {
     366             :     // TODO - Optimize credentials verification logic
     367             :     //        The certificate chain construction and verification is a compute and memory intensive operation.
     368             :     //        It can be optimized by not loading certificate (i.e. rcac) that's local and implicitly trusted.
     369             :     //        The FindValidCert() algorithm will need updates to achieve this refactor.
     370         658 :     constexpr uint8_t kMaxNumCertsInOpCreds = 3;
     371             : 
     372         658 :     ChipCertificateSet certificates;
     373         658 :     ReturnErrorOnFailure(certificates.Init(kMaxNumCertsInOpCreds));
     374             : 
     375         658 :     ReturnErrorOnFailure(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)));
     376             : 
     377         658 :     if (!icac.empty())
     378             :     {
     379         648 :         ReturnErrorOnFailure(certificates.LoadCert(icac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kGenerateTBSHash)));
     380             :     }
     381             : 
     382         658 :     ReturnErrorOnFailure(certificates.LoadCert(noc, BitFlags<CertDecodeFlags>(CertDecodeFlags::kGenerateTBSHash)));
     383             : 
     384         658 :     const ChipDN & nocSubjectDN              = certificates.GetLastCert()[0].mSubjectDN;
     385         658 :     const CertificateKeyId & nocSubjectKeyId = certificates.GetLastCert()[0].mSubjectKeyId;
     386             : 
     387         658 :     const ChipCertificateData * resultCert = nullptr;
     388             :     // FindValidCert() checks the certificate set constructed by loading noc, icac and rcac.
     389             :     // It confirms that the certs link correctly (noc -> icac -> rcac), and have been correctly signed.
     390         658 :     ReturnErrorOnFailure(certificates.FindValidCert(nocSubjectDN, nocSubjectKeyId, context, &resultCert));
     391             : 
     392         654 :     ReturnErrorOnFailure(ExtractNodeIdFabricIdFromOpCert(certificates.GetLastCert()[0], &outNodeId, &outFabricId));
     393             : 
     394             :     CHIP_ERROR err;
     395         654 :     FabricId icacFabricId = kUndefinedFabricId;
     396         654 :     if (!icac.empty())
     397             :     {
     398         645 :         err = ExtractFabricIdFromCert(certificates.GetCertSet()[1], &icacFabricId);
     399         645 :         if (err == CHIP_NO_ERROR)
     400             :         {
     401          15 :             ReturnErrorCodeIf(icacFabricId != outFabricId, CHIP_ERROR_FABRIC_MISMATCH_ON_ICA);
     402             :         }
     403             :         // FabricId is optional field in ICAC and "not found" code is not treated as error.
     404         630 :         else if (err != CHIP_ERROR_NOT_FOUND)
     405             :         {
     406           0 :             return err;
     407             :         }
     408             :     }
     409             : 
     410         654 :     FabricId rcacFabricId = kUndefinedFabricId;
     411         654 :     err                   = ExtractFabricIdFromCert(certificates.GetCertSet()[0], &rcacFabricId);
     412         654 :     if (err == CHIP_NO_ERROR)
     413             :     {
     414         597 :         ReturnErrorCodeIf(rcacFabricId != outFabricId, CHIP_ERROR_WRONG_CERT_DN);
     415             :     }
     416             :     // FabricId is optional field in RCAC and "not found" code is not treated as error.
     417          57 :     else if (err != CHIP_ERROR_NOT_FOUND)
     418             :     {
     419           0 :         return err;
     420             :     }
     421             : 
     422             :     // Extract compressed fabric ID and root public key
     423             :     {
     424             :         uint8_t compressedFabricIdBuf[sizeof(uint64_t)];
     425         654 :         MutableByteSpan compressedFabricIdSpan(compressedFabricIdBuf);
     426         654 :         P256PublicKey rootPubkey(certificates.GetCertSet()[0].mPublicKey);
     427             : 
     428         654 :         ReturnErrorOnFailure(GenerateCompressedFabricId(rootPubkey, outFabricId, compressedFabricIdSpan));
     429             : 
     430             :         // Decode compressed fabric ID accounting for endianness, as GenerateCompressedFabricId()
     431             :         // returns a binary buffer and is agnostic of usage of the output as an integer type.
     432         654 :         outCompressedFabricId = Encoding::BigEndian::Get64(compressedFabricIdBuf);
     433             : 
     434         654 :         if (outRootPublicKey != nullptr)
     435             :         {
     436         640 :             *outRootPublicKey = rootPubkey;
     437             :         }
     438         654 :     }
     439             : 
     440         654 :     outNocPubkey = certificates.GetLastCert()->mPublicKey;
     441             : 
     442         654 :     return CHIP_NO_ERROR;
     443         658 : }
     444             : 
     445           2 : const FabricInfo * FabricTable::FindFabric(const Crypto::P256PublicKey & rootPubKey, FabricId fabricId) const
     446             : {
     447           2 :     return FindFabricCommon(rootPubKey, fabricId);
     448             : }
     449             : 
     450           2 : const FabricInfo * FabricTable::FindIdentity(const Crypto::P256PublicKey & rootPubKey, FabricId fabricId, NodeId nodeId) const
     451             : {
     452           2 :     return FindFabricCommon(rootPubKey, fabricId, nodeId);
     453             : }
     454             : 
     455           4 : const FabricInfo * FabricTable::FindFabricCommon(const Crypto::P256PublicKey & rootPubKey, FabricId fabricId, NodeId nodeId) const
     456             : {
     457           4 :     P256PublicKey candidatePubKey;
     458             : 
     459             :     // Try to match pending fabric first if available
     460           4 :     if (HasPendingFabricUpdate())
     461             :     {
     462           0 :         bool pubKeyAvailable = (mPendingFabric.FetchRootPubkey(candidatePubKey) == CHIP_NO_ERROR);
     463           0 :         auto matchingNodeId  = (nodeId == kUndefinedNodeId) ? mPendingFabric.GetNodeId() : nodeId;
     464           0 :         if (pubKeyAvailable && rootPubKey.Matches(candidatePubKey) && fabricId == mPendingFabric.GetFabricId() &&
     465           0 :             matchingNodeId == mPendingFabric.GetNodeId())
     466             :         {
     467           0 :             return &mPendingFabric;
     468             :         }
     469             :     }
     470             : 
     471           6 :     for (auto & fabric : mStates)
     472             :     {
     473           6 :         auto matchingNodeId = (nodeId == kUndefinedNodeId) ? fabric.GetNodeId() : nodeId;
     474             : 
     475           6 :         if (!fabric.IsInitialized())
     476             :         {
     477           0 :             continue;
     478             :         }
     479           6 :         if (fabric.FetchRootPubkey(candidatePubKey) != CHIP_NO_ERROR)
     480             :         {
     481           0 :             continue;
     482             :         }
     483           6 :         if (rootPubKey.Matches(candidatePubKey) && fabricId == fabric.GetFabricId() && matchingNodeId == fabric.GetNodeId())
     484             :         {
     485           4 :             return &fabric;
     486             :         }
     487             :     }
     488             : 
     489           0 :     return nullptr;
     490           4 : }
     491             : 
     492        1283 : FabricInfo * FabricTable::GetMutableFabricByIndex(FabricIndex fabricIndex)
     493             : {
     494             :     // Try to match pending fabric first if available
     495        1283 :     if (HasPendingFabricUpdate() && (mPendingFabric.GetFabricIndex() == fabricIndex))
     496             :     {
     497           3 :         return &mPendingFabric;
     498             :     }
     499             : 
     500        1929 :     for (auto & fabric : mStates)
     501             :     {
     502        1927 :         if (!fabric.IsInitialized())
     503             :         {
     504          32 :             continue;
     505             :         }
     506             : 
     507        1895 :         if (fabric.GetFabricIndex() == fabricIndex)
     508             :         {
     509        1278 :             return &fabric;
     510             :         }
     511             :     }
     512             : 
     513           2 :     return nullptr;
     514             : }
     515             : 
     516       20959 : const FabricInfo * FabricTable::FindFabricWithIndex(FabricIndex fabricIndex) const
     517             : {
     518       20959 :     if (fabricIndex == kUndefinedFabricIndex)
     519             :     {
     520          48 :         return nullptr;
     521             :     }
     522             : 
     523             :     // Try to match pending fabric first if available
     524       20911 :     if (HasPendingFabricUpdate() && (mPendingFabric.GetFabricIndex() == fabricIndex))
     525             :     {
     526           4 :         return &mPendingFabric;
     527             :     }
     528             : 
     529       40985 :     for (const auto & fabric : mStates)
     530             :     {
     531       40358 :         if (!fabric.IsInitialized())
     532             :         {
     533        9102 :             continue;
     534             :         }
     535             : 
     536       31256 :         if (fabric.GetFabricIndex() == fabricIndex)
     537             :         {
     538       20280 :             return &fabric;
     539             :         }
     540             :     }
     541             : 
     542         627 :     return nullptr;
     543             : }
     544             : 
     545           0 : const FabricInfo * FabricTable::FindFabricWithCompressedId(CompressedFabricId compressedFabricId) const
     546             : {
     547             :     // Try to match pending fabric first if available
     548           0 :     if (HasPendingFabricUpdate() && (mPendingFabric.GetCompressedFabricId() == compressedFabricId))
     549             :     {
     550           0 :         return &mPendingFabric;
     551             :     }
     552             : 
     553           0 :     for (auto & fabric : mStates)
     554             :     {
     555           0 :         if (!fabric.IsInitialized())
     556             :         {
     557           0 :             continue;
     558             :         }
     559             : 
     560           0 :         if (compressedFabricId == fabric.GetPeerId().GetCompressedFabricId())
     561             :         {
     562           0 :             return &fabric;
     563             :         }
     564             :     }
     565           0 :     return nullptr;
     566             : }
     567             : 
     568         732 : CHIP_ERROR FabricTable::FetchRootCert(FabricIndex fabricIndex, MutableByteSpan & outCert) const
     569             : {
     570             :     MATTER_TRACE_SCOPE("FetchRootCert", "Fabric");
     571         732 :     VerifyOrReturnError(mOpCertStore != nullptr, CHIP_ERROR_INCORRECT_STATE);
     572         732 :     return mOpCertStore->GetCertificate(fabricIndex, CertChainElement::kRcac, outCert);
     573             : }
     574             : 
     575           6 : CHIP_ERROR FabricTable::FetchPendingNonFabricAssociatedRootCert(MutableByteSpan & outCert) const
     576             : {
     577             :     MATTER_TRACE_SCOPE("FetchPendingNonFabricAssociatedRootCert", "Fabric");
     578           6 :     VerifyOrReturnError(mOpCertStore != nullptr, CHIP_ERROR_INCORRECT_STATE);
     579           6 :     if (!mStateFlags.Has(StateFlags::kIsTrustedRootPending))
     580             :     {
     581           3 :         return CHIP_ERROR_NOT_FOUND;
     582             :     }
     583             : 
     584           3 :     if (mStateFlags.Has(StateFlags::kIsAddPending))
     585             :     {
     586             :         // The root certificate is already associated with a pending fabric, so
     587             :         // does not exist for purposes of this API.
     588           1 :         return CHIP_ERROR_NOT_FOUND;
     589             :     }
     590             : 
     591           2 :     return FetchRootCert(mFabricIndexWithPendingState, outCert);
     592             : }
     593             : 
     594         667 : CHIP_ERROR FabricTable::FetchICACert(FabricIndex fabricIndex, MutableByteSpan & outCert) const
     595             : {
     596             :     MATTER_TRACE_SCOPE("FetchICACert", "Fabric");
     597         667 :     VerifyOrReturnError(mOpCertStore != nullptr, CHIP_ERROR_INCORRECT_STATE);
     598             : 
     599         667 :     CHIP_ERROR err = mOpCertStore->GetCertificate(fabricIndex, CertChainElement::kIcac, outCert);
     600         667 :     if (err == CHIP_ERROR_NOT_FOUND)
     601             :     {
     602          10 :         if (mOpCertStore->HasCertificateForFabric(fabricIndex, CertChainElement::kNoc))
     603             :         {
     604             :             // Didn't find ICAC, but have NOC: return empty for ICAC since not present in chain, but chain exists
     605          10 :             outCert.reduce_size(0);
     606          10 :             return CHIP_NO_ERROR;
     607             :         }
     608             :     }
     609             : 
     610             :     // For all other cases, delegate to operational cert store for results
     611         657 :     return err;
     612             : }
     613             : 
     614         683 : CHIP_ERROR FabricTable::FetchNOCCert(FabricIndex fabricIndex, MutableByteSpan & outCert) const
     615             : {
     616             :     MATTER_TRACE_SCOPE("FetchNOCCert", "Fabric");
     617         683 :     VerifyOrReturnError(mOpCertStore != nullptr, CHIP_ERROR_INCORRECT_STATE);
     618         683 :     return mOpCertStore->GetCertificate(fabricIndex, CertChainElement::kNoc, outCert);
     619             : }
     620             : 
     621          47 : CHIP_ERROR FabricTable::FetchRootPubkey(FabricIndex fabricIndex, Crypto::P256PublicKey & outPublicKey) const
     622             : {
     623             :     MATTER_TRACE_SCOPE("FetchRootPubkey", "Fabric");
     624          47 :     const FabricInfo * fabricInfo = FindFabricWithIndex(fabricIndex);
     625          47 :     ReturnErrorCodeIf(fabricInfo == nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX);
     626          47 :     return fabricInfo->FetchRootPubkey(outPublicKey);
     627             : }
     628             : 
     629           2 : CHIP_ERROR FabricTable::FetchCATs(const FabricIndex fabricIndex, CATValues & cats) const
     630             : {
     631             :     uint8_t nocBuf[Credentials::kMaxCHIPCertLength];
     632           2 :     MutableByteSpan nocSpan{ nocBuf };
     633           2 :     ReturnErrorOnFailure(FetchNOCCert(fabricIndex, nocSpan));
     634           2 :     ReturnErrorOnFailure(ExtractCATsFromOpCert(nocSpan, cats));
     635           2 :     return CHIP_NO_ERROR;
     636             : }
     637             : 
     638         632 : CHIP_ERROR FabricTable::StoreFabricMetadata(const FabricInfo * fabricInfo) const
     639             : {
     640         632 :     VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
     641         632 :     VerifyOrDie(fabricInfo != nullptr);
     642             : 
     643         632 :     FabricIndex fabricIndex = fabricInfo->GetFabricIndex();
     644         632 :     VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INTERNAL);
     645             : 
     646             :     // TODO: Refactor not to internally rely directly on storage
     647         632 :     ReturnErrorOnFailure(fabricInfo->CommitToStorage(mStorage));
     648             : 
     649         632 :     ChipLogProgress(FabricProvisioning, "Metadata for Fabric 0x%x persisted to storage.", static_cast<unsigned>(fabricIndex));
     650             : 
     651         632 :     return CHIP_NO_ERROR;
     652             : }
     653             : 
     654          12 : CHIP_ERROR FabricTable::LoadFromStorage(FabricInfo * fabric, FabricIndex newFabricIndex)
     655             : {
     656          12 :     VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     657          12 :     VerifyOrReturnError(!fabric->IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
     658             : 
     659             :     uint8_t nocBuf[kMaxCHIPCertLength];
     660          12 :     MutableByteSpan nocSpan{ nocBuf };
     661             :     uint8_t rcacBuf[kMaxCHIPCertLength];
     662          12 :     MutableByteSpan rcacSpan{ rcacBuf };
     663             : 
     664          12 :     CHIP_ERROR err = FetchNOCCert(newFabricIndex, nocSpan);
     665          12 :     if (err == CHIP_NO_ERROR)
     666             :     {
     667          12 :         err = FetchRootCert(newFabricIndex, rcacSpan);
     668             :     }
     669             : 
     670             :     // TODO(#19935): Sweep-away fabrics without RCAC/NOC by deleting everything and marking fabric gone.
     671             : 
     672          12 :     if (err == CHIP_NO_ERROR)
     673             :     {
     674          12 :         err = fabric->LoadFromStorage(mStorage, newFabricIndex, rcacSpan, nocSpan);
     675             :     }
     676             : 
     677          12 :     if (err != CHIP_NO_ERROR)
     678             :     {
     679           0 :         ChipLogError(FabricProvisioning, "Failed to load Fabric (0x%x): %" CHIP_ERROR_FORMAT, static_cast<unsigned>(newFabricIndex),
     680             :                      err.Format());
     681           0 :         fabric->Reset();
     682           0 :         return err;
     683             :     }
     684             : 
     685          12 :     ChipLogProgress(FabricProvisioning,
     686             :                     "Fabric index 0x%x was retrieved from storage. Compressed FabricId 0x" ChipLogFormatX64
     687             :                     ", FabricId 0x" ChipLogFormatX64 ", NodeId 0x" ChipLogFormatX64 ", VendorId 0x%04X",
     688             :                     static_cast<unsigned>(fabric->GetFabricIndex()), ChipLogValueX64(fabric->GetCompressedFabricId()),
     689             :                     ChipLogValueX64(fabric->GetFabricId()), ChipLogValueX64(fabric->GetNodeId()),
     690             :                     to_underlying(fabric->GetVendorId()));
     691             : 
     692          12 :     return CHIP_NO_ERROR;
     693             : }
     694             : 
     695         597 : CHIP_ERROR FabricTable::AddNewFabricForTest(const ByteSpan & rootCert, const ByteSpan & icacCert, const ByteSpan & nocCert,
     696             :                                             const ByteSpan & opKeySpan, FabricIndex * outFabricIndex)
     697             : {
     698         597 :     VerifyOrReturnError(outFabricIndex != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     699             : 
     700         597 :     CHIP_ERROR err = CHIP_ERROR_INTERNAL;
     701             : 
     702         597 :     Crypto::P256Keypair injectedOpKey;
     703         597 :     Crypto::P256SerializedKeypair injectedOpKeysSerialized;
     704             : 
     705         597 :     Crypto::P256Keypair * opKey = nullptr;
     706         597 :     if (!opKeySpan.empty())
     707             :     {
     708         596 :         VerifyOrReturnError(opKeySpan.size() == injectedOpKeysSerialized.Capacity(), CHIP_ERROR_INVALID_ARGUMENT);
     709             : 
     710         596 :         memcpy(injectedOpKeysSerialized.Bytes(), opKeySpan.data(), opKeySpan.size());
     711         596 :         SuccessOrExit(err = injectedOpKeysSerialized.SetLength(opKeySpan.size()));
     712         596 :         SuccessOrExit(err = injectedOpKey.Deserialize(injectedOpKeysSerialized));
     713         596 :         opKey = &injectedOpKey;
     714             :     }
     715             : 
     716         597 :     SuccessOrExit(err = AddNewPendingTrustedRootCert(rootCert));
     717         597 :     SuccessOrExit(err = AddNewPendingFabricWithProvidedOpKey(nocCert, icacCert, VendorId::TestVendor1, opKey,
     718             :                                                              /*isExistingOpKeyExternallyOwned =*/false, outFabricIndex));
     719         597 :     SuccessOrExit(err = CommitPendingFabricData());
     720         597 : exit:
     721         597 :     if (err != CHIP_NO_ERROR)
     722             :     {
     723           0 :         RevertPendingFabricData();
     724             :     }
     725         597 :     return err;
     726         597 : }
     727             : 
     728             : /*
     729             :  * A validation policy we can pass into VerifyCredentials to extract the
     730             :  * latest NotBefore time in the certificate chain without having to load the
     731             :  * certificates into memory again, and one which will pass validation for all
     732             :  * questions of NotBefore / NotAfter validity.
     733             :  *
     734             :  * The rationale is that installed certificates should be valid at the time of
     735             :  * installation by definition.  If they are not and the commissionee and
     736             :  * commissioner disagree enough on current time, CASE will fail and our
     737             :  * fail-safe timer will expire.
     738             :  *
     739             :  * This then is ultimately how we validate that NotBefore / NotAfter in
     740             :  * newly installed certificates is workable.
     741             :  */
     742             : class NotBeforeCollector : public Credentials::CertificateValidityPolicy
     743             : {
     744             : public:
     745         644 :     NotBeforeCollector() : mLatestNotBefore(0) {}
     746        1915 :     CHIP_ERROR ApplyCertificateValidityPolicy(const ChipCertificateData * cert, uint8_t depth,
     747             :                                               CertificateValidityResult result) override
     748             :     {
     749        1915 :         if (cert->mNotBeforeTime > mLatestNotBefore.count())
     750             :         {
     751        1239 :             mLatestNotBefore = System::Clock::Seconds32(cert->mNotBeforeTime);
     752             :         }
     753        1915 :         return CHIP_NO_ERROR;
     754             :     }
     755             :     System::Clock::Seconds32 mLatestNotBefore;
     756             : };
     757             : 
     758         645 : CHIP_ERROR FabricTable::NotifyFabricUpdated(FabricIndex fabricIndex)
     759             : {
     760             :     MATTER_TRACE_SCOPE("NotifyFabricUpdated", "Fabric");
     761         645 :     FabricTable::Delegate * delegate = mDelegateListRoot;
     762        1713 :     while (delegate)
     763             :     {
     764             :         // It is possible that delegate will remove itself from the list in the callback
     765             :         // so we grab the next delegate in the list now.
     766        1068 :         FabricTable::Delegate * nextDelegate = delegate->next;
     767        1068 :         delegate->OnFabricUpdated(*this, fabricIndex);
     768        1068 :         delegate = nextDelegate;
     769             :     }
     770         645 :     return CHIP_NO_ERROR;
     771             : }
     772             : 
     773         629 : CHIP_ERROR FabricTable::NotifyFabricCommitted(FabricIndex fabricIndex)
     774             : {
     775             :     MATTER_TRACE_SCOPE("NotifyFabricCommitted", "Fabric");
     776             : 
     777         629 :     FabricTable::Delegate * delegate = mDelegateListRoot;
     778        1691 :     while (delegate)
     779             :     {
     780             :         // It is possible that delegate will remove itself from the list in the callback
     781             :         // so we grab the next delegate in the list now.
     782        1062 :         FabricTable::Delegate * nextDelegate = delegate->next;
     783        1062 :         delegate->OnFabricCommitted(*this, fabricIndex);
     784        1062 :         delegate = nextDelegate;
     785             :     }
     786         629 :     return CHIP_NO_ERROR;
     787             : }
     788             : 
     789             : CHIP_ERROR
     790         644 : FabricTable::AddOrUpdateInner(FabricIndex fabricIndex, bool isAddition, Crypto::P256Keypair * existingOpKey,
     791             :                               bool isExistingOpKeyExternallyOwned, uint16_t vendorId, AdvertiseIdentity advertiseIdentity)
     792             : {
     793             :     // All parameters pre-validated before we get here
     794             : 
     795         644 :     FabricInfo::InitParams newFabricInfo;
     796         644 :     FabricInfo * fabricEntry    = nullptr;
     797         644 :     FabricId fabricIdToValidate = kUndefinedFabricId;
     798         644 :     CharSpan fabricLabel;
     799             : 
     800         644 :     if (isAddition)
     801             :     {
     802             :         // Initialization for Adding a fabric
     803             : 
     804             :         // Find an available slot.
     805         946 :         for (auto & fabric : mStates)
     806             :         {
     807         946 :             if (fabric.IsInitialized())
     808             :             {
     809         306 :                 continue;
     810             :             }
     811         640 :             fabricEntry = &fabric;
     812         640 :             break;
     813             :         }
     814             : 
     815         640 :         VerifyOrReturnError(fabricEntry != nullptr, CHIP_ERROR_NO_MEMORY);
     816             : 
     817         640 :         newFabricInfo.vendorId    = static_cast<VendorId>(vendorId);
     818         640 :         newFabricInfo.fabricIndex = fabricIndex;
     819             :     }
     820             :     else
     821             :     {
     822             :         // Initialization for Updating fabric: setting up a shadow fabricInfo
     823           4 :         const FabricInfo * existingFabric = FindFabricWithIndex(fabricIndex);
     824           4 :         VerifyOrReturnError(existingFabric != nullptr, CHIP_ERROR_INTERNAL);
     825             : 
     826           4 :         mPendingFabric.Reset();
     827           4 :         fabricEntry = &mPendingFabric;
     828             : 
     829           4 :         newFabricInfo.vendorId    = existingFabric->GetVendorId();
     830           4 :         newFabricInfo.fabricIndex = fabricIndex;
     831             : 
     832           4 :         fabricIdToValidate = existingFabric->GetFabricId();
     833           4 :         fabricLabel        = existingFabric->GetFabricLabel();
     834             :     }
     835             : 
     836             :     // Make sure to not modify any of our state until ValidateIncomingNOCChain passes.
     837         644 :     NotBeforeCollector notBeforeCollector;
     838         644 :     P256PublicKey nocPubKey;
     839             : 
     840             :     // Validate the cert chain prior to adding
     841             :     {
     842         644 :         Platform::ScopedMemoryBuffer<uint8_t> nocBuf;
     843         644 :         Platform::ScopedMemoryBuffer<uint8_t> icacBuf;
     844         644 :         Platform::ScopedMemoryBuffer<uint8_t> rcacBuf;
     845             : 
     846         644 :         ReturnErrorCodeIf(!nocBuf.Alloc(kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY);
     847         644 :         ReturnErrorCodeIf(!icacBuf.Alloc(kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY);
     848         644 :         ReturnErrorCodeIf(!rcacBuf.Alloc(kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY);
     849             : 
     850         644 :         MutableByteSpan nocSpan{ nocBuf.Get(), kMaxCHIPCertLength };
     851         644 :         MutableByteSpan icacSpan{ icacBuf.Get(), kMaxCHIPCertLength };
     852         644 :         MutableByteSpan rcacSpan{ rcacBuf.Get(), kMaxCHIPCertLength };
     853             : 
     854         644 :         ReturnErrorOnFailure(FetchNOCCert(fabricIndex, nocSpan));
     855         644 :         ReturnErrorOnFailure(FetchICACert(fabricIndex, icacSpan));
     856         644 :         ReturnErrorOnFailure(FetchRootCert(fabricIndex, rcacSpan));
     857             : 
     858         644 :         ReturnErrorOnFailure(ValidateIncomingNOCChain(nocSpan, icacSpan, rcacSpan, fabricIdToValidate, &notBeforeCollector,
     859             :                                                       newFabricInfo.compressedFabricId, newFabricInfo.fabricId,
     860             :                                                       newFabricInfo.nodeId, nocPubKey, newFabricInfo.rootPublicKey));
     861         652 :     }
     862             : 
     863         640 :     if (existingOpKey != nullptr)
     864             :     {
     865             :         // Verify that public key in NOC matches public key of the provided keypair.
     866             :         // When operational key is not injected (e.g. when mOperationalKeystore != nullptr)
     867             :         // the check is done by the keystore in `ActivateOpKeypairForFabric`.
     868         619 :         VerifyOrReturnError(existingOpKey->Pubkey().Matches(nocPubKey), CHIP_ERROR_INVALID_PUBLIC_KEY);
     869             : 
     870         619 :         newFabricInfo.operationalKeypair        = existingOpKey;
     871         619 :         newFabricInfo.hasExternallyOwnedKeypair = isExistingOpKeyExternallyOwned;
     872             :     }
     873          21 :     else if (mOperationalKeystore != nullptr)
     874             :     {
     875             :         // If a keystore exists, we activate the operational key now, which also validates if it was previously installed
     876          21 :         if (mOperationalKeystore->HasPendingOpKeypair())
     877             :         {
     878          20 :             ReturnErrorOnFailure(mOperationalKeystore->ActivateOpKeypairForFabric(fabricIndex, nocPubKey));
     879             :         }
     880             :         else
     881             :         {
     882           1 :             VerifyOrReturnError(mOperationalKeystore->HasOpKeypairForFabric(fabricIndex), CHIP_ERROR_KEY_NOT_FOUND);
     883             :         }
     884             :     }
     885             :     else
     886             :     {
     887           0 :         return CHIP_ERROR_INCORRECT_STATE;
     888             :     }
     889             : 
     890         640 :     newFabricInfo.advertiseIdentity = (advertiseIdentity == AdvertiseIdentity::Yes);
     891             : 
     892             :     // Update local copy of fabric data. For add it's a new entry, for update, it's `mPendingFabric` shadow entry.
     893         640 :     ReturnErrorOnFailure(fabricEntry->Init(newFabricInfo));
     894             : 
     895             :     // Set the label, matching add/update semantics of empty/existing.
     896         640 :     fabricEntry->SetFabricLabel(fabricLabel);
     897             : 
     898         640 :     if (isAddition)
     899             :     {
     900         636 :         ChipLogProgress(FabricProvisioning, "Added new fabric at index: 0x%x",
     901             :                         static_cast<unsigned>(fabricEntry->GetFabricIndex()));
     902         636 :         ChipLogProgress(FabricProvisioning, "Assigned compressed fabric ID: 0x" ChipLogFormatX64 ", node ID: 0x" ChipLogFormatX64,
     903             :                         ChipLogValueX64(fabricEntry->GetCompressedFabricId()), ChipLogValueX64(fabricEntry->GetNodeId()));
     904             :     }
     905             :     else
     906             :     {
     907           4 :         ChipLogProgress(FabricProvisioning, "Updated fabric at index: 0x%x, Node ID: 0x" ChipLogFormatX64,
     908             :                         static_cast<unsigned>(fabricEntry->GetFabricIndex()), ChipLogValueX64(fabricEntry->GetNodeId()));
     909             :     }
     910             : 
     911             :     // Failure to update pending Last Known Good Time is non-fatal.  If Last
     912             :     // Known Good Time is incorrect and this causes the commissioner's
     913             :     // certificates to appear invalid, the certificate validity policy will
     914             :     // determine what to do.  And if the validity policy considers this fatal
     915             :     // this will prevent CASE and cause the pending fabric and Last Known Good
     916             :     // Time to be reverted.
     917         640 :     CHIP_ERROR lkgtErr = mLastKnownGoodTime.UpdatePendingLastKnownGoodChipEpochTime(notBeforeCollector.mLatestNotBefore);
     918         640 :     if (lkgtErr != CHIP_NO_ERROR)
     919             :     {
     920             :         // Log but this is not sticky...
     921           0 :         ChipLogError(FabricProvisioning, "Failed to update pending Last Known Good Time: %" CHIP_ERROR_FORMAT, lkgtErr.Format());
     922             :     }
     923             : 
     924             :     // Must be the last thing before we return, as this is undone later on error handling within Delete.
     925         640 :     if (isAddition)
     926             :     {
     927         636 :         mFabricCount++;
     928             :     }
     929             : 
     930         640 :     return CHIP_NO_ERROR;
     931         644 : }
     932             : 
     933          17 : CHIP_ERROR FabricTable::Delete(FabricIndex fabricIndex)
     934             : {
     935             :     MATTER_TRACE_SCOPE("Delete", "Fabric");
     936          17 :     VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     937          17 :     VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_ARGUMENT);
     938             : 
     939             :     {
     940          17 :         FabricTable::Delegate * delegate = mDelegateListRoot;
     941          27 :         while (delegate)
     942             :         {
     943             :             // It is possible that delegate will remove itself from the list in FabricWillBeRemoved,
     944             :             // so we grab the next delegate in the list now.
     945          10 :             FabricTable::Delegate * nextDelegate = delegate->next;
     946          10 :             delegate->FabricWillBeRemoved(*this, fabricIndex);
     947          10 :             delegate = nextDelegate;
     948             :         }
     949             :     }
     950             : 
     951          17 :     FabricInfo * fabricInfo = GetMutableFabricByIndex(fabricIndex);
     952          17 :     if (fabricInfo == &mPendingFabric)
     953             :     {
     954             :         // Asked to Delete while pending an update: reset the pending state and
     955             :         // get back to the underlying fabric data for existing fabric.
     956           0 :         RevertPendingFabricData();
     957           0 :         fabricInfo = GetMutableFabricByIndex(fabricIndex);
     958             :     }
     959             : 
     960          17 :     bool fabricIsInitialized = fabricInfo != nullptr && fabricInfo->IsInitialized();
     961          17 :     CHIP_ERROR metadataErr   = DeleteMetadataFromStorage(fabricIndex); // Delete from storage regardless
     962             : 
     963          17 :     CHIP_ERROR opKeyErr = CHIP_NO_ERROR;
     964          17 :     if (mOperationalKeystore != nullptr)
     965             :     {
     966          16 :         opKeyErr = mOperationalKeystore->RemoveOpKeypairForFabric(fabricIndex);
     967             :         // Not having found data is not an error, we may just have gotten here
     968             :         // on a fail-safe expiry after `RevertPendingFabricData`.
     969          16 :         if (opKeyErr == CHIP_ERROR_INVALID_FABRIC_INDEX)
     970             :         {
     971          14 :             opKeyErr = CHIP_NO_ERROR;
     972             :         }
     973             :     }
     974             : 
     975          17 :     CHIP_ERROR opCertsErr = CHIP_NO_ERROR;
     976          17 :     if (mOpCertStore != nullptr)
     977             :     {
     978          17 :         opCertsErr = mOpCertStore->RemoveOpCertsForFabric(fabricIndex);
     979             :         // Not having found data is not an error, we may just have gotten here
     980             :         // on a fail-safe expiry after `RevertPendingFabricData`.
     981          17 :         if (opCertsErr == CHIP_ERROR_INVALID_FABRIC_INDEX)
     982             :         {
     983           3 :             opCertsErr = CHIP_NO_ERROR;
     984             :         }
     985             :     }
     986             : 
     987          17 :     if (!fabricIsInitialized)
     988             :     {
     989             :         // Make sure to return the error our API promises, not whatever storage
     990             :         // chose to return.
     991           1 :         return CHIP_ERROR_NOT_FOUND;
     992             :     }
     993             : 
     994             :     // Since fabricIsInitialized was true, fabric is not null.
     995          16 :     fabricInfo->Reset();
     996             : 
     997          16 :     if (!mNextAvailableFabricIndex.HasValue())
     998             :     {
     999             :         // We must have been in a situation where CHIP_CONFIG_MAX_FABRICS is 254
    1000             :         // and our fabric table was full, so there was no valid next index.  We
    1001             :         // have a single available index now, though; use it as
    1002             :         // mNextAvailableFabricIndex.
    1003           0 :         mNextAvailableFabricIndex.SetValue(fabricIndex);
    1004             :     }
    1005             :     // If StoreFabricIndexInfo fails here, that's probably OK.  When we try to
    1006             :     // read things from storage later we will realize there is nothing for this
    1007             :     // index.
    1008          16 :     StoreFabricIndexInfo();
    1009             : 
    1010             :     // If we ever start moving the FabricInfo entries around in the array on
    1011             :     // delete, we should update DeleteAllFabrics to handle that.
    1012          16 :     if (mFabricCount == 0)
    1013             :     {
    1014           0 :         ChipLogError(FabricProvisioning, "Trying to delete a fabric, but the current fabric count is already 0");
    1015             :     }
    1016             :     else
    1017             :     {
    1018          16 :         mFabricCount--;
    1019          16 :         ChipLogProgress(FabricProvisioning, "Fabric (0x%x) deleted.", static_cast<unsigned>(fabricIndex));
    1020             :     }
    1021             : 
    1022          16 :     if (mDelegateListRoot != nullptr)
    1023             :     {
    1024           5 :         FabricTable::Delegate * delegate = mDelegateListRoot;
    1025          15 :         while (delegate)
    1026             :         {
    1027             :             // It is possible that delegate will remove itself from the list in OnFabricRemoved,
    1028             :             // so we grab the next delegate in the list now.
    1029          10 :             FabricTable::Delegate * nextDelegate = delegate->next;
    1030          10 :             delegate->OnFabricRemoved(*this, fabricIndex);
    1031          10 :             delegate = nextDelegate;
    1032             :         }
    1033             :     }
    1034             : 
    1035             :     // Only return error after trying really hard to remove everything we could
    1036          16 :     ReturnErrorOnFailure(metadataErr);
    1037           6 :     ReturnErrorOnFailure(opKeyErr);
    1038           6 :     ReturnErrorOnFailure(opCertsErr);
    1039             : 
    1040           6 :     return CHIP_NO_ERROR;
    1041             : }
    1042             : 
    1043           2 : void FabricTable::DeleteAllFabrics()
    1044             : {
    1045             :     static_assert(kMaxValidFabricIndex <= UINT8_MAX, "Cannot create more fabrics than UINT8_MAX");
    1046             : 
    1047           2 :     RevertPendingFabricData();
    1048             : 
    1049           4 :     for (auto & fabric : *this)
    1050             :     {
    1051           2 :         Delete(fabric.GetFabricIndex());
    1052             :     }
    1053           2 : }
    1054             : 
    1055         367 : CHIP_ERROR FabricTable::Init(const FabricTable::InitParams & initParams)
    1056             : {
    1057         367 :     VerifyOrReturnError(initParams.storage != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
    1058         367 :     VerifyOrReturnError(initParams.opCertStore != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
    1059             : 
    1060         367 :     mStorage             = initParams.storage;
    1061         367 :     mOperationalKeystore = initParams.operationalKeystore;
    1062         367 :     mOpCertStore         = initParams.opCertStore;
    1063             : 
    1064         367 :     ChipLogDetail(FabricProvisioning, "Initializing FabricTable from persistent storage");
    1065             : 
    1066             :     // Load the current fabrics from the storage.
    1067             :     static_assert(kMaxValidFabricIndex <= UINT8_MAX, "Cannot create more fabrics than UINT8_MAX");
    1068             : 
    1069         367 :     mFabricCount = 0;
    1070        6239 :     for (auto & fabric : mStates)
    1071             :     {
    1072        5872 :         fabric.Reset();
    1073             :     }
    1074         367 :     mNextAvailableFabricIndex.SetValue(kMinValidFabricIndex);
    1075             : 
    1076             :     // Init failure of Last Known Good Time is non-fatal.  If Last Known Good
    1077             :     // Time is unknown during incoming certificate validation for CASE and
    1078             :     // current time is also unknown, the certificate validity policy will see
    1079             :     // this condition and can act appropriately.
    1080         367 :     mLastKnownGoodTime.Init(mStorage);
    1081             : 
    1082             :     uint8_t buf[IndexInfoTLVMaxSize()];
    1083         367 :     uint16_t size  = sizeof(buf);
    1084         367 :     CHIP_ERROR err = mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::FabricIndexInfo().KeyName(), buf, size);
    1085         367 :     if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
    1086             :     {
    1087             :         // No fabrics yet.  Nothing to be done here.
    1088             :     }
    1089             :     else
    1090             :     {
    1091          19 :         ReturnErrorOnFailure(err);
    1092          19 :         TLV::ContiguousBufferTLVReader reader;
    1093          19 :         reader.Init(buf, size);
    1094             : 
    1095             :         // TODO: A safer way would be to just clean-up the entire fabric table on this situation...
    1096          19 :         err = ReadFabricInfo(reader);
    1097          19 :         if (err != CHIP_NO_ERROR)
    1098             :         {
    1099           0 :             ChipLogError(FabricProvisioning, "Error loading fabric table: %" CHIP_ERROR_FORMAT ", we are in a bad state!",
    1100             :                          err.Format());
    1101             :         }
    1102             : 
    1103          19 :         ReturnErrorOnFailure(err);
    1104             :     }
    1105             : 
    1106         367 :     CommitMarker commitMarker;
    1107         367 :     err = GetCommitMarker(commitMarker);
    1108         367 :     if (err == CHIP_NO_ERROR)
    1109             :     {
    1110             :         // Found a commit marker! We need to possibly delete a loaded fabric
    1111           1 :         ChipLogError(FabricProvisioning, "Found a FabricTable aborted commit for index 0x%x (isAddition: %d), removing!",
    1112             :                      static_cast<unsigned>(commitMarker.fabricIndex), static_cast<int>(commitMarker.isAddition));
    1113             : 
    1114           1 :         mDeletedFabricIndexFromInit = commitMarker.fabricIndex;
    1115             : 
    1116             :         // Can't do better on error. We just have to hope for the best.
    1117           1 :         (void) Delete(commitMarker.fabricIndex);
    1118             :     }
    1119         366 :     else if (err != CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
    1120             :     {
    1121             :         // Got an error, but somehow value is not missing altogether: inconsistent state but touch nothing.
    1122           0 :         ChipLogError(FabricProvisioning, "Error loading Table commit marker: %" CHIP_ERROR_FORMAT ", hope for the best!",
    1123             :                      err.Format());
    1124             :     }
    1125             : 
    1126         367 :     return CHIP_NO_ERROR;
    1127             : }
    1128             : 
    1129           0 : void FabricTable::Forget(FabricIndex fabricIndex)
    1130             : {
    1131           0 :     ChipLogProgress(FabricProvisioning, "Forgetting fabric 0x%x", static_cast<unsigned>(fabricIndex));
    1132             : 
    1133           0 :     auto * fabricInfo = GetMutableFabricByIndex(fabricIndex);
    1134           0 :     VerifyOrReturn(fabricInfo != nullptr);
    1135             : 
    1136           0 :     RevertPendingFabricData();
    1137           0 :     fabricInfo->Reset();
    1138             : }
    1139             : 
    1140         364 : void FabricTable::Shutdown()
    1141             : {
    1142         364 :     VerifyOrReturn(mStorage != nullptr);
    1143         364 :     ChipLogProgress(FabricProvisioning, "Shutting down FabricTable");
    1144             : 
    1145             :     // Remove all links to every delegate
    1146         364 :     FabricTable::Delegate * delegate = mDelegateListRoot;
    1147         620 :     while (delegate)
    1148             :     {
    1149         256 :         FabricTable::Delegate * temp = delegate->next;
    1150         256 :         delegate->next               = nullptr;
    1151         256 :         delegate                     = temp;
    1152             :     }
    1153             : 
    1154         364 :     RevertPendingFabricData();
    1155        6188 :     for (FabricInfo & fabricInfo : mStates)
    1156             :     {
    1157             :         // Clear-out any FabricInfo-owned operational keys and make sure any further
    1158             :         // direct lookups fail.
    1159        5824 :         fabricInfo.Reset();
    1160             :     }
    1161             : 
    1162         364 :     mStorage = nullptr;
    1163             : }
    1164             : 
    1165           4 : FabricIndex FabricTable::GetDeletedFabricFromCommitMarker()
    1166             : {
    1167           4 :     FabricIndex retVal = mDeletedFabricIndexFromInit;
    1168             : 
    1169             :     // Reset for next read
    1170           4 :     mDeletedFabricIndexFromInit = kUndefinedFabricIndex;
    1171             : 
    1172           4 :     return retVal;
    1173             : }
    1174             : 
    1175         734 : CHIP_ERROR FabricTable::AddFabricDelegate(FabricTable::Delegate * delegate)
    1176             : {
    1177         734 :     VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
    1178        1349 :     for (FabricTable::Delegate * iter = mDelegateListRoot; iter != nullptr; iter = iter->next)
    1179             :     {
    1180         956 :         if (iter == delegate)
    1181             :         {
    1182         341 :             return CHIP_NO_ERROR;
    1183             :         }
    1184             :     }
    1185         393 :     delegate->next    = mDelegateListRoot;
    1186         393 :     mDelegateListRoot = delegate;
    1187         393 :     return CHIP_NO_ERROR;
    1188             : }
    1189             : 
    1190         366 : void FabricTable::RemoveFabricDelegate(FabricTable::Delegate * delegateToRemove)
    1191             : {
    1192         366 :     VerifyOrReturn(delegateToRemove != nullptr);
    1193             : 
    1194         366 :     if (delegateToRemove == mDelegateListRoot)
    1195             :     {
    1196             :         // Removing head of the list, keep link to next, may
    1197             :         // be nullptr.
    1198         336 :         mDelegateListRoot = mDelegateListRoot->next;
    1199             :     }
    1200             :     else
    1201             :     {
    1202             :         // Removing some other item: check if next, and
    1203             :         // remove the link, keeping its neighbour.
    1204          30 :         FabricTable::Delegate * currentNode = mDelegateListRoot;
    1205             : 
    1206          31 :         while (currentNode)
    1207             :         {
    1208          31 :             if (currentNode->next == delegateToRemove)
    1209             :             {
    1210          30 :                 FabricTable::Delegate * temp = delegateToRemove->next;
    1211          30 :                 currentNode->next            = temp;
    1212          30 :                 delegateToRemove->next       = nullptr;
    1213          30 :                 return;
    1214             :             }
    1215             : 
    1216           1 :             currentNode = currentNode->next;
    1217             :         }
    1218             :     }
    1219             : }
    1220             : 
    1221           8 : CHIP_ERROR FabricTable::SetLastKnownGoodChipEpochTime(System::Clock::Seconds32 lastKnownGoodChipEpochTime)
    1222             : {
    1223           8 :     CHIP_ERROR err = CHIP_NO_ERROR;
    1224             :     // Find our latest NotBefore time for any installed certificate.
    1225           8 :     System::Clock::Seconds32 latestNotBefore = System::Clock::Seconds32(0);
    1226         136 :     for (auto & fabric : mStates)
    1227             :     {
    1228         128 :         if (!fabric.IsInitialized())
    1229             :         {
    1230         120 :             continue;
    1231             :         }
    1232             :         {
    1233             :             uint8_t rcacBuf[kMaxCHIPCertLength];
    1234           8 :             MutableByteSpan rcacSpan{ rcacBuf };
    1235           8 :             SuccessOrExit(err = FetchRootCert(fabric.GetFabricIndex(), rcacSpan));
    1236             :             chip::System::Clock::Seconds32 rcacNotBefore;
    1237           8 :             SuccessOrExit(err = Credentials::ExtractNotBeforeFromChipCert(rcacSpan, rcacNotBefore));
    1238           8 :             latestNotBefore = rcacNotBefore > latestNotBefore ? rcacNotBefore : latestNotBefore;
    1239             :         }
    1240             :         {
    1241             :             uint8_t icacBuf[kMaxCHIPCertLength];
    1242           8 :             MutableByteSpan icacSpan{ icacBuf };
    1243           8 :             SuccessOrExit(err = FetchICACert(fabric.GetFabricIndex(), icacSpan));
    1244           8 :             if (!icacSpan.empty())
    1245             :             {
    1246             :                 chip::System::Clock::Seconds32 icacNotBefore;
    1247           8 :                 ReturnErrorOnFailure(Credentials::ExtractNotBeforeFromChipCert(icacSpan, icacNotBefore));
    1248           8 :                 latestNotBefore = icacNotBefore > latestNotBefore ? icacNotBefore : latestNotBefore;
    1249             :             }
    1250             :         }
    1251             :         {
    1252             :             uint8_t nocBuf[kMaxCHIPCertLength];
    1253           8 :             MutableByteSpan nocSpan{ nocBuf };
    1254           8 :             SuccessOrExit(err = FetchNOCCert(fabric.GetFabricIndex(), nocSpan));
    1255             :             chip::System::Clock::Seconds32 nocNotBefore;
    1256           8 :             ReturnErrorOnFailure(Credentials::ExtractNotBeforeFromChipCert(nocSpan, nocNotBefore));
    1257           8 :             latestNotBefore = nocNotBefore > latestNotBefore ? nocNotBefore : latestNotBefore;
    1258             :         }
    1259             :     }
    1260             :     // Pass this to the LastKnownGoodTime object so it can make determination
    1261             :     // of the legality of our new proposed time.
    1262           8 :     SuccessOrExit(err = mLastKnownGoodTime.SetLastKnownGoodChipEpochTime(lastKnownGoodChipEpochTime, latestNotBefore));
    1263           8 : exit:
    1264           8 :     if (err != CHIP_NO_ERROR)
    1265             :     {
    1266           4 :         ChipLogError(FabricProvisioning, "Failed to update Known Good Time: %" CHIP_ERROR_FORMAT, err.Format());
    1267             :     }
    1268           8 :     return err;
    1269             : }
    1270             : 
    1271             : namespace {
    1272             : // Increment a fabric index in a way that ensures that it stays in the valid
    1273             : // range [kMinValidFabricIndex, kMaxValidFabricIndex].
    1274         627 : FabricIndex NextFabricIndex(FabricIndex fabricIndex)
    1275             : {
    1276         627 :     if (fabricIndex == kMaxValidFabricIndex)
    1277             :     {
    1278           0 :         return kMinValidFabricIndex;
    1279             :     }
    1280             : 
    1281         627 :     return static_cast<FabricIndex>(fabricIndex + 1);
    1282             : }
    1283             : } // anonymous namespace
    1284             : 
    1285         627 : void FabricTable::UpdateNextAvailableFabricIndex()
    1286             : {
    1287             :     // Only called when mNextAvailableFabricIndex.HasValue()
    1288         627 :     for (FabricIndex candidate = NextFabricIndex(mNextAvailableFabricIndex.Value()); candidate != mNextAvailableFabricIndex.Value();
    1289           0 :          candidate             = NextFabricIndex(candidate))
    1290             :     {
    1291         627 :         if (!FindFabricWithIndex(candidate))
    1292             :         {
    1293         627 :             mNextAvailableFabricIndex.SetValue(candidate);
    1294         627 :             return;
    1295             :         }
    1296             :     }
    1297             : 
    1298           0 :     mNextAvailableFabricIndex.ClearValue();
    1299             : }
    1300             : 
    1301         643 : CHIP_ERROR FabricTable::StoreFabricIndexInfo() const
    1302             : {
    1303             :     uint8_t buf[IndexInfoTLVMaxSize()];
    1304         643 :     TLV::TLVWriter writer;
    1305         643 :     writer.Init(buf);
    1306             : 
    1307             :     TLV::TLVType outerType;
    1308         643 :     ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerType));
    1309             : 
    1310         643 :     if (mNextAvailableFabricIndex.HasValue())
    1311             :     {
    1312         643 :         writer.Put(kNextAvailableFabricIndexTag, mNextAvailableFabricIndex.Value());
    1313             :     }
    1314             :     else
    1315             :     {
    1316           0 :         writer.PutNull(kNextAvailableFabricIndexTag);
    1317             :     }
    1318             : 
    1319             :     TLV::TLVType innerContainerType;
    1320         643 :     ReturnErrorOnFailure(writer.StartContainer(kFabricIndicesTag, TLV::kTLVType_Array, innerContainerType));
    1321             :     // Only enumerate the fabrics that are initialized.
    1322        1580 :     for (const auto & fabric : *this)
    1323             :     {
    1324         937 :         writer.Put(TLV::AnonymousTag(), fabric.GetFabricIndex());
    1325             :     }
    1326         643 :     ReturnErrorOnFailure(writer.EndContainer(innerContainerType));
    1327         643 :     ReturnErrorOnFailure(writer.EndContainer(outerType));
    1328             : 
    1329         643 :     const auto indexInfoLength = writer.GetLengthWritten();
    1330         643 :     VerifyOrReturnError(CanCastTo<uint16_t>(indexInfoLength), CHIP_ERROR_BUFFER_TOO_SMALL);
    1331             : 
    1332         643 :     ReturnErrorOnFailure(mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::FabricIndexInfo().KeyName(), buf,
    1333             :                                                    static_cast<uint16_t>(indexInfoLength)));
    1334             : 
    1335         643 :     return CHIP_NO_ERROR;
    1336             : }
    1337             : 
    1338        1336 : void FabricTable::EnsureNextAvailableFabricIndexUpdated()
    1339             : {
    1340        1336 :     if (!mNextAvailableFabricIndex.HasValue() && mFabricCount < kMaxValidFabricIndex)
    1341             :     {
    1342             :         // We must have a fabric index available here. This situation could
    1343             :         // happen if we fail to store fabric index info when deleting a
    1344             :         // fabric.
    1345           0 :         mNextAvailableFabricIndex.SetValue(kMinValidFabricIndex);
    1346           0 :         if (FindFabricWithIndex(kMinValidFabricIndex))
    1347             :         {
    1348           0 :             UpdateNextAvailableFabricIndex();
    1349             :         }
    1350             :     }
    1351        1336 : }
    1352             : 
    1353          19 : CHIP_ERROR FabricTable::ReadFabricInfo(TLV::ContiguousBufferTLVReader & reader)
    1354             : {
    1355          19 :     ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
    1356             :     TLV::TLVType containerType;
    1357          19 :     ReturnErrorOnFailure(reader.EnterContainer(containerType));
    1358             : 
    1359          19 :     ReturnErrorOnFailure(reader.Next(kNextAvailableFabricIndexTag));
    1360          19 :     if (reader.GetType() == TLV::kTLVType_Null)
    1361             :     {
    1362           0 :         mNextAvailableFabricIndex.ClearValue();
    1363             :     }
    1364             :     else
    1365             :     {
    1366          19 :         ReturnErrorOnFailure(reader.Get(mNextAvailableFabricIndex.Emplace()));
    1367             :     }
    1368             : 
    1369          19 :     ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Array, kFabricIndicesTag));
    1370             :     TLV::TLVType arrayType;
    1371          19 :     ReturnErrorOnFailure(reader.EnterContainer(arrayType));
    1372             : 
    1373             :     CHIP_ERROR err;
    1374          31 :     while ((err = reader.Next()) == CHIP_NO_ERROR)
    1375             :     {
    1376          12 :         if (mFabricCount >= ArraySize(mStates))
    1377             :         {
    1378             :             // We have nowhere to deserialize this fabric info into.
    1379           0 :             return CHIP_ERROR_NO_MEMORY;
    1380             :         }
    1381             : 
    1382          12 :         auto & fabric                  = mStates[mFabricCount];
    1383          12 :         FabricIndex currentFabricIndex = kUndefinedFabricIndex;
    1384          12 :         ReturnErrorOnFailure(reader.Get(currentFabricIndex));
    1385             : 
    1386          12 :         err = LoadFromStorage(&fabric, currentFabricIndex);
    1387          12 :         if (err == CHIP_NO_ERROR)
    1388             :         {
    1389          12 :             ++mFabricCount;
    1390             :         }
    1391             :         else
    1392             :         {
    1393             :             // This could happen if we failed to store our fabric index info
    1394             :             // after we deleted the fabric from storage.  Just ignore this
    1395             :             // fabric index and keep going.
    1396             :         }
    1397             :     }
    1398             : 
    1399          19 :     if (err != CHIP_END_OF_TLV)
    1400             :     {
    1401           0 :         return err;
    1402             :     }
    1403             : 
    1404          19 :     ReturnErrorOnFailure(reader.ExitContainer(arrayType));
    1405             : 
    1406          19 :     ReturnErrorOnFailure(reader.ExitContainer(containerType));
    1407          19 :     ReturnErrorOnFailure(reader.VerifyEndOfContainer());
    1408             : 
    1409          19 :     EnsureNextAvailableFabricIndexUpdated();
    1410             : 
    1411          19 :     return CHIP_NO_ERROR;
    1412             : }
    1413             : 
    1414          22 : Crypto::P256Keypair * FabricTable::AllocateEphemeralKeypairForCASE()
    1415             : {
    1416          22 :     if (mOperationalKeystore != nullptr)
    1417             :     {
    1418           9 :         return mOperationalKeystore->AllocateEphemeralKeypairForCASE();
    1419             :     }
    1420             : 
    1421          13 :     return Platform::New<Crypto::P256Keypair>();
    1422             : }
    1423             : 
    1424          34 : void FabricTable::ReleaseEphemeralKeypair(Crypto::P256Keypair * keypair)
    1425             : {
    1426          34 :     if (mOperationalKeystore != nullptr)
    1427             :     {
    1428          20 :         mOperationalKeystore->ReleaseEphemeralKeypair(keypair);
    1429             :     }
    1430             :     else
    1431             :     {
    1432          14 :         Platform::Delete<Crypto::P256Keypair>(keypair);
    1433             :     }
    1434          34 : }
    1435             : 
    1436         630 : CHIP_ERROR FabricTable::StoreCommitMarker(const CommitMarker & commitMarker)
    1437             : {
    1438             :     uint8_t tlvBuf[CommitMarkerContextTLVMaxSize()];
    1439         630 :     TLV::TLVWriter writer;
    1440         630 :     writer.Init(tlvBuf);
    1441             : 
    1442             :     TLV::TLVType outerType;
    1443         630 :     ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerType));
    1444         630 :     ReturnErrorOnFailure(writer.Put(kMarkerFabricIndexTag, commitMarker.fabricIndex));
    1445         630 :     ReturnErrorOnFailure(writer.Put(kMarkerIsAdditionTag, commitMarker.isAddition));
    1446         630 :     ReturnErrorOnFailure(writer.EndContainer(outerType));
    1447             : 
    1448         630 :     const auto markerContextTLVLength = writer.GetLengthWritten();
    1449         630 :     VerifyOrReturnError(CanCastTo<uint16_t>(markerContextTLVLength), CHIP_ERROR_BUFFER_TOO_SMALL);
    1450             : 
    1451        1260 :     return mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::FailSafeCommitMarkerKey().KeyName(), tlvBuf,
    1452         630 :                                      static_cast<uint16_t>(markerContextTLVLength));
    1453             : }
    1454             : 
    1455         367 : CHIP_ERROR FabricTable::GetCommitMarker(CommitMarker & outCommitMarker)
    1456             : {
    1457             :     uint8_t tlvBuf[CommitMarkerContextTLVMaxSize()];
    1458         367 :     uint16_t tlvSize = sizeof(tlvBuf);
    1459         367 :     ReturnErrorOnFailure(
    1460             :         mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::FailSafeCommitMarkerKey().KeyName(), tlvBuf, tlvSize));
    1461             : 
    1462             :     // If buffer was too small, we won't reach here.
    1463           1 :     TLV::ContiguousBufferTLVReader reader;
    1464           1 :     reader.Init(tlvBuf, tlvSize);
    1465           1 :     ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
    1466             : 
    1467             :     TLV::TLVType containerType;
    1468           1 :     ReturnErrorOnFailure(reader.EnterContainer(containerType));
    1469             : 
    1470           1 :     ReturnErrorOnFailure(reader.Next(kMarkerFabricIndexTag));
    1471           1 :     ReturnErrorOnFailure(reader.Get(outCommitMarker.fabricIndex));
    1472             : 
    1473           1 :     ReturnErrorOnFailure(reader.Next(kMarkerIsAdditionTag));
    1474           1 :     ReturnErrorOnFailure(reader.Get(outCommitMarker.isAddition));
    1475             : 
    1476             :     // Don't try to exit container: we got all we needed. This allows us to
    1477             :     // avoid erroring-out on newer versions.
    1478             : 
    1479           1 :     return CHIP_NO_ERROR;
    1480             : }
    1481             : 
    1482         630 : void FabricTable::ClearCommitMarker()
    1483             : {
    1484         630 :     mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::FailSafeCommitMarkerKey().KeyName());
    1485         630 : }
    1486             : 
    1487         630 : bool FabricTable::HasOperationalKeyForFabric(FabricIndex fabricIndex) const
    1488             : {
    1489         630 :     const FabricInfo * fabricInfo = FindFabricWithIndex(fabricIndex);
    1490         630 :     VerifyOrReturnError(fabricInfo != nullptr, false);
    1491             : 
    1492         630 :     if (fabricInfo->HasOperationalKey())
    1493             :     {
    1494             :         // Legacy case of manually injected keys: delegate to FabricInfo directly
    1495         612 :         return true;
    1496             :     }
    1497          18 :     if (mOperationalKeystore != nullptr)
    1498             :     {
    1499          18 :         return mOperationalKeystore->HasOpKeypairForFabric(fabricIndex);
    1500             :     }
    1501             : 
    1502           0 :     return false;
    1503             : }
    1504             : 
    1505          29 : CHIP_ERROR FabricTable::SignWithOpKeypair(FabricIndex fabricIndex, ByteSpan message, P256ECDSASignature & outSignature) const
    1506             : {
    1507          29 :     const FabricInfo * fabricInfo = FindFabricWithIndex(fabricIndex);
    1508          29 :     VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
    1509             : 
    1510          29 :     if (fabricInfo->HasOperationalKey())
    1511             :     {
    1512             :         // Legacy case of manually injected FabricInfo: delegate to FabricInfo directly
    1513           8 :         return fabricInfo->SignWithOpKeypair(message, outSignature);
    1514             :     }
    1515          21 :     if (mOperationalKeystore != nullptr)
    1516             :     {
    1517          21 :         return mOperationalKeystore->SignWithOpKeypair(fabricIndex, message, outSignature);
    1518             :     }
    1519             : 
    1520           0 :     return CHIP_ERROR_KEY_NOT_FOUND;
    1521             : }
    1522             : 
    1523           0 : bool FabricTable::HasPendingOperationalKey(bool & outIsPendingKeyForUpdateNoc) const
    1524             : {
    1525             :     // We can only manage commissionable pending fail-safe state if we have a keystore
    1526           0 :     bool hasOpKeyPending = mStateFlags.Has(StateFlags::kIsOperationalKeyPending);
    1527             : 
    1528           0 :     if (hasOpKeyPending)
    1529             :     {
    1530             :         // We kept track of whether the last `AllocatePendingOperationalKey` for was for an update,
    1531             :         // so give it back out here.
    1532           0 :         outIsPendingKeyForUpdateNoc = mStateFlags.Has(StateFlags::kIsPendingKeyForUpdateNoc);
    1533             :     }
    1534             : 
    1535           0 :     return hasOpKeyPending;
    1536             : }
    1537             : 
    1538        1306 : bool FabricTable::SetPendingDataFabricIndex(FabricIndex fabricIndex)
    1539             : {
    1540        1306 :     bool isLegal = (mFabricIndexWithPendingState == kUndefinedFabricIndex) || (mFabricIndexWithPendingState == fabricIndex);
    1541             : 
    1542        1306 :     if (isLegal)
    1543             :     {
    1544        1306 :         mFabricIndexWithPendingState = fabricIndex;
    1545             :     }
    1546        1306 :     return isLegal;
    1547             : }
    1548             : 
    1549          22 : CHIP_ERROR FabricTable::AllocatePendingOperationalKey(Optional<FabricIndex> fabricIndex, MutableByteSpan & outputCsr)
    1550             : {
    1551             :     // We can only manage commissionable pending fail-safe state if we have a keystore
    1552          22 :     VerifyOrReturnError(mOperationalKeystore != nullptr, CHIP_ERROR_INCORRECT_STATE);
    1553             : 
    1554             :     // We can only allocate a pending key if no pending state (NOC, ICAC) already present,
    1555             :     // since there can only be one pending state per fail-safe.
    1556          22 :     VerifyOrReturnError(!mStateFlags.Has(StateFlags::kIsPendingFabricDataPresent), CHIP_ERROR_INCORRECT_STATE);
    1557          22 :     VerifyOrReturnError(outputCsr.size() >= Crypto::kMIN_CSR_Buffer_Size, CHIP_ERROR_BUFFER_TOO_SMALL);
    1558             : 
    1559          22 :     EnsureNextAvailableFabricIndexUpdated();
    1560          22 :     FabricIndex fabricIndexToUse = kUndefinedFabricIndex;
    1561             : 
    1562          22 :     if (fabricIndex.HasValue())
    1563             :     {
    1564             :         // Check we not are trying to do an update but also change the root: forbidden
    1565           4 :         ReturnErrorCodeIf(mStateFlags.Has(StateFlags::kIsTrustedRootPending), CHIP_ERROR_INCORRECT_STATE);
    1566             : 
    1567             :         // Fabric update case (e.g. UpdateNOC): we already know the fabric index
    1568           4 :         fabricIndexToUse = fabricIndex.Value();
    1569           4 :         mStateFlags.Set(StateFlags::kIsPendingKeyForUpdateNoc);
    1570             :     }
    1571          18 :     else if (mNextAvailableFabricIndex.HasValue())
    1572             :     {
    1573             :         // Fabric addition case (e.g. AddNOC): we need to allocate for the next pending fabric index
    1574          18 :         fabricIndexToUse = mNextAvailableFabricIndex.Value();
    1575          18 :         mStateFlags.Clear(StateFlags::kIsPendingKeyForUpdateNoc);
    1576             :     }
    1577             :     else
    1578             :     {
    1579             :         // Fabric addition, but adding NOC would fail on table full: let's not allocate a key
    1580           0 :         return CHIP_ERROR_NO_MEMORY;
    1581             :     }
    1582             : 
    1583          22 :     VerifyOrReturnError(IsValidFabricIndex(fabricIndexToUse), CHIP_ERROR_INVALID_FABRIC_INDEX);
    1584          22 :     VerifyOrReturnError(SetPendingDataFabricIndex(fabricIndexToUse), CHIP_ERROR_INCORRECT_STATE);
    1585          22 :     ReturnErrorOnFailure(mOperationalKeystore->NewOpKeypairForFabric(mFabricIndexWithPendingState, outputCsr));
    1586          22 :     mStateFlags.Set(StateFlags::kIsOperationalKeyPending);
    1587             : 
    1588          22 :     return CHIP_NO_ERROR;
    1589             : }
    1590             : 
    1591         640 : CHIP_ERROR FabricTable::AddNewPendingTrustedRootCert(const ByteSpan & rcac)
    1592             : {
    1593         640 :     VerifyOrReturnError(mOpCertStore != nullptr, CHIP_ERROR_INCORRECT_STATE);
    1594             : 
    1595             :     // We should not already have pending NOC chain elements when we get here
    1596         640 :     ReturnErrorCodeIf(
    1597             :         mStateFlags.HasAny(StateFlags::kIsTrustedRootPending, StateFlags::kIsUpdatePending, StateFlags::kIsAddPending),
    1598             :         CHIP_ERROR_INCORRECT_STATE);
    1599             : 
    1600         640 :     EnsureNextAvailableFabricIndexUpdated();
    1601         640 :     FabricIndex fabricIndexToUse = kUndefinedFabricIndex;
    1602             : 
    1603         640 :     if (mNextAvailableFabricIndex.HasValue())
    1604             :     {
    1605         640 :         fabricIndexToUse = mNextAvailableFabricIndex.Value();
    1606             :     }
    1607             :     else
    1608             :     {
    1609             :         // Fabric addition, but adding root would fail on table full: let's not allocate a fabric
    1610           0 :         return CHIP_ERROR_NO_MEMORY;
    1611             :     }
    1612             : 
    1613         640 :     VerifyOrReturnError(IsValidFabricIndex(fabricIndexToUse), CHIP_ERROR_INVALID_FABRIC_INDEX);
    1614         640 :     VerifyOrReturnError(SetPendingDataFabricIndex(fabricIndexToUse), CHIP_ERROR_INCORRECT_STATE);
    1615         640 :     ReturnErrorOnFailure(mOpCertStore->AddNewTrustedRootCertForFabric(fabricIndexToUse, rcac));
    1616             : 
    1617         640 :     mStateFlags.Set(StateFlags::kIsPendingFabricDataPresent);
    1618         640 :     mStateFlags.Set(StateFlags::kIsTrustedRootPending);
    1619         640 :     return CHIP_NO_ERROR;
    1620             : }
    1621             : 
    1622          50 : CHIP_ERROR FabricTable::FindExistingFabricByNocChaining(FabricIndex pendingFabricIndex, const ByteSpan & noc,
    1623             :                                                         FabricIndex & outMatchingFabricIndex) const
    1624             : {
    1625             :     MATTER_TRACE_SCOPE("FindExistingFabricByNocChaining", "Fabric");
    1626             :     // Check whether we already have a matching fabric from a cert chain perspective.
    1627             :     // To do so we have to extract the FabricID from the NOC and the root public key from the RCAC.
    1628             :     // We assume the RCAC is currently readable from OperationalCertificateStore, whether pending
    1629             :     // or persisted.
    1630             :     FabricId fabricId;
    1631             :     {
    1632             :         NodeId unused;
    1633          50 :         ReturnErrorOnFailure(ExtractNodeIdFabricIdFromOpCert(noc, &unused, &fabricId));
    1634             :     }
    1635             : 
    1636             :     // Try to find the root public key from the current existing fabric
    1637          50 :     Crypto::P256PublicKey candidateRootKey;
    1638             :     {
    1639             :         uint8_t tempRcac[kMaxCHIPCertLength];
    1640          50 :         MutableByteSpan tempRcacSpan{ tempRcac };
    1641          50 :         Credentials::P256PublicKeySpan publicKeySpan;
    1642          50 :         ReturnErrorOnFailure(FetchRootCert(pendingFabricIndex, tempRcacSpan));
    1643          50 :         ReturnErrorOnFailure(ExtractPublicKeyFromChipCert(tempRcacSpan, publicKeySpan));
    1644          50 :         candidateRootKey = publicKeySpan;
    1645             :     }
    1646             : 
    1647          59 :     for (auto & existingFabric : *this)
    1648             :     {
    1649          15 :         if (existingFabric.GetFabricId() == fabricId)
    1650             :         {
    1651           9 :             P256PublicKey existingRootKey;
    1652           9 :             ReturnErrorOnFailure(FetchRootPubkey(existingFabric.GetFabricIndex(), existingRootKey));
    1653             : 
    1654           9 :             if (existingRootKey.Matches(candidateRootKey))
    1655             :             {
    1656           6 :                 outMatchingFabricIndex = existingFabric.GetFabricIndex();
    1657           6 :                 return CHIP_NO_ERROR;
    1658             :             }
    1659           9 :         }
    1660             :     }
    1661             : 
    1662             :     // Did not find: set outMatchingFabricIndex to kUndefinedFabricIndex
    1663          44 :     outMatchingFabricIndex = kUndefinedFabricIndex;
    1664          44 :     return CHIP_NO_ERROR;
    1665          50 : }
    1666             : 
    1667         642 : CHIP_ERROR FabricTable::AddNewPendingFabricCommon(const ByteSpan & noc, const ByteSpan & icac, uint16_t vendorId,
    1668             :                                                   Crypto::P256Keypair * existingOpKey, bool isExistingOpKeyExternallyOwned,
    1669             :                                                   AdvertiseIdentity advertiseIdentity, FabricIndex * outNewFabricIndex)
    1670             : {
    1671             :     MATTER_TRACE_SCOPE("AddNewPendingFabricCommon", "Fabric");
    1672         642 :     VerifyOrReturnError(mOpCertStore != nullptr, CHIP_ERROR_INCORRECT_STATE);
    1673         642 :     VerifyOrReturnError(outNewFabricIndex != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
    1674             :     static_assert(kMaxValidFabricIndex <= UINT8_MAX, "Cannot create more fabrics than UINT8_MAX");
    1675             : 
    1676             :     // We should already have a pending root when we get here
    1677         642 :     VerifyOrReturnError(mStateFlags.Has(StateFlags::kIsTrustedRootPending), CHIP_ERROR_INCORRECT_STATE);
    1678             :     // We should not have pending update when we get here
    1679         642 :     VerifyOrReturnError(!mStateFlags.Has(StateFlags::kIsUpdatePending), CHIP_ERROR_INCORRECT_STATE);
    1680             : 
    1681         642 :     EnsureNextAvailableFabricIndexUpdated();
    1682         642 :     FabricIndex fabricIndexToUse = kUndefinedFabricIndex;
    1683         642 :     if (mNextAvailableFabricIndex.HasValue())
    1684             :     {
    1685         642 :         fabricIndexToUse = mNextAvailableFabricIndex.Value();
    1686             :     }
    1687             :     else
    1688             :     {
    1689             :         // Fabric addition, but adding fabric would fail on table full: let's not allocate a fabric
    1690           0 :         return CHIP_ERROR_NO_MEMORY;
    1691             :     }
    1692             : 
    1693             :     // Internal consistency check that mNextAvailableFabricIndex is indeed properly updated...
    1694             :     // TODO: Centralize this a bit.
    1695         642 :     VerifyOrReturnError(IsValidFabricIndex(fabricIndexToUse), CHIP_ERROR_INVALID_FABRIC_INDEX);
    1696             : 
    1697         642 :     if (existingOpKey == nullptr)
    1698             :     {
    1699             :         // If existing operational key not provided, we need to have a keystore present.
    1700             :         // It should already have an operational key pending.
    1701          22 :         VerifyOrReturnError(mOperationalKeystore != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
    1702             :         // Make sure we have an operational key, pending or not
    1703          22 :         VerifyOrReturnError(mOperationalKeystore->HasOpKeypairForFabric(fabricIndexToUse) ||
    1704             :                                 mOperationalKeystore->HasPendingOpKeypair(),
    1705             :                             CHIP_ERROR_KEY_NOT_FOUND);
    1706             :     }
    1707             : 
    1708             :     // Check for new fabric colliding with an existing fabric
    1709         642 :     if (!mStateFlags.Has(StateFlags::kAreCollidingFabricsIgnored))
    1710             :     {
    1711          46 :         FabricIndex collidingFabricIndex = kUndefinedFabricIndex;
    1712          46 :         ReturnErrorOnFailure(FindExistingFabricByNocChaining(fabricIndexToUse, noc, collidingFabricIndex));
    1713          46 :         ReturnErrorCodeIf(collidingFabricIndex != kUndefinedFabricIndex, CHIP_ERROR_FABRIC_EXISTS);
    1714             :     }
    1715             : 
    1716             :     // We don't have a collision, handle the temp insert of NOC/ICAC
    1717         640 :     ReturnErrorOnFailure(mOpCertStore->AddNewOpCertsForFabric(fabricIndexToUse, noc, icac));
    1718         640 :     VerifyOrReturnError(SetPendingDataFabricIndex(fabricIndexToUse), CHIP_ERROR_INCORRECT_STATE);
    1719             : 
    1720         640 :     CHIP_ERROR err = AddOrUpdateInner(fabricIndexToUse, /* isAddition = */ true, existingOpKey, isExistingOpKeyExternallyOwned,
    1721             :                                       vendorId, advertiseIdentity);
    1722         640 :     if (err != CHIP_NO_ERROR)
    1723             :     {
    1724             :         // Revert partial state added on error
    1725           4 :         RevertPendingOpCertsExceptRoot();
    1726           4 :         return err;
    1727             :     }
    1728             : 
    1729         636 :     mStateFlags.Set(StateFlags::kIsAddPending);
    1730         636 :     mStateFlags.Set(StateFlags::kIsPendingFabricDataPresent);
    1731             : 
    1732             :     // Notify that NOC was added (at least transiently)
    1733         636 :     *outNewFabricIndex = fabricIndexToUse;
    1734         636 :     NotifyFabricUpdated(fabricIndexToUse);
    1735             : 
    1736         636 :     return CHIP_NO_ERROR;
    1737             : }
    1738             : 
    1739           4 : CHIP_ERROR FabricTable::UpdatePendingFabricCommon(FabricIndex fabricIndex, const ByteSpan & noc, const ByteSpan & icac,
    1740             :                                                   Crypto::P256Keypair * existingOpKey, bool isExistingOpKeyExternallyOwned,
    1741             :                                                   AdvertiseIdentity advertiseIdentity)
    1742             : {
    1743             :     MATTER_TRACE_SCOPE("UpdatePendingFabricCommon", "Fabric");
    1744           4 :     VerifyOrReturnError(mOpCertStore != nullptr, CHIP_ERROR_INCORRECT_STATE);
    1745           4 :     VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_ARGUMENT);
    1746             : 
    1747           4 :     if (existingOpKey == nullptr)
    1748             :     {
    1749             :         // If existing operational key not provided, we need to have a keystore present.
    1750             :         // It should already have an operational key pending.
    1751           4 :         VerifyOrReturnError(mOperationalKeystore != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
    1752             :         // Make sure we have an operational key, pending or not
    1753           4 :         VerifyOrReturnError(mOperationalKeystore->HasOpKeypairForFabric(fabricIndex) || mOperationalKeystore->HasPendingOpKeypair(),
    1754             :                             CHIP_ERROR_KEY_NOT_FOUND);
    1755             :     }
    1756             : 
    1757             :     // We should should not have a pending root when we get here, since we can't update root on update
    1758           4 :     VerifyOrReturnError(!mStateFlags.Has(StateFlags::kIsTrustedRootPending), CHIP_ERROR_INCORRECT_STATE);
    1759             : 
    1760             :     // We should not have pending add when we get here, due to internal interlocks
    1761           4 :     VerifyOrReturnError(!mStateFlags.Has(StateFlags::kIsAddPending), CHIP_ERROR_INCORRECT_STATE);
    1762             : 
    1763             :     // Make sure we are updating at least an existing FabricIndex
    1764           4 :     const auto * fabricInfo = FindFabricWithIndex(fabricIndex);
    1765           4 :     ReturnErrorCodeIf(fabricInfo == nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX);
    1766             : 
    1767             :     // Check for an existing fabric matching RCAC and FabricID. We must find a correct
    1768             :     // existing fabric that chains to same root. We assume the stored root is correct.
    1769           4 :     if (!mStateFlags.Has(StateFlags::kAreCollidingFabricsIgnored))
    1770             :     {
    1771           4 :         FabricIndex collidingFabricIndex = kUndefinedFabricIndex;
    1772           4 :         ReturnErrorOnFailure(FindExistingFabricByNocChaining(fabricIndex, noc, collidingFabricIndex));
    1773           4 :         ReturnErrorCodeIf(collidingFabricIndex != fabricIndex, CHIP_ERROR_INVALID_FABRIC_INDEX);
    1774             :     }
    1775             : 
    1776             :     // Handle the temp insert of NOC/ICAC
    1777           4 :     ReturnErrorOnFailure(mOpCertStore->UpdateOpCertsForFabric(fabricIndex, noc, icac));
    1778           4 :     VerifyOrReturnError(SetPendingDataFabricIndex(fabricIndex), CHIP_ERROR_INCORRECT_STATE);
    1779             : 
    1780           4 :     CHIP_ERROR err = AddOrUpdateInner(fabricIndex, /* isAddition = */ false, existingOpKey, isExistingOpKeyExternallyOwned,
    1781           4 :                                       fabricInfo->GetVendorId(), advertiseIdentity);
    1782           4 :     if (err != CHIP_NO_ERROR)
    1783             :     {
    1784             :         // Revert partial state added on error
    1785             :         // TODO: Figure-out if there is a better way. We need to make sure we are not inconsistent on elements
    1786             :         //       other than the opcerts.
    1787           0 :         RevertPendingOpCertsExceptRoot();
    1788           0 :         return err;
    1789             :     }
    1790             : 
    1791           4 :     mStateFlags.Set(StateFlags::kIsUpdatePending);
    1792           4 :     mStateFlags.Set(StateFlags::kIsPendingFabricDataPresent);
    1793             : 
    1794             :     // Notify that NOC was updated (at least transiently)
    1795           4 :     NotifyFabricUpdated(fabricIndex);
    1796             : 
    1797           4 :     return CHIP_NO_ERROR;
    1798             : }
    1799             : 
    1800         631 : CHIP_ERROR FabricTable::CommitPendingFabricData()
    1801             : {
    1802         631 :     VerifyOrReturnError((mStorage != nullptr) && (mOpCertStore != nullptr), CHIP_ERROR_INCORRECT_STATE);
    1803             : 
    1804         631 :     bool haveNewTrustedRoot      = mStateFlags.Has(StateFlags::kIsTrustedRootPending);
    1805         631 :     bool isAdding                = mStateFlags.Has(StateFlags::kIsAddPending);
    1806         631 :     bool isUpdating              = mStateFlags.Has(StateFlags::kIsUpdatePending);
    1807         631 :     bool hasPending              = mStateFlags.Has(StateFlags::kIsPendingFabricDataPresent);
    1808         631 :     bool onlyHaveNewTrustedRoot  = hasPending && haveNewTrustedRoot && !(isAdding || isUpdating);
    1809         631 :     bool hasInvalidInternalState = hasPending && (!IsValidFabricIndex(mFabricIndexWithPendingState) || !(isAdding || isUpdating));
    1810             : 
    1811         631 :     FabricIndex fabricIndexBeingCommitted = mFabricIndexWithPendingState;
    1812             : 
    1813             :     // Proceed with Update/Add pre-flight checks
    1814         631 :     if (hasPending && !hasInvalidInternalState)
    1815             :     {
    1816         630 :         if ((isAdding && isUpdating) || (isAdding && !haveNewTrustedRoot))
    1817             :         {
    1818           0 :             ChipLogError(FabricProvisioning, "Found inconsistent interlocks during commit %u/%u/%u!",
    1819             :                          static_cast<unsigned>(isAdding), static_cast<unsigned>(isUpdating),
    1820             :                          static_cast<unsigned>(haveNewTrustedRoot));
    1821           0 :             hasInvalidInternalState = true;
    1822             :         }
    1823             :     }
    1824             : 
    1825             :     // Make sure we actually have a pending fabric
    1826         631 :     FabricInfo * pendingFabricEntry = GetMutableFabricByIndex(fabricIndexBeingCommitted);
    1827             : 
    1828         631 :     if (isUpdating && hasPending && !hasInvalidInternalState)
    1829             :     {
    1830           2 :         if (!mPendingFabric.IsInitialized() || (mPendingFabric.GetFabricIndex() != fabricIndexBeingCommitted) ||
    1831             :             (pendingFabricEntry == nullptr))
    1832             :         {
    1833           0 :             ChipLogError(FabricProvisioning, "Missing pending fabric on update during commit!");
    1834           0 :             hasInvalidInternalState = true;
    1835             :         }
    1836             :     }
    1837             : 
    1838         631 :     if (isAdding && hasPending && !hasInvalidInternalState)
    1839             :     {
    1840         628 :         bool opCertStoreHasRoot = mOpCertStore->HasCertificateForFabric(fabricIndexBeingCommitted, CertChainElement::kRcac);
    1841         628 :         if (!mStateFlags.Has(StateFlags::kIsTrustedRootPending) || !opCertStoreHasRoot)
    1842             :         {
    1843           0 :             ChipLogError(FabricProvisioning, "Missing trusted root for fabric add during commit!");
    1844           0 :             hasInvalidInternalState = true;
    1845             :         }
    1846             :     }
    1847             : 
    1848         631 :     if ((isAdding || isUpdating) && hasPending && !hasInvalidInternalState)
    1849             :     {
    1850         630 :         if (!HasOperationalKeyForFabric(fabricIndexBeingCommitted))
    1851             :         {
    1852           0 :             ChipLogError(FabricProvisioning, "Could not find an operational key during commit!");
    1853           0 :             hasInvalidInternalState = true;
    1854             :         }
    1855             :     }
    1856             : 
    1857             :     // If there was nothing pending, we are either in a completely OK state, or weird internally inconsistent
    1858             :     // state. In either case, let's clear all pending state anyway, in case it was partially stale!
    1859         631 :     if (!hasPending || onlyHaveNewTrustedRoot || hasInvalidInternalState)
    1860             :     {
    1861           1 :         CHIP_ERROR err = CHIP_NO_ERROR;
    1862             : 
    1863           1 :         if (onlyHaveNewTrustedRoot)
    1864             :         {
    1865           1 :             ChipLogError(FabricProvisioning,
    1866             :                          "Failed to commit: tried to commit with only a new trusted root cert. No data committed.");
    1867           1 :             err = CHIP_ERROR_INCORRECT_STATE;
    1868             :         }
    1869           0 :         else if (hasInvalidInternalState)
    1870             :         {
    1871           0 :             ChipLogError(FabricProvisioning, "Failed to commit: internally inconsistent state!");
    1872           0 :             err = CHIP_ERROR_INTERNAL;
    1873             :         }
    1874             :         else
    1875             :         {
    1876             :             // There was nothing pending and no error...
    1877             :         }
    1878             : 
    1879             :         // Clear all pending state anyway, in case it was partially stale!
    1880             :         {
    1881           1 :             mStateFlags.ClearAll();
    1882           1 :             mFabricIndexWithPendingState = kUndefinedFabricIndex;
    1883           1 :             mPendingFabric.Reset();
    1884           1 :             mOpCertStore->RevertPendingOpCerts();
    1885           1 :             if (mOperationalKeystore != nullptr)
    1886             :             {
    1887           1 :                 mOperationalKeystore->RevertPendingKeypair();
    1888             :             }
    1889             :         }
    1890             : 
    1891           1 :         return err;
    1892             :     }
    1893             : 
    1894             :     // ==== Start of actual commit transaction after pre-flight checks ====
    1895         630 :     CHIP_ERROR stickyError  = StoreCommitMarker(CommitMarker{ fabricIndexBeingCommitted, isAdding });
    1896         630 :     bool failedCommitMarker = (stickyError != CHIP_NO_ERROR);
    1897         630 :     if (failedCommitMarker)
    1898             :     {
    1899           0 :         ChipLogError(FabricProvisioning, "Failed to store commit marker, may be inconsistent if reboot happens during fail-safe!");
    1900             :     }
    1901             : 
    1902             :     {
    1903             :         // This scope block is to illustrate the complete commit transaction
    1904             :         // state. We can see it contains a LARGE number of items...
    1905             : 
    1906             :         // Atomically assume data no longer pending, since we are committing it. Do so here
    1907             :         // so that FindFabricBy* will return real data and never pending.
    1908         630 :         mStateFlags.Clear(StateFlags::kIsPendingFabricDataPresent);
    1909             : 
    1910         630 :         if (isUpdating)
    1911             :         {
    1912             :             // This will get the non-pending fabric
    1913           2 :             FabricInfo * existingFabricToUpdate = GetMutableFabricByIndex(fabricIndexBeingCommitted);
    1914             : 
    1915             :             // Multiple interlocks validated the below, so it's fatal if we are somehow incoherent here
    1916           2 :             VerifyOrDie((existingFabricToUpdate != nullptr) && (existingFabricToUpdate != &mPendingFabric));
    1917             : 
    1918             :             // Commit the pending entry to local in-memory fabric metadata, which
    1919             :             // also moves operational keys if not backed by OperationalKeystore
    1920           2 :             *existingFabricToUpdate = std::move(mPendingFabric);
    1921             :         }
    1922             : 
    1923             :         // Store pending metadata first
    1924         630 :         FabricInfo * liveFabricEntry = GetMutableFabricByIndex(fabricIndexBeingCommitted);
    1925         630 :         VerifyOrDie(liveFabricEntry != nullptr);
    1926             : 
    1927         630 :         CHIP_ERROR metadataErr = StoreFabricMetadata(liveFabricEntry);
    1928         630 :         if (metadataErr != CHIP_NO_ERROR)
    1929             :         {
    1930           0 :             ChipLogError(FabricProvisioning, "Failed to commit pending fabric metadata: %" CHIP_ERROR_FORMAT, metadataErr.Format());
    1931             :         }
    1932         630 :         stickyError = (stickyError != CHIP_NO_ERROR) ? stickyError : metadataErr;
    1933             : 
    1934             :         // We can only manage commissionable pending fail-safe state if we have a keystore
    1935         630 :         CHIP_ERROR keyErr = CHIP_NO_ERROR;
    1936         648 :         if ((mOperationalKeystore != nullptr) && mOperationalKeystore->HasOpKeypairForFabric(fabricIndexBeingCommitted) &&
    1937          18 :             mOperationalKeystore->HasPendingOpKeypair())
    1938             :         {
    1939          17 :             keyErr = mOperationalKeystore->CommitOpKeypairForFabric(fabricIndexBeingCommitted);
    1940          17 :             if (keyErr != CHIP_NO_ERROR)
    1941             :             {
    1942           0 :                 ChipLogError(FabricProvisioning, "Failed to commit pending operational keypair %" CHIP_ERROR_FORMAT,
    1943             :                              keyErr.Format());
    1944           0 :                 mOperationalKeystore->RevertPendingKeypair();
    1945             :             }
    1946             :         }
    1947         630 :         stickyError = (stickyError != CHIP_NO_ERROR) ? stickyError : keyErr;
    1948             : 
    1949             :         // For testing only, early return (NEVER OCCURS OTHERWISE) during the commit
    1950             :         // so that clean-ups using the commit marker can be tested.
    1951             : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
    1952             :         {
    1953         630 :             if (mStateFlags.Has(StateFlags::kAbortCommitForTest))
    1954             :             {
    1955             :                 // Clear state so that shutdown doesn't attempt clean-up
    1956           1 :                 mStateFlags.ClearAll();
    1957           1 :                 mFabricIndexWithPendingState = kUndefinedFabricIndex;
    1958           1 :                 mPendingFabric.Reset();
    1959             : 
    1960           1 :                 ChipLogError(FabricProvisioning, "Aborting commit in middle of transaction for testing.");
    1961           1 :                 return CHIP_ERROR_INTERNAL;
    1962             :             }
    1963             :         }
    1964             : #endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
    1965             : 
    1966             :         // Commit operational certs
    1967         629 :         CHIP_ERROR opCertErr = mOpCertStore->CommitOpCertsForFabric(fabricIndexBeingCommitted);
    1968         629 :         if (opCertErr != CHIP_NO_ERROR)
    1969             :         {
    1970           0 :             ChipLogError(FabricProvisioning, "Failed to commit pending operational certificates %" CHIP_ERROR_FORMAT,
    1971             :                          opCertErr.Format());
    1972           0 :             mOpCertStore->RevertPendingOpCerts();
    1973             :         }
    1974         629 :         stickyError = (stickyError != CHIP_NO_ERROR) ? stickyError : opCertErr;
    1975             : 
    1976             :         // Failure to commit Last Known Good Time is non-fatal.  If Last Known
    1977             :         // Good Time is incorrect and this causes incoming certificates to
    1978             :         // appear invalid, the certificate validity policy will see this
    1979             :         // condition and can act appropriately.
    1980         629 :         CHIP_ERROR lkgtErr = mLastKnownGoodTime.CommitPendingLastKnownGoodChipEpochTime();
    1981         629 :         if (lkgtErr != CHIP_NO_ERROR)
    1982             :         {
    1983             :             // Log but this is not sticky...
    1984           0 :             ChipLogError(FabricProvisioning, "Failed to commit Last Known Good Time: %" CHIP_ERROR_FORMAT, lkgtErr.Format());
    1985             :         }
    1986             : 
    1987             :         // If an Add occurred, let's update the fabric index
    1988         629 :         CHIP_ERROR fabricIndexErr = CHIP_NO_ERROR;
    1989         629 :         if (mStateFlags.Has(StateFlags::kIsAddPending))
    1990             :         {
    1991         627 :             UpdateNextAvailableFabricIndex();
    1992         627 :             fabricIndexErr = StoreFabricIndexInfo();
    1993         627 :             if (fabricIndexErr != CHIP_NO_ERROR)
    1994             :             {
    1995           0 :                 ChipLogError(FabricProvisioning, "Failed to commit pending fabric indices: %" CHIP_ERROR_FORMAT,
    1996             :                              fabricIndexErr.Format());
    1997             :             }
    1998             :         }
    1999         629 :         stickyError = (stickyError != CHIP_NO_ERROR) ? stickyError : fabricIndexErr;
    2000             :     }
    2001             : 
    2002             :     // Commit must have same side-effect as reverting all pending data
    2003         629 :     mStateFlags.ClearAll();
    2004         629 :     mFabricIndexWithPendingState = kUndefinedFabricIndex;
    2005         629 :     mPendingFabric.Reset();
    2006             : 
    2007         629 :     if (stickyError != CHIP_NO_ERROR)
    2008             :     {
    2009             :         // Blow-away everything if we got past any storage, even on Update: system state is broken
    2010             :         // TODO: Develop a way to properly revert in the future, but this is very difficult
    2011           0 :         Delete(fabricIndexBeingCommitted);
    2012             : 
    2013           0 :         RevertPendingFabricData();
    2014             :     }
    2015             :     else
    2016             :     {
    2017         629 :         NotifyFabricCommitted(fabricIndexBeingCommitted);
    2018             :     }
    2019             : 
    2020             :     // Clear commit marker no matter what: if we got here, there was no reboot and previous clean-ups
    2021             :     // did their job.
    2022         629 :     ClearCommitMarker();
    2023             : 
    2024         629 :     return stickyError;
    2025             : }
    2026             : 
    2027         388 : void FabricTable::RevertPendingFabricData()
    2028             : {
    2029             :     MATTER_TRACE_SCOPE("RevertPendingFabricData", "Fabric");
    2030             :     // Will clear pending UpdateNoc/AddNOC
    2031         388 :     RevertPendingOpCertsExceptRoot();
    2032             : 
    2033         388 :     if (mOperationalKeystore != nullptr)
    2034             :     {
    2035         386 :         mOperationalKeystore->RevertPendingKeypair();
    2036             :     }
    2037             : 
    2038             :     // Clear everything else
    2039         388 :     if (mOpCertStore != nullptr)
    2040             :     {
    2041         388 :         mOpCertStore->RevertPendingOpCerts();
    2042             :     }
    2043             : 
    2044         388 :     mLastKnownGoodTime.RevertPendingLastKnownGoodChipEpochTime();
    2045             : 
    2046         388 :     mStateFlags.ClearAll();
    2047         388 :     mFabricIndexWithPendingState = kUndefinedFabricIndex;
    2048         388 : }
    2049             : 
    2050         392 : void FabricTable::RevertPendingOpCertsExceptRoot()
    2051             : {
    2052             :     MATTER_TRACE_SCOPE("RevertPendingOpCertsExceptRoot", "Fabric");
    2053         392 :     mPendingFabric.Reset();
    2054             : 
    2055         392 :     if (mStateFlags.Has(StateFlags::kIsPendingFabricDataPresent))
    2056             :     {
    2057          17 :         ChipLogError(FabricProvisioning, "Reverting pending fabric data for fabric 0x%x",
    2058             :                      static_cast<unsigned>(mFabricIndexWithPendingState));
    2059             :     }
    2060             : 
    2061         392 :     if (mOpCertStore != nullptr)
    2062             :     {
    2063         392 :         mOpCertStore->RevertPendingOpCertsExceptRoot();
    2064             :     }
    2065             : 
    2066         392 :     if (mStateFlags.Has(StateFlags::kIsAddPending))
    2067             :     {
    2068             :         // If we have a pending add, let's make sure to kill the pending fabric metadata and return it to viable state.
    2069           8 :         Delete(mFabricIndexWithPendingState);
    2070             :     }
    2071             : 
    2072         392 :     mStateFlags.Clear(StateFlags::kIsAddPending);
    2073         392 :     mStateFlags.Clear(StateFlags::kIsUpdatePending);
    2074         392 :     if (!mStateFlags.Has(StateFlags::kIsTrustedRootPending))
    2075             :     {
    2076         377 :         mFabricIndexWithPendingState = kUndefinedFabricIndex;
    2077             :     }
    2078         392 : }
    2079             : 
    2080           4 : CHIP_ERROR FabricTable::SetFabricLabel(FabricIndex fabricIndex, const CharSpan & fabricLabel)
    2081             : {
    2082           4 :     VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
    2083           4 :     VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
    2084             : 
    2085           4 :     ReturnErrorCodeIf(fabricLabel.size() > kFabricLabelMaxLengthInBytes, CHIP_ERROR_INVALID_ARGUMENT);
    2086             : 
    2087           3 :     FabricInfo * fabricInfo  = GetMutableFabricByIndex(fabricIndex);
    2088           3 :     bool fabricIsInitialized = (fabricInfo != nullptr) && fabricInfo->IsInitialized();
    2089           3 :     VerifyOrReturnError(fabricIsInitialized, CHIP_ERROR_INVALID_FABRIC_INDEX);
    2090             : 
    2091             :     // Update fabric table current in-memory entry, whether pending or not
    2092           3 :     ReturnErrorOnFailure(fabricInfo->SetFabricLabel(fabricLabel));
    2093             : 
    2094           3 :     if (!mStateFlags.HasAny(StateFlags::kIsAddPending, StateFlags::kIsUpdatePending) && (fabricInfo != &mPendingFabric))
    2095             :     {
    2096             :         // Nothing is pending, we have to store immediately.
    2097           2 :         ReturnErrorOnFailure(StoreFabricMetadata(fabricInfo));
    2098             :     }
    2099             : 
    2100           3 :     return CHIP_NO_ERROR;
    2101             : }
    2102             : 
    2103           6 : CHIP_ERROR FabricTable::GetFabricLabel(FabricIndex fabricIndex, CharSpan & outFabricLabel)
    2104             : {
    2105           6 :     const FabricInfo * fabricInfo = FindFabricWithIndex(fabricIndex);
    2106           6 :     VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX);
    2107             : 
    2108           6 :     outFabricLabel = fabricInfo->GetFabricLabel();
    2109           6 :     return CHIP_NO_ERROR;
    2110             : }
    2111             : 
    2112          13 : CHIP_ERROR FabricTable::PeekFabricIndexForNextAddition(FabricIndex & outIndex)
    2113             : {
    2114          13 :     EnsureNextAvailableFabricIndexUpdated();
    2115          13 :     if (!mNextAvailableFabricIndex.HasValue())
    2116             :     {
    2117           0 :         return CHIP_ERROR_NO_MEMORY;
    2118             :     }
    2119             : 
    2120          13 :     FabricIndex index = mNextAvailableFabricIndex.Value();
    2121          13 :     VerifyOrReturnError(IsValidFabricIndex(index), CHIP_ERROR_INVALID_FABRIC_INDEX);
    2122             : 
    2123          13 :     outIndex = index;
    2124          13 :     return CHIP_NO_ERROR;
    2125             : }
    2126             : 
    2127             : } // namespace chip

Generated by: LCOV version 1.14