From c34b839d85186aeafe6b29cfa85dda812f7456d3 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 8 Oct 2019 14:21:18 +0200 Subject: [PATCH 1/7] Improve the descriptions of some entropy test cases --- tests/suites/test_suite_entropy.data | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/suites/test_suite_entropy.data b/tests/suites/test_suite_entropy.data index 11ced64b3..7460d74b8 100644 --- a/tests/suites/test_suite_entropy.data +++ b/tests/suites/test_suite_entropy.data @@ -1,43 +1,43 @@ Create NV seed_file nv_seed_file_create: -Entropy write/update seed file [#1] +Entropy write/update seed file: good entropy_seed_file:"data_files/entropy_seed":0 -Entropy write/update seed file [#2] +Entropy write/update seed file: nonexistent entropy_seed_file:"no_such_dir/file":MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR Entropy too many sources entropy_too_many_sources: -Entropy output length #1 +Entropy output length: 0 entropy_func_len:0:0 -Entropy output length #2 +Entropy output length: 1 entropy_func_len:1:0 -Entropy output length #3 +Entropy output length: 2 entropy_func_len:2:0 -Entropy output length #4 +Entropy output length: 31 entropy_func_len:31:0 -Entropy output length #5 +Entropy output length: 65 > BLOCK_SIZE entropy_func_len:65:MBEDTLS_ERR_ENTROPY_SOURCE_FAILED Entropy failing source entropy_source_fail:"data_files/entropy_seed" -Entropy threshold #1 +Entropy threshold: 16=2*8 entropy_threshold:16:2:8 -Entropy threshold #2 +Entropy threshold: 32=1*32 entropy_threshold:32:1:32 -Entropy threshold #3 +Entropy threshold: 0* never reaches the threshold entropy_threshold:16:0:MBEDTLS_ERR_ENTROPY_SOURCE_FAILED -Entropy threshold #4 +Entropy threshold: 1024 never reached entropy_threshold:1024:1:MBEDTLS_ERR_ENTROPY_SOURCE_FAILED Check NV seed standard IO From ed04a676ee52e3ce54192dd4ff15f79bc5636a9e Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 8 Oct 2019 14:37:27 +0200 Subject: [PATCH 2/7] Entropy tests: support multiple dummy sources Always pass a context object to entropy_dummy_source. This lets us write tests that register more than one source and keep track of how many times each one is called. --- tests/suites/test_suite_entropy.function | 54 ++++++++++++++---------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/tests/suites/test_suite_entropy.function b/tests/suites/test_suite_entropy.function index 0d86eadbe..8563b11f0 100644 --- a/tests/suites/test_suite_entropy.function +++ b/tests/suites/test_suite_entropy.function @@ -3,10 +3,19 @@ #include "mbedtls/entropy_poll.h" #include "string.h" -/* - * Number of calls made to entropy_dummy_source() - */ -static size_t entropy_dummy_calls; +typedef enum +{ + DUMMY_CONSTANT_LENGTH, /* Output context->length bytes */ + DUMMY_REQUESTED_LENGTH, /* Output whatever length was requested */ + DUMMY_FAIL, /* Return an error code */ +} entropy_dummy_instruction; + +typedef struct +{ + entropy_dummy_instruction instruction; + size_t length; /* Length to return for DUMMY_CONSTANT_LENGTH */ + size_t calls; /* Incremented at each call */ +} entropy_dummy_context; /* * Dummy entropy source @@ -14,25 +23,25 @@ static size_t entropy_dummy_calls; * If data is NULL, write exactly the requested length. * Otherwise, write the length indicated by data or error if negative */ -static int entropy_dummy_source( void *data, unsigned char *output, +static int entropy_dummy_source( void *arg, unsigned char *output, size_t len, size_t *olen ) { - entropy_dummy_calls++; + entropy_dummy_context *context = arg; + ++context->calls; - if( data == NULL ) - *olen = len; - else + switch( context->instruction ) { - int *d = (int *) data; - - if( *d < 0 ) + case DUMMY_CONSTANT_LENGTH: + *olen = context->length; + break; + case DUMMY_REQUESTED_LENGTH: + *olen = len; + break; + case DUMMY_FAIL: return( MBEDTLS_ERR_ENTROPY_SOURCE_FAILED ); - else - *olen = *d; } memset( output, 0x2a, *olen ); - return( 0 ); } @@ -144,6 +153,7 @@ void entropy_too_many_sources( ) { mbedtls_entropy_context ctx; size_t i; + entropy_dummy_context dummy = {DUMMY_REQUESTED_LENGTH, 0, 0}; mbedtls_entropy_init( &ctx ); @@ -152,10 +162,10 @@ void entropy_too_many_sources( ) * since we don't know how many sources were automatically added. */ for( i = 0; i < MBEDTLS_ENTROPY_MAX_SOURCES; i++ ) - (void) mbedtls_entropy_add_source( &ctx, entropy_dummy_source, NULL, + (void) mbedtls_entropy_add_source( &ctx, entropy_dummy_source, &dummy, 16, MBEDTLS_ENTROPY_SOURCE_WEAK ); - TEST_ASSERT( mbedtls_entropy_add_source( &ctx, entropy_dummy_source, NULL, + TEST_ASSERT( mbedtls_entropy_add_source( &ctx, entropy_dummy_source, &dummy, 16, MBEDTLS_ENTROPY_SOURCE_WEAK ) == MBEDTLS_ERR_ENTROPY_MAX_SOURCES ); @@ -197,13 +207,13 @@ void entropy_func_len( int len, int ret ) void entropy_source_fail( char * path ) { mbedtls_entropy_context ctx; - int fail = -1; unsigned char buf[16]; + entropy_dummy_context dummy = {DUMMY_FAIL, 0, 0}; mbedtls_entropy_init( &ctx ); TEST_ASSERT( mbedtls_entropy_add_source( &ctx, entropy_dummy_source, - &fail, 16, + &dummy, 16, MBEDTLS_ENTROPY_SOURCE_WEAK ) == 0 ); @@ -229,16 +239,16 @@ exit: void entropy_threshold( int threshold, int chunk_size, int result ) { mbedtls_entropy_context ctx; + entropy_dummy_context dummy = {DUMMY_CONSTANT_LENGTH, chunk_size, 0}; unsigned char buf[MBEDTLS_ENTROPY_BLOCK_SIZE] = { 0 }; int ret; mbedtls_entropy_init( &ctx ); TEST_ASSERT( mbedtls_entropy_add_source( &ctx, entropy_dummy_source, - &chunk_size, threshold, + &dummy, threshold, MBEDTLS_ENTROPY_SOURCE_WEAK ) == 0 ); - entropy_dummy_calls = 0; ret = mbedtls_entropy_func( &ctx, buf, sizeof( buf ) ); if( result >= 0 ) @@ -248,7 +258,7 @@ void entropy_threshold( int threshold, int chunk_size, int result ) // Two times as much calls due to the NV seed update result *= 2; #endif - TEST_ASSERT( entropy_dummy_calls == (size_t) result ); + TEST_ASSERT( dummy.calls == (size_t) result ); } else { From 7f246510d0b5f19e3e1fcf16167b1c4e3cae0143 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 8 Oct 2019 14:51:49 +0200 Subject: [PATCH 3/7] Add a test case for MBEDTLS_ERR_ENTROPY_NO_SOURCES_DEFINED --- tests/suites/test_suite_entropy.data | 3 +++ tests/suites/test_suite_entropy.function | 20 ++++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/tests/suites/test_suite_entropy.data b/tests/suites/test_suite_entropy.data index 7460d74b8..abf36c0ec 100644 --- a/tests/suites/test_suite_entropy.data +++ b/tests/suites/test_suite_entropy.data @@ -7,6 +7,9 @@ entropy_seed_file:"data_files/entropy_seed":0 Entropy write/update seed file: nonexistent entropy_seed_file:"no_such_dir/file":MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR +Entropy no sources +entropy_no_sources: + Entropy too many sources entropy_too_many_sources: diff --git a/tests/suites/test_suite_entropy.function b/tests/suites/test_suite_entropy.function index 8563b11f0..a125f6202 100644 --- a/tests/suites/test_suite_entropy.function +++ b/tests/suites/test_suite_entropy.function @@ -45,7 +45,6 @@ static int entropy_dummy_source( void *arg, unsigned char *output, return( 0 ); } -#if defined(MBEDTLS_ENTROPY_NV_SEED) /* * Ability to clear entropy sources to allow testing with just predefined * entropy sources. This function or tests depending on it might break if there @@ -57,11 +56,12 @@ static int entropy_dummy_source( void *arg, unsigned char *output, * This might break memory checks in the future if sources need 'free-ing' then * as well. */ -void entropy_clear_sources( mbedtls_entropy_context *ctx ) +static void entropy_clear_sources( mbedtls_entropy_context *ctx ) { ctx->source_count = 0; } +#if defined(MBEDTLS_ENTROPY_NV_SEED) /* * NV seed read/write functions that use a buffer instead of a file */ @@ -148,6 +148,22 @@ exit: } /* END_CASE */ +/* BEGIN_CASE */ +void entropy_no_sources( ) +{ + mbedtls_entropy_context ctx; + unsigned char buf[MBEDTLS_ENTROPY_BLOCK_SIZE]; + + mbedtls_entropy_init( &ctx ); + entropy_clear_sources( &ctx ); + TEST_EQUAL( mbedtls_entropy_func( &ctx, buf, sizeof( buf ) ), + MBEDTLS_ERR_ENTROPY_NO_SOURCES_DEFINED ); + +exit: + mbedtls_entropy_free( &ctx ); +} +/* END_CASE */ + /* BEGIN_CASE */ void entropy_too_many_sources( ) { From 65fc0686a7df290fc3f0f58d318241bf8f61c76a Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 8 Oct 2019 15:01:34 +0200 Subject: [PATCH 4/7] Add tests to ensure that we gather as much entropy as expected There were tests to ensure that each entropy source reaches its threshold, but no test that covers the total amount of entropy. Add test cases with a known set of entropy sources and make sure that we always gather at least MBEDTLS_ENTROPY_BLOCK_SIZE bytes from a strong source. --- tests/suites/test_suite_entropy.data | 18 ++++++++++ tests/suites/test_suite_entropy.function | 43 ++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/tests/suites/test_suite_entropy.data b/tests/suites/test_suite_entropy.data index abf36c0ec..b2d20b472 100644 --- a/tests/suites/test_suite_entropy.data +++ b/tests/suites/test_suite_entropy.data @@ -43,6 +43,24 @@ entropy_threshold:16:0:MBEDTLS_ERR_ENTROPY_SOURCE_FAILED Entropy threshold: 1024 never reached entropy_threshold:1024:1:MBEDTLS_ERR_ENTROPY_SOURCE_FAILED +Entropy calls: no strong +entropy_calls:MBEDTLS_ENTROPY_SOURCE_WEAK:MBEDTLS_ENTROPY_SOURCE_WEAK:1:MBEDTLS_ENTROPY_BLOCK_SIZE:MBEDTLS_ERR_ENTROPY_NO_STRONG_SOURCE + +Entropy calls: 1 strong, 1*BLOCK_SIZE +entropy_calls:MBEDTLS_ENTROPY_SOURCE_STRONG:MBEDTLS_ENTROPY_SOURCE_WEAK:1:MBEDTLS_ENTROPY_BLOCK_SIZE:1 + +Entropy calls: 1 strong, 2*(BLOCK_SIZE/2) +entropy_calls:MBEDTLS_ENTROPY_SOURCE_STRONG:MBEDTLS_ENTROPY_SOURCE_WEAK:1:(MBEDTLS_ENTROPY_BLOCK_SIZE+1)/2:2 + +Entropy calls: 1 strong, BLOCK_SIZE*1 +entropy_calls:MBEDTLS_ENTROPY_SOURCE_STRONG:MBEDTLS_ENTROPY_SOURCE_WEAK:1:1:MBEDTLS_ENTROPY_BLOCK_SIZE + +Entropy calls: 1 strong, 2*BLOCK_SIZE to reach threshold +entropy_calls:MBEDTLS_ENTROPY_SOURCE_STRONG:MBEDTLS_ENTROPY_SOURCE_WEAK:MBEDTLS_ENTROPY_BLOCK_SIZE+1:MBEDTLS_ENTROPY_BLOCK_SIZE:2 + +Entropy calls: 2 strong, BLOCK_SIZE/2 each +entropy_calls:MBEDTLS_ENTROPY_SOURCE_STRONG:MBEDTLS_ENTROPY_SOURCE_WEAK:(MBEDTLS_ENTROPY_BLOCK_SIZE+1)/2:(MBEDTLS_ENTROPY_BLOCK_SIZE+1)/2:2 + Check NV seed standard IO entropy_nv_seed_std_io: diff --git a/tests/suites/test_suite_entropy.function b/tests/suites/test_suite_entropy.function index a125f6202..d1d88c5fa 100644 --- a/tests/suites/test_suite_entropy.function +++ b/tests/suites/test_suite_entropy.function @@ -286,6 +286,49 @@ exit: } /* END_CASE */ +/* BEGIN_CASE */ +void entropy_calls( int strength1, int strength2, + int threshold, int chunk_size, + int result ) +{ + /* + * if result >= 0: result = expected number of calls to source 1 + * if result < 0: result = expected return code from mbedtls_entropy_func() + */ + + mbedtls_entropy_context ctx; + entropy_dummy_context dummy1 = {DUMMY_CONSTANT_LENGTH, chunk_size, 0}; + entropy_dummy_context dummy2 = {DUMMY_CONSTANT_LENGTH, chunk_size, 0}; + unsigned char buf[MBEDTLS_ENTROPY_BLOCK_SIZE] = { 0 }; + int ret; + + mbedtls_entropy_init( &ctx ); + entropy_clear_sources( &ctx ); + + TEST_ASSERT( mbedtls_entropy_add_source( &ctx, entropy_dummy_source, + &dummy1, threshold, + strength1 ) == 0 ); + TEST_ASSERT( mbedtls_entropy_add_source( &ctx, entropy_dummy_source, + &dummy2, threshold, + strength2 ) == 0 ); + + ret = mbedtls_entropy_func( &ctx, buf, sizeof( buf ) ); + + if( result >= 0 ) + { + TEST_ASSERT( ret == 0 ); + TEST_ASSERT( dummy1.calls == (size_t) result ); + } + else + { + TEST_ASSERT( ret == result ); + } + +exit: + mbedtls_entropy_free( &ctx ); +} +/* END_CASE */ + /* BEGIN_CASE depends_on:MBEDTLS_ENTROPY_NV_SEED:MBEDTLS_FS_IO */ void nv_seed_file_create( ) { From 85485c73380e44e6d6e00a340883d1972d5f9e21 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 8 Oct 2019 15:04:16 +0200 Subject: [PATCH 5/7] Always gather MBEDTLS_ENTROPY_BLOCK_SIZE bytes of entropy mbedtls_entropy_func returns up to MBEDTLS_ENTROPY_BLOCK_SIZE bytes. This is the output of a hash function and does not indicate how many bytes of entropy went into the hash computation. Enforce that mbedtls_entropy_func gathers a total of MBEDTLS_ENTROPY_BLOCK_SIZE bytes or more from strong sources. Weak sources don't count for this calculation. This is complementary to the per-source threshold mechanism. In particular, we define system sources with a threshold of 32. But when using SHA-512 for the entropy accumulator, MBEDTLS_ENTROPY_BLOCK_SIZE = 64, so users can expect 64 bytes' worth of entropy. Before, you only got 64 bytes of entropy if there were two sources. Now you get 64 bytes of entropy even with a single source with a threshold of 32. --- library/entropy.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/library/entropy.c b/library/entropy.c index f8db1a550..565525396 100644 --- a/library/entropy.c +++ b/library/entropy.c @@ -325,7 +325,8 @@ int mbedtls_entropy_gather( mbedtls_entropy_context *ctx ) int mbedtls_entropy_func( void *data, unsigned char *output, size_t len ) { - int ret, count = 0, i, done; + int ret, count = 0, i, thresholds_reached; + size_t strong_size; mbedtls_entropy_context *ctx = (mbedtls_entropy_context *) data; unsigned char buf[MBEDTLS_ENTROPY_BLOCK_SIZE]; @@ -363,12 +364,17 @@ int mbedtls_entropy_func( void *data, unsigned char *output, size_t len ) if( ( ret = entropy_gather_internal( ctx ) ) != 0 ) goto exit; - done = 1; + thresholds_reached = 1; + strong_size = 0; for( i = 0; i < ctx->source_count; i++ ) + { if( ctx->source[i].size < ctx->source[i].threshold ) - done = 0; + thresholds_reached = 0; + if( ctx->source[i].strong == MBEDTLS_ENTROPY_SOURCE_STRONG ) + strong_size += ctx->source[i].size; + } } - while( ! done ); + while( ! thresholds_reached || strong_size < MBEDTLS_ENTROPY_BLOCK_SIZE ); memset( buf, 0, MBEDTLS_ENTROPY_BLOCK_SIZE ); From ae679390a20d8fbd0a32b7b70f4e569d89939c4a Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Mon, 25 Nov 2019 18:26:23 +0100 Subject: [PATCH 6/7] Fix entropy_calls when MBEDTLS_ENTROPY_NV_SEED is enabled --- tests/suites/test_suite_entropy.function | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/suites/test_suite_entropy.function b/tests/suites/test_suite_entropy.function index d1d88c5fa..1a4fefde3 100644 --- a/tests/suites/test_suite_entropy.function +++ b/tests/suites/test_suite_entropy.function @@ -271,7 +271,8 @@ void entropy_threshold( int threshold, int chunk_size, int result ) { TEST_ASSERT( ret == 0 ); #if defined(MBEDTLS_ENTROPY_NV_SEED) - // Two times as much calls due to the NV seed update + /* If the NV seed functionality is enabled, there are two entropy + * updates: before and after updating the NV seed. */ result *= 2; #endif TEST_ASSERT( dummy.calls == (size_t) result ); @@ -317,6 +318,11 @@ void entropy_calls( int strength1, int strength2, if( result >= 0 ) { TEST_ASSERT( ret == 0 ); +#if defined(MBEDTLS_ENTROPY_NV_SEED) + /* If the NV seed functionality is enabled, there are two entropy + * updates: before and after updating the NV seed. */ + result *= 2; +#endif TEST_ASSERT( dummy1.calls == (size_t) result ); } else From cbd91e013c6cd39b05963ac51ef59a70d458c097 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Mon, 25 Nov 2019 19:50:54 +0100 Subject: [PATCH 7/7] Fix entropy_threshold when MBEDTLS_TEST_NULL_ENTROPY is enabled Don't use the default entropy sources so as not to depend on their characteristics. --- tests/suites/test_suite_entropy.function | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/suites/test_suite_entropy.function b/tests/suites/test_suite_entropy.function index 1a4fefde3..9f10a9043 100644 --- a/tests/suites/test_suite_entropy.function +++ b/tests/suites/test_suite_entropy.function @@ -251,18 +251,26 @@ exit: } /* END_CASE */ -/* BEGIN_CASE depends_on:ENTROPY_HAVE_STRONG */ +/* BEGIN_CASE */ void entropy_threshold( int threshold, int chunk_size, int result ) { mbedtls_entropy_context ctx; - entropy_dummy_context dummy = {DUMMY_CONSTANT_LENGTH, chunk_size, 0}; + entropy_dummy_context strong = + {DUMMY_CONSTANT_LENGTH, MBEDTLS_ENTROPY_BLOCK_SIZE, 0}; + entropy_dummy_context weak = {DUMMY_CONSTANT_LENGTH, chunk_size, 0}; unsigned char buf[MBEDTLS_ENTROPY_BLOCK_SIZE] = { 0 }; int ret; mbedtls_entropy_init( &ctx ); + entropy_clear_sources( &ctx ); + /* Set strong source that reaches its threshold immediately and + * a weak source whose threshold is a test parameter. */ TEST_ASSERT( mbedtls_entropy_add_source( &ctx, entropy_dummy_source, - &dummy, threshold, + &strong, 1, + MBEDTLS_ENTROPY_SOURCE_STRONG ) == 0 ); + TEST_ASSERT( mbedtls_entropy_add_source( &ctx, entropy_dummy_source, + &weak, threshold, MBEDTLS_ENTROPY_SOURCE_WEAK ) == 0 ); ret = mbedtls_entropy_func( &ctx, buf, sizeof( buf ) ); @@ -275,7 +283,7 @@ void entropy_threshold( int threshold, int chunk_size, int result ) * updates: before and after updating the NV seed. */ result *= 2; #endif - TEST_ASSERT( dummy.calls == (size_t) result ); + TEST_ASSERT( weak.calls == (size_t) result ); } else {