|
| 1 | +/* |
| 2 | + * Copyright (c) 2023 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 | +#include "PSAOperationalKeystore.h" |
| 19 | +#include "PersistentStorageOperationalKeystore.h" |
| 20 | + |
| 21 | +#include <lib/support/CHIPMem.h> |
| 22 | + |
| 23 | +#include <psa/crypto.h> |
| 24 | + |
| 25 | +#ifdef CHIP_CONFIG_CRYPTO_PSA_KMU |
| 26 | +#include <cracen_psa_kmu.h> |
| 27 | +#endif |
| 28 | + |
| 29 | +namespace chip { |
| 30 | +namespace Crypto { |
| 31 | + |
| 32 | +PSAOperationalKeystore::PersistentP256Keypair::PersistentP256Keypair(FabricIndex fabricIndex) |
| 33 | +{ |
| 34 | + ToPsaContext(mKeypair).key_id = MakeOperationalKeyId(fabricIndex); |
| 35 | + mInitialized = true; |
| 36 | +} |
| 37 | + |
| 38 | +PSAOperationalKeystore::PersistentP256Keypair::~PersistentP256Keypair() |
| 39 | +{ |
| 40 | + // This class requires explicit control of the key lifetime. Therefore, clear the key ID |
| 41 | + // to prevent it from being destroyed by the base class destructor. |
| 42 | + ToPsaContext(mKeypair).key_id = 0; |
| 43 | +} |
| 44 | + |
| 45 | +inline psa_key_id_t PSAOperationalKeystore::PersistentP256Keypair::GetKeyId() const |
| 46 | +{ |
| 47 | + return ToConstPsaContext(mKeypair).key_id; |
| 48 | +} |
| 49 | + |
| 50 | +bool PSAOperationalKeystore::PersistentP256Keypair::Exists() const |
| 51 | +{ |
| 52 | + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; |
| 53 | + psa_status_t status = psa_get_key_attributes(GetKeyId(), &attributes); |
| 54 | + |
| 55 | + psa_reset_key_attributes(&attributes); |
| 56 | + |
| 57 | + return status == PSA_SUCCESS; |
| 58 | +} |
| 59 | + |
| 60 | +CHIP_ERROR PSAOperationalKeystore::PersistentP256Keypair::Generate() |
| 61 | +{ |
| 62 | + CHIP_ERROR error = CHIP_NO_ERROR; |
| 63 | + psa_status_t status = PSA_SUCCESS; |
| 64 | + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; |
| 65 | + psa_key_id_t keyId = 0; |
| 66 | + size_t publicKeyLength; |
| 67 | + |
| 68 | + Destroy(); |
| 69 | + |
| 70 | + // Type based on ECC with the elliptic curve SECP256r1 -> PSA_ECC_FAMILY_SECP_R1 |
| 71 | + psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); |
| 72 | + psa_set_key_bits(&attributes, kP256_PrivateKey_Length * 8); |
| 73 | + psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); |
| 74 | + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_MESSAGE); |
| 75 | + psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_PERSISTENT); |
| 76 | + psa_set_key_id(&attributes, GetKeyId()); |
| 77 | + |
| 78 | + status = psa_generate_key(&attributes, &keyId); |
| 79 | + VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); |
| 80 | + |
| 81 | + status = psa_export_public_key(keyId, mPublicKey.Bytes(), mPublicKey.Length(), &publicKeyLength); |
| 82 | + VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); |
| 83 | + VerifyOrExit(publicKeyLength == kP256_PublicKey_Length, error = CHIP_ERROR_INTERNAL); |
| 84 | + |
| 85 | +exit: |
| 86 | + psa_reset_key_attributes(&attributes); |
| 87 | + |
| 88 | + return error; |
| 89 | +} |
| 90 | + |
| 91 | +CHIP_ERROR PSAOperationalKeystore::PersistentP256Keypair::Destroy() |
| 92 | +{ |
| 93 | + psa_status_t status = psa_destroy_key(GetKeyId()); |
| 94 | + |
| 95 | + ReturnErrorCodeIf(status == PSA_ERROR_INVALID_HANDLE, CHIP_ERROR_INVALID_FABRIC_INDEX); |
| 96 | + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); |
| 97 | + |
| 98 | + return CHIP_NO_ERROR; |
| 99 | +} |
| 100 | + |
| 101 | +bool PSAOperationalKeystore::HasPendingOpKeypair() const |
| 102 | +{ |
| 103 | + return mPendingFabricIndex != kUndefinedFabricIndex; |
| 104 | +} |
| 105 | + |
| 106 | +bool PSAOperationalKeystore::HasOpKeypairForFabric(FabricIndex fabricIndex) const |
| 107 | +{ |
| 108 | + VerifyOrReturnError(IsValidFabricIndex(fabricIndex), false); |
| 109 | + |
| 110 | + if (mPendingFabricIndex == fabricIndex) |
| 111 | + { |
| 112 | + return mIsPendingKeypairActive; |
| 113 | + } |
| 114 | + |
| 115 | + return PersistentP256Keypair(fabricIndex).Exists(); |
| 116 | +} |
| 117 | + |
| 118 | +CHIP_ERROR PSAOperationalKeystore::NewOpKeypairForFabric(FabricIndex fabricIndex, MutableByteSpan & outCertificateSigningRequest) |
| 119 | +{ |
| 120 | + VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX); |
| 121 | + |
| 122 | + if (HasPendingOpKeypair()) |
| 123 | + { |
| 124 | + VerifyOrReturnError(fabricIndex == mPendingFabricIndex, CHIP_ERROR_INVALID_FABRIC_INDEX); |
| 125 | + } |
| 126 | + |
| 127 | + if (mPendingKeypair == nullptr) |
| 128 | + { |
| 129 | + mPendingKeypair = Platform::New<PersistentP256Keypair>(fabricIndex); |
| 130 | + } |
| 131 | + |
| 132 | + VerifyOrReturnError(mPendingKeypair != nullptr, CHIP_ERROR_NO_MEMORY); |
| 133 | + ReturnErrorOnFailure(mPendingKeypair->Generate()); |
| 134 | + |
| 135 | + size_t csrLength = outCertificateSigningRequest.size(); |
| 136 | + ReturnErrorOnFailure(mPendingKeypair->NewCertificateSigningRequest(outCertificateSigningRequest.data(), csrLength)); |
| 137 | + outCertificateSigningRequest.reduce_size(csrLength); |
| 138 | + mPendingFabricIndex = fabricIndex; |
| 139 | + |
| 140 | + return CHIP_NO_ERROR; |
| 141 | +} |
| 142 | + |
| 143 | +CHIP_ERROR PSAOperationalKeystore::PersistentP256Keypair::Deserialize(P256SerializedKeypair & input) |
| 144 | +{ |
| 145 | + CHIP_ERROR error = CHIP_NO_ERROR; |
| 146 | + psa_status_t status = PSA_SUCCESS; |
| 147 | + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; |
| 148 | + psa_key_id_t keyId = 0; |
| 149 | + VerifyOrReturnError(input.Length() == mPublicKey.Length() + kP256_PrivateKey_Length, CHIP_ERROR_INVALID_ARGUMENT); |
| 150 | + |
| 151 | + Destroy(); |
| 152 | + |
| 153 | + // Type based on ECC with the elliptic curve SECP256r1 -> PSA_ECC_FAMILY_SECP_R1 |
| 154 | + psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); |
| 155 | + psa_set_key_bits(&attributes, kP256_PrivateKey_Length * 8); |
| 156 | + psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); |
| 157 | + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_MESSAGE); |
| 158 | + psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_PERSISTENT); |
| 159 | + psa_set_key_id(&attributes, GetKeyId()); |
| 160 | + |
| 161 | + status = psa_import_key(&attributes, input.ConstBytes() + mPublicKey.Length(), kP256_PrivateKey_Length, &keyId); |
| 162 | + VerifyOrExit(status == PSA_SUCCESS, error = CHIP_ERROR_INTERNAL); |
| 163 | + |
| 164 | + memcpy(mPublicKey.Bytes(), input.ConstBytes(), mPublicKey.Length()); |
| 165 | + |
| 166 | +exit: |
| 167 | + psa_reset_key_attributes(&attributes); |
| 168 | + |
| 169 | + return error; |
| 170 | +} |
| 171 | + |
| 172 | +CHIP_ERROR PSAOperationalKeystore::ActivateOpKeypairForFabric(FabricIndex fabricIndex, const Crypto::P256PublicKey & nocPublicKey) |
| 173 | +{ |
| 174 | + VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && mPendingFabricIndex == fabricIndex, CHIP_ERROR_INVALID_FABRIC_INDEX); |
| 175 | + VerifyOrReturnError(mPendingKeypair->Pubkey().Matches(nocPublicKey), CHIP_ERROR_INVALID_PUBLIC_KEY); |
| 176 | + mIsPendingKeypairActive = true; |
| 177 | + |
| 178 | + return CHIP_NO_ERROR; |
| 179 | +} |
| 180 | + |
| 181 | +CHIP_ERROR PSAOperationalKeystore::CommitOpKeypairForFabric(FabricIndex fabricIndex) |
| 182 | +{ |
| 183 | + VerifyOrReturnError(IsValidFabricIndex(fabricIndex) && mPendingFabricIndex == fabricIndex, CHIP_ERROR_INVALID_FABRIC_INDEX); |
| 184 | + VerifyOrReturnError(mIsPendingKeypairActive, CHIP_ERROR_INCORRECT_STATE); |
| 185 | + |
| 186 | + ReleasePendingKeypair(); |
| 187 | + |
| 188 | + return CHIP_NO_ERROR; |
| 189 | +} |
| 190 | + |
| 191 | +CHIP_ERROR PSAOperationalKeystore::ExportOpKeypairForFabric(FabricIndex fabricIndex, Crypto::P256SerializedKeypair & outKeypair) |
| 192 | +{ |
| 193 | + // Currently exporting the key is forbidden in PSAOperationalKeystore because the PSA_KEY_USAGE_EXPORT usage flag is not set, so |
| 194 | + // there is no need to compile the code for the device, but there should be an implementation for test purposes to verify if |
| 195 | + // the psa_export_key returns an error. |
| 196 | +#if CHIP_CONFIG_TEST |
| 197 | + VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX); |
| 198 | + VerifyOrReturnError(HasOpKeypairForFabric(fabricIndex), CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND); |
| 199 | + |
| 200 | + size_t outSize = 0; |
| 201 | + psa_status_t status = |
| 202 | + psa_export_key(PersistentP256Keypair(fabricIndex).GetKeyId(), outKeypair.Bytes(), outKeypair.Capacity(), &outSize); |
| 203 | + |
| 204 | + if (status == PSA_ERROR_BUFFER_TOO_SMALL) |
| 205 | + { |
| 206 | + return CHIP_ERROR_BUFFER_TOO_SMALL; |
| 207 | + } |
| 208 | + else if (status == PSA_ERROR_NOT_PERMITTED) |
| 209 | + { |
| 210 | + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; |
| 211 | + } |
| 212 | + else if (status != PSA_SUCCESS) |
| 213 | + { |
| 214 | + return CHIP_ERROR_INTERNAL; |
| 215 | + } |
| 216 | + |
| 217 | + outKeypair.SetLength(outSize); |
| 218 | + |
| 219 | + return CHIP_NO_ERROR; |
| 220 | +#else |
| 221 | + return CHIP_ERROR_NOT_IMPLEMENTED; |
| 222 | +#endif |
| 223 | +} |
| 224 | + |
| 225 | +CHIP_ERROR PSAOperationalKeystore::RemoveOpKeypairForFabric(FabricIndex fabricIndex) |
| 226 | +{ |
| 227 | + VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX); |
| 228 | + |
| 229 | + if (mPendingFabricIndex == fabricIndex) |
| 230 | + { |
| 231 | + RevertPendingKeypair(); |
| 232 | + return CHIP_NO_ERROR; |
| 233 | + } |
| 234 | + |
| 235 | + return PersistentP256Keypair(fabricIndex).Destroy(); |
| 236 | +} |
| 237 | + |
| 238 | +void PSAOperationalKeystore::RevertPendingKeypair() |
| 239 | +{ |
| 240 | + VerifyOrReturn(HasPendingOpKeypair()); |
| 241 | + mPendingKeypair->Destroy(); |
| 242 | + ReleasePendingKeypair(); |
| 243 | +} |
| 244 | + |
| 245 | +CHIP_ERROR PSAOperationalKeystore::SignWithOpKeypair(FabricIndex fabricIndex, const ByteSpan & message, |
| 246 | + Crypto::P256ECDSASignature & outSignature) const |
| 247 | +{ |
| 248 | + VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX); |
| 249 | + |
| 250 | + if (mPendingFabricIndex == fabricIndex) |
| 251 | + { |
| 252 | + VerifyOrReturnError(mIsPendingKeypairActive, CHIP_ERROR_INVALID_FABRIC_INDEX); |
| 253 | + return mPendingKeypair->ECDSA_sign_msg(message.data(), message.size(), outSignature); |
| 254 | + } |
| 255 | + |
| 256 | + PersistentP256Keypair keypair(fabricIndex); |
| 257 | + VerifyOrReturnError(keypair.Exists(), CHIP_ERROR_INVALID_FABRIC_INDEX); |
| 258 | + |
| 259 | + return keypair.ECDSA_sign_msg(message.data(), message.size(), outSignature); |
| 260 | +} |
| 261 | + |
| 262 | +Crypto::P256Keypair * PSAOperationalKeystore::AllocateEphemeralKeypairForCASE() |
| 263 | +{ |
| 264 | + return Platform::New<Crypto::P256Keypair>(); |
| 265 | +} |
| 266 | + |
| 267 | +void PSAOperationalKeystore::ReleaseEphemeralKeypair(Crypto::P256Keypair * keypair) |
| 268 | +{ |
| 269 | + Platform::Delete(keypair); |
| 270 | +} |
| 271 | + |
| 272 | +void PSAOperationalKeystore::ReleasePendingKeypair() |
| 273 | +{ |
| 274 | + Platform::Delete(mPendingKeypair); |
| 275 | + mPendingKeypair = nullptr; |
| 276 | + mPendingFabricIndex = kUndefinedFabricIndex; |
| 277 | + mIsPendingKeypairActive = false; |
| 278 | +} |
| 279 | + |
| 280 | +CHIP_ERROR PSAOperationalKeystore::MigrateOpKeypairForFabric(FabricIndex fabricIndex, |
| 281 | + OperationalKeystore & operationalKeystore) const |
| 282 | +{ |
| 283 | + VerifyOrReturnError(IsValidFabricIndex(fabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX); |
| 284 | + |
| 285 | + P256SerializedKeypair serializedKeypair; |
| 286 | + |
| 287 | + // Do not allow overwriting the existing key and just remove it from the previous Operational Keystore if needed. |
| 288 | + if (!HasOpKeypairForFabric(fabricIndex)) |
| 289 | + { |
| 290 | + ReturnErrorOnFailure(operationalKeystore.ExportOpKeypairForFabric(fabricIndex, serializedKeypair)); |
| 291 | + |
| 292 | + PersistentP256Keypair keypair(fabricIndex); |
| 293 | + ReturnErrorOnFailure(keypair.Deserialize(serializedKeypair)); |
| 294 | + |
| 295 | + // Migrated key is not useful anymore, remove it from the previous keystore. |
| 296 | + ReturnErrorOnFailure(operationalKeystore.RemoveOpKeypairForFabric(fabricIndex)); |
| 297 | + } |
| 298 | + else if (operationalKeystore.HasOpKeypairForFabric(fabricIndex)) |
| 299 | + { |
| 300 | + ReturnErrorOnFailure(operationalKeystore.RemoveOpKeypairForFabric(fabricIndex)); |
| 301 | + } |
| 302 | + |
| 303 | + return CHIP_NO_ERROR; |
| 304 | +} |
| 305 | + |
| 306 | +} // namespace Crypto |
| 307 | +} // namespace chip |
0 commit comments