💾 Archived View for lasse.nerdcamp.net › gemlog › 20230812.gmi captured on 2024-05-10 at 10:27:44. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-12-28)
-=-=-=-=-=-=-
Out of curiosity, and to use up some of my big bunch of quad op amps, I decided to build a hardware random number generator. It is comprised of 16 individual 12V-Zener-Diode noise generators, which feed into 8 comparators to generate 8 output bits at once. Those 8 bits can be sampled as a single byte by a microcontroller and then sent over UART to a PC. The principle is based on the Lampert circuit, where two uncorrelated noise sources are fed into a single comparator, to avoid biasing problems due to a shifting operating point in the Zener noise sources.
To avoid having to use an ADC, I decided to simply sample the 0's and 1's that the comparator outputs provide. Lest I have to wait for 8 samples to fill a whole byte, I simply made 8 individual and uncorrelated noise generators that can be sampled at once. With a UART transmit baud rate of ~0.9 megabaud, the generator's outputs are sampled at ~90kHz, which is easily handled using jellybean op amps and comparators like the TL074 and LM339. The "big red button" allows to divide the baud rate down in steps of two, down to only 3600 baud, and thus reduce the sample rate accordingly.
The most important part of the schematic
The Zener generators are powered by ~15V, which are generated by a boost converter from the 5V USB supply, which powers the whole shebang. The booster is not shown on the schematic, but it is a bog standard MC34063 affair. With a correctly chosen current limit resistor, it is electrically silent and fairly efficient, too. No need for the latest fancy SMD stuff here.
The hardware is actually finished for more than a year now, but I haven't used it for anything serious since. Just yesterday my interest was piqued again though by the following article:
I am totally not into anything cryptocurrency-related, and probably never will (in fact, I firmly reject Bitcoin and similar stuff), but I am interested in generating strong cryptographic keys for SSH, PGP and LUKS. One time pads are interesting, too, but I haven't had any use for them to this day.
For testing, I have recorded 20MiB of data. The histogram of a resulting image file looks like this:
The result of ''ent'' is this:
Entropy = 7.885903 bits per byte. Optimum compression would reduce the size of this 20971520 byte file by 1 percent. Chi square distribution for 20971520 samples is 3483056.84, and randomly would exceed this value less than 0.01 percent of the times. Arithmetic mean value of data bytes is 110.9453 (127.5 = random). Monte Carlo value for Pi is 3.469126841 (error 10.43 percent). Serial correlation coefficient is -0.023079 (totally uncorrelated = 0.0).
Certainly not perfect. So I fed little chunks of 128 raw bytes into SHA512, to get 64 ''whitened'' bytes out. Here's the resulting histogram:
And the ''ent''-result:
Entropy = 7.999982 bits per byte. Optimum compression would reduce the size of this 10485760 byte file by 0 percent. Chi square distribution for 10485760 samples is 261.24, and randomly would exceed this value 38.07 percent of the times. Arithmetic mean value of data bytes is 127.5306 (127.5 = random). Monte Carlo value for Pi is 3.140525490 (error 0.03 percent). Serial correlation coefficient is 0.000154 (totally uncorrelated = 0.0).
Looks quite useable to me, now - but then again, what do I know?
What do you think? Any opinions, please reach out and let me know!
Ruminating about the performace of the HRNG for another two days, I came up with a little firmware update. Like any security related microcode updates these days, it came with a big hit in performance - a drop of about 12% in throughput, down to 73,7 kB/s at 921600 baud. Previously I would just read in the whole hardware port (1 clock cycle) and immediately put it out on the UART (20 clock cycles all-in-all). Now I am XOR-ing the newly acquired byte with the previous one, and then put that result out on the UART. This brings three more clock cycles to the table, one for the XOR and two for rotating through the buffer, yielding 23 clock cycles for 10 baud instead of only 20 from before. The resulting histogram looks a lot nicer though:
And the ''ent''-result:
Entropy = 7.998592 bits per byte. Optimum compression would reduce the size of this 20971520 byte file by 0 percent. Chi square distribution for 20971520 samples is 41064.68, and randomly would exceed this value less than 0.01 percent of the times. Arithmetic mean value of data bytes is 125.1412 (127.5 = random). Monte Carlo value for Pi is 3.193315048 (error 1.65 percent). Serial correlation coefficient is 0.020558 (totally uncorrelated = 0.0).
And I guess throwing 1024 bits at SHA-512 is a little overkill; 768 bits ought to be enough, maybe even less than that.
Squashed a serious bug. I really messed up the serial transmit routine on the AVR. Until a couple of days ago, I didn't realize that CBI and SBI actually take two clock cycles to execute on the processor that I chose, and not just one cycle as I wrongly assumed. This introduced a timing error on the stop bit, amounting to a whopping 5%. That would work okay for a couple of characters, but since I'm banging out bytes as fast as possible, that would lead to an error every couple of bytes. Those errors probably occurred somewhat regularly and thus contributed the sawtoothy-ness that is easily seen on the histograms. Now that I have fixed the baud rate error (and finally double-checked it with a logic analyzer), the histogram looks much better, with the top much more like random noise and no regular sawtooth anymore.
Things I've read:
https://reallyreallyrandom.com
Robust, low-cost, auditable random number generation for embedded system security
________________________________________________________________________________________
preamp's gemlog on 2023-08-12,
an overcast saturday in august