/* * Copyright (c) 2022 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Efr32OpaqueKeypair.h" #include "em_device.h" #include #include #include #include #include using chip::Platform::MemoryCalloc; using chip::Platform::MemoryFree; using chip::Crypto::P256ECDHDerivedSecret; using chip::Crypto::P256ECDSASignature; using chip::Crypto::P256Keypair; using chip::Crypto::P256PublicKey; using chip::Crypto::P256SerializedKeypair; namespace chip { namespace DeviceLayer { namespace Internal { /******************************************************************************* * * PSA key ID range for storing Matter Opaque keys * ******************************************************************************/ #define PSA_KEY_ID_FOR_MATTER_MIN (0x00004400) #define PSA_KEY_ID_FOR_MATTER_MAX (0x000045FF) #define PSA_KEY_ID_FOR_MATTER_SIZE (PSA_KEY_ID_FOR_MATTER_MAX - PSA_KEY_ID_FOR_MATTER_MIN + 1) static_assert((kEFR32OpaqueKeyIdPersistentMax - kEFR32OpaqueKeyIdPersistentMin) < PSA_KEY_ID_FOR_MATTER_SIZE, "Not enough PSA range to store all allowed opaque key IDs"); static void _log_PSA_error(psa_status_t status) { if (status != PSA_SUCCESS) { ChipLogError(Crypto, "PSA error: %ld", status); } } /******************************************************************************* * * PSA Crypto backed implementation of EFR32OpaqueKeypair * ******************************************************************************/ static bool is_opaque_key_valid(EFR32OpaqueKeyId id) { if (id == kEFR32OpaqueKeyIdVolatile) { return true; } else if (id >= kEFR32OpaqueKeyIdPersistentMin && id <= (kEFR32OpaqueKeyIdPersistentMin + PSA_KEY_ID_FOR_MATTER_SIZE)) { return true; } return false; } static mbedtls_svc_key_id_t psa_key_id_from_opaque(EFR32OpaqueKeyId id) { if (id == kEFR32OpaqueKeyIdVolatile || !is_opaque_key_valid(id)) { return 0; } return PSA_KEY_ID_FOR_MATTER_MIN + (id - kEFR32OpaqueKeyIdPersistentMin); } static EFR32OpaqueKeyId opaque_key_id_from_psa(mbedtls_svc_key_id_t id) { if (id == 0) { return kEFR32OpaqueKeyIdVolatile; } else if (id >= PSA_KEY_ID_FOR_MATTER_MIN && id <= PSA_KEY_ID_FOR_MATTER_MAX) { return (id + kEFR32OpaqueKeyIdPersistentMin) - PSA_KEY_ID_FOR_MATTER_MIN; } else { return kEFR32OpaqueKeyIdUnknown; } } EFR32OpaqueKeypair::EFR32OpaqueKeypair() { psa_crypto_init(); // Avoid having a reference to PSA datatypes in the signature of this class mContext = MemoryCalloc(1, sizeof(mbedtls_svc_key_id_t)); } EFR32OpaqueKeypair::~EFR32OpaqueKeypair() { // Free key resources if (mContext != nullptr) { // Delete volatile keys, since nobody else can after we drop the key ID. if (!mIsPersistent) { DestroyKey(); } MemoryFree(mContext); mContext = nullptr; } } CHIP_ERROR EFR32OpaqueKeypair::Load(EFR32OpaqueKeyId opaque_id) { CHIP_ERROR error = CHIP_NO_ERROR; psa_status_t status = PSA_ERROR_BAD_STATE; mbedtls_svc_key_id_t key_id = 0; VerifyOrExit(opaque_id != kEFR32OpaqueKeyIdVolatile, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(is_opaque_key_valid(opaque_id), error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(mContext, error = CHIP_ERROR_INCORRECT_STATE); // If the object contains a volatile key, clean it up before reusing the object storage if (mHasKey && !mIsPersistent) { DestroyKey(); } key_id = psa_key_id_from_opaque(opaque_id); status = psa_export_public_key(key_id, mPubkeyRef, mPubkeySize, &mPubkeyLength); if (status == PSA_ERROR_DOES_NOT_EXIST) { error = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND; goto exit; } VerifyOrExit(status == PSA_SUCCESS, { _log_PSA_error(status); error = CHIP_ERROR_INTERNAL; }); // Store the key ID and mark the key as valid *(mbedtls_svc_key_id_t *) mContext = key_id; mHasKey = true; mIsPersistent = true; exit: if (error != CHIP_NO_ERROR) { memset(mPubkeyRef, 0, mPubkeySize); } return error; } CHIP_ERROR EFR32OpaqueKeypair::Create(EFR32OpaqueKeyId opaque_id, EFR32OpaqueKeyUsages usage) { CHIP_ERROR error = CHIP_NO_ERROR; psa_status_t status = PSA_ERROR_BAD_STATE; psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; mbedtls_svc_key_id_t key_id = 0; VerifyOrExit(is_opaque_key_valid(opaque_id), error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(mContext, error = CHIP_ERROR_INCORRECT_STATE); if (opaque_id == kEFR32OpaqueKeyIdVolatile) { psa_set_key_lifetime( &attr, PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_LIFETIME_VOLATILE, sl_psa_get_most_secure_key_location())); } else { psa_key_handle_t key_handle; key_id = psa_key_id_from_opaque(opaque_id); // Check if the key already exists int ret = psa_open_key(key_id, &key_handle); if (PSA_SUCCESS == ret) { // WARNING: Existing key! This is caused by a problem in the key store. // The key must be destroyed, otherwhise the device won't recover. ChipLogError(Crypto, "WARNING: PSA key recycled: %d / %ld", opaque_id, key_id); psa_destroy_key(key_id); } psa_set_key_id(&attr, key_id); psa_set_key_lifetime( &attr, PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(PSA_KEY_LIFETIME_PERSISTENT, sl_psa_get_most_secure_key_location())); } switch (usage) { case EFR32OpaqueKeyUsages::ECDSA_P256_SHA256: psa_set_key_type(&attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); psa_set_key_bits(&attr, 256); psa_set_key_algorithm(&attr, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); // Need hash signing permissions because the CSR generation uses sign_hash internally psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_SIGN_MESSAGE | PSA_KEY_USAGE_SIGN_HASH); break; case EFR32OpaqueKeyUsages::ECDH_P256: psa_set_key_type(&attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); psa_set_key_bits(&attr, 256); psa_set_key_algorithm(&attr, PSA_ALG_ECDH); psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_DERIVE); break; } status = psa_generate_key(&attr, &key_id); VerifyOrExit(status == PSA_SUCCESS, { _log_PSA_error(status); error = CHIP_ERROR_INTERNAL; }); // Export the public key status = psa_export_public_key(key_id, mPubkeyRef, mPubkeySize, &mPubkeyLength); if (PSA_SUCCESS != status) { _log_PSA_error(status); // Key generation succeeded, but pubkey export did not. To avoid // memory leaks, delete the generated key before returning the error psa_destroy_key(key_id); error = CHIP_ERROR_INTERNAL; goto exit; } // Store the key ID and mark the key as valid mHasKey = true; mIsPersistent = opaque_id != kEFR32OpaqueKeyIdVolatile; exit: psa_reset_key_attributes(&attr); if (mContext) { if (CHIP_NO_ERROR == error) { *(mbedtls_svc_key_id_t *) mContext = key_id; } else { *(mbedtls_svc_key_id_t *) mContext = 0; } } return error; } CHIP_ERROR EFR32OpaqueKeypair::GetPublicKey(uint8_t * output, size_t output_size, size_t * output_length) const { CHIP_ERROR error = CHIP_NO_ERROR; VerifyOrExit(mHasKey, error = CHIP_ERROR_INCORRECT_STATE); if (output_size >= mPubkeyLength) { memcpy(output, mPubkeyRef, mPubkeyLength); *output_length = mPubkeyLength; } else { error = CHIP_ERROR_BUFFER_TOO_SMALL; } exit: return error; } EFR32OpaqueKeyId EFR32OpaqueKeypair::GetKeyId() const { if (!mHasKey) { return kEFR32OpaqueKeyIdUnknown; } if (!mIsPersistent) { return kEFR32OpaqueKeyIdVolatile; } return opaque_key_id_from_psa(*(mbedtls_svc_key_id_t *) mContext); } CHIP_ERROR EFR32OpaqueKeypair::Sign(const uint8_t * msg, size_t msg_len, uint8_t * output, size_t output_size, size_t * output_length) const { CHIP_ERROR error = CHIP_NO_ERROR; psa_status_t status = PSA_ERROR_BAD_STATE; VerifyOrExit(mContext, error = CHIP_ERROR_INCORRECT_STATE); VerifyOrExit(mHasKey, error = CHIP_ERROR_INCORRECT_STATE); status = psa_sign_message(*(mbedtls_svc_key_id_t *) mContext, PSA_ALG_ECDSA(PSA_ALG_SHA_256), msg, msg_len, output, output_size, output_length); VerifyOrExit(status == PSA_SUCCESS, { _log_PSA_error(status); error = CHIP_ERROR_INTERNAL; }); exit: return error; } CHIP_ERROR EFR32OpaqueKeypair::Derive(const uint8_t * their_key, size_t their_key_len, uint8_t * output, size_t output_size, size_t * output_length) const { CHIP_ERROR error = CHIP_NO_ERROR; psa_status_t status = PSA_ERROR_BAD_STATE; VerifyOrExit(mHasKey, error = CHIP_ERROR_INCORRECT_STATE); status = psa_raw_key_agreement(PSA_ALG_ECDH, *(mbedtls_svc_key_id_t *) mContext, their_key, their_key_len, output, output_size, output_length); VerifyOrExit(status == PSA_SUCCESS, { _log_PSA_error(status); error = CHIP_ERROR_INTERNAL; }); exit: return error; } CHIP_ERROR EFR32OpaqueKeypair::DestroyKey() { CHIP_ERROR error = CHIP_NO_ERROR; psa_status_t status = PSA_ERROR_BAD_STATE; VerifyOrExit(mHasKey, error = CHIP_ERROR_INCORRECT_STATE); status = psa_destroy_key(*(mbedtls_svc_key_id_t *) mContext); VerifyOrExit(status == PSA_SUCCESS, { _log_PSA_error(status); error = CHIP_ERROR_INTERNAL; }); exit: mHasKey = false; mIsPersistent = false; memset(mPubkeyRef, 0, mPubkeySize); if (mContext) { *(mbedtls_svc_key_id_t *) mContext = 0; } return error; } /******************************************************************************* * * PSA Crypto backed implementation of EFR32OpaqueP256Keypair * ******************************************************************************/ EFR32OpaqueP256Keypair::EFR32OpaqueP256Keypair() { mPubkeyRef = mPubKey.Bytes(); mPubkeySize = mPubKey.Length(); mPubkeyLength = 0; } EFR32OpaqueP256Keypair::~EFR32OpaqueP256Keypair() {} CHIP_ERROR EFR32OpaqueP256Keypair::Initialize(chip::Crypto::ECPKeyTarget key_target) { if (mPubkeyLength > 0) { // already have a key - ECDH use case where CASESession is calling Initialize() return CHIP_NO_ERROR; } ChipLogError(Crypto, "Initialize() is invalid on opaque keys, use Create() instead"); return CHIP_ERROR_NOT_IMPLEMENTED; } CHIP_ERROR EFR32OpaqueP256Keypair::Serialize(P256SerializedKeypair & output) const { ChipLogError(Crypto, "Serialisation is invalid on opaque keys, share the object instead"); return CHIP_ERROR_NOT_IMPLEMENTED; } CHIP_ERROR EFR32OpaqueP256Keypair::Deserialize(P256SerializedKeypair & input) { ChipLogError(Crypto, "Serialisation is invalid on opaque keys"); return CHIP_ERROR_NOT_IMPLEMENTED; } CHIP_ERROR EFR32OpaqueP256Keypair::NewCertificateSigningRequest(uint8_t * out_csr, size_t & csr_length) const { MutableByteSpan csr(out_csr, csr_length); CHIP_ERROR err = GenerateCertificateSigningRequest(this, csr); csr_length = (CHIP_NO_ERROR == err) ? csr.size() : 0; return err; } CHIP_ERROR EFR32OpaqueP256Keypair::ECDSA_sign_msg(const uint8_t * msg, size_t msg_length, P256ECDSASignature & out_signature) const { CHIP_ERROR error = CHIP_NO_ERROR; size_t output_length = 0; VerifyOrExit((msg != nullptr) && (msg_length > 0), error = CHIP_ERROR_INVALID_ARGUMENT); error = Sign(msg, msg_length, out_signature.Bytes(), out_signature.Capacity(), &output_length); SuccessOrExit(error); SuccessOrExit(error = out_signature.SetLength(output_length)); exit: return error; } CHIP_ERROR EFR32OpaqueP256Keypair::ECDH_derive_secret(const P256PublicKey & remote_public_key, P256ECDHDerivedSecret & out_secret) const { CHIP_ERROR error = CHIP_NO_ERROR; size_t output_length = 0; error = Derive(Uint8::to_const_uchar(remote_public_key), remote_public_key.Length(), out_secret.Bytes(), (out_secret.Length() == 0) ? out_secret.Capacity() : out_secret.Length(), &output_length); SuccessOrExit(error); SuccessOrExit(error = out_secret.SetLength(output_length)); exit: return error; } const P256PublicKey & EFR32OpaqueP256Keypair::Pubkey() const { return mPubKey; } } // namespace Internal } // namespace DeviceLayer } // namespace chip