1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

sys/psa_crypto: implement persistent key storage

This commit is contained in:
Lena Boeckmann 2023-11-21 16:14:23 +01:00
parent a9e7b441a7
commit cbadc4f658
27 changed files with 2237 additions and 91 deletions

View File

@ -1503,6 +1503,10 @@ psa_status_t psa_copy_key(psa_key_id_t source_key,
* If a key is currently in use in a multi-part operation, then destroying the key will
* cause the multi-part operation to fail.
*
* @warning This implementation uses a virtual file system for storing and reading persistent keys
* to and from flash. Destroying a key only unlinks the file and does not erase the actual
* key data from flash. Anyone with hardware access can still recover the key material.
*
* @param key Identifier of the key to erase. If this is @ref PSA_KEY_ID_NULL, do nothing and
* return @ref PSA_SUCCESS.
*

View File

@ -8,6 +8,10 @@ ifneq (,$(filter psa_key_slot_mgmt,$(USEMODULE)))
DIRS += psa_key_slot_mgmt
endif
ifneq (,$(filter psa_persistent_storage,$(USEMODULE)))
DIRS += psa_persistent_storage
endif
ifneq (,$(filter psa_se_mgmt,$(USEMODULE)))
DIRS += psa_se_mgmt
endif

View File

@ -3,6 +3,15 @@ ifneq (,$(filter psa_crypto,$(USEMODULE)))
USEMODULE += prng_sha256prng
endif
ifneq (,$(filter psa_persistent_storage, $(USEMODULE)))
USEPKG += nanocbor
USEPKG += littlefs2
USEMODULE += vfs
USEMODULE += vfs_default
USEMODULE += vfs_auto_format
USEMODULE += vfs_auto_mount
endif
# Asymmetric
ifneq (,$(filter psa_asymmetric,$(USEMODULE)))
USEMODULE += psa_key_management

View File

@ -112,8 +112,13 @@
* Persistent keys will also be written into flash memory for later access. To destroy
* them they must be explicitly deleted with the `psa_destroy_key()` function.
*
* @note So far this implementation only supports volatile storage. Persistent storage
* will be added in the future.
* @note Persistent key storage can be optionally enabled on `native` and on the `nRF52840dk`.
* For this, add `USEMODULE += psa_persistent_storage` to your application makefile
* or `CONFIG_MODULE_PSA_PERSISTENT_STORAGE=y` to your `app.config.test` file.
* Example: `tests/sys/psa_crypto_persistent_storage`
*
* @warning Be aware that the current implementation writes keys in plain text to flash memory.
* Anyone with hardware access can read them.
*
* #### Lifetime Encoding
* When creating a key, the user needs to specify a lifetime value, which actually consists
@ -220,6 +225,11 @@
* CFLAGS += -DCONFIG_PSA_PROTECTED_KEY_COUNT=2
* @endcode
*
* @note The key slot count defines the maximum number of keys that can be cached in
* RAM at runtime. It does not limit the number of persistent keys that can be stored
* in flash memory. It is the user's responsibility to keep track of the number of
* persistently stored keys.
*
* ## Available Modules {#available-modules}
* Below are the currently available modules.
* No matter which operation you need, you always have to choose the base module.
@ -233,6 +243,9 @@
* When using `app.config.test` files in your application directory, you need to write the
* names in uppercase and add the prefix `CONFIG_MODULE_` to all of them.
*
* ### Key Storage
* - Persistent Key Storage: psa_persistent_storage
*
* ### Asymmetric Crypto
* - Base: psa_asymmetric
*

View File

@ -0,0 +1,149 @@
/*
* Copyright (C) 2023 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
* @defgroup sys_psa_crypto_cbor_encoder Module for encoding PSA keys in CBOR
* @{
*
* @file psa_crypto_cbor_encoder.h
* @brief
*
* @author Lena Boeckmann <lena.boeckmann@haw-hamburg.de>
*
*/
#ifndef PSA_CRYPTO_CBOR_ENCODER_H
#define PSA_CRYPTO_CBOR_ENCODER_H
#ifdef __cplusplus
extern "C" {
#endif
#include "psa_crypto_slot_management.h"
/**
* @brief Required size of CBOR output buffer from start to end of attributes.
* Adds attributes sizes to CBOR encodings for individual values.
*/
#define CBOR_BUF_SIZE_START ( 1 + /* Array encoding */ \
1 + /* Array encoding */ \
1 + sizeof(psa_key_id_t) + \
1 + sizeof(psa_key_type_t) + \
1 + sizeof(psa_key_bits_t) + \
1 + sizeof(psa_key_lifetime_t) + \
1 + /* Array encoding */ \
1 + sizeof(psa_key_usage_t) + \
1 + sizeof(psa_algorithm_t) \
)
#if PSA_SINGLE_KEY_COUNT
/**
* @brief Required CBOR buffer size to encode a basic PSA key slot containing
* a single key.
*/
#define CBOR_BUF_SIZE_SINGLE_KEY ( CBOR_BUF_SIZE_START + \
3 + /* Bytestring encoding and size */ \
PSA_MAX_KEY_DATA_SIZE \
)
#endif /* PSA_SINGLE_KEY_COUNT */
#if PSA_ASYMMETRIC_KEYPAIR_COUNT
/**
* @brief Required CBOR buffer size to encode a basic PSA key slot containing
* an asymmetric key pair.
*/
#define CBOR_BUF_SIZE_KEY_PAIR ( CBOR_BUF_SIZE_START + \
1 + \
3 + PSA_BITS_TO_BYTES(PSA_MAX_PRIV_KEY_SIZE) + \
3 + PSA_EXPORT_PUBLIC_KEY_MAX_SIZE \
)
#endif /* PSA_ASYMMETRIC_KEYPAIR_COUNT */
#if PSA_PROTECTED_KEY_COUNT && IS_USED(MODULE_PSA_ASYMMETRIC)
/**
* @brief Required CBOR buffer size to encode a basic PSA key slot containing
* a key in protected memory.
*/
#define CBOR_BUF_SIZE_PROT_KEY ( CBOR_BUF_SIZE_START + \
1 + \
1 + sizeof(psa_key_slot_number_t) + \
3 + PSA_EXPORT_PUBLIC_KEY_MAX_SIZE \
)
#elif PSA_PROTECTED_KEY_COUNT
/**
* @brief Required CBOR buffer size to encode a basic PSA key slot containing
* a key in protected memory.
*/
#define CBOR_BUF_SIZE_PROT_KEY ( CBOR_BUF_SIZE_START + \
1 + \
1 + sizeof(psa_key_slot_number_t) \
)
#endif /* PSA_PROTECTED_KEY_COUNT */
/**
* @brief Encodes a basic key slot in CBOR
*
* Single Key Format:
* - [
* [ID, Type, Bits, Lifetime, [Usage, Algorithm]],
* h'key
* ]
*
* Asymmetric Key Pair Format:
* - [
* [ID, Type, Bits, Lifetime, [Usage, Algorithm]],
* [h'private_key, h'public_key]
* ]
*
* Protected Key Format:
* - [
* [ID, Type, Bits, Lifetime, [Usage, Algorithm]],
* [Slot No, *optional: h'public_key*]
* ]
*
* @param[in] slot Pointer to slot containing the key to encode
* @param[in] output Buffer to write the encoded key to
* @param[in] output_len Length of output buffer
* @param[out] output_size Pointer to write actual length of encoding
*
* @return psa_status_t
*/
psa_status_t psa_encode_key_slot(psa_key_slot_t *slot, uint8_t *output,
size_t output_len, size_t *output_size);
/**
* @brief Decode CBOR encoded key data and write to PSA key slot. Only decodes the key and should
* be called in combination with psa_decode_key_attributes.
*
* @param slot Pointer to key slot to write decoded key to
* @param cbor_buf Buffer containing CBOR encoded data
* @param cbor_buf_size Size of @p cbor_buf
* @return psa_status_t
*/
psa_status_t psa_decode_key_slot_data(psa_key_slot_t *slot, uint8_t *cbor_buf,
size_t cbor_buf_size);
/**
* @brief Decode CBOR PSA key attributes. Only decodes key attributes and not the actual key.
* Key can be decoded with psa_decode_key_slot_data.
*
* @param attr Key attribute struct to store decoded attributes
* @param cbor_buf Buffer containing CBOR encoded data
* @param cbor_buf_size Size of @p cbor_buf
* @return psa_status_t
*/
psa_status_t psa_decode_key_attributes(psa_key_attributes_t *attr, uint8_t *cbor_buf,
size_t cbor_buf_size);
#ifdef __cplusplus
}
#endif
#endif /* PSA_CRYPTO_CBOR_ENCODER_H */
/** @} */

View File

@ -0,0 +1,73 @@
/*
* Copyright (C) 2023 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
* @defgroup sys_psa_crypto_pers_stor PSA Crypto Persistent Storage API
* @{
*
* @file psa_crypto_persistent_storage.h
* @brief
*
* @author Lena Boeckmann <lena.boeckmann@haw-hamburg.de>
*
*/
#ifndef PSA_CRYPTO_PERSISTENT_STORAGE_H
#define PSA_CRYPTO_PERSISTENT_STORAGE_H
#ifdef __cplusplus
extern "C" {
#endif
#include "psa/crypto.h"
/**
* @brief Writes a CBOR encoded key slot to a file
*
* @param id ID of slot, used as filename
* @param input Pointer to CBOR encoded data
* @param input_len Length of CBOR encoded data
* @return psa_status_t
*/
psa_status_t psa_write_encoded_key_slot_to_file(psa_key_id_t id,
uint8_t* input,
size_t input_len);
/**
* @brief Reads a CBOR encoded key slot from a file
*
* @param id ID of the desired key
* @param output Output buffer to write CBOR data to
* @param output_size Size of output buffer
* @param output_data_len Actual length of CBOR encoded data
* @return psa_status_t
*/
psa_status_t psa_read_encoded_key_slot_from_file(psa_key_id_t id,
uint8_t *output,
size_t output_size,
size_t *output_data_len);
/**
* @brief Destroy a key in persistent storage
*
* @note This will only remove the link to the key file without erasing the
* key from the flash. The key material can still be recovered by someone
* with access to the hardware.
*
* @param key_id ID of the key to be destroyed
* @return psa_status_t
*/
psa_status_t psa_destroy_persistent_key(psa_key_id_t key_id);
#ifdef __cplusplus
}
#endif
#endif /* PSA_CRYPTO_PERSISTENT_STORAGE_H */
/** @} */

View File

