Merge pull request #285 from gilles-peskine-arm/psa-se_driver-validate_save_persistent

SE driver: make persistent data work
This commit is contained in:
Gilles Peskine 2019-10-09 18:35:33 +02:00 committed by GitHub
commit 36029387de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 244 additions and 49 deletions

View file

@ -186,6 +186,9 @@ static inline void psa_clear_key_slot_number(
* \retval #PSA_ERROR_ALREADY_EXISTS
* There is already a key with the identifier specified in
* \p attributes.
* \retval #PSA_ERROR_NOT_SUPPORTED
* The secure element driver for the specified lifetime does not
* support registering a key.
* \retval #PSA_ERROR_INVALID_ARGUMENT
* \p attributes specifies a lifetime which is not located
* in a secure element.

View file

@ -927,7 +927,14 @@ 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.
*
* This function may update the driver's persistent data through
* \p persistent_data. The core will save the updated persistent data at the
* end of the key creation process. See the description of
* ::psa_drv_se_allocate_key_t for more information.
*
* \param[in,out] drv_context The driver context structure.
* \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[in] key_slot Slot where the key is to be stored.
@ -946,6 +953,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,
void *persistent_data,
const psa_key_attributes_t *attributes,
psa_key_creation_method_t method,
psa_key_slot_number_t key_slot);

View file

