# Random Sequence Generator based on Avalanche Noise

## Introduction

After my experiments with a random sequence generator based on Chua Circuit, I started investigating other methods for building hardware random number generators. One of the simplest way to create truly random sequences uses avalanche noise in a reversed-biased p-n junction.

As everyone knows, the maximum reverse bias voltage that can be applied to a p-n diode is limited by breakdown. In fact, when a diode is reverse biased, a very little current flows and to a first order approximation we can consider the diode an open circuit. As the reverse voltage is increased, though, a point is reached where there is a dramatic increase in current. This rapid increase of the current under reverse bias is what characterize the breakdown and the corresponding applied voltage is referred to as the breakdown voltage.
There are two mechanisms that can cause breakdown, namely avalanche multiplication and quantum mechanical tunneling of carriers through the bandgap. In the first case we say the diode is in avalanche breakdown, and this usually occurs in lightly-doped pn-junctions where the depletion layer is long, while in the second case we talk about Zener breakdown, which occurs in heavily doped pn-junctions where the depletion layer is extremely thin. Neither of the two breakdown mechanisms is destructive. However heating caused by the large breakdown current and high breakdown voltage causes the diode to be destroyed unless sufficient heat sinking is provided. If you're interested, more information about this subject can be found in [1] and [2].

Avalanche noise is the noise produced when a junction diode is operated at the onset of avalanche breakdown. It occurs when carriers acquire enough kinetic energy under the influence of the strong electric field to create additional electron-hole pairs by colliding with the atoms in the crystal lattice. If this process happens to spill over into an avalanche effect, random noise spikes may be observed.

To create such noise one can use the base-emitter juction of a small signal npn transistor, because this junction has a relatively low breakdown voltage for many common devices. The amount of noise created will depend on the physical characteristics of the junction, such as used materials and doping levels. The first thing to do then is to measure the breakdown voltage and the resulting noise amplitude for some of the most common devices, and then decide which one is best suited for this purpose.

## Measuring breakdown voltage

The transistor datasheet usually lists the minimum base-emitter breakdown voltage for a particular emitter current (Ie), when the collector is open. Most of the time the actual value is quite different from this guaranteed minumum. For this reason, to measure this voltage I used the following circuit:

The circuit is composed by a constant current generator and a buffer, while the device under test is label as QX. The constant current generator comprises the two diodes D1 and D2, three resistors R1, R2 and R3 and a pnp transistor Q1. The base of Q1 is at a voltage equal to the sum of the voltage drops across the two diodes. If we suppose that one voltage drop cancels out the voltage drop of the emitter-base junction of Q1, we can see that the emitter current is approximately equal to Ie=0.6V/(R1+R2), where we have assumed a 0.6V forward voltage drop for the diodes. Now we know that Ic=αIe, where α is very close to unity, so we obtain a constan current whose level can be changed by varying R2. This current in injected into the emitter of the device under test, so as to drive its base-emitter junction into breakdown. Lastly the unity-gain buffer realized with an op-amp offers a very high input impedence and a very low output impedence, thus allowing us to precisely measure the breakdown voltage without worrying about obtaining a wrong value due to the internal resistance of the multimeter.
I have tested 7 different devices I had here, at 3 current levels (1μA, 10μA and 100μA), and for each one I have measured the breakdown voltage, and the noise amplitude generated. The results are summarized in the following table:

 Transistor VBD (I=1μA) VBD (I=10μA) VBD (I=100μA) Noise Amplitude (I=10μA) For I=[5-50]μA noise: BC107B 9.30V 9.30V 9.28V 200mVpp inc., max=10μA, dec. BC548A 8.44V 8.46V 8.45V 100mVpp constant BC547B 8.23V 8.22V 8.23V 100mVpp decreases BC547C 8.36V 8.35V 8.34V 120mVpp decreases BC546B 8.19V 8.21V 8.19V 120mVpp inc., max=10μA, dec. 2N3904 10.82V 10.80V 10.76V 400mVpp constant 2N2222A 7.20V 7.25V 7.23V 440mVpp constant

