proof-of-concept: generate random bits on LPC1768 using dueling clocks (systick and WDT/RTC)
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).
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
Diff: main.cpp
- 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); + } +}