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 <crypto/CHIPCryptoPAL.h> 21 : #include <lib/core/CHIPError.h> 22 : #include <lib/core/DataModelTypes.h> 23 : #include <lib/support/Span.h> 24 : 25 : namespace chip { 26 : namespace Crypto { 27 : 28 : class OperationalKeystore 29 : { 30 : public: 31 1 : virtual ~OperationalKeystore() {} 32 : 33 : // ==== API designed for commisionables to support fail-safe (although can be used by controllers) ==== 34 : 35 : /** 36 : * @brief Returns true if a pending operational key exists from a previous 37 : * `NewOpKeypairForFabric` before any `CommitOpKeypairForFabric` or 38 : * `RevertOpKeypairForFabric`. This returns true even if the key is 39 : * NOT ACTIVE (i.e after `NewOpKeypairForFabric` but before 40 : * `ActivateOpKeypairForFabric`). 41 : */ 42 : virtual bool HasPendingOpKeypair() const = 0; 43 : 44 : /** 45 : * @brief Returns whether a usable operational key exists for the given fabric. 46 : * 47 : * Returns true even if the key is not persisted, such as if `ActivateOpKeypairForFabric` 48 : * had been successfully called for a given fabric. Only returns true if a key 49 : * is presently usable such that `SignWithOpKeypair` would succeed for the fabric. Therefore 50 : * if there was no previously persisted key and `NewOpKeypairForFabric` had been called 51 : * but not `ActivateOpKeypairForFabric`, there is only an inactive key, and this would return false. 52 : * 53 : * @param fabricIndex - FabricIndex for which availability of keypair will be checked. 54 : * @return true if there an active operational keypair for the given FabricIndex, false otherwise. 55 : */ 56 : virtual bool HasOpKeypairForFabric(FabricIndex fabricIndex) const = 0; 57 : 58 : /** 59 : * @brief This initializes a new keypair for the given fabric and generates a CSR for it, 60 : * so that it can be passed in a CSRResponse. 61 : * 62 : * The keypair is temporary and becomes usable for `SignWithOpKeypair` only after either 63 : * `ActivateOpKeypairForFabric` is called. It is destroyed if 64 : * `RevertPendingKeypair` is called before `CommitOpKeypairForFabric`. 65 : * If a pending keypair already existed for the given `fabricIndex`, it is replaced by this call. 66 : * 67 : * Only one pending operational keypair is supported at a time. 68 : * 69 : * @param fabricIndex - FabricIndex for which a new keypair must be made available 70 : * @param outCertificateSigningRequest - Buffer to contain the CSR. Must have size at least `kMIN_CSR_Buffer_Size`. 71 : * 72 : * @retval CHIP_NO_ERROR on success 73 : * @retval CHIP_ERROR_BUFFER_TOO_SMALL if `outCertificateSigningRequest` buffer is too small 74 : * @retval CHIP_ERROR_INCORRECT_STATE if the key store is not properly initialized. 75 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there is already a pending keypair for another `fabricIndex` value 76 : * or if fabricIndex is an invalid value. 77 : * @retval CHIP_ERROR_NOT_IMPLEMENTED if only `SignWithOpKeypair` is supported 78 : * @retval other CHIP_ERROR value on internal crypto engine errors 79 : */ 80 : virtual CHIP_ERROR NewOpKeypairForFabric(FabricIndex fabricIndex, MutableByteSpan & outCertificateSigningRequest) = 0; 81 : 82 : /** 83 : * @brief Temporarily activates the operational keypair last generated with `NewOpKeypairForFabric`, so 84 : * that `SignWithOpKeypair` starts using it, but only if it matches the public key associated 85 : * with the last NewOpKeypairForFabric. 86 : * 87 : * This is to be used by AddNOC and UpdateNOC so that a prior key generated by NewOpKeypairForFabric 88 : * can be used for CASE while not committing it yet to permanent storage to remain after fail-safe. 89 : * 90 : * @param fabricIndex - FabricIndex for which to activate the keypair, used for security cross-checking 91 : * @param nocPublicKey - Subject public key associated with an incoming NOC 92 : * 93 : * @retval CHIP_NO_ERROR on success 94 : * @retval CHIP_ERROR_INCORRECT_STATE if the key store is not properly initialized. 95 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there is no operational keypair for `fabricIndex` from a previous 96 : * matching `NewOpKeypairForFabric`. 97 : * @retval CHIP_ERROR_INVALID_PUBLIC_KEY if `nocPublicKey` does not match the public key associated with the 98 : * key pair from last `NewOpKeypairForFabric`. 99 : * @retval CHIP_ERROR_NOT_IMPLEMENTED if only `SignWithOpKeypair` is supported 100 : * @retval other CHIP_ERROR value on internal storage or crypto engine errors 101 : */ 102 : virtual CHIP_ERROR ActivateOpKeypairForFabric(FabricIndex fabricIndex, const Crypto::P256PublicKey & nocPublicKey) = 0; 103 : 104 : /** 105 : * @brief Permanently commit the operational keypair last generated with `NewOpKeypairForFabric`, 106 : * replacing a prior one previously committed, if any, so that `SignWithOpKeypair` for the 107 : * given `FabricIndex` permanently uses the key that was pending. 108 : * 109 : * This is to be used when CommissioningComplete is successfully received 110 : * 111 : * @param fabricIndex - FabricIndex for which to commit the keypair, used for security cross-checking 112 : * 113 : * @retval CHIP_NO_ERROR on success 114 : * @retval CHIP_ERROR_INCORRECT_STATE if the key store is not properly initialized, 115 : * or ActivateOpKeypairForFabric not yet called 116 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there is no pending operational keypair for `fabricIndex` 117 : * @retval CHIP_ERROR_NOT_IMPLEMENTED if only `SignWithOpKeypair` is supported 118 : * @retval other CHIP_ERROR value on internal storage or crypto engine errors 119 : */ 120 : virtual CHIP_ERROR CommitOpKeypairForFabric(FabricIndex fabricIndex) = 0; 121 : 122 : /** 123 : * @brief Try to read out the permanently committed operational keypair and save it to the buffer. 124 : * 125 : * @param fabricIndex - FabricIndex from which the keypair will be exported 126 : * @param outKeypair - a reference to P256SerializedKeypair object to store the exported key. 127 : * @retval CHIP_ERROR on success. 128 : * @retval CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE if the key cannot be exported due to security restrictions. 129 : * @retval CHIP_ERROR_NOT_IMPLEMENTED if the exporting is not implemented for the cryptography backend. 130 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if provided wrong value of `fabricIndex`. 131 : * @retval CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND if there is no keypair found for `fabricIndex`. 132 : * @retval CHIP_ERROR_BUFFER_TOO_SMALL if `outKeyPair` buffer is too small to store the read out keypair. 133 : * @retval other CHIP_ERROR value on internal storage or cryptography backend errors. 134 : */ 135 0 : virtual CHIP_ERROR ExportOpKeypairForFabric(FabricIndex fabricIndex, Crypto::P256SerializedKeypair & outKeypair) 136 : { 137 0 : return CHIP_ERROR_NOT_IMPLEMENTED; 138 : }; 139 : 140 : /** 141 : * @brief Migrate the operational keypair from another Operational keystore (`operationalKeystore`) implementation to this one. 142 : * 143 : * This method assumes that the operational key for given `fabricIndex` exists in the `operationalKeystore` storage or it has 144 : * been already migrated to the new Operational Storage. If a key for the given `fabricIndex` does not exist in any of those 145 : * keystores, then the method retuns CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND. 146 : * 147 : * @param fabricIndex - FabricIndex for which to migrate the operational key 148 : * @param operationalKeystore - a reference to the operationalKeystore implementation that may contain saved operational key 149 : * for Fabric 150 : * @retval CHIP_ERROR on success 151 : * @retval CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE if the key cannot be exported due to security restrictions. 152 : * @retval CHIP_ERROR_NOT_IMPLEMENTED if the exporting is not implemented for the cryptography backend. 153 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there is no keypair found for `fabricIndex`. 154 : * @retval CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND if there is no keypair found for `fabricIndex`. 155 : * @retval other CHIP_ERROR value on internal storage or crypto engine errors. 156 : */ 157 0 : virtual CHIP_ERROR MigrateOpKeypairForFabric(FabricIndex fabricIndex, OperationalKeystore & operationalKeystore) const 158 : { 159 0 : return CHIP_ERROR_NOT_IMPLEMENTED; 160 : }; 161 : 162 : /** 163 : * @brief Permanently remove the keypair associated with a fabric 164 : * 165 : * This is to be used for fail-safe handling and RemoveFabric. Removes both the 166 : * pending operational keypair for the fabricIndex (if any) and the committed one (if any). 167 : * 168 : * @param fabricIndex - FabricIndex for which to remove the keypair 169 : * 170 : * @retval CHIP_NO_ERROR on success 171 : * @retval CHIP_ERROR_INCORRECT_STATE if the key store is not properly initialized. 172 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if there is no operational keypair for `fabricIndex` 173 : * @retval CHIP_ERROR_NOT_IMPLEMENTED if only `SignWithOpKeypair` is supported 174 : * @retval other CHIP_ERROR value on internal storage errors 175 : */ 176 : virtual CHIP_ERROR RemoveOpKeypairForFabric(FabricIndex fabricIndex) = 0; 177 : 178 : /** 179 : * @brief Permanently release the operational keypair last generated with `NewOpKeypairForFabric`, 180 : * such that `SignWithOpKeypair` uses the previously committed key (if one existed). 181 : * 182 : * This is to be used when a fail-safe expires prior to CommissioningComplete. 183 : * 184 : * This method cannot error-out and must always succeed, even on a no-op. This should 185 : * be safe to do given that `CommitOpKeypairForFabric` must succeed to make a new operational 186 : * keypair usable. 187 : */ 188 : virtual void RevertPendingKeypair() = 0; 189 : 190 : // ==== Primary operation required: signature 191 : /** 192 : * @brief Whether `SignWithOpKeypair` may be performed in the background. 193 : * 194 : * If true, `CASESession` may attempt to perform `SignWithOpKeypair` in the 195 : * background. In this case, `OperationalKeystore` should protect itself, 196 : * e.g. with a mutex, as the signing could occur at any time during session 197 : * establishment. 198 : * 199 : * @retval true if `SignWithOpKeypair` may be performed in the background 200 : * @retval false if `SignWithOpKeypair` may NOT be performed in the background 201 : */ 202 0 : virtual bool SupportsSignWithOpKeypairInBackground() const { return false; } 203 : 204 : /** 205 : * @brief Sign a message with a fabric's currently-active operational keypair. 206 : * 207 : * If a Keypair was successfully made temporarily active for the given `fabricIndex` with `ActivateOpKeypairForFabric`, 208 : * then that is the keypair whose private key is used. Otherwise, the last committed private key 209 : * is used, if one exists 210 : * 211 : * @param fabricIndex - FabricIndex whose operational keypair will be used to sign the `message` 212 : * @param message - Message to sign with the currently active operational keypair 213 : * @param outSignature - Buffer to contain the signature 214 : * 215 : * @retval CHIP_NO_ERROR on success 216 : * @retval CHIP_ERROR_INCORRECT_STATE if the key store is not properly initialized. 217 : * @retval CHIP_ERROR_INVALID_FABRIC_INDEX if no active key is found for the given `fabricIndex` or if 218 : * `fabricIndex` is invalid. 219 : * @retval other CHIP_ERROR value on internal crypto engine errors 220 : */ 221 : virtual CHIP_ERROR SignWithOpKeypair(FabricIndex fabricIndex, const ByteSpan & message, 222 : Crypto::P256ECDSASignature & outSignature) const = 0; 223 : 224 : /** 225 : * @brief Create an ephemeral keypair for use in session establishment. 226 : * 227 : * The caller must Initialize() the P256Keypair if needed. It is not done by this method. 228 : * 229 : * This method MUST ONLY be used for CASESession ephemeral keys. 230 : * 231 : * NOTE: The stack will allocate as many of these as there are CASE sessions which 232 : * can be concurrently in the process of establishment. Implementations must 233 : * support more than one such keypair, or be implemented to match the limitations 234 : * enforced by a given product on its concurrent CASE session establishment limits. 235 : * 236 : * WARNING: The return value MUST be released by `ReleaseEphemeralKeypair`. This is because 237 : * Matter CHIPMem.h does not properly support UniquePtr in a way that would 238 : * safely allow classes derived from Crypto::P256Keypair to be released properly. 239 : * 240 : * @return a pointer to a P256Keypair (or derived class thereof), which may evaluate to nullptr 241 : * if running out of memory. 242 : */ 243 : virtual Crypto::P256Keypair * AllocateEphemeralKeypairForCASE() = 0; 244 : 245 : /** 246 : * @brief Release an ephemeral keypair previously provided by `AllocateEphemeralKeypairForCASE()` 247 : */ 248 : virtual void ReleaseEphemeralKeypair(Crypto::P256Keypair * keypair) = 0; 249 : }; 250 : 251 : } // namespace Crypto 252 : } // namespace chip