In this group, the 2N3904 is one of the transistors that generates more noise, but it is also the one with the highest breakdown voltage. Moreover, the noise "pattern" seems to change on the oscilloscope when changing the current. Nonetheless, I'm going to choose this one as a source for avalanche noise.

## White noise generator

There are several circuits on the net that use this principle to generate white noise and from it a truly random sequence. Most of them use non-linear amplifiers though, for example [3], where the Q3 transitor has a bias point that strongly depends on its β, and it is switched on and off by the noise signal (probably going into saturation too). This kind of design may be good to obtain a digital signal, but it's certainly not good to linearly amplify the noise. My goal was trying to design a circuit that could produce a symmetrical, zero-average noise signal from a reverse biased p-n junction in avalanche breakdown, and linearly amplify it to obtain white noise with about a 1MHz bandwidth (this kind of signal could be useful to test audio equipment, for example). From this clean noise, a digital signal can be extracted using a comparator and by sampling this digital signal a truly random bit sequence can then be obtained.
Here's a schematic diagram of my design (click on the images to enlarge):

Let's talk a bit about each block, but first some considerations about the suppy voltage. I chose the 2N3904 as a source for avalanche noise. From the table above we can see that the breakdown voltage for this particular device (and, for the one I had, which was manufactured by On Semiconductor) is 10.8V. This means the supply voltage cannot be less than 10.8V, but that's not enough, because with the configuration chosen it is clear that node 1 will be at about V1=Vbe+10.8=11.4V, assuming Vbe=0.6V. While 12V could work, I set the supply voltage to 13V, and any value in the range [13V, 15V] should work as well.

white noise generator: the first block we encounter is the white noise generator. It is composed by the two transistors Q1 and Q2 and the resistor R1. I found that the key to generate a symmetrical noise voltage with the 2N3904 is to bias it with a very low reverse current, in the order of 50nA. As already stated we have approximately 11.4V at node 1, and this means that the emitter current of Q2 is set by R1, Ie2=(Vcc-V1)/R1=10.6μA. This current divides in collector current (Ic2=αIe2) and base current (Ib2=Ic2/β): it is this base current that flows into the reverse biased emitter-base junction of Q1. Assuming a β=200 for Q2, we get Ib2=53nA. For a small signal, the input impedance of Q2 between its base and collector is high, about βR1, while the small signal gain should be close to unity for big values of R1: I have measured a 250mVpp noise voltage at the collector of Q2.

virtual ground: op-amps are tipically used with a dual supply, but since we are using a single supply here, we need to create a virtual ground at Vcc/2 to bias the following op-amp stages. This is easily done with the voltage divider composed by the two equal resistors R10 and R11, and the unity-gain buffer composed by U2. The additional capacitor C3 is used to filter out disturbs that could come from the supply rails. The virtual ground is labeled Vref in the schematic diagram. A note about the op-amps used: one can of course replace the two tl082 with a tl084, which is what I used in my experiments. The fourth op-amp left can be used to add another gain stage or a filter for example. If you leave it unused, it is a good practice to connect it as a buffer (in- to out) and connect in+ to virtual ground.

buffer: I have separated the generator block and the amplifier block with a buffer made up by 1/2 of U1, R2 and C1: this interposed buffer prevents the second circuit from loading the first circuit unacceptably. In fact, the input impedance of the op-amp is very high (typically >1MΩ), meaning that the input of the op-amp does not load down the noise generator and draws only minimal current from it. The resistor R2 connected to virtual ground is needed to bias the amplifier around that point, while the capacitor C1 blocks the dc component present at node 1.

amplifier: the signal coming from the buffer, is again decoupled by C2 and linearly amplified with the other half of U1: the gain of this stage is R5/R4=3.9, and this brings our initial 250mVpp signal at approximately 1Vpp. Again, this means 1Vpp around virtual ground, so our signal will be in the range [Vcc/2 - 0.5V, Vcc/2 + 0.5V]. If you have used another transistor to generate the avalanche noise it's probable that you'll get a different amplitude at node 8: if so just change the gain of this stage in order to obtain a 1Vpp signal. The analog chain ends here: these four blocks are all we need to generate and amplify the noise. The last block is going to be used to obtain a digital signal from this one.

