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 9 : FabricTestFixture(PersistentStorageDelegate * storage) :
40 9 : mStorage(storage), mRootCertSpan(mRootCertDER, sizeof(mRootCertDER)), mNocSpan(mNocDER, sizeof(mNocDER))
41 9 : {}
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 9 : CHIP_ERROR SetUpTestFabric(FabricIndex & fabricIndexOut)
51 : {
52 9 : ReturnErrorOnFailure(mOpCertStore.Init(mStorage));
53 9 : initParams.opCertStore = &mOpCertStore;
54 9 : initParams.storage = mStorage;
55 9 : initParams.operationalKeystore = nullptr; // Use default
56 9 : ReturnErrorOnFailure(mfabricTable.Init(initParams));
57 :
58 9 : ReturnErrorOnFailure(mfabricTable.SetFabricIndexForNextAddition(fabricIndexOut));
59 9 : ReturnErrorOnFailure(SetUpCertificates());
60 :
61 36 : CHIP_ERROR err = mfabricTable.AddNewFabricForTest(
62 27 : mRootCertSpan, ByteSpan(), mNocSpan, ByteSpan(mSerializedOpKey.Bytes(), mSerializedOpKey.Length()), &fabricIndexOut);
63 9 : ReturnErrorOnFailure(err);
64 :
65 9 : 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 9 : CHIP_ERROR TearDownTestFabric(FabricIndex & fabricIndex)
75 : {
76 9 : 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 9 : CHIP_ERROR err = CHIP_NO_ERROR;
83 :
84 18 : if ((err = mOpCertStore.RemoveOpCertsForFabric(fabricIndex)) != CHIP_NO_ERROR)
85 : {
86 0 : ChipLogError(Test, "TearDownFabric: RemoveOpCertsForFabric failed: %" CHIP_ERROR_FORMAT, err.Format());
87 : }
88 18 : 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 9 : mfabricTable.Shutdown();
94 9 : mOpCertStore.Finish();
95 :
96 9 : return CHIP_NO_ERROR;
97 : }
98 :
99 9 : FabricTable & GetFabricTable() { return mfabricTable; }
100 :
101 : private:
102 9 : CHIP_ERROR SetUpCertificates()
103 : {
104 9 : Crypto::P256Keypair rootCACredentials;
105 9 : ReturnErrorOnFailure(rootCACredentials.Initialize(Crypto::ECPKeyTarget::ECDSA));
106 :
107 9 : Crypto::P256Keypair deviceOpKey;
108 9 : ReturnErrorOnFailure(deviceOpKey.Initialize(Crypto::ECPKeyTarget::ECDSA));
109 :
110 9 : ReturnErrorOnFailure(deviceOpKey.Serialize(mSerializedOpKey));
111 :
112 : // Create temporary X.509 (DER) buffers
113 : uint8_t rootCertDER_temp[chip::Credentials::kMaxDERCertLength];
114 9 : MutableByteSpan rootCertDERSpan(rootCertDER_temp);
115 :
116 : uint8_t nocDER_temp[chip::Credentials::kMaxDERCertLength];
117 9 : MutableByteSpan nocDERSpan(nocDER_temp);
118 :
119 9 : mRootCertSpan = MutableByteSpan(mRootCertDER);
120 9 : mNocSpan = MutableByteSpan(mNocDER);
121 :
122 9 : chip::Credentials::X509CertRequestParams rootRequestParams;
123 9 : rootRequestParams.SerialNumber = kRootCertSerial;
124 9 : rootRequestParams.ValidityStart = kCertValidityStart;
125 9 : rootRequestParams.ValidityEnd = kRootCertValidityDurationSeconds;
126 :
127 9 : const char * rootName = "My Test Root CA";
128 9 : ReturnErrorOnFailure(rootRequestParams.IssuerDN.AddAttribute(
129 : chip::ASN1::kOID_AttributeType_CommonName, CharSpan(rootName, strlen(rootName)), true /* isPrintableString */
130 : ));
131 9 : ReturnErrorOnFailure(rootRequestParams.IssuerDN.AddAttribute(chip::ASN1::kOID_AttributeType_MatterRCACId, kTestRcacId));
132 9 : rootRequestParams.SubjectDN = rootRequestParams.IssuerDN;
133 :
134 9 : ReturnErrorOnFailure(chip::Credentials::NewRootX509Cert(rootRequestParams, rootCACredentials, rootCertDERSpan));
135 :
136 : // Convert X.509 DER to Matter TLV
137 9 : ReturnErrorOnFailure(chip::Credentials::ConvertX509CertToChipCert(rootCertDERSpan, mRootCertSpan));
138 :
139 9 : chip::Credentials::X509CertRequestParams nocRequestParams;
140 9 : nocRequestParams.SerialNumber = kNocSerial;
141 9 : nocRequestParams.ValidityStart = kCertValidityStart;
142 9 : nocRequestParams.ValidityEnd = kNocCertValidityDurationSeconds;
143 9 : nocRequestParams.IssuerDN = rootRequestParams.SubjectDN;
144 9 : ReturnErrorOnFailure(nocRequestParams.SubjectDN.AddAttribute(chip::ASN1::kOID_AttributeType_MatterFabricId, kTestFabricId));
145 9 : ReturnErrorOnFailure(nocRequestParams.SubjectDN.AddAttribute(chip::ASN1::kOID_AttributeType_MatterNodeId, kTestNodeId));
146 9 : ReturnErrorOnFailure(
147 : chip::Credentials::NewNodeOperationalX509Cert(nocRequestParams, deviceOpKey.Pubkey(), rootCACredentials, nocDERSpan));
148 :
149 9 : return chip::Credentials::ConvertX509CertToChipCert(nocDERSpan, mNocSpan);
150 9 : }
151 :
152 : PersistentStorageDelegate * mStorage;
153 :
154 : // Certificates and keys
155 : uint8_t mRootCertDER[chip::Credentials::kMaxDERCertLength];
156 : MutableByteSpan mRootCertSpan;
157 : uint8_t mNocDER[chip::Credentials::kMaxDERCertLength];
158 : MutableByteSpan mNocSpan;
159 : Crypto::P256SerializedKeypair mSerializedOpKey;
160 :
161 : // Fabric-related storage
162 : chip::Credentials::PersistentStorageOpCertStore mOpCertStore;
163 : FabricTable mfabricTable;
164 : FabricTable::InitParams initParams;
165 :
166 : // Test constants (copied from test fixture)
167 : static constexpr FabricId kTestFabricId = 0xDEADBEEF00000001;
168 : static constexpr NodeId kTestNodeId = 0xDEADBEEF00000002;
169 : static constexpr uint64_t kTestRcacId = 0x1111222233334444;
170 : static constexpr uint64_t kRootCertSerial = 1;
171 : static constexpr uint64_t kNocSerial = 2;
172 : static constexpr uint64_t kCertValidityStart = 0;
173 : static constexpr uint32_t kRootCertValidityDurationSeconds = 315360000; // 10 years
174 : static constexpr uint32_t kNocCertValidityDurationSeconds = 31536000; // 1 year
175 : };
176 :
177 : } // namespace Testing
178 : } // namespace chip
|