]> granicus.if.org Git - esp-idf/commitdiff
esp32: Add esp_fill_random() function
authorAngus Gratton <angus@espressif.com>
Wed, 15 Aug 2018 08:20:16 +0000 (18:20 +1000)
committerbot <bot@espressif.com>
Mon, 3 Sep 2018 04:39:45 +0000 (04:39 +0000)
Convenience function to fill a buffer with random bytes.

Add some unit tests (only sanity checks, really.)

12 files changed:
components/bootloader_support/src/bootloader_random.c
components/esp32/hw_random.c
components/esp32/include/esp_system.h
components/esp32/test/test_random.c [new file with mode: 0644]
components/fatfs/test/test_fatfs_sdmmc.c
components/fatfs/test/test_fatfs_spiflash.c
components/libsodium/port/randombytes_esp32.c
components/mbedtls/port/esp_hardware.c
components/newlib/random.c
components/wpa_supplicant/port/os_xtensa.c
examples/bluetooth/blufi/main/blufi_security.c
examples/wifi/espnow/main/espnow_example_main.c

index 36628e6c4f5a64ba9854dba212f2aaa2191ecbb6..eb34d6add2ea1a7f2de56fe5333c6b39f922ec8a 100644 (file)
 
 #ifndef BOOTLOADER_BUILD
 #include "esp_system.h"
-#endif
 
+void bootloader_fill_random(void *buffer, size_t length)
+{
+    return esp_fill_random(buffer, length);
+}
+
+#else
 void bootloader_fill_random(void *buffer, size_t length)
 {
     uint8_t *buffer_bytes = (uint8_t *)buffer;
     uint32_t random;
-#ifdef BOOTLOADER_BUILD
     uint32_t start, now;
-#endif
+
+    assert(buffer != NULL);
 
     for (int i = 0; i < length; i++) {
         if (i == 0 || i % 4 == 0) { /* redundant check is for a compiler warning */
-#ifdef BOOTLOADER_BUILD
             /* in bootloader with ADC feeding HWRNG, we accumulate 1
                bit of entropy per 40 APB cycles (==80 CPU cycles.)
 
@@ -49,14 +53,12 @@ void bootloader_fill_random(void *buffer, size_t length)
                 random ^= REG_READ(WDEV_RND_REG);
                 RSR(CCOUNT, now);
             } while(now - start < 80*32*2); /* extra factor of 2 is precautionary */
-#else
-            random = esp_random();
-#endif
         }
 
         buffer_bytes[i] = random >> ((i % 4) * 8);
     }
 }
+#endif // BOOTLOADER_BUILD
 
 void bootloader_random_enable(void)
 {
index 78ad542fc2637d385c8e3b51586669a172273667..1bd0e6300afd4c99fbc8b6f33625c4f5302de8ae 100644 (file)
@@ -16,6 +16,7 @@
 #include <stdint.h>
 #include <stddef.h>
 #include <string.h>
+#include <sys/param.h>
 #include "esp_attr.h"
 #include "esp_clk.h"
 #include "soc/wdev_reg.h"
@@ -54,3 +55,16 @@ uint32_t IRAM_ATTR esp_random(void)
     last_ccount = ccount;
     return result ^ REG_READ(WDEV_RND_REG);
 }
+
+void esp_fill_random(void *buf, size_t len)
+{
+    assert(buf != NULL);
+    uint8_t *buf_bytes = (uint8_t *)buf;
+    while (len > 0) {
+        uint32_t word = esp_random();
+        uint32_t to_copy = MIN(sizeof(word), len);
+        memcpy(buf_bytes, &word, to_copy);
+        buf_bytes += to_copy;
+        len -= to_copy;
+    }
+}
index b43dbebb82f84652003bac435ea8b5fcd29bbef4..05214c8f57bef81e0ca1f089fb327f9bd126b502 100644 (file)
@@ -151,18 +151,31 @@ uint32_t esp_get_minimum_free_heap_size( void );
 /**
  * @brief  Get one random 32-bit word from hardware RNG
  *
- * The hardware RNG is fully functional whenever an RF subsystem is running (ie Bluetooth or WiFi is enabled). For secure
+ * The hardware RNG is fully functional whenever an RF subsystem is running (ie Bluetooth or WiFi is enabled). For
  * random values, call this function after WiFi or Bluetooth are started.
  *
- * When the app is running without an RF subsystem enabled, it should be considered a PRNG. To help improve this
- * situation, the RNG is pre-seeded with entropy while the IDF bootloader is running. However no new entropy is
- * available during the window of time between when the bootloader exits and an RF subsystem starts. It may be possible
- * to discern a non-random pattern in a very large amount of output captured during this window of time.
+ * If the RF subsystem is not used by the program, the function bootloader_random_enable() can be called to enable an
+ * entropy source. bootloader_random_disable() must be called before RF subsystem or I2S peripheral are used. See these functions'
+ * documentation for more details.
+ *
+ * Any time the app is running without an RF subsystem (or bootloader_random) enabled, RNG hardware should be
+ * considered a PRNG. A very small amount of entropy is available due to pre-seeding while the IDF
+ * bootloader is running, but this should not be relied upon for any use.
  *
  * @return Random value between 0 and UINT32_MAX
  */
 uint32_t esp_random(void);
 
