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

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);
    }
}