From cbadc4f6584444f5af91ab9ba630177c74c42b79 Mon Sep 17 00:00:00 2001 From: Lena Boeckmann Date: Tue, 21 Nov 2023 16:14:23 +0100 Subject: [PATCH] sys/psa_crypto: implement persistent key storage --- sys/include/psa_crypto/psa/crypto.h | 4 + sys/psa_crypto/Makefile | 4 + sys/psa_crypto/Makefile.dep | 9 + sys/psa_crypto/doc.txt | 17 +- .../include/psa_crypto_cbor_encoder.h | 149 +++++ .../include/psa_crypto_persistent_storage.h | 73 +++ .../include/psa_crypto_slot_management.h | 53 ++ sys/psa_crypto/psa_crypto.c | 58 +- .../psa_crypto_slot_management.c | 219 ++++--- .../psa_persistent_storage/Makefile | 3 + .../psa_crypto_cbor_encoder.c | 546 ++++++++++++++++++ .../psa_crypto_persistent_storage.c | 128 ++++ tests/sys/psa_crypto_cbor_encoder/Makefile | 24 + tests/sys/psa_crypto_cbor_encoder/Makefile.ci | 14 + tests/sys/psa_crypto_cbor_encoder/main.c | 31 + .../test_psa_crypto_enc_dec_keypair.c | 117 ++++ .../test_psa_crypto_enc_dec_prot_key.c | 171 ++++++ .../test_psa_crypto_enc_dec_single_key.c | 98 ++++ .../psa_crypto_cbor_encoder/tests/01-run.py | 14 + .../tests_psa_cbor_enc_dec.h | 71 +++ .../psa_crypto_persistent_storage/Makefile | 23 + .../sys/psa_crypto_persistent_storage/main.c | 33 ++ ...psa_crypto_fail_overwrite_persistent_key.c | 90 +++ ...psa_crypto_store_persistent_asym_keypair.c | 152 +++++ ...t_psa_crypto_store_persistent_single_key.c | 144 +++++ .../tests/01-run.py | 14 + .../tests_psa_persistent_storage.h | 61 ++ 27 files changed, 2233 insertions(+), 87 deletions(-) create mode 100644 sys/psa_crypto/include/psa_crypto_cbor_encoder.h create mode 100644 sys/psa_crypto/include/psa_crypto_persistent_storage.h create mode 100644 sys/psa_crypto/psa_persistent_storage/Makefile create mode 100644 sys/psa_crypto/psa_persistent_storage/psa_crypto_cbor_encoder.c create mode 100644 sys/psa_crypto/psa_persistent_storage/psa_crypto_persistent_storage.c create mode 100644 tests/sys/psa_crypto_cbor_encoder/Makefile create mode 100644 tests/sys/psa_crypto_cbor_encoder/Makefile.ci create mode 100644 tests/sys/psa_crypto_cbor_encoder/main.c create mode 100644 tests/sys/psa_crypto_cbor_encoder/test_psa_crypto_enc_dec_keypair.c create mode 100644 tests/sys/psa_crypto_cbor_encoder/test_psa_crypto_enc_dec_prot_key.c create mode 100644 tests/sys/psa_crypto_cbor_encoder/test_psa_crypto_enc_dec_single_key.c create mode 100755 tests/sys/psa_crypto_cbor_encoder/tests/01-run.py create mode 100644 tests/sys/psa_crypto_cbor_encoder/tests_psa_cbor_enc_dec.h create mode 100644 tests/sys/psa_crypto_persistent_storage/Makefile create mode 100644 tests/sys/psa_crypto_persistent_storage/main.c create mode 100644 tests/sys/psa_crypto_persistent_storage/test_psa_crypto_fail_overwrite_persistent_key.c create mode 100644 tests/sys/psa_crypto_persistent_storage/test_psa_crypto_store_persistent_asym_keypair.c create mode 100644 tests/sys/psa_crypto_persistent_storage/test_psa_crypto_store_persistent_single_key.c create mode 100755 tests/sys/psa_crypto_persistent_storage/tests/01-run.py create mode 100644 tests/sys/psa_crypto_persistent_storage/tests_psa_persistent_storage.h diff --git a/sys/include/psa_crypto/psa/crypto.h b/sys/include/psa_crypto/psa/crypto.h index 7ccc494f9ed2..3a025f4da6e7 100644 --- a/sys/include/psa_crypto/psa/crypto.h +++ b/sys/include/psa_crypto/psa/crypto.h @@ -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. * diff --git a/sys/psa_crypto/Makefile b/sys/psa_crypto/Makefile index 6d61a2db19cf..00487e2c6ae9 100644 --- a/sys/psa_crypto/Makefile +++ b/sys/psa_crypto/Makefile @@ -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 diff --git a/sys/psa_crypto/Makefile.dep b/sys/psa_crypto/Makefile.dep index b117add048e7..a46fc0c7e345 100644 --- a/sys/psa_crypto/Makefile.dep +++ b/sys/psa_crypto/Makefile.dep @@ -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 diff --git a/sys/psa_crypto/doc.txt b/sys/psa_crypto/doc.txt index 5a6463f7509a..63111d2bffbb 100644 --- a/sys/psa_crypto/doc.txt +++ b/sys/psa_crypto/doc.txt @@ -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 * diff --git a/sys/psa_crypto/include/psa_crypto_cbor_encoder.h b/sys/psa_crypto/include/psa_crypto_cbor_encoder.h new file mode 100644 index 000000000000..ffc14deb5e7f --- /dev/null +++ b/sys/psa_crypto/include/psa_crypto_cbor_encoder.h @@ -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 + * + */ + +#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 */ +/** @} */ diff --git a/sys/psa_crypto/include/psa_crypto_persistent_storage.h b/sys/psa_crypto/include/psa_crypto_persistent_storage.h new file mode 100644 index 000000000000..ad75af4ccef3 --- /dev/null +++ b/sys/psa_crypto/include/psa_crypto_persistent_storage.h @@ -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 + * + */ + +#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 */ +/** @} */ diff --git a/sys/psa_crypto/include/psa_crypto_slot_management.h b/sys/psa_crypto/include/psa_crypto_slot_management.h index 1f84b545ae24..4d92fb3a8888 100644 --- a/sys/psa_crypto/include/psa_crypto_slot_management.h +++ b/sys/psa_crypto/include/psa_crypto_slot_management.h @@ -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. * diff --git a/sys/psa_crypto/psa_crypto.c b/sys/psa_crypto/psa_crypto.c index ae53ac1e0b37..0e8c43f60eb8 100644 --- a/sys/psa_crypto/psa_crypto.c +++ b/sys/psa_crypto/psa_crypto.c @@ -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); diff --git a/sys/psa_crypto/psa_key_slot_mgmt/psa_crypto_slot_management.c b/sys/psa_crypto/psa_key_slot_mgmt/psa_crypto_slot_management.c index 48ef1cf841bf..25ad3242cc84 100644 --- a/sys/psa_crypto/psa_key_slot_mgmt/psa_crypto_slot_management.c +++ b/sys/psa_crypto/psa_key_slot_mgmt/psa_crypto_slot_management.c @@ -25,26 +25,29 @@ #define ENABLE_DEBUG 0 #include "debug.h" -#if PSA_PROTECTED_KEY_COUNT +#if IS_USED(MODULE_PSA_PERSISTENT_STORAGE) +#include "psa_crypto_persistent_storage.h" +#include "psa_crypto_cbor_encoder.h" + /** - * @brief Structure for a protected key slot. + * @brief Max size required for the CBOR buffer. * - * These slots hold Slot Numbers for keys in protected storage and, if the key type is an - * asymmetric key pair, the public key. + * Depends on the key type and size. */ -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; +#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 - } key; -} psa_prot_key_slot_t; +#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 diff --git a/sys/psa_crypto/psa_persistent_storage/Makefile b/sys/psa_crypto/psa_persistent_storage/Makefile new file mode 100644 index 000000000000..89e0b4a80cac --- /dev/null +++ b/sys/psa_crypto/psa_persistent_storage/Makefile @@ -0,0 +1,3 @@ +INCLUDES += -I$(RIOTBASE)/sys/psa_crypto/include + +include $(RIOTBASE)/Makefile.base diff --git a/sys/psa_crypto/psa_persistent_storage/psa_crypto_cbor_encoder.c b/sys/psa_crypto/psa_persistent_storage/psa_crypto_cbor_encoder.c new file mode 100644 index 000000000000..6c21a94c146f --- /dev/null +++ b/sys/psa_crypto/psa_persistent_storage/psa_crypto_cbor_encoder.c @@ -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 + * + * @} + */ + +#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); +} diff --git a/sys/psa_crypto/psa_persistent_storage/psa_crypto_persistent_storage.c b/sys/psa_crypto/psa_persistent_storage/psa_crypto_persistent_storage.c new file mode 100644 index 000000000000..a132973c6508 --- /dev/null +++ b/sys/psa_crypto/psa_persistent_storage/psa_crypto_persistent_storage.c @@ -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 + * + * @} + */ + +#include +#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; +} diff --git a/tests/sys/psa_crypto_cbor_encoder/Makefile b/tests/sys/psa_crypto_cbor_encoder/Makefile new file mode 100644 index 000000000000..b26ebdcc1dd4 --- /dev/null +++ b/tests/sys/psa_crypto_cbor_encoder/Makefile @@ -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 diff --git a/tests/sys/psa_crypto_cbor_encoder/Makefile.ci b/tests/sys/psa_crypto_cbor_encoder/Makefile.ci new file mode 100644 index 000000000000..6a2fca3cafc6 --- /dev/null +++ b/tests/sys/psa_crypto_cbor_encoder/Makefile.ci @@ -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 \ + # diff --git a/tests/sys/psa_crypto_cbor_encoder/main.c b/tests/sys/psa_crypto_cbor_encoder/main.c new file mode 100644 index 000000000000..e45476afb870 --- /dev/null +++ b/tests/sys/psa_crypto_cbor_encoder/main.c @@ -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 + * + * @} + */ + +#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; +} diff --git a/tests/sys/psa_crypto_cbor_encoder/test_psa_crypto_enc_dec_keypair.c b/tests/sys/psa_crypto_cbor_encoder/test_psa_crypto_enc_dec_keypair.c new file mode 100644 index 000000000000..c4ee2463e4e1 --- /dev/null +++ b/tests/sys/psa_crypto_cbor_encoder/test_psa_crypto_enc_dec_keypair.c @@ -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 + * + * @} + */ + +#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; +} diff --git a/tests/sys/psa_crypto_cbor_encoder/test_psa_crypto_enc_dec_prot_key.c b/tests/sys/psa_crypto_cbor_encoder/test_psa_crypto_enc_dec_prot_key.c new file mode 100644 index 000000000000..a47171f28912 --- /dev/null +++ b/tests/sys/psa_crypto_cbor_encoder/test_psa_crypto_enc_dec_prot_key.c @@ -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 + * + * @} + */ + +#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; +} diff --git a/tests/sys/psa_crypto_cbor_encoder/test_psa_crypto_enc_dec_single_key.c b/tests/sys/psa_crypto_cbor_encoder/test_psa_crypto_enc_dec_single_key.c new file mode 100644 index 000000000000..9d14ebe541ce --- /dev/null +++ b/tests/sys/psa_crypto_cbor_encoder/test_psa_crypto_enc_dec_single_key.c @@ -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 + * + * @} + */ + +#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; +} diff --git a/tests/sys/psa_crypto_cbor_encoder/tests/01-run.py b/tests/sys/psa_crypto_cbor_encoder/tests/01-run.py new file mode 100755 index 000000000000..dc56107fc371 --- /dev/null +++ b/tests/sys/psa_crypto_cbor_encoder/tests/01-run.py @@ -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()) diff --git a/tests/sys/psa_crypto_cbor_encoder/tests_psa_cbor_enc_dec.h b/tests/sys/psa_crypto_cbor_encoder/tests_psa_cbor_enc_dec.h new file mode 100644 index 000000000000..085df037a5d4 --- /dev/null +++ b/tests/sys/psa_crypto_cbor_encoder/tests_psa_cbor_enc_dec.h @@ -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 + * @{ + * + * @file tests_psa_cbor_enc_dec.h + * @brief + * + * @author Lena Boeckmann + * + */ + +#ifndef TESTS_PSA_CBOR_ENC_DEC_H +#define TESTS_PSA_CBOR_ENC_DEC_H + +#include +#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 */ +/** @} */ diff --git a/tests/sys/psa_crypto_persistent_storage/Makefile b/tests/sys/psa_crypto_persistent_storage/Makefile new file mode 100644 index 000000000000..fe4271977e4a --- /dev/null +++ b/tests/sys/psa_crypto_persistent_storage/Makefile @@ -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 diff --git a/tests/sys/psa_crypto_persistent_storage/main.c b/tests/sys/psa_crypto_persistent_storage/main.c new file mode 100644 index 000000000000..76c3aba9047a --- /dev/null +++ b/tests/sys/psa_crypto_persistent_storage/main.c @@ -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 + * + * @} + */ + +#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; +} diff --git a/tests/sys/psa_crypto_persistent_storage/test_psa_crypto_fail_overwrite_persistent_key.c b/tests/sys/psa_crypto_persistent_storage/test_psa_crypto_fail_overwrite_persistent_key.c new file mode 100644 index 000000000000..b4d66c8904b9 --- /dev/null +++ b/tests/sys/psa_crypto_persistent_storage/test_psa_crypto_fail_overwrite_persistent_key.c @@ -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 + * + * @} + */ + +#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; +} diff --git a/tests/sys/psa_crypto_persistent_storage/test_psa_crypto_store_persistent_asym_keypair.c b/tests/sys/psa_crypto_persistent_storage/test_psa_crypto_store_persistent_asym_keypair.c new file mode 100644 index 000000000000..b8210a702f01 --- /dev/null +++ b/tests/sys/psa_crypto_persistent_storage/test_psa_crypto_store_persistent_asym_keypair.c @@ -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 + * + * @} + */ + +#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; +} diff --git a/tests/sys/psa_crypto_persistent_storage/test_psa_crypto_store_persistent_single_key.c b/tests/sys/psa_crypto_persistent_storage/test_psa_crypto_store_persistent_single_key.c new file mode 100644 index 000000000000..a91e94c10b6a --- /dev/null +++ b/tests/sys/psa_crypto_persistent_storage/test_psa_crypto_store_persistent_single_key.c @@ -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 + * + * @} + */ + +#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; +} diff --git a/tests/sys/psa_crypto_persistent_storage/tests/01-run.py b/tests/sys/psa_crypto_persistent_storage/tests/01-run.py new file mode 100755 index 000000000000..dc56107fc371 --- /dev/null +++ b/tests/sys/psa_crypto_persistent_storage/tests/01-run.py @@ -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()) diff --git a/tests/sys/psa_crypto_persistent_storage/tests_psa_persistent_storage.h b/tests/sys/psa_crypto_persistent_storage/tests_psa_persistent_storage.h new file mode 100644 index 000000000000..f37779145e4e --- /dev/null +++ b/tests/sys/psa_crypto_persistent_storage/tests_psa_persistent_storage.h @@ -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 + * @{ + * + * @file tests_psa_cbor_enc_dec.h + * @brief + * + * @author Lena Boeckmann + * + */ + +#ifndef TESTS_PSA_PERSISTENT_STORAGE_H +#define TESTS_PSA_PERSISTENT_STORAGE_H + +#include +#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 */ +/** @} */