+/**
+ * @brief Fill a buffer with random bytes from hardware RNG
+ *
+ * @note This function has the same restrictions regarding available entropy as esp_random()
+ *
+ * @param buf Pointer to buffer to fill with random numbers.
+ * @param len Length of buffer in bytes
+ */
+void esp_fill_random(void *buf, size_t len);
+
 /**
   * @brief  Set base MAC address with the MAC address which is stored in BLK3 of EFUSE or
   *         external storage e.g. flash and EEPROM.
diff --git a/components/esp32/test/test_random.c b/components/esp32/test/test_random.c
new file mode 100644 (file)
index 0000000..b0ce1a0
--- /dev/null
@@ -0,0 +1,67 @@
+#include <stdio.h>
+#include <string.h>
+#include "unity.h"
+#include "esp_system.h"
+
+/* Note: these are just sanity tests, not the same as
+   entropy tests
+*/
+
+TEST_CASE("call esp_random()", "[random]")
+{
+    const size_t NUM_RANDOM = 128; /* in most cases this is massive overkill */
+
+    uint32_t zeroes = UINT32_MAX;
+    uint32_t ones = 0;
+    for (int i = 0; i < NUM_RANDOM - 1; i++) {
+        uint32_t r = esp_random();
+        ones |= r;
+        zeroes &= ~r;
+    }
+
+    /* assuming a 'white' random distribution, we can expect
+       usually at least one time each bit will be zero and at
+       least one time each will be one. Statistically this
+       can still fail, just *very* unlikely to. */
+    TEST_ASSERT_EQUAL_HEX32(0, zeroes);
+    TEST_ASSERT_EQUAL_HEX32(UINT32_MAX, ones);
+}
+
+TEST_CASE("call esp_fill_random()", "[random]")
+{
+    const size_t NUM_BUF = 200;
+    const size_t BUF_SZ = 16;
+    uint8_t buf[NUM_BUF][BUF_SZ];
+    uint8_t zero_buf[BUF_SZ];
+    uint8_t one_buf[BUF_SZ];
+
+    bzero(buf, sizeof(buf));
+    bzero(one_buf, sizeof(zero_buf));
+    memset(zero_buf, 0xFF, sizeof(one_buf));
+
+    for (int i = 0; i < NUM_BUF; i++) {
+        esp_fill_random(buf[i], BUF_SZ);
+    }
+    /* No two 128-bit buffers should be the same
+       (again, statistically this could happen but it's very unlikely) */
+    for (int i = 0; i < NUM_BUF; i++) {
+        for (int j = 0; j < NUM_BUF; j++) {
+            if (i != j) {
+                TEST_ASSERT_NOT_EQUAL(0, memcmp(buf[i], buf[j], BUF_SZ));
+            }
+        }
+    }
+
+    /* Do the same all bits are zero and one at least once test across the buffers */
+    for (int i = 0; i < NUM_BUF; i++) {
+        for (int x = 0; x < BUF_SZ; x++) {
+            zero_buf[x] &= ~buf[i][x];
+            one_buf[x] |= buf[i][x];
+        }
+    }
+    for (int x = 0; x < BUF_SZ; x++) {
+        TEST_ASSERT_EQUAL_HEX8(0, zero_buf[x]);
+        TEST_ASSERT_EQUAL_HEX8(0xFF, one_buf[x]);
+    }
+}
+
index 2ab31f087e1960e78229347c40fe528a1df23555..74ef2207b78d11998c7ea5e4bf9497e3417ae2cc 100644 (file)
@@ -167,9 +167,7 @@ TEST_CASE("(SD) write/read speed test", "[fatfs][sd][test_env=UT_T1_SDMODE][time
 
     const size_t buf_size = 16 * 1024;
     uint32_t* buf = (uint32_t*) calloc(1, buf_size);
-    for (size_t i = 0; i < buf_size / 4; ++i) {
-        buf[i] = esp_random();
-    }
+    esp_fill_random(buf, buf_size);
     const size_t file_size = 1 * 1024 * 1024;
 
     speed_test(buf, 4 * 1024, file_size, true);
index 38ea765ddc5e05018342b410c9e4a6d231a50ca2..9d07249480e128aaf399b4acb4a5ab0774e8dacb 100644 (file)
@@ -162,9 +162,7 @@ TEST_CASE("(WL) write/read speed test", "[fatfs][wear_levelling][timeout=60]")
 
     const size_t buf_size = 16 * 1024;
     uint32_t* buf = (uint32_t*) calloc(1, buf_size);
-    for (size_t i = 0; i < buf_size / 4; ++i) {
-        buf[i] = esp_random();
-    }
+    esp_fill_random(buf, buf_size);
     const size_t file_size = 256 * 1024;
     const char* file = "/spiflash/256k.bin";
 
index 9ff5493cc4fe59f037543c931208b328e1797a6f..73c77ec6924f5243c9e5d159db5e0b540a4ce6fc 100644 (file)
 #include "randombytes_default.h"
 #include "esp_system.h"
 
-static void randombytes_esp32_random_buf(void * const buf, const size_t size)
-{
-    uint8_t *p = (uint8_t *)buf;
-    for (size_t i = 0; i < size; i++) {
-        p[i] = esp_random();
-    }
-}
-
 static const char *randombytes_esp32_implementation_name(void)
 {
     return "esp32";
@@ -39,7 +31,7 @@ const struct randombytes_implementation randombytes_esp32_implementation = {
     .random = esp_random,
     .stir = NULL,
     .uniform = NULL,
-    .buf = randombytes_esp32_random_buf,
+    .buf = esp_fill_random,
     .close = NULL,
 };
 
index 9157662491047ef0f4480a080a2239f6c0621638..09ededb186e39a0c450cd61dfcaa745ad4b38a93 100644 (file)
@@ -7,17 +7,18 @@
 #include <sys/types.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <esp_system.h>
 
-#if defined(MBEDTLS_ENTROPY_HARDWARE_ALT)
+#ifndef MBEDTLS_ENTROPY_HARDWARE_ALT
+#error "MBEDTLS_ENTROPY_HARDWARE_ALT should always be set in ESP-IDF"
+#endif
 
-extern int os_get_random(unsigned char *buf, size_t len);
 int mbedtls_hardware_poll( void *data,
                            unsigned char *output, size_t len, size_t *olen )
 {
-    os_get_random(output, len);
+    esp_fill_random(output, len);
     *olen = len;
-
     return 0;
 }
-#endif
+
 
index 47ebb23ca803ff35d88b21fe19d21e889888e539..5d3e6175c8f48b9047a5f3b355545d652d2aa27e 100644 (file)
@@ -36,17 +36,8 @@ ssize_t getrandom(void *buf, size_t buflen, unsigned int flags)
         return -1;
     }
 
-    uint8_t *dst = (uint8_t *) buf;
-    ssize_t ret = 0;
+    esp_fill_random(buf, buflen);
 
-    while (ret < buflen) {
-        const uint32_t random = esp_random();
-        const int needed = buflen - ret;
-        const int copy_len = MIN(sizeof(random), needed);
-        memcpy(dst + ret, &random, copy_len);
-        ret += copy_len;
-    }
-
-    ESP_LOGD(TAG, "getrandom returns %d", ret);
-    return ret;
+    ESP_LOGD(TAG, "getrandom returns %d", buflen);
+    return buflen;
 }