@ -86,6 +86,51 @@ typedef struct {
#endif /* PSA_SINGLE_KEY_COUNT */
} psa_key_slot_t;
#if PSA_PROTECTED_KEY_COUNT
/**
* @brief Structure for a protected key slot.
*
* These slots hold Slot Numbers for keys in protected storage and, if the key type is an
* asymmetric key pair, the public key.
*/
typedef struct {
clist_node_t node;
size_t lock_count;
psa_key_attributes_t attr;
struct prot_key_data {
psa_key_slot_number_t slot_number;
#if IS_USED(MODULE_PSA_ASYMMETRIC)
uint8_t pubkey_data[PSA_EXPORT_PUBLIC_KEY_MAX_SIZE];
size_t pubkey_data_len;
#endif
} key;
} psa_prot_key_slot_t;
#endif /* PSA_PROTECTED_KEY_COUNT */
#if PSA_ASYMMETRIC_KEYPAIR_COUNT
/**
* @brief Structure for asymmetric key pairs.
*
* Contains asymmetric private and public key pairs.
*
*/
typedef struct {
clist_node_t node;
size_t lock_count;
psa_key_attributes_t attr;
struct key_pair_data {
/** Contains asymmetric private key*/
uint8_t privkey_data[PSA_BITS_TO_BYTES(PSA_MAX_PRIV_KEY_SIZE)];
/** Contains actual size of asymmetric private key */
size_t privkey_data_len;
/** Contains asymmetric public key */
uint8_t pubkey_data[PSA_EXPORT_PUBLIC_KEY_MAX_SIZE];
/*!< Contains actual size of asymmetric private key */
size_t pubkey_data_len;
} key;
} psa_key_pair_slot_t;
#endif /* PSA_ASYMMETRIC_KEYPAIR_COUNT */
/**
* @brief Initializes the allocated key slots and prepares the internal key slot lists.
*/
@ -165,6 +210,14 @@ void psa_wipe_all_key_slots(void);
*/
psa_status_t psa_get_and_lock_key_slot(psa_key_id_t id, psa_key_slot_t **slot);
/**
* @brief Store a key slot in persistent storage
*
* @param slot Pointer to slot to store in persistent storage
* @return psa_status_t
*/
psa_status_t psa_persist_key_slot_in_storage(psa_key_slot_t *slot);
/**
* @brief Find a currently empty key slot that is appropriate for the key.
*

View File

@ -30,6 +30,10 @@
#include "psa_crypto_location_dispatch.h"
#include "psa_crypto_algorithm_dispatch.h"
#if IS_USED(MODULE_PSA_PERSISTENT_STORAGE)
#include "psa_crypto_persistent_storage.h"
#endif /* MODULE_PSA_PERSISTENT_STORAGE */
#include "random.h"
#include "kernel_defines.h"
@ -394,10 +398,10 @@ static psa_status_t psa_key_policy_permits( const psa_key_policy_t *policy,
* @ref PSA_ERROR_NOT_SUPPORTED
* @ref PSA_ERROR_CORRUPTION_DETECTED
*/
static psa_status_t psa_get_and_lock_key_slot_with_policy( psa_key_id_t id,
psa_key_slot_t **p_slot,
psa_key_usage_t usage,
psa_algorithm_t alg)
static psa_status_t psa_get_and_lock_key_slot_with_policy(psa_key_id_t id,
psa_key_slot_t **p_slot,
psa_key_usage_t usage,
psa_algorithm_t alg)
{
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
psa_key_slot_t *slot;
@ -1080,11 +1084,13 @@ static psa_status_t psa_validate_key_attributes(const psa_key_attributes_t *attr
return PSA_ERROR_INVALID_ARGUMENT;
}
}
#if IS_USED(MODULE_PSA_PERSISTENT_STORAGE)
else {
if (!psa_is_valid_key_id(key, 0)) {
if (!psa_is_valid_key_id(key, 1)) {
return PSA_ERROR_INVALID_ARGUMENT;
}
}
#endif /* MODULE_PSA_PERSISTENT_STORAGE */
status = psa_validate_key_policy(&attributes->policy);
if (status != PSA_SUCCESS) {
@ -1152,29 +1158,27 @@ static psa_status_t psa_start_key_creation(psa_key_creation_method_t method,
*
* @param slot Pointer to slot that the key is stored in
* @param driver SE driver, in case the key creation took place on a secure element
* @param key Pointer which will contain the key ID assigned to the key
* @param key_id Pointer which will contain the key ID assigned to the key
*
* @return @ref psa_status_t
*/
static psa_status_t psa_finish_key_creation(psa_key_slot_t *slot, psa_se_drv_data_t *driver,
psa_key_id_t *key)
psa_key_id_t *key_id)
{
psa_status_t status = PSA_SUCCESS;
*key = PSA_KEY_ID_NULL;
/* TODO: Finish persistent key storage */
/* TODO: Finish SE key storage with transaction */
if (status == PSA_SUCCESS) {
*key = slot->attr.id;
status = psa_unlock_key_slot(slot);
if (PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) {
*key_id = slot->attr.id;
}
#if IS_USED(MODULE_PSA_PERSISTENT_STORAGE)
else {
(void)slot;
status = psa_persist_key_slot_in_storage(slot);
}
#endif /* MODULE_PSA_PERSISTENT_STORAGE */
(void)driver;
return status;
psa_status_t unlock_status = psa_unlock_key_slot(slot);
return ((status == PSA_SUCCESS) ? unlock_status : status);
}
/**
@ -1217,6 +1221,17 @@ psa_status_t psa_destroy_key(psa_key_id_t key)
return PSA_ERROR_CORRUPTION_DETECTED;
}
#if IS_USED(MODULE_PSA_PERSISTENT_STORAGE)
if (!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) {
status = psa_destroy_persistent_key(key);
if (status != PSA_SUCCESS) {
DEBUG("PSA destroy key: Persistent key destruction failed: %s\n",
psa_status_to_humanly_readable(status));
return PSA_ERROR_STORAGE_FAILURE;
}
}
#endif /* MODULE_PSA_PERSISTENT_STORAGE */
return psa_wipe_key_slot(slot);
}
@ -1351,8 +1366,6 @@ psa_status_t psa_generate_key(const psa_key_attributes_t *attributes,
psa_key_slot_t *slot = NULL;
psa_se_drv_data_t *driver = NULL;
*key = PSA_KEY_ID_NULL;
if (!lib_initialized) {
return PSA_ERROR_BAD_STATE;
}
@ -1365,6 +1378,10 @@ psa_status_t psa_generate_key(const psa_key_attributes_t *attributes,
return PSA_ERROR_INVALID_ARGUMENT;
}
if (PSA_KEY_LIFETIME_IS_VOLATILE(attributes->lifetime)) {
*key = PSA_KEY_ID_NULL;
}
/* Find empty slot */
status = psa_start_key_creation(PSA_KEY_CREATION_GENERATE, attributes, &slot, &driver);
if (status != PSA_SUCCESS) {
@ -1387,7 +1404,6 @@ psa_status_t psa_generate_key(const psa_key_attributes_t *attributes,
}
status = psa_finish_key_creation(slot, driver, key);
if (status != PSA_SUCCESS) {
psa_fail_key_creation(slot, driver);
}
@ -1485,7 +1501,9 @@ psa_status_t psa_import_key(const psa_key_attributes_t *attributes,
return PSA_ERROR_INVALID_ARGUMENT;
}
*key = PSA_KEY_ID_NULL;
if (PSA_KEY_LIFETIME_IS_VOLATILE(attributes->lifetime)) {
*key = PSA_KEY_ID_NULL;
}
/* Find empty slot */
status = psa_start_key_creation(PSA_KEY_CREATION_IMPORT, attributes, &slot, &driver);

View File

@ -25,26 +25,29 @@
#define ENABLE_DEBUG 0
#include "debug.h"
#if PSA_PROTECTED_KEY_COUNT
/**
* @brief Structure for a protected key slot.
*
* These slots hold Slot Numbers for keys in protected storage and, if the key type is an
* asymmetric key pair, the public key.
*/
typedef struct {
clist_node_t node;
size_t lock_count;
psa_key_attributes_t attr;
struct prot_key_data {
psa_key_slot_number_t slot_number;
#if IS_USED(MODULE_PSA_ASYMMETRIC)
uint8_t pubkey_data[PSA_EXPORT_PUBLIC_KEY_MAX_SIZE];
size_t pubkey_data_len;
#endif
} key;
} psa_prot_key_slot_t;
#if IS_USED(MODULE_PSA_PERSISTENT_STORAGE)
#include "psa_crypto_persistent_storage.h"
#include "psa_crypto_cbor_encoder.h"
/**
* @brief Max size required for the CBOR buffer.
*
* Depends on the key type and size.
*/
#if PSA_ASYMMETRIC_KEYPAIR_COUNT
#define CBOR_BUF_MAX_SIZE (CBOR_BUF_SIZE_KEY_PAIR)
#elif PSA_PROTECTED_KEY_COUNT && IS_USED(MODULE_PSA_ASYMMETRIC)
#define CBOR_BUF_MAX_SIZE (CBOR_BUF_SIZE_PROT_KEY)
#elif PSA_SINGLE_KEY_COUNT
#define CBOR_BUF_MAX_SIZE (CBOR_BUF_SIZE_SINGLE_KEY)
#elif PSA_PROTECTED_KEY_COUNT
#define CBOR_BUF_MAX_SIZE (CBOR_BUF_SIZE_PROT_KEY)
#else
#define CBOR_BUF_MAX_SIZE (0)
#endif
#endif /* MODULE_PSA_PERSISTENT_STORAGE */
#if PSA_PROTECTED_KEY_COUNT
/**
* @brief Array containing the protected key slots
*/
@ -57,28 +60,6 @@ static clist_node_t protected_list_empty;
#endif /* PSA_PROTECTED_KEY_COUNT */
#if PSA_ASYMMETRIC_KEYPAIR_COUNT
/**
* @brief Structure for asymmetric key pairs.
*
* Contains asymmetric private and public key pairs.
*
*/
typedef struct {
clist_node_t node;
size_t lock_count;
psa_key_attributes_t attr;
struct key_pair_data {
/** Contains asymmetric private key*/
uint8_t privkey_data[PSA_BITS_TO_BYTES(PSA_MAX_PRIV_KEY_SIZE)];
/** Contains actual size of asymmetric private key */
size_t privkey_data_len;
/** Contains asymmetric public key */
uint8_t pubkey_data[PSA_EXPORT_PUBLIC_KEY_MAX_SIZE];
/*!< Contains actual size of asymmetric private key */
size_t pubkey_data_len;
} key;
} psa_key_pair_slot_t;
/**
* @brief Array containing the asymmetric key slots
*/
@ -276,6 +257,19 @@ static int node_id_equals_key_id(clist_node_t *n, void *arg)
return 0;
}
#if IS_USED(MODULE_PSA_PERSISTENT_STORAGE)
static int node_lifetime_is_persistent(clist_node_t *n, void *arg)
{
psa_key_slot_t *slot = container_of(n, psa_key_slot_t, node);
if (!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) {
return 1;
}
(void) arg;
return 0;
}
#endif /* MODULE_PSA_PERSISTENT_STORAGE */
/**
* @brief Find the key slot containing the key with a specified ID
*
@ -291,37 +285,107 @@ static psa_status_t psa_get_and_lock_key_slot_in_memory(psa_key_id_t id, psa_key
{
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
if (psa_key_id_is_volatile(id)) {
clist_node_t *slot_node = clist_foreach(&key_slot_list, node_id_equals_key_id, &id);
if (slot_node == NULL) {
return PSA_ERROR_INVALID_HANDLE;
}
clist_node_t *slot_node = clist_foreach(&key_slot_list, node_id_equals_key_id, &id);
if (slot_node == NULL) {
return PSA_ERROR_DOES_NOT_EXIST;
}
psa_key_slot_t *slot = container_of(slot_node, psa_key_slot_t, node);
status = psa_lock_key_slot(slot);
if (status == PSA_SUCCESS) {
*p_slot = slot;
}
psa_key_slot_t *slot = container_of(slot_node, psa_key_slot_t, node);
status = psa_lock_key_slot(slot);
if (status == PSA_SUCCESS) {
*p_slot = slot;
}
return status;
}
#if IS_USED(MODULE_PSA_PERSISTENT_STORAGE)
/**
* @brief Reads a persistently stored key from storage
*
* @param id ID of the desired key
* @param p_slot Pointer to store key slot in
* @return psa_status_t
*/
static psa_status_t psa_get_persisted_key_slot_from_storage(psa_key_id_t id,
psa_key_slot_t **p_slot)
{
uint8_t cbor_buf[CBOR_BUF_MAX_SIZE];
size_t cbor_encoded_len;
psa_key_attributes_t attr = psa_key_attributes_init();
psa_status_t status = psa_read_encoded_key_slot_from_file(id, cbor_buf, sizeof(cbor_buf), &cbor_encoded_len);
if (status != PSA_SUCCESS) {
return status;
}
return PSA_ERROR_NOT_SUPPORTED;
/* Decode key attributes first, to get information about key */
status = psa_decode_key_attributes(&attr, cbor_buf, cbor_encoded_len);
if (status != PSA_SUCCESS) {
return status;
}
/* Allocate key slot for specific key type */
status = psa_allocate_empty_key_slot(&attr.id, &attr, p_slot);
if (status != PSA_SUCCESS) {
return status;
}
(*p_slot)->attr = attr;
/* Decode rest of key data */
return psa_decode_key_slot_data(*p_slot, cbor_buf, cbor_encoded_len);
}
psa_status_t psa_persist_key_slot_in_storage(psa_key_slot_t *slot)
{
if (psa_key_id_is_volatile(slot->attr.id) ||
PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) {
DEBUG("[psa_crypto_slot_mgmt] persist key: ID or lifetime is volatile\n");
return PSA_ERROR_INVALID_ARGUMENT;
}
uint8_t cbor_buf[CBOR_BUF_MAX_SIZE];
size_t cbor_enc_size;
psa_encode_key_slot(slot, cbor_buf, sizeof(cbor_buf), &cbor_enc_size);
DEBUG("[psa_crypto_slot_mgmt] Max Key Slot Size: %d | CBOR enc size: %d\n",
(int) CBOR_BUF_MAX_SIZE, (int) cbor_enc_size);
return psa_write_encoded_key_slot_to_file(slot->attr.id, cbor_buf, cbor_enc_size);
}
/**
* @brief Find and wipe a persistent key slot in local storage to make room for a new key
*
* @return PSA_SUCCESS
* @return PSA_ERROR_INSUFFICIENT_STORAGE No persistent key found in local storage
* PSA_ERROR_DOES_NOT_EXIST
*/
static psa_status_t psa_find_and_wipe_persistent_key_from_local_storage(void)
{
clist_node_t *slot_node = clist_foreach(&key_slot_list, node_lifetime_is_persistent, NULL);
if (slot_node == NULL) {
return PSA_ERROR_INSUFFICIENT_STORAGE;
}
return psa_wipe_key_slot(container_of(slot_node, psa_key_slot_t, node));
}
#endif /* MODULE_PSA_PERSISTENT_STORAGE */
psa_status_t psa_get_and_lock_key_slot(psa_key_id_t id, psa_key_slot_t **p_slot)
{
/* TODO validate ID */
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
*p_slot = NULL;
/* Try to find key in volatile key slot list */
status = psa_get_and_lock_key_slot_in_memory(id, p_slot);
if (status != PSA_ERROR_DOES_NOT_EXIST) {
return status;
}
/* TODO: get persistent key from storage and load into slot */
#if IS_USED(MODULE_PSA_PERSISTENT_STORAGE)
if (status == PSA_ERROR_DOES_NOT_EXIST && !psa_key_id_is_volatile(id)) {
return psa_get_persisted_key_slot_from_storage(id, p_slot);
}
#endif /* MODULE_PSA_PERSISTENT_STORAGE */
return status;
}
@ -347,13 +411,20 @@ static psa_status_t psa_allocate_key_slot_in_list(psa_key_slot_t **p_slot,
/* Check if any empty elements of this key slot type are left */
if (clist_is_empty(empty_list)) {
#if IS_USED(MODULE_PSA_PERSISTENT_STORAGE)
/* If no slots left: Look for slot in list with persistent key
(key will be stored in persistent memory and slot can be reused) */
psa_status_t status = psa_find_and_wipe_persistent_key_from_local_storage();
if (status != PSA_SUCCESS) {
DEBUG("Key Slot MGMT: No PSA Key Slot available\n");
return status;
}
#else
DEBUG("Key Slot MGMT: No PSA Key Slot available\n");
return PSA_ERROR_INSUFFICIENT_STORAGE;
#endif /* MODULE_PSA_PERSISTENT_STORAGE */
}
/* TODO: If no slots left: Look for slot in list with persistent key
(key will be stored in persistent memory and slot can be reused) */
/* Remove key slote node from empty list and append to actual list */
clist_node_t *new_slot = clist_rpop(empty_list);
@ -365,15 +436,15 @@ static psa_status_t psa_allocate_key_slot_in_list(psa_key_slot_t **p_slot,
return PSA_SUCCESS;
}
psa_status_t psa_allocate_empty_key_slot( psa_key_id_t *id,
const psa_key_attributes_t *attr,
psa_key_slot_t **p_slot)
psa_status_t psa_allocate_empty_key_slot(psa_key_id_t *id,
const psa_key_attributes_t *attr,
psa_key_slot_t **p_slot)
{
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
psa_key_slot_t *new_slot = NULL;
/* Change later, when we also have persistent keys */
if (key_id_count == PSA_KEY_ID_VOLATILE_MAX) {
if (PSA_KEY_LIFETIME_IS_VOLATILE(attr->lifetime) &&
key_id_count == PSA_KEY_ID_VOLATILE_MAX) {
DEBUG("Key Slot MGMT: Maximum key ID reached\n");
return PSA_ERROR_INSUFFICIENT_STORAGE;
}
@ -392,7 +463,19 @@ psa_status_t psa_allocate_empty_key_slot( psa_key_id_t *id,
*id = 0;
return status;
}
*id = key_id_count++;
if (PSA_KEY_LIFETIME_IS_VOLATILE(attr->lifetime)) {
*id = key_id_count++;
}
#if IS_USED(MODULE_PSA_PERSISTENT_STORAGE)
else if (!psa_key_id_is_volatile(attr->id)) {
*id = attr->id;
}
#endif /* MODULE_PSA_PERSISTENT_STORAGE */
else {
DEBUG("Key Slot MGMT: invalid lifetime or ID\n");
return PSA_ERROR_INVALID_ARGUMENT;
}
*p_slot = new_slot;
return PSA_SUCCESS;
@ -456,7 +539,13 @@ psa_status_t psa_validate_key_persistence(psa_key_lifetime_t lifetime)
if (PSA_KEY_LIFETIME_IS_VOLATILE(lifetime)) {
return PSA_SUCCESS;
}
/* TODO: Implement persistent key storage */
#if IS_USED(MODULE_PSA_PERSISTENT_STORAGE)
if (PSA_KEY_LIFETIME_GET_PERSISTENCE(lifetime) == PSA_KEY_LIFETIME_PERSISTENT) {
return PSA_SUCCESS;
}
#endif /* MODULE_PSA_PERSISTENT_STORAGE */
return PSA_ERROR_NOT_SUPPORTED;
}
@ -505,7 +594,7 @@ size_t psa_get_key_data_from_key_slot(const psa_key_slot_t *slot, uint8_t **key_
return key_data_size;
}
#if IS_USED(MODULE_PSA_SECURE_ELEMENT) && PSA_PROTECTED_KEY_COUNT
#if PSA_PROTECTED_KEY_COUNT
psa_key_slot_number_t * psa_key_slot_get_slot_number(const psa_key_slot_t *slot)
{
return &(((psa_prot_key_slot_t *)slot)->key.slot_number);
@ -540,7 +629,7 @@ void psa_get_public_key_data_from_key_slot(const psa_key_slot_t *slot, uint8_t *
#endif
}
}
#if PSA_PROTECTED_KEY_COUNT && IS_USED(MODULE_PSA_ASYMMETRIC)
#if (PSA_PROTECTED_KEY_COUNT && PSA_ASYMMETRIC_KEYPAIR_COUNT)
*pubkey_data = ((psa_prot_key_slot_t *)slot)->key.pubkey_data;
*pubkey_data_len = &((psa_prot_key_slot_t *)slot)->key.pubkey_data_len;
#endif

View File

@ -0,0 +1,3 @@
INCLUDES += -I$(RIOTBASE)/sys/psa_crypto/include
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,546 @@
/*
* Copyright (C) 2023 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 sys_psa_crypto_slot_mgmt
* @{
*
* @file
* @brief API to encode PSA Crypto keys to CBOR for persistent storage
*
* @author Lena Boeckmann <lena.boeckmann@haw-hamburg.de>
*
* @}
*/
#include "nanocbor/nanocbor.h"
#include "psa_crypto_slot_management.h"
#define ENABLE_DEBUG 0
#include "debug.h"
/**
* @brief Convert a nanocbor error to a string for debugging
*
* @param res Nanocbor error value
* @return const char*
*/
static const char *nanocbor_error_to_string(int res)
{
switch (res) {
case NANOCBOR_ERR_OVERFLOW:
return "NANOCBOR_ERR_OVERFLOW";
case NANOCBOR_ERR_INVALID_TYPE:
return "NANOCBOR_ERR_INVALID_TYPE";
case NANOCBOR_ERR_END:
return "NANOCBOR_ERR_END";
case NANOCBOR_ERR_RECURSION:
return "NANOCBOR_ERR_RECURSION";
case NANOCBOR_NOT_FOUND:
return "NANOCBOR_NOT_FOUND";
default:
return "Error value not recognized";
}
}
/**
* @brief Convert nanocbor error to PSA Crypto status value
*
* @param res Nanocbor error value
* @return psa_status_t
*/
static psa_status_t nanocbor_error_to_psa_status(int res)
{
switch (res) {
case NANOCBOR_ERR_END:
return PSA_ERROR_BUFFER_TOO_SMALL;
case NANOCBOR_NOT_FOUND:
return PSA_ERROR_DOES_NOT_EXIST;
default:
return PSA_ERROR_GENERIC_ERROR;
}
}
/**
* @brief Encode a set of key attributes in CBOR
*
* @param enc Active nanocbor encoder
* @param attr Pointer to key attributes to encode
* @return psa_status_t
*/
static psa_status_t psa_encode_key_attributes(nanocbor_encoder_t *enc, psa_key_attributes_t *attr)
{
int res;
res = nanocbor_fmt_array(enc, 5);
if (res < 0) {
goto error;
}
res = nanocbor_fmt_uint(enc, attr->id);
if (res < 0) {
goto error;
}
res = nanocbor_fmt_uint(enc, attr->type);
if (res < 0) {
goto error;
}
res = nanocbor_fmt_uint(enc, attr->bits);
if (res < 0) {
goto error;
}
res = nanocbor_fmt_uint(enc, attr->lifetime);
if (res < 0) {
goto error;
}
/* Policy */
res = nanocbor_fmt_array(enc, 2);
if (res < 0) {
goto error;
}
res = nanocbor_fmt_uint(enc, attr->policy.usage);
if (res < 0) {
goto error;
}
res = nanocbor_fmt_uint(enc, attr->policy.alg);
if (res < 0) {
goto error;
}
return PSA_SUCCESS;
error:
DEBUG("[persistent storage] error while encoding key attributes: %d = %s", res,
nanocbor_error_to_string(res));
return nanocbor_error_to_psa_status(res);
}
#if PSA_PROTECTED_KEY_COUNT
/**
* @brief Encode contents of a PSA key slot containing a protected key
*
* @param enc Active nanocbor encoder
* @param slot Pointer to key slot to encode
* @return psa_status_t
*/
static psa_status_t psa_encode_protected_key_slot(nanocbor_encoder_t *enc, psa_key_slot_t *slot)
{
int res;
int key_array_size = 1;
psa_key_slot_number_t *slot_no = psa_key_slot_get_slot_number(slot);
#if IS_USED(MODULE_PSA_ASYMMETRIC)
if (PSA_KEY_TYPE_IS_KEY_PAIR(slot->attr.type)) {
key_array_size = 2;
}
#endif /* MODULE_PSA_ASYMMETRIC */
res = nanocbor_fmt_array(enc, key_array_size);
if (res < 0) {
goto error;
}
res = nanocbor_fmt_uint(enc, *slot_no);
if (res < 0) {
goto error;
}
#if IS_USED(MODULE_PSA_ASYMMETRIC)
if (PSA_KEY_TYPE_IS_KEY_PAIR(slot->attr.type)) {
uint8_t *pubkey_data;
size_t *pubkey_data_len;
psa_get_public_key_data_from_key_slot(slot, &pubkey_data, &pubkey_data_len);
res = nanocbor_put_bstr(enc, pubkey_data, *pubkey_data_len);
if (res < 0) {
goto error;
}
}
#endif /* MODULE_PSA_ASYMMETRIC */
return PSA_SUCCESS;
error:
DEBUG("[persistent storage] error while encoding protected key: %d = %s", res,
nanocbor_error_to_string(res));
return nanocbor_error_to_psa_status(res);
}
#endif /* PSA_PROTECTED_KEY_COUNT */
#if PSA_ASYMMETRIC_KEYPAIR_COUNT
/**
* @brief Encode contents of a PSA key slot containing an asymmetric key pair
*
* @param enc Active nanocbor encoder
* @param slot Pointer to key slot to encode
* @return psa_status_t
*/
static psa_status_t psa_encode_asymmetric_key_pair(nanocbor_encoder_t *enc, psa_key_slot_t *slot)
{
int res;
size_t *privkey_data_len;
size_t *pubkey_data_len;
uint8_t *privkey_data;
uint8_t *pubkey_data;
psa_get_key_data_from_key_slot(slot, &privkey_data, &privkey_data_len);
psa_get_public_key_data_from_key_slot(slot, &pubkey_data, &pubkey_data_len);
res = nanocbor_fmt_array(enc, 2); /* Contains private and public key*/
if (res < 0) {
goto error;
}
res = nanocbor_put_bstr(enc, privkey_data, *privkey_data_len);
if (res < 0) {
goto error;
}
res = nanocbor_put_bstr(enc, pubkey_data, *pubkey_data_len);
if (res < 0) {
goto error;
}
return PSA_SUCCESS;
error:
DEBUG("[persistent storage] error while encoding asymmetric key: %d = %s", res,
nanocbor_error_to_string(res));
return nanocbor_error_to_psa_status(res);
}
#endif /* PSA_ASYMMETRIC_KEYPAIR_COUNT */
#if PSA_SINGLE_KEY_COUNT
/**
* @brief Encode contents of a PSA key slot containing a single key
*
* @param enc Active nanocbor encoder
* @param slot Pointer to key slot to encode
* @return psa_status_t
*/
static psa_status_t psa_encode_single_key(nanocbor_encoder_t *enc, psa_key_slot_t *slot)
{
size_t *key_data_len;
uint8_t *key_data;
psa_get_key_data_from_key_slot(slot, &key_data, &key_data_len);
int res = nanocbor_put_bstr(enc, key_data, *key_data_len);
if (res < 0) {
DEBUG("[persistent storage] error while encoding single key: %d = %s", res,
nanocbor_error_to_string(res));
return nanocbor_error_to_psa_status(res);
}
return PSA_SUCCESS;
}
#endif /* PSA_SINGLE_KEY_COUNT */
psa_status_t psa_encode_key_slot(psa_key_slot_t *slot, uint8_t *output,
size_t output_len, size_t *output_size)
{
int res;
psa_status_t status;
nanocbor_encoder_t enc;
nanocbor_encoder_init(&enc, output, output_len);
*output_size = 0;
/* Key Struct */
res = nanocbor_fmt_array(&enc, 2);
if (res < 0) {
goto error;
}
status = psa_encode_key_attributes(&enc, &slot->attr);
if (status != PSA_SUCCESS) {
return status;
}
#if PSA_PROTECTED_KEY_COUNT
if (psa_key_lifetime_is_external(slot->attr.lifetime)) {
status = psa_encode_protected_key_slot(&enc, slot);
if (status != PSA_SUCCESS) {
return status;
}
goto done;
}
#endif /* PSA_PROTECTED_KEY_COUNT */
#if PSA_ASYMMETRIC_KEYPAIR_COUNT
if (PSA_KEY_TYPE_IS_KEY_PAIR(slot->attr.type)) {
status = psa_encode_asymmetric_key_pair(&enc, slot);
if (status != PSA_SUCCESS) {
return status;
}
goto done;
}
#endif /* PSA_ASYMMETRIC_KEYPAIR_COUNT */
#if PSA_SINGLE_KEY_COUNT
status = psa_encode_single_key(&enc, slot);
if (status != PSA_SUCCESS) {
return status;
}
goto done;
#else
return PSA_ERROR_NOT_SUPPORTED;
#endif /* PSA_SINGLE_KEY_COUNT */
done:
*output_size = nanocbor_encoded_len(&enc);
#if ENABLE_DEBUG
printf("CBOR Encoded Length: %d\n", *output_size);
puts("CBOR Encoded Key Slot:\n");
for (size_t i = 0; i < output_len+1; i++){
printf("%02x ", output[i]);
}
printf("\n");
#endif
return PSA_SUCCESS;
error:
DEBUG("[persistent storage] error while encoding key slot: %s",
nanocbor_error_to_string(res));
return nanocbor_error_to_psa_status(res);
}
psa_status_t psa_decode_key_attributes(psa_key_attributes_t *attr,
uint8_t *cbor_buf, size_t cbor_buf_size)
{
int res = 0;
nanocbor_value_t dec;
nanocbor_decoder_init(&dec, cbor_buf, cbor_buf_size);
nanocbor_value_t key_slot;
res = nanocbor_enter_array(&dec, &key_slot);
if (res < 0) {
goto error;
}
nanocbor_value_t attr_dec;
res = nanocbor_enter_array(&key_slot, &attr_dec);
if (res < 0) {
goto error;
}
res = nanocbor_get_uint32(&attr_dec, &attr->id);
if (res < 0) {
goto error;
}
res = nanocbor_get_uint16(&attr_dec, &attr->type);
if (res < 0) {
goto error;
}
res = nanocbor_get_uint16(&attr_dec, &attr->bits);
if (res < 0) {
goto error;
}
res = nanocbor_get_uint32(&attr_dec, &attr->lifetime);
if (res < 0) {
goto error;
}
nanocbor_value_t policy;
res = nanocbor_enter_array(&attr_dec, &policy);
if (res < 0) {
goto error;
}
res = nanocbor_get_uint32(&policy, &attr->policy.usage);
if (res < 0) {
goto error;
}
res = nanocbor_get_uint32(&policy, &attr->policy.alg);
if (res < 0) {
goto error;
}
#if ENABLE_DEBUG
puts("Key Attributes:\n");
printf("ID: %x\n", (int) attr->id);
printf("Type: %x\n", (int) attr->type);
printf("Bits: %d\n", (int) attr->bits);
printf("Lifetime: %x\n", (int) attr->lifetime);
printf("Usage: %x\n", (int) attr->policy.usage);
printf("Algorithm: %x\n", (int) attr->policy.alg);
#endif
return PSA_SUCCESS;
error:
DEBUG("[persistent storage] error while decoding key attributes: %s",
nanocbor_error_to_string(res));
return nanocbor_error_to_psa_status(res);
}
/**
* @brief Decode CBOR encoded key data
*
* @param key Nanocbor value object containing CBOR encoding of the key data
* @param key_data Output buffer to write key value
* @param key_len Pointer to write actual key size
* @return psa_status_t
*/
static psa_status_t psa_decode_key_data(nanocbor_value_t *key, uint8_t *key_data,
size_t key_data_size, size_t *key_len)
{
size_t len = 0;
const uint8_t *buf;
int res = nanocbor_get_bstr(key, &buf, &len);
if (res < 0 || len <= 0) {
key_len = NULL;
key_data = NULL;
DEBUG("[persistent storage] error while decoding key: %s",
nanocbor_error_to_string(res));
return nanocbor_error_to_psa_status(res);
}
if (len > key_data_size) {
key_len = NULL;
key_data = NULL;
DEBUG("[persistent storage] error while decoding key: %s",
nanocbor_error_to_string(res));
return PSA_ERROR_CORRUPTION_DETECTED;
}
memcpy(key_data, buf, len);
*key_len = len;
return PSA_SUCCESS;
}
#if PSA_PROTECTED_KEY_COUNT
/**
* @brief Decode CBOR encoded protected key
*
* @param key Nanocbor value object containing CBOR encoded key data
* @param slot Key slot to write decoded key to
* @return psa_status_t
*/
static psa_status_t psa_decode_protected_key_slot(nanocbor_value_t *key, psa_key_slot_t *slot)
{
int res = 0;
nanocbor_value_t key_arr;
res = nanocbor_enter_array(key, &key_arr);
if (res < 0) {
goto error;
}
psa_key_slot_number_t *slot_no = psa_key_slot_get_slot_number(slot);
res = nanocbor_get_uint64(&key_arr, slot_no);
if (res < 0) {
goto error;
}
#if IS_USED(MODULE_PSA_ASYMMETRIC)
if (PSA_KEY_TYPE_IS_KEY_PAIR(slot->attr.type)) {
size_t *pubkey_data_len;
uint8_t *pubkey_data;
psa_get_public_key_data_from_key_slot(slot, &pubkey_data, &pubkey_data_len);
return psa_decode_key_data(&key_arr, pubkey_data,
PSA_EXPORT_PUBLIC_KEY_MAX_SIZE, pubkey_data_len);
}
#endif /* MODULE_PSA_ASYMMETRIC */
return PSA_SUCCESS;
error:
DEBUG("[persistent storage] error while decoding protected key slot: %s",
nanocbor_error_to_string(res));
return nanocbor_error_to_psa_status(res);
}
#endif /* PSA_PROTECTED_KEY_COUNT */
#if PSA_ASYMMETRIC_KEYPAIR_COUNT
/**
* @brief Decode CBOR encoded asymmetric key pair
*
* @param key Nanocbor value object containing CBOR encoded key data
* @param slot Key slot to write decoded key to
* @return psa_status_t
*/
static psa_status_t psa_decode_asymmetric_keypair_slot(nanocbor_value_t *key, psa_key_slot_t *slot)
{
int res = 0;
psa_status_t status;
size_t *privkey_data_len;
size_t *pubkey_data_len;
uint8_t *privkey_data;
uint8_t *pubkey_data;
psa_get_key_data_from_key_slot(slot, &privkey_data, &privkey_data_len);
psa_get_public_key_data_from_key_slot(slot, &pubkey_data, &pubkey_data_len);
nanocbor_value_t key_arr;
res = nanocbor_enter_array(key, &key_arr);
if (res < 0) {
goto error;
}
status = psa_decode_key_data(&key_arr, privkey_data,
PSA_BITS_TO_BYTES(PSA_MAX_PRIV_KEY_SIZE), privkey_data_len);
if (status != PSA_SUCCESS) {
return status;
}
return psa_decode_key_data(&key_arr, pubkey_data,
PSA_EXPORT_PUBLIC_KEY_MAX_SIZE, pubkey_data_len);
error:
DEBUG("[persistent storage] error while decoding asymmetric key pair: %s",
nanocbor_error_to_string(res));
return nanocbor_error_to_psa_status(res);
}
#endif /* PSA_ASYMMETRIC_KEYPAIR_COUNT */
psa_status_t psa_decode_key_slot_data(psa_key_slot_t *slot, uint8_t *cbor_buf, size_t cbor_buf_size)
{
int res = 0;
psa_status_t status = PSA_ERROR_GENERIC_ERROR;
nanocbor_value_t dec;
nanocbor_decoder_init(&dec, cbor_buf, cbor_buf_size);
nanocbor_value_t key_slot;
res = nanocbor_enter_array(&dec, &key_slot);
if (res < 0) {
goto error;
}
/* We should have decoded the attributes before, so we can skip them here */
nanocbor_skip(&key_slot);
#if PSA_PROTECTED_KEY_COUNT
if (psa_key_lifetime_is_external(slot->attr.lifetime)) {
return psa_decode_protected_key_slot(&key_slot, slot);
}
#endif /* PSA_PROTECTED_KEY_COUNT */
#if PSA_ASYMMETRIC_KEYPAIR_COUNT
if (PSA_KEY_TYPE_IS_KEY_PAIR(slot->attr.type)) {
return psa_decode_asymmetric_keypair_slot(&key_slot, slot);
}
#endif /* PSA_ASYMMETRIC_KEYPAIR_COUNT */
#if PSA_SINGLE_KEY_COUNT
size_t *key_data_len;
uint8_t *key_data;
psa_get_key_data_from_key_slot(slot, &key_data, &key_data_len);
return psa_decode_key_data(&key_slot, key_data, PSA_MAX_KEY_DATA_SIZE, key_data_len);
#endif /* PSA_SINGLE_KEY_COUNT */
return status;
error:
DEBUG("[persistent storage] error while decoding key slot: %s",
nanocbor_error_to_string(res));
return nanocbor_error_to_psa_status(res);
}

View File

@ -0,0 +1,128 @@
/*
* Copyright (C) 2023 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 Implementation of persistent storage for PSA Crypto keys
*
* @author Lena Boeckmann <lena.boeckmann@haw-hamburg.de>
*
* @}
*/
#include <fcntl.h>
#include "psa/crypto.h"
#include "psa_crypto_slot_management.h"
#include "board.h"
#include "vfs.h"
#include "fs/littlefs2_fs.h"
#include "mtd.h"
#define ENABLE_DEBUG 0
#include "debug.h"
XFA_USE(vfs_mount_t, vfs_mountpoints_xfa);
#define UINT32_T_MAX_STRING_LEN (10)
/* dir path needs to be as long as mount point + psa_key_id_t as string */
#define STRING_PATH_LEN (strlen(vfs_mountpoints_xfa[0].mount_point) + \
UINT32_T_MAX_STRING_LEN)
psa_status_t psa_write_encoded_key_slot_to_file(psa_key_id_t id,
uint8_t* input,
size_t input_len)
{
/* dir path needs to be as long as mount point + psa_key_id_t as string */
char string_path[STRING_PATH_LEN];
sprintf(string_path, "%s/%d", vfs_mountpoints_xfa[0].mount_point, (int) id);
/* Check whether file already exists */
int fd = vfs_open(string_path, O_RDWR, 0);
if (fd >= 0) {
DEBUG("[psa_crypto] persist key: key with this ID already exists in storage: %d\n", fd);
vfs_close(fd);
return PSA_ERROR_ALREADY_EXISTS;
}
/* If file does not exist, create one */
fd = vfs_open(string_path, O_CREAT | O_RDWR, 0);
if (fd <= 0) {
DEBUG("[psa_crypto] persist key: Can not open file: %d\n", fd);
return PSA_ERROR_STORAGE_FAILURE;
}
if (vfs_write(fd, input, input_len) < 0) {
DEBUG("[psa_crypto] persist key: Can not write to file: %d\n", fd);
return PSA_ERROR_STORAGE_FAILURE;
}
if (vfs_close(fd) != 0) {
DEBUG("[psa_crypto] persist key: Can not close file: %d\n", fd);
return PSA_ERROR_STORAGE_FAILURE;
}
return PSA_SUCCESS;
}
psa_status_t psa_read_encoded_key_slot_from_file(psa_key_id_t id,
uint8_t *output,
size_t output_size,
size_t *output_data_len)
{
char string_path[STRING_PATH_LEN];
sprintf(string_path, "%s/%d", vfs_mountpoints_xfa[0].mount_point, (int) id);
int fd = vfs_open(string_path, O_RDONLY, 0);
if (fd <= 0) {
DEBUG("[psa_crypto] read persisted key: Can not open file: %d\n", fd);
return (fd == -2 ? PSA_ERROR_DOES_NOT_EXIST : PSA_ERROR_STORAGE_FAILURE);
}
ssize_t data_bytes = vfs_read(fd, output, output_size);
if (data_bytes < 0) {
DEBUG("[psa_crypto] read persisted key: Can not read from file\n");
return (fd == -2 ? PSA_ERROR_DOES_NOT_EXIST : PSA_ERROR_STORAGE_FAILURE);
}
if (vfs_close(fd) != 0) {
DEBUG("[psa_crypto] read persisted key: Can not close file: %d\n", fd);
return PSA_ERROR_STORAGE_FAILURE;
}
*output_data_len = data_bytes;
return PSA_SUCCESS;
}
psa_status_t psa_destroy_persistent_key(psa_key_id_t key_id)
{
if (psa_key_id_is_volatile(key_id)) {
DEBUG("[psa_crypto] persist key: ID is volatile\n");
return PSA_ERROR_INVALID_ARGUMENT;
}
char string_path[STRING_PATH_LEN];
sprintf(string_path, "%s/%d", vfs_mountpoints_xfa[0].mount_point, (int) key_id);
int fd = vfs_unlink(string_path);
if (fd < 0) {
DEBUG("[psa_crypto] destroy persisted key: Can not unlink file: %d\n", fd);
return PSA_ERROR_STORAGE_FAILURE;
}
return PSA_SUCCESS;
}

View File

@ -0,0 +1,24 @@
include ../Makefile.sys_common
USEMODULE += embunit
# This configuration also builds the backend implementations for all those selected
# modules, some of which depend on the PSA hash functions. So to make this test
# compile, we add the psa_hash module.
USEMODULE += psa_hash
USEMODULE += psa_hash_sha_256
USEMODULE += psa_crypto
USEMODULE += psa_persistent_storage
USEMODULE += psa_cipher
USEMODULE += psa_cipher_aes_128_cbc
USEMODULE += psa_asymmetric
USEMODULE += psa_asymmetric_ecc_p256r1
CFLAGS += -DCONFIG_PSA_ASYMMETRIC_KEYPAIR_COUNT=1
CFLAGS += -DCONFIG_PSA_SINGLE_KEY_COUNT=1
CFLAGS += -DCONFIG_PSA_PROTECTED_KEY_COUNT=1
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,14 @@
BOARD_INSUFFICIENT_MEMORY := \
arduino-duemilanove \
arduino-leonardo \
arduino-nano \
arduino-uno \
atmega328p \
atmega328p-xplained-mini \
atmega8 \
nucleo-f031k6 \
nucleo-l011k4 \
samd10-xmini \
stk3200 \
stm32f030f4-demo \
#

View File

@ -0,0 +1,31 @@
/*
* Copyright (C) 2023 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 tests
* @{
*
* @file
* @brief Test application for the PSA Crypto key encoding module
*
* @author Lena Boeckmann <lena.boeckmann@haw-hamburg.de>
*
* @}
*/
#include "tests_psa_cbor_enc_dec.h"
int main(void)
{
TESTS_START();
TESTS_RUN(tests_psa_crypto_enc_dec_single_key());
TESTS_RUN(tests_psa_crypto_enc_dec_keypair());
TESTS_RUN(tests_psa_crypto_enc_dec_protected_key());
TESTS_END();
return 0;
}

View File

@ -0,0 +1,117 @@
/*
* Copyright (C) 2023 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 tests
* @{
*
* @file
* @brief Tests CBOR encoding of a PSA Crypto asymmetric key pair slot.
*
* @author Lena Boeckmann <lena.boeckmann@haw-hamburg.de>
*
* @}
*/
#include "embUnit.h"
#include "psa/crypto.h"
#include "psa_crypto_cbor_encoder.h"
#include "tests_psa_cbor_enc_dec.h"
/**
* @brief Data has been obtained by manually encoding and decoding keys,
* until they fit the desired format and checking the output with
* https://cbor.me.
*
* Diagnostic notation:
* [
* [268435459, 28946, 256, 1, [12288, 100664841]],
* [h'27FC4D41F3DE49F786314B74AA67DE4BA961C38F4F896F045A537102B43D2039',
* h'0456CB81D1CBDE44F51DCCB12602670D76DDE784ED8D30721CCA5059F920AD62
* 87749EC9CB2675C51B69A68956102E8F6F7257B9B993ED8899EAFD53823DCAB641']
* ]
*/
static uint8_t cbor_encoded_data[] = {
0x82, 0x85, 0x1a, 0x10, 0x00, 0x00, 0x03, 0x19,
0x71, 0x12, 0x19, 0x01, 0x00, 0x01, 0x82, 0x19,
0x30, 0x00, 0x1a, 0x06, 0x00, 0x06, 0x09, 0x82,
0x58, 0x20, 0x27, 0xfc, 0x4d, 0x41, 0xf3, 0xde,
0x49, 0xf7, 0x86, 0x31, 0x4b, 0x74, 0xaa, 0x67,
0xde, 0x4b, 0xa9, 0x61, 0xc3, 0x8f, 0x4f, 0x89,
0x6f, 0x04, 0x5a, 0x53, 0x71, 0x02, 0xb4, 0x3d,
0x20, 0x39, 0x58, 0x41, 0x04, 0x56, 0xcb, 0x81,
0xd1, 0xcb, 0xde, 0x44, 0xf5, 0x1d, 0xcc, 0xb1,
0x26, 0x02, 0x67, 0x0d, 0x76, 0xdd, 0xe7, 0x84,
0xed, 0x8d, 0x30, 0x72, 0x1c, 0xca, 0x50, 0x59,
0xf9, 0x20, 0xad, 0x62, 0x87, 0x74, 0x9e, 0xc9,
0xcb, 0x26, 0x75, 0xc5, 0x1b, 0x69, 0xa6, 0x89,
0x56, 0x10, 0x2e, 0x8f, 0x6f, 0x72, 0x57, 0xb9,
0xb9, 0x93, 0xed, 0x88, 0x99, 0xea, 0xfd, 0x53,
0x82, 0x3d, 0xca, 0xb6, 0x41
};
static void _init_key_slot(psa_key_pair_slot_t *slot)
{
psa_key_lifetime_t lifetime = 1;
psa_key_usage_t usage = PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH;
psa_set_key_lifetime(&slot->attr, lifetime);
psa_set_key_algorithm(&slot->attr, ECC_ALG);
psa_set_key_usage_flags(&slot->attr, usage);
psa_set_key_type(&slot->attr, ECC_KEY_TYPE);
psa_set_key_bits(&slot->attr, ECC_KEY_SIZE);
psa_set_key_id(&slot->attr, 0x10000003);
memcpy(slot->key.privkey_data, privkey, sizeof(privkey));
slot->key.privkey_data_len = sizeof(privkey);
memcpy(slot->key.pubkey_data, pubkey, sizeof(pubkey));
slot->key.pubkey_data_len = sizeof(pubkey);
}
/**
* @brief CBOR encoding of key pair slot should equal @c cbor_encoded_data
*/
static void test_encode_asymmetric_keypair_slot(void)
{
size_t encoded_size;
uint8_t cbor_enc[sizeof(cbor_encoded_data)];
psa_key_pair_slot_t slot;
_init_key_slot(&slot);
TEST_ASSERT_PSA(psa_encode_key_slot((psa_key_slot_t *)&slot, cbor_enc, sizeof(cbor_enc), &encoded_size));
TEST_ASSERT_EQUAL_INT(sizeof(cbor_encoded_data), encoded_size);
TEST_ASSERT_MESSAGE(0 == memcmp(cbor_enc, cbor_encoded_data,
encoded_size), "wrong cbor encoding");
}
/**
* @brief Decoded key slot should equal key slot structure initialized
* in @c test_encode_asymmetric_keypair_slot.
*/
static void test_decode_asymmetric_keypair_slot(void)
{
psa_key_pair_slot_t slot;
TEST_ASSERT_PSA(psa_decode_key_attributes(&slot.attr, cbor_encoded_data, sizeof(cbor_encoded_data)));
TEST_ASSERT_PSA(psa_decode_key_slot_data((psa_key_slot_t *)&slot, cbor_encoded_data, sizeof(cbor_encoded_data)));
TEST_ASSERT_MESSAGE(0 == memcmp((uint8_t *)&slot, (uint8_t *)&slot,
sizeof(psa_key_pair_slot_t)), "wrong cbor decoding");
}
Test* tests_psa_crypto_enc_dec_keypair(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
new_TestFixture(test_encode_asymmetric_keypair_slot),
new_TestFixture(test_decode_asymmetric_keypair_slot),
};
EMB_UNIT_TESTCALLER(psa_crypto_enc_dec_keypair_tests, NULL, NULL, fixtures);
return (Test *)&psa_crypto_enc_dec_keypair_tests;
}

View File

@ -0,0 +1,171 @@
/*
* Copyright (C) 2023 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 tests
* @{
*
* @file
* @brief Tests CBOR encoding of PSA Crypto protected key slots
*
* @author Lena Boeckmann <lena.boeckmann@haw-hamburg.de>
*
* @}
*/
#include "embUnit.h"
#include "psa/crypto.h"
#include "psa_crypto_cbor_encoder.h"
#include "tests_psa_cbor_enc_dec.h"
/**
* @brief Testdata has been obtained by manually encoding and decoding keys,
* until they fit the desired format and checking the output with
* https://cbor.me.
*
* Diagnostic notation:
* [
* [8, 9216, 128, 2147483648, [768, 71319552]],
* [4]
* ]
*/
static uint8_t cbor_encoded_data[] = {
0x82, 0x85, 0x08, 0x19, 0x24, 0x00, 0x18, 0x80,
0x1a, 0x80, 0x00, 0x00, 0x00, 0x82, 0x19, 0x03,
0x00, 0x1a, 0x04, 0x40, 0x40, 0x00, 0x81, 0x04
};
/**
* @brief Testdata has been obtained by manually encoding and decoding keys,
* until they fit the desired format and checking the output with
* https://cbor.me.
*
* Diagnostic notation:
* [
* [8, 28946, 256, 2147483648, [12288, 100664841]],
* [4, h'0456CB81D1CBDE44F51DCCB12602670D76DDE784ED8D30721CCA5059F920AD62877
* 49EC9CB2675C51B69A68956102E8F6F7257B9B993ED8899EAFD53823DCAB641']
* ]
*/
static uint8_t cbor_encoded_data_with_pubkey[] = {
0x82, 0x85, 0x08, 0x19, 0x71, 0x12, 0x19, 0x01,
0x00, 0x1a, 0x80, 0x00, 0x00, 0x00, 0x82, 0x19,
0x30, 0x00, 0x1a, 0x06, 0x00, 0x06, 0x09, 0x82,
0x04, 0x58, 0x41, 0x04, 0x56, 0xcb, 0x81, 0xd1,
0xcb, 0xde, 0x44, 0xf5, 0x1d, 0xcc, 0xb1, 0x26,
0x02, 0x67, 0x0d, 0x76, 0xdd, 0xe7, 0x84, 0xed,
0x8d, 0x30, 0x72, 0x1c, 0xca, 0x50, 0x59, 0xf9,
0x20, 0xad, 0x62, 0x87, 0x74, 0x9e, 0xc9, 0xcb,
0x26, 0x75, 0xc5, 0x1b, 0x69, 0xa6, 0x89, 0x56,
0x10, 0x2e, 0x8f, 0x6f, 0x72, 0x57, 0xb9, 0xb9,
0x93, 0xed, 0x88, 0x99, 0xea, 0xfd, 0x53, 0x82,
0x3d, 0xca, 0xb6, 0x41
};
static void _init_key_slot(psa_prot_key_slot_t *slot, int is_pubkey)
{
psa_key_lifetime_t lifetime = PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION
(PSA_KEY_LIFETIME_VOLATILE, PSA_KEY_LOCATION_SE_MIN);
psa_set_key_lifetime(&slot->attr, lifetime);
psa_set_key_id(&slot->attr, 8);
slot->key.slot_number = 4;
if (!is_pubkey) {
psa_key_usage_t usage = PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT;
psa_set_key_algorithm(&slot->attr, PSA_ALG_CBC_NO_PADDING);
psa_set_key_usage_flags(&slot->attr, usage);
psa_set_key_type(&slot->attr, PSA_KEY_TYPE_AES);
psa_set_key_bits(&slot->attr, PSA_BYTES_TO_BITS(sizeof(AES_KEY)));
}
else {
psa_key_usage_t usage = PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH;
psa_set_key_algorithm(&slot->attr, ECC_ALG);
psa_set_key_usage_flags(&slot->attr, usage);
psa_set_key_type(&slot->attr, ECC_KEY_TYPE);
psa_set_key_bits(&slot->attr, ECC_KEY_SIZE);
psa_set_key_id(&slot->attr, 8);
memcpy(slot->key.pubkey_data, pubkey, sizeof(pubkey));
slot->key.pubkey_data_len = sizeof(pubkey);
}
}
/**
* @brief CBOR encoding of key pair slot should equal @c cbor_encoded_data
*/
static void test_encode_protected_key_slot(void)
{
size_t encoded_size;
uint8_t cbor_enc[sizeof(cbor_encoded_data)];
psa_prot_key_slot_t slot;
_init_key_slot(&slot, 0);
TEST_ASSERT_PSA(psa_encode_key_slot((psa_key_slot_t *)&slot, cbor_enc, sizeof(cbor_enc), &encoded_size));
TEST_ASSERT_EQUAL_INT(sizeof(cbor_encoded_data), encoded_size);
TEST_ASSERT_MESSAGE(0 == memcmp(cbor_enc, cbor_encoded_data, encoded_size),
"wrong cbor encoding");
}
/**
* @brief CBOR encoding of key pair slot should equal @c cbor_encoded_data_with_pubkey
*/
static void test_encode_protected_key_slot_with_pubkey(void)
{
size_t encoded_size;
uint8_t cbor_enc_with_pubkey[sizeof(cbor_encoded_data_with_pubkey)];
psa_prot_key_slot_t slot;
_init_key_slot(&slot, 1);
TEST_ASSERT_PSA(psa_encode_key_slot((psa_key_slot_t *)&slot, cbor_enc_with_pubkey, sizeof(cbor_enc_with_pubkey), &encoded_size));
TEST_ASSERT_EQUAL_INT(sizeof(cbor_encoded_data_with_pubkey), encoded_size);
TEST_ASSERT_MESSAGE(0 == memcmp(cbor_enc_with_pubkey, cbor_encoded_data_with_pubkey,
encoded_size), "wrong cbor encoding");
}
/**
* @brief Decoded key slot should equal key slot structure initialized
* in @c test_encode_protected_key_slot.
*/
static void test_decode_protected_key_slot(void)
{
psa_prot_key_slot_t slot;
TEST_ASSERT_PSA(psa_decode_key_attributes(&slot.attr, cbor_encoded_data, sizeof(cbor_encoded_data)));
TEST_ASSERT_PSA(psa_decode_key_slot_data((psa_key_slot_t *)&slot, cbor_encoded_data, sizeof(cbor_encoded_data)));
TEST_ASSERT_MESSAGE(0 == memcmp((uint8_t *)&slot, (uint8_t *)&slot,
sizeof(psa_prot_key_slot_t)), "wrong cbor decoding");
}
/**
* @brief Decoded key slot should equal key slot structure initialized
* in @c test_encode_protected_key_slot_with_pubkey.
*/
static void test_decode_protected_key_slot_with_pubkey(void)
{
psa_prot_key_slot_t slot;
TEST_ASSERT_PSA(psa_decode_key_attributes(&slot.attr, cbor_encoded_data_with_pubkey, sizeof(cbor_encoded_data_with_pubkey)));
TEST_ASSERT_PSA(psa_decode_key_slot_data((psa_key_slot_t *)&slot, cbor_encoded_data_with_pubkey, sizeof(cbor_encoded_data_with_pubkey)));
TEST_ASSERT_MESSAGE(0 == memcmp((uint8_t *)&slot, (uint8_t *)&slot,
sizeof(psa_prot_key_slot_t)), "wrong cbor decoding");
}
Test* tests_psa_crypto_enc_dec_protected_key(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
new_TestFixture(test_encode_protected_key_slot),
new_TestFixture(test_decode_protected_key_slot),
new_TestFixture(test_encode_protected_key_slot_with_pubkey),
new_TestFixture(test_decode_protected_key_slot_with_pubkey)
};
EMB_UNIT_TESTCALLER(psa_crypto_enc_dec_protected_key_tests, NULL, NULL, fixtures);
return (Test *)&psa_crypto_enc_dec_protected_key_tests;
}

View File

@ -0,0 +1,98 @@
/*
* Copyright (C) 2023 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 tests
* @{
*
* @file
* @brief Tests CBOR encoding of a PSA Crypto key slot containing one single key
*
* @author Lena Boeckmann <lena.boeckmann@haw-hamburg.de>
*
* @}
*/
#include "embUnit.h"
#include "psa/crypto.h"
#include "psa_crypto_cbor_encoder.h"
#include "tests_psa_cbor_enc_dec.h"
/**
* @brief Data has been obtained by manually encoding and decoding keys,
* until they fit the desired format and checking the output with
* https://cbor.me.
*
* Diagnostic notation:
* [
* [268435459, 9216, 128, 1, [256, 71319552]],
* h'2B7E151628AED218ABF7158809CF4F3C'
* ]
*/
static uint8_t cbor_encoded_data[] = {
0x82, 0x85, 0x1a, 0x10, 0x00, 0x00, 0x03, 0x19,
0x24, 0x00, 0x18, 0x80, 0x01, 0x82, 0x19, 0x01,
0x00, 0x1a, 0x04, 0x40, 0x40, 0x00, 0x50, 0x2b,
0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0x18, 0xab,
0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
};
static void _init_key_slot(psa_key_slot_t *slot)
{
psa_set_key_type(&slot->attr, PSA_KEY_TYPE_AES);
psa_set_key_algorithm(&slot->attr, PSA_ALG_CBC_NO_PADDING);
psa_set_key_bits(&slot->attr, PSA_BYTES_TO_BITS(sizeof(AES_KEY)));
psa_set_key_lifetime(&slot->attr, 0x00000001);
psa_set_key_usage_flags(&slot->attr, PSA_KEY_USAGE_ENCRYPT);
psa_set_key_id(&slot->attr, 0x10000003);
memcpy(slot->key.data, AES_KEY, sizeof(AES_KEY));
slot->key.data_len = sizeof(AES_KEY);
}
/**
* @brief CBOR encoding of key pair slot should equal @c cbor_encoded_data
*/
static void test_encode_single_key_slot(void)
{
size_t encoded_size;
uint8_t cbor_enc[sizeof(cbor_encoded_data)];
psa_key_slot_t slot;
_init_key_slot(&slot);
TEST_ASSERT_PSA(psa_encode_key_slot(&slot, cbor_enc, sizeof(cbor_enc), &encoded_size));
TEST_ASSERT_EQUAL_INT(sizeof(cbor_encoded_data), encoded_size);
TEST_ASSERT_MESSAGE(0 == memcmp(cbor_enc, cbor_encoded_data,
encoded_size), "wrong cbor encoding");
}
/**
* @brief Decoded key slot should equal key slot structure initialized
* in @c test_encode_single_key_slot.
*/
static void test_decode_single_key_slot(void)
{
psa_key_slot_t slot;
TEST_ASSERT_PSA(psa_decode_key_attributes(&(slot.attr), cbor_encoded_data, sizeof(cbor_encoded_data)));
TEST_ASSERT_PSA(psa_decode_key_slot_data(&slot, cbor_encoded_data, sizeof(cbor_encoded_data)));
TEST_ASSERT_MESSAGE(0 == memcmp((uint8_t *)&slot, (uint8_t *)&slot,
sizeof(psa_key_slot_t)), "wrong cbor decoding");
}
Test* tests_psa_crypto_enc_dec_single_key(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
new_TestFixture(test_encode_single_key_slot),
new_TestFixture(test_decode_single_key_slot),
};
EMB_UNIT_TESTCALLER(psa_crypto_enc_dec_single_key_tests, NULL, NULL, fixtures);
return (Test *)&psa_crypto_enc_dec_single_key_tests;
}

View File

@ -0,0 +1,14 @@
#!/usr/bin/env python3
# Copyright (C) 2023 TU Dresden
#
# 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.
import sys
from testrunner import run_check_unittests
if __name__ == "__main__":
sys.exit(run_check_unittests())

View File

@ -0,0 +1,71 @@
/*
* Copyright (C) 2023 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
* @defgroup <name> <description>
* @{
*
* @file tests_psa_cbor_enc_dec.h
* @brief
*
* @author Lena Boeckmann <lena.boeckmann@haw-hamburg.de>
*
*/
#ifndef TESTS_PSA_CBOR_ENC_DEC_H
#define TESTS_PSA_CBOR_ENC_DEC_H
#include <stdint.h>
#include "embUnit.h"
#ifdef __cplusplus
extern "C" {
#endif
#define TEST_ASSERT_PSA(func_) TEST_ASSERT_MESSAGE((func_) == PSA_SUCCESS, #func_ " failed");
#define ECC_KEY_SIZE (256)
#define ECC_KEY_TYPE (PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1))
#define ECC_ALG_HASH (PSA_ALG_SHA_256)
#define ECC_ALG (PSA_ALG_ECDSA(ECC_ALG_HASH))
static const uint8_t privkey[] = {
0x27, 0xfc, 0x4d, 0x41, 0xf3, 0xde, 0x49, 0xf7,
0x86, 0x31, 0x4b, 0x74, 0xaa, 0x67, 0xde, 0x4b,
0xa9, 0x61, 0xc3, 0x8f, 0x4f, 0x89, 0x6f, 0x04,
0x5a, 0x53, 0x71, 0x02, 0xb4, 0x3d, 0x20, 0x39
};
static const uint8_t pubkey[] = {
0x04,
0x56, 0xcb, 0x81, 0xd1, 0xcb, 0xde, 0x44, 0xf5,
0x1d, 0xcc, 0xb1, 0x26, 0x02, 0x67, 0x0d, 0x76,
0xdd, 0xe7, 0x84, 0xed, 0x8d, 0x30, 0x72, 0x1c,
0xca, 0x50, 0x59, 0xf9, 0x20, 0xad, 0x62, 0x87,
0x74, 0x9e, 0xc9, 0xcb, 0x26, 0x75, 0xc5, 0x1b,
0x69, 0xa6, 0x89, 0x56, 0x10, 0x2e, 0x8f, 0x6f,
0x72, 0x57, 0xb9, 0xb9, 0x93, 0xed, 0x88, 0x99,
0xea, 0xfd, 0x53, 0x82, 0x3d, 0xca, 0xb6, 0x41
};
static const uint8_t AES_KEY[] = {
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0x18,
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
};
Test* tests_psa_crypto_enc_dec_single_key(void);
Test* tests_psa_crypto_enc_dec_keypair(void);
Test* tests_psa_crypto_enc_dec_protected_key(void);
#ifdef __cplusplus
}
#endif
#endif /* TESTS_PSA_CBOR_ENC_DEC_H */
/** @} */

View File

@ -0,0 +1,23 @@
include ../Makefile.sys_common
BOARD_WHITELIST = \
native \
nrf52840dk \
#
USEMODULE += embunit
USEMODULE += psa_crypto
USEMODULE += psa_persistent_storage
USEMODULE += psa_cipher
USEMODULE += psa_cipher_aes_128_cbc
USEMODULE += psa_asymmetric
USEMODULE += psa_asymmetric_ecc_ed25519
CFLAGS += -DCONFIG_PSA_SINGLE_KEY_COUNT=1
CFLAGS += -DCONFIG_PSA_ASYMMETRIC_KEYPAIR_COUNT=1
CFLAGS += -DTHREAD_STACKSIZE_MAIN=\(4*THREAD_STACKSIZE_DEFAULT\)
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2023 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 tests
* @{
*
* @file
* @brief Test application for the PSA Crypto key encoding module
*
* @author Lena Boeckmann <lena.boeckmann@haw-hamburg.de>
*
* @}
*/
#include "psa/crypto.h"
#include "tests_psa_persistent_storage.h"
int main(void)
{
TESTS_START();
psa_crypto_init();
TESTS_RUN(tests_psa_persistent_single_key_storage());
TESTS_RUN(tests_psa_persistent_asym_keypair_storage());
TESTS_RUN(tests_psa_fail_overwrite_existing_key());
TESTS_END();
return 0;
}

View File

@ -0,0 +1,90 @@
/*
* Copyright (C) 2023 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 tests
* @{
*
* @file
* @brief Verify that PSA Crypto keys cannot be overwritten in storage
*
* @author Lena Boeckmann <lena.boeckmann@haw-hamburg.de>
*
* @}
*/
#include "embUnit.h"
#include "psa/crypto.h"
#include "tests_psa_persistent_storage.h"
static psa_key_id_t key_id = 1;
/**
* @brief A persistently stored key must not be overwritten by new key with same ID
*/
static void test_psa_fail_overwrite_existing_key(void)
{
psa_key_attributes_t attr = psa_key_attributes_init();
psa_key_usage_t usage = PSA_KEY_USAGE_ENCRYPT;
psa_set_key_algorithm(&attr, PSA_ALG_CBC_NO_PADDING);
psa_set_key_usage_flags(&attr, usage);
psa_set_key_bits(&attr, 128);
psa_set_key_type(&attr, PSA_KEY_TYPE_AES);
psa_set_key_id(&attr, key_id);
psa_key_lifetime_t lifetime = PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION
(PSA_KEY_LIFETIME_PERSISTENT, PSA_KEY_LOCATION_LOCAL_STORAGE);
psa_set_key_lifetime(&attr, lifetime);
/* Import persistent key */
TEST_ASSERT_PSA_SUCCESS(psa_import_key(&attr, KEY_128, AES_128_KEY_SIZE, &key_id));
/* Import different key with same ID, should fail */
TEST_ASSERT_PSA_ALREADY_EXISTS(psa_import_key(&attr, OVERWRITE_KEY_128, AES_128_KEY_SIZE, &key_id));
}
/**
* @brief After deleting a persistent key, a new key with same ID can be imported
*/
static void test_psa_delete_key_and_import_key_with_same_id(void)
{
psa_key_attributes_t attr = psa_key_attributes_init();
psa_key_usage_t usage = PSA_KEY_USAGE_ENCRYPT;
psa_set_key_algorithm(&attr, PSA_ALG_CBC_NO_PADDING);
psa_set_key_usage_flags(&attr, usage);
psa_set_key_bits(&attr, 128);
psa_set_key_type(&attr, PSA_KEY_TYPE_AES);
psa_set_key_id(&attr, key_id);
psa_key_lifetime_t lifetime = PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION
(PSA_KEY_LIFETIME_PERSISTENT, PSA_KEY_LOCATION_LOCAL_STORAGE);
psa_set_key_lifetime(&attr, lifetime);
/* Destroy persistent key */
TEST_ASSERT_PSA_SUCCESS(psa_destroy_key(key_id));
/* Import different key with same ID, should succeed */
TEST_ASSERT_PSA_SUCCESS(psa_import_key(&attr, OVERWRITE_KEY_128, AES_128_KEY_SIZE, &key_id));
/* Destroy persistent key again, to clean up */
TEST_ASSERT_PSA_SUCCESS(psa_destroy_key(key_id));
}
Test* tests_psa_fail_overwrite_existing_key(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
new_TestFixture(test_psa_fail_overwrite_existing_key),
new_TestFixture(test_psa_delete_key_and_import_key_with_same_id),
};
EMB_UNIT_TESTCALLER(tests_psa_fail_overwrite_existing_key_tests, NULL, NULL, fixtures);
return (Test *)&tests_psa_fail_overwrite_existing_key_tests;
}

View File

@ -0,0 +1,152 @@
/*
* Copyright (C) 2023 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 tests
* @{
*
* @file
* @brief Tests CBOR encoding of a PSA Crypto asymmetric key pair slot.
*
* @author Lena Boeckmann <lena.boeckmann@haw-hamburg.de>
*
* @}
*/
#include "embUnit.h"
#include "psa/crypto.h"
#include "tests_psa_persistent_storage.h"
#define EDDSA_MESSAGE_SIZE (127)
#define ECC_KEY_SIZE (255)
#define ECC_KEY_TYPE (PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS))
#define ECC_ALG (PSA_ALG_PURE_EDDSA)
#define ECC_PUBLIC_KEY_SIZE (PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(ECC_KEY_TYPE, ECC_KEY_SIZE))
#define SIGNATURE_SIZE (PSA_SIGN_OUTPUT_SIZE(ECC_KEY_TYPE, ECC_KEY_SIZE, ECC_ALG))
static psa_key_attributes_t privkey_attr;
static psa_key_attributes_t pubkey_attr;
static psa_key_id_t privkey_id = 1;
static psa_key_id_t pubkey_id = 2;
static psa_key_id_t overwrite_privkey_id = 3;
static const uint8_t msg[EDDSA_MESSAGE_SIZE] = { 0x0b };
static void _test_setup(void)
{
uint8_t public_key[ECC_PUBLIC_KEY_SIZE];
size_t pubkey_length;
privkey_attr = psa_key_attributes_init();
pubkey_attr = psa_key_attributes_init();
psa_key_usage_t usage = PSA_KEY_USAGE_SIGN_MESSAGE | PSA_KEY_USAGE_VERIFY_MESSAGE;
psa_key_lifetime_t lifetime = PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION
(PSA_KEY_LIFETIME_PERSISTENT, PSA_KEY_LOCATION_LOCAL_STORAGE);
psa_set_key_algorithm(&privkey_attr, ECC_ALG);
psa_set_key_usage_flags(&privkey_attr, usage);
psa_set_key_type(&privkey_attr, ECC_KEY_TYPE);
psa_set_key_bits(&privkey_attr, ECC_KEY_SIZE);
psa_set_key_id(&privkey_attr, privkey_id);
psa_set_key_lifetime(&privkey_attr, lifetime);
/* Generate persistent key */
TEST_ASSERT_PSA_SUCCESS(psa_generate_key(&privkey_attr, &privkey_id));
/* Export and import public key for verification */
TEST_ASSERT_PSA_SUCCESS(psa_export_public_key(privkey_id, public_key,
sizeof(public_key), &pubkey_length));
psa_set_key_usage_flags(&pubkey_attr, PSA_KEY_USAGE_VERIFY_MESSAGE);
psa_set_key_algorithm(&pubkey_attr, ECC_ALG);
psa_set_key_bits(&pubkey_attr, PSA_BYTES_TO_BITS(pubkey_length));
psa_set_key_type(&pubkey_attr, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_TWISTED_EDWARDS));
TEST_ASSERT_PSA_SUCCESS(psa_import_key(&pubkey_attr, public_key, pubkey_length, &pubkey_id));
}
static void _test_destroy_keys(int cleanup)
{
/* Destroy first key */
TEST_ASSERT_PSA_SUCCESS(psa_destroy_key(privkey_id));
/* Destroy second key */
TEST_ASSERT_PSA_SUCCESS(psa_destroy_key(pubkey_id));
if (cleanup) {
/* Destroy last key, just to clean up */
TEST_ASSERT_PSA_SUCCESS(psa_destroy_key(overwrite_privkey_id));
}
}
/**
* @brief A persistently stored key should still be accessible after
* overwriting it in local memory
*/
static void test_psa_store_persistent_asym_keypair(void)
{
uint8_t signature[SIGNATURE_SIZE];
size_t sig_length;
_test_setup();
/* Generate second keypair to overwrite the first one in volatile memory */
psa_set_key_id(&privkey_attr, overwrite_privkey_id);
TEST_ASSERT_PSA_SUCCESS(psa_generate_key(&privkey_attr, &overwrite_privkey_id));
/* Generate message signature with first key */
TEST_ASSERT_PSA_SUCCESS(psa_sign_message(privkey_id, ECC_ALG, msg, sizeof(msg), signature, sizeof(signature), &sig_length));
/* Verify signature with public key of first keypair */
TEST_ASSERT_PSA_SUCCESS(psa_verify_message(pubkey_id, ECC_ALG, msg,
sizeof(msg), signature, sig_length));
_test_destroy_keys(1);
}
/**
* @brief After destroying a persistent key, it should not be available anymore
*/
static void test_psa_delete_persistent_asym_keypair(void)
{
uint8_t signature[SIGNATURE_SIZE];
size_t sig_length;
_test_setup();
/* Generate message signature with first key */
TEST_ASSERT_PSA_SUCCESS(psa_sign_message(privkey_id, ECC_ALG, msg, sizeof(msg), signature, sizeof(signature), &sig_length));
/* Verify signature with public key of first keypair */
TEST_ASSERT_PSA_SUCCESS(psa_verify_message(pubkey_id, ECC_ALG, msg,
sizeof(msg), signature, sig_length));
_test_destroy_keys(0);
/* Signature and verification with deleted keys should fail */
TEST_ASSERT_PSA_DOES_NOT_EXIST(psa_sign_message(privkey_id, ECC_ALG, msg, sizeof(msg),
signature, sizeof(signature), &sig_length));
TEST_ASSERT_PSA_DOES_NOT_EXIST(psa_verify_message(pubkey_id, ECC_ALG, msg, sizeof(msg),
signature, sig_length));
}
Test* tests_psa_persistent_asym_keypair_storage(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
new_TestFixture(test_psa_store_persistent_asym_keypair),
new_TestFixture(test_psa_delete_persistent_asym_keypair),
};
EMB_UNIT_TESTCALLER(tests_psa_persistent_asym_keypair_storage_tests, NULL, NULL, fixtures);
return (Test *)&tests_psa_persistent_asym_keypair_storage_tests;
}

