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