From eb7bdaa1778c8628df982f3a3146b924f97fba1c Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 21 Apr 2021 22:05:34 +0200 Subject: [PATCH] Add storage tests for lifetimes Test keys with various persistence levels, enumerated from the metadata tests. For read-only keys, do not attempt to create or destroy the key through the API, only to read a key that has been injected into storage directly through filesystem access. Do not test keys with a non-default location, since they require a driver and we do not yet have a dependency mechanism to require the presence of a driver for a specific location value. Signed-off-by: Gilles Peskine --- scripts/mbedtls_dev/psa_storage.py | 4 ++ tests/scripts/generate_psa_tests.py | 39 ++++++++++++++++++- ...t_suite_psa_crypto_storage_format.function | 22 ++++++++--- 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/scripts/mbedtls_dev/psa_storage.py b/scripts/mbedtls_dev/psa_storage.py index 3a740072e6..45f0380e7b 100644 --- a/scripts/mbedtls_dev/psa_storage.py +++ b/scripts/mbedtls_dev/psa_storage.py @@ -164,6 +164,10 @@ class Key: """ return self.bytes().hex() + def location_value(self) -> int: + """The numerical value of the location encoded in the key's lifetime.""" + return self.lifetime.value() >> 8 + class TestKey(unittest.TestCase): # pylint: disable=line-too-long diff --git a/tests/scripts/generate_psa_tests.py b/tests/scripts/generate_psa_tests.py index 8c53414f25..9615c29a3f 100755 --- a/tests/scripts/generate_psa_tests.py +++ b/tests/scripts/generate_psa_tests.py @@ -295,6 +295,38 @@ class StorageFormat: *extra_arguments]) return tc + def key_for_lifetime( + self, + lifetime: str, + ) -> StorageKey: + """Construct a test key for the given lifetime.""" + short = lifetime + short = re.sub(r'PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION', + r'', short) + short = re.sub(r'PSA_KEY_[A-Z]+_', r'', short) + description = 'lifetime: ' + short + key = StorageKey(version=self.version, + id=1, lifetime=lifetime, + type='PSA_KEY_TYPE_RAW_DATA', bits=8, + usage='PSA_KEY_USAGE_EXPORT', alg=0, alg2=0, + material=b'L', + description=description) + return key + + def all_keys_for_lifetimes(self) -> Iterator[StorageKey]: + """Generate test keys covering lifetimes.""" + lifetimes = sorted(self.constructors.lifetimes) + expressions = self.constructors.generate_expressions(lifetimes) + for lifetime in expressions: + # Don't attempt to create or load a volatile key in storage + if 'VOLATILE' in lifetime: + continue + # Don't attempt to create a read-only key in storage, + # but do attempt to load one. + if 'READ_ONLY' in lifetime and self.forward: + continue + yield self.key_for_lifetime(lifetime) + def key_for_usage_flags( self, usage_flags: List[str], @@ -395,12 +427,17 @@ class StorageFormat: # one go, which is a significant performance gain as the information # includes numerical values obtained by compiling a C program. keys = [] #type: List[StorageKey] + keys += self.all_keys_for_lifetimes() keys += self.all_keys_for_usage_flags() keys += self.all_keys_for_types() keys += self.all_keys_for_algorithms() for key in keys: + if key.location_value() != 0: + # Skip keys with a non-default location, because they + # require a driver and we currently have no mechanism to + # determine whether a driver is available. + continue yield self.make_test_case(key) - # To do: vary id, lifetime class TestGenerator: diff --git a/tests/suites/test_suite_psa_crypto_storage_format.function b/tests/suites/test_suite_psa_crypto_storage_format.function index 34d63a745b..003c70be07 100644 --- a/tests/suites/test_suite_psa_crypto_storage_format.function +++ b/tests/suites/test_suite_psa_crypto_storage_format.function @@ -8,6 +8,7 @@ #include #define TEST_FLAG_EXERCISE 0x00000001 +#define TEST_FLAG_READ_ONLY 0x00000002 /** Write a key with the given attributes and key material to storage. * Test that it has the expected representation. @@ -115,10 +116,22 @@ static int test_read_key( const psa_key_attributes_t *expected_attributes, psa_get_key_algorithm( expected_attributes ) ) ); } - /* Destroy the key. Confirm through direct access to the storage. */ - PSA_ASSERT( psa_destroy_key( key_id ) ); - TEST_EQUAL( PSA_ERROR_DOES_NOT_EXIST, - psa_its_get_info( uid, &storage_info ) ); + + if( flags & TEST_FLAG_READ_ONLY ) + { + /* Read-only keys cannot be removed through the API. + * The key will be removed through ITS in the cleanup code below. + * Purge the key from memory so that the test framework doesn't + * think the test is leaking it. */ + PSA_ASSERT( psa_purge_key( key_id ) ); + } + else + { + /* Destroy the key. Confirm through direct access to the storage. */ + PSA_ASSERT( psa_destroy_key( key_id ) ); + TEST_EQUAL( PSA_ERROR_DOES_NOT_EXIST, + psa_its_get_info( uid, &storage_info ) ); + } ok = 1; @@ -219,7 +232,6 @@ void key_storage_read( int lifetime_arg, int type_arg, int bits_arg, exit: psa_reset_key_attributes( &attributes ); - psa_destroy_key( key_id ); PSA_DONE( ); } /* END_CASE */