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 756 : CHIP_ERROR FabricInfo::Init(const FabricInfo::InitParams & initParams)
116 : {
117 756 : ReturnErrorOnFailure(initParams.AreValid());
118 :
119 756 : Reset();
120 :
121 756 : mNodeId = initParams.nodeId;
122 756 : mFabricId = initParams.fabricId;
123 756 : mFabricIndex = initParams.fabricIndex;
124 756 : mCompressedFabricId = initParams.compressedFabricId;
125 756 : mRootPublicKey = initParams.rootPublicKey;
126 756 : mVendorId = static_cast<VendorId>(initParams.vendorId);
127 756 : mShouldAdvertiseIdentity = initParams.advertiseIdentity;
128 :
129 : // Deal with externally injected keys
130 756 : if (initParams.operationalKeypair != nullptr)
131 : {
132 726 : if (initParams.hasExternallyOwnedKeypair)
133 : {
134 28 : ReturnErrorOnFailure(SetExternallyOwnedOperationalKeypair(initParams.operationalKeypair));
135 : }
136 : else
137 : {
138 698 : ReturnErrorOnFailure(SetOperationalKeypair(initParams.operationalKeypair));
139 : }
140 : }
141 :
142 756 : return CHIP_NO_ERROR;
143 : }
144 :
145 5 : void FabricInfo::operator=(FabricInfo && other)
146 : {
147 5 : Reset();
148 :
149 5 : mNodeId = other.mNodeId;
150 5 : mFabricId = other.mFabricId;
151 5 : mFabricIndex = other.mFabricIndex;
152 5 : mCompressedFabricId = other.mCompressedFabricId;
153 5 : mRootPublicKey = other.mRootPublicKey;
154 5 : mVendorId = other.mVendorId;
155 5 : mShouldAdvertiseIdentity = other.mShouldAdvertiseIdentity;
156 :
157 5 : SetFabricLabel(other.GetFabricLabel());
158 :
159 : // Transfer ownership of operational keypair (if it was nullptr, it stays that way).
160 5 : mOperationalKey = other.mOperationalKey;
161 5 : mHasExternallyOwnedOperationalKey = other.mHasExternallyOwnedOperationalKey;
162 5 : other.mOperationalKey = nullptr;
163 5 : other.mHasExternallyOwnedOperationalKey = false;
164 :
165 5 : other.Reset();
166 5 : }
167 :
168 746 : CHIP_ERROR FabricInfo::CommitToStorage(PersistentStorageDelegate * storage) const
169 : {
170 : {
171 : uint8_t buf[MetadataTLVMaxSize()];
172 746 : TLV::TLVWriter writer;
173 746 : writer.Init(buf);
174 :
175 : TLV::TLVType outerType;
176 746 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerType));
177 :
178 746 : ReturnErrorOnFailure(writer.Put(kVendorIdTag, mVendorId));
179 :
180 746 : ReturnErrorOnFailure(writer.PutString(kFabricLabelTag, CharSpan::fromCharString(mFabricLabel)));
181 :
182 746 : ReturnErrorOnFailure(writer.EndContainer(outerType));
183 :
184 746 : const auto metadataLength = writer.GetLengthWritten();
185 746 : VerifyOrReturnError(CanCastTo<uint16_t>(metadataLength), CHIP_ERROR_BUFFER_TOO_SMALL);
186 746 : 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 746 : 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 764 : CHIP_ERROR FabricInfo::SetFabricLabel(const CharSpan & fabricLabel)
250 : {
251 764 : Platform::CopyString(mFabricLabel, fabricLabel);
252 :
253 764 : 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 28 : CHIP_ERROR FabricInfo::SetExternallyOwnedOperationalKeypair(P256Keypair * keyPair)
303 : {
304 28 : VerifyOrReturnError(keyPair != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
305 28 : if (!mHasExternallyOwnedOperationalKey && mOperationalKey != nullptr)
306 : {
307 0 : chip::Platform::Delete(mOperationalKey);
308 0 : mOperationalKey = nullptr;
309 : }
310 :
311 28 : mHasExternallyOwnedOperationalKey = true;
312 28 : mOperationalKey = keyPair;
313 28 : return CHIP_NO_ERROR;
314 : }
315 :
316 760 : 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 760 : 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 760 : validContext.Reset();
343 760 : validContext.mRequiredKeyUsages.Set(KeyUsageFlags::kDigitalSignature);
344 760 : validContext.mRequiredKeyPurposes.Set(KeyPurposeFlags::kServerAuth);
345 760 : validContext.mValidityPolicy = policy;
346 :
347 760 : ChipLogProgress(FabricProvisioning, "Validating NOC chain");
348 760 : CHIP_ERROR err = FabricTable::VerifyCredentials(noc, icac, rcac, validContext, outCompressedFabricId, outFabricId, outNodeId,
349 : outNocPubkey, &outRootPubkey);
350 760 : 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 756 : if (existingFabricId != kUndefinedFabricId)
364 : {
365 8 : VerifyOrReturnError(existingFabricId == outFabricId, CHIP_ERROR_UNSUPPORTED_CERT_FORMAT);
366 : }
367 :
368 756 : ChipLogProgress(FabricProvisioning, "NOC chain validation successful");
369 756 : return CHIP_NO_ERROR;
370 760 : }
371 :
372 9 : CHIP_ERROR FabricInfo::SignWithOpKeypair(ByteSpan message, P256ECDSASignature & outSignature) const
373 : {
374 : MATTER_TRACE_SCOPE("SignWithOpKeypair", "Fabric");
375 9 : VerifyOrReturnError(mOperationalKey != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
376 :
377 9 : return mOperationalKey->ECDSA_sign_msg(message.data(), message.size(), outSignature);
378 : }
379 :
380 70 : CHIP_ERROR FabricInfo::FetchRootPubkey(Crypto::P256PublicKey & outPublicKey) const
381 : {
382 : MATTER_TRACE_SCOPE("FetchRootPubKey", "Fabric");
383 70 : VerifyOrReturnError(IsInitialized(), CHIP_ERROR_KEY_NOT_FOUND);
384 70 : outPublicKey = mRootPublicKey;
385 70 : return CHIP_NO_ERROR;
386 : }
387 :
388 9 : CHIP_ERROR FabricTable::VerifyCredentials(FabricIndex fabricIndex, ByteSpan noc, ByteSpan icac, ValidationContext & context,
389 : CompressedFabricId & outCompressedFabricId, FabricId & outFabricId, NodeId & outNodeId,
390 : Crypto::P256PublicKey & outNocPubkey, Crypto::P256PublicKey * outRootPublicKey) const
391 : {
392 : MATTER_TRACE_SCOPE("VerifyCredentials", "Fabric");
393 9 : assertChipStackLockedByCurrentThread();
394 : uint8_t rootCertBuf[kMaxCHIPCertLength];
395 9 : MutableByteSpan rootCertSpan{ rootCertBuf };
396 9 : ReturnErrorOnFailure(FetchRootCert(fabricIndex, rootCertSpan));
397 18 : return VerifyCredentials(noc, icac, rootCertSpan, context, outCompressedFabricId, outFabricId, outNodeId, outNocPubkey,
398 9 : outRootPublicKey);
399 : }
400 :
401 777 : 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 777 : constexpr uint8_t kMaxNumCertsInOpCreds = 3;
410 :
411 777 : ChipCertificateSet certificates;
412 777 : ReturnErrorOnFailure(certificates.Init(kMaxNumCertsInOpCreds));
413 :
414 777 : ReturnErrorOnFailure(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)));
415 :
416 777 : if (!icac.empty())
417 : {
418 753 : ReturnErrorOnFailure(certificates.LoadCert(icac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kGenerateTBSHash)));
419 : }
420 :
421 777 : ReturnErrorOnFailure(certificates.LoadCert(noc, BitFlags<CertDecodeFlags>(CertDecodeFlags::kGenerateTBSHash)));
422 :
423 777 : const ChipDN & nocSubjectDN = certificates.GetLastCert()[0].mSubjectDN;
424 777 : const CertificateKeyId & nocSubjectKeyId = certificates.GetLastCert()[0].mSubjectKeyId;
425 :
426 777 : 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 777 : ReturnErrorOnFailure(certificates.FindValidCert(nocSubjectDN, nocSubjectKeyId, context, &resultCert));
430 :
431 773 : ReturnErrorOnFailure(ExtractNodeIdFabricIdFromOpCert(certificates.GetLastCert()[0], &outNodeId, &outFabricId));
432 :
433 : CHIP_ERROR err;
434 773 : FabricId icacFabricId = kUndefinedFabricId;
435 773 : if (!icac.empty())
436 : {
437 750 : err = ExtractFabricIdFromCert(certificates.GetCertSet()[1], &icacFabricId);
438 750 : if (err == CHIP_NO_ERROR)
439 : {
440 21 : 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 729 : else if (err != CHIP_ERROR_NOT_FOUND)
444 : {
445 0 : return err;
446 : }
447 : }
448 :
449 773 : FabricId rcacFabricId = kUndefinedFabricId;
450 773 : err = ExtractFabricIdFromCert(certificates.GetCertSet()[0], &rcacFabricId);
451 773 : 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 73 : 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 773 : MutableByteSpan compressedFabricIdSpan(compressedFabricIdBuf);
465 773 : P256PublicKey rootPubkey(certificates.GetCertSet()[0].mPublicKey);
466 :
467 773 : 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 773 : outCompressedFabricId = Encoding::BigEndian::Get64(compressedFabricIdBuf);
472 :
473 773 : if (outRootPublicKey != nullptr)
474 : {
475 756 : *outRootPublicKey = rootPubkey;
476 : }
477 773 : }
478 :
479 773 : outNocPubkey = certificates.GetLastCert()->mPublicKey;
480 :
481 773 : return CHIP_NO_ERROR;
482 777 : }
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 1543 : FabricInfo * FabricTable::GetMutableFabricByIndex(FabricIndex fabricIndex)
532 : {
533 : // Try to match pending fabric first if available
534 1543 : if (HasPendingFabricUpdate() && (mPendingFabric.GetFabricIndex() == fabricIndex))
535 : {
536 7 : return &mPendingFabric;
537 : }
538 :
539 2384 : for (auto & fabric : mStates)
540 : {
541 2377 : if (!fabric.IsInitialized())
542 : {
543 110 : continue;
544 : }
545 :
546 2267 : if (fabric.GetFabricIndex() == fabricIndex)
547 : {
548 1529 : return &fabric;
549 : }
550 : }
551 :
552 7 : return nullptr;
553 : }
554 :
555 32170 : const FabricInfo * FabricTable::FindFabricWithIndex(FabricIndex fabricIndex) const
556 : {
557 32170 : if (fabricIndex == kUndefinedFabricIndex)
558 : {
559 48 : return nullptr;
560 : }
561 :
562 : // Try to match pending fabric first if available
563 32122 : if (HasPendingFabricUpdate() && (mPendingFabric.GetFabricIndex() == fabricIndex))
564 : {
565 22 : return &mPendingFabric;
566 : }
567 :
568 59510 : for (const auto & fabric : mStates)
569 : {
570 58770 : if (!fabric.IsInitialized())
571 : {
572 10744 : continue;
573 : }
574 :
575 48026 : if (fabric.GetFabricIndex() == fabricIndex)
576 : {
577 31360 : return &fabric;
578 : }
579 : }
580 :
581 740 : 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 863 : CHIP_ERROR FabricTable::FetchRootCert(FabricIndex fabricIndex, MutableByteSpan & outCert) const
608 : {
609 : MATTER_TRACE_SCOPE("FetchRootCert", "Fabric");
610 863 : VerifyOrReturnError(mOpCertStore != nullptr, CHIP_ERROR_INCORRECT_STATE);
611 863 : 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 786 : CHIP_ERROR FabricTable::FetchICACert(FabricIndex fabricIndex, MutableByteSpan & outCert) const
634 : {
635 : MATTER_TRACE_SCOPE("FetchICACert", "Fabric");
636 786 : VerifyOrReturnError(mOpCertStore != nullptr, CHIP_ERROR_INCORRECT_STATE);
637 :
638 786 : CHIP_ERROR err = mOpCertStore->GetCertificate(fabricIndex, CertChainElement::kIcac, outCert);
639 786 : if (err == CHIP_ERROR_NOT_FOUND)
640 : {
641 24 : 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 24 : outCert.reduce_size(0);
645 24 : return CHIP_NO_ERROR;
646 : }
647 : }
648 :
649 : // For all other cases, delegate to operational cert store for results
650 762 : return err;
651 : }
652 :
653 808 : CHIP_ERROR FabricTable::FetchNOCCert(FabricIndex fabricIndex, MutableByteSpan & outCert) const
654 : {
655 : MATTER_TRACE_SCOPE("FetchNOCCert", "Fabric");
656 808 : VerifyOrReturnError(mOpCertStore != nullptr, CHIP_ERROR_INCORRECT_STATE);
657 808 : return mOpCertStore->GetCertificate(fabricIndex, CertChainElement::kNoc, outCert);
658 : }
659 :
660 56 : CHIP_ERROR FabricTable::FetchRootPubkey(FabricIndex fabricIndex, Crypto::P256PublicKey & outPublicKey) const
661 : {
662 : MATTER_TRACE_SCOPE("FetchRootPubkey", "Fabric");
663 56 : const FabricInfo * fabricInfo = FindFabricWithIndex(fabricIndex);
664 56 : VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX);
665 56 : 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 746 : CHIP_ERROR FabricTable::StoreFabricMetadata(const FabricInfo * fabricInfo) const
678 : {
679 746 : VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
680 746 : VerifyOrDie(fabricInfo != nullptr);
681 :
682 746 : FabricIndex fabricIndex = fabricInfo->GetFabricIndex();
683 746 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INTERNAL);
684 :
685 : // TODO: Refactor not to internally rely directly on storage
686 746 : ReturnErrorOnFailure(fabricInfo->CommitToStorage(mStorage));
687 :
688 746 : ChipLogProgress(FabricProvisioning, "Metadata for Fabric 0x%x persisted to storage.", static_cast<unsigned>(fabricIndex));
689 :
690 746 : 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 760 : NotBeforeCollector() : mLatestNotBefore(0) {}
764 2258 : CHIP_ERROR ApplyCertificateValidityPolicy(const ChipCertificateData * cert, uint8_t depth,
765 : CertificateValidityResult result) override
766 : {
767 2258 : if (cert->mNotBeforeTime > mLatestNotBefore.count())
768 : {
769 1457 : mLatestNotBefore = System::Clock::Seconds32(cert->mNotBeforeTime);
770 : }
771 2258 : return CHIP_NO_ERROR;
772 : }
773 : System::Clock::Seconds32 mLatestNotBefore;
774 : };
775 :
776 761 : CHIP_ERROR FabricTable::NotifyFabricUpdated(FabricIndex fabricIndex)
777 : {
778 : MATTER_TRACE_SCOPE("NotifyFabricUpdated", "Fabric");
779 761 : FabricTable::Delegate * delegate = mDelegateListRoot;
780 1468 : 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 761 : return CHIP_NO_ERROR;
789 : }
790 :
791 743 : CHIP_ERROR FabricTable::NotifyFabricCommitted(FabricIndex fabricIndex)
792 : {
793 : MATTER_TRACE_SCOPE("NotifyFabricCommitted", "Fabric");
794 :
795 743 : FabricTable::Delegate * delegate = mDelegateListRoot;
796 1445 : 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 743 : return CHIP_NO_ERROR;
805 : }
806 :
807 : CHIP_ERROR
808 760 : 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 760 : FabricInfo::InitParams newFabricInfo;
814 760 : FabricInfo * fabricEntry = nullptr;
815 760 : FabricId fabricIdToValidate = kUndefinedFabricId;
816 760 : CharSpan fabricLabel;
817 :
818 760 : if (isAddition)
819 : {
820 : // Initialization for Adding a fabric
821 :
822 : // Find an available slot.
823 1112 : for (auto & fabric : mStates)
824 : {
825 1112 : if (fabric.IsInitialized())
826 : {
827 360 : continue;
828 : }
829 752 : fabricEntry = &fabric;
830 752 : break;
831 : }
832 :
833 752 : VerifyOrReturnError(fabricEntry != nullptr, CHIP_ERROR_NO_MEMORY);
834 :
835 752 : newFabricInfo.vendorId = static_cast<VendorId>(vendorId);
836 752 : newFabricInfo.fabricIndex = fabricIndex;
837 : }
838 : else
839 : {
840 : // Initialization for Updating fabric: setting up a shadow fabricInfo
841 8 : const FabricInfo * existingFabric = FindFabricWithIndex(fabricIndex);
842 8 : VerifyOrReturnError(existingFabric != nullptr, CHIP_ERROR_INTERNAL);
843 :
844 8 : mPendingFabric.Reset();
845 8 : fabricEntry = &mPendingFabric;
846 :
847 8 : newFabricInfo.vendorId = existingFabric->GetVendorId();
848 8 : newFabricInfo.fabricIndex = fabricIndex;
849 :
850 8 : fabricIdToValidate = existingFabric->GetFabricId();
851 8 : fabricLabel = existingFabric->GetFabricLabel();
852 : }
853 :
854 : // Make sure to not modify any of our state until ValidateIncomingNOCChain passes.
855 760 : NotBeforeCollector notBeforeCollector;
856 760 : P256PublicKey nocPubKey;
857 :
858 : // Validate the cert chain prior to adding
859 : {
860 760 : Platform::ScopedMemoryBuffer<uint8_t> nocBuf;
861 760 : Platform::ScopedMemoryBuffer<uint8_t> icacBuf;
862 760 : Platform::ScopedMemoryBuffer<uint8_t> rcacBuf;
863 :
864 760 : VerifyOrReturnError(nocBuf.Alloc(kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY);
865 760 : VerifyOrReturnError(icacBuf.Alloc(kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY);
866 760 : VerifyOrReturnError(rcacBuf.Alloc(kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY);
867 :
868 760 : MutableByteSpan nocSpan{ nocBuf.Get(), kMaxCHIPCertLength };
869 760 : MutableByteSpan icacSpan{ icacBuf.Get(), kMaxCHIPCertLength };
870 760 : MutableByteSpan rcacSpan{ rcacBuf.Get(), kMaxCHIPCertLength };
871 :
872 760 : ReturnErrorOnFailure(FetchNOCCert(fabricIndex, nocSpan));
873 760 : ReturnErrorOnFailure(FetchICACert(fabricIndex, icacSpan));
874 760 : ReturnErrorOnFailure(FetchRootCert(fabricIndex, rcacSpan));
875 :
876 760 : ReturnErrorOnFailure(ValidateIncomingNOCChain(nocSpan, icacSpan, rcacSpan, fabricIdToValidate, ¬BeforeCollector,
877 : newFabricInfo.compressedFabricId, newFabricInfo.fabricId,
878 : newFabricInfo.nodeId, nocPubKey, newFabricInfo.rootPublicKey));
879 768 : }
880 :
881 756 : 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 726 : VerifyOrReturnError(existingOpKey->Pubkey().Matches(nocPubKey), CHIP_ERROR_INVALID_PUBLIC_KEY);
887 :
888 726 : newFabricInfo.operationalKeypair = existingOpKey;
889 726 : newFabricInfo.hasExternallyOwnedKeypair = isExistingOpKeyExternallyOwned;
890 : }
891 30 : 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 30 : if (mOperationalKeystore->HasPendingOpKeypair())
895 : {
896 29 : 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 756 : 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 756 : ReturnErrorOnFailure(fabricEntry->Init(newFabricInfo));
912 :
913 : // Set the label, matching add/update semantics of empty/existing.
914 756 : fabricEntry->SetFabricLabel(fabricLabel);
915 :
916 756 : if (isAddition)
917 : {
918 748 : ChipLogProgress(FabricProvisioning, "Added new fabric at index: 0x%x",
919 : static_cast<unsigned>(fabricEntry->GetFabricIndex()));
920 748 : ChipLogProgress(FabricProvisioning, "Assigned compressed fabric ID: 0x" ChipLogFormatX64 ", node ID: 0x" ChipLogFormatX64,
921 : ChipLogValueX64(fabricEntry->GetCompressedFabricId()), ChipLogValueX64(fabricEntry->GetNodeId()));
922 : }
923 : else
924 : {
925 8 : 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 756 : CHIP_ERROR lkgtErr = mLastKnownGoodTime.UpdatePendingLastKnownGoodChipEpochTime(notBeforeCollector.mLatestNotBefore);
936 756 : 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 756 : if (isAddition)
944 : {
945 748 : mFabricCount++;
946 : }
947 :
948 756 : return CHIP_NO_ERROR;
949 760 : }
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 437 : CHIP_ERROR FabricTable::Init(const FabricTable::InitParams & initParams)
1075 : {
1076 437 : VerifyOrReturnError(initParams.storage != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
1077 437 : VerifyOrReturnError(initParams.opCertStore != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
1078 :
1079 437 : mStorage = initParams.storage;
1080 437 : mOperationalKeystore = initParams.operationalKeystore;
1081 437 : mOpCertStore = initParams.opCertStore;
1082 :
1083 437 : 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 437 : mFabricCount = 0;
1089 7429 : for (auto & fabric : mStates)
1090 : {
1091 6992 : fabric.Reset();
1092 : }
1093 437 : 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 437 : mLastKnownGoodTime.Init(mStorage);
1100 :
1101 : uint8_t buf[IndexInfoTLVMaxSize()];
1102 437 : uint16_t size = sizeof(buf);
1103 437 : CHIP_ERROR err = mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::FabricIndexInfo().KeyName(), buf, size);
1104 437 : 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 437 : CommitMarker commitMarker;
1126 437 : err = GetCommitMarker(commitMarker);
1127 437 : 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 436 : 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 437 : 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 433 : void FabricTable::Shutdown()
1160 : {
1161 433 : VerifyOrReturn(mStorage != nullptr);
1162 433 : ChipLogProgress(FabricProvisioning, "Shutting down FabricTable");
1163 :
1164 : // Remove all links to every delegate
1165 433 : FabricTable::Delegate * delegate = mDelegateListRoot;
1166 724 : while (delegate)
1167 : {
1168 291 : FabricTable::Delegate * temp = delegate->next;
1169 291 : delegate->next = nullptr;
1170 291 : delegate = temp;
1171 : }
1172 :
1173 433 : RevertPendingFabricData();
1174 7361 : for (FabricInfo & fabricInfo : mStates)
1175 : {
1176 : // Clear-out any FabricInfo-owned operational keys and make sure any further
1177 : // direct lookups fail.
1178 6928 : fabricInfo.Reset();
1179 : }
1180 :
1181 433 : 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 849 : CHIP_ERROR FabricTable::AddFabricDelegate(FabricTable::Delegate * delegate)
1195 : {
1196 849 : VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
1197 1164 : for (FabricTable::Delegate * iter = mDelegateListRoot; iter != nullptr; iter = iter->next)
1198 : {
1199 440 : if (iter == delegate)
1200 : {
1201 125 : return CHIP_NO_ERROR;
1202 : }
1203 : }
1204 724 : delegate->next = mDelegateListRoot;
1205 724 : mDelegateListRoot = delegate;
1206 724 : return CHIP_NO_ERROR;
1207 : }
1208 :
1209 431 : void FabricTable::RemoveFabricDelegate(FabricTable::Delegate * delegateToRemove)
1210 : {
1211 431 : VerifyOrReturn(delegateToRemove != nullptr);
1212 :
1213 431 : if (delegateToRemove == mDelegateListRoot)
1214 : {
1215 : // Removing head of the list, keep link to next, may
1216 : // be nullptr.
1217 137 : 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 739 : FabricIndex NextFabricIndex(FabricIndex fabricIndex)
1294 : {
1295 739 : if (fabricIndex == kMaxValidFabricIndex)
1296 : {
1297 0 : return kMinValidFabricIndex;
1298 : }
1299 :
1300 739 : return static_cast<FabricIndex>(fabricIndex + 1);
1301 : }
1302 : } // anonymous namespace
1303 :
1304 738 : void FabricTable::UpdateNextAvailableFabricIndex()
1305 : {
1306 : // Only called when mNextAvailableFabricIndex.HasValue()
1307 739 : for (FabricIndex candidate = NextFabricIndex(mNextAvailableFabricIndex.Value()); candidate != mNextAvailableFabricIndex.Value();
1308 1 : candidate = NextFabricIndex(candidate))
1309 : {
1310 739 : if (!FindFabricWithIndex(candidate))
1311 : {
1312 738 : mNextAvailableFabricIndex.SetValue(candidate);
1313 738 : return;
1314 : }
1315 : }
1316 :
1317 0 : mNextAvailableFabricIndex.ClearValue();
1318 : }
1319 :
1320 756 : CHIP_ERROR FabricTable::StoreFabricIndexInfo() const
1321 : {
1322 : uint8_t buf[IndexInfoTLVMaxSize()];
1323 756 : TLV::TLVWriter writer;
1324 756 : writer.Init(buf);
1325 :
1326 : TLV::TLVType outerType;
1327 756 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerType));
1328 :
1329 756 : if (mNextAvailableFabricIndex.HasValue())
1330 : {
1331 756 : writer.Put(kNextAvailableFabricIndexTag, mNextAvailableFabricIndex.Value());
1332 : }
1333 : else
1334 : {
1335 0 : writer.PutNull(kNextAvailableFabricIndexTag);
1336 : }
1337 :
1338 : TLV::TLVType innerContainerType;
1339 756 : ReturnErrorOnFailure(writer.StartContainer(kFabricIndicesTag, TLV::kTLVType_Array, innerContainerType));
1340 : // Only enumerate the fabrics that are initialized.
1341 1859 : for (const auto & fabric : *this)
1342 : {
1343 1103 : writer.Put(TLV::AnonymousTag(), fabric.GetFabricIndex());
1344 : }
1345 756 : ReturnErrorOnFailure(writer.EndContainer(innerContainerType));
1346 756 : ReturnErrorOnFailure(writer.EndContainer(outerType));
1347 :
1348 756 : const auto indexInfoLength = writer.GetLengthWritten();
1349 756 : VerifyOrReturnError(CanCastTo<uint16_t>(indexInfoLength), CHIP_ERROR_BUFFER_TOO_SMALL);
1350 :
1351 756 : ReturnErrorOnFailure(mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::FabricIndexInfo().KeyName(), buf,
1352 : static_cast<uint16_t>(indexInfoLength)));
1353 :
1354 756 : return CHIP_NO_ERROR;
1355 : }
1356 :
1357 1571 : void FabricTable::EnsureNextAvailableFabricIndexUpdated()
1358 : {
1359 1571 : 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 1571 : }
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 26 : Crypto::P256Keypair * FabricTable::AllocateEphemeralKeypairForCASE()
1434 : {
1435 26 : if (mOperationalKeystore != nullptr)
1436 : {
1437 11 : return mOperationalKeystore->AllocateEphemeralKeypairForCASE();
1438 : }
1439 :
1440 15 : return Platform::New<Crypto::P256Keypair>();
1441 : }
1442 :
1443 36 : void FabricTable::ReleaseEphemeralKeypair(Crypto::P256Keypair * keypair)
1444 : {
1445 36 : if (mOperationalKeystore != nullptr)
1446 : {
1447 20 : mOperationalKeystore->ReleaseEphemeralKeypair(keypair);
1448 : }
1449 : else
1450 : {
1451 16 : Platform::Delete<Crypto::P256Keypair>(keypair);
1452 : }
1453 36 : }
1454 :
1455 744 : CHIP_ERROR FabricTable::StoreCommitMarker(const CommitMarker & commitMarker)
1456 : {
1457 : uint8_t tlvBuf[CommitMarkerContextTLVMaxSize()];
1458 744 : TLV::TLVWriter writer;
1459 744 : writer.Init(tlvBuf);
1460 :
1461 : TLV::TLVType outerType;
1462 744 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerType));
1463 744 : ReturnErrorOnFailure(writer.Put(kMarkerFabricIndexTag, commitMarker.fabricIndex));
1464 744 : ReturnErrorOnFailure(writer.Put(kMarkerIsAdditionTag, commitMarker.isAddition));
1465 744 : ReturnErrorOnFailure(writer.EndContainer(outerType));
1466 :
1467 744 : const auto markerContextTLVLength = writer.GetLengthWritten();
1468 744 : VerifyOrReturnError(CanCastTo<uint16_t>(markerContextTLVLength), CHIP_ERROR_BUFFER_TOO_SMALL);
1469 :
1470 1488 : return mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::FabricTableCommitMarkerKey().KeyName(), tlvBuf,
1471 744 : static_cast<uint16_t>(markerContextTLVLength));
1472 : }
1473 :
1474 437 : CHIP_ERROR FabricTable::GetCommitMarker(CommitMarker & outCommitMarker)
1475 : {
1476 : uint8_t tlvBuf[CommitMarkerContextTLVMaxSize()];
1477 437 : uint16_t tlvSize = sizeof(tlvBuf);
1478 437 : 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 744 : void FabricTable::ClearCommitMarker()
1502 : {
1503 744 : mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::FabricTableCommitMarkerKey().KeyName());
1504 744 : }
1505 :
1506 744 : bool FabricTable::HasOperationalKeyForFabric(FabricIndex fabricIndex) const
1507 : {
1508 744 : const FabricInfo * fabricInfo = FindFabricWithIndex(fabricIndex);
1509 744 : VerifyOrReturnError(fabricInfo != nullptr, false);
1510 :
1511 744 : if (fabricInfo->HasOperationalKey())
1512 : {
1513 : // Legacy case of manually injected keys: delegate to FabricInfo directly
1514 719 : return true;
1515 : }
1516 25 : if (mOperationalKeystore != nullptr)
1517 : {
1518 25 : return mOperationalKeystore->HasOpKeypairForFabric(fabricIndex);
1519 : }
1520 :
1521 0 : return false;
1522 : }
1523 :
1524 34 : CHIP_ERROR FabricTable::SignWithOpKeypair(FabricIndex fabricIndex, ByteSpan message, P256ECDSASignature & outSignature) const
1525 : {
1526 34 : const FabricInfo * fabricInfo = FindFabricWithIndex(fabricIndex);
1527 34 : VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
1528 :
1529 34 : if (fabricInfo->HasOperationalKey())
1530 : {
1531 : // Legacy case of manually injected FabricInfo: delegate to FabricInfo directly
1532 9 : return fabricInfo->SignWithOpKeypair(message, outSignature);
1533 : }
1534 25 : if (mOperationalKeystore != nullptr)
1535 : {
1536 25 : 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 1545 : bool FabricTable::SetPendingDataFabricIndex(FabricIndex fabricIndex)
1558 : {
1559 1545 : bool isLegal = (mFabricIndexWithPendingState == kUndefinedFabricIndex) || (mFabricIndexWithPendingState == fabricIndex);
1560 :
1561 1545 : if (isLegal)
1562 : {
1563 1545 : mFabricIndexWithPendingState = fabricIndex;
1564 : }
1565 1545 : return isLegal;
1566 : }
1567 :
1568 32 : FabricIndex FabricTable::GetPendingNewFabricIndex() const
1569 : {
1570 32 : if (mStateFlags.Has(StateFlags::kIsAddPending))
1571 : {
1572 2 : return mFabricIndexWithPendingState;
1573 : }
1574 :
1575 30 : return kUndefinedFabricIndex;
1576 : }
1577 :
1578 32 : 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 32 : 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 32 : VerifyOrReturnError(!mStateFlags.Has(StateFlags::kIsPendingFabricDataPresent), CHIP_ERROR_INCORRECT_STATE);
1586 32 : VerifyOrReturnError(outputCsr.size() >= Crypto::kMIN_CSR_Buffer_Size, CHIP_ERROR_BUFFER_TOO_SMALL);
1587 :
1588 32 : EnsureNextAvailableFabricIndexUpdated();
1589 32 : FabricIndex fabricIndexToUse = kUndefinedFabricIndex;
1590 :
1591 32 : if (fabricIndex.HasValue())
1592 : {
1593 : // Check we not are trying to do an update but also change the root: forbidden
1594 9 : VerifyOrReturnError(!mStateFlags.Has(StateFlags::kIsTrustedRootPending), CHIP_ERROR_INCORRECT_STATE);
1595 :
1596 : // Fabric update case (e.g. UpdateNOC): we already know the fabric index
1597 9 : fabricIndexToUse = fabricIndex.Value();
1598 9 : mStateFlags.Set(StateFlags::kIsPendingKeyForUpdateNoc);
1599 : }
1600 23 : else if (mNextAvailableFabricIndex.HasValue())
1601 : {
1602 : // Fabric addition case (e.g. AddNOC): we need to allocate for the next pending fabric index
1603 23 : fabricIndexToUse = mNextAvailableFabricIndex.Value();
1604 23 : 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 32 : VerifyOrReturnError(IsValidFabricIndex(fabricIndexToUse), CHIP_ERROR_INVALID_FABRIC_INDEX);
1613 32 : VerifyOrReturnError(SetPendingDataFabricIndex(fabricIndexToUse), CHIP_ERROR_INCORRECT_STATE);
1614 32 : ReturnErrorOnFailure(mOperationalKeystore->NewOpKeypairForFabric(mFabricIndexWithPendingState, outputCsr));
1615 32 : mStateFlags.Set(StateFlags::kIsOperationalKeyPending);
1616 :
1617 32 : return CHIP_NO_ERROR;
1618 : }
1619 :
1620 753 : CHIP_ERROR FabricTable::AddNewPendingTrustedRootCert(const ByteSpan & rcac)
1621 : {
1622 753 : VerifyOrReturnError(mOpCertStore != nullptr, CHIP_ERROR_INCORRECT_STATE);
1623 :
1624 : // We should not already have pending NOC chain elements when we get here
1625 753 : VerifyOrReturnError(
1626 : !mStateFlags.HasAny(StateFlags::kIsTrustedRootPending, StateFlags::kIsUpdatePending, StateFlags::kIsAddPending),
1627 : CHIP_ERROR_INCORRECT_STATE);
1628 :
1629 753 : EnsureNextAvailableFabricIndexUpdated();
1630 753 : FabricIndex fabricIndexToUse = kUndefinedFabricIndex;
1631 :
1632 753 : if (mNextAvailableFabricIndex.HasValue())
1633 : {
1634 753 : 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 753 : VerifyOrReturnError(IsValidFabricIndex(fabricIndexToUse), CHIP_ERROR_INVALID_FABRIC_INDEX);
1643 753 : VerifyOrReturnError(SetPendingDataFabricIndex(fabricIndexToUse), CHIP_ERROR_INCORRECT_STATE);
1644 753 : ReturnErrorOnFailure(mOpCertStore->AddNewTrustedRootCertForFabric(fabricIndexToUse, rcac));
1645 :
1646 753 : mStateFlags.Set(StateFlags::kIsPendingFabricDataPresent);
1647 753 : mStateFlags.Set(StateFlags::kIsTrustedRootPending);
1648 753 : return CHIP_NO_ERROR;
1649 : }
1650 :
1651 62 : 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 62 : ReturnErrorOnFailure(ExtractNodeIdFabricIdFromOpCert(noc, &unused, &fabricId));
1663 : }
1664 :
1665 : // Try to find the root public key from the current existing fabric
1666 62 : Crypto::P256PublicKey candidateRootKey;
1667 : {
1668 : uint8_t tempRcac[kMaxCHIPCertLength];
1669 62 : MutableByteSpan tempRcacSpan{ tempRcac };
1670 62 : Credentials::P256PublicKeySpan publicKeySpan;
1671 62 : ReturnErrorOnFailure(FetchRootCert(pendingFabricIndex, tempRcacSpan));
1672 62 : ReturnErrorOnFailure(ExtractPublicKeyFromChipCert(tempRcacSpan, publicKeySpan));
1673 62 : candidateRootKey = publicKeySpan;
1674 : }
1675 :
1676 73 : for (auto & existingFabric : *this)
1677 : {
1678 21 : if (existingFabric.GetFabricId() == fabricId)
1679 : {
1680 14 : P256PublicKey existingRootKey;
1681 14 : ReturnErrorOnFailure(FetchRootPubkey(existingFabric.GetFabricIndex(), existingRootKey));
1682 :
1683 14 : if (existingRootKey.Matches(candidateRootKey))
1684 : {
1685 10 : outMatchingFabricIndex = existingFabric.GetFabricIndex();
1686 10 : return CHIP_NO_ERROR;
1687 : }
1688 14 : }
1689 : }
1690 :
1691 : // Did not find: set outMatchingFabricIndex to kUndefinedFabricIndex
1692 52 : outMatchingFabricIndex = kUndefinedFabricIndex;
1693 52 : return CHIP_NO_ERROR;
1694 62 : }
1695 :
1696 754 : 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 754 : VerifyOrReturnError(mOpCertStore != nullptr, CHIP_ERROR_INCORRECT_STATE);
1702 754 : 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 754 : VerifyOrReturnError(mStateFlags.Has(StateFlags::kIsTrustedRootPending), CHIP_ERROR_INCORRECT_STATE);
1707 : // We should not have pending update when we get here
1708 754 : VerifyOrReturnError(!mStateFlags.Has(StateFlags::kIsUpdatePending), CHIP_ERROR_INCORRECT_STATE);
1709 :
1710 754 : EnsureNextAvailableFabricIndexUpdated();
1711 754 : FabricIndex fabricIndexToUse = kUndefinedFabricIndex;
1712 754 : if (mNextAvailableFabricIndex.HasValue())
1713 : {
1714 754 : 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 754 : VerifyOrReturnError(IsValidFabricIndex(fabricIndexToUse), CHIP_ERROR_INVALID_FABRIC_INDEX);
1725 :
1726 754 : 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 27 : VerifyOrReturnError(mOperationalKeystore != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
1731 : // Make sure we have an operational key, pending or not
1732 27 : 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 754 : if (!mStateFlags.Has(StateFlags::kAreCollidingFabricsIgnored))
1739 : {
1740 54 : FabricIndex collidingFabricIndex = kUndefinedFabricIndex;
1741 54 : ReturnErrorOnFailure(FindExistingFabricByNocChaining(fabricIndexToUse, noc, collidingFabricIndex));
1742 54 : VerifyOrReturnError(collidingFabricIndex == kUndefinedFabricIndex, CHIP_ERROR_FABRIC_EXISTS);
1743 : }
1744 :
1745 : // We don't have a collision, handle the temp insert of NOC/ICAC
1746 752 : ReturnErrorOnFailure(mOpCertStore->AddNewOpCertsForFabric(fabricIndexToUse, noc, icac));
1747 752 : VerifyOrReturnError(SetPendingDataFabricIndex(fabricIndexToUse), CHIP_ERROR_INCORRECT_STATE);
1748 :
1749 752 : CHIP_ERROR err = AddOrUpdateInner(fabricIndexToUse, /* isAddition = */ true, existingOpKey, isExistingOpKeyExternallyOwned,
1750 : vendorId, advertiseIdentity);
1751 752 : if (err != CHIP_NO_ERROR)
1752 : {
1753 : // Revert partial state added on error
1754 4 : RevertPendingOpCertsExceptRoot();
1755 4 : return err;
1756 : }
1757 :
1758 748 : mStateFlags.Set(StateFlags::kIsAddPending);
1759 748 : mStateFlags.Set(StateFlags::kIsPendingFabricDataPresent);
1760 :
1761 : // Notify that NOC was added (at least transiently)
1762 748 : *outNewFabricIndex = fabricIndexToUse;
1763 748 : NotifyFabricUpdated(fabricIndexToUse);
1764 :
1765 748 : return CHIP_NO_ERROR;
1766 : }
1767 :
1768 9 : 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 9 : VerifyOrReturnError(mOpCertStore != nullptr, CHIP_ERROR_INCORRECT_STATE);
1774 9 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_ARGUMENT);
1775 :
1776 9 : 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 9 : VerifyOrReturnError(mOperationalKeystore != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
1781 : // Make sure we have an operational key, pending or not
1782 9 : 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 9 : 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 9 : VerifyOrReturnError(!mStateFlags.Has(StateFlags::kIsAddPending), CHIP_ERROR_INCORRECT_STATE);
1791 :
1792 : // Make sure we are updating at least an existing FabricIndex
1793 9 : const auto * fabricInfo = FindFabricWithIndex(fabricIndex);
1794 9 : VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX);
1795 :
1796 : // Cannot have a VVSC already if ICAC is provided.
1797 9 : if (!icac.empty())
1798 : {
1799 : uint8_t vvscBuffer[kMaxCHIPCertLength];
1800 3 : MutableByteSpan vvscSpan{ vvscBuffer };
1801 :
1802 3 : CHIP_ERROR err = mOpCertStore->GetVidVerificationElement(
1803 : fabricIndex, OperationalCertificateStore::VidVerificationElement::kVvsc, vvscSpan);
1804 3 : if (err == CHIP_NO_ERROR)
1805 : {
1806 3 : if (!vvscSpan.empty())
1807 : {
1808 1 : ChipLogError(
1809 : FabricProvisioning,
1810 : "Received an UpdateNOC storage request with ICAC when VVSC already present. VVSC must be removed first.");
1811 1 : return CHIP_ERROR_INCORRECT_STATE;
1812 : }
1813 : }
1814 0 : else if (err != CHIP_ERROR_NOT_IMPLEMENTED)
1815 : {
1816 0 : return err;
1817 : }
1818 : }
1819 :
1820 : // Check for an existing fabric matching RCAC and FabricID. We must find a correct
1821 : // existing fabric that chains to same root. We assume the stored root is correct.
1822 8 : if (!mStateFlags.Has(StateFlags::kAreCollidingFabricsIgnored))
1823 : {
1824 8 : FabricIndex collidingFabricIndex = kUndefinedFabricIndex;
1825 8 : ReturnErrorOnFailure(FindExistingFabricByNocChaining(fabricIndex, noc, collidingFabricIndex));
1826 8 : VerifyOrReturnError(collidingFabricIndex == fabricIndex, CHIP_ERROR_INVALID_FABRIC_INDEX);
1827 : }
1828 :
1829 : // Handle the temp insert of NOC/ICAC
1830 8 : ReturnErrorOnFailure(mOpCertStore->UpdateOpCertsForFabric(fabricIndex, noc, icac));
1831 8 : VerifyOrReturnError(SetPendingDataFabricIndex(fabricIndex), CHIP_ERROR_INCORRECT_STATE);
1832 :
1833 8 : CHIP_ERROR err = AddOrUpdateInner(fabricIndex, /* isAddition = */ false, existingOpKey, isExistingOpKeyExternallyOwned,
1834 8 : fabricInfo->GetVendorId(), advertiseIdentity);
1835 8 : if (err != CHIP_NO_ERROR)
1836 : {
1837 : // Revert partial state added on error
1838 : // TODO: Figure-out if there is a better way. We need to make sure we are not inconsistent on elements
1839 : // other than the opcerts.
1840 0 : RevertPendingOpCertsExceptRoot();
1841 0 : return err;
1842 : }
1843 :
1844 8 : mStateFlags.Set(StateFlags::kIsUpdatePending);
1845 8 : mStateFlags.Set(StateFlags::kIsPendingFabricDataPresent);
1846 :
1847 : // Notify that NOC was updated (at least transiently)
1848 8 : NotifyFabricUpdated(fabricIndex);
1849 :
1850 8 : return CHIP_NO_ERROR;
1851 : }
1852 :
1853 745 : CHIP_ERROR FabricTable::CommitPendingFabricData()
1854 : {
1855 745 : VerifyOrReturnError((mStorage != nullptr) && (mOpCertStore != nullptr), CHIP_ERROR_INCORRECT_STATE);
1856 :
1857 745 : bool haveNewTrustedRoot = mStateFlags.Has(StateFlags::kIsTrustedRootPending);
1858 745 : bool isAdding = mStateFlags.Has(StateFlags::kIsAddPending);
1859 745 : bool isUpdating = mStateFlags.Has(StateFlags::kIsUpdatePending);
1860 745 : bool hasPending = mStateFlags.Has(StateFlags::kIsPendingFabricDataPresent);
1861 745 : bool onlyHaveNewTrustedRoot = hasPending && haveNewTrustedRoot && !(isAdding || isUpdating);
1862 745 : bool hasInvalidInternalState = hasPending && (!IsValidFabricIndex(mFabricIndexWithPendingState) || !(isAdding || isUpdating));
1863 :
1864 745 : FabricIndex fabricIndexBeingCommitted = mFabricIndexWithPendingState;
1865 :
1866 : // Proceed with Update/Add pre-flight checks
1867 745 : if (hasPending && !hasInvalidInternalState)
1868 : {
1869 744 : if ((isAdding && isUpdating) || (isAdding && !haveNewTrustedRoot))
1870 : {
1871 0 : ChipLogError(FabricProvisioning, "Found inconsistent interlocks during commit %u/%u/%u!",
1872 : static_cast<unsigned>(isAdding), static_cast<unsigned>(isUpdating),
1873 : static_cast<unsigned>(haveNewTrustedRoot));
1874 0 : hasInvalidInternalState = true;
1875 : }
1876 : }
1877 :
1878 : // Make sure we actually have a pending fabric
1879 745 : FabricInfo * pendingFabricEntry = GetMutableFabricByIndex(fabricIndexBeingCommitted);
1880 :
1881 745 : if (isUpdating && hasPending && !hasInvalidInternalState)
1882 : {
1883 5 : if (!mPendingFabric.IsInitialized() || (mPendingFabric.GetFabricIndex() != fabricIndexBeingCommitted) ||
1884 : (pendingFabricEntry == nullptr))
1885 : {
1886 0 : ChipLogError(FabricProvisioning, "Missing pending fabric on update during commit!");
1887 0 : hasInvalidInternalState = true;
1888 : }
1889 : }
1890 :
1891 745 : if (isAdding && hasPending && !hasInvalidInternalState)
1892 : {
1893 739 : bool opCertStoreHasRoot = mOpCertStore->HasCertificateForFabric(fabricIndexBeingCommitted, CertChainElement::kRcac);
1894 739 : if (!mStateFlags.Has(StateFlags::kIsTrustedRootPending) || !opCertStoreHasRoot)
1895 : {
1896 0 : ChipLogError(FabricProvisioning, "Missing trusted root for fabric add during commit!");
1897 0 : hasInvalidInternalState = true;
1898 : }
1899 : }
1900 :
1901 745 : if ((isAdding || isUpdating) && hasPending && !hasInvalidInternalState)
1902 : {
1903 744 : if (!HasOperationalKeyForFabric(fabricIndexBeingCommitted))
1904 : {
1905 0 : ChipLogError(FabricProvisioning, "Could not find an operational key during commit!");
1906 0 : hasInvalidInternalState = true;
1907 : }
1908 : }
1909 :
1910 : // If there was nothing pending, we are either in a completely OK state, or weird internally inconsistent
1911 : // state. In either case, let's clear all pending state anyway, in case it was partially stale!
1912 745 : if (!hasPending || onlyHaveNewTrustedRoot || hasInvalidInternalState)
1913 : {
1914 1 : CHIP_ERROR err = CHIP_NO_ERROR;
1915 :
1916 1 : if (onlyHaveNewTrustedRoot)
1917 : {
1918 1 : ChipLogError(FabricProvisioning,
1919 : "Failed to commit: tried to commit with only a new trusted root cert. No data committed.");
1920 1 : err = CHIP_ERROR_INCORRECT_STATE;
1921 : }
1922 0 : else if (hasInvalidInternalState)
1923 : {
1924 0 : ChipLogError(FabricProvisioning, "Failed to commit: internally inconsistent state!");
1925 0 : err = CHIP_ERROR_INTERNAL;
1926 : }
1927 : else
1928 : {
1929 : // There was nothing pending and no error...
1930 : }
1931 :
1932 : // Clear all pending state anyway, in case it was partially stale!
1933 : {
1934 1 : mStateFlags.ClearAll();
1935 1 : mFabricIndexWithPendingState = kUndefinedFabricIndex;
1936 1 : mPendingFabric.Reset();
1937 1 : mOpCertStore->RevertPendingOpCerts();
1938 1 : if (mOperationalKeystore != nullptr)
1939 : {
1940 1 : mOperationalKeystore->RevertPendingKeypair();
1941 : }
1942 : }
1943 :
1944 1 : return err;
1945 : }
1946 :
1947 : // ==== Start of actual commit transaction after pre-flight checks ====
1948 744 : CHIP_ERROR stickyError = StoreCommitMarker(CommitMarker{ fabricIndexBeingCommitted, isAdding });
1949 744 : bool failedCommitMarker = (stickyError != CHIP_NO_ERROR);
1950 744 : if (failedCommitMarker)
1951 : {
1952 0 : ChipLogError(FabricProvisioning, "Failed to store commit marker, may be inconsistent if reboot happens during fail-safe!");
1953 : }
1954 :
1955 : {
1956 : // This scope block is to illustrate the complete commit transaction
1957 : // state. We can see it contains a LARGE number of items...
1958 :
1959 : // Atomically assume data no longer pending, since we are committing it. Do so here
1960 : // so that FindFabricBy* will return real data and never pending.
1961 744 : mStateFlags.Clear(StateFlags::kIsPendingFabricDataPresent);
1962 :
1963 744 : if (isUpdating)
1964 : {
1965 : // This will get the non-pending fabric
1966 5 : FabricInfo * existingFabricToUpdate = GetMutableFabricByIndex(fabricIndexBeingCommitted);
1967 :
1968 : // Multiple interlocks validated the below, so it's fatal if we are somehow incoherent here
1969 5 : VerifyOrDie((existingFabricToUpdate != nullptr) && (existingFabricToUpdate != &mPendingFabric));
1970 :
1971 : // Commit the pending entry to local in-memory fabric metadata, which
1972 : // also moves operational keys if not backed by OperationalKeystore
1973 5 : *existingFabricToUpdate = std::move(mPendingFabric);
1974 : }
1975 :
1976 : // Store pending metadata first
1977 744 : FabricInfo * liveFabricEntry = GetMutableFabricByIndex(fabricIndexBeingCommitted);
1978 744 : VerifyOrDie(liveFabricEntry != nullptr);
1979 :
1980 744 : CHIP_ERROR metadataErr = StoreFabricMetadata(liveFabricEntry);
1981 744 : if (metadataErr != CHIP_NO_ERROR)
1982 : {
1983 0 : ChipLogError(FabricProvisioning, "Failed to commit pending fabric metadata: %" CHIP_ERROR_FORMAT, metadataErr.Format());
1984 : }
1985 744 : stickyError = (stickyError != CHIP_NO_ERROR) ? stickyError : metadataErr;
1986 :
1987 : // We can only manage commissionable pending fail-safe state if we have a keystore
1988 744 : CHIP_ERROR keyErr = CHIP_NO_ERROR;
1989 769 : if ((mOperationalKeystore != nullptr) && mOperationalKeystore->HasOpKeypairForFabric(fabricIndexBeingCommitted) &&
1990 25 : mOperationalKeystore->HasPendingOpKeypair())
1991 : {
1992 24 : keyErr = mOperationalKeystore->CommitOpKeypairForFabric(fabricIndexBeingCommitted);
1993 24 : if (keyErr != CHIP_NO_ERROR)
1994 : {
1995 0 : ChipLogError(FabricProvisioning, "Failed to commit pending operational keypair %" CHIP_ERROR_FORMAT,
1996 : keyErr.Format());
1997 0 : mOperationalKeystore->RevertPendingKeypair();
1998 : }
1999 : }
2000 744 : stickyError = (stickyError != CHIP_NO_ERROR) ? stickyError : keyErr;
2001 :
2002 : // For testing only, early return (NEVER OCCURS OTHERWISE) during the commit
2003 : // so that clean-ups using the commit marker can be tested.
2004 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
2005 : {
2006 744 : if (mStateFlags.Has(StateFlags::kAbortCommitForTest))
2007 : {
2008 : // Clear state so that shutdown doesn't attempt clean-up
2009 1 : mStateFlags.ClearAll();
2010 1 : mFabricIndexWithPendingState = kUndefinedFabricIndex;
2011 1 : mPendingFabric.Reset();
2012 :
2013 1 : ChipLogError(FabricProvisioning, "Aborting commit in middle of transaction for testing.");
2014 1 : return CHIP_ERROR_INTERNAL;
2015 : }
2016 : }
2017 : #endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
2018 :
2019 : // Commit operational certs
2020 743 : CHIP_ERROR opCertErr = mOpCertStore->CommitOpCertsForFabric(fabricIndexBeingCommitted);
2021 743 : if (opCertErr != CHIP_NO_ERROR)
2022 : {
2023 0 : ChipLogError(FabricProvisioning, "Failed to commit pending operational certificates %" CHIP_ERROR_FORMAT,
2024 : opCertErr.Format());
2025 0 : mOpCertStore->RevertPendingOpCerts();
2026 : }
2027 743 : stickyError = (stickyError != CHIP_NO_ERROR) ? stickyError : opCertErr;
2028 :
2029 : // Failure to commit Last Known Good Time is non-fatal. If Last Known
2030 : // Good Time is incorrect and this causes incoming certificates to
2031 : // appear invalid, the certificate validity policy will see this
2032 : // condition and can act appropriately.
2033 743 : CHIP_ERROR lkgtErr = mLastKnownGoodTime.CommitPendingLastKnownGoodChipEpochTime();
2034 743 : if (lkgtErr != CHIP_NO_ERROR)
2035 : {
2036 : // Log but this is not sticky...
2037 0 : ChipLogError(FabricProvisioning, "Failed to commit Last Known Good Time: %" CHIP_ERROR_FORMAT, lkgtErr.Format());
2038 : }
2039 :
2040 : // If an Add occurred, let's update the fabric index
2041 743 : CHIP_ERROR fabricIndexErr = CHIP_NO_ERROR;
2042 743 : if (mStateFlags.Has(StateFlags::kIsAddPending))
2043 : {
2044 738 : UpdateNextAvailableFabricIndex();
2045 738 : fabricIndexErr = StoreFabricIndexInfo();
2046 738 : if (fabricIndexErr != CHIP_NO_ERROR)
2047 : {
2048 0 : ChipLogError(FabricProvisioning, "Failed to commit pending fabric indices: %" CHIP_ERROR_FORMAT,
2049 : fabricIndexErr.Format());
2050 : }
2051 : }
2052 743 : stickyError = (stickyError != CHIP_NO_ERROR) ? stickyError : fabricIndexErr;
2053 : }
2054 :
2055 : // Commit must have same side-effect as reverting all pending data
2056 743 : mStateFlags.ClearAll();
2057 743 : mFabricIndexWithPendingState = kUndefinedFabricIndex;
2058 743 : mPendingFabric.Reset();
2059 :
2060 743 : if (stickyError != CHIP_NO_ERROR)
2061 : {
2062 : // Blow-away everything if we got past any storage, even on Update: system state is broken
2063 : // TODO: Develop a way to properly revert in the future, but this is very difficult
2064 0 : Delete(fabricIndexBeingCommitted);
2065 :
2066 0 : RevertPendingFabricData();
2067 : }
2068 : else
2069 : {
2070 743 : NotifyFabricCommitted(fabricIndexBeingCommitted);
2071 : }
2072 :
2073 : // Clear commit marker no matter what: if we got here, there was no reboot and previous clean-ups
2074 : // did their job.
2075 743 : ClearCommitMarker();
2076 :
2077 743 : return stickyError;
2078 : }
2079 :
2080 461 : void FabricTable::RevertPendingFabricData()
2081 : {
2082 : MATTER_TRACE_SCOPE("RevertPendingFabricData", "Fabric");
2083 : // Will clear pending UpdateNoc/AddNOC
2084 461 : RevertPendingOpCertsExceptRoot();
2085 :
2086 461 : if (mOperationalKeystore != nullptr)
2087 : {
2088 458 : mOperationalKeystore->RevertPendingKeypair();
2089 : }
2090 :
2091 : // Clear everything else
2092 461 : if (mOpCertStore != nullptr)
2093 : {
2094 460 : mOpCertStore->RevertPendingOpCerts();
2095 : }
2096 :
2097 461 : mLastKnownGoodTime.RevertPendingLastKnownGoodChipEpochTime();
2098 :
2099 461 : mStateFlags.ClearAll();
2100 461 : mFabricIndexWithPendingState = kUndefinedFabricIndex;
2101 461 : }
2102 :
2103 465 : void FabricTable::RevertPendingOpCertsExceptRoot()
2104 : {
2105 : MATTER_TRACE_SCOPE("RevertPendingOpCertsExceptRoot", "Fabric");
2106 465 : mPendingFabric.Reset();
2107 :
2108 465 : if (mStateFlags.Has(StateFlags::kIsPendingFabricDataPresent))
2109 : {
2110 20 : ChipLogError(FabricProvisioning, "Reverting pending fabric data for fabric 0x%x",
2111 : static_cast<unsigned>(mFabricIndexWithPendingState));
2112 : }
2113 :
2114 465 : if (mOpCertStore != nullptr)
2115 : {
2116 464 : mOpCertStore->RevertPendingOpCertsExceptRoot();
2117 : }
2118 :
2119 465 : if (mStateFlags.Has(StateFlags::kIsAddPending))
2120 : {
2121 : // If we have a pending add, let's make sure to kill the pending fabric metadata and return it to viable state.
2122 9 : Delete(mFabricIndexWithPendingState);
2123 : }
2124 :
2125 465 : mStateFlags.Clear(StateFlags::kIsAddPending);
2126 465 : mStateFlags.Clear(StateFlags::kIsUpdatePending);
2127 465 : if (!mStateFlags.Has(StateFlags::kIsTrustedRootPending))
2128 : {
2129 448 : mFabricIndexWithPendingState = kUndefinedFabricIndex;
2130 : }
2131 465 : }
2132 :
2133 4 : CHIP_ERROR FabricTable::SetFabricLabel(FabricIndex fabricIndex, const CharSpan & fabricLabel)
2134 : {
2135 4 : VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
2136 4 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
2137 :
2138 4 : VerifyOrReturnError(fabricLabel.size() <= kFabricLabelMaxLengthInBytes, CHIP_ERROR_INVALID_ARGUMENT);
2139 :
2140 3 : FabricInfo * fabricInfo = GetMutableFabricByIndex(fabricIndex);
2141 3 : bool fabricIsInitialized = (fabricInfo != nullptr) && fabricInfo->IsInitialized();
2142 3 : VerifyOrReturnError(fabricIsInitialized, CHIP_ERROR_INVALID_FABRIC_INDEX);
2143 :
2144 : // Update fabric table current in-memory entry, whether pending or not
2145 3 : ReturnErrorOnFailure(fabricInfo->SetFabricLabel(fabricLabel));
2146 :
2147 3 : if (!mStateFlags.HasAny(StateFlags::kIsAddPending, StateFlags::kIsUpdatePending) && (fabricInfo != &mPendingFabric))
2148 : {
2149 : // Nothing is pending, we have to store immediately.
2150 2 : ReturnErrorOnFailure(StoreFabricMetadata(fabricInfo));
2151 : }
2152 :
2153 3 : return CHIP_NO_ERROR;
2154 : }
2155 :
2156 6 : CHIP_ERROR FabricTable::GetFabricLabel(FabricIndex fabricIndex, CharSpan & outFabricLabel)
2157 : {
2158 6 : const FabricInfo * fabricInfo = FindFabricWithIndex(fabricIndex);
2159 6 : VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX);
2160 :
2161 6 : outFabricLabel = fabricInfo->GetFabricLabel();
2162 6 : return CHIP_NO_ERROR;
2163 : }
2164 :
2165 13 : CHIP_ERROR FabricTable::PeekFabricIndexForNextAddition(FabricIndex & outIndex)
2166 : {
2167 13 : EnsureNextAvailableFabricIndexUpdated();
2168 13 : if (!mNextAvailableFabricIndex.HasValue())
2169 : {
2170 0 : return CHIP_ERROR_NO_MEMORY;
2171 : }
2172 :
2173 13 : FabricIndex index = mNextAvailableFabricIndex.Value();
2174 13 : VerifyOrReturnError(IsValidFabricIndex(index), CHIP_ERROR_INVALID_FABRIC_INDEX);
2175 :
2176 13 : outIndex = index;
2177 13 : return CHIP_NO_ERROR;
2178 : }
2179 :
2180 5 : CHIP_ERROR FabricTable::SetFabricIndexForNextAddition(FabricIndex fabricIndex)
2181 : {
2182 5 : VerifyOrReturnError(!mStateFlags.Has(StateFlags::kIsPendingFabricDataPresent), CHIP_ERROR_INCORRECT_STATE);
2183 4 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
2184 :
2185 3 : const FabricInfo * fabricInfo = FindFabricWithIndex(fabricIndex);
2186 3 : VerifyOrReturnError(fabricInfo == nullptr, CHIP_ERROR_FABRIC_EXISTS);
2187 :
2188 2 : mNextAvailableFabricIndex.SetValue(fabricIndex);
2189 2 : return CHIP_NO_ERROR;
2190 : }
2191 :
2192 8 : CHIP_ERROR FabricTable::SetShouldAdvertiseIdentity(FabricIndex fabricIndex, AdvertiseIdentity advertiseIdentity)
2193 : {
2194 8 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
2195 :
2196 6 : FabricInfo * fabricInfo = GetMutableFabricByIndex(fabricIndex);
2197 6 : bool fabricIsInitialized = (fabricInfo != nullptr) && fabricInfo->IsInitialized();
2198 6 : VerifyOrReturnError(fabricIsInitialized, CHIP_ERROR_INVALID_FABRIC_INDEX);
2199 :
2200 4 : fabricInfo->SetShouldAdvertiseIdentity(advertiseIdentity == AdvertiseIdentity::Yes);
2201 :
2202 4 : return CHIP_NO_ERROR;
2203 : }
2204 :
2205 5 : CHIP_ERROR FabricTable::FetchVIDVerificationStatement(FabricIndex fabricIndex, MutableByteSpan & outVIDVerificationStatement) const
2206 : {
2207 5 : VerifyOrReturnError(mOpCertStore != nullptr, CHIP_ERROR_INCORRECT_STATE);
2208 5 : return mOpCertStore->GetVidVerificationElement(
2209 5 : fabricIndex, OperationalCertificateStore::VidVerificationElement::kVidVerificationStatement, outVIDVerificationStatement);
2210 : }
2211 :
2212 0 : CHIP_ERROR FabricTable::FetchVVSC(FabricIndex fabricIndex, MutableByteSpan & outVVSC) const
2213 : {
2214 0 : VerifyOrReturnError(mOpCertStore != nullptr, CHIP_ERROR_INCORRECT_STATE);
2215 :
2216 0 : return mOpCertStore->GetVidVerificationElement(fabricIndex, OperationalCertificateStore::VidVerificationElement::kVvsc,
2217 0 : outVVSC);
2218 : return CHIP_NO_ERROR;
2219 : }
2220 :
2221 6 : CHIP_ERROR FabricTable::SignVIDVerificationRequest(FabricIndex fabricIndex, ByteSpan clientChallenge, ByteSpan attestationChallenge,
2222 : SignVIDVerificationResponseData & outResponse)
2223 : {
2224 6 : FabricInfo * fabricInfo = GetMutableFabricByIndex(fabricIndex);
2225 6 : VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
2226 :
2227 5 : P256PublicKey rootPublicKey;
2228 5 : ReturnErrorOnFailure(fabricInfo->FetchRootPubkey(rootPublicKey));
2229 :
2230 : // Step 1: Generate FabricBindingMessage for given fabric.
2231 : uint8_t fabricBindingMessageBuffer[kVendorFabricBindingMessageV1Size];
2232 5 : MutableByteSpan fabricBindingMessageSpan{ fabricBindingMessageBuffer };
2233 :
2234 5 : ReturnErrorOnFailure(
2235 : GenerateVendorFabricBindingMessage(FabricBindingVersion::kVersion1, rootPublicKey, fabricInfo->GetFabricId(),
2236 : static_cast<uint16_t>(fabricInfo->GetVendorId()), fabricBindingMessageSpan));
2237 5 : VerifyOrReturnError(fabricBindingMessageSpan.size() == kVendorFabricBindingMessageV1Size, CHIP_ERROR_INTERNAL);
2238 :
2239 : // Step 2: Recover VIDVerificationStatement, if any.
2240 : uint8_t vidVerificationStatementBuffer[kVendorIdVerificationStatementV1Size];
2241 5 : MutableByteSpan vidVerificationStatementSpan{ vidVerificationStatementBuffer };
2242 :
2243 5 : ReturnErrorOnFailure(FetchVIDVerificationStatement(fabricIndex, vidVerificationStatementSpan));
2244 :
2245 : // Step 3: Generate VidVerificationToBeSigned
2246 : uint8_t vidVerificationTbsBuffer[kVendorIdVerificationTbsV1MaxSize];
2247 5 : MutableByteSpan vidVerificationTbsSpan{ vidVerificationTbsBuffer };
2248 :
2249 5 : P256ECDSASignature signature;
2250 5 : auto signatureBuffer = Platform::ScopedMemoryBufferWithSize<uint8_t>();
2251 5 : VerifyOrReturnError(signatureBuffer.Calloc(signature.Capacity()), CHIP_ERROR_BUFFER_TOO_SMALL);
2252 :
2253 5 : ReturnErrorOnFailure(GenerateVendorIdVerificationToBeSigned(fabricIndex, clientChallenge, attestationChallenge,
2254 : fabricBindingMessageSpan, vidVerificationStatementSpan,
2255 : vidVerificationTbsSpan));
2256 :
2257 : // Step 4: Sign the statement with the operational key.
2258 2 : ReturnErrorOnFailure(SignWithOpKeypair(fabricIndex, vidVerificationTbsSpan, signature));
2259 2 : memcpy(signatureBuffer.Get(), signature.Bytes(), signature.Capacity());
2260 :
2261 2 : outResponse.fabricIndex = fabricIndex;
2262 2 : outResponse.fabricBindingVersion = fabricBindingMessageSpan[0];
2263 2 : outResponse.signature = std::move(signatureBuffer);
2264 :
2265 2 : return CHIP_NO_ERROR;
2266 5 : }
2267 :
2268 13 : CHIP_ERROR FabricTable::SetVIDVerificationStatementElements(FabricIndex fabricIndex, Optional<uint16_t> vendorId,
2269 : Optional<ByteSpan> VIDVerificationStatement, Optional<ByteSpan> VVSC,
2270 : bool & outFabricTableWasChanged)
2271 : {
2272 13 : VerifyOrReturnError(mOpCertStore != nullptr, CHIP_ERROR_INTERNAL);
2273 13 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_ARGUMENT);
2274 :
2275 13 : FabricInfo * fabricInfo = GetMutableFabricByIndex(fabricIndex);
2276 13 : VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
2277 :
2278 : // PendingNew fabric means AddNOC had been called. ShadowPending fabric is the overlay version
2279 : // that is used when UpdateNOC had been called. This is to detect either condition of not
2280 : // fully committed fabric.
2281 27 : bool isTargetFabricPending = (GetPendingNewFabricIndex() == fabricIndex) ||
2282 14 : ((GetShadowPendingFabricEntry() != nullptr) && (GetShadowPendingFabricEntry()->GetFabricIndex() == fabricIndex));
2283 :
2284 13 : outFabricTableWasChanged = false;
2285 :
2286 : // Start with VVSC first as it's the most likely to fail.
2287 13 : if (VVSC.HasValue())
2288 : {
2289 9 : if (mOpCertStore->HasCertificateForFabric(fabricIndex, OperationalCertificateStore::CertChainElement::kIcac))
2290 : {
2291 1 : ChipLogError(FabricProvisioning,
2292 : "Received SetVIDVerificationStatement storage request with VVSC when ICAC already present. ICAC must be "
2293 : "removed first.");
2294 1 : return CHIP_ERROR_INCORRECT_STATE;
2295 : }
2296 :
2297 8 : ReturnErrorOnFailure(mOpCertStore->UpdateVidVerificationSignerCertForFabric(fabricIndex, VVSC.Value()));
2298 : }
2299 :
2300 12 : if (VIDVerificationStatement.HasValue())
2301 : {
2302 6 : bool wasVvsEqual = false;
2303 : {
2304 : // This is in a scope to save stack space from getting too deep.
2305 : uint8_t vidVerificationStatementBuffer[Crypto::kVendorIdVerificationStatementV1Size];
2306 6 : MutableByteSpan vidVerificationStatementSpan{ vidVerificationStatementBuffer };
2307 6 : ReturnErrorOnFailure(mOpCertStore->GetVidVerificationElement(
2308 : fabricIndex, OperationalCertificateStore::VidVerificationElement::kVidVerificationStatement,
2309 : vidVerificationStatementSpan));
2310 6 : wasVvsEqual = vidVerificationStatementSpan.data_equal(VIDVerificationStatement.Value());
2311 : }
2312 :
2313 6 : if (!wasVvsEqual)
2314 : {
2315 5 : ReturnErrorOnFailure(
2316 : mOpCertStore->UpdateVidVerificationStatementForFabric(fabricIndex, VIDVerificationStatement.Value()));
2317 5 : outFabricTableWasChanged = true;
2318 : }
2319 : }
2320 :
2321 12 : if (vendorId.HasValue())
2322 : {
2323 1 : if (static_cast<uint16_t>(fabricInfo->GetVendorId()) != vendorId.Value())
2324 : {
2325 1 : fabricInfo->SetVendorId(static_cast<VendorId>(vendorId.Value()));
2326 : // Immediately commit Vendor ID if not a pending fabric. Otherwise the commit occurs on CommissioningComplete.
2327 1 : if (!isTargetFabricPending)
2328 : {
2329 0 : ReturnErrorOnFailure(StoreFabricMetadata(fabricInfo));
2330 0 : outFabricTableWasChanged = true;
2331 : }
2332 : }
2333 : }
2334 :
2335 12 : return CHIP_NO_ERROR;
2336 : }
2337 :
2338 : } // namespace chip
|