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