This commit is contained in:
Bill Cox
2014-10-17 11:22:51 -04:00
parent edf556750f
commit d4b8fdae6f
2 changed files with 95 additions and 20 deletions

View File

@@ -360,6 +360,28 @@ we should be able to keep the output generating close to 1 bit of entropy per cl
this example, both noise samples had over 10X the minimum resolution in unpreditable
noise, and easily contributed a bit of entropy each to our 31-ish bit entropy pool.
### Whitening the Right Way
Data coming from true random number generators is never 100% random. I am aware of no
exceptions. Whitening is required in all cases before the data is suitable for use in
cryptography.
Most TRNGs build whitening into the hardware. This is a _mistake_, at least if the
whitening cannot be turned off. Respectable TRNGs, such as [OneRNG](http://onerng.info/)
and [Entropy Key](http://www.entropykey.co.uk/) provide hardware whitening, but also
provide access to the raw data streams for health analysis.
I prefer to follow the KISS rule when it comes to security. The more complex the TRNG
key, the more likely it is insecure. Therefore, the initial Infinite Noise Multiplier
does not even have a microcontroller onboard, and only returns raw data, direct from the
noise source. Whitening is done in the INM driver.
The INM driver uses the reference versoin of the SHA3 "sponge" with a 1600 bit state. The
state of the sponge needs to be made unpredictable. It is initialized with 3200 bits of
entropy before any data is output. After that, reading bytes from the SHA3 sponge blocks
until twice as many bytes of entropy have been fed into the sponge from the INM. Data is
fed into the sponge 64-bits at a time.
### Non-Power-of-Two Multiplication
The circuit shown in infnoise_fast multiplies by 1.82 every clock rather than 2.0. As
@@ -392,6 +414,33 @@ The ratio of the clocks it takes with amplification 2 vs K is:
It takes log(2)/log(K) more cycles to insure the output is different.
the entropy shifted out with exactly 2X amplification will be 1 bit per clock.
### Health Monitoring
Infinite Noise Multipliers output entropy at a predictable rate, which is measured
continually. If the entropy per bit deviates from the theoretical value of log(K)/log(2)
by more than 5% during the previous 20,000 bits, then the driver exits with an error code.
Some deviation is expected, since K is dependent on two resistor values, which can be off
by 1% each. Also, a significant amplitude noise in the system can cause more entropy to
be output than predicted. The estimated entropy per bit are continually estimated and
compared to expected values.
Entropy of a string of bits is measured as 1 over the probability of that string occuring.
The previous 12 bits of data are used to guess the next bit, based on the history of what
comes next.
Also, sequences of 1's or 0's longer than the max predicted are detected and cause the
driver to exit with a failure code. For the board level implementation above, With 1%
resistors we should see a gain of no higher than 1.86. The maximum Vref possible is
Vsupply(1.01/(1.01 + 1/1.01)) = Vsupply\*0.505. So the maximum value after multipling a
value below Vref is 0.94\*Vsupply, or equivalently 0.06\*Vsupply. If synchronous noise is
injected that subtracts more than 0.028\*Vsupply, then this noise could hold the signal at
0V forever. This is over 90mV, so we should be OK. Assuming no more than 50mV of
negative synchronous signal is injected, we get the sequence 0.045\*Vsupply,
0.069\*Vsupply, 0.113\*Vsupply, .195\*Vsupply, 0.347\*Vsupply, and finally 0.631\*Vsupply.
This is a total of 5 sequential 0's. Therefore, any sequence of 6 zeros or ones causes
the driver to abort with an error condision. The maximum length depends on the components
used.
### Free As in Freedom
I, Bill Cox, came up with the original CMOS based Infinite Noise Multiplier architecture

View File

@@ -29,6 +29,8 @@ confirmed.
#define INM_MIN_SAMPLE_SIZE 100
#define INM_ACCURACY 1.05
#define INM_MAX_SEQUENCE 5
#define INM_MAX_COUNT (1 << 14)
static uint8_t inmN;
static uint32_t inmPrevBits;
@@ -39,14 +41,16 @@ static double inmK, inmExpectedEntropyPerBit;
// 1/(2^inmNumBitsOfEntropy * inmCurrentProbability).
static uint32_t inmNumBitsOfEntropy;
static double inmCurrentProbability;
static uint64_t inmTotalBits;
static bool inmPrevBit;
// Print the tables of statistics.
static void inmDumpStats(void) {
uint32_t i;
for(i = 0; i < 1 << inmN; i++) {
if(inmOnes[i] > 0 || inmZeros[i] > 0) {
//if(inmOnes[i] > 0 || inmZeros[i] > 0) {
printf("%x ones:%u zeros:%u\n", i, inmOnes[i], inmZeros[i]);
}
//}
}
}
@@ -77,6 +81,8 @@ bool inmHealthCheckerStart(uint8_t N, double K) {
inmOnes = calloc(1u << N, sizeof(uint32_t));
inmZeros = calloc(1u << N, sizeof(uint32_t));
inmExpectedEntropyPerBit = log(K)/log(2.0);
inmTotalBits = 0;
inmPrevBit = false;
if(inmOnes == NULL || inmZeros == NULL) {
inmHealthCheckerStop();
return false;
@@ -95,6 +101,7 @@ static void resetStats(void) {
// This should be called for each bit generated.
bool inmHealthCheckerAddBit(bool bit) {
inmTotalBits++;
if(inmOnes[inmPrevBits] > INM_MIN_SAMPLE_SIZE ||
inmZeros[inmPrevBits] > INM_MIN_SAMPLE_SIZE) {
uint32_t total = inmZeros[inmPrevBits] + inmOnes[inmPrevBits];
@@ -120,9 +127,11 @@ bool inmHealthCheckerAddBit(bool bit) {
} else {
inmZeros[inmPrevBits]++;
}
inmPrevBits = (inmPrevBits << 1) & ((1 << inmN)-1);
if(bit) {
inmPrevBits |= 1;
// Check for max sequence of 0's or 1's.
uint32_t lowBits = inmPrevBits & ((1 << (INM_MAX_SEQUENCE+1))-1);
if(lowBits == 0 || lowBits == ((1 << (INM_MAX_SEQUENCE+1))-1)) {
printf("Maximum sequence of %d 0's or 1's exceeded\n", INM_MAX_SEQUENCE);
return false;
}
//printf("prevBits: %x\n", inmPrevBits);
if(inmNumBitsSampled < 10000) {
@@ -133,14 +142,15 @@ bool inmHealthCheckerAddBit(bool bit) {
resetStats();
return true;
}
if(inmNumBitsOfEntropy > 10000) {
// Check the entropy is in line with expectations
uint32_t expectedEntropy = inmExpectedEntropyPerBit*inmNumBitsCounted;
if(inmNumBitsOfEntropy > expectedEntropy*INM_ACCURACY || inmNumBitsOfEntropy < expectedEntropy/INM_ACCURACY) {
printf("entropy:%u, expected entropy:%u, num bits counted:%u, num bits sampled:%u\n",
inmNumBitsOfEntropy, expectedEntropy, inmNumBitsCounted, inmNumBitsSampled);
return false;
}
if(inmNumBitsSampled == 10000) {
printf("Generated a total of %lu bits to initialize health checker\n", inmTotalBits);
}
// Check the entropy is in line with expectations
uint32_t expectedEntropy = inmExpectedEntropyPerBit*inmNumBitsCounted;
if(inmNumBitsOfEntropy > expectedEntropy*INM_ACCURACY || inmNumBitsOfEntropy < expectedEntropy/INM_ACCURACY) {
printf("entropy:%u, expected entropy:%u, num bits counted:%u, num bits sampled:%u\n",
inmNumBitsOfEntropy, expectedEntropy, inmNumBitsCounted, inmNumBitsSampled);
return false;
}
return true;
}
@@ -212,13 +222,20 @@ static void reduceStatsIfNeeded(void) {
maxValue = ones;
}
}
if(maxValue > (1 << 30)) {
if(maxValue > INM_MAX_COUNT) {
printf("Scaling stats...\n");
for(i = 0; i < (1 << inmN); i++) {
inmZeros[i] >>= 1;
inmOnes[i] >>= 1;
inmZeros[i] = inmZeros[i]*INM_MAX_COUNT/maxValue;
inmOnes[i] = inmOnes[i]*INM_MAX_COUNT/maxValue;
}
}
/*
if(numBitsSampled > 20000) {
inmNumBitsCounted = inmNumBitsCounted*20000/inmNumBitsSampled;
inmNumBitsOfEntropy = inmNumBitsOfEntropy*20000/inmNumBitsSampled;
inmNumBitsSampled = 20000;
}
*/
}
/* This could be built with one opamp for the multiplier, a comparator with
@@ -240,25 +257,34 @@ static inline bool updateA(double *A, double K, double noise) {
}
static inline bool computeRandBit(double *A, double K, double noiseAmplitude) {
inmPrevBits = (inmPrevBits << 1) & ((1 << inmN)-1);
if(inmPrevBit) {
inmPrevBits |= 1;
}
double noise = noiseAmplitude*(((double)rand()/RAND_MAX) - 0.5);
return updateA(A, K, noise);
inmPrevBit = updateA(A, K, noise);
return inmPrevBit;
}
int main() {
//double K = sqrt(2.0);
double K = 1.82;
uint8_t N = 12;
uint8_t N = 7;
inmHealthCheckerStart(N, K);
srand(time(NULL));
double A = (double)rand()/RAND_MAX; // Simulating INM
double noiseAmplitude = 1.0/(1 << 10);
uint32_t i;
for(i = 0; i < 1 << 26; i++) {
for(i = 0; i < 32; i++) {
// Throw away some initial bits.
computeRandBit(&A, K, noiseAmplitude);
}
for(i = 0; i < 1 << 24; i++) {
bool bit = computeRandBit(&A, K, noiseAmplitude);
if(!inmHealthCheckerAddBit(bit)) {
printf("Failed health check!\n");
resetStats();
//return 1;
return 1;
} else if(inmNumBitsCounted > 0 && (inmNumBitsCounted & 0xfffff) == 0) {
printf("Estimated entropy per bit: %f, estimated K: %f\n", inmHealthCheckerEstimateEntropyPerBit(),
inmHealthCheckerEstimateK());