Hardware Random Number Generator
December 8, 2015
I initially wrote the solution using Python on my Raspberry Pi, but here I’ll give a solution in Scheme that reads from /dev/random. Our first function gives a key suitable for the Diana Cryptosystem by generating 32-bit random integers and scaling them onto the 26-character alphabet:
(define (rand-letter)
(with-input-from-file "/dev/random" (lambda ()
(do ((i 4 (- i 1)) (r 0 (+ (* r 256) (char->integer (read-char)))))
((zero? i) (integer->char (+ (modulo r 26) 65)))))))
(define (keypad n)
(do ((n n (- n 1)) (cs (list) (cons (rand-letter) cs)))
((zero? n) (list->string cs))))
And here is the trigraph used for encryption and decryption:
(define (trigraph msg key)
(define (i->a i) (integer->char (+ i 65)))
(define (a->i a) (- (char->integer a) 65))
(define (tri c k) (i->a (modulo (- 25 (a->i k) (a->i c)) 26)))
(let* ((msg (string->list msg))
(key (take (length msg) (string->list key))))
(list->string (map tri msg key))))
Here we give a simple example:
> (define key (keypad 25)) > key "HDFHIJNULLPVJNHTNKNBAXPYR" > (trigraph "ATTACKATDAWN" key) "SDBSPGMMLOOR" > (trigraph "SDBSPGMMLOOR" key) "ATTACKATDAWN"
We used take from the Standard Prelude. You can run the program at http://ideone.com/UVOd8M.
Aha! Now I can generate cryptographically secure messages in the shell! Wonder what that says:
I learned (man 4 random) that /dev/random can block, but my laptop’s entropy pool seems to stay above 3000 bits; a server with other users seems to have around 180 bits of available entropy; these keep changing whether I read the device or not; both have poolsize 4096.But I don’t really know if these are working as intended. Interesting anyway.
Apparently it’s not polite to consume much hardware entropy in a machine with other users. The above was on my personal laptop. But as I said, it was barely noticeable.
Maybe I do have a dedicated entropy generator? And a backdoor so that the NSA can read my message, while I myself cannot? That’s not fair :)
Nice problem, didn’t know the Pi had a hardware RNG. This seems to be the only actual documentation around: http://pastehtml.com/view/crkxyohmp.rtxt – as well as looking at the driver source (bcm2708-rng.c and bcm2835rng.c by Lubomir Rintel). Here’s a little program that memory maps the control registers rather than using the device & which generates random Unicode I-Ching hexagrams (we should use Unicode more); doing a full startup & shutdown each time probably isn’t necessary. Takes 8 secs to generate a million hexagrams (including startup time). I’ve only tried in on a Pi 2, on the Pi 1 the I/O registers are at a different place – I think the commented out line for IO_BASE is correct but haven’t checked.
#include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <locale.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> #define CHECK(e) ((e)?(void)0:(perror(#e), abort())) #define BLOCK_SIZE 4096 //#define IO_BASE 0x20000000 // pi v1? #define IO_BASE 0x3F000000 // pi v2 // Use 32-bit addressing, hence the shifts #define RNG_CTRL (0x0>>2) #define RNG_STATUS (0x4>>2) #define RNG_DATA (0x8>>2) #define RNG_FF_THRESHOLD (0xc>>2) #define RNG_BASE (IO_BASE + 0x00104000) #define RNG_RBGEN 0x1 // enable rng #define RNG_RBG2X 0x2 // double speed, 'less random' mode #define RNG_WARMUP_COUNT 0x40000 // discard initial numbers generated volatile uint32_t *rng_base = NULL; // Register memory map uint32_t getrand() { while ((rng_base[RNG_STATUS] >> 24) == 0); return rng_base[RNG_DATA]; } int main (int argc, char *argv[]) { setlocale(LC_ALL,""); int n = atoi(argv[1]); int fd = open ("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC); CHECK(fd >= 0); rng_base = (uint32_t*)mmap(0, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, RNG_BASE); CHECK(rng_base != (void*)-1); rng_base[RNG_STATUS] = RNG_WARMUP_COUNT; rng_base[RNG_CTRL] = RNG_RBGEN /*| RNG_RBG2X*/; uint64_t t = 0; int nbits = 0; for (int i = 1; i <= n; i++) { if (nbits < 6) { t |= (uint64_t)getrand() << nbits; nbits += 32; } printf("%lc ", (int)(0x4DC0 + (t & ((1<<6)-1)))); t >>= 6; nbits -= 6; if (i == n || i%16 == 0) printf("\n"); } rng_base[RNG_CTRL] = 0; }