Line data Source code
1 : /*
2 : * Copyright (c) 2022 Project CHIP Authors
3 : * All rights reserved.
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 : #pragma once
19 :
20 : #include <credentials/CHIPCert.h>
21 : #include <crypto/CHIPCryptoPAL.h>
22 : #include <lib/core/CHIPError.h>
23 : #include <lib/core/DataModelTypes.h>
24 : #include <lib/core/Optional.h>
25 : #include <lib/support/CHIPMem.h>
26 : #include <lib/support/CodeUtils.h>
27 :
28 : namespace chip {
29 : namespace Credentials {
30 :
31 : class TestOnlyLocalCertificateAuthority
32 : {
33 : public:
34 21 : TestOnlyLocalCertificateAuthority()
35 21 : {
36 : // Initializing the default start validity to start of 2021.
37 : chip::ASN1::ASN1UniversalTime effectiveTime;
38 21 : CHIP_ZERO_AT(effectiveTime);
39 21 : effectiveTime.Year = 2021;
40 21 : effectiveTime.Month = 1;
41 21 : effectiveTime.Day = 1;
42 21 : VerifyOrDie(ASN1ToChipEpochTime(effectiveTime, mNow) == CHIP_NO_ERROR);
43 21 : }
44 :
45 21 : ~TestOnlyLocalCertificateAuthority()
46 : {
47 21 : mRootKeypair.reset();
48 21 : mLastRcac.Free();
49 21 : mLastNoc.Free();
50 21 : mLastIcac.Free();
51 21 : }
52 :
53 : // Non-copyable
54 : TestOnlyLocalCertificateAuthority(TestOnlyLocalCertificateAuthority const &) = delete;
55 : void operator=(TestOnlyLocalCertificateAuthority const &) = delete;
56 :
57 18 : TestOnlyLocalCertificateAuthority & Init()
58 : {
59 18 : Crypto::P256SerializedKeypair emptyKeypair;
60 18 : return Init(emptyKeypair);
61 18 : }
62 :
63 21 : TestOnlyLocalCertificateAuthority & Init(Crypto::P256SerializedKeypair & rootKeyPair)
64 : {
65 21 : SuccessOrExit(mCurrentStatus);
66 :
67 21 : mRootKeypair = Platform::MakeUnique<Crypto::P256Keypair>();
68 21 : VerifyOrExit(mRootKeypair != nullptr, mCurrentStatus = CHIP_ERROR_NO_MEMORY);
69 :
70 21 : if (rootKeyPair.Length() != 0)
71 : {
72 3 : mCurrentStatus = mRootKeypair->Deserialize(rootKeyPair);
73 3 : SuccessOrExit(mCurrentStatus);
74 : }
75 : else
76 : {
77 18 : mRootKeypair->Initialize(Crypto::ECPKeyTarget::ECDSA);
78 : }
79 21 : mCurrentStatus = GenerateRootCert(*mRootKeypair.get());
80 21 : SuccessOrExit(mCurrentStatus);
81 21 : exit:
82 21 : return *this;
83 : }
84 :
85 35 : TestOnlyLocalCertificateAuthority & SetIncludeIcac(bool includeIcac)
86 : {
87 35 : mIncludeIcac = includeIcac;
88 35 : mCurrentStatus = (mCurrentStatus != CHIP_NO_ERROR) ? mCurrentStatus : CHIP_NO_ERROR;
89 35 : return *this;
90 : }
91 :
92 : void ResetIssuer()
93 : {
94 : mCurrentStatus = CHIP_NO_ERROR;
95 : mIncludeIcac = false;
96 : mLastNoc.Free();
97 : mLastIcac.Free();
98 : }
99 :
100 35 : CHIP_ERROR GetStatus() { return mCurrentStatus; }
101 21 : bool IsSuccess() { return mCurrentStatus == CHIP_NO_ERROR; }
102 :
103 35 : ByteSpan GetNoc() const { return ByteSpan{ mLastNoc.Get(), mLastNoc.AllocatedSize() }; }
104 22 : ByteSpan GetIcac() const { return mIncludeIcac ? ByteSpan{ mLastIcac.Get(), mLastIcac.AllocatedSize() } : ByteSpan{}; }
105 28 : ByteSpan GetRcac() const { return ByteSpan{ mLastRcac.Get(), mLastRcac.AllocatedSize() }; }
106 :
107 35 : TestOnlyLocalCertificateAuthority & GenerateNocChain(FabricId fabricId, NodeId nodeId,
108 : const Crypto::P256PublicKey & nocPublicKey)
109 : {
110 35 : if (mCurrentStatus != CHIP_NO_ERROR)
111 : {
112 0 : return *this;
113 : }
114 :
115 35 : if (mRootKeypair.get() == nullptr)
116 : {
117 0 : mCurrentStatus = CHIP_ERROR_NO_SHARED_TRUSTED_ROOT;
118 0 : return *this;
119 : }
120 :
121 35 : mLastIcac.Free();
122 35 : mLastNoc.Free();
123 35 : mCurrentStatus = GenerateCertChainInternal(fabricId, nodeId, nocPublicKey);
124 35 : return *this;
125 : }
126 :
127 34 : TestOnlyLocalCertificateAuthority & GenerateNocChain(FabricId fabricId, NodeId nodeId, const ByteSpan & csr)
128 : {
129 34 : if (mCurrentStatus != CHIP_NO_ERROR)
130 : {
131 0 : return *this;
132 : }
133 :
134 34 : Crypto::P256PublicKey nocPublicKey;
135 34 : mCurrentStatus = Crypto::VerifyCertificateSigningRequest(csr.data(), csr.size(), nocPublicKey);
136 34 : if (mCurrentStatus != CHIP_NO_ERROR)
137 : {
138 0 : return *this;
139 : }
140 :
141 34 : return GenerateNocChain(fabricId, nodeId, nocPublicKey);
142 34 : }
143 :
144 : protected:
145 35 : CHIP_ERROR GenerateCertChainInternal(FabricId fabricId, NodeId nodeId, const Crypto::P256PublicKey & nocPublicKey)
146 : {
147 35 : ChipDN rcac_dn;
148 35 : ChipDN icac_dn;
149 35 : ChipDN noc_dn;
150 :
151 : // Get subject DN of RCAC as our issuer field for ICAC and/or NOC depending on if ICAC is present
152 35 : ReturnErrorOnFailure(ExtractSubjectDNFromChipCert(ByteSpan{ mLastRcac.Get(), mLastRcac.AllocatedSize() }, rcac_dn));
153 :
154 35 : Crypto::P256Keypair icacKeypair;
155 35 : ReturnErrorOnFailure(icacKeypair.Initialize(Crypto::ECPKeyTarget::ECDSA)); // Maybe we won't use it, but it's OK
156 :
157 35 : Crypto::P256Keypair * nocIssuerKeypair = mRootKeypair.get();
158 35 : ChipDN * issuer_dn = &rcac_dn;
159 :
160 : // Generate ICAC if needed
161 35 : if (mIncludeIcac)
162 : {
163 22 : Platform::ScopedMemoryBufferWithSize<uint8_t> icacDerBuf;
164 22 : VerifyOrReturnError(icacDerBuf.Alloc(Credentials::kMaxDERCertLength), CHIP_ERROR_NO_MEMORY);
165 22 : Platform::ScopedMemoryBufferWithSize<uint8_t> icacChipBuf;
166 22 : VerifyOrReturnError(icacChipBuf.Alloc(Credentials::kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY);
167 :
168 22 : ReturnErrorOnFailure(icac_dn.AddAttribute_MatterFabricId(fabricId));
169 22 : ReturnErrorOnFailure(icac_dn.AddAttribute_MatterICACId(1234));
170 :
171 22 : X509CertRequestParams icac_request = { 0, mNow, mNow + mValidity, icac_dn, rcac_dn };
172 :
173 22 : MutableByteSpan icacDerSpan{ icacDerBuf.Get(), icacDerBuf.AllocatedSize() };
174 22 : ReturnErrorOnFailure(Credentials::NewICAX509Cert(icac_request, icacKeypair.Pubkey(), *mRootKeypair.get(), icacDerSpan));
175 :
176 22 : MutableByteSpan icacChipSpan{ icacChipBuf.Get(), icacChipBuf.AllocatedSize() };
177 22 : ReturnErrorOnFailure(Credentials::ConvertX509CertToChipCert(icacDerSpan, icacChipSpan));
178 :
179 22 : VerifyOrReturnError(mLastIcac.Alloc(icacChipSpan.size()), CHIP_ERROR_NO_MEMORY);
180 :
181 22 : memcpy(mLastIcac.Get(), icacChipSpan.data(), icacChipSpan.size());
182 :
183 22 : nocIssuerKeypair = &icacKeypair;
184 22 : issuer_dn = &icac_dn;
185 22 : }
186 :
187 : // Generate NOC always, either issued from ICAC if present or from RCAC
188 : {
189 35 : Platform::ScopedMemoryBufferWithSize<uint8_t> nocDerBuf;
190 35 : VerifyOrReturnError(nocDerBuf.Alloc(Credentials::kMaxDERCertLength), CHIP_ERROR_NO_MEMORY);
191 35 : Platform::ScopedMemoryBufferWithSize<uint8_t> nocChipBuf;
192 35 : VerifyOrReturnError(nocChipBuf.Alloc(Credentials::kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY);
193 :
194 35 : ReturnErrorOnFailure(noc_dn.AddAttribute_MatterFabricId(fabricId));
195 35 : ReturnErrorOnFailure(noc_dn.AddAttribute_MatterNodeId(nodeId));
196 :
197 35 : X509CertRequestParams noc_request = { 0, mNow, mNow + mValidity, noc_dn, *issuer_dn };
198 :
199 35 : MutableByteSpan nocDerSpan{ nocDerBuf.Get(), nocDerBuf.AllocatedSize() };
200 35 : ReturnErrorOnFailure(Credentials::NewNodeOperationalX509Cert(noc_request, nocPublicKey, *nocIssuerKeypair, nocDerSpan));
201 :
202 35 : MutableByteSpan nocChipSpan{ nocChipBuf.Get(), nocChipBuf.AllocatedSize() };
203 35 : ReturnErrorOnFailure(Credentials::ConvertX509CertToChipCert(nocDerSpan, nocChipSpan));
204 :
205 35 : VerifyOrReturnError(mLastNoc.Alloc(nocChipSpan.size()), CHIP_ERROR_NO_MEMORY);
206 :
207 35 : memcpy(mLastNoc.Get(), nocChipSpan.data(), nocChipSpan.size());
208 35 : }
209 :
210 35 : return CHIP_NO_ERROR;
211 35 : }
212 :
213 21 : CHIP_ERROR GenerateRootCert(Crypto::P256Keypair & rootKeyPair)
214 : {
215 21 : ChipDN rcac_dn;
216 21 : const uint64_t kIssuerId = 1234567;
217 :
218 21 : Platform::ScopedMemoryBufferWithSize<uint8_t> rcacDerBuf;
219 21 : VerifyOrReturnError(rcacDerBuf.Alloc(Credentials::kMaxDERCertLength), CHIP_ERROR_NO_MEMORY);
220 21 : Platform::ScopedMemoryBufferWithSize<uint8_t> rcacChipBuf;
221 21 : VerifyOrReturnError(rcacChipBuf.Alloc(Credentials::kMaxCHIPCertLength), CHIP_ERROR_NO_MEMORY);
222 :
223 21 : ReturnErrorOnFailure(rcac_dn.AddAttribute_MatterRCACId(kIssuerId));
224 :
225 21 : X509CertRequestParams rcac_request = { 0, mNow, mNow + mValidity, rcac_dn, rcac_dn };
226 :
227 21 : MutableByteSpan rcacDerSpan{ rcacDerBuf.Get(), rcacDerBuf.AllocatedSize() };
228 21 : ReturnErrorOnFailure(Credentials::NewRootX509Cert(rcac_request, rootKeyPair, rcacDerSpan));
229 :
230 21 : MutableByteSpan rcacChipSpan{ rcacChipBuf.Get(), rcacChipBuf.AllocatedSize() };
231 21 : ReturnErrorOnFailure(Credentials::ConvertX509CertToChipCert(rcacDerSpan, rcacChipSpan));
232 :
233 21 : VerifyOrReturnError(mLastRcac.Alloc(rcacChipSpan.size()), CHIP_ERROR_NO_MEMORY);
234 21 : memcpy(mLastRcac.Get(), rcacChipSpan.data(), rcacChipSpan.size());
235 :
236 21 : return CHIP_NO_ERROR;
237 21 : }
238 :
239 : uint32_t mNow = 0;
240 :
241 : // By default, let's set validity to 10 years
242 : uint32_t mValidity = 365 * 24 * 60 * 60 * 10;
243 :
244 : CHIP_ERROR mCurrentStatus = CHIP_NO_ERROR;
245 : bool mIncludeIcac = false;
246 :
247 : Platform::ScopedMemoryBufferWithSize<uint8_t> mLastNoc;
248 : Platform::ScopedMemoryBufferWithSize<uint8_t> mLastIcac;
249 : Platform::ScopedMemoryBufferWithSize<uint8_t> mLastRcac;
250 :
251 : Platform::UniquePtr<Crypto::P256Keypair> mRootKeypair;
252 : };
253 :
254 : } // namespace Credentials
255 : } // namespace chip
|