From 5ec7b078ea0f35c16403cbac3896e158919c0a83 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 30 Nov 2018 18:53:28 +0100 Subject: [PATCH] Add tests for the new slot management mechanism Add unit tests for handle allocation and release. --- tests/CMakeLists.txt | 1 + ...test_suite_psa_crypto_slot_management.data | 59 +++ ..._suite_psa_crypto_slot_management.function | 391 ++++++++++++++++++ 3 files changed, 451 insertions(+) create mode 100644 tests/suites/test_suite_psa_crypto_slot_management.data create mode 100644 tests/suites/test_suite_psa_crypto_slot_management.function diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 56ce9338a..21cdfaba2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -116,6 +116,7 @@ add_test_suite(psa_crypto_hash) add_test_suite(psa_crypto_init) add_test_suite(psa_crypto_metadata) add_test_suite(psa_crypto_persistent_key) +add_test_suite(psa_crypto_slot_management) add_test_suite(psa_crypto_storage_file) add_test_suite(shax) add_test_suite(ssl) diff --git a/tests/suites/test_suite_psa_crypto_slot_management.data b/tests/suites/test_suite_psa_crypto_slot_management.data new file mode 100644 index 000000000..133f4c873 --- /dev/null +++ b/tests/suites/test_suite_psa_crypto_slot_management.data @@ -0,0 +1,59 @@ +Transient slot, check after closing +transient_slot_lifecycle:PSA_KEY_TYPE_RAW_DATA:128:0:0:"0123456789abcdef0123456789abcdef":CLOSE_BY_CLOSE + +Transient slot, check after destroying +transient_slot_lifecycle:PSA_KEY_TYPE_RAW_DATA:128:0:0:"0123456789abcdef0123456789abcdef":CLOSE_BY_DESTROY + +Transient slot, check after restart +transient_slot_lifecycle:PSA_KEY_TYPE_RAW_DATA:128:0:0:"0123456789abcdef0123456789abcdef":CLOSE_BY_SHUTDOWN + +Persistent slot, check after closing +persistent_slot_lifecycle:PSA_KEY_LIFETIME_PERSISTENT:1:PSA_KEY_TYPE_RAW_DATA:128:0:0:"0123456789abcdef0123456789abcdef":CLOSE_BY_CLOSE + +Persistent slot, check after destroying +persistent_slot_lifecycle:PSA_KEY_LIFETIME_PERSISTENT:1:PSA_KEY_TYPE_RAW_DATA:128:0:0:"0123456789abcdef0123456789abcdef":CLOSE_BY_DESTROY + +Persistent slot, check after restart +persistent_slot_lifecycle:PSA_KEY_LIFETIME_PERSISTENT:1:PSA_KEY_TYPE_RAW_DATA:128:0:0:"0123456789abcdef0123456789abcdef":CLOSE_BY_SHUTDOWN + +Attempt to overwrite: close before, same type +create_existent:PSA_KEY_LIFETIME_PERSISTENT:1:PSA_KEY_TYPE_RAW_DATA:CLOSE_BEFORE + +Attempt to overwrite: close before, different type +depends_on:MBEDTLS_AES_C +create_existent:PSA_KEY_LIFETIME_PERSISTENT:1:PSA_KEY_TYPE_AES:CLOSE_BEFORE + +Attempt to overwrite: close after, same type +create_existent:PSA_KEY_LIFETIME_PERSISTENT:1:PSA_KEY_TYPE_RAW_DATA:CLOSE_AFTER + +Attempt to overwrite: close after, different type +depends_on:MBEDTLS_AES_C +create_existent:PSA_KEY_LIFETIME_PERSISTENT:1:PSA_KEY_TYPE_AES:CLOSE_AFTER + +Attempt to overwrite: keep open, same type +create_existent:PSA_KEY_LIFETIME_PERSISTENT:1:PSA_KEY_TYPE_RAW_DATA:KEEP_OPEN + +Attempt to overwrite: keep open, different type +depends_on:MBEDTLS_AES_C +create_existent:PSA_KEY_LIFETIME_PERSISTENT:1:PSA_KEY_TYPE_AES:KEEP_OPEN + +Open failure: non-existent identifier +open_fail:PSA_KEY_LIFETIME_PERSISTENT:1:PSA_ERROR_EMPTY_SLOT + +Open failure: volatile lifetime +open_fail:PSA_KEY_LIFETIME_VOLATILE:1:PSA_ERROR_INVALID_ARGUMENT + +Open failure: invalid lifetime +open_fail:0x7fffffff:0:PSA_ERROR_INVALID_ARGUMENT + +Create failure: volatile lifetime +create_fail:PSA_KEY_LIFETIME_VOLATILE:1:PSA_KEY_TYPE_RAW_DATA:8:PSA_ERROR_INVALID_ARGUMENT + +Create failure: invalid lifetime +create_fail:0x7fffffff:0:PSA_KEY_TYPE_RAW_DATA:8:PSA_ERROR_INVALID_ARGUMENT + +Close/destroy invalid handle +invalid_handle: + +Open many transient handles +many_transient_handles:42 diff --git a/tests/suites/test_suite_psa_crypto_slot_management.function b/tests/suites/test_suite_psa_crypto_slot_management.function new file mode 100644 index 000000000..1f1984e3f --- /dev/null +++ b/tests/suites/test_suite_psa_crypto_slot_management.function @@ -0,0 +1,391 @@ +/* BEGIN_HEADER */ +#include + +#if defined(MBEDTLS_PSA_CRYPTO_SPM) +#include "spm/psa_defs.h" +#endif +#include "psa/crypto.h" + +#include "psa_crypto_storage.h" + +#define PSA_ASSERT( expr ) TEST_ASSERT( ( expr ) == PSA_SUCCESS ) + +typedef enum +{ + CLOSE_BY_CLOSE, + CLOSE_BY_DESTROY, + CLOSE_BY_SHUTDOWN, +} close_method_t; + +typedef enum +{ + KEEP_OPEN, + CLOSE_BEFORE, + CLOSE_AFTER, +} reopen_policy_t; + +/* All test functions that create persistent keys must call + * `TEST_MAX_KEY_ID( key_id )` before creating a persistent key with this + * identifier, and must call psa_purge_key_storage() in their cleanup + * code. */ + +/* There is no API to purge all keys. For this test suite, require that + * all key IDs be less than a certain maximum. */ +#define MAX_KEY_ID_FOR_TEST 32 +#define TEST_MAX_KEY_ID( key_id ) \ + TEST_ASSERT( ( key_id ) <= MAX_KEY_ID_FOR_TEST ) +void psa_purge_key_storage( void ) +{ + psa_key_id_t i; + /* The tests may have potentially created key ids from 1 to + * MAX_KEY_ID_FOR_TEST. In addition, run the destroy function on key id + * 0, which file-based storage uses as a temporary file. */ + for( i = 0; i <= MAX_KEY_ID_FOR_TEST; i++ ) + psa_destroy_persistent_key( i ); +} + +static int psa_key_policy_equal( psa_key_policy_t *p1, + psa_key_policy_t *p2 ) +{ + return( psa_key_policy_get_usage( p1 ) == psa_key_policy_get_usage( p2 ) && + psa_key_policy_get_algorithm( p1 ) == psa_key_policy_get_algorithm( p2 ) ); +} + +/* END_HEADER */ + +/* BEGIN_DEPENDENCIES + * depends_on:MBEDTLS_PSA_CRYPTO_C + * END_DEPENDENCIES + */ + +/* BEGIN_CASE */ +void transient_slot_lifecycle( int type_arg, int max_bits_arg, + int alg_arg, int usage_arg, + data_t *key_data, + int close_method_arg ) +{ + psa_key_type_t type = type_arg; + size_t max_bits = max_bits_arg; + psa_algorithm_t alg = alg_arg; + psa_key_usage_t usage_flags = usage_arg; + close_method_t close_method = close_method_arg; + psa_key_type_t read_type; + psa_key_handle_t handle = 0; + psa_key_policy_t policy; + + PSA_ASSERT( psa_crypto_init( ) ); + + /* Get a handle and import a key. */ + PSA_ASSERT( psa_allocate_key( type, max_bits, &handle ) ); + TEST_ASSERT( handle != 0 ); + psa_key_policy_init( &policy ); + psa_key_policy_set_usage( &policy, usage_flags, alg ); + PSA_ASSERT( psa_set_key_policy( handle, &policy ) ); + PSA_ASSERT( psa_import_key( handle, type, key_data->x, key_data->len ) ); + PSA_ASSERT( psa_get_key_information( handle, &read_type, NULL ) ); + TEST_ASSERT( read_type == type ); + + /* Do something that invalidates the handle. */ + switch( close_method ) + { + case CLOSE_BY_CLOSE: + PSA_ASSERT( psa_close_key( handle ) ); + break; + case CLOSE_BY_DESTROY: + PSA_ASSERT( psa_destroy_key( handle ) ); + break; + case CLOSE_BY_SHUTDOWN: + mbedtls_psa_crypto_free( ); + PSA_ASSERT( psa_crypto_init( ) ); + break; + } + /* Test that the handle is now invalid. */ + TEST_ASSERT( psa_get_key_information( handle, &read_type, NULL ) == + PSA_ERROR_INVALID_HANDLE ); + TEST_ASSERT( psa_close_key( handle ) == PSA_ERROR_INVALID_HANDLE ); + +exit: + mbedtls_psa_crypto_free( ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void persistent_slot_lifecycle( int lifetime_arg, int id_arg, + int type_arg, int max_bits_arg, + int alg_arg, int usage_arg, + data_t *key_data, + int close_method_arg ) +{ + psa_key_lifetime_t lifetime = lifetime_arg; + psa_key_id_t id = id_arg; + psa_key_type_t type = type_arg; + size_t max_bits = max_bits_arg; + psa_algorithm_t alg = alg_arg; + psa_key_usage_t usage_flags = usage_arg; + close_method_t close_method = close_method_arg; + psa_key_type_t read_type; + psa_key_handle_t handle = 0; + psa_key_policy_t policy; + + TEST_MAX_KEY_ID( id ); + + PSA_ASSERT( psa_crypto_init( ) ); + + /* Get a handle and import a key. */ + PSA_ASSERT( psa_create_key( lifetime, id, type, max_bits, &handle ) ); + TEST_ASSERT( handle != 0 ); + psa_key_policy_init( &policy ); + psa_key_policy_set_usage( &policy, usage_flags, alg ); + PSA_ASSERT( psa_set_key_policy( handle, &policy ) ); + PSA_ASSERT( psa_import_key( handle, type, key_data->x, key_data->len ) ); + PSA_ASSERT( psa_get_key_information( handle, &read_type, NULL ) ); + TEST_ASSERT( read_type == type ); + + /* Close the key and reopen it. */ + PSA_ASSERT( psa_close_key( handle ) ); + PSA_ASSERT( psa_open_key( lifetime, id, &handle ) ); + PSA_ASSERT( psa_get_key_information( handle, &read_type, NULL ) ); + TEST_ASSERT( read_type == type ); + + /* Do something that invalidates the handle. */ + switch( close_method ) + { + case CLOSE_BY_CLOSE: + PSA_ASSERT( psa_close_key( handle ) ); + break; + case CLOSE_BY_DESTROY: + PSA_ASSERT( psa_destroy_key( handle ) ); + break; + case CLOSE_BY_SHUTDOWN: + mbedtls_psa_crypto_free( ); + PSA_ASSERT( psa_crypto_init( ) ); + break; + } + /* Test that the handle is now invalid. */ + TEST_ASSERT( psa_get_key_information( handle, &read_type, NULL ) == + PSA_ERROR_INVALID_HANDLE ); + TEST_ASSERT( psa_close_key( handle ) == PSA_ERROR_INVALID_HANDLE ); + + /* Try to reopen the key. If we destroyed it, check that it doesn't + * exist, otherwise check that it still exists. */ + switch( close_method ) + { + case CLOSE_BY_CLOSE: + case CLOSE_BY_SHUTDOWN: + PSA_ASSERT( psa_open_key( lifetime, id, &handle ) ); + PSA_ASSERT( psa_get_key_information( handle, &read_type, NULL ) ); + TEST_ASSERT( read_type == type ); + break; + case CLOSE_BY_DESTROY: + TEST_ASSERT( psa_open_key( lifetime, id, &handle ) == + PSA_ERROR_EMPTY_SLOT ); + break; + } + +exit: + mbedtls_psa_crypto_free( ); + psa_purge_key_storage( ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void create_existent( int lifetime_arg, int id_arg, + int new_type_arg, + int reopen_policy_arg ) +{ + psa_key_lifetime_t lifetime = lifetime_arg; + psa_key_id_t id = id_arg; + psa_key_handle_t handle1 = 0, handle2 = 0; + psa_key_policy_t policy1, read_policy; + psa_key_type_t type1 = PSA_KEY_TYPE_RAW_DATA; + psa_key_type_t type2 = new_type_arg; + psa_key_type_t read_type; + const uint8_t material1[16] = "test material #1"; + size_t bits1 = PSA_BYTES_TO_BITS( sizeof( material1 ) ); + size_t read_bits; + uint8_t reexported[sizeof( material1 )]; + size_t reexported_length; + reopen_policy_t reopen_policy = reopen_policy_arg; + + TEST_MAX_KEY_ID( id ); + + PSA_ASSERT( psa_crypto_init( ) ); + + /* Create a key. */ + PSA_ASSERT( psa_create_key( lifetime, id, type1, bits1, &handle1 ) ); + TEST_ASSERT( handle1 != 0 ); + psa_key_policy_init( &policy1 ); + psa_key_policy_set_usage( &policy1, PSA_KEY_USAGE_EXPORT, 0 ); + PSA_ASSERT( psa_set_key_policy( handle1, &policy1 ) ); + PSA_ASSERT( psa_import_key( handle1, type1, + material1, sizeof( material1 ) ) ); + + if( reopen_policy == CLOSE_BEFORE ) + PSA_ASSERT( psa_close_key( handle1 ) ); + + /* Attempt to create a new key in the same slot. */ + TEST_ASSERT( psa_create_key( lifetime, id, type2, bits1, &handle2 ) == + PSA_ERROR_OCCUPIED_SLOT ); + TEST_ASSERT( handle2 == 0 ); + + if( reopen_policy == CLOSE_AFTER ) + PSA_ASSERT( psa_close_key( handle1 ) ); + if( reopen_policy == CLOSE_BEFORE || reopen_policy == CLOSE_AFTER ) + PSA_ASSERT( psa_open_key( lifetime, id, &handle1 ) ); + + /* Check that the original key hasn't changed. */ + PSA_ASSERT( psa_get_key_policy( handle1, &read_policy ) ); + TEST_ASSERT( psa_key_policy_equal( &read_policy, &policy1 ) ); + PSA_ASSERT( psa_get_key_information( handle1, &read_type, &read_bits ) ); + TEST_ASSERT( read_type == type1 ); + TEST_ASSERT( read_bits == bits1 ); + PSA_ASSERT( psa_export_key( handle1, + reexported, sizeof( reexported ), + &reexported_length ) ); + ASSERT_COMPARE( material1, sizeof( material1 ), + reexported, reexported_length ); + +exit: + mbedtls_psa_crypto_free( ); + psa_purge_key_storage( ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void open_fail( int lifetime_arg, int id_arg, + int expected_status_arg ) +{ + psa_key_lifetime_t lifetime = lifetime_arg; + psa_key_id_t id = id_arg; + psa_status_t expected_status = expected_status_arg; + psa_key_handle_t handle = 0xdead; + + PSA_ASSERT( psa_crypto_init( ) ); + + TEST_ASSERT( psa_open_key( lifetime, id, &handle ) == expected_status ); + TEST_ASSERT( handle == 0 ); + +exit: + mbedtls_psa_crypto_free( ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void create_fail( int lifetime_arg, int id_arg, + int type_arg, int max_bits_arg, + int expected_status_arg ) +{ + psa_key_lifetime_t lifetime = lifetime_arg; + psa_key_id_t id = id_arg; + psa_key_type_t type = type_arg; + size_t max_bits = max_bits_arg; + psa_status_t expected_status = expected_status_arg; + psa_key_handle_t handle = 0xdead; + + TEST_MAX_KEY_ID( id ); + + PSA_ASSERT( psa_crypto_init( ) ); + + TEST_ASSERT( psa_create_key( lifetime, id, + type, max_bits, + &handle ) == expected_status ); + TEST_ASSERT( handle == 0 ); + +exit: + mbedtls_psa_crypto_free( ); + psa_purge_key_storage( ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void invalid_handle( ) +{ + psa_key_handle_t handle1 = 0; + psa_key_policy_t policy; + psa_key_type_t read_type; + size_t read_bits; + uint8_t material[1] = "a"; + + PSA_ASSERT( psa_crypto_init( ) ); + + /* Allocate a handle and store a key in it. */ + PSA_ASSERT( psa_allocate_key( PSA_KEY_TYPE_RAW_DATA, 1, &handle1 ) ); + TEST_ASSERT( handle1 != 0 ); + psa_key_policy_init( &policy ); + psa_key_policy_set_usage( &policy, 0, 0 ); + PSA_ASSERT( psa_set_key_policy( handle1, &policy ) ); + PSA_ASSERT( psa_import_key( handle1, PSA_KEY_TYPE_RAW_DATA, + material, sizeof( material ) ) ); + + /* Attempt to close and destroy some invalid handles. */ + TEST_ASSERT( psa_close_key( 0 ) == PSA_ERROR_INVALID_HANDLE ); + TEST_ASSERT( psa_close_key( handle1 - 1 ) == PSA_ERROR_INVALID_HANDLE ); + TEST_ASSERT( psa_close_key( handle1 + 1 ) == PSA_ERROR_INVALID_HANDLE ); + /* At the moment the implementation returns INVALID_ARGUMENT for 0 + * because of the transitional support for non-allocated slot numbers. + * When this is removed, the error will switch to INVALID_HANDLE. */ + TEST_ASSERT( psa_destroy_key( 0 ) == PSA_ERROR_INVALID_ARGUMENT ); + TEST_ASSERT( psa_destroy_key( handle1 - 1 ) == PSA_ERROR_INVALID_HANDLE ); + TEST_ASSERT( psa_destroy_key( handle1 + 1 ) == PSA_ERROR_INVALID_HANDLE ); + + /* After all this, check that the original handle is intact. */ + PSA_ASSERT( psa_get_key_information( handle1, &read_type, &read_bits ) ); + TEST_ASSERT( read_type == PSA_KEY_TYPE_RAW_DATA ); + TEST_ASSERT( read_bits == PSA_BYTES_TO_BITS( sizeof( material ) ) ); + PSA_ASSERT( psa_close_key( handle1 ) ); + +exit: + mbedtls_psa_crypto_free( ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void many_transient_handles( int max_handles_arg ) +{ + psa_key_handle_t *handles = NULL; + size_t max_handles = max_handles_arg; + size_t i, j; + psa_status_t status; + psa_key_policy_t policy; + uint8_t exported[sizeof( size_t )]; + size_t exported_length; + size_t max_bits = PSA_BITS_TO_BYTES( sizeof( exported ) ); + + ASSERT_ALLOC( handles, max_handles ); + PSA_ASSERT( psa_crypto_init( ) ); + psa_key_policy_init( &policy ); + psa_key_policy_set_usage( &policy, PSA_KEY_USAGE_EXPORT, 0 ); + + for( i = 0; i < max_handles; i++ ) + { + status = psa_allocate_key( PSA_KEY_TYPE_RAW_DATA, max_bits, + &handles[i] ); + if( status == PSA_ERROR_INSUFFICIENT_MEMORY ) + break; + TEST_ASSERT( status == PSA_SUCCESS ); + TEST_ASSERT( handles[i] != 0 ); + for( j = 0; j < i; j++ ) + TEST_ASSERT( handles[i] != handles[j] ); + PSA_ASSERT( psa_set_key_policy( handles[i], &policy ) ); + PSA_ASSERT( psa_import_key( handles[i], PSA_KEY_TYPE_RAW_DATA, + (uint8_t *) &i, sizeof( i ) ) ); + } + max_handles = i; + + for( i = 1; i < max_handles; i++ ) + { + PSA_ASSERT( psa_close_key( handles[i - 1] ) ); + PSA_ASSERT( psa_export_key( handles[i], + exported, sizeof( exported ), + &exported_length ) ); + ASSERT_COMPARE( exported, exported_length, + (uint8_t *) &i, sizeof( i ) ); + } + PSA_ASSERT( psa_close_key( handles[i - 1] ) ); + +exit: + mbedtls_psa_crypto_free( ); + mbedtls_free( handles ); +} +/* END_CASE */ +