index 9ecde1f1138fe7601716fac9fd81e76a9782753e..7c2177c4d44690e5ceca353c5297db1b62e1eced 100644 (file)
@@ -39,26 +39,9 @@ unsigned long os_random(void)
     return esp_random();
 }
 
-unsigned long r_rand(void) __attribute__((alias("os_random")));
-
-
 int os_get_random(unsigned char *buf, size_t len)
 {
-    int i, j;
-    unsigned long tmp;
-
-    for (i = 0; i < ((len + 3) & ~3) / 4; i++) {
-        tmp = r_rand();
-
-        for (j = 0; j < 4; j++) {
-            if ((i * 4 + j) < len) {
-                buf[i * 4 + j] = (uint8_t)(tmp >> (j * 8));
-            } else {
-                break;
-            }
-        }
-    }
-
+    esp_fill_random(buf, len);
     return 0;
 }
 
index c32d3ad491414ea38fd3bcfe475087f84d96a442..49ba6c1c695a64c28ef2d4bf7775537941abb853 100644 (file)
@@ -61,11 +61,7 @@ static struct blufi_security *blufi_sec;
 
 static int myrand( void *rng_state, unsigned char *output, size_t len )
 {
-    size_t i;
-
-    for( i = 0; i < len; ++i )
-        output[i] = esp_random();
-
+    esp_fill_random(output, len);
     return( 0 );
 }
 
index afb1428c52536b73fffd4aa8c52868f74113a27a..c4e949bff6fae6c3e4f1dd3bed50168412297f06 100644 (file)
@@ -144,7 +144,6 @@ int example_espnow_data_parse(uint8_t *data, uint16_t data_len, uint8_t *state,
 void example_espnow_data_prepare(example_espnow_send_param_t *send_param)
 {
     example_espnow_data_t *buf = (example_espnow_data_t *)send_param->buffer;
-    int i = 0;
 
     assert(send_param->len >= sizeof(example_espnow_data_t));
 
@@ -153,9 +152,8 @@ void example_espnow_data_prepare(example_espnow_send_param_t *send_param)
     buf->seq_num = s_example_espnow_seq[buf->type]++;
     buf->crc = 0;
     buf->magic = send_param->magic;
-    for (i = 0; i < send_param->len - sizeof(example_espnow_data_t); i++) {
-        buf->payload[i] = (uint8_t)esp_random();
-    }
+    /* Fill all remaining bytes after the data with random values */
+    esp_fill_random(buf->payload, send_param->len - sizeof(example_espnow_data_t));
     buf->crc = crc16_le(UINT16_MAX, (uint8_t const *)buf, send_param->len);
 }