/* BEGIN_HEADER */ #include "psa_crypto_helpers.h" #include "psa/crypto_se_driver.h" #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 /****************************************************************/ /* Test driver helpers */ /****************************************************************/ /** The minimum valid lifetime value for a secure element driver. */ #define MIN_DRIVER_LIFETIME 2 /** The driver detected a condition that shouldn't happen. * This is probably a bug in the library. */ #define PSA_ERROR_DETECTED_BY_DRIVER ((psa_status_t)( -500 )) /** Like #TEST_ASSERT for use in a driver method, with no cleanup. * * If an error happens, this macro returns from the calling function. * * Use this macro to assert on guarantees provided by the core. */ #define DRIVER_ASSERT_RETURN( TEST ) \ do { \ if( ! (TEST) ) \ { \ test_fail( #TEST, __LINE__, __FILE__ ); \ return( PSA_ERROR_DETECTED_BY_DRIVER ); \ } \ } while( 0 ) /** Like #TEST_ASSERT for use in a driver method, with cleanup. * * In case of error, this macro sets `status` and jumps to the * label `exit`. * * Use this macro to assert on guarantees provided by the core. */ #define DRIVER_ASSERT( TEST ) \ do { \ if( ! (TEST) ) \ { \ test_fail( #TEST, __LINE__, __FILE__ ); \ status = PSA_ERROR_DETECTED_BY_DRIVER; \ goto exit; \ } \ } while( 0 ) /** Like #PSA_ASSERT for a PSA API call that calls a driver underneath. * * Run the code \p expr. If this returns \p expected_status, * do nothing. If this returns #PSA_ERROR_DETECTED_BY_DRIVER, * jump directly to the `exit` label. If this returns any other * status, call test_fail() then jump to `exit`. * * The special case for #PSA_ERROR_DETECTED_BY_DRIVER is because in this * case, the test driver code is expected to have called test_fail() * already, so we make sure not to overwrite the failure information. */ #define PSA_ASSERT_VIA_DRIVER( expr, expected_status ) \ do { \ psa_status_t PSA_ASSERT_VIA_DRIVER_status = ( expr ); \ if( PSA_ASSERT_VIA_DRIVER_status == PSA_ERROR_DETECTED_BY_DRIVER ) \ goto exit; \ if( PSA_ASSERT_VIA_DRIVER_status != ( expected_status ) ) \ { \ test_fail( #expr, __LINE__, __FILE__ ); \ goto exit; \ } \ } while( 0 ) /****************************************************************/ /* Miscellaneous driver methods */ /****************************************************************/ typedef struct { psa_key_slot_number_t slot_number; psa_key_creation_method_t method; psa_status_t status; } validate_slot_number_directions_t; 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 ); DRIVER_ASSERT_RETURN( method == validate_slot_number_directions.method ); return( validate_slot_number_directions.status ); } /* 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, psa_key_creation_method_t method, psa_key_slot_number_t *slot_number ) { psa_key_slot_number_t *p_counter = persistent_data; (void) attributes; (void) method; if( context->persistent_data_size != sizeof( psa_key_slot_number_t ) ) return( PSA_ERROR_DETECTED_BY_DRIVER ); ++*p_counter; if( *p_counter == 0 ) return( PSA_ERROR_INSUFFICIENT_STORAGE ); shadow_counter = *p_counter; *slot_number = *p_counter; return( PSA_SUCCESS ); } /* Null import: do nothing, but pretend it worked. */ static psa_status_t null_import( psa_drv_se_context_t *context, psa_key_slot_number_t slot_number, const psa_key_attributes_t *attributes, const uint8_t *data, size_t data_length, size_t *bits ) { (void) context; (void) slot_number; (void) attributes; (void) data; /* We're supposed to return a key size. Return one that's correct for * plain data keys. */ *bits = PSA_BYTES_TO_BITS( data_length ); return( PSA_SUCCESS ); } /* Null generate: do nothing, but pretend it worked. */ static psa_status_t null_generate( psa_drv_se_context_t *context, psa_key_slot_number_t slot_number, const psa_key_attributes_t *attributes, uint8_t *pubkey, size_t pubkey_size, size_t *pubkey_length ) { (void) context; (void) slot_number; (void) attributes; DRIVER_ASSERT_RETURN( *pubkey_length == 0 ); if( ! PSA_KEY_TYPE_IS_KEY_PAIR( psa_get_key_type( attributes ) ) ) { DRIVER_ASSERT_RETURN( pubkey == NULL ); DRIVER_ASSERT_RETURN( pubkey_size == 0 ); } return( PSA_SUCCESS ); } /* Null destroy: do nothing, but pretend it worked. */ static psa_status_t null_destroy( psa_drv_se_context_t *context, void *persistent_data, psa_key_slot_number_t slot_number ) { (void) context; (void) persistent_data; (void) slot_number; return( PSA_SUCCESS ); } /****************************************************************/ /* RAM-based test driver */ /****************************************************************/ #define RAM_MAX_KEY_SIZE 64 typedef struct { psa_key_lifetime_t lifetime; psa_key_type_t type; size_t bits; uint8_t content[RAM_MAX_KEY_SIZE]; } ram_slot_t; static ram_slot_t ram_slots[16]; /* A type with at least ARRAY_LENGTH(ram_slots) bits, containing a * 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. * * In case of error, zero out ram_slots[slot_number]. But don't * do that if the error is PSA_ERROR_DETECTED_BY_DRIVER: in this case * you don't need to clean up (ram_slot_reset() will take care of it * in the test case function's cleanup code) and it might be wrong * (if slot_number is invalid). */ static psa_status_t ram_create_common( psa_drv_se_context_t *context, psa_key_slot_number_t slot_number, const psa_key_attributes_t *attributes, size_t required_storage ) { (void) context; DRIVER_ASSERT_RETURN( slot_number < ARRAY_LENGTH( ram_slots ) ); ram_slots[slot_number].lifetime = psa_get_key_lifetime( attributes ); ram_slots[slot_number].type = psa_get_key_type( attributes ); ram_slots[slot_number].bits = psa_get_key_bits( attributes ); if( required_storage > sizeof( ram_slots[slot_number].content ) ) { memset( &ram_slots[slot_number], 0, sizeof( ram_slots[slot_number] ) ); return( PSA_ERROR_INSUFFICIENT_STORAGE ); } return( PSA_SUCCESS ); } /* This function does everything except actually generating key material. * After calling it, you must copy the desired key material to * ram_slots[slot_number].content. */ static psa_status_t ram_fake_generate( psa_drv_se_context_t *context, psa_key_slot_number_t slot_number, const psa_key_attributes_t *attributes, uint8_t *pubkey, size_t pubkey_size, size_t *pubkey_length ) { psa_status_t status; size_t required_storage = PSA_KEY_EXPORT_MAX_SIZE( psa_get_key_type( attributes ), psa_get_key_bits( attributes ) ); DRIVER_ASSERT_RETURN( *pubkey_length == 0 ); if( ! PSA_KEY_TYPE_IS_KEY_PAIR( psa_get_key_type( attributes ) ) ) { DRIVER_ASSERT_RETURN( pubkey == NULL ); DRIVER_ASSERT_RETURN( pubkey_size == 0 ); } status = ram_create_common( context, slot_number, attributes, required_storage ); return( status ); } static psa_status_t ram_import( psa_drv_se_context_t *context, psa_key_slot_number_t slot_number, const psa_key_attributes_t *attributes, const uint8_t *data, size_t data_length, size_t *bits ) { psa_key_type_t type = psa_get_key_type( attributes ); psa_status_t status = ram_create_common( context, slot_number, attributes, data_length ); if( status != PSA_SUCCESS ) return( status ); /* The RAM driver only works for certain key types: raw keys, * and ECC key pairs. This is true in particular of the bit-size * calculation here. */ if( PSA_KEY_TYPE_IS_UNSTRUCTURED( type ) ) *bits = PSA_BYTES_TO_BITS( data_length ); else if ( PSA_KEY_TYPE_IS_ECC_KEY_PAIR( type ) ) *bits = PSA_ECC_CURVE_BITS( PSA_KEY_TYPE_GET_CURVE( type ) ); else { memset( &ram_slots[slot_number], 0, sizeof( ram_slots[slot_number] ) ); return( PSA_ERROR_NOT_SUPPORTED ); } ram_slots[slot_number].bits = *bits; memcpy( ram_slots[slot_number].content, data, data_length ); return( PSA_SUCCESS ); } static psa_status_t ram_export( psa_drv_se_context_t *context, psa_key_slot_number_t slot_number, uint8_t *data, size_t data_size, size_t *data_length ) { size_t actual_size; (void) context; DRIVER_ASSERT_RETURN( slot_number < ARRAY_LENGTH( ram_slots ) ); actual_size = PSA_BITS_TO_BYTES( ram_slots[slot_number].bits ); if( actual_size > data_size ) return( PSA_ERROR_BUFFER_TOO_SMALL ); *data_length = actual_size; memcpy( data, ram_slots[slot_number].content, actual_size ); return( PSA_SUCCESS ); } static psa_status_t ram_export_public( psa_drv_se_context_t *context, psa_key_slot_number_t slot_number, uint8_t *data, size_t data_size, size_t *data_length ) { psa_status_t status; psa_key_handle_t handle; psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; (void) context; DRIVER_ASSERT_RETURN( slot_number < ARRAY_LENGTH( ram_slots ) ); DRIVER_ASSERT_RETURN( PSA_KEY_TYPE_IS_KEY_PAIR( ram_slots[slot_number].type ) ); psa_set_key_type( &attributes, ram_slots[slot_number].type ); status = psa_import_key( &attributes, ram_slots[slot_number].content, PSA_BITS_TO_BYTES( ram_slots[slot_number].bits ), &handle ); if( status != PSA_SUCCESS ) return( status ); status = psa_export_public_key( handle, data, data_size, data_length ); psa_destroy_key( handle ); return( PSA_SUCCESS ); } static psa_status_t ram_destroy( psa_drv_se_context_t *context, void *persistent_data, psa_key_slot_number_t slot_number ) { ram_slot_usage_t *slot_usage = persistent_data; DRIVER_ASSERT_RETURN( context->persistent_data_size == sizeof( ram_slot_usage_t ) ); 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 ); } static psa_status_t ram_allocate( 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 ) { ram_slot_usage_t *slot_usage = persistent_data; (void) attributes; (void) method; DRIVER_ASSERT_RETURN( context->persistent_data_size == sizeof( ram_slot_usage_t ) ); for( *slot_number = ram_min_slot; *slot_number < ARRAY_LENGTH( ram_slots ); ++( *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 ) ) return( PSA_ERROR_INVALID_ARGUMENT ); return( PSA_SUCCESS ); } static psa_status_t ram_sign( psa_drv_se_context_t *context, psa_key_slot_number_t slot_number, psa_algorithm_t alg, const uint8_t *hash, size_t hash_length, uint8_t *signature, size_t signature_size, size_t *signature_length ) { ram_slot_t *slot; psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; psa_key_handle_t handle = 0; psa_status_t status = PSA_ERROR_GENERIC_ERROR; (void) context; DRIVER_ASSERT_RETURN( slot_number < ARRAY_LENGTH( ram_slots ) ); slot = &ram_slots[slot_number]; psa_set_key_usage_flags( &attributes, PSA_KEY_USAGE_SIGN ); psa_set_key_algorithm( &attributes, alg ); psa_set_key_type( &attributes, slot->type ); DRIVER_ASSERT( psa_import_key( &attributes, slot->content, PSA_BITS_TO_BYTES( slot->bits ), &handle ) == PSA_SUCCESS ); status = psa_asymmetric_sign( handle, alg, hash, hash_length, signature, signature_size, signature_length ); exit: psa_destroy_key( handle ); return( status ); } static psa_status_t ram_verify( psa_drv_se_context_t *context, psa_key_slot_number_t slot_number, psa_algorithm_t alg, const uint8_t *hash, size_t hash_length, const uint8_t *signature, size_t signature_length ) { ram_slot_t *slot; psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; psa_key_handle_t handle = 0; psa_status_t status = PSA_ERROR_GENERIC_ERROR; (void) context; DRIVER_ASSERT_RETURN( slot_number < ARRAY_LENGTH( ram_slots ) ); slot = &ram_slots[slot_number]; psa_set_key_usage_flags( &attributes, PSA_KEY_USAGE_VERIFY ); psa_set_key_algorithm( &attributes, alg ); psa_set_key_type( &attributes, slot->type ); DRIVER_ASSERT( psa_import_key( &attributes, slot->content, PSA_BITS_TO_BYTES( slot->bits ), &handle ) == PSA_SUCCESS ); status = psa_asymmetric_verify( handle, alg, hash, hash_length, signature, signature_length ); exit: psa_destroy_key( handle ); return( status ); } /****************************************************************/ /* Other test helper functions */ /****************************************************************/ typedef enum { SIGN_IN_SOFTWARE_AND_PARALLEL_CREATION, SIGN_IN_DRIVER_AND_PARALLEL_CREATION, SIGN_IN_DRIVER_THEN_EXPORT_PUBLIC, } sign_verify_method_t; /* Check that the attributes of a key reported by psa_get_key_attributes() * are consistent with the attributes used when creating the key. */ static int check_key_attributes( psa_key_handle_t handle, const psa_key_attributes_t *reference_attributes ) { int ok = 0; psa_key_attributes_t actual_attributes = PSA_KEY_ATTRIBUTES_INIT; PSA_ASSERT( psa_get_key_attributes( handle, &actual_attributes ) ); TEST_EQUAL( psa_get_key_id( &actual_attributes ), psa_get_key_id( reference_attributes ) ); TEST_EQUAL( psa_get_key_lifetime( &actual_attributes ), psa_get_key_lifetime( reference_attributes ) ); TEST_EQUAL( psa_get_key_type( &actual_attributes ), psa_get_key_type( reference_attributes ) ); TEST_EQUAL( psa_get_key_usage_flags( &actual_attributes ), psa_get_key_usage_flags( reference_attributes ) ); TEST_EQUAL( psa_get_key_algorithm( &actual_attributes ), psa_get_key_algorithm( reference_attributes ) ); TEST_EQUAL( psa_get_key_enrollment_algorithm( &actual_attributes ), psa_get_key_enrollment_algorithm( reference_attributes ) ); if( psa_get_key_bits( reference_attributes ) != 0 ) { TEST_EQUAL( psa_get_key_bits( &actual_attributes ), psa_get_key_bits( reference_attributes ) ); } { psa_key_slot_number_t actual_slot_number = 0xdeadbeef; psa_key_slot_number_t desired_slot_number = 0xb90cc011; psa_key_lifetime_t lifetime = psa_get_key_lifetime( &actual_attributes ); psa_status_t status = psa_get_key_slot_number( &actual_attributes, &actual_slot_number ); if( lifetime < MIN_DRIVER_LIFETIME ) { /* The key is not in a secure element. */ TEST_EQUAL( status, PSA_ERROR_INVALID_ARGUMENT ); } else { /* The key is in a secure element. If it had been created * in a specific slot, check that it is reported there. */ PSA_ASSERT( status ); status = psa_get_key_slot_number( reference_attributes, &desired_slot_number ); if( status == PSA_SUCCESS ) { TEST_EQUAL( desired_slot_number, actual_slot_number ); } } } ok = 1; 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. */ static int is_status_smoke_free( psa_status_t status ) { switch( status ) { case PSA_SUCCESS: case PSA_ERROR_NOT_SUPPORTED: case PSA_ERROR_NOT_PERMITTED: case PSA_ERROR_BUFFER_TOO_SMALL: case PSA_ERROR_INVALID_ARGUMENT: case PSA_ERROR_INVALID_SIGNATURE: case PSA_ERROR_INVALID_PADDING: return( 1 ); default: return( 0 ); } } #define SMOKE_ASSERT( expr ) \ TEST_ASSERT( is_status_smoke_free( expr ) ) /* Smoke test a key. There are mostly no wrong answers here since we pass * mostly bogus parameters: the goal is to ensure that there is no memory * corruption or crash. This test function is most useful when run under * an environment with sanity checks such as ASan or MSan. */ static int smoke_test_key( psa_key_handle_t handle ) { int ok = 0; psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; psa_mac_operation_t mac_operation = PSA_MAC_OPERATION_INIT; psa_cipher_operation_t cipher_operation = PSA_CIPHER_OPERATION_INIT; psa_key_derivation_operation_t derivation_operation = PSA_KEY_DERIVATION_OPERATION_INIT; uint8_t buffer[80]; /* large enough for a public key for ECDH */ size_t length; psa_key_handle_t handle2 = 0; SMOKE_ASSERT( psa_get_key_attributes( handle, &attributes ) ); SMOKE_ASSERT( psa_export_key( handle, buffer, sizeof( buffer ), &length ) ); SMOKE_ASSERT( psa_export_public_key( handle, buffer, sizeof( buffer ), &length ) ); SMOKE_ASSERT( psa_copy_key( handle, &attributes, &handle2 ) ); if( handle2 != 0 ) PSA_ASSERT( psa_close_key( handle2 ) ); SMOKE_ASSERT( psa_mac_sign_setup( &mac_operation, handle, PSA_ALG_CMAC ) ); PSA_ASSERT( psa_mac_abort( &mac_operation ) ); SMOKE_ASSERT( psa_mac_verify_setup( &mac_operation, handle, PSA_ALG_HMAC( PSA_ALG_SHA_256 ) ) ); PSA_ASSERT( psa_mac_abort( &mac_operation ) ); SMOKE_ASSERT( psa_cipher_encrypt_setup( &cipher_operation, handle, PSA_ALG_CTR ) ); PSA_ASSERT( psa_cipher_abort( &cipher_operation ) ); SMOKE_ASSERT( psa_cipher_decrypt_setup( &cipher_operation, handle, PSA_ALG_CTR ) ); PSA_ASSERT( psa_cipher_abort( &cipher_operation ) ); SMOKE_ASSERT( psa_aead_encrypt( handle, PSA_ALG_CCM, buffer, sizeof( buffer ), NULL, 0, buffer, sizeof( buffer), buffer, sizeof( buffer), &length ) ); SMOKE_ASSERT( psa_aead_decrypt( handle, PSA_ALG_CCM, buffer, sizeof( buffer ), NULL, 0, buffer, sizeof( buffer), buffer, sizeof( buffer), &length ) ); SMOKE_ASSERT( psa_asymmetric_sign( handle, PSA_ALG_ECDSA_ANY, buffer, 32, buffer, sizeof( buffer ), &length ) ); SMOKE_ASSERT( psa_asymmetric_verify( handle, PSA_ALG_ECDSA_ANY, buffer, 32, buffer, sizeof( buffer ) ) ); SMOKE_ASSERT( psa_asymmetric_encrypt( handle, PSA_ALG_RSA_PKCS1V15_CRYPT, buffer, 10, NULL, 0, buffer, sizeof( buffer ), &length ) ); SMOKE_ASSERT( psa_asymmetric_decrypt( handle, PSA_ALG_RSA_PKCS1V15_CRYPT, buffer, sizeof( buffer ), NULL, 0, buffer, sizeof( buffer ), &length ) ); #if defined(MBEDTLS_SHA256_C) /* Try the key in a plain key derivation. */ PSA_ASSERT( psa_key_derivation_setup( &derivation_operation, PSA_ALG_HKDF( PSA_ALG_SHA_256 ) ) ); PSA_ASSERT( psa_key_derivation_input_bytes( &derivation_operation, PSA_KEY_DERIVATION_INPUT_SALT, NULL, 0 ) ); SMOKE_ASSERT( psa_key_derivation_input_key( &derivation_operation, PSA_KEY_DERIVATION_INPUT_SECRET, handle ) ); PSA_ASSERT( psa_key_derivation_abort( &derivation_operation ) ); /* If the key is asymmetric, try it in a key agreement, both as * part of a derivation operation and standalone. */ if( psa_export_public_key( handle, buffer, sizeof( buffer ), &length ) == PSA_SUCCESS ) { psa_algorithm_t alg = PSA_ALG_KEY_AGREEMENT( PSA_ALG_ECDH, PSA_ALG_HKDF( PSA_ALG_SHA_256 ) ); PSA_ASSERT( psa_key_derivation_setup( &derivation_operation, alg ) ); PSA_ASSERT( psa_key_derivation_input_bytes( &derivation_operation, PSA_KEY_DERIVATION_INPUT_SALT, NULL, 0 ) ); SMOKE_ASSERT( psa_key_derivation_key_agreement( &derivation_operation, PSA_KEY_DERIVATION_INPUT_SECRET, handle, buffer, length ) ); PSA_ASSERT( psa_key_derivation_abort( &derivation_operation ) ); SMOKE_ASSERT( psa_raw_key_agreement( alg, handle, buffer, length, buffer, sizeof( buffer ), &length ) ); } #endif /* MBEDTLS_SHA256_C */ ok = 1; exit: psa_reset_key_attributes( &attributes ); return( ok ); } #define MAX_KEY_ID_FOR_TEST 10 static void psa_purge_storage( void ) { psa_key_id_t id; psa_key_lifetime_t lifetime; /* 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( id = 0; id <= MAX_KEY_ID_FOR_TEST; id++ ) psa_destroy_persistent_key( id ); /* Purge the transaction file. */ psa_crypto_stop_transaction( ); /* Purge driver persistent data. */ for( lifetime = 0; lifetime < PSA_MAX_SE_LIFETIME; lifetime++ ) psa_destroy_se_persistent_data( lifetime ); } /* END_HEADER */ /* BEGIN_DEPENDENCIES * depends_on:MBEDTLS_PSA_CRYPTO_SE_C * END_DEPENDENCIES */ /* BEGIN_CASE */ void register_one( int lifetime, int version, int expected_status_arg ) { psa_status_t expected_status = expected_status_arg; psa_drv_se_t driver; memset( &driver, 0, sizeof( driver ) ); driver.hal_version = version; TEST_EQUAL( psa_register_se_driver( lifetime, &driver ), expected_status ); PSA_ASSERT( psa_crypto_init( ) ); exit: PSA_DONE( ); } /* END_CASE */ /* BEGIN_CASE */ void register_twice( int count ) { psa_drv_se_t driver; psa_key_lifetime_t lifetime; psa_key_lifetime_t max = MIN_DRIVER_LIFETIME + count; memset( &driver, 0, sizeof( driver ) ); driver.hal_version = PSA_DRV_SE_HAL_VERSION; for( lifetime = MIN_DRIVER_LIFETIME; lifetime < max; lifetime++ ) PSA_ASSERT( psa_register_se_driver( lifetime, &driver ) ); for( lifetime = MIN_DRIVER_LIFETIME; lifetime < max; lifetime++ ) TEST_EQUAL( psa_register_se_driver( lifetime, &driver ), PSA_ERROR_ALREADY_EXISTS ); PSA_ASSERT( psa_crypto_init( ) ); exit: PSA_DONE( ); } /* END_CASE */ /* BEGIN_CASE */ void register_max( ) { psa_drv_se_t driver; psa_key_lifetime_t lifetime; psa_key_lifetime_t max = MIN_DRIVER_LIFETIME + PSA_MAX_SE_DRIVERS; memset( &driver, 0, sizeof( driver ) ); driver.hal_version = PSA_DRV_SE_HAL_VERSION; for( lifetime = MIN_DRIVER_LIFETIME; lifetime < max; lifetime++ ) PSA_ASSERT( psa_register_se_driver( lifetime, &driver ) ); TEST_EQUAL( psa_register_se_driver( lifetime, &driver ), PSA_ERROR_INSUFFICIENT_MEMORY ); PSA_ASSERT( psa_crypto_init( ) ); exit: PSA_DONE( ); } /* END_CASE */ /* BEGIN_CASE */ void key_creation_import_export( int min_slot, int restart ) { 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}; uint8_t exported[sizeof( key_material )]; size_t exported_length; 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_allocate = ram_allocate; key_management.p_import = ram_import; key_management.p_destroy = ram_destroy; key_management.p_export = ram_export; ram_min_slot = min_slot; 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_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 ) { 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 ) ); } /* Test that the key was created in the expected slot. */ TEST_ASSERT( ram_slots[min_slot].type == PSA_KEY_TYPE_RAW_DATA ); /* Test the key attributes, including the reported slot number. */ psa_set_key_bits( &attributes, PSA_BYTES_TO_BITS( sizeof( key_material ) ) ); psa_set_key_slot_number( &attributes, min_slot ); if( ! check_key_attributes( handle, &attributes ) ) goto exit; /* Test the key data. */ PSA_ASSERT( psa_export_key( handle, exported, sizeof( exported ), &exported_length ) ); ASSERT_COMPARE( key_material, sizeof( key_material ), exported, exported_length ); 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 ); /* Test that the key has been erased from the designated slot. */ TEST_ASSERT( ram_slots[min_slot].type == 0 ); exit: PSA_DONE( ); ram_slots_reset( ); psa_purge_storage( ); } /* 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; 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 ) { 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 ) ); } /* 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 ) ); 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 ); exit: PSA_DONE( ); ram_slots_reset( ); psa_purge_storage( ); } /* END_CASE */ /* BEGIN_CASE */ void import_key_smoke( int type_arg, int alg_arg, data_t *key_material ) { psa_key_type_t type = type_arg; psa_algorithm_t alg = alg_arg; 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; 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( psa_key_slot_number_t ); key_management.p_allocate = counter_allocate; key_management.p_import = null_import; key_management.p_destroy = null_destroy; 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_SIGN | PSA_KEY_USAGE_VERIFY | PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT | PSA_KEY_USAGE_EXPORT ); psa_set_key_algorithm( &attributes, alg ); psa_set_key_type( &attributes, type ); 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 ) ) goto exit; /* Restart and try again. */ 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; /* 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 */ /* BEGIN_CASE */ void generate_key_not_supported( int type_arg, int bits_arg ) { psa_key_type_t type = type_arg; size_t bits = bits_arg; 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; 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( psa_key_slot_number_t ); key_management.p_allocate = counter_allocate; /* No p_generate method */ PSA_ASSERT( psa_register_se_driver( lifetime, &driver ) ); PSA_ASSERT( psa_crypto_init( ) ); psa_set_key_id( &attributes, id ); psa_set_key_lifetime( &attributes, lifetime ); psa_set_key_type( &attributes, type ); psa_set_key_bits( &attributes, bits ); TEST_EQUAL( psa_generate_key( &attributes, &handle ), PSA_ERROR_NOT_SUPPORTED ); exit: PSA_DONE( ); counter_reset( ); psa_purge_storage( ); } /* END_CASE */ /* BEGIN_CASE */ void generate_key_smoke( int type_arg, int bits_arg, int alg_arg ) { psa_key_type_t type = type_arg; psa_key_bits_t bits = bits_arg; psa_algorithm_t alg = alg_arg; 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; 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( psa_key_slot_number_t ); key_management.p_allocate = counter_allocate; key_management.p_generate = null_generate; key_management.p_destroy = null_destroy; 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_SIGN | PSA_KEY_USAGE_VERIFY | PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT | PSA_KEY_USAGE_EXPORT ); psa_set_key_algorithm( &attributes, alg ); 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 ) ) goto exit; /* Restart and try again. */ 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; /* 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 */ /* BEGIN_CASE */ void sign_verify( int flow, int type_arg, int alg_arg, int bits_arg, data_t *key_material, data_t *input ) { psa_key_type_t type = type_arg; psa_algorithm_t alg = alg_arg; size_t bits = bits_arg; /* Pass bits=0 to import, bits>0 to fake-generate */ int generating = ( bits != 0 ); psa_drv_se_t driver; psa_drv_se_key_management_t key_management; psa_drv_se_asymmetric_t asymmetric; psa_key_lifetime_t lifetime = 2; psa_key_id_t id = 1; psa_key_handle_t drv_handle = 0; /* key managed by the driver */ psa_key_handle_t sw_handle = 0; /* transparent key */ psa_key_attributes_t sw_attributes = PSA_KEY_ATTRIBUTES_INIT; psa_key_attributes_t drv_attributes; uint8_t signature[PSA_ASYMMETRIC_SIGNATURE_MAX_SIZE]; size_t signature_length; memset( &driver, 0, sizeof( driver ) ); memset( &key_management, 0, sizeof( key_management ) ); memset( &asymmetric, 0, sizeof( asymmetric ) ); driver.hal_version = PSA_DRV_SE_HAL_VERSION; driver.key_management = &key_management; driver.asymmetric = &asymmetric; driver.persistent_data_size = sizeof( ram_slot_usage_t ); key_management.p_allocate = ram_allocate; key_management.p_destroy = ram_destroy; if( generating ) key_management.p_generate = ram_fake_generate; else key_management.p_import = ram_import; switch( flow ) { case SIGN_IN_SOFTWARE_AND_PARALLEL_CREATION: break; case SIGN_IN_DRIVER_AND_PARALLEL_CREATION: asymmetric.p_sign = ram_sign; break; case SIGN_IN_DRIVER_THEN_EXPORT_PUBLIC: asymmetric.p_sign = ram_sign; key_management.p_export_public = ram_export_public; break; default: TEST_ASSERT( ! "unsupported flow (should be SIGN_IN_xxx)" ); break; } asymmetric.p_verify = ram_verify; PSA_ASSERT( psa_register_se_driver( lifetime, &driver ) ); PSA_ASSERT( psa_crypto_init( ) ); /* Prepare to create two keys with the same key material: a transparent * key, and one that goes through the driver. */ psa_set_key_usage_flags( &sw_attributes, PSA_KEY_USAGE_SIGN | PSA_KEY_USAGE_VERIFY ); psa_set_key_algorithm( &sw_attributes, alg ); psa_set_key_type( &sw_attributes, type ); drv_attributes = sw_attributes; psa_set_key_id( &drv_attributes, id ); psa_set_key_lifetime( &drv_attributes, lifetime ); /* Create the key in the driver. */ if( generating ) { psa_set_key_bits( &drv_attributes, bits ); PSA_ASSERT( psa_generate_key( &drv_attributes, &drv_handle ) ); /* Since we called a generate method that does not actually * generate material, store the desired result of generation in * the mock secure element storage. */ PSA_ASSERT( psa_get_key_attributes( drv_handle, &drv_attributes ) ); TEST_ASSERT( key_material->len == PSA_BITS_TO_BYTES( bits ) ); memcpy( ram_slots[ram_min_slot].content, key_material->x, key_material->len ); } else { PSA_ASSERT( psa_import_key( &drv_attributes, key_material->x, key_material->len, &drv_handle ) ); } /* Either import the same key in software, or export the driver's * public key and import that. */ switch( flow ) { case SIGN_IN_SOFTWARE_AND_PARALLEL_CREATION: case SIGN_IN_DRIVER_AND_PARALLEL_CREATION: PSA_ASSERT( psa_import_key( &sw_attributes, key_material->x, key_material->len, &sw_handle ) ); break; case SIGN_IN_DRIVER_THEN_EXPORT_PUBLIC: { uint8_t public_key[PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE( PSA_VENDOR_ECC_MAX_CURVE_BITS )]; size_t public_key_length; PSA_ASSERT( psa_export_public_key( drv_handle, public_key, sizeof( public_key ), &public_key_length ) ); psa_set_key_type( &sw_attributes, PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR( type ) ); PSA_ASSERT( psa_import_key( &sw_attributes, public_key, public_key_length, &sw_handle ) ); break; } } /* Sign with the chosen key. */ switch( flow ) { case SIGN_IN_DRIVER_AND_PARALLEL_CREATION: case SIGN_IN_DRIVER_THEN_EXPORT_PUBLIC: PSA_ASSERT_VIA_DRIVER( psa_asymmetric_sign( drv_handle, alg, input->x, input->len, signature, sizeof( signature ), &signature_length ), PSA_SUCCESS ); break; case SIGN_IN_SOFTWARE_AND_PARALLEL_CREATION: PSA_ASSERT( psa_asymmetric_sign( sw_handle, alg, input->x, input->len, signature, sizeof( signature ), &signature_length ) ); break; } /* Verify with both keys. */ PSA_ASSERT( psa_asymmetric_verify( sw_handle, alg, input->x, input->len, signature, signature_length ) ); PSA_ASSERT_VIA_DRIVER( psa_asymmetric_verify( drv_handle, alg, input->x, input->len, signature, signature_length ), PSA_SUCCESS ); /* Change the signature and verify again. */ signature[0] ^= 1; TEST_EQUAL( psa_asymmetric_verify( sw_handle, alg, input->x, input->len, signature, signature_length ), PSA_ERROR_INVALID_SIGNATURE ); PSA_ASSERT_VIA_DRIVER( psa_asymmetric_verify( drv_handle, alg, input->x, input->len, signature, signature_length ), PSA_ERROR_INVALID_SIGNATURE ); exit: psa_destroy_key( drv_handle ); psa_destroy_key( sw_handle ); PSA_DONE( ); ram_slots_reset( ); psa_purge_storage( ); } /* END_CASE */ /* BEGIN_CASE */ void register_key_smoke_test( int lifetime_arg, int validate, int expected_status_arg ) { psa_key_lifetime_t lifetime = lifetime_arg; psa_status_t expected_status = expected_status_arg; psa_drv_se_t driver; psa_drv_se_key_management_t key_management; psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; psa_key_id_t id = 1; size_t bit_size = 48; psa_key_slot_number_t wanted_slot = 0x123456789; psa_key_handle_t handle = 0; psa_status_t status; memset( &driver, 0, sizeof( driver ) ); driver.hal_version = PSA_DRV_SE_HAL_VERSION; memset( &key_management, 0, sizeof( key_management ) ); driver.key_management = &key_management; key_management.p_destroy = null_destroy; if( validate >= 0 ) { key_management.p_validate_slot_number = validate_slot_number_as_directed; validate_slot_number_directions.slot_number = wanted_slot; validate_slot_number_directions.method = PSA_KEY_CREATION_REGISTER; validate_slot_number_directions.status = ( validate > 0 ? PSA_SUCCESS : PSA_ERROR_NOT_PERMITTED ); } PSA_ASSERT( psa_register_se_driver( MIN_DRIVER_LIFETIME, &driver ) ); PSA_ASSERT( psa_crypto_init( ) ); 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_bits( &attributes, bit_size ); psa_set_key_slot_number( &attributes, wanted_slot ); status = mbedtls_psa_register_se_key( &attributes ); TEST_EQUAL( status, expected_status ); if( status != PSA_SUCCESS ) goto exit; /* Test that the key exists and has the expected attributes. */ PSA_ASSERT( psa_open_key( id, &handle ) ); if( ! check_key_attributes( handle, &attributes ) ) goto exit; PSA_ASSERT( psa_close_key( handle ) ); /* Restart and try again. */ PSA_DONE( ); PSA_ASSERT( psa_register_se_driver( lifetime, &driver ) ); PSA_ASSERT( psa_crypto_init( ) ); PSA_ASSERT( psa_open_key( id, &handle ) ); if( ! check_key_attributes( handle, &attributes ) ) goto exit; /* This time, destroy the key. */ PSA_ASSERT( psa_destroy_key( handle ) ); handle = 0; TEST_EQUAL( psa_open_key( id, &handle ), PSA_ERROR_DOES_NOT_EXIST ); exit: psa_reset_key_attributes( &attributes ); psa_destroy_key( handle ); PSA_DONE( ); psa_purge_storage( ); memset( &validate_slot_number_directions, 0, sizeof( validate_slot_number_directions ) ); } /* END_CASE */