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 : #include <crypto/OperationalKeystore.h>
19 : #include <lib/core/CHIPError.h>
20 : #include <lib/core/CHIPPersistentStorageDelegate.h>
21 : #include <lib/core/DataModelTypes.h>
22 : #include <lib/core/TLV.h>
23 : #include <lib/support/CHIPMem.h>
24 : #include <lib/support/CodeUtils.h>
25 : #include <lib/support/DefaultStorageKeyAllocator.h>
26 : #include <lib/support/SafeInt.h>
27 :
28 : #include "PersistentStorageOperationalKeystore.h"
29 :
30 : namespace chip {
31 :
32 : using namespace chip::Crypto;
33 :
34 : namespace {
35 :
36 : // Tags for our operational keypair storage.
37 : constexpr TLV::Tag kOpKeyVersionTag = TLV::ContextTag(0);
38 : constexpr TLV::Tag kOpKeyDataTag = TLV::ContextTag(1);
39 :
40 : // If this version grows beyond UINT16_MAX, adjust OpKeypairTLVMaxSize
41 : // accordingly.
42 : constexpr uint16_t kOpKeyVersion = 1;
43 :
44 : constexpr size_t OpKeyTLVMaxSize()
45 : {
46 : // Version and serialized key
47 : return TLV::EstimateStructOverhead(sizeof(uint16_t), Crypto::P256SerializedKeypair::Capacity());
48 : }
49 :
50 : /** WARNING: This can leave the operational key on the stack somewhere, since many of the platform
51 : * APIs use stack buffers and do not sanitize! This implementation is for example purposes
52 : * only of the API and it is recommended to avoid directly accessing raw private key bits
53 : * in storage.
54 : */
55 19 : CHIP_ERROR StoreOperationalKey(FabricIndex fabricIndex, PersistentStorageDelegate * storage, P256Keypair * keypair)
56 : {
57 19 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && (storage != nullptr) && (keypair != nullptr),
58 : CHIP_ERROR_INVALID_ARGUMENT);
59 :
60 : // Use a SensitiveDataBuffer to get RAII secret data clearing on scope exit.
61 19 : Crypto::SensitiveDataBuffer<OpKeyTLVMaxSize()> buf;
62 19 : TLV::TLVWriter writer;
63 :
64 19 : writer.Init(buf.Bytes(), buf.Capacity());
65 :
66 : TLV::TLVType outerType;
67 19 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerType));
68 :
69 19 : ReturnErrorOnFailure(writer.Put(kOpKeyVersionTag, kOpKeyVersion));
70 :
71 : {
72 : // P256SerializedKeypair has RAII secret clearing
73 19 : Crypto::P256SerializedKeypair serializedOpKey;
74 19 : ReturnErrorOnFailure(keypair->Serialize(serializedOpKey));
75 :
76 19 : ReturnErrorOnFailure(writer.Put(kOpKeyDataTag, ByteSpan(serializedOpKey.Bytes(), serializedOpKey.Length())));
77 19 : }
78 :
79 19 : ReturnErrorOnFailure(writer.EndContainer(outerType));
80 :
81 19 : const auto opKeyLength = writer.GetLengthWritten();
82 19 : VerifyOrReturnError(CanCastTo<uint16_t>(opKeyLength), CHIP_ERROR_BUFFER_TOO_SMALL);
83 19 : ReturnErrorOnFailure(storage->SyncSetKeyValue(DefaultStorageKeyAllocator::FabricOpKey(fabricIndex).KeyName(), buf.ConstBytes(),
84 : static_cast<uint16_t>(opKeyLength)));
85 :
86 19 : return CHIP_NO_ERROR;
87 19 : }
88 :
89 19 : CHIP_ERROR ExportStoredOpKey(FabricIndex fabricIndex, PersistentStorageDelegate * storage,
90 : Crypto::P256SerializedKeypair & serializedOpKey)
91 : {
92 19 : VerifyOrReturnError(storage != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
93 19 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
94 :
95 : // Use a SensitiveDataBuffer to get RAII secret data clearing on scope exit.
96 19 : Crypto::SensitiveDataBuffer<OpKeyTLVMaxSize()> buf;
97 :
98 : // Load up the operational key structure from storage
99 19 : uint16_t size = static_cast<uint16_t>(buf.Capacity());
100 19 : ReturnErrorOnFailure(
101 : storage->SyncGetKeyValue(DefaultStorageKeyAllocator::FabricOpKey(fabricIndex).KeyName(), buf.Bytes(), size));
102 :
103 16 : buf.SetLength(static_cast<size_t>(size));
104 :
105 : // Read-out the operational key TLV entry.
106 16 : TLV::ContiguousBufferTLVReader reader;
107 16 : reader.Init(buf.Bytes(), buf.Length());
108 :
109 16 : ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
110 : TLV::TLVType containerType;
111 16 : ReturnErrorOnFailure(reader.EnterContainer(containerType));
112 :
113 16 : ReturnErrorOnFailure(reader.Next(kOpKeyVersionTag));
114 : uint16_t opKeyVersion;
115 16 : ReturnErrorOnFailure(reader.Get(opKeyVersion));
116 16 : VerifyOrReturnError(opKeyVersion == kOpKeyVersion, CHIP_ERROR_VERSION_MISMATCH);
117 :
118 16 : ReturnErrorOnFailure(reader.Next(kOpKeyDataTag));
119 : {
120 16 : ByteSpan keyData;
121 16 : ReturnErrorOnFailure(reader.GetByteView(keyData));
122 :
123 : // Unfortunately, we have to copy the data into a P256SerializedKeypair.
124 16 : VerifyOrReturnError(keyData.size() <= serializedOpKey.Capacity(), CHIP_ERROR_BUFFER_TOO_SMALL);
125 :
126 16 : ReturnErrorOnFailure(reader.ExitContainer(containerType));
127 :
128 16 : memcpy(serializedOpKey.Bytes(), keyData.data(), keyData.size());
129 16 : serializedOpKey.SetLength(keyData.size());
130 : }
131 :
132 16 : return CHIP_NO_ERROR;
133 19 : }
134 :
135 : /** WARNING: This can leave the operational key on the stack somewhere, since many of the platform
136 : * APIs use stack buffers and do not sanitize! This implementation is for example purposes
137 : * only of the API and it is recommended to avoid directly accessing raw private key bits
138 : * in storage.
139 : */
140 16 : CHIP_ERROR SignWithStoredOpKey(FabricIndex fabricIndex, PersistentStorageDelegate * storage, const ByteSpan & message,
141 : P256ECDSASignature & outSignature)
142 : {
143 16 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && (storage != nullptr), CHIP_ERROR_INVALID_ARGUMENT);
144 :
145 : // Use RAII scoping for the transient keypair, to make sure it doesn't get leaked on any error paths.
146 : // Key is put in heap since signature is a costly stack operation and P256Keypair is
147 : // a costly class depending on the backend.
148 16 : auto transientOperationalKeypair = Platform::MakeUnique<P256Keypair>();
149 16 : if (!transientOperationalKeypair)
150 : {
151 0 : return CHIP_ERROR_NO_MEMORY;
152 : }
153 :
154 : // Scope 1: Load up the keypair data from storage
155 16 : P256SerializedKeypair serializedOpKey;
156 16 : CHIP_ERROR err = ExportStoredOpKey(fabricIndex, storage, serializedOpKey);
157 16 : if (CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND == err)
158 : {
159 2 : return CHIP_ERROR_INVALID_FABRIC_INDEX;
160 : }
161 :
162 : // Load-up key material
163 : // WARNING: This makes use of the raw key bits
164 14 : ReturnErrorOnFailure(transientOperationalKeypair->Deserialize(serializedOpKey));
165 :
166 : // Scope 2: Sign message with the keypair
167 14 : return transientOperationalKeypair->ECDSA_sign_msg(message.data(), message.size(), outSignature);
168 16 : }
169 :
170 : } // namespace
171 :
172 682 : bool PersistentStorageOperationalKeystore::HasOpKeypairForFabric(FabricIndex fabricIndex) const
173 : {
174 682 : VerifyOrReturnError(mStorage != nullptr, false);
175 682 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex), false);
176 :
177 : // If there was a pending keypair, then there's really a usable key
178 682 : if (mIsPendingKeypairActive && (fabricIndex == mPendingFabricIndex) && (mPendingKeypair != nullptr))
179 : {
180 36 : return true;
181 : }
182 :
183 : // TODO(#16958): need to actually read the key to know if it's there due to platforms not
184 : // properly enforcing CHIP_ERROR_BUFFER_TOO_SMALL behavior needed by
185 : // PersistentStorageDelegate. Very unfortunate, needs fixing ASAP.
186 :
187 : // Use a SensitiveDataBuffer to get RAII secret data clearing on scope exit.
188 646 : Crypto::SensitiveDataBuffer<OpKeyTLVMaxSize()> buf;
189 :
190 646 : uint16_t keySize = static_cast<uint16_t>(buf.Capacity());
191 : CHIP_ERROR err =
192 646 : mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::FabricOpKey(fabricIndex).KeyName(), buf.Bytes(), keySize);
193 :
194 646 : return (err == CHIP_NO_ERROR);
195 646 : }
196 :
197 28 : CHIP_ERROR PersistentStorageOperationalKeystore::NewOpKeypairForFabric(FabricIndex fabricIndex,
198 : MutableByteSpan & outCertificateSigningRequest)
199 : {
200 28 : VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
201 27 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
202 : // If a key is pending, we cannot generate for a different fabric index until we commit or revert.
203 25 : if ((mPendingFabricIndex != kUndefinedFabricIndex) && (fabricIndex != mPendingFabricIndex))
204 : {
205 1 : return CHIP_ERROR_INVALID_FABRIC_INDEX;
206 : }
207 24 : VerifyOrReturnError(outCertificateSigningRequest.size() >= Crypto::kMIN_CSR_Buffer_Size, CHIP_ERROR_BUFFER_TOO_SMALL);
208 :
209 : // Replace previous pending keypair, if any was previously allocated
210 24 : ResetPendingKey();
211 :
212 24 : mPendingKeypair = Platform::New<Crypto::P256Keypair>();
213 24 : VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_NO_MEMORY);
214 :
215 24 : mPendingKeypair->Initialize(Crypto::ECPKeyTarget::ECDSA);
216 24 : size_t csrLength = outCertificateSigningRequest.size();
217 24 : CHIP_ERROR err = mPendingKeypair->NewCertificateSigningRequest(outCertificateSigningRequest.data(), csrLength);
218 24 : if (err != CHIP_NO_ERROR)
219 : {
220 0 : ResetPendingKey();
221 0 : return err;
222 : }
223 :
224 24 : outCertificateSigningRequest.reduce_size(csrLength);
225 24 : mPendingFabricIndex = fabricIndex;
226 :
227 24 : return CHIP_NO_ERROR;
228 : }
229 :
230 24 : CHIP_ERROR PersistentStorageOperationalKeystore::ActivateOpKeypairForFabric(FabricIndex fabricIndex,
231 : const Crypto::P256PublicKey & nocPublicKey)
232 : {
233 24 : VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
234 23 : VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX);
235 23 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && (fabricIndex == mPendingFabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
236 :
237 : // Validate public key being activated matches last generated pending keypair
238 22 : VerifyOrReturnError(mPendingKeypair->Pubkey().Matches(nocPublicKey), CHIP_ERROR_INVALID_PUBLIC_KEY);
239 :
240 21 : mIsPendingKeypairActive = true;
241 :
242 21 : return CHIP_NO_ERROR;
243 : }
244 :
245 20 : CHIP_ERROR PersistentStorageOperationalKeystore::CommitOpKeypairForFabric(FabricIndex fabricIndex)
246 : {
247 20 : VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
248 19 : VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX);
249 19 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && (fabricIndex == mPendingFabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
250 18 : VerifyOrReturnError(mIsPendingKeypairActive == true, CHIP_ERROR_INCORRECT_STATE);
251 :
252 : // Try to store persistent key. On failure, leave everything pending as-is
253 18 : CHIP_ERROR err = StoreOperationalKey(fabricIndex, mStorage, mPendingKeypair);
254 18 : ReturnErrorOnFailure(err);
255 :
256 : // If we got here, we succeeded and can reset the pending key: next `SignWithOpKeypair` will use the stored key.
257 18 : ResetPendingKey();
258 18 : return CHIP_NO_ERROR;
259 : }
260 :
261 3 : CHIP_ERROR PersistentStorageOperationalKeystore::ExportOpKeypairForFabric(FabricIndex fabricIndex,
262 : Crypto::P256SerializedKeypair & outKeypair)
263 : {
264 3 : VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
265 3 : return ExportStoredOpKey(fabricIndex, mStorage, outKeypair);
266 : }
267 :
268 17 : CHIP_ERROR PersistentStorageOperationalKeystore::RemoveOpKeypairForFabric(FabricIndex fabricIndex)
269 : {
270 17 : VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
271 16 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
272 :
273 : // Remove pending state if matching
274 16 : if ((mPendingKeypair != nullptr) && (fabricIndex == mPendingFabricIndex))
275 : {
276 1 : RevertPendingKeypair();
277 : }
278 :
279 16 : CHIP_ERROR err = mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::FabricOpKey(fabricIndex).KeyName());
280 16 : if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
281 : {
282 14 : err = CHIP_ERROR_INVALID_FABRIC_INDEX;
283 : }
284 :
285 16 : return err;
286 : }
287 :
288 387 : void PersistentStorageOperationalKeystore::RevertPendingKeypair()
289 : {
290 387 : VerifyOrReturn(mStorage != nullptr);
291 :
292 : // Just reset the pending key, we never stored anything
293 387 : ResetPendingKey();
294 : }
295 :
296 17 : CHIP_ERROR PersistentStorageOperationalKeystore::SignWithOpKeypair(FabricIndex fabricIndex, const ByteSpan & message,
297 : Crypto::P256ECDSASignature & outSignature) const
298 : {
299 17 : VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
300 17 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
301 :
302 17 : if (mIsPendingKeypairActive && (fabricIndex == mPendingFabricIndex))
303 : {
304 1 : VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_INTERNAL);
305 : // We have an override key: sign with it!
306 1 : return mPendingKeypair->ECDSA_sign_msg(message.data(), message.size(), outSignature);
307 : }
308 :
309 16 : return SignWithStoredOpKey(fabricIndex, mStorage, message, outSignature);
310 : }
311 :
312 2 : Crypto::P256Keypair * PersistentStorageOperationalKeystore::AllocateEphemeralKeypairForCASE()
313 : {
314 : // DO NOT CUT AND PASTE without considering the ReleaseEphemeralKeypair().
315 : // If allocating a derived class, then `ReleaseEphemeralKeypair` MUST
316 : // de-allocate the derived class after up-casting the base class pointer.
317 2 : return Platform::New<Crypto::P256Keypair>();
318 : }
319 :
320 3 : void PersistentStorageOperationalKeystore::ReleaseEphemeralKeypair(Crypto::P256Keypair * keypair)
321 : {
322 : // DO NOT CUT AND PASTE without considering the AllocateEphemeralKeypairForCASE().
323 : // This must delete the same concrete class as allocated in `AllocateEphemeralKeypairForCASE`
324 3 : Platform::Delete<Crypto::P256Keypair>(keypair);
325 3 : }
326 :
327 6 : CHIP_ERROR PersistentStorageOperationalKeystore::MigrateOpKeypairForFabric(FabricIndex fabricIndex,
328 : OperationalKeystore & operationalKeystore) const
329 : {
330 6 : VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
331 6 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
332 :
333 4 : P256SerializedKeypair serializedKeypair;
334 :
335 : // Do not allow overwriting the existing key and just remove it from the previous Operational Keystore if needed.
336 4 : if (!HasOpKeypairForFabric(fabricIndex))
337 : {
338 2 : ReturnErrorOnFailure(operationalKeystore.ExportOpKeypairForFabric(fabricIndex, serializedKeypair));
339 :
340 1 : auto operationalKeypair = Platform::MakeUnique<P256Keypair>();
341 1 : if (!operationalKeypair)
342 : {
343 0 : return CHIP_ERROR_NO_MEMORY;
344 : }
345 :
346 1 : ReturnErrorOnFailure(operationalKeypair->Deserialize(serializedKeypair));
347 1 : ReturnErrorOnFailure(StoreOperationalKey(fabricIndex, mStorage, operationalKeypair.get()));
348 :
349 1 : ReturnErrorOnFailure(operationalKeystore.RemoveOpKeypairForFabric(fabricIndex));
350 1 : }
351 2 : else if (operationalKeystore.HasOpKeypairForFabric(fabricIndex))
352 : {
353 1 : ReturnErrorOnFailure(operationalKeystore.RemoveOpKeypairForFabric(fabricIndex));
354 : }
355 :
356 3 : return CHIP_NO_ERROR;
357 4 : }
358 :
359 : } // namespace chip
|