View File

@ -0,0 +1,144 @@
/*
* Copyright (C) 2023 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 tests
* @{
*
* @file
* @brief Generate and use a persistently stored key in PSA Crypto
*
* @author Lena Boeckmann <lena.boeckmann@haw-hamburg.de>
*
* @}
*/
#include "embUnit.h"
#include "psa/crypto.h"
#include "tests_psa_persistent_storage.h"
#define PLAINTEXT_LEN (32)
#define ENCR_OUTPUT_SIZE (PSA_CIPHER_ENCRYPT_OUTPUT_SIZE(PSA_KEY_TYPE_AES, \
PSA_ALG_CBC_NO_PADDING, \
PLAINTEXT_LEN))
static uint8_t PLAINTEXT[] = {
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51
};
static psa_key_id_t key_id_1 = 1;
static psa_key_id_t key_id_2 = 2;
static void _test_setup(void)
{
psa_key_attributes_t attr = psa_key_attributes_init();
psa_key_usage_t usage = PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT;
psa_set_key_algorithm(&attr, PSA_ALG_CBC_NO_PADDING);
psa_set_key_usage_flags(&attr, usage);
psa_set_key_bits(&attr, 128);
psa_set_key_type(&attr, PSA_KEY_TYPE_AES);
psa_set_key_id(&attr, key_id_1);
psa_key_lifetime_t lifetime = PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION
(PSA_KEY_LIFETIME_PERSISTENT, PSA_KEY_LOCATION_LOCAL_STORAGE);
psa_set_key_lifetime(&attr, lifetime);
/* Import persistent key */
TEST_ASSERT_PSA_SUCCESS(psa_import_key(&attr, KEY_128, AES_128_KEY_SIZE, &key_id_1));
/* Import second key to overwrite the first one */
psa_set_key_id(&attr, key_id_2);
TEST_ASSERT_PSA_SUCCESS(psa_import_key(&attr, OVERWRITE_KEY_128, AES_128_KEY_SIZE,
&key_id_2));
}
static void _test_destroy_keys(void)
{
/* Destroy first key */
TEST_ASSERT_PSA_SUCCESS(psa_destroy_key(key_id_1));
/* Destroy second key */
TEST_ASSERT_PSA_SUCCESS(psa_destroy_key(key_id_2));
}
/**
* @brief A persistently stored key should still be accessible after
* overwriting it in local memory
*/
static void test_psa_store_single_persistent_key(void)
{
uint8_t cipher_out[ENCR_OUTPUT_SIZE];
uint8_t plain_out[PLAINTEXT_LEN];
size_t output_len = 0;
_test_setup();
/* Generate cipher with first key */
TEST_ASSERT_PSA_SUCCESS(psa_cipher_encrypt(key_id_1, PSA_ALG_CBC_NO_PADDING, PLAINTEXT,
PLAINTEXT_LEN, cipher_out, ENCR_OUTPUT_SIZE, &output_len));
TEST_ASSERT_PSA_SUCCESS(psa_cipher_decrypt(key_id_1, PSA_ALG_CBC_NO_PADDING, cipher_out,
sizeof(cipher_out), plain_out, sizeof(plain_out), &output_len));
TEST_ASSERT_MESSAGE(0 == memcmp(plain_out, PLAINTEXT, PLAINTEXT_LEN),
"first key, wrong plaintext");
/* Generate cipher with second key */
TEST_ASSERT_PSA_SUCCESS(psa_cipher_encrypt(key_id_2, PSA_ALG_CBC_NO_PADDING, PLAINTEXT,
PLAINTEXT_LEN, cipher_out, ENCR_OUTPUT_SIZE, &output_len));
TEST_ASSERT_PSA_SUCCESS(psa_cipher_decrypt(key_id_2, PSA_ALG_CBC_NO_PADDING, cipher_out,
sizeof(cipher_out), plain_out, sizeof(plain_out), &output_len));
TEST_ASSERT_MESSAGE(0 == memcmp(plain_out, PLAINTEXT, PLAINTEXT_LEN),
"second key, wrong plaintext");
_test_destroy_keys();
}
/**
* @brief After destroying a persistent key, it should not be available anymore
*/
static void test_psa_delete_single_persistent_key(void)
{
uint8_t cipher_out[ENCR_OUTPUT_SIZE];
size_t output_len = 0;
_test_setup();
/* Generate cipher with first key */
TEST_ASSERT_PSA_SUCCESS(psa_cipher_encrypt(key_id_1, PSA_ALG_CBC_NO_PADDING, PLAINTEXT,
PLAINTEXT_LEN, cipher_out, ENCR_OUTPUT_SIZE, &output_len));
/* Generate cipher with second key */
TEST_ASSERT_PSA_SUCCESS(psa_cipher_encrypt(key_id_2, PSA_ALG_CBC_NO_PADDING, PLAINTEXT,
PLAINTEXT_LEN, cipher_out, ENCR_OUTPUT_SIZE, &output_len));
_test_destroy_keys();
/* Encryption with deleted keys should fail */
TEST_ASSERT_PSA_DOES_NOT_EXIST(psa_cipher_encrypt(key_id_1, PSA_ALG_CBC_NO_PADDING, PLAINTEXT,
PLAINTEXT_LEN, cipher_out, ENCR_OUTPUT_SIZE, &output_len));
TEST_ASSERT_PSA_DOES_NOT_EXIST(psa_cipher_encrypt(key_id_2, PSA_ALG_CBC_NO_PADDING,
PLAINTEXT, PLAINTEXT_LEN, cipher_out, ENCR_OUTPUT_SIZE,
&output_len));
}
Test* tests_psa_persistent_single_key_storage(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
new_TestFixture(test_psa_store_single_persistent_key),
new_TestFixture(test_psa_delete_single_persistent_key),
};
EMB_UNIT_TESTCALLER(tests_psa_persistent_single_key_storage_tests, NULL, NULL, fixtures);
return (Test *)&tests_psa_persistent_single_key_storage_tests;
}

