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 <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 6 : 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 0 : virtual bool HasPendingVidVerificationElements() const
63 : {
64 : // Default false to match the CHIP_ERROR_NOT_IMPLEMENTED for default versions in this base class.
65 0 : return false;
66 : }
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 0 : virtual CHIP_ERROR UpdateVidVerificationSignerCertForFabric(FabricIndex fabricIndex, ByteSpan vvsc)
273 : {
274 0 : return CHIP_ERROR_NOT_IMPLEMENTED;
275 : }
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 0 : virtual CHIP_ERROR UpdateVidVerificationStatementForFabric(FabricIndex fabricIndex, ByteSpan vidVerificationStatement)
304 : {
305 0 : return CHIP_ERROR_NOT_IMPLEMENTED;
306 : }
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 0 : virtual CHIP_ERROR GetVidVerificationElement(FabricIndex fabricIndex, VidVerificationElement element,
359 : MutableByteSpan & outElement) const
360 : {
361 0 : return CHIP_ERROR_NOT_IMPLEMENTED;
362 : }
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 : explicit OpCertStoreTransaction(OperationalCertificateStore & store) : mStore(store) {}
385 : ~OpCertStoreTransaction()
386 : {
387 : // This is a no-op if CommitOpCertsForFabric had been called on the store
388 : mStore.RevertPendingOpCerts();
389 : }
390 :
391 : // Non-copyable
392 : OpCertStoreTransaction(OpCertStoreTransaction const &) = delete;
393 : void operator=(OpCertStoreTransaction const &) = delete;
394 :
395 : OperationalCertificateStore * operator->() { return &mStore; }
396 :
397 : private:
398 : OperationalCertificateStore & mStore;
399 : };
400 :
401 : } // namespace Credentials
402 : } // namespace chip
|