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