Skip to content

Secure element key registration #183

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions include/psa/crypto_extra.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate error code - this might be confusing in the API documentation? (or is this pattern used elsewhere)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pattern is used elsewhere: each \retval is an error reason, and some error reasons can map to the same error code. The alternative is something like

 * \retval #PSA_ERROR_INVALID_ARGUMENT
 *         \p attributes specifies a lifetime which is not located
 *         in a secure eleement.
 *
 *         Alternatively, …

I have no strong feelings either way, but I'd only change this if we change it elsewhere.

* 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 */

/**@}*/
Expand Down
69 changes: 57 additions & 12 deletions include/psa/crypto_se_driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -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().
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this in the doxygen ifndef block?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it's an Mbed Crypto extension, so it shouldn't be included in the PSA specification. We normally resolve this by using different files, but this is an enum so it can't be defined partly in one file and partly in another. __DOXYGEN_ONLY__ is not defined when generating the Mbed Crypto documentation.

*
* 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.
*
* 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 core calls a key creation function in the driver.
*
* 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,
Expand All @@ -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.
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
107 changes: 96 additions & 11 deletions library/psa_crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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,
Expand All @@ -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 );
Expand Down Expand Up @@ -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
Expand All @@ -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 );
Expand Down Expand Up @@ -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 )
Expand Down Expand Up @@ -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 */

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -1848,6 +1862,74 @@ psa_status_t psa_import_key( const psa_key_attributes_t *attributes,
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 )
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 )
{
Expand Down Expand Up @@ -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 )
{
Expand Down
6 changes: 4 additions & 2 deletions library/psa_crypto_se.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
{
Expand All @@ -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
Expand All @@ -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 );
Expand Down
1 change: 1 addition & 0 deletions library/psa_crypto_se.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 );

Expand Down
Loading