current switch: this block is actually made up by a constant current generator (D1, D2, Q3, R6 and R7) and a differential pair (Q4, Q5) with resistive loads (R8, R9). Its job is to function as a comparator and to decide if the instantaneous noise voltage if positive or negative with respect to virtual ground. The constant current generator is similar to the one I've used for measuring the breakdown voltage: this time the generated current is equal to Ig=0.6V/R6=500μA.
The differential pair acts as a current switch, effectively steering the constant current on R8 or R9 according with instantaneous value of the voltage at the base of Q4. Ideally, when this voltage is Vref, the current Ig is split in half, and both Q4 and Q5 collectors are at 2.5V. When Vb4 ≥ Vref + 100mV, Q4 is off, VR8=0V and all the current Ig goes to R9, pulling the collector of Q5 to 5V. Lastly, when Vb4 ≤ Vref - 100mV, the opposite is true, Q5 is off, and VR8=5V while VR9=0.
Since we are driving the base of Q4 with a 1Vpp signal, we expect all the current Ig to go either to R8 or to R9, thus forming a digital signal. The high level of this signal can be changed by varying R8, allowing us to interface it with TTL or CMOS circuits working at any voltage in the [0, 5V] range. The values of R8, R9 and the current Ig can be changed if necessary, as long as the transistors in the differential pair are not allowed to go into saturation: if that happens, they would take a long time to turn off, slowing down the maximum speed of the digital signal. Lastly, it is important to choose Q4 and Q5 so that their β values are as close as possible in order to avoid mismatches in the behaviour of the differential pair.

## Spice simulations

As always, after the first pencil and paper design phase, I have played with the simulator a little until I was satisfied with the results of the simulation. All the nodes have been labeled with progressive numbers on the schematic diagram, and these numbers will be used in the netlist that you can download here. The simulations have been performed using ngspice. To launch the simulation run:

`\$ ngspice random-generator-avalanche.net`

The reverse-biased p-n junction has been modeled using a fixed voltage generator (the breakdown voltage) in series with a small signal voltage generator (the avalanche noise) with a pseudo-random waveform (trrandom generator in ngspice). The amplitude of this generator has been set to 250mVpp, according to the value measured on the circuit.
The following pictures show the frequency response (ac analysis) of the buffer and the amplifier together (v(8)/v(1)) and the time-domain signals v(8) (the output of the amplifier, noise) and v(13) (the collector of Q4, digital signal):

Good! On to building the circuit.

## Circuit test

Well, not much to say here really: I built the circuit on a breadboard, and it worked as soon as I powered it up. I measured a virtual ground voltage of 6.52V and the current delivered to the differential pair from the constant current generator was 525μA. The noise signal at the oscilloscope looks nice and symmetrical and the digital signal presents sharp rising/falling edges and seem to stay 50% of time time high, and 50% low, as we expected (top ch1: voltage at node 8, 500mV/div; bottom ch2: voltage at node 13, 1V/div; horizontal scale: 1ms/div):

## Acquiring the sequences

I would have liked to use a microcontroller to acquire the sequences, but I thought for the first experiments the Raspberry Pi would do. Before connecting the generator to the Raspberry Pi, we need to make sure that the voltage levels at the digital output are in the [0, 3.3V] range. As explained above, this level can be regulated by varying R8 while visualizing the signal on the oscilloscope. If you do not have an oscilloscope, you can use this simple trick: disconnect the base of Q4 from the output of the amplifier and, using a trimmer connected to Vcc and gnd, apply a variable voltage in the range [Vref - 1V, Vref + 1V] to the base of Q4. R8 should be tuned so that the voltage across it never exceeds 3.3V, or if you want to be safe 3V.
This time there is no need to minimize bias inherent in the operation of the generator, if all went well we should get a probability of 1s and 0s very close to 50%. Of course I'm going to assume that the bit-stream still contain bias and correlation, so a de-skewing technique for reducing bias and correlation will be used anyway.
On the software side I have basically used the same code I had written for the Chua random generator: at the beginning GPIO4 is configured as input, then one bit is read (and its value is optionally printed), next we wait for a delay and repeat. This delay has been set to 500μs, producing a 2000bit/s stream.
To minimize writings to the SD card the program used reads bits for an hour into a memory buffer and then appends this buffer to the output file. The process is repeated until you send SIGINT, in which case the partially full buffer is appended to the output file before the program terminates. It can be started via ssh and left running for long acquisitions using the following command:

