Matter SDK Coverage Report
Current view: top level - credentials - FabricTable.cpp (source / functions) Coverage Total Hit
Test: SHA:4cbce7f768f16e614f5a8ccb8cd93c92cbeae70d Lines: 87.9 % 963 846
Test Date: 2025-04-26 07:09:35 Functions: 92.1 % 76 70

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

Generated by: LCOV version 2.0-1