diff --git a/include/psa/crypto_se_driver.h b/include/psa/crypto_se_driver.h index 69cdababac..9a5d97da7a 100644 --- a/include/psa/crypto_se_driver.h +++ b/include/psa/crypto_se_driver.h @@ -811,6 +811,42 @@ typedef struct { /**@{*/ /** \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: + * -# 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. + * + * 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. + * + * In case of errors, other behaviors are possible. + * - If the PSA Cryptography subsystem dies after the first step, + * for example because the device has lost power abruptly, + * the second step may never happen, or may happen after a reset + * and re-initialization. Alternatively, after a reset and + * re-initialization, the core may call + * psa_drv_se_key_management_t::p_destroy on the slot number that + * was allocated (or validated) instead of calling a key creation function. + * - If an error occurs, the core may call + * psa_drv_se_key_management_t::p_destroy on the slot number that + * was allocated (or validated) instead of calling a key creation function. + * + * Errors and system resets also have an impact on the driver's persistent + * data. If a reset happens before the overall key creation process is + * completed (before or after the second step above), it is unspecified + * whether the persistent data after the reset is identical to what it + * was before or after the call to `p_allocate` (or `p_validate_slot_number`). * * \param[in,out] drv_context The driver context structure. * \param[in,out] persistent_data A pointer to the persistent data @@ -833,6 +869,42 @@ typedef psa_status_t (*psa_drv_se_allocate_key_t)( const psa_key_attributes_t *attributes, psa_key_slot_number_t *key_slot); +/** \brief A function that determines whether a slot number is valid + * for a key. + * + * To create a key in a specific slot in a secure element, the core + * first calls this function to validate the choice of slot number, + * then calls a function to create the key material in that slot. + * See the documentation of #psa_drv_se_allocate_key_t for more details. + * + * As of the PSA Cryptography API specification version 1.0, there is no way + * for applications to trigger a call to this function. However some + * implementations offer the capability to create or declare a key in + * a specific slot via implementation-specific means, generally for the + * 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. + * + * \retval #PSA_SUCCESS + * The given slot number is valid for a key with the given + * attributes. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The given slot number is not valid for a key with the + * given attributes. This includes the case where the slot + * number is not valid at all. + * \retval #PSA_ERROR_ALREADY_EXISTS + * There is already a key with the specified slot number. + * Drivers may choose to return this error from the key + * creation function instead. + */ +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_slot_number_t key_slot); + /** \brief A function that imports a key into a secure element in binary format * * This function can support any output from psa_export_key(). Refer to the @@ -977,8 +1049,10 @@ typedef psa_status_t (*psa_drv_se_generate_key_t)(psa_drv_se_context_t *drv_cont * If one of the functions is not implemented, it should be set to NULL. */ typedef struct { - /** Function that allocates a slot. */ + /** Function that allocates a slot for a key. */ psa_drv_se_allocate_key_t p_allocate; + /** Function that checks the validity of a slot for a key. */ + psa_drv_se_validate_slot_number_t p_validate_slot_number; /** Function that performs a key import operation */ psa_drv_se_import_key_t p_import; /** Function that performs a generation */ diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 5cb88de7e9..856d8622d4 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -1582,10 +1582,6 @@ static psa_status_t psa_start_key_creation( * we can roll back to a state where the key doesn't exist. */ if( *p_drv != NULL ) { - /* Choosing a slot number is not supported yet. */ - if( attributes->core.flags & MBEDTLS_PSA_KA_FLAG_HAS_SLOT_NUMBER ) - return( PSA_ERROR_NOT_SUPPORTED ); - status = psa_find_se_slot_for_key( attributes, *p_drv, &slot->data.se.slot_number ); if( status != PSA_SUCCESS ) diff --git a/library/psa_crypto_se.c b/library/psa_crypto_se.c index bc73251808..ca38e20651 100644 --- a/library/psa_crypto_se.c +++ b/library/psa_crypto_se.c @@ -201,7 +201,6 @@ psa_status_t psa_find_se_slot_for_key( psa_key_slot_number_t *slot_number ) { psa_status_t status; - psa_drv_se_allocate_key_t p_allocate = NULL; /* If the lifetime is wrong, it's a bug in the library. */ if( driver->lifetime != psa_get_key_lifetime( attributes ) ) @@ -210,17 +209,33 @@ psa_status_t psa_find_se_slot_for_key( /* If the driver doesn't support key creation in any way, give up now. */ if( driver->methods->key_management == NULL ) return( PSA_ERROR_NOT_SUPPORTED ); - p_allocate = driver->methods->key_management->p_allocate; - /* If the driver doesn't tell us how to allocate a slot, that's - * not supported for the time being. */ - if( p_allocate == NULL ) - return( PSA_ERROR_NOT_SUPPORTED ); - - status = p_allocate( &driver->context, - driver->internal.persistent_data, - attributes, - slot_number ); + if( psa_get_key_slot_number( attributes, slot_number ) == PSA_SUCCESS ) + { + /* The application wants to use a specific slot. Allow it if + * the driver supports it. On a system with isolation, + * the crypto service must check that the application is + * permitted to request this slot. */ + psa_drv_se_validate_slot_number_t p_validate_slot_number = + 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, + *slot_number ); + } + else + { + /* The application didn't tell us which slot to use. Let the driver + * choose. This is the normal case. */ + psa_drv_se_allocate_key_t p_allocate = + driver->methods->key_management->p_allocate; + if( p_allocate == NULL ) + return( PSA_ERROR_NOT_SUPPORTED ); + status = p_allocate( &driver->context, + driver->internal.persistent_data, + attributes, + slot_number ); + } return( status ); } 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 6fb65f02a3..e6482ddbc8 100644 --- a/tests/suites/test_suite_psa_crypto_se_driver_hal.data +++ b/tests/suites/test_suite_psa_crypto_se_driver_hal.data @@ -39,6 +39,21 @@ key_creation_import_export:0:1 SE key import-export, check after restart (slot 3) key_creation_import_export:3:1 +Key creation in a specific slot (0) +key_creation_in_chosen_slot:0:0:PSA_SUCCESS + +Key creation in a specific slot (max) +key_creation_in_chosen_slot:ARRAY_LENGTH( ram_slots ) - 1:0:PSA_SUCCESS + +Key creation in a specific slot (0, restart) +key_creation_in_chosen_slot:0:1:PSA_SUCCESS + +Key creation in a specific slot (max, restart) +key_creation_in_chosen_slot:ARRAY_LENGTH( ram_slots ) - 1:1:PSA_SUCCESS + +Key creation in a specific slot (too large) +key_creation_in_chosen_slot:ARRAY_LENGTH( ram_slots ):0:PSA_ERROR_INVALID_ARGUMENT + Key creation smoke test: AES-CTR key_creation_smoke:PSA_KEY_TYPE_AES:PSA_ALG_CTR:"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 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 9a5746476f..0fab0433f4 100644 --- a/tests/suites/test_suite_psa_crypto_se_driver_hal.function +++ b/tests/suites/test_suite_psa_crypto_se_driver_hal.function @@ -177,6 +177,18 @@ static psa_status_t ram_allocate( psa_drv_se_context_t *context, return( PSA_ERROR_INSUFFICIENT_STORAGE ); } +static psa_status_t ram_validate_slot_number( + psa_drv_se_context_t *context, + const psa_key_attributes_t *attributes, + psa_key_slot_number_t slot_number ) +{ + (void) context; + (void) attributes; + if( slot_number >= ARRAY_LENGTH( ram_slots ) ) + return( PSA_ERROR_INVALID_ARGUMENT ); + return( PSA_SUCCESS ); +} + /****************************************************************/ @@ -536,6 +548,74 @@ exit: } /* END_CASE */ +/* BEGIN_CASE */ +void key_creation_in_chosen_slot( int slot_arg, + int restart, + int expected_status_arg ) +{ + psa_key_slot_number_t wanted_slot = slot_arg; + psa_status_t expected_status = expected_status_arg; + psa_status_t status; + psa_drv_se_t driver; + psa_drv_se_key_management_t key_management; + psa_key_lifetime_t lifetime = 2; + psa_key_id_t id = 1; + psa_key_handle_t handle = 0; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + const uint8_t key_material[3] = {0xfa, 0xca, 0xde}; + + memset( &driver, 0, sizeof( driver ) ); + memset( &key_management, 0, sizeof( key_management ) ); + driver.hal_version = PSA_DRV_SE_HAL_VERSION; + driver.key_management = &key_management; + driver.persistent_data_size = sizeof( ram_slot_usage_t ); + key_management.p_validate_slot_number = ram_validate_slot_number; + key_management.p_import = ram_import; + key_management.p_destroy = ram_destroy; + key_management.p_export = ram_export; + + PSA_ASSERT( psa_register_se_driver( lifetime, &driver ) ); + PSA_ASSERT( psa_crypto_init( ) ); + + /* Create a key. */ + 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_slot_number( &attributes, wanted_slot ); + status = psa_import_key( &attributes, + key_material, sizeof( key_material ), + &handle ); + TEST_EQUAL( status, expected_status ); + + if( status != PSA_SUCCESS ) + goto exit; + + /* Maybe restart, to check that the information is saved correctly. */ + if( restart ) + { + mbedtls_psa_crypto_free( ); + PSA_ASSERT( psa_register_se_driver( lifetime, &driver ) ); + PSA_ASSERT( psa_crypto_init( ) ); + PSA_ASSERT( psa_open_key( id, &handle ) ); + } + + /* Test that the key was created in the expected slot. */ + TEST_EQUAL( ram_slots[wanted_slot].type, PSA_KEY_TYPE_RAW_DATA ); + + /* Test that the key is reported with the correct attributes, + * including the expected slot. */ + PSA_ASSERT( psa_get_key_attributes( handle, &attributes ) ); + + PSA_ASSERT( psa_destroy_key( handle ) ); + +exit: + PSA_DONE( ); + ram_slots_reset( ); + psa_purge_storage( ); +} +/* END_CASE */ + /* BEGIN_CASE */ void key_creation_smoke( int type_arg, int alg_arg, data_t *key_material )