Matter SDK Coverage Report
Current view: top level - credentials - FabricTable.cpp (source / functions) Coverage Total Hit
Test: SHA:2a48c1efeab1c0f76f3adb3a0940b0f7de706453 Lines: 90.5 % 978 885
Test Date: 2026-01-31 08:14:20 Functions: 93.4 % 76 71

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

Generated by: LCOV version 2.0-1