From a5b894f7e21e116ce3822441bcbbab58b84a0d7a Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Wed, 21 Oct 2020 09:04:34 +0200 Subject: [PATCH] psa: mgmt: Add key slot reuse When looking for an empty key slot to store the description of a key, if all key slots are in use, reuse the first encountered and unaccessed key slot containing the description of a permanent key. Signed-off-by: Ronald Cron --- library/psa_crypto_slot_management.c | 63 ++++-- ...test_suite_psa_crypto_slot_management.data | 20 ++ ..._suite_psa_crypto_slot_management.function | 186 ++++++++++++++++++ 3 files changed, 255 insertions(+), 14 deletions(-) diff --git a/library/psa_crypto_slot_management.c b/library/psa_crypto_slot_management.c index 9271e1451..5a1fc741f 100644 --- a/library/psa_crypto_slot_management.c +++ b/library/psa_crypto_slot_management.c @@ -173,27 +173,62 @@ void psa_wipe_all_key_slots( void ) psa_status_t psa_get_empty_key_slot( psa_key_id_t *volatile_key_id, psa_key_slot_t **p_slot ) { + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; size_t slot_idx; + psa_key_slot_t *selected_slot, *unaccessed_permanent_key_slot; if( ! global_data.key_slots_initialized ) - return( PSA_ERROR_BAD_STATE ); - - for( slot_idx = PSA_KEY_SLOT_COUNT; slot_idx > 0; slot_idx-- ) { - *p_slot = &global_data.key_slots[ slot_idx - 1 ]; - if( ! psa_is_key_slot_occupied( *p_slot ) ) - { - *volatile_key_id = PSA_KEY_ID_VOLATILE_MIN + - ( (psa_key_id_t)slot_idx ) - 1; - - psa_increment_key_slot_access_count( *p_slot ); - - return( PSA_SUCCESS ); - } + status = PSA_ERROR_BAD_STATE; + goto error; } + selected_slot = unaccessed_permanent_key_slot = NULL; + for( slot_idx = 0; slot_idx < PSA_KEY_SLOT_COUNT; slot_idx++ ) + { + psa_key_slot_t *slot = &global_data.key_slots[ slot_idx ]; + if( ! psa_is_key_slot_occupied( slot ) ) + { + selected_slot = slot; + break; + } + + if( ( unaccessed_permanent_key_slot == NULL ) && + ( ! PSA_KEY_LIFETIME_IS_VOLATILE( slot->attr.lifetime ) ) && + ( ! psa_is_key_slot_accessed( slot ) ) ) + unaccessed_permanent_key_slot = slot; + } + + /* + * If there is no unused key slot and there is at least one unaccessed key + * slot containing the description of a permament key, recycle the first + * such key slot we encountered. If we need later on to operate on the + * permanent key we evict now, we will reload its description from storage. + */ + if( ( selected_slot == NULL ) && + ( unaccessed_permanent_key_slot != NULL ) ) + { + selected_slot = unaccessed_permanent_key_slot; + selected_slot->access_count = 1; + psa_wipe_key_slot( selected_slot ); + } + + if( selected_slot != NULL ) + { + *volatile_key_id = PSA_KEY_ID_VOLATILE_MIN + + ( (psa_key_id_t)( selected_slot - global_data.key_slots ) ); + *p_slot = selected_slot; + psa_increment_key_slot_access_count( selected_slot ); + + return( PSA_SUCCESS ); + } + status = PSA_ERROR_INSUFFICIENT_MEMORY; + +error: *p_slot = NULL; - return( PSA_ERROR_INSUFFICIENT_MEMORY ); + *volatile_key_id = 0; + + return( status ); } #if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) diff --git a/tests/suites/test_suite_psa_crypto_slot_management.data b/tests/suites/test_suite_psa_crypto_slot_management.data index 253342559..d2d6c01b9 100644 --- a/tests/suites/test_suite_psa_crypto_slot_management.data +++ b/tests/suites/test_suite_psa_crypto_slot_management.data @@ -186,3 +186,23 @@ invalid_handle:INVALID_HANDLE_HUGE:PSA_ERROR_INVALID_HANDLE:PSA_ERROR_INVALID_HA Open many transient keys many_transient_keys:42 + +# Eviction from a key slot to be able to import a new permanent key. +Key slot eviction to import a new permanent key +key_slot_eviction_to_import_new_key:PSA_KEY_LIFETIME_PERSISTENT + +# Eviction from a key slot to be able to import a new volatile key. +Key slot eviction to import a new volatile key +key_slot_eviction_to_import_new_key:PSA_KEY_LIFETIME_VOLATILE + +# Check that non reusable key slots are not deleted/overwritten in case of key +# slot starvation: +# . An attempt to access a permanent key while all RAM key slots are occupied +# by volatile keys fails and does not lead to volatile key data to be +# spoiled. +# . With all key slot in use with one containing a permanent key, an attempt +# to copy the permanent key fails (the permanent key slot cannot be reclaimed +# as it is accessed by the copy process) without the permament key data and +# volatile key data being spoiled. +Non reusable key slots integrity in case of key slot starvation +non_reusable_key_slots_integrity_in_case_of_key_slot_starvation diff --git a/tests/suites/test_suite_psa_crypto_slot_management.function b/tests/suites/test_suite_psa_crypto_slot_management.function index 66bf0a46e..94bcade12 100644 --- a/tests/suites/test_suite_psa_crypto_slot_management.function +++ b/tests/suites/test_suite_psa_crypto_slot_management.function @@ -877,3 +877,189 @@ exit: } /* END_CASE */ +/* BEGIN_CASE depends_on:MBEDTLS_PSA_CRYPTO_STORAGE_C */ +void key_slot_eviction_to_import_new_key( int lifetime_arg ) +{ + psa_key_lifetime_t lifetime = (psa_key_lifetime_t)lifetime_arg; + size_t i; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + uint8_t exported[sizeof( size_t )]; + size_t exported_length; + mbedtls_svc_key_id_t key, returned_key_id; + + PSA_ASSERT( psa_crypto_init( ) ); + + psa_set_key_usage_flags( &attributes, PSA_KEY_USAGE_EXPORT ); + psa_set_key_algorithm( &attributes, 0 ); + psa_set_key_type( &attributes, PSA_KEY_TYPE_RAW_DATA ); + + /* + * Create PSA_KEY_SLOT_COUNT persistent keys. + */ + for( i = 0; i < PSA_KEY_SLOT_COUNT; i++ ) + { + key = mbedtls_svc_key_id_make( i, i + 1 ); + psa_set_key_id( &attributes, key ); + PSA_ASSERT( psa_import_key( &attributes, + (uint8_t *) &i, sizeof( i ), + &returned_key_id ) ); + TEST_ASSERT( mbedtls_svc_key_id_equal( returned_key_id, key ) ); + } + + /* + * Create a new persistent or volatile key. When creating the key, + * one of the description of the previously created persistent key + * is removed from the RAM key slots. This makes room to store its + * description in RAM. + */ + i = PSA_KEY_SLOT_COUNT; + key = mbedtls_svc_key_id_make( i, i + 1 ); + psa_set_key_id( &attributes, key ); + + if( lifetime == PSA_KEY_LIFETIME_VOLATILE ) + psa_set_key_lifetime( &attributes, PSA_KEY_LIFETIME_VOLATILE ); + + PSA_ASSERT( psa_import_key( &attributes, + (uint8_t *) &i, sizeof( i ), + &returned_key_id ) ); + if( lifetime != PSA_KEY_LIFETIME_VOLATILE ) + TEST_ASSERT( mbedtls_svc_key_id_equal( returned_key_id, key ) ); + + /* + * Check that we can export all ( PSA_KEY_SLOT_COUNT + 1 ) keys, + * that they have the expected value and destroy them. In that process, + * the description of the persistent key that was evicted from the RAM + * slots when creating the last key is restored in a RAM slot to export + * its value. + */ + for( i = 0; i <= PSA_KEY_SLOT_COUNT; i++ ) + { + if( i < PSA_KEY_SLOT_COUNT ) + key = mbedtls_svc_key_id_make( i, i + 1 ); + else + key = returned_key_id; + + PSA_ASSERT( psa_export_key( key, + exported, sizeof( exported ), + &exported_length ) ); + ASSERT_COMPARE( exported, exported_length, + (uint8_t *) &i, sizeof( i ) ); + PSA_ASSERT( psa_destroy_key( key ) ); + } + +exit: + PSA_DONE( ); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_PSA_CRYPTO_STORAGE_C */ +void non_reusable_key_slots_integrity_in_case_of_key_slot_starvation( ) +{ + psa_status_t status; + size_t i; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + uint8_t exported[sizeof( size_t )]; + size_t exported_length; + mbedtls_svc_key_id_t permanent_key = MBEDTLS_SVC_KEY_ID_INIT; + mbedtls_svc_key_id_t permanent_key2 = MBEDTLS_SVC_KEY_ID_INIT; + mbedtls_svc_key_id_t returned_key_id = MBEDTLS_SVC_KEY_ID_INIT; + mbedtls_svc_key_id_t *keys = NULL; + + TEST_ASSERT( PSA_KEY_SLOT_COUNT >= 1 ); + + ASSERT_ALLOC( keys, PSA_KEY_SLOT_COUNT ); + PSA_ASSERT( psa_crypto_init( ) ); + + psa_set_key_usage_flags( &attributes, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_COPY ); + psa_set_key_algorithm( &attributes, 0 ); + psa_set_key_type( &attributes, PSA_KEY_TYPE_RAW_DATA ); + + /* + * Create a permanent key + */ + permanent_key = mbedtls_svc_key_id_make( 0x100, 0x205 ); + psa_set_key_id( &attributes, permanent_key ); + PSA_ASSERT( psa_import_key( &attributes, + (uint8_t *) &permanent_key, + sizeof( permanent_key ), + &returned_key_id ) ); + TEST_ASSERT( mbedtls_svc_key_id_equal( returned_key_id, permanent_key ) ); + + /* + * Create PSA_KEY_SLOT_COUNT volatile keys + */ + psa_set_key_lifetime( &attributes, PSA_KEY_LIFETIME_VOLATILE ); + for( i = 0; i < PSA_KEY_SLOT_COUNT; i++ ) + { + PSA_ASSERT( psa_import_key( &attributes, + (uint8_t *) &i, sizeof( i ), + &keys[i]) ); + } + psa_reset_key_attributes( &attributes ); + + /* + * Check that we cannot access the persistent key as all slots are + * occupied by volatile keys and the implementation needs to load the + * persistent key description in a slot to be able to access it. + */ + status = psa_get_key_attributes( permanent_key, &attributes ); + TEST_EQUAL( status, PSA_ERROR_INSUFFICIENT_MEMORY ); + + /* + * Check we can export the volatile key created last and that it has the + * expected value. Then, destroy it. + */ + PSA_ASSERT( psa_export_key( keys[PSA_KEY_SLOT_COUNT - 1], + exported, sizeof( exported ), + &exported_length ) ); + i = PSA_KEY_SLOT_COUNT - 1; + ASSERT_COMPARE( exported, exported_length, (uint8_t *) &i, sizeof( i ) ); + PSA_ASSERT( psa_destroy_key( keys[PSA_KEY_SLOT_COUNT - 1] ) ); + + /* + * Check that we can now access the persistent key again. + */ + PSA_ASSERT( psa_get_key_attributes( permanent_key, &attributes ) ); + TEST_ASSERT( mbedtls_svc_key_id_equal( attributes.core.id, + permanent_key ) ); + + /* + * Check that we cannot copy the persistent key as all slots are occupied + * by the permanent key and the volatile keys and the slot containing the + * permanent key cannot be reclaimed as it contains the key to copy. + */ + permanent_key2 = mbedtls_svc_key_id_make( 0x100, 0x204 ); + psa_set_key_id( &attributes, permanent_key2 ); + status = psa_copy_key( permanent_key, &attributes, &returned_key_id ); + TEST_EQUAL( status, PSA_ERROR_INSUFFICIENT_MEMORY ); + + /* + * Check we can export the remaining volatile keys and that they have the + * expected values. + */ + for( i = 0; i < ( PSA_KEY_SLOT_COUNT - 1 ); i++ ) + { + PSA_ASSERT( psa_export_key( keys[i], + exported, sizeof( exported ), + &exported_length ) ); + ASSERT_COMPARE( exported, exported_length, + (uint8_t *) &i, sizeof( i ) ); + PSA_ASSERT( psa_destroy_key( keys[i] ) ); + } + + /* + * Check we can export the persistent key and that it have the expected + * value. + */ + + PSA_ASSERT( psa_export_key( permanent_key, exported, sizeof( exported ), + &exported_length ) ); + ASSERT_COMPARE( exported, exported_length, + (uint8_t *) &permanent_key, sizeof( permanent_key ) ); +exit: + psa_destroy_key( permanent_key ); + PSA_DONE( ); + mbedtls_free( keys ); +} +/* END_CASE */