`\$ nohup sudo ./avalanche_hour &`

The bitstream will be saved in ascii format, where each bit is either a "0" or a "1" ascii character, with no newlines between two bits. If necessary, for example to import this data in Matlab/Octave, the newlines can easily be added.
I have left the system running for 60 hours, thus acquiring a total of 432Mbit, and then split this big file in 4 slices of 108Mbit each:

## Basic tests

The same disclaimer I used for my previous random number generator still holds: I am by no means an expert in this field, but I have been trying to analyze the data collected to the best of my knowledge to make sure I didn't make any obvious mistake.

The first thing I did on the these sequences was testing them for systematic bias. To do this I divided a sequence in blocks of N=100000 bits and for each block an error was calculated using the following formula: ((number_of_ones - N/2) / (N/2)) * 100. If systematic bias is low, we expect these error values to be around 0. This analysis for the first sequence is shown in the following picture:

The absolute value of the error is ≤1%, except for one or two blocks out of 1800 (that can always be discarded), which is acceptable for some applications. So without de-skewing systematic bias is indeed very low.

The next test is a simple and easy way to visually check if a sequence looks random: a bitmap image is generated from the acquired bitstream, where each pixel of this image represents one bit. This is not, of course, a serious test, but it's enough to show if something is obviously wrong. The image can be generated with the command:

`\$ cat seq1_108000000_500us | ./make_bitmap 400 400`

The following table shows the images for all the sequences:

 Sequence 1 Sequence 2 Sequence 3 Sequence 4

These bitmaps do not show any patter, and they look indistinguishable from white noise to the human eye, as expected.

Another simple way to test for randomness is evaluating the Monte Carlo for π as explained in [4]. This method is based on this idea: we take a square and inscribe within it a circle that touches each edge of the square. We know that if the radius of the circle is r, then the area of the circle is πr², and the area of the square is 4r². If we calculate the ratio, q, of the circle area to the square area, we get q = π/4; we can therefore calculate π with π = 4q. The same result still holds if we work in the first quadrant.
The ratio q can be found using pairs of random points (x, y) that we extract from our sequence. In particular (x,y) pairs are generated using blocks of consecutive 48-bit, with each coordinate being a 24-bit number. If we count the number of points that fall inside the circle and divide this number by the total number of points we get an estimation of q. For independent random points that are uniformly distributed within the square, this method should give us a sequence that slowly converges to π.
These are the results for the first 10000 points of each sequence:

```\$ cat seq1_108000000_500us | ./montecarlo_pi 10000 2>points | tail -n1
n=10000  pi=3.132000000  e=-0.305%
\$ cat seq2_108000000_500us | ./montecarlo_pi 10000 2>points | tail -n1
n=10000  pi=3.143600000  e= 0.064%
\$ cat seq3_108000000_500us | ./montecarlo_pi 10000 2>points | tail -n1
n=10000  pi=3.136400000  e=-0.165%
\$ cat seq4_108000000_500us | ./montecarlo_pi 10000 2>points | tail -n1
n=10000  pi=3.122000000  e=-0.624%
```

Full log for this test here.

 Sequence 1 Sequence 2 Sequence 3 Sequence 4

Lastly, I took 882000 bits from the first sequence and saved them as wave audio file (mono, 44100Hz, s16le, 10 seconds). This file can be downloaded here.

## Statistical tests

