Added capability to write entropy to /dev/random

This commit is contained in:
Bill Cox
2014-10-22 16:49:08 -04:00
parent 5ceb735848
commit 347adf3dc1
6 changed files with 173 additions and 59 deletions

View File

@@ -1,7 +1,7 @@
all: infnoise healthcheck findlongest
infnoise: infnoise.c healthcheck.c Keccak/KeccakF-1600-reference.c Keccak/brg_endian.h
gcc -Wall -std=c11 -O3 -m64 -march=native -I Keccak -o infnoise infnoise.c healthcheck.c Keccak/KeccakF-1600-reference.c -lftdi -lm
infnoise: infnoise.c infnoise.h healthcheck.c writeentropy.c Keccak/KeccakF-1600-reference.c Keccak/brg_endian.h
gcc -Wall -std=c11 -O3 -m64 -march=native -I Keccak -o infnoise infnoise.c healthcheck.c writeentropy.c Keccak/KeccakF-1600-reference.c -lftdi -lm
healthcheck: healthcheck.c
gcc -Wall -std=c11 -O3 -m64 -march=native -D TEST_HEALTHCHECK -o healthcheck healthcheck.c -lm

View File

@@ -20,13 +20,11 @@ confirmed.
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include "healthcheck.h"
#include "infnoise.h"
#define INM_MIN_DATA 80000
#define INM_MIN_SAMPLE_SIZE 100
@@ -242,17 +240,13 @@ bool inmHealthCheckOkToUseData(void) {
}
// Just return the entropy level added so far in bytes;
uint32_t inmHealthCheckGetEntropyLevel(void) {
return inmEntropyLevel/8;
uint32_t inmGetEntropyLevel(void) {
return inmEntropyLevel;
}
// Reduce the entropy level by numBytes.
void inmHealthCheckReduceEntropyLevel(uint32_t numBytes) {
if(numBytes*8 > inmEntropyLevel) {
fprintf(stderr, "Entropy pool underflow\n");
exit(1);
}
inmEntropyLevel -= numBytes*8;
void inmClearEntropyLevel(void) {
inmEntropyLevel = 0;
}
#ifdef TEST_HEALTHCHECK

View File

@@ -4,6 +4,8 @@ bool inmHealthCheckAddBit(bool bit, bool even);
bool inmHealthCheckOkToUseData(void);
double inmHealthCheckEstimateK(void);
double inmHealthCheckEstimateEntropyPerBit(void);
// Returns number of bytes of entropy added so far
uint32_t inmHealthCheckGetEntropyLevel(void);
void inmHealthCheckReduceEntropyLevel(uint32_t numBytes);
uint32_t inmGetEntropyLevel(void);
void inmClearEntropyLevel(void);
void inmWriteEntropyToPool(uint8_t bytes, uint32_t length, uint32_t entropy);

View File

@@ -5,7 +5,7 @@
#include <stdio.h>
#include <string.h>
#include <ftdi.h>
#include "healthcheck.h"
#include "infnoise.h"
#include "KeccakF-1600-interface.h"
// The FT240X has a 512 byte buffer. Must be multiple of 64
@@ -20,8 +20,10 @@
// Extract the INM output from the data received. Basically, either COMP1 or COMP2
// changes, not both, so alternate reading bits from them. We get 1 INM bit of output
// per byte read. Feed bits from the INM to the health checker.
static void extractBytes(uint8_t *bytes, uint8_t *inBuf, bool raw) {
// per byte read. Feed bits from the INM to the health checker. Return the expected
// bits of entropy.
static uint32_t extractBytes(uint8_t *bytes, uint8_t *inBuf, bool raw) {
inmClearEntropyLevel();
uint32_t i;
for(i = 0; i < BUFLEN/8; i++) {
uint32_t j;
@@ -38,7 +40,7 @@ static void extractBytes(uint8_t *bytes, uint8_t *inBuf, bool raw) {
byte = (byte << 1) | bit;
// This is a good place to feed the bit from the INM to the health checker.
//printf("Adding bit %u\n", bit);
if(!raw && !inmHealthCheckAddBit(bit, j & 1)) {
if(!inmHealthCheckAddBit(bit, j & 1)) {
fprintf(stderr, "Health check of Infinite Noise Multiplier failed!\n");
exit(1);
}
@@ -46,41 +48,39 @@ static void extractBytes(uint8_t *bytes, uint8_t *inBuf, bool raw) {
//printf("extracted byte:%x\n", byte);
bytes[i] = byte;
}
return inmGetEntropyLevel();
}
// Write the bytes to either stdout, or /dev/random. Cut the entropy estimate in half to
// be conservative.
static void outputBytes(uint8_t *bytes, uint32_t length, uint32_t entropy, bool writeDevRandom) {
if(!writeDevRandom) {
if(fwrite(bytes, 1, length, stdout) != length) {
fprintf(stderr, "Unable to write output from Infinite Noise Multiplier\n");
exit(1);
}
} else {
inmWaitForPoolToHaveRoom();
inmWriteEntropyToPool(bytes, length, entropy);
}
}
// Send the new bytes through the health checker and also into the Keccak sponge.
// Output bytes from the sponge only if the health checker says it's OK, and only
// output half the entropy we get from the INM, just to be paranoid.
static void processBytes(uint8_t *keccakState, uint8_t *bytes, bool raw) {
static void processBytes(uint8_t *keccakState, uint8_t *bytes, uint32_t entropy, bool raw, bool writeDevRandom) {
if(raw) {
// In raw mode, we disable the health check and whitening, and just output raw
// data from the INM.
if(fwrite(bytes, 1, BUFLEN/8, stdout) != BUFLEN/8) {
fprintf(stderr, "Unable to write output from Infinite Noise Multiplier\n");
exit(1);
}
// In raw mode, we just output raw data from the INM.
outputBytes(bytes, BUFLEN/8, entropy, writeDevRandom);
return;
}
uint32_t i;
for(i = 0; i < BUFLEN/64; i++) {
// It's always OK to absorb bytes, even if they are not verified for randomness
KeccakAbsorb(keccakState, bytes + i*8, 1);
if(inmHealthCheckOkToUseData() && inmHealthCheckGetEntropyLevel() >= 16) {
// Only output byes if we have enough entropy and health check passes
// Also, we output data at 1/2 the rate of entropy added to the sponge
//uint32_t j;
//for(j = 0; j < 1 << 24; j++) {
uint8_t dataOut[8];
KeccakPermutation(keccakState);
KeccakExtract(keccakState, dataOut, 1);
if(fwrite(dataOut, 1, 8, stdout) != 8) {
fprintf(stderr, "Unable to write output from Infinite Noise Multiplier\n");
exit(1);
}
//}
inmHealthCheckReduceEntropyLevel(16);
}
}
KeccakAbsorb(keccakState, bytes, BUFLEN/64);
KeccakPermutation(keccakState);
// Only output byes if we have enough entropy and health check passes
// Also, we output data at 1/2 the rate of entropy added to the sponge
uint8_t dataOut[BUFLEN/8];
KeccakExtract(keccakState, dataOut, BUFLEN/64);
outputBytes(dataOut, BUFLEN/8, entropy, writeDevRandom);
}
int main(int argc, char **argv)
@@ -88,27 +88,41 @@ int main(int argc, char **argv)
struct ftdi_context ftdic;
bool raw = false;
bool debug = false;
bool writeDevRandom = false;
bool noOutput = false;
// Process arguments
if(argc > 2) {
fprintf(stderr, "Usage: infnoise [--raw]\n"
" infnoise --debug\n");
return 1;
}
if(argc == 2) {
if(!strcmp(argv[1], "--raw")) {
while(argc > 1) {
argc--;
if(!strcmp(argv[argc], "--raw")) {
raw = true;
} else if(!strcmp(argv[1], "--debug")) {
} else if(!strcmp(argv[argc], "--debug")) {
debug = true;
} else if(!strcmp(argv[argc], "--dev-random")) {
writeDevRandom = true;
} else if(!strcmp(argv[argc], "--no-output")) {
noOutput = true;
} else {
fprintf(stderr, "Usage: infnoise [--raw]\n"
" infnoise --debug\n");
fprintf(stderr, "Usage: infnoise [options]\n"
"Options are:\n"
" --debug - turn on some debug output\n"
" --dev-random - write entropy to /dev/random instead of stdout\n"
" --raw - do not whiten the output\n"
" --no-output - do not write random output data\n");
return 1;
}
}
if(debug && !writeDevRandom) {
// No sense writing data to stdout if debug is on
noOutput = true;
}
// Initialize FTDI context
ftdi_init(&ftdic);
if(writeDevRandom) {
inmWriteEntropyStart(BUFLEN/8, debug);
}
if(!inmHealthCheckStart(14, 1.82, debug)) {
puts("Can't intialize health checker\n");
return 1;
@@ -177,9 +191,9 @@ int main(int argc, char **argv)
return -1;
}
uint8_t bytes[BUFLEN/8];
extractBytes(bytes, inBuf, raw);
if(!debug) {
processBytes(keccakState, bytes, raw);
uint32_t entropy = extractBytes(bytes, inBuf, raw);
if(!noOutput && inmHealthCheckOkToUseData()) {
processBytes(keccakState, bytes, entropy, raw, writeDevRandom);
}
}
return 0;

14
software/infnoise.h Normal file
View File

@@ -0,0 +1,14 @@
#include <stdbool.h>
#include <stdint.h>
bool inmHealthCheckStart(uint8_t N, double K, bool debug);
void inmHealthCheckStop(void);
bool inmHealthCheckAddBit(bool bit, bool even);
bool inmHealthCheckOkToUseData(void);
double inmHealthCheckEstimateK(void);
double inmHealthCheckEstimateEntropyPerBit(void);
uint32_t inmGetEntropyLevel(void);
void inmClearEntropyLevel(void);
void inmWriteEntropyStart(uint32_t bufLen, bool debug);
void inmWriteEntropyToPool(uint8_t *bytes, uint32_t length, uint32_t entropy);
void inmWaitForPoolToHaveRoom(void);

90
software/writeentropy.c Normal file
View File

@@ -0,0 +1,90 @@
// This writes entropy to the Linux /dev/random pool using ioctl, so that entropy increases.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/poll.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/random.h>
#include "infnoise.h"
#define SIZE_PROC_FILENAME "/proc/sys/kernel/random/poolsize"
#define FILL_PROC_FILENAME "/proc/sys/kernel/random/write_wakeup_threshold"
static uint32_t inmBufLen;
static bool inmDebug;
static int inmDevRandomFD;
static uint32_t inmFillWatermark;
static struct rand_pool_info *inmPoolInfo;
// Find the entropy pool size.
static uint32_t readNumberFromFile(char *fileName) {
FILE *file = fopen(fileName, "r");
if(file == NULL) {
fprintf(stderr, "Unable to open %s\n", fileName);
exit(1);
}
char buf[42];
uint32_t i = 0;
int c = getc(file);
while(c != EOF) {
buf[i++] = c;
c = getc(file);
}
buf[i] = '\0';
uint32_t value = atoi(buf);
fclose(file);
return value;
}
// Open /dev/random
void inmWriteEntropyStart(uint32_t bufLen, bool debug) {
inmBufLen = bufLen;
inmDebug = debug;
//inmDevRandomFD = open("/dev/random", O_WRONLY);
inmDevRandomFD = open("/dev/random", O_RDWR);
if(inmDevRandomFD < 0) {
fprintf(stderr, "Unable to open /dev/random\n");
exit(1);
}
inmPoolInfo = calloc(1, sizeof(struct rand_pool_info) + bufLen);
if(inmPoolInfo == NULL) {
fprintf(stderr, "Unable to allocate memory\n");
exit(1);
}
inmFillWatermark = readNumberFromFile(FILL_PROC_FILENAME);
if(inmDebug) {
printf("Entropy pool size:%u\n", readNumberFromFile(SIZE_PROC_FILENAME));
}
}
// Block until either the entropy pool has room, or 1 second has passed.
void inmWaitForPoolToHaveRoom(void) {
printf("starting select\n");
int ent_count;
struct pollfd pfd = {
fd: inmDevRandomFD,
events: POLLOUT,
};
int64_t timeout_usec;
if (ioctl(inmDevRandomFD, RNDGETENTCNT, &ent_count) == 0 && ent_count < inmFillWatermark) {
printf("Not full\n");
return;
}
timeout_usec = 1000; // One second
poll(&pfd, 1, timeout_usec);
printf("Finished select\n");
}
// Add the bytes to the entropy pool. This can be unwhitenened, but the estimated bits of
// entropy needs to be accurate or pessimistic. Return false if the Linux entropy pool is
// full after writing.
void inmWriteEntropyToPool(uint8_t *bytes, uint32_t length, uint32_t entropy) {
inmPoolInfo->entropy_count = entropy;
inmPoolInfo->buf_size = length;
memcpy(inmPoolInfo->buf, bytes, length);
//printf("Writing %u bytes with %u bits of entropy to /dev/random\n", length, entropy);
ioctl(inmDevRandomFD, RNDADDENTROPY, inmPoolInfo);
}