Files
GClarkson 9d06f983af Imported more library files
Not compiling currently
2025-04-12 23:37:19 +01:00

620 lines
21 KiB
C

/***************************************************************************//**
* @file
* @brief This file implements abstracting the PSA CRYPTO interface.
*******************************************************************************
* # License
* <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
*******************************************************************************
*
* The licensor of this software is Silicon Laboratories Inc. Your use of this
* software is governed by the terms of Silicon Labs Master Software License
* Agreement (MSLA) available at
* www.silabs.com/about-us/legal/master-software-license-agreement. This
* software is distributed to you in Source Code format and is governed by the
* sections of the MSLA applicable to Source Code.
*
******************************************************************************/
#include "security_manager.h"
#include "em_device.h"
#include "psa/crypto.h"
#include "sl_psa_crypto.h"
#include "sli_psa_crypto.h"
#include "psa/internal_trusted_storage.h"
#include "psa/sli_internal_trusted_storage.h"
#ifdef SL_COMPONENT_CATALOG_PRESENT
#include "sl_component_catalog.h"
#endif // SL_COMPONENT_CATALOG_PRESENT
#if defined(SL_CATALOG_NVM3_PRESENT)
#include "nvm3.h"
#endif
#define SL_SEC_MAN_AES_BLOCK_SIZE 16
#define SL_SEC_MAN_TRANSIENT_KEY_ID 0x200F0
static void sl_sec_man_set_key_attributes(psa_key_id_t * sl_psa_key_id,
psa_key_attributes_t *sl_psa_key_attr,
psa_key_type_t sl_psa_key_type,
psa_algorithm_t sl_psa_key_algorithm,
psa_key_usage_t sl_psa_key_usage,
psa_key_persistence_t sl_psa_key_persistence,
size_t sl_psa_key_len)
{
// By default, prefer to wrap (encrypt) keys in storage
psa_key_location_t sl_psa_key_location = sl_psa_get_most_secure_key_location();
// Dont wrap volatile keys used for HMAC operation, as opaque keys cannot be used for multi-part HMAC.
if (sl_psa_key_type == PSA_KEY_TYPE_HMAC && sl_psa_key_persistence == PSA_KEY_PERSISTENCE_VOLATILE) {
sl_psa_key_location = PSA_KEY_LOCATION_LOCAL_STORAGE;
}
// This next function gives us our preferred location if the chip supports it,
// else, we get PSA_KEY_LOCATION_LOCAL_STORAGE
sl_psa_set_key_lifetime_with_location_preference(sl_psa_key_attr,
sl_psa_key_persistence,
sl_psa_key_location); // preferred location
if (sl_psa_key_persistence == PSA_KEY_PERSISTENCE_DEFAULT) {
psa_set_key_id(sl_psa_key_attr, *sl_psa_key_id);
}
psa_set_key_usage_flags(sl_psa_key_attr, sl_psa_key_usage);
psa_set_key_algorithm(sl_psa_key_attr, sl_psa_key_algorithm);
psa_set_key_type(sl_psa_key_attr, sl_psa_key_type);
// If we are importing a public key, dont set key bits, as it is not needed
if (!PSA_KEY_TYPE_IS_ECC_PUBLIC_KEY(sl_psa_key_type)) {
psa_set_key_bits(sl_psa_key_attr, sl_psa_key_len);
}
}
psa_status_t sl_sec_man_import_key(psa_key_id_t * sl_psa_key_id,
psa_key_type_t sl_psa_key_type,
psa_algorithm_t sl_psa_key_algorithm,
psa_key_usage_t sl_psa_key_usage,
psa_key_persistence_t sl_psa_key_persistence,
const uint8_t * sl_psa_key_literal,
size_t sl_key_literal_len)
{
psa_status_t status;
psa_key_attributes_t sl_psa_key_attr = PSA_KEY_ATTRIBUTES_INIT;
if ((sl_psa_key_id == NULL) || (sl_psa_key_literal == NULL)) {
status = PSA_ERROR_INVALID_ARGUMENT;
goto exit;
}
sl_sec_man_set_key_attributes(sl_psa_key_id, &sl_psa_key_attr, sl_psa_key_type, sl_psa_key_algorithm,
sl_psa_key_usage, sl_psa_key_persistence, (sl_key_literal_len * 8));
/* Import the key */
status = psa_import_key(&sl_psa_key_attr, sl_psa_key_literal, sl_key_literal_len, sl_psa_key_id);
exit:
return status;
}
psa_status_t sl_sec_man_generate_key(psa_key_id_t * sl_psa_key_id,
psa_key_type_t sl_psa_key_type,
psa_algorithm_t sl_psa_key_algorithm,
psa_key_usage_t sl_psa_key_usage,
psa_key_persistence_t sl_psa_key_persistence,
size_t sl_psa_key_len)
{
psa_status_t status;
psa_key_attributes_t sl_psa_key_attr = PSA_KEY_ATTRIBUTES_INIT;
if (sl_psa_key_id == NULL) {
status = PSA_ERROR_INVALID_ARGUMENT;
goto exit;
}
sl_sec_man_set_key_attributes(sl_psa_key_id, &sl_psa_key_attr, sl_psa_key_type, sl_psa_key_algorithm,
sl_psa_key_usage, sl_psa_key_persistence, sl_psa_key_len);
/* Import the key */
status = psa_generate_key(&sl_psa_key_attr, sl_psa_key_id);
exit:
return status;
}
psa_status_t sl_sec_man_export_key(psa_key_id_t sl_psa_key_id,
uint8_t * sl_psa_key_buffer,
size_t sl_psa_key_buffer_len,
size_t * sl_psa_key_len)
{
psa_status_t status;
if ((sl_psa_key_buffer == NULL) || (sl_psa_key_len == NULL)) {
status = PSA_ERROR_INVALID_ARGUMENT;
goto exit;
}
status = psa_export_key(sl_psa_key_id, sl_psa_key_buffer, sl_psa_key_buffer_len, sl_psa_key_len);
exit:
return status;
}
psa_status_t sl_sec_man_get_key_attributes(psa_key_id_t sl_psa_key_id,
psa_key_attributes_t *sl_psa_key_attributes)
{
psa_status_t status;
if (sl_psa_key_attributes == NULL) {
status = PSA_ERROR_INVALID_ARGUMENT;
goto exit;
}
status = psa_get_key_attributes(sl_psa_key_id, sl_psa_key_attributes);
exit:
return status;
}
psa_status_t sl_sec_man_destroy_key(psa_key_id_t sl_psa_key_id)
{
return psa_destroy_key(sl_psa_key_id);
}
psa_status_t sl_sec_man_copy_key(psa_key_id_t sl_psa_source_key_id,
psa_key_attributes_t *sl_psa_key_attributes,
psa_key_id_t *sl_psa_dest_key_id)
{
psa_status_t status;
bool overwrite_original;
if (sl_psa_key_attributes == NULL || sl_psa_dest_key_id == NULL) {
status = PSA_ERROR_INVALID_ARGUMENT;
goto exit;
}
// Do we need to overwrite original key?
overwrite_original = (sl_psa_source_key_id == *sl_psa_dest_key_id);
if (overwrite_original) {
#ifndef SL_TRUSTZONE_NONSECURE
// Set the target key id to a default value.
// Note, this needs to be either in thread or zigbee NVM range.
*sl_psa_dest_key_id = SL_SEC_MAN_TRANSIENT_KEY_ID;
#else
status = PSA_ERROR_NOT_SUPPORTED;
goto exit;
#endif
}
// Set the key id for the new key
psa_set_key_id(sl_psa_key_attributes, *sl_psa_dest_key_id);
// Copy the key from source to destnation
status = psa_copy_key(sl_psa_source_key_id, sl_psa_key_attributes, sl_psa_dest_key_id);
if (status != PSA_SUCCESS) {
goto exit;
}
#if !defined(SL_TRUSTZONE_NONSECURE) && defined(MBEDTLS_PSA_CRYPTO_STORAGE_C)
if (overwrite_original) {
// After making a copy, if we need to replace the old key,
// first destroy the original key.
psa_destroy_key(sl_psa_source_key_id);
//Set the new key's keyid to match the original keyid
status = sli_psa_its_change_key_id(*sl_psa_dest_key_id, sl_psa_source_key_id);
}
#endif
exit:
return status;
}
psa_status_t sl_sec_man_aes_encrypt(psa_key_id_t sl_psa_key_id,
psa_algorithm_t sl_psa_aes_alg,
const uint8_t * sl_psa_aes_input,
uint8_t * sl_psa_aes_output)
{
size_t sl_psa_aes_enc_len = 0;
psa_status_t status = PSA_SUCCESS;
if ((sl_psa_aes_input == NULL) || (sl_psa_aes_output == NULL)) {
status = PSA_ERROR_INVALID_ARGUMENT;
goto exit;
}
status = psa_cipher_encrypt(sl_psa_key_id, sl_psa_aes_alg, sl_psa_aes_input, SL_SEC_MAN_AES_BLOCK_SIZE, sl_psa_aes_output,
SL_SEC_MAN_AES_BLOCK_SIZE, &sl_psa_aes_enc_len);
exit:
return status;
}
psa_status_t sl_sec_man_aes_decrypt(psa_key_id_t sl_psa_key_id,
psa_algorithm_t sl_psa_aes_alg,
const uint8_t * sl_psa_aes_input,
uint8_t * sl_psa_aes_output)
{
size_t sl_psa_aes_enc_len = 0;
psa_status_t status = PSA_SUCCESS;
if ((sl_psa_aes_input == NULL) || (sl_psa_aes_output == NULL)) {
status = PSA_ERROR_INVALID_ARGUMENT;
goto exit;
}
status = psa_cipher_decrypt(sl_psa_key_id, sl_psa_aes_alg, sl_psa_aes_input, SL_SEC_MAN_AES_BLOCK_SIZE, sl_psa_aes_output,
SL_SEC_MAN_AES_BLOCK_SIZE, &sl_psa_aes_enc_len);
exit:
return status;
}
//Use PSA for AES-CCM* encryption and tagging operations
psa_status_t sl_sec_man_aes_ccm_crypt(psa_key_id_t sl_psa_key_id,
uint8_t* nonce,
bool encrypt,
const uint8_t* input,
uint8_t encryption_start_index,
uint8_t length,
uint8_t mic_length,
uint8_t* output)
{
psa_status_t psa_status;
//contains size of output + MIC
size_t output_length;
size_t NONCE_LENGTH = 13;
//return full output packet (including unencrypted authentication data)
memmove(output, input, encryption_start_index);
psa_algorithm_t aes_ccm_tag_alg = PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, mic_length);
if (encrypt) {
psa_status = psa_aead_encrypt(
sl_psa_key_id,
aes_ccm_tag_alg,
(const uint8_t *) nonce, NONCE_LENGTH,
input, encryption_start_index,
input + encryption_start_index, length - encryption_start_index,
output + encryption_start_index, length - encryption_start_index + mic_length, &output_length);
} else {
psa_status = psa_aead_decrypt(
sl_psa_key_id,
aes_ccm_tag_alg,
(const uint8_t *) nonce, NONCE_LENGTH,
input, encryption_start_index,
input + encryption_start_index, length - encryption_start_index + mic_length,
output + encryption_start_index, length - encryption_start_index, &output_length);
}
if (psa_status == PSA_ERROR_INVALID_SIGNATURE) {
return psa_status;
}
if (psa_status != PSA_SUCCESS || (!encrypt && output_length != (size_t) (length - encryption_start_index))
|| (encrypt && output_length != (size_t) (length + mic_length - encryption_start_index))) {
return PSA_ERROR_INVALID_ARGUMENT;
} else {
return PSA_SUCCESS;
}
}
psa_status_t sl_sec_man_hmac_start(psa_mac_operation_t *sl_psa_hmac_ctx, psa_key_id_t sl_psa_key_id)
{
psa_status_t status = PSA_SUCCESS;
if (sl_psa_hmac_ctx == NULL) {
status = PSA_ERROR_INVALID_ARGUMENT;
goto exit;
}
status = psa_mac_sign_setup(sl_psa_hmac_ctx, sl_psa_key_id, PSA_ALG_HMAC(PSA_ALG_SHA_256));
exit:
return status;
}
psa_status_t sl_sec_man_hmac_update(psa_mac_operation_t *sl_psa_hmac_ctx,
const uint8_t * sl_psa_hmac_buffer,
size_t sl_psa_hmac_buffer_len)
{
psa_status_t status = PSA_SUCCESS;
if ((sl_psa_hmac_ctx == NULL) || (sl_psa_hmac_buffer == NULL)) {
status = PSA_ERROR_INVALID_ARGUMENT;
goto exit;
}
status = psa_mac_update(sl_psa_hmac_ctx, sl_psa_hmac_buffer, sl_psa_hmac_buffer_len);
exit:
return status;
}
psa_status_t sl_sec_man_hmac_finish(psa_mac_operation_t *sl_psa_hmac_ctx,
const uint8_t * sl_psa_hmac_buffer,
size_t sl_psa_hmac_buffer_len)
{
psa_status_t status = PSA_SUCCESS;
size_t sl_psa_mac_length = 0;
if ((sl_psa_hmac_ctx == NULL) || (sl_psa_hmac_buffer == NULL)) {
status = PSA_ERROR_INVALID_ARGUMENT;
goto exit;
}
status = psa_mac_sign_finish(sl_psa_hmac_ctx, (uint8_t *)sl_psa_hmac_buffer,
sl_psa_hmac_buffer_len, &sl_psa_mac_length);
exit:
return status;
}
psa_status_t sl_sec_man_hmac_deinit(psa_mac_operation_t *sl_psa_hmac_ctx)
{
psa_status_t status = PSA_SUCCESS;
if (sl_psa_hmac_ctx == NULL) {
status = PSA_ERROR_INVALID_ARGUMENT;
goto exit;
}
status = psa_mac_abort(sl_psa_hmac_ctx);
exit:
return status;
}
psa_status_t sl_sec_man_key_derivation_extract(psa_key_derivation_operation_t *sl_psa_key_derivation_ctx,
psa_algorithm_t sl_psa_key_derivation_algorithm,
psa_key_id_t sl_psa_key_id,
const uint8_t * sl_psa_key_derivation_salt,
uint16_t sl_psa_key_derivation_salt_length)
{
psa_status_t status = PSA_SUCCESS;
if (sl_psa_key_derivation_ctx == NULL) {
status = PSA_ERROR_INVALID_ARGUMENT;
goto exit;
}
*sl_psa_key_derivation_ctx = psa_key_derivation_operation_init();
// PRK is calculated as HMAC-Hash(aSalt, aInputKey)
status = psa_key_derivation_setup(sl_psa_key_derivation_ctx, PSA_ALG_HKDF(sl_psa_key_derivation_algorithm));
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_key_derivation_input_bytes(sl_psa_key_derivation_ctx, PSA_KEY_DERIVATION_INPUT_SALT,
sl_psa_key_derivation_salt, sl_psa_key_derivation_salt_length);
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_key_derivation_input_key(sl_psa_key_derivation_ctx, PSA_KEY_DERIVATION_INPUT_SECRET, sl_psa_key_id);
if (status != PSA_SUCCESS) {
goto exit;
}
exit:
if (status != PSA_SUCCESS) {
psa_key_derivation_abort(sl_psa_key_derivation_ctx);
}
return status;
}
psa_status_t sl_sec_man_key_derivation_expand(psa_key_derivation_operation_t *sl_psa_key_derivation_ctx,
const uint8_t * sl_psa_key_derivation_info,
uint16_t sl_psa_key_derivation_info_length,
uint8_t * sl_psa_key_derivation_output_key,
uint16_t sl_psa_key_derivation_output_key_len)
{
psa_status_t status = PSA_SUCCESS;
if ((sl_psa_key_derivation_ctx == NULL) || (sl_psa_key_derivation_output_key == NULL)
|| (sl_psa_key_derivation_info == NULL)) {
status = PSA_ERROR_INVALID_ARGUMENT;
goto exit;
}
status = psa_key_derivation_input_bytes(sl_psa_key_derivation_ctx, PSA_KEY_DERIVATION_INPUT_INFO,
sl_psa_key_derivation_info, sl_psa_key_derivation_info_length);
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_key_derivation_output_bytes(sl_psa_key_derivation_ctx, sl_psa_key_derivation_output_key,
sl_psa_key_derivation_output_key_len);
if (status != PSA_SUCCESS) {
goto exit;
}
exit:
if (status != PSA_SUCCESS) {
psa_key_derivation_abort(sl_psa_key_derivation_ctx);
}
return status;
}
psa_hash_operation_t sl_sec_man_hash_init(void)
{
return psa_hash_operation_init();
}
psa_status_t sl_sec_man_hash_deinit(psa_hash_operation_t *sl_psa_hash_ctx)
{
psa_status_t status = PSA_SUCCESS;
if (sl_psa_hash_ctx == NULL) {
status = PSA_ERROR_INVALID_ARGUMENT;
goto exit;
}
status = psa_hash_abort(sl_psa_hash_ctx);
exit:
return status;
}
psa_status_t sl_sec_man_hash_start(psa_hash_operation_t *sl_psa_hash_ctx, psa_algorithm_t sl_psa_hash_alg)
{
psa_status_t status = PSA_SUCCESS;
if (sl_psa_hash_ctx == NULL) {
status = PSA_ERROR_INVALID_ARGUMENT;
goto exit;
}
status = psa_hash_setup(sl_psa_hash_ctx, sl_psa_hash_alg);
exit:
return status;
}
psa_status_t sl_sec_man_hash_update(psa_hash_operation_t *sl_psa_hash_ctx,
uint8_t * sl_psa_hash_buffer,
uint16_t sl_psa_hash_buffer_len)
{
psa_status_t status = PSA_SUCCESS;
if ((sl_psa_hash_ctx == NULL) || (sl_psa_hash_buffer == NULL)) {
status = PSA_ERROR_INVALID_ARGUMENT;
goto exit;
}
status = psa_hash_update(sl_psa_hash_ctx, sl_psa_hash_buffer, sl_psa_hash_buffer_len);
exit:
return status;
}
psa_status_t sl_sec_man_hash_finish(psa_hash_operation_t *sl_psa_hash_ctx,
uint8_t * sl_psa_hash,
uint16_t sl_psa_hash_size,
size_t * sl_psa_hash_len)
{
psa_status_t status = PSA_SUCCESS;
if ((sl_psa_hash_ctx == NULL) || (sl_psa_hash == NULL) || (sl_psa_hash_len == NULL)) {
status = PSA_ERROR_INVALID_ARGUMENT;
goto exit;
}
status = psa_hash_finish(sl_psa_hash_ctx, sl_psa_hash, sl_psa_hash_size, sl_psa_hash_len);
exit:
return status;
}
psa_status_t sl_sec_man_get_random(uint8_t *sl_psa_output_buffer,
uint16_t sl_psa_output_size)
{
psa_status_t status = PSA_SUCCESS;
if (sl_psa_output_buffer == NULL) {
status = PSA_ERROR_INVALID_ARGUMENT;
goto exit;
}
status = psa_generate_random(sl_psa_output_buffer, sl_psa_output_size);
exit:
return status;
}
psa_status_t sl_sec_man_export_public_key(psa_key_id_t sl_psa_key_id,
uint8_t *sl_psa_output_buffer,
size_t sl_output_buffer_size,
size_t *sl_psa_key_len)
{
psa_status_t status = PSA_SUCCESS;
if ((sl_psa_output_buffer == NULL) && (sl_psa_key_len == NULL)) {
status = PSA_ERROR_INVALID_ARGUMENT;
goto exit;
}
status = psa_export_public_key(sl_psa_key_id, sl_psa_output_buffer, sl_output_buffer_size, sl_psa_key_len);
exit:
return status;
}
psa_status_t sl_sec_man_sign(psa_key_id_t sl_psa_key_id,
psa_algorithm_t sl_dsa_algorithm,
const uint8_t *sl_dsa_input_buf,
size_t sl_dsa_input_size,
uint8_t *sl_dsa_signature_buf,
size_t sl_dsa_signature_size,
size_t *sl_dsa_signature_len,
bool sl_is_hash)
{
psa_status_t status = PSA_SUCCESS;
if ((sl_dsa_input_buf == NULL) && (sl_dsa_signature_buf == NULL) && (sl_dsa_signature_len == NULL)) {
status = PSA_ERROR_INVALID_ARGUMENT;
goto exit;
}
if (sl_is_hash) {
status = psa_sign_hash(sl_psa_key_id,
sl_dsa_algorithm,
sl_dsa_input_buf,
sl_dsa_input_size,
sl_dsa_signature_buf,
sl_dsa_signature_size,
sl_dsa_signature_len);
} else {
status = psa_sign_message(sl_psa_key_id,
sl_dsa_algorithm,
sl_dsa_input_buf,
sl_dsa_input_size,
sl_dsa_signature_buf,
sl_dsa_signature_size,
sl_dsa_signature_len);
}
exit:
return status;
}
psa_status_t sl_sec_man_verify(psa_key_id_t sl_psa_key_id,
psa_algorithm_t sl_dsa_algorithm,
const uint8_t *sl_dsa_input_buf,
size_t sl_dsa_input_size,
const uint8_t *sl_dsa_signature_buf,
size_t sl_dsa_signature_size,
bool sl_is_hash)
{
psa_status_t status = PSA_SUCCESS;
if ((sl_dsa_input_buf == NULL) && (sl_dsa_signature_buf == NULL)) {
status = PSA_ERROR_INVALID_ARGUMENT;
goto exit;
}
if (sl_is_hash) {
status = psa_verify_hash(sl_psa_key_id,
sl_dsa_algorithm,
sl_dsa_input_buf,
sl_dsa_input_size,
sl_dsa_signature_buf,
sl_dsa_signature_size);
} else {
status = psa_verify_message(sl_psa_key_id,
sl_dsa_algorithm,
sl_dsa_input_buf,
sl_dsa_input_size,
sl_dsa_signature_buf,
sl_dsa_signature_size);
}
exit:
return status;
}