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