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 2 : 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 : * or if a VVSC is present and `icac` is not empty.
193 : * @retval other CHIP_ERROR value on internal errors
194 : */
195 : virtual CHIP_ERROR UpdateOpCertsForFabric(FabricIndex fabricIndex, const ByteSpan & noc, const ByteSpan & icac) = 0;
196 :
197 : /**
198 : * @brief Permanently commit the certificate chain last configured via successful calls to
199 : * legal combinations of `AddNewTrustedRootCertForFabric`, `AddNewOpCertsForFabric` or
200 : * `UpdateOpCertsForFabric`, replacing previously committed data, if any.
201 : *
202 : * This is to be used when CommissioningComplete is successfully received
203 : *
204 : * @param fabricIndex - FabricIndex for which to commit the certificate chain, used for security cross-checking
205 : *
206 : * @retval CHIP_NO_ERROR on success
207 : * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized,
208 : * or if no valid pending state is available.
209 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there is no pending certificate chain for `fabricIndex`
210 : * @retval other CHIP_ERROR value on internal storage errors
211 : */
212 : virtual CHIP_ERROR CommitOpCertsForFabric(FabricIndex fabricIndex) = 0;
213 :
214 : /**
215 : * @brief Permanently remove the certificate chain associated with a fabric.
216 : *
217 : * This is to be used for RemoveFabric. Removes both the pending operational cert chain
218 : * elements for the fabricIndex (if any) and the committed ones (if any).
219 : *
220 : * This must also remove any VID Verification statement elements, if they exist, since
221 : * those are associated with the opcerts.
222 : *
223 : * @param fabricIndex - FabricIndex for which to remove the operational cert chain
224 : *
225 : * @retval CHIP_NO_ERROR on success
226 : * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized.
227 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there was no operational certificate data at all for `fabricIndex`
228 : * @retval other CHIP_ERROR value on internal storage errors
229 : */
230 : virtual CHIP_ERROR RemoveOpCertsForFabric(FabricIndex fabricIndex) = 0;
231 :
232 : /**
233 : * @brief Permanently release the operational certificate chain made via successful calls to
234 : * legal combinations of `AddNewTrustedRootCertForFabric`, `AddNewOpCertsForFabric` or
235 : * `UpdateOpCertsForFabric`, if any.
236 : *
237 : * This is to be used when a fail-safe expires prior to CommissioningComplete.
238 : *
239 : * This method cannot error-out and must always succeed, even on a no-op. This should
240 : * be safe to do given that `CommitOpCertsForFabric` must succeed to make an operation
241 : * certificate chain usable.
242 : */
243 : virtual void RevertPendingOpCerts() = 0;
244 :
245 : /**
246 : * @brief Update the VidVerificationSigningCertificate (VVSC) for the given fabric, including
247 : * possibly removing it (if an empty vvsc buffer is provided).
248 : *
249 : * If a fabric was pending, the certificate is temporary until committed by `CommitOpCertsForFabric`
250 : * or reverted by `RevertPendingOpCerts`. Otherwise it is immediately commited/erased.
251 : *
252 : * Only one pending VVSC certificate is supported at a time and it is illegal
253 : * to call this method if there was is not already an operational certificate chain
254 : * pending or committed for the given fabric.
255 : *
256 : * Cryptographic signature verification or path validation are not enforced by this method.
257 : *
258 : * If there is no existing persisted or pending trusted root certificate and NOC chain for the given
259 : * fabricIndex, this method will return CHIP_ERROR_INCORRECT_STATE since it is
260 : * illegal in this implementation to store a VVSC without a fabric already there.
261 : *
262 : * @param fabricIndex - FabricIndex for which to update the VidVerificationSigningCert
263 : * @param vvsc - Buffer containing the VVSC payload
264 : *
265 : * @retval CHIP_NO_ERROR on success
266 : * @retval CHIP_ERROR_NO_MEMORY if there is insufficient memory to maintain the temporary `vvsc` cert copy
267 : * @retval CHIP_ERROR_INVALID_ARGUMENT if the VVSC is an invalid size
268 : * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized, or if there are
269 : * no associated root and NOC chain for the given `fabricIndex`.
270 : * @retval CHIP_ERROR_NOT_IMPLEMENTED if this method is not implemented (e.g. for simple controller-only cases).
271 : * @retval other CHIP_ERROR value on internal errors
272 : */
273 0 : virtual CHIP_ERROR UpdateVidVerificationSignerCertForFabric(FabricIndex fabricIndex, ByteSpan vvsc)
274 : {
275 0 : return CHIP_ERROR_NOT_IMPLEMENTED;
276 : }
277 :
278 : /**
279 : * @brief Update the VidVerificationStatement for the given fabric, including
280 : * possibly removing it (if an empty `vidVerificationStatement` buffer is provided).
281 : *
282 : * If a fabric was pending, the statement is temporary until committed by `CommitOpCertsForFabric`
283 : * or reverted by `RevertPendingOpCerts`. Otherwise it is immediately commited/erased.
284 : *
285 : * Only one pending statement is supported at a time and it is illegal
286 : * to call this method if there was is not already an operational certificate chain
287 : * pending or committed for the given fabric.
288 : *
289 : * If there is no existing persisted or pending trusted root certificate and NOC chain for the given
290 : * fabricIndex, this method will return CHIP_ERROR_INCORRECT_STATE since it is
291 : * illegal in this implementation to store a VVSC without a fabric already there.
292 : *
293 : * @param fabricIndex - FabricIndex for which to update the VidVerificationSigningCert
294 : * @param vidVerificationStatement - Buffer containing the VidVerificationStatement payload
295 : *
296 : * @retval CHIP_NO_ERROR on success
297 : * @retval CHIP_ERROR_NO_MEMORY if there is insufficient memory to maintain the temporary `vidVerificatioNStatement` copy
298 : * @retval CHIP_ERROR_INVALID_ARGUMENT if the vidVerificationStatement is an invalid format
299 : * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized, or if there are
300 : * no associated root and NOC chain for the given `fabricIndex`.
301 : * @retval CHIP_ERROR_NOT_IMPLEMENTED if this method is not implemented (e.g. for simple controller-only cases).
302 : * @retval other CHIP_ERROR value on internal errors
303 : */
304 0 : virtual CHIP_ERROR UpdateVidVerificationStatementForFabric(FabricIndex fabricIndex, ByteSpan vidVerificationStatement)
305 : {
306 0 : return CHIP_ERROR_NOT_IMPLEMENTED;
307 : }
308 :
309 : /**
310 : * @brief Same as RevertPendingOpCerts(), but leaves pending Trusted Root certs if they had
311 : * been added. This is is an operation to support the complex error handling of
312 : * AddNOC, where we don't want to have "sticking" ICAC/NOC after validation
313 : * problems, but don't want to lose the RCAC given in an AddTrustedRootCertificate
314 : * command.
315 : */
316 : virtual void RevertPendingOpCertsExceptRoot() = 0;
317 :
318 : /**
319 : * @brief Get the operational certificate element requested, giving the pending data or committed
320 : * data depending on prior `AddNewTrustedRootCertForFabric`, `AddNewOpCertsForFabric` or
321 : * `UpdateOpCertsForFabric` calls.
322 : *
323 : * On success, the `outCertificate` span is resized to the size of the actual certificate read-back.
324 : *
325 : * @param fabricIndex - fabricIndex for which to get the certificate
326 : * @param element - which element of the cerficate chain to get
327 : * @param outCertificate - buffer to contain the certificate obtained from persistent or temporary storage
328 : *
329 : * @retval CHIP_NO_ERROR on success.
330 : * @retval CHIP_ERROR_BUFFER_TOO_SMALL if `outCertificate` is too small to fit the certificate found.
331 : * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized.
332 : * @retval CHIP_ERROR_NOT_FOUND if the element cannot be found.
333 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if the fabricIndex is invalid.
334 : * @retval other CHIP_ERROR value on internal storage errors.
335 : */
336 : virtual CHIP_ERROR GetCertificate(FabricIndex fabricIndex, CertChainElement element,
337 : MutableByteSpan & outCertificate) const = 0;
338 :
339 : /**
340 : * @brief Get the VidVerification element requested, giving the pending data or committed
341 : * data depending on prior `UpdateVidVerificationSignerCertForFabric`, or
342 : * `UpdateVidVerificationStatemmentForFabric` calls.
343 : *
344 : * If element is not found, outElement is resized to 0 bytes (empty).
345 : *
346 : * On success, the `outElement` span is resized to the size of the actual element read-back.
347 : *
348 : * @param fabricIndex - fabricIndex for which to get the certificate
349 : * @param element - which element to get
350 : * @param outElement- buffer to contain the element obtained from persistent or temporary storage
351 : *
352 : * @retval CHIP_NO_ERROR on success.
353 : * @retval CHIP_ERROR_BUFFER_TOO_SMALL if `outElement` is too small to fit the certificate found.
354 : * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized.
355 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if the fabricIndex is invalid.
356 : * @retval CHIP_ERROR_NOT_IMPLEMENTED if this method is not implemented (e.g. for simple controller-only cases).
357 : * @retval other CHIP_ERROR value on internal storage errors.
358 : */
359 0 : virtual CHIP_ERROR GetVidVerificationElement(FabricIndex fabricIndex, VidVerificationElement element,
360 : MutableByteSpan & outElement) const
361 : {
362 0 : return CHIP_ERROR_NOT_IMPLEMENTED;
363 : }
364 : };
365 :
366 : /**
367 : * @brief RAII class to operate on an OperationalCertificateStore with auto-revert if not committed.
368 : *
369 : * Use as:
370 : *
371 : * CHIP_ERROR FunctionWillReturnWithPendingReverted(....)
372 : * {
373 : * OpCertStoreTransaction transaction(opCertStore);
374 : *
375 : * ReturnErrorOnFailure(transaction->AddNewTrustedRootCertForFabric(...));
376 : * ReturnErrorOnFailure(transaction->AddNewOpCertsForFabric(...));
377 : * ReturnErrorOnFailure(transaction->CommitOpCertsForFabric(...));
378 : *
379 : * return CHIP_NO_ERROR;
380 : * }
381 : */
382 : class OpCertStoreTransaction
383 : {
384 : public:
385 : explicit OpCertStoreTransaction(OperationalCertificateStore & store) : mStore(store) {}
386 : ~OpCertStoreTransaction()
387 : {
388 : // This is a no-op if CommitOpCertsForFabric had been called on the store
389 : mStore.RevertPendingOpCerts();
390 : }
391 :
392 : // Non-copyable
393 : OpCertStoreTransaction(OpCertStoreTransaction const &) = delete;
394 : void operator=(OpCertStoreTransaction const &) = delete;
395 :
396 : OperationalCertificateStore * operator->() { return &mStore; }
397 :
398 : private:
399 : OperationalCertificateStore & mStore;
400 : };
401 :
402 : } // namespace Credentials
403 : } // namespace chip
|