Merge pull request #293 from gilles-peskine-arm/entropy-min

Always gather MBEDTLS_ENTROPY_BLOCK_SIZE bytes of entropy
diff --git a/library/entropy.c b/library/entropy.c
index d7091cb..a40ce80 100644
--- a/library/entropy.c
+++ b/library/entropy.c
@@ -327,7 +327,8 @@
 
 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];
 
@@ -365,12 +366,17 @@
         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 );
 
diff --git a/tests/suites/test_suite_entropy.data b/tests/suites/test_suite_entropy.data
index 11ced64..b2d20b4 100644
--- a/tests/suites/test_suite_entropy.data
+++ b/tests/suites/test_suite_entropy.data
@@ -1,45 +1,66 @@
 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 no sources
+entropy_no_sources:
+
 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
 
+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 0d86ead..9f10a90 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,29 +23,28 @@
  * 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 );
 }
 
-#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
@@ -48,11 +56,12 @@
  * 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
  */
@@ -140,10 +149,27 @@
 /* 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(  )
 {
     mbedtls_entropy_context ctx;
     size_t i;
+    entropy_dummy_context dummy = {DUMMY_REQUESTED_LENGTH, 0, 0};
 
     mbedtls_entropy_init( &ctx );
 
@@ -152,10 +178,10 @@
      * 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 +223,13 @@
 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 );
 
@@ -225,30 +251,87 @@
 }
 /* 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 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,
-                                     &chunk_size, 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 );
 
-    entropy_dummy_calls = 0;
     ret = mbedtls_entropy_func( &ctx, buf, sizeof( buf ) );
 
     if( result >= 0 )
     {
         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( entropy_dummy_calls == (size_t) result );
+        TEST_ASSERT( weak.calls == (size_t) result );
+    }
+    else
+    {
+        TEST_ASSERT( ret == result );
+    }
+
+exit:
+    mbedtls_entropy_free( &ctx );
+}
+/* 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 );
+#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
     {