@ -1579,7 +1579,7 @@ static psa_status_t psa_start_key_creation(
#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
/* For a key in a secure element, we need to do three things
* when creating a key (but not when registering an existing key):
* when creating or registering a 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
@ -1592,7 +1592,7 @@ 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 && method != PSA_KEY_CREATION_REGISTER )
if( *p_drv != NULL )
{
status = psa_find_se_slot_for_key( attributes, method, *p_drv,
&slot->data.se.slot_number );
@ -1609,6 +1609,12 @@ static psa_status_t psa_start_key_creation(
return( status );
}
}
if( *p_drv == NULL && method == PSA_KEY_CREATION_REGISTER )
{
/* Key registration only makes sense with a secure element. */
return( PSA_ERROR_INVALID_ARGUMENT );
}
#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
return( status );
@ -1883,7 +1889,6 @@ psa_status_t mbedtls_psa_register_se_key(
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.
@ -1900,37 +1905,6 @@ psa_status_t mbedtls_psa_register_se_key(
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:
@ -5713,6 +5687,12 @@ psa_status_t psa_crypto_init( void )
if( status != PSA_SUCCESS )
goto exit;
#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
status = psa_init_all_se_drivers( );
if( status != PSA_SUCCESS )
goto exit;
#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
#if defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS)
status = psa_crypto_load_transaction( );
if( status == PSA_SUCCESS )

View file

@ -222,9 +222,16 @@ psa_status_t psa_find_se_slot_for_key(
if( p_validate_slot_number == NULL )
return( PSA_ERROR_NOT_SUPPORTED );
status = p_validate_slot_number( &driver->context,
driver->internal.persistent_data,
attributes, method,
*slot_number );
}
else if( method == PSA_KEY_CREATION_REGISTER )
{
/* The application didn't specify a slot number. This doesn't
* make sense when registering a slot. */
return( PSA_ERROR_INVALID_ARGUMENT );
}
else
{
/* The application didn't tell us which slot to use. Let the driver
@ -265,6 +272,31 @@ psa_status_t psa_destroy_se_key( psa_se_drv_table_entry_t *driver,
return( status == PSA_SUCCESS ? storage_status : status );
}
psa_status_t psa_init_all_se_drivers( void )
{
size_t i;
for( i = 0; i < PSA_MAX_SE_DRIVERS; i++ )
{
psa_se_drv_table_entry_t *driver = &driver_table[i];
if( driver->lifetime == 0 )
continue; /* skipping unused entry */
const psa_drv_se_t *methods = psa_get_se_driver_methods( driver );
if( methods->p_init != NULL )
{
psa_status_t status = methods->p_init(
&driver->context,
driver->internal.persistent_data,
driver->lifetime );
if( status != PSA_SUCCESS )
return( status );
status = psa_save_se_persistent_data( driver );
if( status != PSA_SUCCESS )
return( status );
}
}
return( PSA_SUCCESS );
}
/****************************************************************/
@ -309,6 +341,8 @@ psa_status_t psa_register_se_driver(
driver_table[i].lifetime = lifetime;
driver_table[i].methods = methods;
driver_table[i].internal.persistent_data_size =
methods->persistent_data_size;
if( methods->persistent_data_size != 0 )
{
@ -326,8 +360,6 @@ psa_status_t psa_register_se_driver(
if( status != PSA_SUCCESS && status != PSA_ERROR_DOES_NOT_EXIST )
goto error;
}
driver_table[i].internal.persistent_data_size =
methods->persistent_data_size;
return( PSA_SUCCESS );

View file

@ -66,6 +66,12 @@
*/
void psa_unregister_all_se_drivers( void );
/** Initialize all secure element drivers.
*
* Called from psa_crypto_init().
*/
psa_status_t psa_init_all_se_drivers( void );
/** A structure that describes a registered secure element driver.
*
* A secure element driver table entry contains a pointer to the

View file

@ -121,23 +121,23 @@ Key generation smoke test: HMAC-SHA-256
generate_key_smoke:PSA_KEY_TYPE_HMAC:256:PSA_ALG_HMAC( PSA_ALG_SHA_256 )
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)
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: rejected
register_key_smoke_test:MIN_DRIVER_LIFETIME:0:PSA_ERROR_NOT_PERMITTED
Key registration: not supported
register_key_smoke_test:MIN_DRIVER_LIFETIME:-1:PSA_ERROR_NOT_SUPPORTED
Import-sign-verify: sign in driver, ECDSA
depends_on:MBEDTLS_ECDSA_C:MBEDTLS_ECP_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED
sign_verify:SIGN_IN_DRIVER_AND_PARALLEL_CREATION:PSA_KEY_TYPE_ECC_KEY_PAIR( PSA_ECC_CURVE_SECP256R1 ):PSA_ALG_ECDSA_ANY:0:"49c9a8c18c4b885638c431cf1df1c994131609b580d4fd43a0cab17db2f13eee":"54686973206973206e6f74206120686173682e"

View file

@ -5,6 +5,13 @@
#include "psa_crypto_se.h"
#include "psa_crypto_storage.h"
/* Invasive peeking: check the persistent data */
#if defined(MBEDTLS_PSA_ITS_FILE_C)
#include "psa_crypto_its.h"
#else /* Native ITS implementation */
#include "psa/error.h"
#include "psa/internal_trusted_storage.h"
#endif
/****************************************************************/
@ -90,11 +97,13 @@ 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,
void *persistent_data,
const psa_key_attributes_t *attributes,
psa_key_creation_method_t method,
psa_key_slot_number_t slot_number )
{
(void) context;
(void) persistent_data;
(void) attributes;
DRIVER_ASSERT_RETURN( slot_number ==
validate_slot_number_directions.slot_number );
@ -104,6 +113,11 @@ static psa_status_t validate_slot_number_as_directed(
}
/* Allocate slot numbers with a monotonic counter. */
static psa_key_slot_number_t shadow_counter;
static void counter_reset( void )
{
shadow_counter = 0;
}
static psa_status_t counter_allocate( psa_drv_se_context_t *context,
void *persistent_data,
const psa_key_attributes_t *attributes,
@ -118,6 +132,7 @@ static psa_status_t counter_allocate( psa_drv_se_context_t *context,
++*p_counter;
if( *p_counter == 0 )
return( PSA_ERROR_INSUFFICIENT_STORAGE );
shadow_counter = *p_counter;
*slot_number = *p_counter;
return( PSA_SUCCESS );
}
@ -193,12 +208,15 @@ static ram_slot_t ram_slots[16];
* bit vector indicating which slots are in use. */
typedef uint16_t ram_slot_usage_t;
static ram_slot_usage_t ram_shadow_slot_usage;
static uint8_t ram_min_slot = 0;
static void ram_slots_reset( void )
{
memset( ram_slots, 0, sizeof( ram_slots ) );
ram_min_slot = 0;
ram_shadow_slot_usage = 0;
}
/* Common parts of key creation.
@ -342,6 +360,7 @@ static psa_status_t ram_destroy( psa_drv_se_context_t *context,
DRIVER_ASSERT_RETURN( slot_number < ARRAY_LENGTH( ram_slots ) );
memset( &ram_slots[slot_number], 0, sizeof( ram_slots[slot_number] ) );
*slot_usage &= ~(ram_slot_usage_t)( 1 << slot_number );
ram_shadow_slot_usage = *slot_usage;
return( PSA_SUCCESS );
}
@ -360,18 +379,23 @@ static psa_status_t ram_allocate( psa_drv_se_context_t *context,
++( *slot_number ) )
{
if( ! ( *slot_usage & 1 << *slot_number ) )
{
ram_shadow_slot_usage = *slot_usage;
return( PSA_SUCCESS );
}
}
return( PSA_ERROR_INSUFFICIENT_STORAGE );
}
static psa_status_t ram_validate_slot_number(
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 )
{
(void) context;
(void) persistent_data;
(void) attributes;
(void) method;
if( slot_number >= ARRAY_LENGTH( ram_slots ) )
@ -522,6 +546,37 @@ exit:
return( ok );
}
/* Get the file UID corresponding to the specified lifetime.
* If this changes, the storage format version must change.
* See psa_get_se_driver_its_file_uid() in psa_crypto_se.c.
*/
psa_storage_uid_t file_uid_for_lifetime( psa_key_lifetime_t lifetime )
{
if( lifetime > PSA_MAX_SE_LIFETIME )
return( 0 );
return( 0xfffffe00 + lifetime );
}
/* Check that the persistent data of a driver has its expected content. */
static int check_persistent_data( psa_key_lifetime_t lifetime,
const void *expected_data,
size_t size )
{
psa_storage_uid_t uid = file_uid_for_lifetime( lifetime );
struct psa_storage_info_t info;
uint8_t *loaded = NULL;
PSA_ASSERT( psa_its_get_info( uid, &info ) );
ASSERT_ALLOC( loaded, info.size );
PSA_ASSERT( psa_its_get( uid, 0, info.size, loaded, NULL ) );
ASSERT_COMPARE( expected_data, size, loaded, info.size );
return( 1 );
exit:
mbedtls_free( loaded );
return( 0 );
}
/* Check that a function's return status is "smoke-free", i.e. that
* it's an acceptable error code when calling an API function that operates
* on a key with potentially bogus parameters. */
@ -776,6 +831,10 @@ void key_creation_import_export( int min_slot, int restart )
PSA_ASSERT( psa_import_key( &attributes,
key_material, sizeof( key_material ),
&handle ) );
if( ! check_persistent_data( lifetime,
&ram_shadow_slot_usage,
sizeof( ram_shadow_slot_usage ) ) )
goto exit;
/* Maybe restart, to check that the information is saved correctly. */
if( restart )
@ -783,6 +842,10 @@ void key_creation_import_export( int min_slot, int restart )
mbedtls_psa_crypto_free( );
PSA_ASSERT( psa_register_se_driver( lifetime, &driver ) );
PSA_ASSERT( psa_crypto_init( ) );
if( ! check_persistent_data( lifetime,
&ram_shadow_slot_usage,
sizeof( ram_shadow_slot_usage ) ) )
goto exit;
PSA_ASSERT( psa_open_key( id, &handle ) );
}
@ -805,6 +868,10 @@ void key_creation_import_export( int min_slot, int restart )
PSA_ASSERT( psa_destroy_key( handle ) );
handle = 0;
if( ! check_persistent_data( lifetime,
&ram_shadow_slot_usage,
sizeof( ram_shadow_slot_usage ) ) )
goto exit;
TEST_EQUAL( psa_open_key( id, &handle ),
PSA_ERROR_DOES_NOT_EXIST );
@ -860,6 +927,10 @@ void key_creation_in_chosen_slot( int slot_arg,
if( status != PSA_SUCCESS )
goto exit;
if( ! check_persistent_data( lifetime,
&ram_shadow_slot_usage,
sizeof( ram_shadow_slot_usage ) ) )
goto exit;
/* Maybe restart, to check that the information is saved correctly. */
if( restart )
@ -867,6 +938,10 @@ void key_creation_in_chosen_slot( int slot_arg,
mbedtls_psa_crypto_free( );
PSA_ASSERT( psa_register_se_driver( lifetime, &driver ) );
PSA_ASSERT( psa_crypto_init( ) );
if( ! check_persistent_data( lifetime,
&ram_shadow_slot_usage,
sizeof( ram_shadow_slot_usage ) ) )
goto exit;
PSA_ASSERT( psa_open_key( id, &handle ) );
}
@ -879,6 +954,10 @@ void key_creation_in_chosen_slot( int slot_arg,
PSA_ASSERT( psa_destroy_key( handle ) );
handle = 0;
if( ! check_persistent_data( lifetime,
&ram_shadow_slot_usage,
sizeof( ram_shadow_slot_usage ) ) )
goto exit;
TEST_EQUAL( psa_open_key( id, &handle ),
PSA_ERROR_DOES_NOT_EXIST );
@ -926,6 +1005,9 @@ void import_key_smoke( int type_arg, int alg_arg,
PSA_ASSERT( psa_import_key( &attributes,
key_material->x, key_material->len,
&handle ) );
if( ! check_persistent_data( lifetime,
&shadow_counter, sizeof( shadow_counter ) ) )
goto exit;
/* Do stuff with the key. */
if( ! smoke_test_key( handle ) )
@ -935,6 +1017,9 @@ void import_key_smoke( int type_arg, int alg_arg,
mbedtls_psa_crypto_free( );
PSA_ASSERT( psa_register_se_driver( lifetime, &driver ) );
PSA_ASSERT( psa_crypto_init( ) );
if( ! check_persistent_data( lifetime,
&shadow_counter, sizeof( shadow_counter ) ) )
goto exit;
PSA_ASSERT( psa_open_key( id, &handle ) );
if( ! smoke_test_key( handle ) )
goto exit;
@ -942,11 +1027,15 @@ void import_key_smoke( int type_arg, int alg_arg,
/* We're done. */
PSA_ASSERT( psa_destroy_key( handle ) );
handle = 0;
if( ! check_persistent_data( lifetime,
&shadow_counter, sizeof( shadow_counter ) ) )
goto exit;
TEST_EQUAL( psa_open_key( id, &handle ),
PSA_ERROR_DOES_NOT_EXIST );
exit:
PSA_DONE( );
counter_reset( );
psa_purge_storage( );
}
/* END_CASE */
@ -983,6 +1072,7 @@ void generate_key_not_supported( int type_arg, int bits_arg )
exit:
PSA_DONE( );
counter_reset( );
psa_purge_storage( );
}
/* END_CASE */
@ -1023,6 +1113,9 @@ void generate_key_smoke( int type_arg, int bits_arg, int alg_arg )
psa_set_key_type( &attributes, type );
psa_set_key_bits( &attributes, bits );
PSA_ASSERT( psa_generate_key( &attributes, &handle ) );
if( ! check_persistent_data( lifetime,
&shadow_counter, sizeof( shadow_counter ) ) )
goto exit;
/* Do stuff with the key. */
if( ! smoke_test_key( handle ) )
@ -1032,6 +1125,9 @@ void generate_key_smoke( int type_arg, int bits_arg, int alg_arg )
mbedtls_psa_crypto_free( );
PSA_ASSERT( psa_register_se_driver( lifetime, &driver ) );
PSA_ASSERT( psa_crypto_init( ) );
if( ! check_persistent_data( lifetime,
&shadow_counter, sizeof( shadow_counter ) ) )
goto exit;
PSA_ASSERT( psa_open_key( id, &handle ) );
if( ! smoke_test_key( handle ) )
goto exit;
@ -1039,11 +1135,15 @@ void generate_key_smoke( int type_arg, int bits_arg, int alg_arg )
/* We're done. */
PSA_ASSERT( psa_destroy_key( handle ) );
handle = 0;
if( ! check_persistent_data( lifetime,
&shadow_counter, sizeof( shadow_counter ) ) )
goto exit;
TEST_EQUAL( psa_open_key( id, &handle ),
PSA_ERROR_DOES_NOT_EXIST );
exit:
PSA_DONE( );
counter_reset( );
psa_purge_storage( );
}
/* END_CASE */

View file

@ -1,3 +1,12 @@
SE init mock test: success
mock_init:2:PSA_SUCCESS:PSA_SUCCESS:PSA_SUCCESS:1
SE init mock test: failure
mock_init:2:PSA_SUCCESS:PSA_ERROR_HARDWARE_FAILURE:PSA_ERROR_HARDWARE_FAILURE:1
SE init mock test: invalid lifetime
mock_init:1:PSA_ERROR_INVALID_ARGUMENT:PSA_ERROR_BAD_STATE:PSA_SUCCESS:0
SE key importing mock test
mock_import:PSA_SUCCESS:PSA_SUCCESS:0:PSA_SUCCESS

View file

@ -5,6 +5,13 @@
#include "psa_crypto_se.h"
#include "psa_crypto_storage.h"
static struct
{
uint16_t called;
psa_key_lifetime_t lifetime;
psa_status_t return_value;
} mock_init_data;
static struct
{
uint16_t called;
@ -92,6 +99,7 @@ static void psa_purge_storage( void )
static void mock_teardown( void )
{
memset( &mock_init_data, 0, sizeof( mock_init_data ) );
memset( &mock_import_data, 0, sizeof( mock_import_data ) );
memset( &mock_export_data, 0, sizeof( mock_export_data ) );
memset( &mock_export_public_data, 0, sizeof( mock_export_public_data ) );
@ -103,6 +111,18 @@ static void mock_teardown( void )
psa_purge_storage( );
}
static psa_status_t mock_init( psa_drv_se_context_t *drv_context,
void *persistent_data,
psa_key_lifetime_t lifetime )
{
(void) drv_context;
(void) persistent_data;
mock_init_data.called++;
mock_init_data.lifetime = lifetime;
return( mock_init_data.return_value );
}
static psa_status_t mock_generate( psa_drv_se_context_t *drv_context,
psa_key_slot_number_t key_slot,
const psa_key_attributes_t *attributes,
@ -258,6 +278,42 @@ psa_status_t mock_destroy( psa_drv_se_context_t *context,
* END_DEPENDENCIES
*/
/* BEGIN_CASE */
void mock_init( int lifetime_arg,
int expected_register_status_arg,
int driver_status_arg,
int expected_psa_status_arg,
int expected_called )
{
psa_key_lifetime_t lifetime = lifetime_arg;
psa_status_t expected_register_status = expected_register_status_arg;
psa_status_t driver_status = driver_status_arg;
psa_status_t expected_psa_status = expected_psa_status_arg;
psa_drv_se_t driver = {
.hal_version = PSA_DRV_SE_HAL_VERSION,
.p_init = mock_init,
};
int psa_crypto_init_called = 0;
mock_init_data.return_value = driver_status;
TEST_EQUAL( psa_register_se_driver( lifetime, &driver ),
expected_register_status );
psa_crypto_init_called = 1;
TEST_EQUAL( psa_crypto_init( ), expected_psa_status );
TEST_EQUAL( mock_init_data.called, expected_called );
if( expected_called )
TEST_EQUAL( mock_init_data.lifetime, lifetime );
exit:
if( psa_crypto_init_called )
PSA_DONE( );
mock_teardown( );
}
/* END_CASE */
/* BEGIN_CASE */
void mock_import( int mock_alloc_return_value,
int mock_import_return_value,
@ -335,6 +391,7 @@ void mock_export( int mock_export_return_value, int expected_result )
memset( &key_management, 0, sizeof( key_management ) );
driver.hal_version = PSA_DRV_SE_HAL_VERSION;
driver.key_management = &key_management;
driver.p_init = mock_init;
key_management.p_import = mock_import;
key_management.p_export = mock_export;
key_management.p_destroy = mock_destroy;