diff --git a/include/psa/crypto_extra.h b/include/psa/crypto_extra.h index 130ce7544b..3550122367 100644 --- a/include/psa/crypto_extra.h +++ b/include/psa/crypto_extra.h @@ -175,6 +175,44 @@ static inline void psa_clear_key_slot_number( attributes->core.flags &= ~MBEDTLS_PSA_KA_FLAG_HAS_SLOT_NUMBER; } +/** Register a key that is already present in a secure element. + * + * The key must be located in a secure element designated by the + * lifetime field in \p attributes, in the slot set with + * psa_set_key_slot_number() in the attribute structure. + * This function makes the key available through the key identifier + * specified in \p attributes. + * + * \param[in] attributes The attributes of the existing key. + * + * \retval #PSA_SUCCESS + * The key was successfully registered. + * Note that depending on the design of the driver, this may or may + * not guarantee that a key actually exists in the designated slot + * and is compatible with the specified attributes. + * \retval #PSA_ERROR_ALREADY_EXISTS + * There is already a key with the identifier specified in + * \p attributes. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p attributes specifies a lifetime which is not located + * in a secure element. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * No slot number is specified in \p attributes, + * or the specified slot number is not valid. + * \retval #PSA_ERROR_NOT_PERMITTED + * The caller is not authorized to register the specified key slot. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY + * \retval #PSA_ERROR_COMMUNICATION_FAILURE + * \retval #PSA_ERROR_HARDWARE_FAILURE + * \retval #PSA_ERROR_CORRUPTION_DETECTED + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t mbedtls_psa_register_se_key( + const psa_key_attributes_t *attributes); + #endif /* MBEDTLS_PSA_CRYPTO_SE_C */ /**@}*/ diff --git a/include/psa/crypto_se_driver.h b/include/psa/crypto_se_driver.h index 9a5d97da7a..f04aa3468e 100644 --- a/include/psa/crypto_se_driver.h +++ b/include/psa/crypto_se_driver.h @@ -810,25 +810,66 @@ typedef struct { */ /**@{*/ +/** An enumeration indicating how a key is created. + */ +typedef enum +{ + PSA_KEY_CREATION_IMPORT, /**< During psa_import_key() */ + PSA_KEY_CREATION_GENERATE, /**< During psa_generate_key() */ + PSA_KEY_CREATION_DERIVE, /**< During psa_key_derivation_output_key() */ + PSA_KEY_CREATION_COPY, /**< During psa_copy_key() */ + +#ifndef __DOXYGEN_ONLY__ + /** A key is being registered with mbedtls_psa_register_se_key(). + * + * The core only passes this value to + * psa_drv_se_key_management_t::p_validate_slot_number, not to + * psa_drv_se_key_management_t::p_allocate. The call to + * `p_validate_slot_number` is not followed by any other call to the + * driver: the key is considered successfully registered if the call to + * `p_validate_slot_number` succeeds, or if `p_validate_slot_number` is + * null. + * + * With this creation method, the driver must return #PSA_SUCCESS if + * the given attributes are compatible with the existing key in the slot, + * and #PSA_ERROR_DOES_NOT_EXIST if the driver can determine that there + * is no key with the specified slot number. + * + * This is an Mbed Crypto extension. + */ + PSA_KEY_CREATION_REGISTER, +#endif +} psa_key_creation_method_t; + /** \brief A function that allocates a slot for a key. * * To create a key in a specific slot in a secure element, the core * first calls this function to determine a valid slot number, * then calls a function to create the key material in that slot. - * For example, in nominal conditions (that is, if no error occurs), - * the effect of a call to psa_import_key() with a lifetime that places - * the key in a secure element is the following: + * In nominal conditions (that is, if no error occurs), + * the effect of a call to a key creation function in the PSA Cryptography + * API with a lifetime that places the key in a secure element is the + * following: * -# The core calls psa_drv_se_key_management_t::p_allocate * (or in some implementations * psa_drv_se_key_management_t::p_validate_slot_number). The driver * selects (or validates) a suitable slot number given the key attributes * and the state of the secure element. - * -# The core calls psa_drv_se_key_management_t::p_import to import - * the key material in the selected slot. + * -# The core calls a key creation function in the driver. * - * Other key creation methods lead to similar sequences. For example, the - * sequence for psa_generate_key() is the same except that the second step - * is a call to psa_drv_se_key_management_t::p_generate. + * The key creation functions in the PSA Cryptography API are: + * - psa_import_key(), which causes + * a call to `p_allocate` with \p method = #PSA_KEY_CREATION_IMPORT + * then a call to psa_drv_se_key_management_t::p_import. + * - psa_generate_key(), which causes + * a call to `p_allocate` with \p method = #PSA_KEY_CREATION_GENERATE + * then a call to psa_drv_se_key_management_t::p_import. + * - psa_key_derivation_output_key(), which causes + * a call to `p_allocate` with \p method = #PSA_KEY_CREATION_DERIVE + * then a call to psa_drv_se_key_derivation_t::p_derive. + * - psa_copy_key(), which causes + * a call to `p_allocate` with \p method = #PSA_KEY_CREATION_COPY + * then a call to psa_drv_se_key_management_t::p_export. * * In case of errors, other behaviors are possible. * - If the PSA Cryptography subsystem dies after the first step, @@ -852,6 +893,7 @@ typedef struct { * \param[in,out] persistent_data A pointer to the persistent data * that allows writing. * \param[in] attributes Attributes of the key. + * \param method The way in which the key is being created. * \param[out] key_slot Slot where the key will be stored. * This must be a valid slot for a key of the * chosen type. It must be unoccupied. @@ -867,6 +909,7 @@ typedef psa_status_t (*psa_drv_se_allocate_key_t)( psa_drv_se_context_t *drv_context, void *persistent_data, const psa_key_attributes_t *attributes, + psa_key_creation_method_t method, psa_key_slot_number_t *key_slot); /** \brief A function that determines whether a slot number is valid @@ -884,9 +927,10 @@ typedef psa_status_t (*psa_drv_se_allocate_key_t)( * sake of initial device provisioning or onboarding. Such a mechanism may * be added to a future version of the PSA Cryptography API specification. * - * \param[in,out] drv_context The driver context structure. - * \param[in] attributes Attributes of the key. - * \param[in] key_slot Slot where the key is to be stored. + * \param[in,out] drv_context The driver context structure. + * \param[in] attributes Attributes of the key. + * \param method The way in which the key is being created. + * \param[in] key_slot Slot where the key is to be stored. * * \retval #PSA_SUCCESS * The given slot number is valid for a key with the given @@ -903,6 +947,7 @@ typedef psa_status_t (*psa_drv_se_allocate_key_t)( typedef psa_status_t (*psa_drv_se_validate_slot_number_t)( psa_drv_se_context_t *drv_context, const psa_key_attributes_t *attributes, + psa_key_creation_method_t method, psa_key_slot_number_t key_slot); /** \brief A function that imports a key into a secure element in binary format diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 856d8622d4..87ac037b60 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -1520,6 +1520,7 @@ static psa_status_t psa_validate_key_attributes( * In case of failure at any step, stop the sequence and call * psa_fail_key_creation(). * + * \param method An identification of the calling function. * \param[in] attributes Key attributes for the new key. * \param[out] handle On success, a handle for the allocated slot. * \param[out] p_slot On success, a pointer to the prepared slot. @@ -1532,6 +1533,7 @@ static psa_status_t psa_validate_key_attributes( * You must call psa_fail_key_creation() to wipe and free the slot. */ static psa_status_t psa_start_key_creation( + psa_key_creation_method_t method, const psa_key_attributes_t *attributes, psa_key_handle_t *handle, psa_key_slot_t **p_slot, @@ -1540,6 +1542,7 @@ static psa_status_t psa_start_key_creation( psa_status_t status; psa_key_slot_t *slot; + (void) method; *p_drv = NULL; status = psa_validate_key_attributes( attributes, p_drv ); @@ -1567,7 +1570,8 @@ static psa_status_t psa_start_key_creation( slot->attr.flags &= ~MBEDTLS_PSA_KA_MASK_EXTERNAL_ONLY; #if defined(MBEDTLS_PSA_CRYPTO_SE_C) - /* For a key in a secure element, we need to do three things: + /* For a key in a secure element, we need to do three things + * when creating a key (but not when registering an existing key): * create the key file in internal storage, create the * key inside the secure element, and update the driver's * persistent data. Start a transaction that will encompass these @@ -1580,9 +1584,9 @@ static psa_status_t psa_start_key_creation( * secure element driver updates its persistent state, but we do not yet * save the driver's persistent state, so that if the power fails, * we can roll back to a state where the key doesn't exist. */ - if( *p_drv != NULL ) + if( *p_drv != NULL && method != PSA_KEY_CREATION_REGISTER ) { - status = psa_find_se_slot_for_key( attributes, *p_drv, + status = psa_find_se_slot_for_key( attributes, method, *p_drv, &slot->data.se.slot_number ); if( status != PSA_SUCCESS ) return( status ); @@ -1674,7 +1678,13 @@ static psa_status_t psa_finish_key_creation( #endif /* defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) */ #if defined(MBEDTLS_PSA_CRYPTO_SE_C) - if( driver != NULL ) + /* Finish the transaction for a key creation. This does not + * happen when registering an existing key. Detect this case + * by checking whether a transaction is in progress (actual + * creation of a key in a secure element requires a transaction, + * but registration doesn't use one). */ + if( driver != NULL && + psa_crypto_transaction.unknown.type == PSA_CRYPTO_TRANSACTION_CREATE_KEY ) { status = psa_save_se_persistent_data( driver ); if( status != PSA_SUCCESS ) @@ -1717,9 +1727,12 @@ static void psa_fail_key_creation( psa_key_slot_t *slot, * to internal storage), we need to destroy the key in the secure * element. */ - /* Abort the ongoing transaction if any. We already did what it - * takes to undo any partial creation. All that's left is to update - * the transaction data itself. */ + /* Abort the ongoing transaction if any (there may not be one if + * the creation process failed before starting one, or if the + * key creation is a registration of a key in a secure element). + * Earlier functions must already have done what it takes to undo any + * partial creation. All that's left is to update the transaction data + * itself. */ (void) psa_crypto_stop_transaction( ); #endif /* MBEDTLS_PSA_CRYPTO_SE_C */ @@ -1796,7 +1809,8 @@ psa_status_t psa_import_key( const psa_key_attributes_t *attributes, psa_key_slot_t *slot = NULL; psa_se_drv_table_entry_t *driver = NULL; - status = psa_start_key_creation( attributes, handle, &slot, &driver ); + status = psa_start_key_creation( PSA_KEY_CREATION_IMPORT, attributes, + handle, &slot, &driver ); if( status != PSA_SUCCESS ) goto exit; @@ -1848,6 +1862,74 @@ exit: return( status ); } +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) +psa_status_t mbedtls_psa_register_se_key( + const psa_key_attributes_t *attributes ) +{ + psa_status_t status; + psa_key_slot_t *slot = NULL; + psa_se_drv_table_entry_t *driver = NULL; + const psa_drv_se_t *drv; + psa_key_handle_t handle = 0; + + /* Leaving attributes unspecified is not currently supported. + * It could make sense to query the key type and size from the + * secure element, but not all secure elements support this + * and the driver HAL doesn't currently support it. */ + if( psa_get_key_type( attributes ) == PSA_KEY_TYPE_NONE ) + return( PSA_ERROR_NOT_SUPPORTED ); + if( psa_get_key_bits( attributes ) == 0 ) + return( PSA_ERROR_NOT_SUPPORTED ); + + status = psa_start_key_creation( PSA_KEY_CREATION_REGISTER, attributes, + &handle, &slot, &driver ); + if( status != PSA_SUCCESS ) + goto exit; + + if( driver == NULL ) + { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + drv = psa_get_se_driver_methods( driver ); + + if ( psa_get_key_slot_number( attributes, + &slot->data.se.slot_number ) != PSA_SUCCESS ) + { + /* The application didn't specify a slot number. This doesn't + * make sense when registering a slot. */ + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + /* If the driver has a slot number validation method, call it. + * If it doesn't, it means the secure element is unable to validate + * anything and so we have to trust the application. */ + if( drv->key_management != NULL && + drv->key_management->p_validate_slot_number != NULL ) + { + status = drv->key_management->p_validate_slot_number( + psa_get_se_driver_context( driver ), + attributes, + PSA_KEY_CREATION_REGISTER, + slot->data.se.slot_number ); + if( status != PSA_SUCCESS ) + goto exit; + } + + status = psa_finish_key_creation( slot, driver ); + +exit: + if( status != PSA_SUCCESS ) + { + psa_fail_key_creation( slot, driver ); + } + /* Registration doesn't keep the key in RAM. */ + psa_close_key( handle ); + return( status ); +} +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + static psa_status_t psa_copy_key_material( const psa_key_slot_t *source, psa_key_slot_t *target ) { @@ -1899,7 +1981,8 @@ psa_status_t psa_copy_key( psa_key_handle_t source_handle, if( status != PSA_SUCCESS ) goto exit; - status = psa_start_key_creation( &actual_attributes, + status = psa_start_key_creation( PSA_KEY_CREATION_COPY, + &actual_attributes, target_handle, &target_slot, &driver ); if( status != PSA_SUCCESS ) goto exit; @@ -4817,7 +4900,8 @@ psa_status_t psa_key_derivation_output_key( const psa_key_attributes_t *attribut psa_status_t status; psa_key_slot_t *slot = NULL; psa_se_drv_table_entry_t *driver = NULL; - status = psa_start_key_creation( attributes, handle, &slot, &driver ); + status = psa_start_key_creation( PSA_KEY_CREATION_DERIVE, + attributes, handle, &slot, &driver ); #if defined(MBEDTLS_PSA_CRYPTO_SE_C) if( driver != NULL ) { @@ -5863,7 +5947,8 @@ psa_status_t psa_generate_key( const psa_key_attributes_t *attributes, psa_status_t status; psa_key_slot_t *slot = NULL; psa_se_drv_table_entry_t *driver = NULL; - status = psa_start_key_creation( attributes, handle, &slot, &driver ); + status = psa_start_key_creation( PSA_KEY_CREATION_GENERATE, + attributes, handle, &slot, &driver ); #if defined(MBEDTLS_PSA_CRYPTO_SE_C) if( driver != NULL ) { diff --git a/library/psa_crypto_se.c b/library/psa_crypto_se.c index ca38e20651..523c621058 100644 --- a/library/psa_crypto_se.c +++ b/library/psa_crypto_se.c @@ -197,6 +197,7 @@ psa_status_t psa_destroy_se_persistent_data( psa_key_lifetime_t lifetime ) psa_status_t psa_find_se_slot_for_key( const psa_key_attributes_t *attributes, + psa_key_creation_method_t method, psa_se_drv_table_entry_t *driver, psa_key_slot_number_t *slot_number ) { @@ -220,7 +221,8 @@ psa_status_t psa_find_se_slot_for_key( driver->methods->key_management->p_validate_slot_number; if( p_validate_slot_number == NULL ) return( PSA_ERROR_NOT_SUPPORTED ); - status = p_validate_slot_number( &driver->context, attributes, + status = p_validate_slot_number( &driver->context, + attributes, method, *slot_number ); } else @@ -233,7 +235,7 @@ psa_status_t psa_find_se_slot_for_key( return( PSA_ERROR_NOT_SUPPORTED ); status = p_allocate( &driver->context, driver->internal.persistent_data, - attributes, + attributes, method, slot_number ); } return( status ); diff --git a/library/psa_crypto_se.h b/library/psa_crypto_se.h index 378c78ffe3..900a72bd3c 100644 --- a/library/psa_crypto_se.h +++ b/library/psa_crypto_se.h @@ -135,6 +135,7 @@ psa_drv_se_context_t *psa_get_se_driver_context( */ psa_status_t psa_find_se_slot_for_key( const psa_key_attributes_t *attributes, + psa_key_creation_method_t method, psa_se_drv_table_entry_t *driver, psa_key_slot_number_t *slot_number ); diff --git a/tests/suites/test_suite_psa_crypto_se_driver_hal.data b/tests/suites/test_suite_psa_crypto_se_driver_hal.data index e6482ddbc8..267c7b88b7 100644 --- a/tests/suites/test_suite_psa_crypto_se_driver_hal.data +++ b/tests/suites/test_suite_psa_crypto_se_driver_hal.data @@ -110,3 +110,21 @@ key_creation_smoke:PSA_KEY_TYPE_ECC_KEY_PAIR( PSA_ECC_CURVE_SECP256R1 ):PSA_ALG_ Generate key: not supported generate_key_not_supported:PSA_KEY_TYPE_AES:128 + +Key registration: smoke test +register_key_smoke_test:MIN_DRIVER_LIFETIME:-1:PSA_SUCCESS + +Key registration: invalid lifetime (volatile) +register_key_smoke_test:PSA_KEY_LIFETIME_VOLATILE:-1:PSA_ERROR_INVALID_ARGUMENT + +Key registration: invalid lifetime (internal storage) +register_key_smoke_test:PSA_KEY_LIFETIME_PERSISTENT:-1:PSA_ERROR_INVALID_ARGUMENT + +Key registration: invalid lifetime (no registered driver) +register_key_smoke_test:MIN_DRIVER_LIFETIME + 1:-1:PSA_ERROR_INVALID_ARGUMENT + +Key registration: with driver validation (accepted) +register_key_smoke_test:MIN_DRIVER_LIFETIME:1:PSA_SUCCESS + +Key registration: with driver validation (rejected) +register_key_smoke_test:MIN_DRIVER_LIFETIME:0:PSA_ERROR_NOT_PERMITTED diff --git a/tests/suites/test_suite_psa_crypto_se_driver_hal.function b/tests/suites/test_suite_psa_crypto_se_driver_hal.function index 0fab0433f4..4673835d5c 100644 --- a/tests/suites/test_suite_psa_crypto_se_driver_hal.function +++ b/tests/suites/test_suite_psa_crypto_se_driver_hal.function @@ -37,14 +37,38 @@ /* Miscellaneous driver methods */ /****************************************************************/ +typedef struct +{ + psa_key_slot_number_t slot_number; + psa_key_creation_method_t method; + psa_status_t status; +} validate_slot_number_directions_t; +static validate_slot_number_directions_t validate_slot_number_directions; + +/* Validate a choice of slot number as directed. */ +static psa_status_t validate_slot_number_as_directed( + psa_drv_se_context_t *context, + const psa_key_attributes_t *attributes, + psa_key_creation_method_t method, + psa_key_slot_number_t slot_number ) +{ + (void) context; + (void) attributes; + DRIVER_ASSERT( slot_number == validate_slot_number_directions.slot_number ); + DRIVER_ASSERT( method == validate_slot_number_directions.method ); + return( validate_slot_number_directions.status ); +} + /* Allocate slot numbers with a monotonic counter. */ static psa_status_t counter_allocate( psa_drv_se_context_t *context, void *persistent_data, const psa_key_attributes_t *attributes, + psa_key_creation_method_t method, psa_key_slot_number_t *slot_number ) { psa_key_slot_number_t *p_counter = persistent_data; (void) attributes; + (void) method; if( context->persistent_data_size != sizeof( psa_key_slot_number_t ) ) return( PSA_ERROR_DETECTED_BY_DRIVER ); ++*p_counter; @@ -162,10 +186,12 @@ static psa_status_t ram_destroy( psa_drv_se_context_t *context, static psa_status_t ram_allocate( psa_drv_se_context_t *context, void *persistent_data, const psa_key_attributes_t *attributes, + psa_key_creation_method_t method, psa_key_slot_number_t *slot_number ) { ram_slot_usage_t *slot_usage = persistent_data; (void) attributes; + (void) method; DRIVER_ASSERT( context->persistent_data_size == sizeof( ram_slot_usage_t ) ); for( *slot_number = ram_min_slot; *slot_number < ARRAY_LENGTH( ram_slots ); @@ -180,10 +206,12 @@ static psa_status_t ram_allocate( psa_drv_se_context_t *context, static psa_status_t ram_validate_slot_number( psa_drv_se_context_t *context, const psa_key_attributes_t *attributes, + psa_key_creation_method_t method, psa_key_slot_number_t slot_number ) { (void) context; (void) attributes; + (void) method; if( slot_number >= ARRAY_LENGTH( ram_slots ) ) return( PSA_ERROR_INVALID_ARGUMENT ); return( PSA_SUCCESS ); @@ -710,3 +738,74 @@ exit: psa_purge_storage( ); } /* END_CASE */ + +/* BEGIN_CASE */ +void register_key_smoke_test( int lifetime_arg, + int validate, + int expected_status_arg ) +{ + psa_key_lifetime_t lifetime = lifetime_arg; + psa_status_t expected_status = expected_status_arg; + psa_drv_se_t driver; + psa_drv_se_key_management_t key_management; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_key_id_t id = 1; + size_t bit_size = 48; + psa_key_slot_number_t wanted_slot = 0x123456789; + psa_key_handle_t handle = 0; + psa_status_t status; + + memset( &driver, 0, sizeof( driver ) ); + driver.hal_version = PSA_DRV_SE_HAL_VERSION; + if( validate >= 0 ) + { + memset( &key_management, 0, sizeof( key_management ) ); + driver.key_management = &key_management; + key_management.p_validate_slot_number = validate_slot_number_as_directed; + validate_slot_number_directions.slot_number = wanted_slot; + validate_slot_number_directions.method = PSA_KEY_CREATION_REGISTER; + validate_slot_number_directions.status = + ( validate > 0 ? PSA_SUCCESS : PSA_ERROR_NOT_PERMITTED ); + } + + PSA_ASSERT( psa_register_se_driver( MIN_DRIVER_LIFETIME, &driver ) ); + PSA_ASSERT( psa_crypto_init( ) ); + + psa_set_key_id( &attributes, id ); + psa_set_key_lifetime( &attributes, lifetime ); + psa_set_key_usage_flags( &attributes, PSA_KEY_USAGE_EXPORT ); + psa_set_key_type( &attributes, PSA_KEY_TYPE_RAW_DATA ); + psa_set_key_bits( &attributes, bit_size ); + psa_set_key_slot_number( &attributes, wanted_slot ); + + status = mbedtls_psa_register_se_key( &attributes ); + TEST_EQUAL( status, expected_status ); + + if( status != PSA_SUCCESS ) + goto exit; + + /* Test that the key exists and has the expected attributes. */ + PSA_ASSERT( psa_open_key( id, &handle ) ); + if( ! check_key_attributes( handle, &attributes ) ) + goto exit; + PSA_ASSERT( psa_close_key( handle ) ); + + /* Restart and try again. */ + PSA_DONE( ); + PSA_ASSERT( psa_register_se_driver( lifetime, &driver ) ); + PSA_ASSERT( psa_crypto_init( ) ); + PSA_ASSERT( psa_open_key( id, &handle ) ); + if( ! check_key_attributes( handle, &attributes ) ) + goto exit; + /* This time, destroy the key. */ + PSA_ASSERT( psa_destroy_key( handle ) ); + +exit: + psa_reset_key_attributes( &attributes ); + psa_destroy_key( handle ); + PSA_DONE( ); + psa_purge_storage( ); + memset( &validate_slot_number_directions, 0, + sizeof( validate_slot_number_directions ) ); +} +/* END_CASE */