diff --git a/include/mbedtls/config.h b/include/mbedtls/config.h index c47c4714a..c1619fbad 100644 --- a/include/mbedtls/config.h +++ b/include/mbedtls/config.h @@ -1103,6 +1103,16 @@ */ //#define MBEDTLS_ENTROPY_NV_SEED +/** + * \def MBEDTLS_PSA_HAS_ITS_IO + * + * Enable the non-volatile secure storage usage. + * + * This is crucial on systems that do not have a HW TRNG support. + * + */ +//#define MBEDTLS_PSA_HAS_ITS_IO + /** * \def MBEDTLS_MEMORY_DEBUG * diff --git a/include/psa/crypto_extra.h b/include/psa/crypto_extra.h index 2d03f7311..b6f5adc89 100644 --- a/include/psa/crypto_extra.h +++ b/include/psa/crypto_extra.h @@ -34,6 +34,9 @@ extern "C" { #endif +/* UID for secure storage seed */ +#define PSA_CRYPTO_ITS_RANDOM_SEED_UID 0xFFFFFF52 + /** * \brief Library deinitialization. * @@ -44,6 +47,75 @@ extern "C" { */ void mbedtls_psa_crypto_free( void ); + +#if ( defined(MBEDTLS_ENTROPY_NV_SEED) && defined(MBEDTLS_PSA_HAS_ITS_IO) ) +/** + * \brief Inject an initial entropy seed for the random generator into + * secure storage. + * + * This function injects data to be used as a seed for the random generator + * used by the PSA Crypto implementation. On devices that lack a trusted + * entropy source (preferably a hardware random number generator), + * the Mbed PSA Crypto implementation uses this value to seed its + * random generator. + * + * On devices without a trusted entropy source, this function must be + * called exactly once in the lifetime of the device. On devices with + * a trusted entropy source, calling this function is optional. + * In all cases, this function may only be called before calling any + * other function in the PSA Crypto API, including psa_crypto_init(). + * + * When this function returns successfully, it populates a file in + * persistent storage. Once the file has been created, this function + * can no longer succeed. + * + * If any error occurs, this function does not change the system state. + * You can call this function again after correcting the reason for the + * error if possible. + * + * \warning This function **can** fail! Callers MUST check the return status. + * + * \warning If you use this function, you should use it as part of a + * factory provisioning process. The value of the injected seed + * is critical to the security of the device. It must be + * *secret*, *unpredictable* and (statistically) *unique per device*. + * You should be generate it randomly using a cryptographically + * secure random generator seeded from trusted entropy sources. + * You should transmit it securely to the device and ensure + * that its value is not leaked or stored anywhere beyond the + * needs of transmitting it from the point of generation to + * the call of this function, and erase all copies of the value + * once this function returns. + * + * This is an Mbed TLS extension. + * + * \param seed[in] Buffer containing the seed value to inject. + * \param seed_size Size of the \p seed buffer. + * The size of the seed in bytes must be greater + * or equal to both #MBEDTLS_ENTROPY_MIN_PLATFORM + * and #MBEDTLS_ENTROPY_BLOCK_SIZE. + * It must be less or equal to + * #MBEDTLS_ENTROPY_MAX_SEED_SIZE. + * + * \retval #PSA_SUCCESS + * The seed value was injected successfully. The random generator + * of the PSA Crypto implementation is now ready for use. + * You may now call psa_crypto_init() and use the PSA Crypto + * implementation. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p seed_size is out of range. + * \retval #PSA_ERROR_STORAGE_FAILURE + * \retval `PSA_ITS_ERROR_XXX` + * There was a failure reading or writing from storage. + * \retval #PSA_ERROR_NOT_PERMITTED + * The library has already been initialized. It is no longer + * possible to call this function. + */ +psa_status_t mbedtls_psa_inject_entropy(const unsigned char *seed, + size_t seed_size); + +#endif + #ifdef __cplusplus } #endif diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 05b8a8e8b..291dcdb0d 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -69,6 +69,7 @@ #include "mbedtls/ecdh.h" #include "mbedtls/ecp.h" #include "mbedtls/entropy.h" +#include "mbedtls/entropy_poll.h" #include "mbedtls/error.h" #include "mbedtls/gcm.h" #include "mbedtls/md2.h" @@ -85,7 +86,9 @@ #include "mbedtls/sha512.h" #include "mbedtls/xtea.h" - +#if ( defined(MBEDTLS_ENTROPY_NV_SEED) && defined(MBEDTLS_PSA_HAS_ITS_IO) ) +#include "psa_prot_internal_storage.h" +#endif #define ARRAY_LENGTH( array ) ( sizeof( array ) / sizeof( *( array ) ) ) @@ -4225,6 +4228,73 @@ psa_status_t psa_generate_random( uint8_t *output, return( mbedtls_to_psa_error( ret ) ); } +#if ( defined(MBEDTLS_ENTROPY_NV_SEED) && defined(MBEDTLS_PSA_HAS_ITS_IO) ) + +/* Support function for error conversion between psa_its error codes to psa crypto */ +static psa_status_t its_to_psa_error( psa_its_status_t ret ) +{ + switch( ret ) + { + case PSA_ITS_SUCCESS: + return( PSA_SUCCESS ); + + case PSA_ITS_ERROR_KEY_NOT_FOUND: + return( PSA_ERROR_EMPTY_SLOT ); + + case PSA_ITS_ERROR_STORAGE_FAILURE: + return( PSA_ERROR_STORAGE_FAILURE ); + + case PSA_ITS_ERROR_INSUFFICIENT_SPACE: + return( PSA_ERROR_INSUFFICIENT_STORAGE ); + + case PSA_ITS_ERROR_INVALID_KEY: + case PSA_PS_ERROR_OFFSET_INVALID: + case PSA_ITS_ERROR_INCORRECT_SIZE: + case PSA_ITS_ERROR_BAD_POINTER: + return( PSA_ERROR_INVALID_ARGUMENT ); + + case PSA_ITS_ERROR_FLAGS_NOT_SUPPORTED: + return( PSA_ERROR_NOT_SUPPORTED ); + + case PSA_ITS_ERROR_WRITE_ONCE: + return( PSA_ERROR_OCCUPIED_SLOT ); + + default: + return( PSA_ERROR_UNKNOWN_ERROR ); + } +} + +psa_status_t mbedtls_psa_inject_entropy( const unsigned char *seed, + size_t seed_size ) +{ + psa_status_t status; + psa_its_status_t its_status; + struct psa_its_info_t p_info; + if( global_data.initialized ) + return( PSA_ERROR_NOT_PERMITTED ); + + if( ( ( seed_size < MBEDTLS_ENTROPY_MIN_PLATFORM ) || + ( seed_size < MBEDTLS_ENTROPY_BLOCK_SIZE ) ) || + ( seed_size > MBEDTLS_ENTROPY_MAX_SEED_SIZE ) ) + return( PSA_ERROR_INVALID_ARGUMENT ); + + its_status = psa_its_get_info( PSA_CRYPTO_ITS_RANDOM_SEED_UID, &p_info ); + status = its_to_psa_error( its_status ); + + if( PSA_ITS_ERROR_KEY_NOT_FOUND == its_status ) /* No seed exists */ + { + its_status = psa_its_set( PSA_CRYPTO_ITS_RANDOM_SEED_UID, seed_size, seed, 0 ); + status = its_to_psa_error( its_status ); + } + else if( PSA_ITS_SUCCESS == its_status ) + { + /* You should not be here. Seed needs to be injected only once */ + status = PSA_ERROR_NOT_PERMITTED; + } + return( status ); +} +#endif + psa_status_t psa_generate_key( psa_key_slot_t key, psa_key_type_t type, size_t bits, diff --git a/library/version_features.c b/library/version_features.c index af8149052..590f949f4 100644 --- a/library/version_features.c +++ b/library/version_features.c @@ -402,6 +402,9 @@ static const char *features[] = { #if defined(MBEDTLS_ENTROPY_NV_SEED) "MBEDTLS_ENTROPY_NV_SEED", #endif /* MBEDTLS_ENTROPY_NV_SEED */ +#if defined(MBEDTLS_PSA_HAS_ITS_IO) + "MBEDTLS_PSA_HAS_ITS_IO", +#endif /* MBEDTLS_PSA_HAS_ITS_IO */ #if defined(MBEDTLS_MEMORY_DEBUG) "MBEDTLS_MEMORY_DEBUG", #endif /* MBEDTLS_MEMORY_DEBUG */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 34658c8e1..95d60ff31 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -111,6 +111,7 @@ add_test_suite(pkparse) add_test_suite(pkwrite) add_test_suite(poly1305) add_test_suite(psa_crypto) +add_test_suite(psa_crypto_entropy) add_test_suite(psa_crypto_hash) add_test_suite(psa_crypto_metadata) add_test_suite(psa_crypto_persistent_key) diff --git a/tests/suites/test_suite_psa_crypto_entropy.data b/tests/suites/test_suite_psa_crypto_entropy.data new file mode 100644 index 000000000..61593e9d6 --- /dev/null +++ b/tests/suites/test_suite_psa_crypto_entropy.data @@ -0,0 +1,18 @@ +PSA validate entropy injection: good, minimum size +validate_entropy_seed_injection:MBEDTLS_PSA_INJECT_ENTROPY_MIN_SIZE:PSA_SUCCESS:MBEDTLS_PSA_INJECT_ENTROPY_MIN_SIZE:PSA_ERROR_NOT_PERMITTED + +PSA validate entropy injection: good, max size +validate_entropy_seed_injection:MBEDTLS_ENTROPY_MAX_SEED_SIZE:PSA_SUCCESS:MBEDTLS_ENTROPY_MAX_SEED_SIZE:PSA_ERROR_NOT_PERMITTED + +PSA validate entropy injection: bad, too big +validate_entropy_seed_injection:MBEDTLS_ENTROPY_MAX_SEED_SIZE+1:PSA_ERROR_INVALID_ARGUMENT:MBEDTLS_PSA_INJECT_ENTROPY_MIN_SIZE:PSA_SUCCESS + +PSA validate entropy injection: bad, too small using MBEDTLS_ENTROPY_MIN_PLATFORM +validate_entropy_seed_injection:MBEDTLS_ENTROPY_MIN_PLATFORM-1:PSA_ERROR_INVALID_ARGUMENT:MBEDTLS_PSA_INJECT_ENTROPY_MIN_SIZE:PSA_SUCCESS + +PSA validate entropy injection: bad, too small using MBEDTLS_ENTROPY_BLOCK_SIZE +validate_entropy_seed_injection:MBEDTLS_ENTROPY_BLOCK_SIZE-1:PSA_ERROR_INVALID_ARGUMENT:MBEDTLS_PSA_INJECT_ENTROPY_MIN_SIZE:PSA_SUCCESS + +PSA validate entropy injection: before and after crypto_init +run_entropy_inject_with_crypto_init: + diff --git a/tests/suites/test_suite_psa_crypto_entropy.function b/tests/suites/test_suite_psa_crypto_entropy.function new file mode 100644 index 000000000..46c77e97c --- /dev/null +++ b/tests/suites/test_suite_psa_crypto_entropy.function @@ -0,0 +1,102 @@ +/* BEGIN_HEADER */ +#include + +#include "psa/crypto.h" +#include "psa_prot_internal_storage.h" +#include "mbedtls/entropy.h" +#include "mbedtls/entropy_poll.h" + +/* MAX value support macro */ +#if !defined(MAX) +#define MAX(a,b) (((a)>(b))?(a):(b)) +#endif + +/* Calculating the minimum allowed entropy size in bytes */ +#define MBEDTLS_PSA_INJECT_ENTROPY_MIN_SIZE MAX(MBEDTLS_ENTROPY_MIN_PLATFORM, MBEDTLS_ENTROPY_BLOCK_SIZE) + +/* END_HEADER */ + +/* BEGIN_DEPENDENCIES + * depends_on:MBEDTLS_ENTROPY_NV_SEED:MBEDTLS_PSA_HAS_ITS_IO:MBEDTLS_PSA_CRYPTO_C + * END_DEPENDENCIES + */ + +/* BEGIN_CASE */ +void validate_entropy_seed_injection( int seed_length_a, + int expected_status_a, + int seed_length_b, + int expected_status_b ) +{ + psa_its_status_t its_status; + psa_status_t status; + uint8_t output[32] = { 0 }; + uint8_t zeros[32] = { 0 }; + uint8_t *seed = NULL; + int i; + int seed_size; + if( seed_length_a > seed_length_b ) + { + seed_size = seed_length_a; + } + else + { + seed_size = seed_length_b; + } + ASSERT_ALLOC( seed, seed_size ); + /* fill seed with some data */ + for( i = 0; i < seed_size; ++i ) + { + seed[i] = i; + } + its_status = psa_its_remove( PSA_CRYPTO_ITS_RANDOM_SEED_UID ); + TEST_ASSERT( ( its_status == PSA_ITS_SUCCESS ) || + ( its_status == PSA_ITS_ERROR_KEY_NOT_FOUND ) ); + status = mbedtls_psa_inject_entropy( seed, seed_length_a ); + TEST_ASSERT( status == expected_status_a ); + status = mbedtls_psa_inject_entropy( seed, seed_length_b ); + TEST_ASSERT( status == expected_status_b ); + TEST_ASSERT( psa_crypto_init( ) == PSA_SUCCESS ); + TEST_ASSERT( psa_generate_random( output, + sizeof( output ) ) == PSA_SUCCESS ); + TEST_ASSERT( memcmp( output, zeros, sizeof( output ) ) != 0 ); +exit: + mbedtls_free( seed ); + psa_its_remove( PSA_CRYPTO_ITS_RANDOM_SEED_UID ); + mbedtls_psa_crypto_free( ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void run_entropy_inject_with_crypto_init( ) +{ + psa_its_status_t its_status; + psa_status_t status; + int i; + uint8_t seed[MBEDTLS_PSA_INJECT_ENTROPY_MIN_SIZE] = { 0 }; + /* fill seed with some data */ + for( i = 0; i < sizeof( seed ); ++i ) + { + seed[i] = i; + } + its_status = psa_its_remove( PSA_CRYPTO_ITS_RANDOM_SEED_UID ); + TEST_ASSERT( ( its_status == PSA_ITS_SUCCESS ) || + ( its_status == PSA_ITS_ERROR_KEY_NOT_FOUND ) ); + status = mbedtls_psa_inject_entropy( seed, sizeof( seed ) ); + TEST_ASSERT( status == PSA_SUCCESS ); + its_status = psa_its_remove( PSA_CRYPTO_ITS_RANDOM_SEED_UID ); + TEST_ASSERT( its_status == PSA_ITS_SUCCESS ); + status = psa_crypto_init( ); + TEST_ASSERT( status == PSA_ERROR_INSUFFICIENT_ENTROPY ); + status = mbedtls_psa_inject_entropy( seed, sizeof( seed ) ); + TEST_ASSERT( status == PSA_SUCCESS ); + status = psa_crypto_init( ); + TEST_ASSERT( status == PSA_SUCCESS ); + mbedtls_psa_crypto_free( ); + /* The seed is written by nv_seed callback functions therefore the injection will fail */ + status = mbedtls_psa_inject_entropy( seed, sizeof( seed ) ); + TEST_ASSERT( status == PSA_ERROR_NOT_PERMITTED ); +exit: + psa_its_remove( PSA_CRYPTO_ITS_RANDOM_SEED_UID ); + mbedtls_psa_crypto_free( ); +} +/* END_CASE */