/src/connectedhomeip/src/credentials/OperationalCertificateStore.h
Line | Count | Source (jump to first uncovered line) |
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 <lib/core/CHIPError.h> |
21 | | #include <lib/core/DataModelTypes.h> |
22 | | #include <lib/support/Span.h> |
23 | | |
24 | | namespace chip { |
25 | | namespace Credentials { |
26 | | |
27 | | class OperationalCertificateStore |
28 | | { |
29 | | public: |
30 | | enum class CertChainElement : uint8_t |
31 | | { |
32 | | kRcac = 0, |
33 | | kIcac = 1, |
34 | | kNoc = 2 |
35 | | }; |
36 | | |
37 | | enum class VidVerificationElement : uint8_t |
38 | | { |
39 | | kVidVerificationStatement = 0, |
40 | | kVvsc = 1, |
41 | | }; |
42 | | |
43 | 0 | virtual ~OperationalCertificateStore() {} |
44 | | |
45 | | // ==== API designed for commisionables to support fail-safe (although can be used by controllers) ==== |
46 | | |
47 | | /** |
48 | | * @brief Returns true if a pending root certificate exists and is active from a previous |
49 | | * `AddNewTrustedRootCertForFabric`. |
50 | | */ |
51 | | virtual bool HasPendingRootCert() const = 0; |
52 | | |
53 | | /** |
54 | | * @brief Returns true if a pending operational certificate chain exists and is active from a previous |
55 | | * `AddNewOpCertsForFabric` or `UpdateOpCertsForFabric`. |
56 | | */ |
57 | | virtual bool HasPendingNocChain() const = 0; |
58 | | |
59 | | /** |
60 | | * @brief Returns true if either a pending VVSC or VIDVerificationStatement exists and is active. |
61 | | */ |
62 | | virtual bool HasPendingVidVerificationElements() const |
63 | 0 | { |
64 | | // Default false to match the CHIP_ERROR_NOT_IMPLEMENTED for default versions in this base class. |
65 | 0 | return false; |
66 | 0 | } |
67 | | |
68 | | /** |
69 | | * @brief Returns whether a usable operational certificates chain exists for the given fabric. |
70 | | * |
71 | | * Returns true even if the certificates are not persisted yet. Only returns true if a certificate |
72 | | * is presently usable such that `GetCertificate` would succeed for the fabric. |
73 | | * |
74 | | * @param fabricIndex - FabricIndex for which availability of certificate will be checked. |
75 | | * @param element - Element of the certificate chain whose presence needs to be checked |
76 | | * @return true if there an active obtainable operational certificate of the given type for the given FabricIndex, |
77 | | * false otherwise. |
78 | | */ |
79 | | virtual bool HasCertificateForFabric(FabricIndex fabricIndex, CertChainElement element) const = 0; |
80 | | |
81 | | /** |
82 | | * @brief Add and temporarily activate a new Trusted Root Certificate for the given fabric |
83 | | * |
84 | | * The certificate is temporary until committed or reverted. |
85 | | * The certificate is committed to storage only on `CommitOpCertsForFabric`. |
86 | | * The certificate is destroyed if `RevertPendingOpCerts` is called before `CommitOpCertsForFabric`. |
87 | | * |
88 | | * Only one pending trusted root certificate is supported at a time and it is illegal |
89 | | * to call this method if there is already a persisted root certificate for the given |
90 | | * fabric. |
91 | | * |
92 | | * Uniqueness constraints for roots (see AddTrustedRootCertificate command in spec) are not |
93 | | * enforced by this method and must be done as a more holistic check elsewhere. Cryptographic |
94 | | * signature verification or path validation are not enforced by this method. |
95 | | * |
96 | | * If `UpdateOpCertsForFabric` had been called before this method, this method will return |
97 | | * CHIP_ERROR_INCORRECT_STATE since it is illegal to update trusted roots when updating an |
98 | | * existing NOC chain. |
99 | | * |
100 | | * @param fabricIndex - FabricIndex for which a new trusted root certificate should be added |
101 | | * @param rcac - Buffer containing the root certificate to add. |
102 | | * |
103 | | * @retval CHIP_NO_ERROR on success |
104 | | * @retval CHIP_ERROR_NO_MEMORY if there is insufficient memory to maintain the temporary root cert |
105 | | * @retval CHIP_ERROR_INVALID_ARGUMENT if the certificate is empty or too large |
106 | | * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized, if this method |
107 | | * is called after `UpdateOpCertsForFabric`, or if there was |
108 | | * already a pending or persisted root certificate for the given `fabricIndex`. |
109 | | * @retval other CHIP_ERROR value on internal errors |
110 | | */ |
111 | | virtual CHIP_ERROR AddNewTrustedRootCertForFabric(FabricIndex fabricIndex, const ByteSpan & rcac) = 0; |
112 | | |
113 | | /** |
114 | | * @brief Add and temporarily activate an operational certificate chain for the given fabric. |
115 | | * |
116 | | * The certificate chain is temporary until committed or reverted. |
117 | | * The certificate chain is committed to storage on `CommitOpCertsForFabric`. |
118 | | * The certificate chain is destroyed if `RevertPendingOpCerts` is called before `CommitOpCertsForFabric`. |
119 | | * |
120 | | * Only one pending operational certificate chain is supported at a time and it is illegal |
121 | | * to call this method if there is already a persisted certificate chain for the given |
122 | | * fabric. |
123 | | * |
124 | | * Cryptographic signature verification or path validation are not enforced by this method. |
125 | | * |
126 | | * If `UpdateOpCertsForFabric` had been called before this method, this method will return |
127 | | * CHIP_ERROR_INCORRECT_STATE since it is illegal to add a certificate chain after |
128 | | * updating an existing NOC and before committing or reverting the update. |
129 | | * |
130 | | * If `AddNewTrustedRootCertForFabric` had not been called before this method, this method will |
131 | | * return CHIP_ERROR_INCORRECT_STATE since it is illegal in this implementation to store an |
132 | | * NOC chain without associated root. |
133 | | * |
134 | | * NOTE: The Matter spec allows AddNOC without AddTrustedRootCertificate if the NOC |
135 | | * chains to an existing root, to support root reuse. In this implementation, we expect each |
136 | | * fabric to store the root with the rest of the chain. Because of this, callers must ensure |
137 | | * that if an AddNOC command is done and no trusted root was added, that the requisite existing |
138 | | * root be "copied over" to match. |
139 | | * |
140 | | * @param fabricIndex - FabricIndex for which to add a new operational certificate chain |
141 | | * @param noc - Buffer containing the NOC certificate to add |
142 | | * @param icac - Buffer containing the ICAC certificate to add. If no ICAC is needed, `icac.empty()` must be true. |
143 | | * |
144 | | * @retval CHIP_NO_ERROR on success |
145 | | * @retval CHIP_ERROR_NO_MEMORY if there is insufficient memory to maintain the temporary `noc` and `icac` cert copies |
146 | | * @retval CHIP_ERROR_INVALID_ARGUMENT if either the noc or icac are invalid sizes |
147 | | * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if `fabricIndex` mismatches the one from a previous successful |
148 | | * `AddNewTrustedRootCertForFabric`. |
149 | | * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized, if this method |
150 | | * is called after `UpdateOpCertsForFabric`, if there was |
151 | | * already a pending or persisted operational cert chain for the given `fabricIndex`, or |
152 | | * if AddNewTrustedRootCertForFabric had not yet been called for the given `fabricIndex`. |
153 | | * |
154 | | * @retval other CHIP_ERROR value on internal errors |
155 | | */ |
156 | | virtual CHIP_ERROR AddNewOpCertsForFabric(FabricIndex fabricIndex, const ByteSpan & noc, const ByteSpan & icac) = 0; |
157 | | |
158 | | /** |
159 | | * @brief Update and temporarily activate an existing operational certificate chain for the given fabric. |
160 | | * |
161 | | * The certificate chain is temporary until committed or reverted. |
162 | | * The certificate chain is committed to storage on `CommitOpCertsForFabric`. |
163 | | * The certificate chain is reverted to prior storage if `RevertPendingOpCerts` is called |
164 | | * before `CommitOpCertsForFabric`. |
165 | | * |
166 | | * Only one pending operational certificate chain is supported at a time and it is illegal |
167 | | * to call this method if there was not already a persisted certificate chain for the given |
168 | | * fabric. |
169 | | * |
170 | | * Cryptographic signature verification or path validation are not enforced by this method. |
171 | | * |
172 | | * If `AddNewOpCertsForFabric` had been called before this method, this method will return |
173 | | * CHIP_ERROR_INCORRECT_STATE since it is illegal to update a certificate chain after |
174 | | * adding an existing NOC and before committing or reverting the addition. |
175 | | * |
176 | | * If there is no existing persisted trusted root certificate and NOC chain for the given |
177 | | * fabricIndex, this method will return CHIP_ERROR_INCORRECT_STATE since it is |
178 | | * illegal in this implementation to store an NOC chain without associated root, and it is illegal |
179 | | * to update an opcert for a fabric not already configured. |
180 | | * |
181 | | * @param fabricIndex - FabricIndex for which to update the operational certificate chain |
182 | | * @param noc - Buffer containing the new NOC certificate to use |
183 | | * @param icac - Buffer containing the ICAC certificate to use. If no ICAC is needed, `icac.empty()` must be true. |
184 | | * |
185 | | * @retval CHIP_NO_ERROR on success |
186 | | * @retval CHIP_ERROR_NO_MEMORY if there is insufficient memory to maintain the temporary `noc` and `icac` cert copies |
187 | | * @retval CHIP_ERROR_INVALID_ARGUMENT if either the noc or icac are invalid sizes |
188 | | * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized, if this method |
189 | | * is called after `AddNewOpCertsForFabric`, if there was |
190 | | * already a pending cert chain for the given `fabricIndex`, if there are |
191 | | * no associated persisted root and NOC chain for the given `fabricIndex`. |
192 | | * @retval other CHIP_ERROR value on internal errors |
193 | | */ |
194 | | virtual CHIP_ERROR UpdateOpCertsForFabric(FabricIndex fabricIndex, const ByteSpan & noc, const ByteSpan & icac) = 0; |
195 | | |
196 | | /** |
197 | | * @brief Permanently commit the certificate chain last configured via successful calls to |
198 | | * legal combinations of `AddNewTrustedRootCertForFabric`, `AddNewOpCertsForFabric` or |
199 | | * `UpdateOpCertsForFabric`, replacing previously committed data, if any. |
200 | | * |
201 | | * This is to be used when CommissioningComplete is successfully received |
202 | | * |
203 | | * @param fabricIndex - FabricIndex for which to commit the certificate chain, used for security cross-checking |
204 | | * |
205 | | * @retval CHIP_NO_ERROR on success |
206 | | * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized, |
207 | | * or if no valid pending state is available. |
208 | | * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there is no pending certificate chain for `fabricIndex` |
209 | | * @retval other CHIP_ERROR value on internal storage errors |
210 | | */ |
211 | | virtual CHIP_ERROR CommitOpCertsForFabric(FabricIndex fabricIndex) = 0; |
212 | | |
213 | | /** |
214 | | * @brief Permanently remove the certificate chain associated with a fabric. |
215 | | * |
216 | | * This is to be used for RemoveFabric. Removes both the pending operational cert chain |
217 | | * elements for the fabricIndex (if any) and the committed ones (if any). |
218 | | * |
219 | | * This must also remove any VID Verification statement elements, if they exist, since |
220 | | * those are associated with the opcerts. |
221 | | * |
222 | | * @param fabricIndex - FabricIndex for which to remove the operational cert chain |
223 | | * |
224 | | * @retval CHIP_NO_ERROR on success |
225 | | * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized. |
226 | | * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there was no operational certificate data at all for `fabricIndex` |
227 | | * @retval other CHIP_ERROR value on internal storage errors |
228 | | */ |
229 | | virtual CHIP_ERROR RemoveOpCertsForFabric(FabricIndex fabricIndex) = 0; |
230 | | |
231 | | /** |
232 | | * @brief Permanently release the operational certificate chain made via successful calls to |
233 | | * legal combinations of `AddNewTrustedRootCertForFabric`, `AddNewOpCertsForFabric` or |
234 | | * `UpdateOpCertsForFabric`, if any. |
235 | | * |
236 | | * This is to be used when a fail-safe expires prior to CommissioningComplete. |
237 | | * |
238 | | * This method cannot error-out and must always succeed, even on a no-op. This should |
239 | | * be safe to do given that `CommitOpCertsForFabric` must succeed to make an operation |
240 | | * certificate chain usable. |
241 | | */ |
242 | | virtual void RevertPendingOpCerts() = 0; |
243 | | |
244 | | /** |
245 | | * @brief Update the VidVerificationSigningCertificate (VVSC) for the given fabric, including |
246 | | * possibly removing it (if an empty vvsc buffer is provided). |
247 | | * |
248 | | * If a fabric was pending, the certificate is temporary until committed by `CommitOpCertsForFabric` |
249 | | * or reverted by `RevertPendingOpCerts`. Otherwise it is immediately commited/erased. |
250 | | * |
251 | | * Only one pending VVSC certificate is supported at a time and it is illegal |
252 | | * to call this method if there was is not already an operational certificate chain |
253 | | * pending or committed for the given fabric. |
254 | | * |
255 | | * Cryptographic signature verification or path validation are not enforced by this method. |
256 | | * |
257 | | * If there is no existing persisted or pending trusted root certificate and NOC chain for the given |
258 | | * fabricIndex, this method will return CHIP_ERROR_INCORRECT_STATE since it is |
259 | | * illegal in this implementation to store a VVSC without a fabric already there. |
260 | | * |
261 | | * @param fabricIndex - FabricIndex for which to update the VidVerificationSigningCert |
262 | | * @param vvsc - Buffer containing the VVSC payload |
263 | | * |
264 | | * @retval CHIP_NO_ERROR on success |
265 | | * @retval CHIP_ERROR_NO_MEMORY if there is insufficient memory to maintain the temporary `vvsc` cert copy |
266 | | * @retval CHIP_ERROR_INVALID_ARGUMENT if the VVSC is an invalid size |
267 | | * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized, or if there are |
268 | | * no associated root and NOC chain for the given `fabricIndex`. |
269 | | * @retval CHIP_ERROR_NOT_IMPLEMENTED if this method is not implemented (e.g. for simple controller-only cases). |
270 | | * @retval other CHIP_ERROR value on internal errors |
271 | | */ |
272 | | virtual CHIP_ERROR UpdateVidVerificationSignerCertForFabric(FabricIndex fabricIndex, ByteSpan vvsc) |
273 | 0 | { |
274 | 0 | return CHIP_ERROR_NOT_IMPLEMENTED; |
275 | 0 | } |
276 | | |
277 | | /** |
278 | | * @brief Update the VidVerificationStatement for the given fabric, including |
279 | | * possibly removing it (if an empty `vidVerificationStatement` buffer is provided). |
280 | | * |
281 | | * If a fabric was pending, the statement is temporary until committed by `CommitOpCertsForFabric` |
282 | | * or reverted by `RevertPendingOpCerts`. Otherwise it is immediately commited/erased. |
283 | | * |
284 | | * Only one pending statement is supported at a time and it is illegal |
285 | | * to call this method if there was is not already an operational certificate chain |
286 | | * pending or committed for the given fabric. |
287 | | * |
288 | | * If there is no existing persisted or pending trusted root certificate and NOC chain for the given |
289 | | * fabricIndex, this method will return CHIP_ERROR_INCORRECT_STATE since it is |
290 | | * illegal in this implementation to store a VVSC without a fabric already there. |
291 | | * |
292 | | * @param fabricIndex - FabricIndex for which to update the VidVerificationSigningCert |
293 | | * @param vidVerificationStatement - Buffer containing the VidVerificationStatement payload |
294 | | * |
295 | | * @retval CHIP_NO_ERROR on success |
296 | | * @retval CHIP_ERROR_NO_MEMORY if there is insufficient memory to maintain the temporary `vidVerificatioNStatement` copy |
297 | | * @retval CHIP_ERROR_INVALID_ARGUMENT if the vidVerificationStatement is an invalid format |
298 | | * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized, or if there are |
299 | | * no associated root and NOC chain for the given `fabricIndex`. |
300 | | * @retval CHIP_ERROR_NOT_IMPLEMENTED if this method is not implemented (e.g. for simple controller-only cases). |
301 | | * @retval other CHIP_ERROR value on internal errors |
302 | | */ |
303 | | virtual CHIP_ERROR UpdateVidVerificationStatementForFabric(FabricIndex fabricIndex, ByteSpan vidVerificationStatement) |
304 | 0 | { |
305 | 0 | return CHIP_ERROR_NOT_IMPLEMENTED; |
306 | 0 | } |
307 | | |
308 | | /** |
309 | | * @brief Same as RevertPendingOpCerts(), but leaves pending Trusted Root certs if they had |
310 | | * been added. This is is an operation to support the complex error handling of |
311 | | * AddNOC, where we don't want to have "sticking" ICAC/NOC after validation |
312 | | * problems, but don't want to lose the RCAC given in an AddTrustedRootCertificate |
313 | | * command. |
314 | | */ |
315 | | virtual void RevertPendingOpCertsExceptRoot() = 0; |
316 | | |
317 | | /** |
318 | | * @brief Get the operational certificate element requested, giving the pending data or committed |
319 | | * data depending on prior `AddNewTrustedRootCertForFabric`, `AddNewOpCertsForFabric` or |
320 | | * `UpdateOpCertsForFabric` calls. |
321 | | * |
322 | | * On success, the `outCertificate` span is resized to the size of the actual certificate read-back. |
323 | | * |
324 | | * @param fabricIndex - fabricIndex for which to get the certificate |
325 | | * @param element - which element of the cerficate chain to get |
326 | | * @param outCertificate - buffer to contain the certificate obtained from persistent or temporary storage |
327 | | * |
328 | | * @retval CHIP_NO_ERROR on success. |
329 | | * @retval CHIP_ERROR_BUFFER_TOO_SMALL if `outCertificate` is too small to fit the certificate found. |
330 | | * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized. |
331 | | * @retval CHIP_ERROR_NOT_FOUND if the element cannot be found. |
332 | | * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if the fabricIndex is invalid. |
333 | | * @retval other CHIP_ERROR value on internal storage errors. |
334 | | */ |
335 | | virtual CHIP_ERROR GetCertificate(FabricIndex fabricIndex, CertChainElement element, |
336 | | MutableByteSpan & outCertificate) const = 0; |
337 | | |
338 | | /** |
339 | | * @brief Get the VidVerification element requested, giving the pending data or committed |
340 | | * data depending on prior `UpdateVidVerificationSignerCertForFabric`, or |
341 | | * `UpdateVidVerificationStatemmentForFabric` calls. |
342 | | * |
343 | | * If element is not found, outElement is resized to 0 bytes (empty). |
344 | | * |
345 | | * On success, the `outElement` span is resized to the size of the actual element read-back. |
346 | | * |
347 | | * @param fabricIndex - fabricIndex for which to get the certificate |
348 | | * @param element - which element to get |
349 | | * @param outElement- buffer to contain the element obtained from persistent or temporary storage |
350 | | * |
351 | | * @retval CHIP_NO_ERROR on success. |
352 | | * @retval CHIP_ERROR_BUFFER_TOO_SMALL if `outElement` is too small to fit the certificate found. |
353 | | * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized. |
354 | | * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if the fabricIndex is invalid. |
355 | | * @retval CHIP_ERROR_NOT_IMPLEMENTED if this method is not implemented (e.g. for simple controller-only cases). |
356 | | * @retval other CHIP_ERROR value on internal storage errors. |
357 | | */ |
358 | | virtual CHIP_ERROR GetVidVerificationElement(FabricIndex fabricIndex, VidVerificationElement element, |
359 | | MutableByteSpan & outElement) const |
360 | 0 | { |
361 | 0 | return CHIP_ERROR_NOT_IMPLEMENTED; |
362 | 0 | } |
363 | | }; |
364 | | |
365 | | /** |
366 | | * @brief RAII class to operate on an OperationalCertificateStore with auto-revert if not committed. |
367 | | * |
368 | | * Use as: |
369 | | * |
370 | | * CHIP_ERROR FunctionWillReturnWithPendingReverted(....) |
371 | | * { |
372 | | * OpCertStoreTransaction transaction(opCertStore); |
373 | | * |
374 | | * ReturnErrorOnFailure(transaction->AddNewTrustedRootCertForFabric(...)); |
375 | | * ReturnErrorOnFailure(transaction->AddNewOpCertsForFabric(...)); |
376 | | * ReturnErrorOnFailure(transaction->CommitOpCertsForFabric(...)); |
377 | | * |
378 | | * return CHIP_NO_ERROR; |
379 | | * } |
380 | | */ |
381 | | class OpCertStoreTransaction |
382 | | { |
383 | | public: |
384 | 0 | explicit OpCertStoreTransaction(OperationalCertificateStore & store) : mStore(store) {} |
385 | | ~OpCertStoreTransaction() |
386 | 0 | { |
387 | 0 | // This is a no-op if CommitOpCertsForFabric had been called on the store |
388 | 0 | mStore.RevertPendingOpCerts(); |
389 | 0 | } |
390 | | |
391 | | // Non-copyable |
392 | | OpCertStoreTransaction(OpCertStoreTransaction const &) = delete; |
393 | | void operator=(OpCertStoreTransaction const &) = delete; |
394 | | |
395 | 0 | OperationalCertificateStore * operator->() { return &mStore; } |
396 | | |
397 | | private: |
398 | | OperationalCertificateStore & mStore; |
399 | | }; |
400 | | |
401 | | } // namespace Credentials |
402 | | } // namespace chip |