Coverage Report

Created: 2026-03-27 06:51

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