updates
This commit is contained in:
49
README.md
49
README.md
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user