1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/pkg/cryptoauthlib/psa_atca_driver/psa_atca_se_driver.c
2023-08-31 14:38:50 +02:00

692 lines
22 KiB
C

/*
* Copyright (C) 2021 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup sys_psa_crypto
* @{
*
* @file
* @brief Glue code translating between PSA Crypto and the Microchip Cryptoauth Library APIs
*
* @author Lena Boeckmann <lena.boeckmann@haw-hamburg.de>
*
* @}
*/
#include "atca_params.h"
#include "psa/crypto.h"
#include "psa_crypto_se_driver.h"
#define ENABLE_DEBUG 0
#include "debug.h"
/**
* @brief AES block size supported by ATCA devices
*/
#define AES_128_BLOCK_SIZE (16)
/**
* @brief AES key size supported by ATCA devices
*/
#define AES_128_KEY_SIZE (16)
/**
* @brief Size of an ellipic curve public key supported by ATCA devices
*/
#define ECC_P256_PUB_KEY_SIZE (64)
/**
* @brief Maximum IV length supported by ATCA devices
*/
#define ATCA_MAX_IV_LEN (16)
/**
* @brief Size of ATCA TempKey Register
*/
#define ATCA_WRITE_BUFFER_SIZE (32)
/**
* @brief Check whether a specified algorithm is supported by this driver
*
* @param alg Algorithm of type @ref psa_algorithm_t
*
* @return int
* 1 if @c alg is supported
* 0 otherwise
*/
#define ALG_IS_SUPPORTED(alg) \
((alg == PSA_ALG_ECB_NO_PADDING) || \
(alg == PSA_ALG_CBC_NO_PADDING) || \
(alg == PSA_ALG_ECDSA(PSA_ALG_SHA_256)) || \
(alg == PSA_ALG_HMAC(PSA_ALG_SHA_256)))
/**
* @brief Check whether a specified key size is supported by this driver
*
* @param size Size of the specified key
* @param type Type of the specified key
*
* @return int
* 1 if @c size is supported
* 0 otherwise
*/
#define KEY_SIZE_IS_SUPPORTED(size, type) \
((type == PSA_KEY_TYPE_AES && size == AES_128_KEY_SIZE) || \
(PSA_KEY_TYPE_IS_ECC(type) && size == (ECC_P256_PUB_KEY_SIZE + 1)) || \
(type == PSA_KEY_TYPE_HMAC && size == 32))
/**
* @brief Convert ATCA status values to PSA errors
*
* @param error
*
* @return @ref psa_status_t
*/
static psa_status_t atca_to_psa_error(ATCA_STATUS error)
{
switch (error) {
case ATCA_NOT_LOCKED:
case ATCA_EXECUTION_ERROR:
case ATCA_FUNC_FAIL:
return PSA_ERROR_BAD_STATE;
case ATCA_WAKE_FAILED:
case ATCA_RX_FAIL:
case ATCA_RX_NO_RESPONSE:
case ATCA_TX_TIMEOUT:
case ATCA_RX_TIMEOUT:
case ATCA_TOO_MANY_COMM_RETRIES:
case ATCA_COMM_FAIL:
case ATCA_TIMEOUT:
case ATCA_TX_FAIL:
return PSA_ERROR_COMMUNICATION_FAILURE;
case ATCA_RX_CRC_ERROR:
case ATCA_STATUS_CRC:
return PSA_ERROR_DATA_CORRUPT;
case ATCA_SMALL_BUFFER:
return PSA_ERROR_BUFFER_TOO_SMALL;
case ATCA_BAD_OPCODE:
case ATCA_BAD_PARAM:
case ATCA_INVALID_SIZE:
case ATCA_INVALID_ID:
return PSA_ERROR_INVALID_ARGUMENT;
case ATCA_UNIMPLEMENTED:
return PSA_ERROR_NOT_SUPPORTED;
default:
return PSA_ERROR_GENERIC_ERROR;
}
}
const char *atca_status_to_humanly_readable(ATCA_STATUS status)
{
switch (status) {
case ATCA_NOT_LOCKED:
return "ATCA_NOT_LOCKED";
case ATCA_EXECUTION_ERROR:
return "ATCA_EXECUTION_ERROR";
case ATCA_FUNC_FAIL:
return "ATCA_FUNC_FAIL";
case ATCA_WAKE_FAILED:
return "ATCA_WAKE_FAILED";
case ATCA_RX_FAIL:
return "ATCA_RX_FAIL";
case ATCA_RX_NO_RESPONSE:
return "ATCA_RX_NO_RESPONSE";
case ATCA_TX_TIMEOUT:
return "ATCA_TX_TIMEOUT";
case ATCA_RX_TIMEOUT:
return "ATCA_RX_TIMEOUT";
case ATCA_TOO_MANY_COMM_RETRIES:
return "ATCA_TOO_MANY_COMM_RETRIES";
case ATCA_COMM_FAIL:
return "ATCA_COMM_FAIL";
case ATCA_TIMEOUT:
return "ATCA_TIMEOUT";
case ATCA_TX_FAIL:
return "ATCA_TX_FAIL";
case ATCA_RX_CRC_ERROR:
return "ATCA_RX_CRC_ERROR";
case ATCA_STATUS_CRC:
return "ATCA_STATUS_CRC";
case ATCA_SMALL_BUFFER:
return "ATCA_SMALL_BUFFER";
case ATCA_BAD_OPCODE:
return "ATCA_BAD_OPCODE";
case ATCA_BAD_PARAM:
return "ATCA_BAD_PARAM";
case ATCA_INVALID_SIZE:
return "ATCA_INVALID_SIZE";
case ATCA_INVALID_ID:
return "ATCA_INVALID_ID";
case ATCA_UNIMPLEMENTED:
return "ATCA_UNIMPLEMENTED";
default:
return "Error value not recognized";
}
}
/* Secure Element Cipher Functions */
/**
* @brief Set up a driver specific AES CBC mode operation
*
* @param ctx
* @param dev
* @param key_slot
*/
static void atca_cbc_setup(atca_aes_cbc_ctx_t *ctx,
ATCADevice dev,
psa_key_slot_number_t key_slot)
{
ctx->device = dev;
ctx->key_id = key_slot;
ctx->key_block = 0;
}
psa_status_t atca_cipher_setup(psa_drv_se_context_t *drv_context,
void *op_context,
psa_key_slot_number_t key_slot,
psa_algorithm_t algorithm,
psa_encrypt_or_decrypt_t direction)
{
ATCADevice dev = (ATCADevice)drv_context->transient_data;
psa_se_cipher_context_t *ctx = (psa_se_cipher_context_t *)op_context;
/* Only device type ATECC608 supports AES operations */
if (dev->mIface.mIfaceCFG->devtype != ATECC608) {
DEBUG("ATCA Cipher Setup: Only ATECC608 devices support AES operations.\n");
return PSA_ERROR_NOT_SUPPORTED;
}
switch (algorithm) {
case PSA_ALG_CBC_NO_PADDING:
atca_cbc_setup(&ctx->drv_ctx.atca_aes_cbc, dev, key_slot);
break;
default:
DEBUG("ATCA Cipher Setup: Algorithm not supported by implementation.\n");
return PSA_ERROR_NOT_SUPPORTED;
}
ctx->direction = direction;
return PSA_SUCCESS;
}
psa_status_t atca_cipher_set_iv(void *op_context,
const uint8_t *p_iv,
size_t iv_length)
{
atca_aes_cbc_ctx_t *ctx = &((psa_se_cipher_context_t *)op_context)->drv_ctx.atca_aes_cbc;
if (iv_length != ATCA_MAX_IV_LEN) {
DEBUG("ATCA Cipher Set IV: Invalid IV length: Expected %d, was %d\n", \
ATCA_MAX_IV_LEN, iv_length);
return PSA_ERROR_INVALID_ARGUMENT;
}
memcpy(ctx->ciphertext, p_iv, iv_length);
return PSA_SUCCESS;
}
psa_status_t atca_cipher_update(void *op_context,
const uint8_t *p_input,
size_t input_size,
uint8_t *p_output,
size_t output_size,
size_t *p_output_length)
{
psa_se_cipher_context_t *ctx = (psa_se_cipher_context_t *)op_context;
ATCA_STATUS status = ATCA_EXECUTION_ERROR;
size_t offset = 0;
for (size_t data_block = 0; data_block < (input_size / AES_128_BLOCK_SIZE); data_block++) {
offset = data_block * AES_128_BLOCK_SIZE;
if (ctx->direction == PSA_CRYPTO_DRIVER_ENCRYPT) {
status = atcab_aes_cbc_encrypt_block(&ctx->drv_ctx.atca_aes_cbc, p_input + offset,
p_output + offset);
}
else {
status = atcab_aes_cbc_decrypt_block(&ctx->drv_ctx.atca_aes_cbc, p_input + offset,
p_output + offset);
}
if (status != ATCA_SUCCESS) {
DEBUG("ATCA Cipher Update failed. ATCA Error: %s\n",
atca_status_to_humanly_readable(status));
return atca_to_psa_error(status);
}
}
*p_output_length += input_size;
(void)output_size;
return PSA_SUCCESS;
}
psa_status_t atca_cipher_finish(void *op_context,
uint8_t *p_output,
size_t output_size,
size_t *p_output_length)
{
(void)op_context;
(void)p_output;
(void)output_size;
(void)p_output_length;
return PSA_SUCCESS;
}
psa_status_t atca_cipher_ecb(psa_drv_se_context_t *drv_context,
psa_key_slot_number_t key_slot,
psa_algorithm_t algorithm,
psa_encrypt_or_decrypt_t direction,
const uint8_t *p_input,
size_t input_size,
uint8_t *p_output,
size_t output_size)
{
ATCA_STATUS status;
ATCADevice dev = (ATCADevice)drv_context->transient_data;
size_t offset = 0;
if (dev->mIface.mIfaceCFG->devtype != ATECC608) {
DEBUG("ATCA Cipher ECB: Only ATECC608 devices support AES operations.\n");
return PSA_ERROR_NOT_SUPPORTED;
}
if (algorithm != PSA_ALG_ECB_NO_PADDING || direction != PSA_CRYPTO_DRIVER_ENCRYPT) {
DEBUG("ATCA Cipher ECB: Only AES ECB encryption without padding is supported.\n");
return PSA_ERROR_NOT_SUPPORTED;
}
if (input_size % AES_128_BLOCK_SIZE != 0) {
DEBUG("ATCA Cipher ECB: Input must be multiple of 16, was %d.\n", input_size);
return PSA_ERROR_INVALID_ARGUMENT;
}
do {
status = calib_aes_encrypt(dev, key_slot, 0, p_input + offset, p_output + offset);
if (status != ATCA_SUCCESS) {
DEBUG("ATCA Cipher ECB encrypt failed. ATCA Error: %s\n",
atca_status_to_humanly_readable(status));
return atca_to_psa_error(status);
}
offset += AES_128_BLOCK_SIZE;
} while (offset < input_size);
(void)output_size;
return PSA_SUCCESS;
}
/* Secure Element Key Management Functions */
psa_status_t atca_allocate(psa_drv_se_context_t *drv_context,
void *persistent_data,
const psa_key_attributes_t *attributes,
psa_key_creation_method_t method,
psa_key_slot_number_t *key_slot)
{
if (!ALG_IS_SUPPORTED(attributes->policy.alg)) {
DEBUG("ATCA allocate: Algorithm is not supported by device.\n");
return PSA_ERROR_NOT_SUPPORTED;
}
psa_key_type_t type = attributes->type;
psa_se_config_t *device_config = (psa_se_config_t *)persistent_data;
psa_atca_slot_config_t *slot_config = device_config->slots;
for (int i = 0; i < 16; i++) {
if (slot_config[i].key_type_allowed == type) {
/* If a slot is occupied, but the stored key is not marked as persistent,
we treat it as an empty slot and overwrite the key */
if (slot_config[i].slot_occupied && slot_config[i].key_persistent) {
continue;
}
*key_slot = i;
(&slot_config[i])->slot_occupied = 1;
DEBUG("Allocating slot %d for key type %x\n", (int)*key_slot, attributes->type);
if (PSA_KEY_LIFETIME_GET_PERSISTENCE(attributes->lifetime) != \
PSA_KEY_PERSISTENCE_VOLATILE) {
(&slot_config[i])->key_persistent = 1;
}
return PSA_SUCCESS;
}
}
(void)drv_context;
(void)method;
DEBUG("ATCA allocate: No free slot available.\n");
return PSA_ERROR_INSUFFICIENT_STORAGE;
}
psa_status_t atca_destroy(psa_drv_se_context_t *drv_context,
void *persistent_data,
psa_key_slot_number_t key_slot)
{
/**
* The ATECCX08A driver does not provide a key destruction function.
*
* This function just sets the `slot_occupied` flag in the driver's persistent
* data to 0, in case the key generation or import goes wrong, so the slot can
* be reused.
*/
psa_atca_slot_config_t *slot_config = (psa_atca_slot_config_t *)persistent_data;
DEBUG("Setting Key Slot %d to not occupied.\n", (int)key_slot);
(&slot_config[key_slot])->slot_occupied = 0;
(void)drv_context;
return PSA_SUCCESS;
}
psa_status_t atca_import(psa_drv_se_context_t *drv_context,
psa_key_slot_number_t key_slot,
const psa_key_attributes_t *attributes,
const uint8_t *data,
size_t data_length,
size_t *bits)
{
ATCA_STATUS status;
ATCADevice dev = (ATCADevice)drv_context->transient_data;
if (!ALG_IS_SUPPORTED(attributes->policy.alg)) {
DEBUG("ATCA import: Algorithm is not supported by device.\n");
return PSA_ERROR_NOT_SUPPORTED;
}
if (!KEY_SIZE_IS_SUPPORTED(data_length, attributes->type)) {
DEBUG("ATCA import: Key size is not supported by device.\n");
return PSA_ERROR_NOT_SUPPORTED;
}
if (PSA_KEY_TYPE_IS_ECC_PUBLIC_KEY(attributes->type)) {
status = calib_write_pubkey(dev, key_slot, data + 1);
if (status != ATCA_SUCCESS) {
DEBUG("ATCA Write Pubkey failed. ATCA Error: %s\n",
atca_status_to_humanly_readable(status));
return atca_to_psa_error(status);
}
*bits = PSA_BYTES_TO_BITS(data_length);
return PSA_SUCCESS;
}
else {
if (data_length > ATCA_WRITE_BUFFER_SIZE) {
DEBUG("ATCA import: Key size too large.\n");
return PSA_ERROR_INVALID_ARGUMENT;
}
uint8_t buf_in[ATCA_WRITE_BUFFER_SIZE] = { 0 };
/* AES keys can be written to slots in 32 or 64 byte chunks. For this we
copy them into a 32 byte buffer first and pad the empty bits with 0 */
memcpy(buf_in, data, data_length);
status = calib_write_bytes_zone(dev, ATCA_ZONE_DATA, key_slot, 0, buf_in, sizeof(buf_in));
if (status != ATCA_SUCCESS) {
DEBUG("ATCA Write AES key failed. ATCA Error: %s\n",
atca_status_to_humanly_readable(status));
return atca_to_psa_error(status);
}
*bits = PSA_BYTES_TO_BITS(data_length);
return PSA_SUCCESS;
}
DEBUG("ATCA import: Operation is not supported.\n");
return PSA_ERROR_NOT_SUPPORTED;
}
psa_status_t atca_generate_key(psa_drv_se_context_t *drv_context,
psa_key_slot_number_t key_slot,
const psa_key_attributes_t *attributes,
uint8_t *pubkey, size_t pubkey_size, size_t *pubkey_length)
{
ATCA_STATUS status;
ATCADevice dev = (ATCADevice)drv_context->transient_data;
if (!PSA_KEY_TYPE_IS_ECC(attributes->type)) {
DEBUG("ATCA Generate Key: Only ECC key generation is supported.\n");
return PSA_ERROR_NOT_SUPPORTED;
}
if (pubkey_size > PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(attributes->type, attributes->bits)) {
DEBUG("ATCA Generate Key: Pubkey size not supported. Expected %d, was %d.\n", \
PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(attributes->type, attributes->bits), pubkey_size);
return PSA_ERROR_NOT_SUPPORTED;
}
if (pubkey != NULL) {
/**
* PSA supports the uncompressed binary format to encode ECC public keys.
* This means, that both the x and y coordinate are stored.
* This format is encoded by adding an extra byte containing the value 0x04
* (see also "Technical Guideline BSI TR-03111").
*
* The ATECCX08A stores and exports the x and y coordinate, so all we need to do is
* add the byte before the coordinates.
*/
pubkey[0] = 0x04;
*pubkey_length = PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(attributes->type, attributes->bits);
status = calib_genkey(dev, key_slot, &pubkey[1]);
}
else {
status = calib_genkey(dev, key_slot, NULL);
}
if (status != ATCA_SUCCESS) {
DEBUG("ATCA Genkey failed. ATCA Error: %s\n",
atca_status_to_humanly_readable(status));
return atca_to_psa_error(status);
}
return PSA_SUCCESS;
}
psa_status_t atca_export_public_key(psa_drv_se_context_t *drv_context,
psa_key_slot_number_t key_slot,
uint8_t *p_data,
size_t data_size,
size_t *p_data_length)
{
ATCA_STATUS status;
ATCADevice dev = (ATCADevice)drv_context->transient_data;
if (data_size < ECC_P256_PUB_KEY_SIZE) {
DEBUG("ATCA Export public key: Buffer too small, expected %d, was %d\n", \
ECC_P256_PUB_KEY_SIZE, data_size);
return PSA_ERROR_BUFFER_TOO_SMALL;
}
status = calib_get_pubkey(dev, key_slot, &p_data[1]);
if (status != ATCA_SUCCESS) {
DEBUG("ATCA Get Pubkey failed. ATCA Error: %s\n",
atca_status_to_humanly_readable(status));
return atca_to_psa_error(status);
}
/**
* PSA supports the uncompressed binary format to encode ECC public keys.
* This means, that both the x and y coordinate are stored.
* This format is encoded by adding an extra byte containing the value 0x04
* (see also "Technical Guideline BSI TR-03111").
*
* The ATECCX08A stored and exports the x and y coordinate, so all we need to do is
* add the byte before the coordinates.
*/
p_data[0] = 0x04;
/* Since we added the encoding byte, the stored public key is now 65 bytes long instead of 64 */
*p_data_length = ECC_P256_PUB_KEY_SIZE + 1;
return PSA_SUCCESS;
}
psa_status_t atca_sign(psa_drv_se_context_t *drv_context,
psa_key_slot_number_t key_slot,
psa_algorithm_t alg,
const uint8_t *p_hash,
size_t hash_length,
uint8_t *p_signature,
size_t signature_size,
size_t *p_signature_length)
{
ATCA_STATUS status;
ATCADevice dev = (ATCADevice)drv_context->transient_data;
if (alg != PSA_ALG_ECDSA(PSA_ALG_SHA_256)) {
DEBUG("ATCA Sign: Only ECDSA with SHA256 Messages is supported.\n");
return PSA_ERROR_NOT_SUPPORTED;
}
if ((signature_size != ECC_P256_PUB_KEY_SIZE) || \
(hash_length != PSA_HASH_LENGTH(PSA_ALG_SHA_256))) {
DEBUG("ATCA Sign: Invalid signature or hash size. Expected Sig: %d |\
Hash %d, were Sig: %d | Hash: %d\n",\
ECC_P256_PUB_KEY_SIZE, PSA_HASH_LENGTH(PSA_ALG_SHA_256),\
signature_size, hash_length);
return PSA_ERROR_INVALID_ARGUMENT;
}
status = calib_sign(dev, key_slot, p_hash, p_signature);
if (status != ATCA_SUCCESS) {
DEBUG("ATCA Sign failed. ATCA Error: %s\n",
atca_status_to_humanly_readable(status));
return atca_to_psa_error(status);
}
*p_signature_length = signature_size;
return PSA_SUCCESS;
}
psa_status_t atca_verify(psa_drv_se_context_t *drv_context,
psa_key_slot_number_t key_slot,
psa_algorithm_t alg,
const uint8_t *p_hash,
size_t hash_length,
const uint8_t *p_signature,
size_t signature_length)
{
ATCA_STATUS status;
ATCADevice dev = (ATCADevice)drv_context->transient_data;
bool is_verified;
/* We only support the operation on public keys, if they're stored on a device. */
if (alg != PSA_ALG_ECDSA(PSA_ALG_SHA_256)) {
DEBUG("ATCA Verify: Only ECDSA with SHA256 Messages is supported.\n");
return PSA_ERROR_NOT_SUPPORTED;
}
if ((signature_length != ECC_P256_PUB_KEY_SIZE) || \
(hash_length != PSA_HASH_LENGTH(PSA_ALG_SHA_256))) {
DEBUG("ATCA Sign: Invalid signature or hash size. Expected Sig: %d |\
Hash %d, were Sig: %d | Hash: %d\n",\
ECC_P256_PUB_KEY_SIZE, PSA_HASH_LENGTH(PSA_ALG_SHA_256),\
signature_length, hash_length);
return PSA_ERROR_INVALID_ARGUMENT;
}
status = calib_verify_stored(dev, p_hash, p_signature, key_slot, &is_verified);
if (status != ATCA_SUCCESS) {
DEBUG("ATCA Verify failed. ATCA Error: %s\n",
atca_status_to_humanly_readable(status));
return atca_to_psa_error(status);
}
return is_verified ? PSA_SUCCESS : PSA_ERROR_INVALID_SIGNATURE;
}
psa_status_t atca_generate_mac(psa_drv_se_context_t *drv_context,
const uint8_t *p_input,
size_t input_length,
psa_key_slot_number_t key_slot,
psa_algorithm_t alg,
uint8_t *p_mac,
size_t mac_size,
size_t *p_mac_length)
{
ATCA_STATUS status;
ATCADevice dev = (ATCADevice)drv_context->transient_data;
if (!PSA_ALG_IS_HMAC(alg)) {
DEBUG("ATCA Generate MAC: Only HMAC SHA256 is supported.\n");
return PSA_ERROR_NOT_SUPPORTED;
}
if (mac_size < PSA_HASH_LENGTH(PSA_ALG_SHA_256)) {
DEBUG("ATCA Generate Mac: Buffer too small, expected %d, was %d\n", \
PSA_HASH_LENGTH(PSA_ALG_SHA_256), mac_size);
return PSA_ERROR_BUFFER_TOO_SMALL;
}
status = calib_sha_hmac(dev, p_input, input_length, key_slot, p_mac, SHA_MODE_TARGET_OUT_ONLY);
if (status != ATCA_SUCCESS) {
DEBUG("ATCA SHA HMAC failed. ATCA Error: %s\n",
atca_status_to_humanly_readable(status));
return atca_to_psa_error(status);
}
*p_mac_length = PSA_HASH_LENGTH(PSA_ALG_SHA_256);
return PSA_SUCCESS;
}
static psa_drv_se_mac_t atca_mac = {
.context_size = 0,
.p_setup = NULL,
.p_update = NULL,
.p_finish = NULL,
.p_finish_verify = NULL,
.p_abort = NULL,
.p_mac = atca_generate_mac,
.p_mac_verify = NULL,
};
static psa_drv_se_cipher_t atca_cipher = {
.context_size = 0,
.p_setup = atca_cipher_setup,
.p_set_iv = atca_cipher_set_iv,
.p_update = atca_cipher_update,
.p_finish = atca_cipher_finish,
.p_abort = NULL,
.p_ecb = atca_cipher_ecb
};
static psa_drv_se_key_management_t atca_key_management = {
.p_allocate = atca_allocate,
.p_validate_slot_number = NULL,
.p_import = atca_import,
.p_generate = atca_generate_key,
.p_destroy = atca_destroy,
.p_export = NULL,
.p_export_public = atca_export_public_key
};
static psa_drv_se_asymmetric_t atca_asymmetric = {
.p_sign = atca_sign,
.p_verify = atca_verify,
.p_encrypt = NULL,
.p_decrypt = NULL
};
psa_drv_se_t atca_methods = {
.hal_version = PSA_DRV_SE_HAL_VERSION,
.persistent_data_size = 0,
.p_init = NULL,
.key_management = &atca_key_management,
.mac = &atca_mac,
.cipher = &atca_cipher,
.aead = NULL,
.asymmetric = &atca_asymmetric,
.derivation = NULL
};