Line data Source code
1 : /*
2 : * Copyright (c) 2025 Project CHIP Authors
3 : *
4 : * Licensed under the Apache License, Version 2.0 (the "License");
5 : * you may not use this file except in compliance with the License.
6 : * You may obtain a copy of the License at
7 : *
8 : * http://www.apache.org/licenses/LICENSE-2.0
9 : *
10 : * Unless required by applicable law or agreed to in writing, software
11 : * distributed under the License is distributed on an "AS IS" BASIS,
12 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : * See the License for the specific language governing permissions and
14 : * limitations under the License.
15 : */
16 : #pragma once
17 : #include <credentials/FabricTable.h>
18 : #include <credentials/PersistentStorageOpCertStore.h>
19 : #include <crypto/CHIPCryptoPAL.h>
20 : #include <lib/core/CHIPError.h>
21 : #include <lib/core/CHIPPersistentStorageDelegate.h>
22 : #include <lib/support/logging/CHIPLogging.h>
23 : #include <memory>
24 : #include <optional>
25 : #include <vector>
26 :
27 : namespace chip {
28 : namespace Testing {
29 : /**
30 : * @brief Fixture class to manage the lifecycle of a Fabric for testing.
31 : *
32 : * This encapsulates the logic required to initialize a FabricTable,
33 : * create a mock Fabric, and manage related certificate storage.
34 : * It is designed to be used by ClusterTester or a test fixture but not mandatory.
35 : */
36 : class FabricTestFixture
37 : {
38 : public:
39 26 : FabricTestFixture(PersistentStorageDelegate * storage) :
40 26 : mStorage(storage), mRootCertSpan(mRootCertDER, sizeof(mRootCertDER)), mNocSpan(mNocDER, sizeof(mNocDER))
41 26 : {}
42 :
43 : /**
44 : * @brief Initializes the Fabric table and adds a new test fabric.
45 : *
46 : * @param fabricIndexOut Output parameter for the newly created FabricIndex.
47 : * @return CHIP_ERROR
48 : */
49 :
50 26 : CHIP_ERROR SetUpTestFabric(FabricIndex & fabricIndexOut)
51 : {
52 26 : ReturnErrorOnFailure(mOpCertStore.Init(mStorage));
53 26 : initParams.opCertStore = &mOpCertStore;
54 26 : initParams.storage = mStorage;
55 26 : initParams.operationalKeystore = nullptr; // Use default
56 26 : ReturnErrorOnFailure(mfabricTable.Init(initParams));
57 :
58 26 : ReturnErrorOnFailure(mfabricTable.SetFabricIndexForNextAddition(fabricIndexOut));
59 26 : ReturnErrorOnFailure(SetUpCertificates());
60 :
61 104 : CHIP_ERROR err = mfabricTable.AddNewFabricForTest(
62 78 : mRootCertSpan, ByteSpan(), mNocSpan, ByteSpan(mSerializedOpKey.Bytes(), mSerializedOpKey.Length()), &fabricIndexOut);
63 26 : ReturnErrorOnFailure(err);
64 :
65 26 : return mfabricTable.CommitPendingFabricData();
66 : }
67 :
68 : /**
69 : * @brief Tears down the created fabric and cleans up storage.
70 : *
71 : * @param fabricIndex The FabricIndex to tear down.
72 : * @return CHIP_ERROR
73 : */
74 26 : CHIP_ERROR TearDownTestFabric(FabricIndex & fabricIndex)
75 : {
76 26 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
77 :
78 : // Credentials::SetGroupDataProvider(nullptr); // Done in the test fixture's TearDown
79 : // GroupDataProviderImpl::RemoveFabric is usually done separately
80 : // However, we just need to ensure the fabric is deleted from FabricTable and storage is cleaned.
81 :
82 26 : CHIP_ERROR err = CHIP_NO_ERROR;
83 :
84 52 : if ((err = mOpCertStore.RemoveOpCertsForFabric(fabricIndex)) != CHIP_NO_ERROR)
85 : {
86 0 : ChipLogError(Test, "TearDownFabric: RemoveOpCertsForFabric failed: %" CHIP_ERROR_FORMAT, err.Format());
87 : }
88 52 : if ((err = mfabricTable.Delete(fabricIndex)) != CHIP_NO_ERROR)
89 : {
90 0 : ChipLogError(Test, "TearDownFabric: Delete fabric failed: %" CHIP_ERROR_FORMAT, err.Format());
91 : }
92 :
93 26 : mfabricTable.Shutdown();
94 26 : mOpCertStore.Finish();
95 :
96 26 : return CHIP_NO_ERROR;
97 : }
98 :
99 45 : FabricTable & GetFabricTable() { return mfabricTable; }
100 :
101 : /**
102 : * @brief Adds an additional test fabric to an already-initialized FabricTable.
103 : * Must be called after SetUpTestFabric().
104 : *
105 : * @param fabricIndexOut Desired FabricIndex for the new fabric; updated on success.
106 : * @return CHIP_ERROR
107 : */
108 2 : CHIP_ERROR AddAdditionalTestFabric(FabricIndex & fabricIndexOut)
109 : {
110 2 : ReturnErrorOnFailure(mfabricTable.SetFabricIndexForNextAddition(fabricIndexOut));
111 2 : ReturnErrorOnFailure(SetUpCertificates());
112 8 : CHIP_ERROR err = mfabricTable.AddNewFabricForTestIgnoringCollisions(
113 6 : mRootCertSpan, ByteSpan(), mNocSpan, ByteSpan(mSerializedOpKey.Bytes(), mSerializedOpKey.Length()), &fabricIndexOut);
114 2 : ReturnErrorOnFailure(err);
115 2 : return mfabricTable.CommitPendingFabricData();
116 : }
117 :
118 : private:
119 28 : CHIP_ERROR SetUpCertificates()
120 : {
121 28 : Crypto::P256Keypair rootCACredentials;
122 28 : ReturnErrorOnFailure(rootCACredentials.Initialize(Crypto::ECPKeyTarget::ECDSA));
123 :
124 28 : Crypto::P256Keypair deviceOpKey;
125 28 : ReturnErrorOnFailure(deviceOpKey.Initialize(Crypto::ECPKeyTarget::ECDSA));
126 :
127 28 : ReturnErrorOnFailure(deviceOpKey.Serialize(mSerializedOpKey));
128 :
129 : // Create temporary X.509 (DER) buffers
130 : uint8_t rootCertDER_temp[chip::Credentials::kMaxDERCertLength];
131 28 : MutableByteSpan rootCertDERSpan(rootCertDER_temp);
132 :
133 : uint8_t nocDER_temp[chip::Credentials::kMaxDERCertLength];
134 28 : MutableByteSpan nocDERSpan(nocDER_temp);
135 :
136 28 : mRootCertSpan = MutableByteSpan(mRootCertDER);
137 28 : mNocSpan = MutableByteSpan(mNocDER);
138 :
139 28 : chip::Credentials::X509CertRequestParams rootRequestParams;
140 28 : rootRequestParams.SerialNumber = kRootCertSerial;
141 28 : rootRequestParams.ValidityStart = kCertValidityStart;
142 28 : rootRequestParams.ValidityEnd = kRootCertValidityDurationSeconds;
143 :
144 28 : const char * rootName = "My Test Root CA";
145 28 : ReturnErrorOnFailure(rootRequestParams.IssuerDN.AddAttribute(
146 : chip::ASN1::kOID_AttributeType_CommonName, CharSpan(rootName, strlen(rootName)), true /* isPrintableString */
147 : ));
148 28 : ReturnErrorOnFailure(rootRequestParams.IssuerDN.AddAttribute(chip::ASN1::kOID_AttributeType_MatterRCACId, kTestRcacId));
149 28 : rootRequestParams.SubjectDN = rootRequestParams.IssuerDN;
150 :
151 28 : ReturnErrorOnFailure(chip::Credentials::NewRootX509Cert(rootRequestParams, rootCACredentials, rootCertDERSpan));
152 :
153 : // Convert X.509 DER to Matter TLV
154 28 : ReturnErrorOnFailure(chip::Credentials::ConvertX509CertToChipCert(rootCertDERSpan, mRootCertSpan));
155 :
156 28 : chip::Credentials::X509CertRequestParams nocRequestParams;
157 28 : nocRequestParams.SerialNumber = kNocSerial;
158 28 : nocRequestParams.ValidityStart = kCertValidityStart;
159 28 : nocRequestParams.ValidityEnd = kNocCertValidityDurationSeconds;
160 28 : nocRequestParams.IssuerDN = rootRequestParams.SubjectDN;
161 28 : ReturnErrorOnFailure(nocRequestParams.SubjectDN.AddAttribute(chip::ASN1::kOID_AttributeType_MatterFabricId, kTestFabricId));
162 28 : ReturnErrorOnFailure(nocRequestParams.SubjectDN.AddAttribute(chip::ASN1::kOID_AttributeType_MatterNodeId, kTestNodeId));
163 28 : ReturnErrorOnFailure(
164 : chip::Credentials::NewNodeOperationalX509Cert(nocRequestParams, deviceOpKey.Pubkey(), rootCACredentials, nocDERSpan));
165 :
166 28 : return chip::Credentials::ConvertX509CertToChipCert(nocDERSpan, mNocSpan);
167 28 : }
168 :
169 : PersistentStorageDelegate * mStorage;
170 :
171 : // Certificates and keys
172 : uint8_t mRootCertDER[chip::Credentials::kMaxDERCertLength];
173 : MutableByteSpan mRootCertSpan;
174 : uint8_t mNocDER[chip::Credentials::kMaxDERCertLength];
175 : MutableByteSpan mNocSpan;
176 : Crypto::P256SerializedKeypair mSerializedOpKey;
177 :
178 : // Fabric-related storage
179 : chip::Credentials::PersistentStorageOpCertStore mOpCertStore;
180 : FabricTable mfabricTable;
181 : FabricTable::InitParams initParams;
182 :
183 : // Test constants (copied from test fixture)
184 : static constexpr FabricId kTestFabricId = 0xDEADBEEF00000001;
185 : static constexpr NodeId kTestNodeId = 0xDEADBEEF00000002;
186 : static constexpr uint64_t kTestRcacId = 0x1111222233334444;
187 : static constexpr uint64_t kRootCertSerial = 1;
188 : static constexpr uint64_t kNocSerial = 2;
189 : static constexpr uint64_t kCertValidityStart = 0;
190 : static constexpr uint32_t kRootCertValidityDurationSeconds = 315360000; // 10 years
191 : static constexpr uint32_t kNocCertValidityDurationSeconds = 31536000; // 1 year
192 : };
193 :
194 : } // namespace Testing
195 : } // namespace chip
|