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@0:808fc29f4d37, 2015-07-25 (annotated)
- Committer:
- manitou
- Date:
- Sat Jul 25 15:27:38 2015 +0000
- Revision:
- 0:808fc29f4d37
initial
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
manitou | 0:808fc29f4d37 | 1 | |
manitou | 0:808fc29f4d37 | 2 | // random bits from systick and WDT |
manitou | 0:808fc29f4d37 | 3 | // based on arduino/teensy Entropy lib |
manitou | 0:808fc29f4d37 | 4 | // https://sites.google.com/site/astudyofentropy/project-definition/timer-jitter-entropy-sources/entropy-library |
manitou | 0:808fc29f4d37 | 5 | // seems WDT ISR will only fire once, so check free-running WDT periodically |
manitou | 0:808fc29f4d37 | 6 | // enable systick as free running 24-bit clock to beat against RTC crystal |
manitou | 0:808fc29f4d37 | 7 | #include "mbed.h" |
manitou | 0:808fc29f4d37 | 8 | |
manitou | 0:808fc29f4d37 | 9 | Timer tmr; |
manitou | 0:808fc29f4d37 | 10 | Serial pc(USBTX, USBRX); |
manitou | 0:808fc29f4d37 | 11 | |
manitou | 0:808fc29f4d37 | 12 | void kick() { |
manitou | 0:808fc29f4d37 | 13 | // restart WDT countdown |
manitou | 0:808fc29f4d37 | 14 | __disable_irq(); |
manitou | 0:808fc29f4d37 | 15 | LPC_WDT->WDFEED = 0xAA; |
manitou | 0:808fc29f4d37 | 16 | LPC_WDT->WDFEED = 0x55; |
manitou | 0:808fc29f4d37 | 17 | __enable_irq(); |
manitou | 0:808fc29f4d37 | 18 | } |
manitou | 0:808fc29f4d37 | 19 | |
manitou | 0:808fc29f4d37 | 20 | void wdt_init() { |
manitou | 0:808fc29f4d37 | 21 | LPC_WDT->WDCLKSEL = 0x02; // Set CLK src 0 IRC 1 PCLK 2 RTC |
manitou | 0:808fc29f4d37 | 22 | LPC_WDT->WDTC = 0xfffffff; // max countdown |
manitou | 0:808fc29f4d37 | 23 | LPC_WDT->WDMOD = 0x01; // enable but no reset |
manitou | 0:808fc29f4d37 | 24 | kick(); |
manitou | 0:808fc29f4d37 | 25 | } |
manitou | 0:808fc29f4d37 | 26 | |
manitou | 0:808fc29f4d37 | 27 | void systick_init() { |
manitou | 0:808fc29f4d37 | 28 | SysTick->LOAD = 0x123456; // doesn't really matter |
manitou | 0:808fc29f4d37 | 29 | SysTick->VAL = 0; |
manitou | 0:808fc29f4d37 | 30 | SysTick->CTRL = 4 | 1; //CLKSOURCE=CPU clock | ENABLE |
manitou | 0:808fc29f4d37 | 31 | } |
manitou | 0:808fc29f4d37 | 32 | |
manitou | 0:808fc29f4d37 | 33 | #if 0 |
manitou | 0:808fc29f4d37 | 34 | volatile unsigned int rword; |
manitou | 0:808fc29f4d37 | 35 | void ticker_isr() { |
manitou | 0:808fc29f4d37 | 36 | uint32_t t; |
manitou | 0:808fc29f4d37 | 37 | t= LPC_WDT->WDTV; |
manitou | 0:808fc29f4d37 | 38 | while (t == LPC_WDT->WDTV); // wait til new value |
manitou | 0:808fc29f4d37 | 39 | rword = SysTick->VAL; |
manitou | 0:808fc29f4d37 | 40 | } |
manitou | 0:808fc29f4d37 | 41 | |
manitou | 0:808fc29f4d37 | 42 | Ticker ticker; |
manitou | 0:808fc29f4d37 | 43 | #endif |
manitou | 0:808fc29f4d37 | 44 | |
manitou | 0:808fc29f4d37 | 45 | // entropy collection |
manitou | 0:808fc29f4d37 | 46 | const uint8_t gWDT_buffer_SIZE=32; |
manitou | 0:808fc29f4d37 | 47 | const uint8_t WDT_POOL_SIZE=8; |
manitou | 0:808fc29f4d37 | 48 | uint8_t gWDT_buffer[gWDT_buffer_SIZE]; |
manitou | 0:808fc29f4d37 | 49 | uint8_t gWDT_buffer_position; |
manitou | 0:808fc29f4d37 | 50 | uint8_t gWDT_loop_counter; |
manitou | 0:808fc29f4d37 | 51 | volatile uint8_t gWDT_pool_start; |
manitou | 0:808fc29f4d37 | 52 | volatile uint8_t gWDT_pool_end; |
manitou | 0:808fc29f4d37 | 53 | volatile uint8_t gWDT_pool_count; |
manitou | 0:808fc29f4d37 | 54 | volatile uint32_t gWDT_entropy_pool[WDT_POOL_SIZE]; |
manitou | 0:808fc29f4d37 | 55 | |
manitou | 0:808fc29f4d37 | 56 | static void collect() { |
manitou | 0:808fc29f4d37 | 57 | uint32_t t; |
manitou | 0:808fc29f4d37 | 58 | t= LPC_WDT->WDTV; |
manitou | 0:808fc29f4d37 | 59 | while (t == LPC_WDT->WDTV); // wait til new value |
manitou | 0:808fc29f4d37 | 60 | // TODO kick WDT to avoid reset when counter < 100000 or schedule |
manitou | 0:808fc29f4d37 | 61 | gWDT_buffer[gWDT_buffer_position] = SysTick->VAL; |
manitou | 0:808fc29f4d37 | 62 | gWDT_buffer_position++; |
manitou | 0:808fc29f4d37 | 63 | if (gWDT_buffer_position >= gWDT_buffer_SIZE) |
manitou | 0:808fc29f4d37 | 64 | { |
manitou | 0:808fc29f4d37 | 65 | gWDT_pool_end = (gWDT_pool_start + gWDT_pool_count) % WDT_POOL_SIZE; |
manitou | 0:808fc29f4d37 | 66 | // The following code is an implementation of Jenkin's one at a time hash |
manitou | 0:808fc29f4d37 | 67 | // This hash function has had preliminary testing to verify that it |
manitou | 0:808fc29f4d37 | 68 | // produces reasonably uniform random results when using WDT jitter |
manitou | 0:808fc29f4d37 | 69 | // on a variety of Arduino platforms |
manitou | 0:808fc29f4d37 | 70 | for(gWDT_loop_counter = 0; gWDT_loop_counter < gWDT_buffer_SIZE; ++gWDT_loop_counter) |
manitou | 0:808fc29f4d37 | 71 | { |
manitou | 0:808fc29f4d37 | 72 | gWDT_entropy_pool[gWDT_pool_end] += gWDT_buffer[gWDT_loop_counter]; |
manitou | 0:808fc29f4d37 | 73 | gWDT_entropy_pool[gWDT_pool_end] += (gWDT_entropy_pool[gWDT_pool_end] << 10); |
manitou | 0:808fc29f4d37 | 74 | gWDT_entropy_pool[gWDT_pool_end] ^= (gWDT_entropy_pool[gWDT_pool_end] >> 6); |
manitou | 0:808fc29f4d37 | 75 | } |
manitou | 0:808fc29f4d37 | 76 | gWDT_entropy_pool[gWDT_pool_end] += (gWDT_entropy_pool[gWDT_pool_end] << 3); |
manitou | 0:808fc29f4d37 | 77 | gWDT_entropy_pool[gWDT_pool_end] ^= (gWDT_entropy_pool[gWDT_pool_end] >> 11); |
manitou | 0:808fc29f4d37 | 78 | gWDT_entropy_pool[gWDT_pool_end] += (gWDT_entropy_pool[gWDT_pool_end] << 15); |
manitou | 0:808fc29f4d37 | 79 | gWDT_entropy_pool[gWDT_pool_end] = gWDT_entropy_pool[gWDT_pool_end]; |
manitou | 0:808fc29f4d37 | 80 | gWDT_buffer_position = 0; // Start collecting the next 32 bytes of Timer 1 counts |
manitou | 0:808fc29f4d37 | 81 | if (gWDT_pool_count == WDT_POOL_SIZE) // The entropy pool is full |
manitou | 0:808fc29f4d37 | 82 | gWDT_pool_start = (gWDT_pool_start + 1) % WDT_POOL_SIZE; |
manitou | 0:808fc29f4d37 | 83 | else // Add another unsigned long (32 bits) to the entropy pool |
manitou | 0:808fc29f4d37 | 84 | ++gWDT_pool_count; |
manitou | 0:808fc29f4d37 | 85 | } |
manitou | 0:808fc29f4d37 | 86 | } |
manitou | 0:808fc29f4d37 | 87 | |
manitou | 0:808fc29f4d37 | 88 | |
manitou | 0:808fc29f4d37 | 89 | uint32_t random() { |
manitou | 0:808fc29f4d37 | 90 | uint32_t retVal; |
manitou | 0:808fc29f4d37 | 91 | |
manitou | 0:808fc29f4d37 | 92 | while (gWDT_pool_count < 1) collect(); // gather entropy |
manitou | 0:808fc29f4d37 | 93 | |
manitou | 0:808fc29f4d37 | 94 | __disable_irq(); // crtical section |
manitou | 0:808fc29f4d37 | 95 | retVal = gWDT_entropy_pool[gWDT_pool_start]; |
manitou | 0:808fc29f4d37 | 96 | gWDT_pool_start = (gWDT_pool_start + 1) % WDT_POOL_SIZE; |
manitou | 0:808fc29f4d37 | 97 | --gWDT_pool_count; |
manitou | 0:808fc29f4d37 | 98 | __enable_irq(); |
manitou | 0:808fc29f4d37 | 99 | return(retVal); |
manitou | 0:808fc29f4d37 | 100 | } |
manitou | 0:808fc29f4d37 | 101 | |
manitou | 0:808fc29f4d37 | 102 | #define REPS 50 |
manitou | 0:808fc29f4d37 | 103 | void display() { |
manitou | 0:808fc29f4d37 | 104 | uint32_t r,t; |
manitou | 0:808fc29f4d37 | 105 | int i; |
manitou | 0:808fc29f4d37 | 106 | float bps; |
manitou | 0:808fc29f4d37 | 107 | |
manitou | 0:808fc29f4d37 | 108 | t=tmr.read_us(); |
manitou | 0:808fc29f4d37 | 109 | for (i=0;i<REPS;i++)r=random(); |
manitou | 0:808fc29f4d37 | 110 | t= tmr.read_us() -t; |
manitou | 0:808fc29f4d37 | 111 | bps = REPS*32.e6/t; |
manitou | 0:808fc29f4d37 | 112 | printf("%f bps %0x\n",bps,r); |
manitou | 0:808fc29f4d37 | 113 | wait(3.0); |
manitou | 0:808fc29f4d37 | 114 | } |
manitou | 0:808fc29f4d37 | 115 | |
manitou | 0:808fc29f4d37 | 116 | void logger() { |
manitou | 0:808fc29f4d37 | 117 | // await start byte from host then start sending random numbers |
manitou | 0:808fc29f4d37 | 118 | // ./logger 5000000 for diehard tests |
manitou | 0:808fc29f4d37 | 119 | unsigned int rng; |
manitou | 0:808fc29f4d37 | 120 | char *bytes = (char *) &rng; |
manitou | 0:808fc29f4d37 | 121 | |
manitou | 0:808fc29f4d37 | 122 | pc.getc(); // await byte from host |
manitou | 0:808fc29f4d37 | 123 | while(1) { |
manitou | 0:808fc29f4d37 | 124 | rng = random(); |
manitou | 0:808fc29f4d37 | 125 | for (int i=0; i<4;i++) pc.putc(bytes[i]); |
manitou | 0:808fc29f4d37 | 126 | } |
manitou | 0:808fc29f4d37 | 127 | } |
manitou | 0:808fc29f4d37 | 128 | |
manitou | 0:808fc29f4d37 | 129 | int main() { |
manitou | 0:808fc29f4d37 | 130 | |
manitou | 0:808fc29f4d37 | 131 | gWDT_buffer_position=0; |
manitou | 0:808fc29f4d37 | 132 | gWDT_pool_start = 0; |
manitou | 0:808fc29f4d37 | 133 | gWDT_pool_end = 0; |
manitou | 0:808fc29f4d37 | 134 | gWDT_pool_count = 0; |
manitou | 0:808fc29f4d37 | 135 | tmr.start(); |
manitou | 0:808fc29f4d37 | 136 | systick_init(); |
manitou | 0:808fc29f4d37 | 137 | wdt_init(); |
manitou | 0:808fc29f4d37 | 138 | |
manitou | 0:808fc29f4d37 | 139 | // ticker.attach_us(&ticker_isr,1000); // 1/8192 122 us |
manitou | 0:808fc29f4d37 | 140 | |
manitou | 0:808fc29f4d37 | 141 | while(1) { |
manitou | 0:808fc29f4d37 | 142 | display(); |
manitou | 0:808fc29f4d37 | 143 | // logger(); |
manitou | 0:808fc29f4d37 | 144 | // printf("rword %x\n",rword); wait(3.0); |
manitou | 0:808fc29f4d37 | 145 | } |
manitou | 0:808fc29f4d37 | 146 | } |