A natural source of random bits may not give unbiased bits as direct output. Many applications, especially in cryptography, rely on sequences of unbiased bits. There are various techniques to extract unbiased bits from a defective generator with unknown bias. These are called de-skewing techniques or whitening algorithms. These techniques also eliminate the correlation in the output of the natural sources of random bits. John von Neumann invented a simple algorithm to fix simple bias, and reduce correlation. It considers successive pairs of consecutive, non-overlapping bits from the input stream, taking one of three actions: when two successive bits are equal, they are discarded; a sequence of 1,0 becomes a 1; and a sequence of 0,1 becomes a 0. It thus represents a falling edge with a 1, and a rising edge with a 0. This eliminates simple bias, and is easy to implement. This technique works no matter how the bits have been generated. It cannot assure randomness in its output, however. What it can do is transform a biased random bit stream into an unbiased one. However, significant numbers of bits are discarded. Since we established that systematic bias is very low, we can affirm that the probability of 1 is p ~ 1/2, and in this case the discard of input pairs is minimum, giving us an output stream that is 1/4 the length of the input on average.

I have considered the following tools to analyze the sequences:

rngtest: rngtest works on blocks of 20000 bits at a time, using the FIPS 140-2 (errata of 2001-10-10) tests to verify the randomness of the block of data. It will use the first 32 bits of data to bootstrap the FIPS tests (these bits are not tested for randomness). Each block is subjected to five tests: monobit test, poker test, runs test, long run test, continuos run test. If any of these fails the block fails the test. For more informations, visit [5].
I have ran this test on all the sequences before applying de-skewing. This is because some whitener designs can pass statistical tests with no random input. While detecting a large deviation from perfection would be a sign that a true random noise source has become degraded, small deviations are normal and can be an indication of proper operation.
This is what the output of rngtest looks like on the first sequence:

```\$ cat seq1_108000000_500us | ./ascii_to_bin | rngtest
rngtest 2-unofficial-mt.12
Copyright (c) 2004 by Henrique de Moraes Holschuh
This is free software; see the source for copying conditions.  There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

rngtest: starting FIPS tests...
rngtest: entropy source exhausted!
rngtest: bits received from input: 108000000
rngtest: FIPS 140-2 successes: 5398
rngtest: FIPS 140-2 failures: 1
rngtest: FIPS 140-2(2001-10-10) Monobit: 0
rngtest: FIPS 140-2(2001-10-10) Poker: 0
rngtest: FIPS 140-2(2001-10-10) Runs: 1
rngtest: FIPS 140-2(2001-10-10) Long run: 0
rngtest: FIPS 140-2(2001-10-10) Continuous run: 0
rngtest: input channel speed: (min=1.093; avg=27.612; max=19073.486)Mibits/s
rngtest: FIPS tests speed: (min=40.324; avg=97.746; max=99.861)Mibits/s
rngtest: Program run time: 4796018 microseconds
```

We get only a failure over 5399 blocks, which is 0.018%. The results for all the sequence are summarized in the following table, where PASS means that the fraction of blocks that failed the test is less than 0.5%:

 Sequence 1 Sequence 2 Sequence 3 Sequence 4 FIPS 140-2 PASS PASS PASS PASS

Full log for this test here.

ent: ent applies various tests to sequences of bytes stored in files and reports the results of those tests. The program is useful for evaluating pseudorandom number generators for encryption and statistical sampling applications, compression algorithms, and other applications where the information density of a file is of interest. It can work in bit mode or byte mode. See [6] for an explation of its output.
Before running this test, each sequence has been passed through the de-skewing step. ent output for the first sequence:

```\$ cat seq1_108000000_500us | ./deskewing | ./ascii_to_bin | ent -b
Entropy = 1.000000 bits per bit.

Optimum compression would reduce the size
of this 27013344 bit file by 0 percent.

Chi square distribution for 27013344 samples is 0.76, and randomly
would exceed this value 50.00 percent of the times.

Arithmetic mean value of data bits is 0.4999 (0.5 = random).
Monte Carlo value for Pi is 3.138345849 (error 0.10 percent).
Serial correlation coefficient is 0.000228 (totally uncorrelated = 0.0).

\$ cat seq1_108000000_500us | ./deskewing | ./ascii_to_bin | ent
Entropy = 7.999945 bits per byte.

Optimum compression would reduce the size
of this 3376668 byte file by 0 percent.

Chi square distribution for 3376668 samples is 255.86, and randomly
would exceed this value 50.00 percent of the times.

Arithmetic mean value of data bytes is 127.5140 (127.5 = random).
Monte Carlo value for Pi is 3.138345849 (error 0.10 percent).
Serial correlation coefficient is 0.000051 (totally uncorrelated = 0.0).
```

