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