mbedtls/tests/suites/test_suite_psa_crypto_se_driver_hal.function
Gilles Peskine 105736653f SE keys: test that no function goes crazy
Run all functions that take a key handle as input with a key that is
in a secure element. All calls are expected to error out one way or
another (not permitted by policy, invalid key type, method not
implemented in the secure element, ...). The goal of this test is to
ensure that nothing bad happens (e.g. invalid pointer dereference).

Run with various key types and algorithms to get good coverage.
2019-07-24 13:45:36 +02:00

560 lines
20 KiB
Plaintext

/* BEGIN_HEADER */
#include "psa_crypto_helpers.h"
#include "psa/crypto_se_driver.h"
#include "psa_crypto_se.h"
#include "psa_crypto_storage.h"
/****************************************************************/
/* 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.
*
* Use this macro to assert on guarantees provided by the core.
*/
#define DRIVER_ASSERT( TEST ) \
do { \
if( ! (TEST) ) \
{ \
test_fail( #TEST, __LINE__, __FILE__ ); \
return( PSA_ERROR_DETECTED_BY_DRIVER ); \
} \
} while( 0 )
/****************************************************************/
/* Miscellaneous driver methods */
/****************************************************************/
/* Allocate slot numbers with a monotonic counter. */
static psa_status_t counter_allocate( psa_drv_se_context_t *context,
void *persistent_data,
const psa_key_attributes_t *attributes,
psa_key_slot_number_t *slot_number )
{
psa_key_slot_number_t *p_counter = persistent_data;
(void) attributes;
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 );
*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,
psa_key_lifetime_t lifetime,
psa_key_type_t type,
psa_algorithm_t algorithm,
psa_key_usage_t usage,
const uint8_t *p_data,
size_t data_length )
{
(void) context;
(void) slot_number;
(void) lifetime;
(void) type;
(void) algorithm;
(void) usage;
(void) p_data;
(void) data_length;
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 uint8_t ram_min_slot = 0;
static void ram_slots_reset( void )
{
memset( ram_slots, 0, sizeof( ram_slots ) );
ram_min_slot = 0;
}
static psa_status_t ram_import( psa_drv_se_context_t *context,
psa_key_slot_number_t slot_number,
psa_key_lifetime_t lifetime,
psa_key_type_t type,
psa_algorithm_t algorithm,
psa_key_usage_t usage,
const uint8_t *p_data,
size_t data_length )
{
(void) context;
DRIVER_ASSERT( slot_number < ARRAY_LENGTH( ram_slots ) );
if( data_length > sizeof( ram_slots[slot_number].content ) )
return( PSA_ERROR_INSUFFICIENT_STORAGE );
ram_slots[slot_number].lifetime = lifetime;
ram_slots[slot_number].type = type;
ram_slots[slot_number].bits = PSA_BYTES_TO_BITS( data_length );
(void) algorithm;
(void) usage;
memcpy( ram_slots[slot_number].content, p_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 *p_data,
size_t data_size,
size_t *p_data_length )
{
size_t actual_size;
(void) context;
DRIVER_ASSERT( 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 );
*p_data_length = actual_size;
memcpy( p_data, ram_slots[slot_number].content, actual_size );
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( context->persistent_data_size == sizeof( ram_slot_usage_t ) );
DRIVER_ASSERT( 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 );
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_slot_number_t *slot_number )
{
ram_slot_usage_t *slot_usage = persistent_data;
(void) attributes;
DRIVER_ASSERT( 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 ) )
return( PSA_SUCCESS );
}
return( PSA_ERROR_INSUFFICIENT_STORAGE );
}
/****************************************************************/
/* Other test helper functions */
/****************************************************************/
/* 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 ) );
/* 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( ) );
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 );
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 ) );
/* 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_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;
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 ) );
/* 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( ) );
PSA_ASSERT( psa_open_key( id, &handle ) );
if( ! smoke_test_key( handle ) )
goto exit;
/* We're done. */
PSA_ASSERT( psa_destroy_key( handle ) );
exit:
PSA_DONE( );
ram_slots_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;
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( );
ram_slots_reset( );
psa_purge_storage( );
}
/* END_CASE */