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 : TEMPORARY_RETURN_IGNORED 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 952 : bool PersistentStorageOperationalKeystore::HasOpKeypairForFabric(FabricIndex fabricIndex) const
176 : {
177 952 : VerifyOrReturnError(mStorage != nullptr, false);
178 952 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex), false);
179 :
180 : // If there was a pending keypair, then there's really a usable key
181 952 : 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 902 : Crypto::SensitiveDataBuffer<OpKeyTLVMaxSize()> buf;
192 :
193 902 : uint16_t keySize = static_cast<uint16_t>(buf.Capacity());
194 : CHIP_ERROR err =
195 902 : mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::FabricOpKey(fabricIndex).KeyName(), buf.Bytes(), keySize);
196 :
197 1804 : return (err == CHIP_NO_ERROR);
198 902 : }
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 : TEMPORARY_RETURN_IGNORED mPendingKeypair->Initialize(Crypto::ECPKeyTarget::ECDSA);
219 35 : size_t csrLength = outCertificateSigningRequest.size();
220 35 : CHIP_ERROR err = mPendingKeypair->NewCertificateSigningRequest(outCertificateSigningRequest.data(), csrLength);
221 70 : if (err != CHIP_NO_ERROR)
222 : {
223 0 : ResetPendingKey();
224 0 : return err;
225 : }
226 :
227 35 : outCertificateSigningRequest.reduce_size(csrLength);
228 35 : mPendingFabricIndex = fabricIndex;
229 :
230 35 : return CHIP_NO_ERROR;
231 : }
232 :
233 33 : CHIP_ERROR PersistentStorageOperationalKeystore::ActivateOpKeypairForFabric(FabricIndex fabricIndex,
234 : const Crypto::P256PublicKey & nocPublicKey)
235 : {
236 33 : VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
237 32 : VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX);
238 32 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && (fabricIndex == mPendingFabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
239 :
240 : // Validate public key being activated matches last generated pending keypair
241 31 : VerifyOrReturnError(mPendingKeypair->Pubkey().Matches(nocPublicKey), CHIP_ERROR_INVALID_PUBLIC_KEY);
242 :
243 30 : mIsPendingKeypairActive = true;
244 :
245 30 : return CHIP_NO_ERROR;
246 : }
247 :
248 27 : CHIP_ERROR PersistentStorageOperationalKeystore::CommitOpKeypairForFabric(FabricIndex fabricIndex)
249 : {
250 27 : VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
251 26 : VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_INVALID_FABRIC_INDEX);
252 26 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && (fabricIndex == mPendingFabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
253 25 : VerifyOrReturnError(mIsPendingKeypairActive == true, CHIP_ERROR_INCORRECT_STATE);
254 :
255 : // Try to store persistent key. On failure, leave everything pending as-is
256 25 : CHIP_ERROR err = StoreOperationalKey(fabricIndex, mStorage, mPendingKeypair);
257 25 : ReturnErrorOnFailure(err);
258 :
259 : // If we got here, we succeeded and can reset the pending key: next `SignWithOpKeypair` will use the stored key.
260 25 : ResetPendingKey();
261 25 : return CHIP_NO_ERROR;
262 : }
263 :
264 3 : CHIP_ERROR PersistentStorageOperationalKeystore::ExportOpKeypairForFabric(FabricIndex fabricIndex,
265 : Crypto::P256SerializedKeypair & outKeypair)
266 : {
267 3 : VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
268 3 : return ExportStoredOpKey(fabricIndex, mStorage, outKeypair);
269 : }
270 :
271 21 : CHIP_ERROR PersistentStorageOperationalKeystore::RemoveOpKeypairForFabric(FabricIndex fabricIndex)
272 : {
273 21 : VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
274 20 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
275 :
276 : // Remove pending state if matching
277 20 : if ((mPendingKeypair != nullptr) && (fabricIndex == mPendingFabricIndex))
278 : {
279 2 : RevertPendingKeypair();
280 : }
281 :
282 20 : CHIP_ERROR err = mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::FabricOpKey(fabricIndex).KeyName());
283 40 : if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
284 : {
285 18 : err = CHIP_ERROR_INVALID_FABRIC_INDEX;
286 : }
287 :
288 20 : return err;
289 : }
290 :
291 523 : void PersistentStorageOperationalKeystore::RevertPendingKeypair()
292 : {
293 523 : VerifyOrReturn(mStorage != nullptr);
294 :
295 : // Just reset the pending key, we never stored anything
296 523 : ResetPendingKey();
297 : }
298 :
299 19 : CHIP_ERROR PersistentStorageOperationalKeystore::SignWithOpKeypair(FabricIndex fabricIndex, const ByteSpan & message,
300 : Crypto::P256ECDSASignature & outSignature) const
301 : {
302 19 : VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
303 19 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
304 :
305 19 : if (mIsPendingKeypairActive && (fabricIndex == mPendingFabricIndex))
306 : {
307 1 : VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_INTERNAL);
308 : // We have an override key: sign with it!
309 1 : return mPendingKeypair->ECDSA_sign_msg(message.data(), message.size(), outSignature);
310 : }
311 :
312 18 : return SignWithStoredOpKey(fabricIndex, mStorage, message, outSignature);
313 : }
314 :
315 2 : Crypto::P256Keypair * PersistentStorageOperationalKeystore::AllocateEphemeralKeypairForCASE()
316 : {
317 : // DO NOT CUT AND PASTE without considering the ReleaseEphemeralKeypair().
318 : // If allocating a derived class, then `ReleaseEphemeralKeypair` MUST
319 : // de-allocate the derived class after up-casting the base class pointer.
320 2 : return Platform::New<Crypto::P256Keypair>();
321 : }
322 :
323 3 : void PersistentStorageOperationalKeystore::ReleaseEphemeralKeypair(Crypto::P256Keypair * keypair)
324 : {
325 : // DO NOT CUT AND PASTE without considering the AllocateEphemeralKeypairForCASE().
326 : // This must delete the same concrete class as allocated in `AllocateEphemeralKeypairForCASE`
327 3 : Platform::Delete<Crypto::P256Keypair>(keypair);
328 3 : }
329 :
330 6 : CHIP_ERROR PersistentStorageOperationalKeystore::MigrateOpKeypairForFabric(FabricIndex fabricIndex,
331 : OperationalKeystore & operationalKeystore) const
332 : {
333 6 : VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
334 6 : VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
335 :
336 4 : P256SerializedKeypair serializedKeypair;
337 :
338 : // Do not allow overwriting the existing key and just remove it from the previous Operational Keystore if needed.
339 4 : if (!HasOpKeypairForFabric(fabricIndex))
340 : {
341 2 : ReturnErrorOnFailure(operationalKeystore.ExportOpKeypairForFabric(fabricIndex, serializedKeypair));
342 :
343 1 : auto operationalKeypair = Platform::MakeUnique<P256Keypair>();
344 1 : if (!operationalKeypair)
345 : {
346 0 : return CHIP_ERROR_NO_MEMORY;
347 : }
348 :
349 1 : ReturnErrorOnFailure(operationalKeypair->Deserialize(serializedKeypair));
350 1 : ReturnErrorOnFailure(StoreOperationalKey(fabricIndex, mStorage, operationalKeypair.get()));
351 :
352 1 : ReturnErrorOnFailure(operationalKeystore.RemoveOpKeypairForFabric(fabricIndex));
353 1 : }
354 2 : else if (operationalKeystore.HasOpKeypairForFabric(fabricIndex))
355 : {
356 1 : ReturnErrorOnFailure(operationalKeystore.RemoveOpKeypairForFabric(fabricIndex));
357 : }
358 :
359 3 : return CHIP_NO_ERROR;
360 4 : }
361 :
362 : } // namespace chip
|