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 1 : virtual ~OperationalCertificateStore() {}
38 :
39 : // ==== API designed for commisionables to support fail-safe (although can be used by controllers) ====
40 :
41 : /**
42 : * @brief Returns true if a pending root certificate exists and is active from a previous
43 : * `AddNewTrustedRootCertForFabric`.
44 : */
45 : virtual bool HasPendingRootCert() const = 0;
46 :
47 : /**
48 : * @brief Returns true if a pending operational certificate chain exists and is active from a previous
49 : * `AddNewOpCertsForFabric` or `UpdateOpCertsForFabric`.
50 : */
51 : virtual bool HasPendingNocChain() const = 0;
52 :
53 : /**
54 : * @brief Returns whether a usable operational certificates chain exists for the given fabric.
55 : *
56 : * Returns true even if the certificates are not persisted yet. Only returns true if a certificate
57 : * is presently usable such that `GetCertificate` would succeed for the fabric.
58 : *
59 : * @param fabricIndex - FabricIndex for which availability of certificate will be checked.
60 : * @param element - Element of the certificate chain whose presence needs to be checked
61 : * @return true if there an active obtainable operational certificate of the given type for the given FabricIndex,
62 : * false otherwise.
63 : */
64 : virtual bool HasCertificateForFabric(FabricIndex fabricIndex, CertChainElement element) const = 0;
65 :
66 : /**
67 : * @brief Add and temporarily activate a new Trusted Root Certificate for the given fabric
68 : *
69 : * The certificate is temporary until committed or reverted.
70 : * The certificate is committed to storage only on `CommitOpCertsForFabric`.
71 : * The certificate is destroyed if `RevertPendingOpCerts` is called before `CommitOpCertsForFabric`.
72 : *
73 : * Only one pending trusted root certificate is supported at a time and it is illegal
74 : * to call this method if there is already a persisted root certificate for the given
75 : * fabric.
76 : *
77 : * Uniqueness constraints for roots (see AddTrustedRootCertificate command in spec) are not
78 : * enforced by this method and must be done as a more holistic check elsewhere. Cryptographic
79 : * signature verification or path validation are not enforced by this method.
80 : *
81 : * If `UpdateOpCertsForFabric` had been called before this method, this method will return
82 : * CHIP_ERROR_INCORRECT_STATE since it is illegal to update trusted roots when updating an
83 : * existing NOC chain.
84 : *
85 : * @param fabricIndex - FabricIndex for which a new trusted root certificate should be added
86 : * @param rcac - Buffer containing the root certificate to add.
87 : *
88 : * @retval CHIP_NO_ERROR on success
89 : * @retval CHIP_ERROR_NO_MEMORY if there is insufficient memory to maintain the temporary root cert
90 : * @retval CHIP_ERROR_INVALID_ARGUMENT if the certificate is empty or too large
91 : * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized, if this method
92 : * is called after `UpdateOpCertsForFabric`, or if there was
93 : * already a pending or persisted root certificate for the given `fabricIndex`.
94 : * @retval other CHIP_ERROR value on internal errors
95 : */
96 : virtual CHIP_ERROR AddNewTrustedRootCertForFabric(FabricIndex fabricIndex, const ByteSpan & rcac) = 0;
97 :
98 : /**
99 : * @brief Add and temporarily activate an operational certificate chain for the given fabric.
100 : *
101 : * The certificate chain is temporary until committed or reverted.
102 : * The certificate chain is committed to storage on `CommitOpCertsForFabric`.
103 : * The certificate chain is destroyed if `RevertPendingOpCerts` is called before `CommitOpCertsForFabric`.
104 : *
105 : * Only one pending operational certificate chain is supported at a time and it is illegal
106 : * to call this method if there is already a persisted certificate chain for the given
107 : * fabric.
108 : *
109 : * Cryptographic signature verification or path validation are not enforced by this method.
110 : *
111 : * If `UpdateOpCertsForFabric` had been called before this method, this method will return
112 : * CHIP_ERROR_INCORRECT_STATE since it is illegal to add a certificate chain after
113 : * updating an existing NOC and before committing or reverting the update.
114 : *
115 : * If `AddNewTrustedRootCertForFabric` had not been called before this method, this method will
116 : * return CHIP_ERROR_INCORRECT_STATE since it is illegal in this implementation to store an
117 : * NOC chain without associated root.
118 : *
119 : * NOTE: The Matter spec allows AddNOC without AddTrustedRootCertificate if the NOC
120 : * chains to an existing root, to support root reuse. In this implementation, we expect each
121 : * fabric to store the root with the rest of the chain. Because of this, callers must ensure
122 : * that if an AddNOC command is done and no trusted root was added, that the requisite existing
123 : * root be "copied over" to match.
124 : *
125 : * @param fabricIndex - FabricIndex for which to add a new operational certificate chain
126 : * @param noc - Buffer containing the NOC certificate to add
127 : * @param icac - Buffer containing the ICAC certificate to add. If no ICAC is needed, `icac.empty()` must be true.
128 : *
129 : * @retval CHIP_NO_ERROR on success
130 : * @retval CHIP_ERROR_NO_MEMORY if there is insufficient memory to maintain the temporary `noc` and `icac` cert copies
131 : * @retval CHIP_ERROR_INVALID_ARGUMENT if either the noc or icac are invalid sizes
132 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if `fabricIndex` mismatches the one from a previous successful
133 : * `AddNewTrustedRootCertForFabric`.
134 : * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized, if this method
135 : * is called after `UpdateOpCertsForFabric`, or if there was
136 : * already a pending or persisted operational cert chain for the given `fabricIndex`,
137 : * or if AddNewTrustedRootCertForFabric had not yet been called for the given `fabricIndex`.
138 : * @retval other CHIP_ERROR value on internal errors
139 : */
140 : virtual CHIP_ERROR AddNewOpCertsForFabric(FabricIndex fabricIndex, const ByteSpan & noc, const ByteSpan & icac) = 0;
141 :
142 : /**
143 : * @brief Update and temporarily activate an existing operational certificate chain for the given fabric.
144 : *
145 : * The certificate chain is temporary until committed or reverted.
146 : * The certificate chain is committed to storage on `CommitOpCertsForFabric`.
147 : * The certificate chain is reverted to prior storage if `RevertPendingOpCerts` is called
148 : * before `CommitOpCertsForFabric`.
149 : *
150 : * Only one pending operational certificate chain is supported at a time and it is illegal
151 : * to call this method if there was not already a persisted certificate chain for the given
152 : * fabric.
153 : *
154 : * Cryptographic signature verification or path validation are not enforced by this method.
155 : *
156 : * If `AddNewOpCertsForFabric` had been called before this method, this method will return
157 : * CHIP_ERROR_INCORRECT_STATE since it is illegal to update a certificate chain after
158 : * adding an existing NOC and before committing or reverting the addition.
159 : *
160 : * If there is no existing persisted trusted root certificate and NOC chain for the given
161 : * fabricIndex, this method will return CHIP_ERROR_INCORRECT_STATE since it is
162 : * illegal in this implementation to store an NOC chain without associated root, and it is illegal
163 : * to update an opcert for a fabric not already configured.
164 : *
165 : * @param fabricIndex - FabricIndex for which to update the operational certificate chain
166 : * @param noc - Buffer containing the new NOC certificate to use
167 : * @param icac - Buffer containing the ICAC certificate to use. If no ICAC is needed, `icac.empty()` must be true.
168 : *
169 : * @retval CHIP_NO_ERROR on success
170 : * @retval CHIP_ERROR_NO_MEMORY if there is insufficient memory to maintain the temporary `noc` and `icac` cert copies
171 : * @retval CHIP_ERROR_INVALID_ARGUMENT if either the noc or icac are invalid sizes
172 : * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized, if this method
173 : * is called after `AddNewOpCertsForFabric`, or if there was
174 : * already a pending cert chain for the given `fabricIndex`, or if there are
175 : * no associated persisted root and NOC chain for for the given `fabricIndex`.
176 : * @retval other CHIP_ERROR value on internal errors
177 : */
178 : virtual CHIP_ERROR UpdateOpCertsForFabric(FabricIndex fabricIndex, const ByteSpan & noc, const ByteSpan & icac) = 0;
179 :
180 : /**
181 : * @brief Permanently commit the certificate chain last configured via successful calls to
182 : * legal combinations of `AddNewTrustedRootCertForFabric`, `AddNewOpCertsForFabric` or
183 : * `UpdateOpCertsForFabric`, replacing previously committed data, if any.
184 : *
185 : * This is to be used when CommissioningComplete is successfully received
186 : *
187 : * @param fabricIndex - FabricIndex for which to commit the certificate chain, used for security cross-checking
188 : *
189 : * @retval CHIP_NO_ERROR on success
190 : * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized,
191 : * or if no valid pending state is available.
192 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there is no pending certificate chain for `fabricIndex`
193 : * @retval other CHIP_ERROR value on internal storage errors
194 : */
195 : virtual CHIP_ERROR CommitOpCertsForFabric(FabricIndex fabricIndex) = 0;
196 :
197 : /**
198 : * @brief Permanently remove the certificate chain associated with a fabric.
199 : *
200 : * This is to be used for RemoveFabric. Removes both the pending operational cert chain
201 : * elements for the fabricIndex (if any) and the committed ones (if any).
202 : *
203 : * @param fabricIndex - FabricIndex for which to remove the operational cert chain
204 : *
205 : * @retval CHIP_NO_ERROR on success
206 : * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized.
207 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there was no operational certificate data at all for `fabricIndex`
208 : * @retval other CHIP_ERROR value on internal storage errors
209 : */
210 : virtual CHIP_ERROR RemoveOpCertsForFabric(FabricIndex fabricIndex) = 0;
211 :
212 : /**
213 : * @brief Permanently release the operational certificate chain made via successful calls to
214 : * legal combinations of `AddNewTrustedRootCertForFabric`, `AddNewOpCertsForFabric` or
215 : * `UpdateOpCertsForFabric`, if any.
216 : *
217 : * This is to be used when a fail-safe expires prior to CommissioningComplete.
218 : *
219 : * This method cannot error-out and must always succeed, even on a no-op. This should
220 : * be safe to do given that `CommitOpCertsForFabric` must succeed to make an operation
221 : * certificate chain usable.
222 : */
223 : virtual void RevertPendingOpCerts() = 0;
224 :
225 : /**
226 : * @brief Same as RevertPendingOpCerts(), but leaves pending Trusted Root certs if they had
227 : * been added. This is is an operation to support the complex error handling of
228 : * AddNOC, where we don't want to have "sticking" ICAC/NOC after validation
229 : * problems, but don't want to lose the RCAC given in an AddTrustedRootCertificate
230 : * command.
231 : */
232 : virtual void RevertPendingOpCertsExceptRoot() = 0;
233 :
234 : /**
235 : * @brief Get the operational certificate element requested, giving the pending data or committed
236 : * data depending on prior `AddNewTrustedRootCertForFabric`, `AddNewOpCertsForFabric` or
237 : * `UpdateOpCertsForFabric` calls.
238 : *
239 : * On success, the `outCertificate` span is resized to the size of the actual certificate read-back.
240 : *
241 : * @param fabricIndex - fabricIndex for which to get the certificate
242 : * @param element - which element of the cerficate chain to get
243 : * @param outCertificate - buffer to contain the certificate obtained from persistent or temporary storage
244 : *
245 : * @retval CHIP_NO_ERROR on success.
246 : * @retval CHIP_ERROR_BUFFER_TOO_SMALL if `outCertificate` is too small to fit the certificate found.
247 : * @retval CHIP_ERROR_INCORRECT_STATE if the certificate store is not properly initialized.
248 : * @retval CHIP_ERROR_NOT_FOUND if the element cannot be found.
249 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if the fabricIndex is invalid.
250 : * @retval other CHIP_ERROR value on internal storage errors.
251 : */
252 : virtual CHIP_ERROR GetCertificate(FabricIndex fabricIndex, CertChainElement element,
253 : MutableByteSpan & outCertificate) const = 0;
254 : };
255 :
256 : /**
257 : * @brief RAII class to operate on an OperationalCertificateStore with auto-revert if not committed.
258 : *
259 : * Use as:
260 : *
261 : * CHIP_ERROR FunctionWillReturnWithPendingReverted(....)
262 : * {
263 : * OpCertStoreTransaction transaction(opCertStore);
264 : *
265 : * ReturnErrorOnFailure(transaction->AddNewTrustedRootCertForFabric(...));
266 : * ReturnErrorOnFailure(transaction->AddNewOpCertsForFabric(...));
267 : * ReturnErrorOnFailure(transaction->CommitOpCertsForFabric(...));
268 : *
269 : * return CHIP_NO_ERROR;
270 : * }
271 : */
272 : class OpCertStoreTransaction
273 : {
274 : public:
275 : explicit OpCertStoreTransaction(OperationalCertificateStore & store) : mStore(store) {}
276 : ~OpCertStoreTransaction()
277 : {
278 : // This is a no-op if CommitOpCertsForFabric had been called on the store
279 : mStore.RevertPendingOpCerts();
280 : }
281 :
282 : // Non-copyable
283 : OpCertStoreTransaction(OpCertStoreTransaction const &) = delete;
284 : void operator=(OpCertStoreTransaction const &) = delete;
285 :
286 : OperationalCertificateStore * operator->() { return &mStore; }
287 :
288 : private:
289 : OperationalCertificateStore & mStore;
290 : };
291 :
292 : } // namespace Credentials
293 : } // namespace chip
|