Matter SDK Coverage Report
Current view: top level - credentials - FabricTable.cpp (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 90.3 % 907 819
Test Date: 2025-01-17 19:00:11 Functions: 94.4 % 72 68

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

Generated by: LCOV version 2.0-1