blob: 7a74c13759607026d6490ee1deb78cedacf69e95 [file] [log] [blame]
/*
* 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 <psa/crypto.h>
#include <lib/core/CHIPSafeCasts.h>
#include <lib/support/CHIPMem.h>
#include <lib/support/SafeInt.h>
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");
#if defined(SEMAILBOX_PRESENT) && (_SILICON_LABS_SECURITY_FEATURE == _SILICON_LABS_SECURITY_FEATURE_VAULT)
#define PSA_CRYPTO_LOCATION_FOR_DEVICE PSA_KEY_LOCATION_SL_SE_OPAQUE
#elif defined(CRYPTOACC_PRESENT) && defined(SEPUF_PRESENT) && defined(SL_TRUSTZONE_NONSECURE)
#define PSA_CRYPTO_LOCATION_FOR_DEVICE PSA_KEY_LOCATION_SL_CRYPTOACC_OPAQUE
#else
#define PSA_CRYPTO_LOCATION_FOR_DEVICE PSA_KEY_LOCATION_LOCAL_STORAGE
#endif
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, PSA_CRYPTO_LOCATION_FOR_DEVICE));
}
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, PSA_CRYPTO_LOCATION_FOR_DEVICE));
}
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