proof-of-concept: generate random bits on LPC1768 using dueling clocks (systick and WDT/RTC)

Dependencies:   mbed

random bits from dueling clocks

Using dueling clocks to generate random bits is described by Walter Anderson at https://sites.google.com/site/astudyofentropy/project-definition/timer-jitter-entropy-sources/entropy-library

There are implementations for AVR(UNO etc.) and ARM-based (teensy) MCUs.

This mbed LPC1768 implementation uses systick clock versus the RTC crystal (32khz) as a source to the WDT timer. The LPC1768 WDT interrupt cannot be cleared, so this implementation generates the random bits when they are requested. The WDT scales the source clock by /4, so the random bit rate is about 8192 bits/second. If your board doesn't have a 32khz crystal, it is also possible to source the WDT from the 4 MHz IRC oscillator.

I collected several megabytes of random bits and they passed various random-bit testers (rngtest, ent, NIST's STS).

/media/uploads/manitou/mbed.png

Another mbed random bit generator using ADC noise and mixing with SHA256 is desribed at https://developer.mbed.org/users/Remco/notebook/secure-hardware-random-number-using-the-mbed and an mbed teensy 3.1 generator

One could also just use these generators to create a seed for a hash-based PRNG.

Some ARM chips have builtin hardware TRNG's (DUE, pyboard, Raspberry PI) and Intel Edison.

FYI, RNG data on other MCUs https://github.com/manitou48/DUEZoo/blob/master/RNGperf.txt

and Anderson's spreadsheet

https://docs.google.com/spreadsheet/pub?key=0AukiKiYKrSl9dHNIX19oZ0ZqNDc1RDNMa042SzhZT0E&output=html

Revision:
0:808fc29f4d37
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Sat Jul 25 15:27:38 2015 +0000
@@ -0,0 +1,146 @@
+
+// random bits from systick and WDT
+// based on arduino/teensy Entropy lib
+// https://sites.google.com/site/astudyofentropy/project-definition/timer-jitter-entropy-sources/entropy-library
+// seems WDT ISR will only fire once, so check free-running WDT periodically
+// enable systick as free running 24-bit clock to beat against RTC crystal
+#include "mbed.h"
+
+Timer tmr;
+Serial pc(USBTX, USBRX);
+
+void kick() {
+    // restart WDT countdown
+    __disable_irq();
+    LPC_WDT->WDFEED = 0xAA;
+    LPC_WDT->WDFEED = 0x55;
+    __enable_irq();
+}
+
+void wdt_init() {
+    LPC_WDT->WDCLKSEL = 0x02;               // Set CLK src 0 IRC  1 PCLK   2 RTC
+    LPC_WDT->WDTC = 0xfffffff;    // max countdown
+    LPC_WDT->WDMOD = 0x01;  // enable but no reset
+    kick();
+}
+
+void systick_init() {
+    SysTick->LOAD = 0x123456;   // doesn't really matter
+    SysTick->VAL  = 0;
+    SysTick->CTRL  = 4 |  1; //CLKSOURCE=CPU clock | ENABLE
+}
+
+#if 0
+volatile unsigned int rword;
+void ticker_isr() {
+    uint32_t t;
+    t= LPC_WDT->WDTV;
+    while (t == LPC_WDT->WDTV);  // wait til new value
+    rword = SysTick->VAL;
+}
+
+Ticker ticker;
+#endif
+
+// entropy collection
+ const uint8_t gWDT_buffer_SIZE=32;
+ const uint8_t WDT_POOL_SIZE=8;
+ uint8_t gWDT_buffer[gWDT_buffer_SIZE];
+ uint8_t gWDT_buffer_position;
+ uint8_t gWDT_loop_counter;
+ volatile uint8_t gWDT_pool_start;
+ volatile uint8_t gWDT_pool_end;
+ volatile uint8_t gWDT_pool_count;
+ volatile uint32_t gWDT_entropy_pool[WDT_POOL_SIZE];
+
+static void collect() {
+    uint32_t t;
+    t= LPC_WDT->WDTV;
+    while (t == LPC_WDT->WDTV);  // wait til new value
+    //  TODO kick WDT to avoid reset when counter < 100000 or schedule
+  gWDT_buffer[gWDT_buffer_position] = SysTick->VAL;
+  gWDT_buffer_position++; 
+  if (gWDT_buffer_position >= gWDT_buffer_SIZE)
+  {
+    gWDT_pool_end = (gWDT_pool_start + gWDT_pool_count) % WDT_POOL_SIZE;
+    // The following code is an implementation of Jenkin's one at a time hash
+    // This hash function has had preliminary testing to verify that it
+    // produces reasonably uniform random results when using WDT jitter
+    // on a variety of Arduino platforms
+    for(gWDT_loop_counter = 0; gWDT_loop_counter < gWDT_buffer_SIZE; ++gWDT_loop_counter)
+      {
+    gWDT_entropy_pool[gWDT_pool_end] += gWDT_buffer[gWDT_loop_counter];
+    gWDT_entropy_pool[gWDT_pool_end] += (gWDT_entropy_pool[gWDT_pool_end] << 10);
+    gWDT_entropy_pool[gWDT_pool_end] ^= (gWDT_entropy_pool[gWDT_pool_end] >> 6);
+      }
+    gWDT_entropy_pool[gWDT_pool_end] += (gWDT_entropy_pool[gWDT_pool_end] << 3);
+    gWDT_entropy_pool[gWDT_pool_end] ^= (gWDT_entropy_pool[gWDT_pool_end] >> 11);
+    gWDT_entropy_pool[gWDT_pool_end] += (gWDT_entropy_pool[gWDT_pool_end] << 15);
+    gWDT_entropy_pool[gWDT_pool_end] = gWDT_entropy_pool[gWDT_pool_end];
+    gWDT_buffer_position = 0; // Start collecting the next 32 bytes of Timer 1 counts
+    if (gWDT_pool_count == WDT_POOL_SIZE) // The entropy pool is full
+      gWDT_pool_start = (gWDT_pool_start + 1) % WDT_POOL_SIZE;
+    else // Add another unsigned long (32 bits) to the entropy pool
+      ++gWDT_pool_count;
+  }
+}
+
+
+uint32_t random() {
+    uint32_t retVal;
+
+    while (gWDT_pool_count < 1) collect(); // gather entropy
+    
+    __disable_irq();  // crtical section
+    retVal = gWDT_entropy_pool[gWDT_pool_start];
+    gWDT_pool_start = (gWDT_pool_start + 1) % WDT_POOL_SIZE;
+    --gWDT_pool_count;
+    __enable_irq();
+    return(retVal);
+}
+
+#define REPS 50
+void display() {
+    uint32_t r,t;
+    int i;
+    float bps;
+
+    t=tmr.read_us();
+    for (i=0;i<REPS;i++)r=random();
+    t= tmr.read_us() -t;
+    bps = REPS*32.e6/t;
+    printf("%f bps  %0x\n",bps,r);
+    wait(3.0);
+}
+
+void logger() {
+    // await start byte from host then start sending random numbers
+    //  ./logger 5000000  for diehard tests
+    unsigned int rng;
+    char *bytes = (char *) &rng;
+
+    pc.getc();    // await byte from host
+    while(1) {
+        rng = random();
+        for (int i=0; i<4;i++) pc.putc(bytes[i]);
+    }
+}
+
+int main() {
+
+    gWDT_buffer_position=0;
+    gWDT_pool_start = 0;
+    gWDT_pool_end = 0;
+    gWDT_pool_count = 0;
+    tmr.start();
+    systick_init();
+    wdt_init();
+
+ //   ticker.attach_us(&ticker_isr,1000);  // 1/8192  122 us
+
+    while(1) {
+        display(); 
+      //   logger();
+   //     printf("rword %x\n",rword); wait(3.0);
+    }
+}