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