View File

@ -0,0 +1,14 @@
#!/usr/bin/env python3
# Copyright (C) 2023 TU Dresden
#
# 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.
import sys
from testrunner import run_check_unittests
if __name__ == "__main__":
sys.exit(run_check_unittests())

View File

@ -0,0 +1,61 @@
/*
* Copyright (C) 2023 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
* @defgroup <name> <description>
* @{
*
* @file tests_psa_cbor_enc_dec.h
* @brief
*
* @author Lena Boeckmann <lena.boeckmann@haw-hamburg.de>
*
*/
#ifndef TESTS_PSA_PERSISTENT_STORAGE_H
#define TESTS_PSA_PERSISTENT_STORAGE_H
#include <stdint.h>
#include "embUnit.h"
#ifdef __cplusplus
extern "C" {
#endif
#define AES_128_KEY_SIZE (16)
#define TEST_ASSERT_PSA_SUCCESS(func_) TEST_ASSERT_MESSAGE(func_ == PSA_SUCCESS, \
#func_ " failed");
#define TEST_ASSERT_PSA_DOES_NOT_EXIST(func_) TEST_ASSERT_MESSAGE(func_ == \
PSA_ERROR_DOES_NOT_EXIST, #func_ " failed");
#define TEST_ASSERT_PSA_ALREADY_EXISTS(func_) TEST_ASSERT_MESSAGE(func_ == \
PSA_ERROR_ALREADY_EXISTS, #func_ " failed");
#define AES_128_KEY_SIZE (16)
static const uint8_t KEY_128[AES_128_KEY_SIZE] = {
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
};
static const uint8_t OVERWRITE_KEY_128[AES_128_KEY_SIZE] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10
};
Test* tests_psa_persistent_single_key_storage(void);
Test* tests_psa_persistent_asym_keypair_storage(void);
Test* tests_psa_fail_overwrite_existing_key(void);
#ifdef __cplusplus
}
#endif
#endif /* TESTS_PSA_PERSISTENT_STORAGE_H */
/** @} */