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 <crypto/CHIPCryptoPAL.h>
21 : #include <lib/core/CHIPError.h>
22 : #include <lib/core/DataModelTypes.h>
23 : #include <lib/support/Span.h>
24 :
25 : namespace chip {
26 : namespace Crypto {
27 :
28 : class OperationalKeystore
29 : {
30 : public:
31 1 : virtual ~OperationalKeystore() {}
32 :
33 : // ==== API designed for commisionables to support fail-safe (although can be used by controllers) ====
34 :
35 : /**
36 : * @brief Returns true if a pending operational key exists from a previous
37 : * `NewOpKeypairForFabric` before any `CommitOpKeypairForFabric` or
38 : * `RevertOpKeypairForFabric`. This returns true even if the key is
39 : * NOT ACTIVE (i.e after `NewOpKeypairForFabric` but before
40 : * `ActivateOpKeypairForFabric`).
41 : */
42 : virtual bool HasPendingOpKeypair() const = 0;
43 :
44 : /**
45 : * @brief Returns whether a usable operational key exists for the given fabric.
46 : *
47 : * Returns true even if the key is not persisted, such as if `ActivateOpKeypairForFabric`
48 : * had been successfully called for a given fabric. Only returns true if a key
49 : * is presently usable such that `SignWithOpKeypair` would succeed for the fabric. Therefore
50 : * if there was no previously persisted key and `NewOpKeypairForFabric` had been called
51 : * but not `ActivateOpKeypairForFabric`, there is only an inactive key, and this would return false.
52 : *
53 : * @param fabricIndex - FabricIndex for which availability of keypair will be checked.
54 : * @return true if there an active operational keypair for the given FabricIndex, false otherwise.
55 : */
56 : virtual bool HasOpKeypairForFabric(FabricIndex fabricIndex) const = 0;
57 :
58 : /**
59 : * @brief This initializes a new keypair for the given fabric and generates a CSR for it,
60 : * so that it can be passed in a CSRResponse.
61 : *
62 : * The keypair is temporary and becomes usable for `SignWithOpKeypair` only after either
63 : * `ActivateOpKeypairForFabric` is called. It is destroyed if
64 : * `RevertPendingKeypair` is called before `CommitOpKeypairForFabric`.
65 : * If a pending keypair already existed for the given `fabricIndex`, it is replaced by this call.
66 : *
67 : * Only one pending operational keypair is supported at a time.
68 : *
69 : * @param fabricIndex - FabricIndex for which a new keypair must be made available
70 : * @param outCertificateSigningRequest - Buffer to contain the CSR. Must have size at least `kMIN_CSR_Buffer_Size`.
71 : *
72 : * @retval CHIP_NO_ERROR on success
73 : * @retval CHIP_ERROR_BUFFER_TOO_SMALL if `outCertificateSigningRequest` buffer is too small
74 : * @retval CHIP_ERROR_INCORRECT_STATE if the key store is not properly initialized.
75 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there is already a pending keypair for another `fabricIndex` value
76 : * or if fabricIndex is an invalid value.
77 : * @retval CHIP_ERROR_NOT_IMPLEMENTED if only `SignWithOpKeypair` is supported
78 : * @retval other CHIP_ERROR value on internal crypto engine errors
79 : */
80 : virtual CHIP_ERROR NewOpKeypairForFabric(FabricIndex fabricIndex, MutableByteSpan & outCertificateSigningRequest) = 0;
81 :
82 : /**
83 : * @brief Temporarily activates the operational keypair last generated with `NewOpKeypairForFabric`, so
84 : * that `SignWithOpKeypair` starts using it, but only if it matches the public key associated
85 : * with the last NewOpKeypairForFabric.
86 : *
87 : * This is to be used by AddNOC and UpdateNOC so that a prior key generated by NewOpKeypairForFabric
88 : * can be used for CASE while not committing it yet to permanent storage to remain after fail-safe.
89 : *
90 : * @param fabricIndex - FabricIndex for which to activate the keypair, used for security cross-checking
91 : * @param nocPublicKey - Subject public key associated with an incoming NOC
92 : *
93 : * @retval CHIP_NO_ERROR on success
94 : * @retval CHIP_ERROR_INCORRECT_STATE if the key store is not properly initialized.
95 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there is no operational keypair for `fabricIndex` from a previous
96 : * matching `NewOpKeypairForFabric`.
97 : * @retval CHIP_ERROR_INVALID_PUBLIC_KEY if `nocPublicKey` does not match the public key associated with the
98 : * key pair from last `NewOpKeypairForFabric`.
99 : * @retval CHIP_ERROR_NOT_IMPLEMENTED if only `SignWithOpKeypair` is supported
100 : * @retval other CHIP_ERROR value on internal storage or crypto engine errors
101 : */
102 : virtual CHIP_ERROR ActivateOpKeypairForFabric(FabricIndex fabricIndex, const Crypto::P256PublicKey & nocPublicKey) = 0;
103 :
104 : /**
105 : * @brief Permanently commit the operational keypair last generated with `NewOpKeypairForFabric`,
106 : * replacing a prior one previously committed, if any, so that `SignWithOpKeypair` for the
107 : * given `FabricIndex` permanently uses the key that was pending.
108 : *
109 : * This is to be used when CommissioningComplete is successfully received
110 : *
111 : * @param fabricIndex - FabricIndex for which to commit the keypair, used for security cross-checking
112 : *
113 : * @retval CHIP_NO_ERROR on success
114 : * @retval CHIP_ERROR_INCORRECT_STATE if the key store is not properly initialized,
115 : * or ActivateOpKeypairForFabric not yet called
116 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there is no pending operational keypair for `fabricIndex`
117 : * @retval CHIP_ERROR_NOT_IMPLEMENTED if only `SignWithOpKeypair` is supported
118 : * @retval other CHIP_ERROR value on internal storage or crypto engine errors
119 : */
120 : virtual CHIP_ERROR CommitOpKeypairForFabric(FabricIndex fabricIndex) = 0;
121 :
122 : /**
123 : * @brief Try to read out the permanently committed operational keypair and save it to the buffer.
124 : *
125 : * @param fabricIndex - FabricIndex from which the keypair will be exported
126 : * @param outKeypair - a reference to P256SerializedKeypair object to store the exported key.
127 : * @retval CHIP_ERROR on success.
128 : * @retval CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE if the key cannot be exported due to security restrictions.
129 : * @retval CHIP_ERROR_NOT_IMPLEMENTED if the exporting is not implemented for the cryptography backend.
130 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if provided wrong value of `fabricIndex`.
131 : * @retval CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND if there is no keypair found for `fabricIndex`.
132 : * @retval CHIP_ERROR_BUFFER_TOO_SMALL if `outKeyPair` buffer is too small to store the read out keypair.
133 : * @retval other CHIP_ERROR value on internal storage or cryptography backend errors.
134 : */
135 0 : virtual CHIP_ERROR ExportOpKeypairForFabric(FabricIndex fabricIndex, Crypto::P256SerializedKeypair & outKeypair)
136 : {
137 0 : return CHIP_ERROR_NOT_IMPLEMENTED;
138 : };
139 :
140 : /**
141 : * @brief Migrate the operational keypair from another Operational keystore (`operationalKeystore`) implementation to this one.
142 : *
143 : * This method assumes that the operational key for given `fabricIndex` exists in the `operationalKeystore` storage or it has
144 : * been already migrated to the new Operational Storage. If a key for the given `fabricIndex` does not exist in any of those
145 : * keystores, then the method retuns CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND.
146 : *
147 : * @param fabricIndex - FabricIndex for which to migrate the operational key
148 : * @param operationalKeystore - a reference to the operationalKeystore implementation that may contain saved operational key
149 : * for Fabric
150 : * @retval CHIP_ERROR on success
151 : * @retval CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE if the key cannot be exported due to security restrictions.
152 : * @retval CHIP_ERROR_NOT_IMPLEMENTED if the exporting is not implemented for the cryptography backend.
153 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there is no keypair found for `fabricIndex`.
154 : * @retval CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND if there is no keypair found for `fabricIndex`.
155 : * @retval other CHIP_ERROR value on internal storage or crypto engine errors.
156 : */
157 0 : virtual CHIP_ERROR MigrateOpKeypairForFabric(FabricIndex fabricIndex, OperationalKeystore & operationalKeystore) const
158 : {
159 0 : return CHIP_ERROR_NOT_IMPLEMENTED;
160 : };
161 :
162 : /**
163 : * @brief Permanently remove the keypair associated with a fabric
164 : *
165 : * This is to be used for fail-safe handling and RemoveFabric. Removes both the
166 : * pending operational keypair for the fabricIndex (if any) and the committed one (if any).
167 : *
168 : * @param fabricIndex - FabricIndex for which to remove the keypair
169 : *
170 : * @retval CHIP_NO_ERROR on success
171 : * @retval CHIP_ERROR_INCORRECT_STATE if the key store is not properly initialized.
172 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there is no operational keypair for `fabricIndex`
173 : * @retval CHIP_ERROR_NOT_IMPLEMENTED if only `SignWithOpKeypair` is supported
174 : * @retval other CHIP_ERROR value on internal storage errors
175 : */
176 : virtual CHIP_ERROR RemoveOpKeypairForFabric(FabricIndex fabricIndex) = 0;
177 :
178 : /**
179 : * @brief Permanently release the operational keypair last generated with `NewOpKeypairForFabric`,
180 : * such that `SignWithOpKeypair` uses the previously committed key (if one existed).
181 : *
182 : * This is to be used when a fail-safe expires prior to CommissioningComplete.
183 : *
184 : * This method cannot error-out and must always succeed, even on a no-op. This should
185 : * be safe to do given that `CommitOpKeypairForFabric` must succeed to make a new operational
186 : * keypair usable.
187 : */
188 : virtual void RevertPendingKeypair() = 0;
189 :
190 : // ==== Primary operation required: signature
191 : /**
192 : * @brief Whether `SignWithOpKeypair` may be performed in the background.
193 : *
194 : * If true, `CASESession` may attempt to perform `SignWithOpKeypair` in the
195 : * background. In this case, `OperationalKeystore` should protect itself,
196 : * e.g. with a mutex, as the signing could occur at any time during session
197 : * establishment.
198 : *
199 : * @retval true if `SignWithOpKeypair` may be performed in the background
200 : * @retval false if `SignWithOpKeypair` may NOT be performed in the background
201 : */
202 0 : virtual bool SupportsSignWithOpKeypairInBackground() const { return false; }
203 :
204 : /**
205 : * @brief Sign a message with a fabric's currently-active operational keypair.
206 : *
207 : * If a Keypair was successfully made temporarily active for the given `fabricIndex` with `ActivateOpKeypairForFabric`,
208 : * then that is the keypair whose private key is used. Otherwise, the last committed private key
209 : * is used, if one exists
210 : *
211 : * @param fabricIndex - FabricIndex whose operational keypair will be used to sign the `message`
212 : * @param message - Message to sign with the currently active operational keypair
213 : * @param outSignature - Buffer to contain the signature
214 : *
215 : * @retval CHIP_NO_ERROR on success
216 : * @retval CHIP_ERROR_INCORRECT_STATE if the key store is not properly initialized.
217 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if no active key is found for the given `fabricIndex` or if
218 : * `fabricIndex` is invalid.
219 : * @retval other CHIP_ERROR value on internal crypto engine errors
220 : */
221 : virtual CHIP_ERROR SignWithOpKeypair(FabricIndex fabricIndex, const ByteSpan & message,
222 : Crypto::P256ECDSASignature & outSignature) const = 0;
223 :
224 : /**
225 : * @brief Create an ephemeral keypair for use in session establishment.
226 : *
227 : * The caller must Initialize() the P256Keypair if needed. It is not done by this method.
228 : *
229 : * This method MUST ONLY be used for CASESession ephemeral keys.
230 : *
231 : * NOTE: The stack will allocate as many of these as there are CASE sessions which
232 : * can be concurrently in the process of establishment. Implementations must
233 : * support more than one such keypair, or be implemented to match the limitations
234 : * enforced by a given product on its concurrent CASE session establishment limits.
235 : *
236 : * WARNING: The return value MUST be released by `ReleaseEphemeralKeypair`. This is because
237 : * Matter CHIPMem.h does not properly support UniquePtr in a way that would
238 : * safely allow classes derived from Crypto::P256Keypair to be released properly.
239 : *
240 : * @return a pointer to a P256Keypair (or derived class thereof), which may evaluate to nullptr
241 : * if running out of memory.
242 : */
243 : virtual Crypto::P256Keypair * AllocateEphemeralKeypairForCASE() = 0;
244 :
245 : /**
246 : * @brief Release an ephemeral keypair previously provided by `AllocateEphemeralKeypairForCASE()`
247 : */
248 : virtual void ReleaseEphemeralKeypair(Crypto::P256Keypair * keypair) = 0;
249 : };
250 :
251 : } // namespace Crypto
252 : } // namespace chip
|