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
main.cpp
- Committer:
- manitou
- Date:
- 2015-07-25
- Revision:
- 0:808fc29f4d37
File content as of revision 0:808fc29f4d37:
// 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); } }