From 5f08f7478b6da61a52768bea83de0e909cecc164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikolai=20G=C3=BCtschow?= Date: Fri, 2 Feb 2024 14:59:46 +0100 Subject: [PATCH] sys/psa_crypto: ed25519 private key {ex,im}port --- pkg/c25519/psa_c25519/edsign.c | 12 ++ .../psa_cryptocell_310/ecc_ed25519.c | 27 ++++ sys/include/psa_crypto/psa/crypto_sizes.h | 48 +++++- .../include/psa_crypto_algorithm_dispatch.h | 15 ++ sys/psa_crypto/include/psa_ecc.h | 13 ++ sys/psa_crypto/psa_crypto.c | 102 ++++++++++-- .../psa_crypto_algorithm_dispatch.c | 67 ++++++++ sys/psa_crypto/psa_crypto_location_dispatch.c | 10 +- tests/sys/psa_crypto/Makefile | 2 + tests/sys/psa_crypto/Makefile.ci | 15 ++ tests/sys/psa_crypto/main.c | 152 ++++++++++++++++-- 11 files changed, 428 insertions(+), 35 deletions(-) diff --git a/pkg/c25519/psa_c25519/edsign.c b/pkg/c25519/psa_c25519/edsign.c index 35f67a4664..9a57c35315 100644 --- a/pkg/c25519/psa_c25519/edsign.c +++ b/pkg/c25519/psa_c25519/edsign.c @@ -36,6 +36,18 @@ psa_status_t psa_generate_ecc_ed25519_key_pair( uint8_t *priv_key_buffer, uint8_ return PSA_SUCCESS; } +psa_status_t psa_derive_ecc_ed25519_public_key( const uint8_t *priv_key_buffer, uint8_t *pub_key_buffer, + size_t priv_key_buffer_length, + size_t *pub_key_buffer_length) +{ + *pub_key_buffer_length = EDSIGN_PUBLIC_KEY_SIZE; + + edsign_sec_to_pub(pub_key_buffer, priv_key_buffer); + + (void)priv_key_buffer_length; + return PSA_SUCCESS; +} + psa_status_t psa_ecc_ed25519_sign_message( const uint8_t *priv_key_buffer, size_t priv_key_buffer_size, const uint8_t *pub_key_buffer, diff --git a/pkg/driver_cryptocell_310/psa_cryptocell_310/ecc_ed25519.c b/pkg/driver_cryptocell_310/psa_cryptocell_310/ecc_ed25519.c index a8d675f89b..f32d62c07e 100644 --- a/pkg/driver_cryptocell_310/psa_cryptocell_310/ecc_ed25519.c +++ b/pkg/driver_cryptocell_310/psa_cryptocell_310/ecc_ed25519.c @@ -60,6 +60,33 @@ done: return CRYS_to_psa_error(ret); } +psa_status_t psa_derive_ecc_ed25519_public_key( const uint8_t *priv_key_buffer, uint8_t *pub_key_buffer, + size_t priv_key_buffer_length, + size_t *pub_key_buffer_length) +{ + CRYS_ECEDW_TempBuff_t tmp; + CRYSError_t ret; + + /* contains seed (private key), concatenated with public key */ + uint8_t secret_key[CRYS_ECEDW_ORD_SIZE_IN_BYTES + CRYS_ECEDW_MOD_SIZE_IN_BYTES] = { 0x0 }; + size_t secret_key_size = sizeof(secret_key); + + *pub_key_buffer_length = CRYS_ECEDW_MOD_SIZE_IN_BYTES; + + cryptocell_310_enable(); + ret = CRYS_ECEDW_SeedKeyPair(priv_key_buffer, priv_key_buffer_length, secret_key, &secret_key_size, + pub_key_buffer, pub_key_buffer_length, &tmp); + cryptocell_310_disable(); + if (ret != CRYS_OK) { + DEBUG("CRYS_ECEDW_SeedKeyPair failed with %s\n", cryptocell310_status_to_humanly_readable(ret)); + goto done; + } + +done: + explicit_bzero(&secret_key, sizeof(secret_key)); + return CRYS_to_psa_error(ret); +} + psa_status_t psa_ecc_ed25519_sign_message(const uint8_t *priv_key_buffer, size_t priv_key_buffer_size, const uint8_t *pub_key_buffer, diff --git a/sys/include/psa_crypto/psa/crypto_sizes.h b/sys/include/psa_crypto/psa/crypto_sizes.h index 1d3177faa8..edbfb2da4d 100644 --- a/sys/include/psa_crypto/psa/crypto_sizes.h +++ b/sys/include/psa_crypto/psa/crypto_sizes.h @@ -791,6 +791,22 @@ extern "C" { #define PSA_ASYMMETRIC_ENCRYPT_OUTPUT_SIZE(key_type, key_bits, alg) \ /* implementation-defined value */ +/** + * @brief Maximum size of the export encoding of an ECC keypair. + * + * @details The representation of an ECC keypair follows + * https://arm-software.github.io/psa-api/crypto/1.1/api/keys/management.html#key-formats + * and is dependent on the family: + * - for twisted Edwards curves: 32B + * - for Weierstrass curves: `ceiling(m/8)`-byte string, big-endian + * where m is the bit size associated with the curve. + */ +#define PSA_KEY_EXPORT_ECC_KEY_MAX_SIZE(key_type, key_bits) \ + (size_t)\ + (PSA_KEY_TYPE_ECC_GET_FAMILY(key_type) == PSA_ECC_FAMILY_TWISTED_EDWARDS ? 32 : \ + (PSA_KEY_TYPE_ECC_GET_FAMILY(key_type) == PSA_ECC_FAMILY_SECP_R1 ? PSA_BITS_TO_BYTES(key_bits) : \ + 0)) + /** * @brief Sufficient output buffer size for @ref psa_export_key(). * @@ -828,7 +844,9 @@ extern "C" { * Unspecified if the parameters are not valid. */ #define PSA_EXPORT_KEY_OUTPUT_SIZE(key_type, key_bits) \ - /* implementation-defined value */ + (PSA_KEY_TYPE_IS_PUBLIC_KEY(key_type) ? PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(key_type, key_bits) : \ + (PSA_KEY_TYPE_IS_ECC(key_type) ? PSA_KEY_EXPORT_ECC_KEY_MAX_SIZE(key_type, key_bits) : \ + 0)) /** * @brief Check whether the key size is a valid ECC size for key type. @@ -861,7 +879,19 @@ extern "C" { * * See also @ref PSA_EXPORT_KEY_OUTPUT_SIZE(). */ -#define PSA_EXPORT_KEY_PAIR_MAX_SIZE /* implementation-defined value */ +#if (IS_USED(MODULE_PSA_ASYMMETRIC_ECC_P256R1) || \ + IS_USED(MODULE_PSA_SECURE_ELEMENT_ATECCX08A_ECC_P256)) +#define PSA_EXPORT_KEY_PAIR_MAX_SIZE \ + (PSA_EXPORT_KEY_OUTPUT_SIZE(PSA_ECC_FAMILY_SECT_R1, 256)) +#elif (IS_USED(MODULE_PSA_ASYMMETRIC_ECC_ED25519)) +#define PSA_EXPORT_KEY_PAIR_MAX_SIZE \ + (PSA_EXPORT_KEY_OUTPUT_SIZE(PSA_ECC_FAMILY_TWISTED_EDWARDS, 255)) +#elif (IS_USED(MODULE_PSA_ASYMMETRIC_ECC_P192R1)) +#define PSA_EXPORT_KEY_PAIR_MAX_SIZE \ + (PSA_EXPORT_KEY_OUTPUT_SIZE(PSA_ECC_FAMILY_SECT_R1, 192)) +#else +#define PSA_EXPORT_KEY_PAIR_MAX_SIZE 0 +#endif /** * @brief Get curve size from ECC public key @@ -958,13 +988,17 @@ extern "C" { * See also @ref PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(@p key_type, @p key_bits). */ #if (IS_USED(MODULE_PSA_ASYMMETRIC_ECC_P256R1) || \ - IS_USED(MODULE_PSA_ASYMMETRIC_ECC_P192R1) || \ - IS_USED(MODULE_PSA_SECURE_ELEMENT_ATECCX08A_ECC_P256)) + IS_USED(MODULE_PSA_SECURE_ELEMENT_ATECCX08A_ECC_P256)) #define PSA_EXPORT_PUBLIC_KEY_MAX_SIZE \ - (PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(PSA_ECC_FAMILY_SECT_R1, PSA_MAX_PRIV_KEY_SIZE)) + (PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(PSA_ECC_FAMILY_SECT_R1, 256)) +#elif (IS_USED(MODULE_PSA_ASYMMETRIC_ECC_P192R1)) +#define PSA_EXPORT_PUBLIC_KEY_MAX_SIZE \ + (PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(PSA_ECC_FAMILY_SECT_R1, 192)) +#elif (IS_USED(MODULE_PSA_ASYMMETRIC_ECC_ED25519)) +#define PSA_EXPORT_PUBLIC_KEY_MAX_SIZE \ + (PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(PSA_ECC_FAMILY_TWISTED_EDWARDS, 255)) #else -#define PSA_EXPORT_PUBLIC_KEY_MAX_SIZE \ - (PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(PSA_ECC_FAMILY_TWISTED_EDWARDS, PSA_MAX_PRIV_KEY_SIZE)) +#define PSA_EXPORT_PUBLIC_KEY_MAX_SIZE 0 #endif /** diff --git a/sys/psa_crypto/include/psa_crypto_algorithm_dispatch.h b/sys/psa_crypto/include/psa_crypto_algorithm_dispatch.h index dd0e16085d..08f47cf930 100644 --- a/sys/psa_crypto/include/psa_crypto_algorithm_dispatch.h +++ b/sys/psa_crypto/include/psa_crypto_algorithm_dispatch.h @@ -175,6 +175,21 @@ psa_status_t psa_algorithm_dispatch_verify_message( const psa_key_attributes_t * */ psa_status_t psa_algorithm_dispatch_generate_key( const psa_key_attributes_t *attributes, psa_key_slot_t *slot); + +/** + * @brief Dispatch the key import function to a specific backend. + * See psa_import_key() + * + * @param attributes + * @param data + * @param data_length + * @param slot + * @param bits + * @return psa_status_t + */ +psa_status_t psa_algorithm_dispatch_import_key(const psa_key_attributes_t *attributes, + const uint8_t *data, size_t data_length, + psa_key_slot_t *slot, size_t *bits); #endif #if IS_USED(MODULE_PSA_CIPHER) diff --git a/sys/psa_crypto/include/psa_ecc.h b/sys/psa_crypto/include/psa_ecc.h index 70639a45c0..42a3e6f404 100644 --- a/sys/psa_crypto/include/psa_ecc.h +++ b/sys/psa_crypto/include/psa_ecc.h @@ -253,6 +253,19 @@ psa_status_t psa_generate_ecc_ed25519_key_pair( uint8_t *priv_key_buffer, uint8_ size_t *priv_key_buffer_length, size_t *pub_key_buffer_length); +/** + * @brief Low level wrapper function to call a driver for deriving an ed25519 public key from the private key. + * + * @param[in] priv_key_buffer + * @param[out] pub_key_buffer + * @param[in] priv_key_buffer_length + * @param[inout] pub_key_buffer_length + * @return @ref psa_status_t + */ +psa_status_t psa_derive_ecc_ed25519_public_key( const uint8_t *priv_key_buffer, uint8_t *pub_key_buffer, + size_t priv_key_buffer_length, + size_t *pub_key_buffer_length); + /** * @brief Low level wrapper function to call a driver for an ECC hash signature * with an ed25519 key. diff --git a/sys/psa_crypto/psa_crypto.c b/sys/psa_crypto/psa_crypto.c index ae53ac1e0b..5f977cb50e 100644 --- a/sys/psa_crypto/psa_crypto.c +++ b/sys/psa_crypto/psa_crypto.c @@ -1220,16 +1220,102 @@ psa_status_t psa_destroy_key(psa_key_id_t key) return psa_wipe_key_slot(slot); } +/** + * @brief Export key that is stored in local memory + * + * See @ref psa_export_key + * + * @param key_buffer + * @param key_buffer_size + * @param data + * @param data_size + * @param data_length + * + * @return @ref PSA_SUCCESS + * @ref PSA_ERROR_INVALID_ARGUMENT + */ +static psa_status_t psa_builtin_export_key(const uint8_t *key_buffer, + size_t key_buffer_size, + uint8_t *data, + size_t data_size, + size_t *data_length) +{ + if (!key_buffer || !data || !data_length) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + if (key_buffer_size == 0 || data_size == 0) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + if (data_size < key_buffer_size) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + memcpy(data, key_buffer, key_buffer_size); + *data_length = key_buffer_size; + + return PSA_SUCCESS; +} + psa_status_t psa_export_key(psa_key_id_t key, uint8_t *data, size_t data_size, size_t *data_length) { - (void)key; - (void)data; - (void)data_size; - (void)data_length; - return PSA_ERROR_NOT_SUPPORTED; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot; + uint8_t *privkey_data = NULL; + size_t *privkey_data_len = NULL; + + if (!lib_initialized) { + return PSA_ERROR_BAD_STATE; + } + + if (!data || !data_length) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + *data_length = 0; + + status = psa_get_and_lock_key_slot_with_policy(key, &slot, PSA_KEY_USAGE_EXPORT, 0); + if (status != PSA_SUCCESS) { + unlock_status = psa_unlock_key_slot(slot); + if (unlock_status != PSA_SUCCESS) { + status = unlock_status; + } + return status; + } + + psa_key_lifetime_t lifetime = psa_get_key_lifetime(&slot->attr); + if (psa_key_lifetime_is_external(lifetime)) { + /* key export from an external device is currently not supported */ + status = PSA_ERROR_NOT_SUPPORTED; + unlock_status = psa_unlock_key_slot(slot); + if (unlock_status != PSA_SUCCESS) { + status = unlock_status; + } + return status; + } + + if (!PSA_KEY_TYPE_IS_ECC(slot->attr.type) || + PSA_KEY_TYPE_ECC_GET_FAMILY(slot->attr.type) != PSA_ECC_FAMILY_TWISTED_EDWARDS) { + /* key export is currently only supported for ed25519 keys */ + status = PSA_ERROR_NOT_SUPPORTED; + unlock_status = psa_unlock_key_slot(slot); + if (unlock_status != PSA_SUCCESS) { + status = unlock_status; + } + return status; + } + + psa_get_key_data_from_key_slot(slot, &privkey_data, &privkey_data_len); + + status = + psa_builtin_export_key(privkey_data, *privkey_data_len, data, data_size, data_length); + + unlock_status = psa_unlock_key_slot(slot); + return ((status == PSA_SUCCESS) ? unlock_status : status); } /** @@ -1260,10 +1346,6 @@ static psa_status_t psa_builtin_export_public_key( const uint8_t *key_buffer, DEBUG("PSA Crypto Builtin Export Key: Output buffer too small\n"); return PSA_ERROR_BUFFER_TOO_SMALL; } - /** Some implementations and drivers can generate a public key from existing private key - * material. This implementation does not support the recalculation of a public key, yet. - * It requires the key to already exist in local memory and just copies it into the data - * output. */ memcpy(data, key_buffer, key_buffer_size); *data_length = key_buffer_size; @@ -1456,8 +1538,10 @@ psa_status_t psa_builtin_import_key(const psa_key_attributes_t *attributes, if (data_length > PSA_EXPORT_PUBLIC_KEY_MAX_SIZE) { return PSA_ERROR_NOT_SUPPORTED; } + memcpy(key_buffer, data, data_length); *key_buffer_length = data_length; + return PSA_SUCCESS; } return status; diff --git a/sys/psa_crypto/psa_crypto_algorithm_dispatch.c b/sys/psa_crypto/psa_crypto_algorithm_dispatch.c index 3a247f857a..46cfbb58d5 100644 --- a/sys/psa_crypto/psa_crypto_algorithm_dispatch.c +++ b/sys/psa_crypto/psa_crypto_algorithm_dispatch.c @@ -427,6 +427,73 @@ psa_status_t psa_algorithm_dispatch_generate_key( const psa_key_attributes_t * return psa_builtin_generate_key(attributes, key_data, *key_bytes, key_bytes); } + +psa_status_t psa_algorithm_dispatch_import_key(const psa_key_attributes_t *attributes, + const uint8_t *data, size_t data_length, + psa_key_slot_t *slot, size_t *bits) +{ + uint8_t *key_data = NULL; + size_t *key_bytes = NULL; + size_t key_data_size; + + key_data_size = psa_get_key_data_from_key_slot(slot, &key_data, &key_bytes); + + /** + * Asymmetric keys needs special import handling: + * The public key needs to be derived from the imported private key. + */ + if (PSA_KEY_TYPE_IS_KEY_PAIR(attributes->type)) { + psa_asym_key_t asym_key = PSA_INVALID_OPERATION; + uint8_t *pubkey_data = NULL; + size_t *pubkey_data_len = NULL; + psa_get_public_key_data_from_key_slot(slot, &pubkey_data, &pubkey_data_len); + + if (PSA_KEY_TYPE_IS_ECC_KEY_PAIR(attributes->type)) { + asym_key = + PSA_ENCODE_ECC_KEY_TYPE(attributes->bits, + PSA_KEY_TYPE_ECC_GET_FAMILY(attributes->type)); + + if (asym_key == PSA_INVALID_OPERATION) { + return PSA_ERROR_INVALID_ARGUMENT; + } + } + + // derive and save public from private key + psa_status_t ret = PSA_ERROR_NOT_SUPPORTED; + switch (asym_key) { +#if IS_USED(MODULE_PSA_ASYMMETRIC_ECC_P192R1) + case PSA_ECC_P192_R1: + // todo: support for Weierstrass curves + (void)slot; + ret = PSA_ERROR_NOT_SUPPORTED; + break; +#endif +#if IS_USED(MODULE_PSA_ASYMMETRIC_ECC_P256R1) + case PSA_ECC_P256_R1: + // todo: support for Weierstrass curves + (void)slot; + ret = PSA_ERROR_NOT_SUPPORTED; + break; +#endif +#if IS_USED(MODULE_PSA_ASYMMETRIC_ECC_ED25519) + case PSA_ECC_ED25519: + ret = psa_derive_ecc_ed25519_public_key(data, pubkey_data, data_length, pubkey_data_len); + break; +#endif + default: + (void)slot; + ret = PSA_ERROR_NOT_SUPPORTED; + break; + } + if (ret == PSA_SUCCESS) { + /* save private key data */ + memcpy(key_data, data, data_length); + *key_bytes = data_length; + } + return ret; + } + return psa_builtin_import_key(attributes, data, data_length, key_data, key_data_size, key_bytes, bits); +} #endif /* MODULE_PSA_KEY_MANAGEMENT */ #if IS_USED(MODULE_PSA_CIPHER) diff --git a/sys/psa_crypto/psa_crypto_location_dispatch.c b/sys/psa_crypto/psa_crypto_location_dispatch.c index d23fde67df..d83160873b 100644 --- a/sys/psa_crypto/psa_crypto_location_dispatch.c +++ b/sys/psa_crypto/psa_crypto_location_dispatch.c @@ -66,15 +66,10 @@ psa_status_t psa_location_dispatch_import_key( const psa_key_attributes_t *attri const uint8_t *data, size_t data_length, psa_key_slot_t *slot, size_t *bits) { - psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION(attributes->lifetime); - uint8_t *key_data = NULL; - size_t *key_bytes = NULL; - size_t key_data_size; - - key_data_size = psa_get_key_data_from_key_slot(slot, &key_data, &key_bytes); #if IS_USED(MODULE_PSA_SECURE_ELEMENT) + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; const psa_drv_se_t *drv; psa_drv_se_context_t *drv_context; psa_key_slot_number_t *slot_number = psa_key_slot_get_slot_number(slot); @@ -100,9 +95,8 @@ psa_status_t psa_location_dispatch_import_key( const psa_key_attributes_t *attri switch (location) { case PSA_KEY_LOCATION_LOCAL_STORAGE: - return psa_builtin_import_key(attributes, data, data_length, key_data, key_data_size, key_bytes, bits); + return psa_algorithm_dispatch_import_key(attributes, data, data_length, slot, bits); default: - (void)status; return PSA_ERROR_NOT_SUPPORTED; } } diff --git a/tests/sys/psa_crypto/Makefile b/tests/sys/psa_crypto/Makefile index 3396041f59..ea7111839f 100644 --- a/tests/sys/psa_crypto/Makefile +++ b/tests/sys/psa_crypto/Makefile @@ -4,6 +4,8 @@ USEMODULE += embunit USEMODULE += psa_crypto +CFLAGS += -DTHREAD_STACKSIZE_MAIN=\(8*THREAD_STACKSIZE_DEFAULT\) + CFLAGS += -DCONFIG_PSA_ASYMMETRIC_KEYPAIR_COUNT=1 CFLAGS += -DCONFIG_PSA_SINGLE_KEY_COUNT=1 diff --git a/tests/sys/psa_crypto/Makefile.ci b/tests/sys/psa_crypto/Makefile.ci index 645faf5d90..2d2fc716e2 100644 --- a/tests/sys/psa_crypto/Makefile.ci +++ b/tests/sys/psa_crypto/Makefile.ci @@ -6,6 +6,21 @@ BOARD_INSUFFICIENT_MEMORY := \ atmega328p \ atmega328p-xplained-mini \ atmega8 \ + bluepill-stm32f030c8 \ + i-nucleo-lrwan1 \ + nucleo-f030r8 \ + nucleo-f031k6 \ + nucleo-f042k6 \ nucleo-l011k4 \ + nucleo-l031k6 \ + nucleo-l053r8 \ + samd10-xmini \ + slstk3400a \ + stk3200 \ stm32f030f4-demo \ + stm32f0discovery \ + stm32g0316-disco \ + stm32l0538-disco \ + waspmote-pro \ + weact-g030f6 \ # diff --git a/tests/sys/psa_crypto/main.c b/tests/sys/psa_crypto/main.c index dfbe20237d..5c65d1b1d2 100644 --- a/tests/sys/psa_crypto/main.c +++ b/tests/sys/psa_crypto/main.c @@ -22,37 +22,167 @@ #include "embUnit.h" #include "psa/crypto.h" -#define TEST_ASSERT_PSA(func_) TEST_ASSERT_MESSAGE(func_ == PSA_SUCCESS, #func_ " failed"); +void addFailurePSA(const char *func, psa_status_t errcode, long line, const char *file) +{ + static char msg[128]; + strncpy(msg, func, sizeof(msg)); + strcat(msg, ": "); + strcat(msg, psa_status_to_humanly_readable(errcode)); + addFailure(msg, line, file); +} + +#define TEST_ASSERT_PSA(func_, do_) { psa_status_t ret = func_; if (ret != PSA_SUCCESS) { addFailurePSA(#func_, ret, __LINE__, __FILE__); do_; } } +#define TEST_ASSERT_PSA_CLEANUP(func_) TEST_ASSERT_PSA(func_, goto cleanup) +#define TEST_ASSERT_PSA_RETURN(func_) TEST_ASSERT_PSA(func_, return) +#define TEST_ASSERT_PSA_CONTINUE(func_) TEST_ASSERT_PSA(func_, ) /* * A second call to psa_crypto_init() should not reset key data. */ static void test_init_twice(void) { - TEST_ASSERT_PSA(psa_crypto_init()); + const psa_key_type_t key_type = PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS); + const size_t key_bits = 255; + const psa_algorithm_t key_alg = PSA_ALG_PURE_EDDSA; + uint8_t key_data[PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(key_type, key_bits)]; + size_t key_data_len; psa_key_id_t key_id = PSA_KEY_ID_NULL; psa_key_attributes_t key_attr = psa_key_attributes_init(); - psa_set_key_algorithm(&key_attr, PSA_ALG_PURE_EDDSA); + psa_set_key_algorithm(&key_attr, key_alg); psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_EXPORT); - psa_set_key_bits(&key_attr, 255); - psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS)); - TEST_ASSERT_PSA(psa_generate_key(&key_attr, &key_id)); + psa_set_key_bits(&key_attr, key_bits); + psa_set_key_type(&key_attr, key_type); - uint8_t key_data[PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(psa_get_key_type(&key_attr), psa_get_key_bits(&key_attr))]; - size_t key_data_len; + TEST_ASSERT_PSA_CLEANUP(psa_crypto_init()); + TEST_ASSERT_PSA_CLEANUP(psa_generate_key(&key_attr, &key_id)); - TEST_ASSERT_PSA(psa_export_public_key(key_id, key_data, sizeof(key_data), &key_data_len)); - TEST_ASSERT_PSA(psa_crypto_init()); - TEST_ASSERT_PSA(psa_export_public_key(key_id, key_data, sizeof(key_data), &key_data_len)); + TEST_ASSERT_PSA_CLEANUP(psa_export_public_key(key_id, key_data, sizeof(key_data), &key_data_len)); + TEST_ASSERT_PSA_CLEANUP(psa_crypto_init()); + TEST_ASSERT_PSA_CLEANUP(psa_export_public_key(key_id, key_data, sizeof(key_data), &key_data_len)); +cleanup: + TEST_ASSERT_PSA_CONTINUE(psa_destroy_key(key_id)); +} + +/** + * Exporting and re-importing a private Ed25519 key should result in the same public key and signature. + */ +static void test_exported_key_is_identical_when_imported_again_ed25519(void) +{ + const psa_key_type_t key_type = PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS); + const size_t key_bits = 255; + const psa_algorithm_t key_alg = PSA_ALG_PURE_EDDSA; + + // buffers to hold randomly generated Ed25519 keypair + uint8_t privkey[PSA_EXPORT_KEY_OUTPUT_SIZE(key_type, key_bits)]; + size_t privkey_len; + uint8_t pubkey[PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(key_type, key_bits)]; + size_t pubkey_len; + + // buffer to hold Ed25519 public key derived from imported private key + uint8_t pubkey2[PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(key_type, key_bits)]; + size_t pubkey2_len; + + // buffers to hold signature for verification + uint8_t msg[] = "Hello world!"; + uint8_t sig[PSA_SIGN_OUTPUT_SIZE(key_type, key_bits, key_alg)]; + size_t sig_len; + uint8_t sig2[PSA_SIGN_OUTPUT_SIZE(key_type, key_bits, key_alg)]; + size_t sig2_len; + + // work on Ed25519 keypair which can be exported and used for signing messages + psa_key_id_t key_id = PSA_KEY_ID_NULL; + psa_key_attributes_t key_attr = psa_key_attributes_init(); + psa_set_key_algorithm(&key_attr, key_alg); + psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_SIGN_MESSAGE | PSA_KEY_USAGE_EXPORT); + psa_set_key_bits(&key_attr, key_bits); + psa_set_key_type(&key_attr, key_type); + + // randomly generate keypair + TEST_ASSERT_PSA_CLEANUP(psa_crypto_init()); + TEST_ASSERT_PSA_CLEANUP(psa_generate_key(&key_attr, &key_id)); + + // sign msg with generated keypair + TEST_ASSERT_PSA_CLEANUP(psa_sign_message(key_id, key_alg, msg, sizeof(msg), sig, sizeof(sig), &sig_len)); + + // export public and private key, then free slot + TEST_ASSERT_PSA_CLEANUP(psa_export_public_key(key_id, pubkey, sizeof(pubkey), &pubkey_len)); + TEST_ASSERT_PSA_CLEANUP(psa_export_key(key_id, privkey, sizeof(privkey), &privkey_len)); + TEST_ASSERT_PSA_CLEANUP(psa_destroy_key(key_id)); + + // import private key and compare public keys + psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_SIGN_MESSAGE); + TEST_ASSERT_PSA_CLEANUP(psa_import_key(&key_attr, privkey, privkey_len, &key_id)); + TEST_ASSERT_PSA_CLEANUP(psa_export_public_key(key_id, pubkey2, sizeof(pubkey2), &pubkey2_len)); + TEST_ASSERT(pubkey_len == pubkey2_len && memcmp(pubkey, pubkey2, pubkey_len) == 0); + + // sign msg with imported key and compare signatures + TEST_ASSERT_PSA_CLEANUP(psa_sign_message(key_id, key_alg, msg, sizeof(msg), sig2, sizeof(sig2), &sig2_len)); + TEST_ASSERT(sig_len == sig2_len && memcmp(sig, sig2, sig_len) == 0); + +cleanup: + TEST_ASSERT_PSA_CONTINUE(psa_destroy_key(key_id)); +} + +/** + * psa_export_key() is an alias for psa_export_public_key() if the given key is a public key + */ +static void test_export_public_key_ed25519(void) +{ + const psa_key_type_t key_type = PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS); + const psa_key_type_t key_type2 = PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_TWISTED_EDWARDS); + const size_t key_bits = 255; + const psa_algorithm_t key_alg = PSA_ALG_PURE_EDDSA; + + // buffers to hold exported public key + uint8_t pubkey[PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(key_type, key_bits)]; + size_t pubkey_len; + uint8_t pubkey2[PSA_EXPORT_KEY_OUTPUT_SIZE(key_type2, key_bits)]; + size_t pubkey2_len; + + // work on Ed25519 keypair which has no special usage permissions + // as exporting the public key is allowed even without PSA_KEY_USAGE_EXPORT + psa_key_id_t key_id = PSA_KEY_ID_NULL; + psa_key_attributes_t key_attr = psa_key_attributes_init(); + psa_set_key_algorithm(&key_attr, key_alg); + psa_set_key_usage_flags(&key_attr, 0); + psa_set_key_bits(&key_attr, key_bits); + psa_set_key_type(&key_attr, key_type); + + psa_key_id_t key_id2 = PSA_KEY_ID_NULL; + + // randomly generate keypair + TEST_ASSERT_PSA_CLEANUP(psa_crypto_init()); + TEST_ASSERT_PSA_CLEANUP(psa_generate_key(&key_attr, &key_id)); + + // export public key + TEST_ASSERT_PSA_CLEANUP(psa_export_public_key(key_id, pubkey, sizeof(pubkey), &pubkey_len)); + + // import public key into single-key slot + psa_set_key_type(&key_attr, key_type2); + TEST_ASSERT_PSA_CLEANUP(psa_import_key(&key_attr, pubkey, pubkey_len, &key_id2)); + + // export public key from single slot using `psa_export_key()` and compare + TEST_ASSERT_PSA_CLEANUP(psa_export_key(key_id2, pubkey2, sizeof(pubkey2), &pubkey2_len)); + TEST_ASSERT(pubkey_len == pubkey2_len && memcmp(pubkey, pubkey2, pubkey_len) == 0); + + // export public key from single slot using `psa_export_public_key()` and compare again + TEST_ASSERT_PSA_CLEANUP(psa_export_public_key(key_id2, pubkey2, sizeof(pubkey2), &pubkey2_len)); + TEST_ASSERT(pubkey_len == pubkey2_len && memcmp(pubkey, pubkey2, pubkey_len) == 0); + +cleanup: + TEST_ASSERT_PSA_CONTINUE(psa_destroy_key(key_id)); + TEST_ASSERT_PSA_CONTINUE(psa_destroy_key(key_id2)); } static Test *tests_psa_crypto(void) { EMB_UNIT_TESTFIXTURES(fixtures) { new_TestFixture(test_init_twice), + new_TestFixture(test_exported_key_is_identical_when_imported_again_ed25519), + new_TestFixture(test_export_public_key_ed25519), }; EMB_UNIT_TESTCALLER(tests, NULL, NULL, fixtures);