/* Driver for the Infinite Noise Multiplier USB stick */ // Required to include clock_gettime #define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #pragma warning(push) // Suppress -W4 `warning C4214: nonstandard extension used: bit field types other than int` in ftd2xx.h::_FTDCB #pragma warning(disable : 4214) #include "VisualStudio\ftdi\ftd2xx.h" #pragma warning(pop) #include "infnoise.h" #include "libinfnoise_private.h" #include "Keccak\KeccakF-1600-interface.h" // Convert an address value 0 to 15 to an 8-bit value using ADDR0 .. ADDR3. static uint8_t makeAddress(uint8_t addr) { uint8_t value = 0; if(addr & 1) { value |= 1 << ADDR0; } if(addr & 2) { value |= 1 << ADDR1; } if(addr & 4) { value |= 1 << ADDR2; } if(addr & 8) { value |= 1 << ADDR3; } return value; } // Extract a value form 0 to 15 from the ADDR0 .. ADDR3 bits. static uint8_t extractAddress(uint8_t value) { uint8_t addr = 0; if(value & (1 << ADDR0)) { addr |= 1; } if(value & (1 << ADDR1)) { addr |= 2; } if(value & (1 << ADDR2)) { addr |= 4; } if(value & (1 << ADDR3)) { addr |= 8; } return addr; } // 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. Return the expected // bits of entropy. static uint32_t extractBytes(uint8_t *bytes, uint8_t *inBuf, bool raw) { if (raw) { // We don't currently using `raw` in here } inmClearEntropyLevel(); //printf("New batch\n"); uint32_t i; for(i = 0; i < BUFLEN/8; i++) { uint32_t j; uint8_t byte = 0; for(j = 0; j < 8; j++) { //printf("%x ", inBuf[i*8 + j] & ~MASK); uint8_t val = inBuf[i*8 + j]; uint8_t evenBit = (val >> COMP2) & 1; uint8_t oddBit = (val >> COMP1) & 1; bool even = j & 1; // Use the even bit if j is odd uint8_t bit = even? oddBit : evenBit; byte = (byte << 1) | bit; // This is a good place to feed the bit from the INM to the health checker. uint8_t addr = extractAddress(val); if(!inmHealthCheckAddBit(evenBit, oddBit, even)) { fputs("Health check of Infinite Noise Multiplier failed!\n", stderr); fprintf(stderr, "Address: %u, adding evenBit:%u oddBit:%u even:%u\n", addr, evenBit, oddBit, even); exit(1); } } //printf("extracted byte:%x\n", byte); bytes[i] = byte; } return inmGetEntropyLevel(); } // Write the bytes to either stdout, or /dev/random. Use the lower of the measured // entropy and the provable lower bound on average entropy. static void outputBytes(FILE* outFile, uint8_t *bytes, uint32_t length, uint32_t entropy, bool writeDevRandom) { if(entropy > inmExpectedEntropyPerBit*BUFLEN/INM_ACCURACY) { entropy = (uint32_t)(inmExpectedEntropyPerBit*BUFLEN/INM_ACCURACY); } if(!writeDevRandom) { if(fwrite(bytes, 1, length, outFile) != length) { fputs("Unable to write output from Infinite Noise Multiplier\n", stderr); exit(1); } fflush(outFile); } else { fprintf(stderr, "/dev/random not supported in Windows"); exit(1); } } // Whiten the output, if requested, with a Keccak sponge. Output bytes only if the health // checker says it's OK. Using outputMultiplier > 1 is a nice way to generate a lot more // cryptographically secure pseudo-random data than the INM generates. This allows a user // to generate hundreds of MiB per second if needed, for use as cryptogrpahic keys. static void processBytes(FILE* outFile, uint8_t *keccakState, uint8_t *bytes, uint32_t entropy, bool raw, bool writeDevRandom, uint32_t outputMultiplier) { if(raw) { // In raw mode, we just output raw data from the INM. outputBytes(outFile, bytes, BUFLEN/8, entropy, writeDevRandom); return; } // Note that BUFLEN has to be less than 1600 by enough to make the sponge secure, // since outputting all 1600 bits would tell an attacker the Keccak state, allowing // him to predict any further output, when outputMultiplier > 1, until the next call // to processBytes. All 512 bits are absorbed before sqeezing data out to insure that // we instantly recover (reseed) from a state compromise, which is when an attacker // gets a snapshot of the keccak state. BUFLEN must be a multiple of 64, since // Keccak-1600 uses 64-bit "lanes". KeccakAbsorb(keccakState, bytes, BUFLEN/64); uint8_t dataOut[16*8]; while(outputMultiplier > 0) { // Write up to 1024 bits at a time. uint32_t numLanes = 16; if(outputMultiplier < 4) { numLanes = outputMultiplier*4; } KeccakExtract(keccakState, dataOut, numLanes); // Extract does not do a permute, so do it here. KeccakPermutation(keccakState); uint32_t entropyThisTime = entropy; if(entropyThisTime > numLanes*64) { entropyThisTime = numLanes*64; } outputBytes(outFile, dataOut, numLanes*8, entropyThisTime, writeDevRandom); outputMultiplier -= numLanes/4; entropy -= entropyThisTime; } } // Initialize the Infinite Noise Multiplier USB ineterface. static bool initializeUSB(FT_HANDLE *ftdic, char **message) { *message = NULL; // Open FTDI device based on FT240X vendor & product IDs if (FT_Open(0, ftdic) != FT_OK) { *message = "Can't find Infinite Noise Multiplier\n"; return false; } // Set high baud rate if (FT_SetBaudRate(*ftdic, 30000) != FT_OK) { *message = "Setting baud rate failed\n"; return false; } // Enable syncrhonous bitbang mode if (FT_SetBitMode(*ftdic, MASK, BITMODE_SYNCBB) != FT_OK) { *message = "Can't enable bit-bang mode\n"; return false; } // Just test to see that we can write and read. uint8_t buf[64] = {0,}; DWORD bytesWritten; if(FT_Write(*ftdic, buf, 64, &bytesWritten) != FT_OK || bytesWritten != 64) { *message = "USB write failed\n"; return false; } DWORD bytesRead; if(FT_Read(*ftdic, buf, 64, &bytesRead) != FT_OK || bytesRead != 64) { *message = "USB read failed\n"; return false; } return true; } /* // Return the difference in the times as a double in microseconds. static double diffTime(struct timespec *start, struct timespec *end) { uint32_t seconds = end->tv_sec - start->tv_sec; int32_t nanoseconds = end->tv_nsec - start->tv_nsec; return seconds*1e6 + nanoseconds/1000.0; } */ int main(int argc, char **argv) { FT_HANDLE ftdic; bool raw = false; bool debug = false; bool writeDevRandom = false; bool noOutput = false; char* outFileName = NULL; uint32_t outputMultiplier = 2; int xArg; // Process arguments for(xArg = 1; xArg < argc; xArg++) { if(!strcmp(argv[xArg], "--raw")) { raw = true; } else if(!strcmp(argv[xArg], "--debug")) { debug = true; } else if(!strcmp(argv[xArg], "--dev-random")) { writeDevRandom = true; } else if(!strcmp(argv[xArg], "--no-output")) { noOutput = true; } else if(!strcmp(argv[xArg], "--multiplier") && xArg+1 < argc) { xArg++; outputMultiplier = atoi(argv[xArg]); if(outputMultiplier == 0) { fputs("Multiplier must be > 0\n", stderr); return 1; } } else if(!strcmp(argv[xArg], "--help") || !strcmp(argv[xArg], "--?") || xArg < argc-1) { // Got a help option or something unexpected that's not the final arg fputs( "Usage: infnoise [options] [outFile]\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" " --multiplier - write 256 bits * value for each 512 bits written to\n" " the Keccak sponge\n" " --no-output - do not write random output data\n", stderr); return 1; } else { // Final unparsed arg == output file specified outFileName = argv[xArg]; } } FILE* outFile; if (outFileName) { outFile = _fsopen(outFileName, "wb", _SH_DENYWR); if (!outFile) { fprintf(stderr, "Unable to open file %s\n", argv[xArg]); return 1; } } else { outFile = stdout; fflush(outFile); int result = _setmode(_fileno(outFile), _O_BINARY); if (result == -1) { fprintf(stderr, "Cannot set binary mode for outFile"); return 1; } } /* if(writeDevRandom) { inmWriteEntropyStart(BUFLEN/8, debug); } */ if(!inmHealthCheckStart(PREDICTION_BITS, DESIGN_K, debug)) { fputs("Can't initialize health checker\n", stderr); return 1; } uint8_t keccakState[KeccakPermutationSizeInBytes]; KeccakInitializeState(keccakState); char *message; if(!initializeUSB(&ftdic, &message)) { // Sometimes have to do it twice - not sure why //ftdi_usb_close(&ftdic); if(!initializeUSB(&ftdic, &message)) { fputs(message, stderr); return 1; } } // Endless loop: set SW1EN and SW2EN alternately uint32_t i; uint8_t outBuf[BUFLEN], inBuf[BUFLEN]; for(i = 0; i < BUFLEN; i++) { // Alternate Ph1 and Ph2 - maybe should have both off in between outBuf[i] = i & 1? (1 << SWEN2) : (1 << SWEN1); outBuf[i] |= makeAddress(i & 0xf); } uint64_t good = 0; /* uint64_t bad = 0; */ while(true) { /* struct timespec start; clock_gettime(CLOCK_REALTIME, &start); */ DWORD numBytes; if(FT_Write(ftdic, outBuf, BUFLEN, &numBytes) != FT_OK || numBytes != BUFLEN) { fputs("USB write failed\n", stderr); return -1; } if(FT_Read(ftdic, inBuf, BUFLEN, &numBytes) != FT_OK || numBytes != BUFLEN) { fputs("USB read failed\n", stderr); return -1; } /* struct timespec end; clock_gettime(CLOCK_REALTIME, &end); uint32_t us = diffTime(&start, &end); //printf("diffTime:%u us\n", us); */ // if(us <= MAX_MICROSEC_FOR_SAMPLES) { uint8_t bytes[BUFLEN/8]; uint32_t entropy = extractBytes(bytes, inBuf, raw); if(!noOutput && inmHealthCheckOkToUseData() && inmEntropyOnTarget(entropy, BUFLEN)) { processBytes(outFile, keccakState, bytes, entropy, raw, writeDevRandom, outputMultiplier); } good++; /* } else { bad++; } */ //if(((good + bad) & 0xff) == 0) { //printf("Good %lu, bad %lu\n", good, bad); //} fflush(stdout); fflush(stderr); } return 0; }