From 347adf3dc17b700f6b525864ad39b6b21563c56e Mon Sep 17 00:00:00 2001 From: Bill Cox Date: Wed, 22 Oct 2014 16:49:08 -0400 Subject: [PATCH] Added capability to write entropy to /dev/random --- software/Makefile | 4 +- software/healthcheck.c | 16 ++----- software/healthcheck.h | 6 ++- software/infnoise.c | 102 +++++++++++++++++++++++----------------- software/infnoise.h | 14 ++++++ software/writeentropy.c | 90 +++++++++++++++++++++++++++++++++++ 6 files changed, 173 insertions(+), 59 deletions(-) create mode 100644 software/infnoise.h create mode 100644 software/writeentropy.c diff --git a/software/Makefile b/software/Makefile index af4d316..a9584f0 100644 --- a/software/Makefile +++ b/software/Makefile @@ -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 diff --git a/software/healthcheck.c b/software/healthcheck.c index aa98453..21467cb 100644 --- a/software/healthcheck.c +++ b/software/healthcheck.c @@ -20,13 +20,11 @@ confirmed. */ -#include -#include #include #include #include #include -#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 diff --git a/software/healthcheck.h b/software/healthcheck.h index 40ca1ef..64dee23 100644 --- a/software/healthcheck.h +++ b/software/healthcheck.h @@ -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); diff --git a/software/infnoise.c b/software/infnoise.c index 16d38c5..4eeedf1 100644 --- a/software/infnoise.c +++ b/software/infnoise.c @@ -5,7 +5,7 @@ #include #include #include -#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; diff --git a/software/infnoise.h b/software/infnoise.h new file mode 100644 index 0000000..9292c9d --- /dev/null +++ b/software/infnoise.h @@ -0,0 +1,14 @@ +#include +#include + +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); diff --git a/software/writeentropy.c b/software/writeentropy.c new file mode 100644 index 0000000..7327dde --- /dev/null +++ b/software/writeentropy.c @@ -0,0 +1,90 @@ +// This writes entropy to the Linux /dev/random pool using ioctl, so that entropy increases. + +#include +#include +#include +#include +#include +#include +#include +#include +#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); +}