Summary of the results:

 Sequence number Length Entropy Chi Square Mean Monte Carlo for π Serial correlation 1 27013344 1.000000 0.76 (50%) 0.4999 3.138345849 (e=0.10%) 0.000228 2 27011912 1.000000 0.34 (50%) 0.4999 3.138345849 (e=0.06%) -0.000017 3 27013528 1.000000 0.78 (50%) 0.5001 3.141435123 (e=0.01%) 0.000141 4 27014560 1.000000 1.42 (25%) 0.4999 3.141091998 (e=0.02%) -0.000158

Let's repeat this test for all the data we've acquired:

```\$ cat seq* | ./deskewing | ./ascii_to_bin | ent -b
Entropy = 1.000000 bits per bit.

Optimum compression would reduce the size
of this 108053352 bit file by 0 percent.

Chi square distribution for 108053352 samples is 0.78, and randomly
would exceed this value 50.00 percent of the times.

Arithmetic mean value of data bits is 0.5000 (0.5 = random).
Monte Carlo value for Pi is 3.140223650 (error 0.04 percent).
Serial correlation coefficient is 0.000048 (totally uncorrelated = 0.0).

\$ cat seq* | ./deskewing | ./ascii_to_bin | ent
Entropy = 7.999985 bits per byte.

Optimum compression would reduce the size
of this 13506669 byte file by 0 percent.

Chi square distribution for 13506669 samples is 271.69, and randomly
would exceed this value 25.00 percent of the times.

Arithmetic mean value of data bytes is 127.5085 (127.5 = random).
Monte Carlo value for Pi is 3.140223650 (error 0.04 percent).
Serial correlation coefficient is 0.000001 (totally uncorrelated = 0.0).
```

Full log for this test here.

dieharder: dieharder is a random number generator (rng) testing suite. It is intended to test generators, and is designed to permit one to push a weak generator to unambiguous failure (at the e.g. 0.0001% level), not leave one in the "limbo" of 1% or 5% maybe-failure. More information about dieharder can be found at [7], or by reading its manpage.
The problem with dieharder is that it can consume a lot of random numbers in the course of running all the tests. For this reason I have limited the tests ran on the sequences acquired to the statistical tests only for the moment (option -s [1|2|3]). These test have been performed on the complete set of data (432Mbit) after de-skewing. The option -t 31250 has been used to divide the data in blocks of 1Mbit. Summary of the output:

```Assessment: PASSED at > 5% for STS Monobit Test
Assessment: PASSED at > 5% for STS Runs Test
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
Assessment: PASSED at > 5% for STS Serial Test (Generalized)
```

Full log for this test here.

All the scripts and utilities I have used while testing this generator can be downloaded here.

## Conclusions

I have to say that I am very satisfied with the results I got with this circuit, considering that it seems very stable and can output 2kbit/s (or 500bit/s after de-skewing), which is more that enough for something built with a quad op-amp and some transistors, but if speed is an issue it's probable that a sampling time lower than 500μs can be used. Also, having an analog output with white noise in the audio bandwidth can surely come in handy.
The random sequences acquired with this generator seem to pass all the usual statistical tests, and this is a good sign, but of course if you want to use it for a serious cryptographic application, I'd recommend you to extensively test it.
Lastly, I want to encourage everyone interested in these techniques, to build and test this circuit, and improve from it: from my point of view it is always interesting to get your hands dirty with discrete components and analog design.

Note: I have tried to be as rigorous as possible while building the circuit, acquiring the sequences and performing the tests, but it is quite possible that I have made some mistake in the process. If so, I'd be glad to be pointed in the right direction. My email is on the homepage of this site.