/* Library for the Infinite Noise Multiplier USB stick */ // Required to include clock_gettime #define _POSIX_C_SOURCE 200809L #define INFNOISE_VENDOR_ID 0x0403 #define INFNOISE_PRODUCT_ID 0x6015 #include #include #include #include #include #include #include #include #include #include "libinfnoise_private.h" #include "KeccakF-1600-interface.h" #if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__FreeBSD__) #include #endif uint8_t keccakState[KeccakPermutationSizeInBytes]; bool initInfnoise(struct infnoise_context *context, char *serial, bool keccak, bool debug) { context->message=""; context->entropyThisTime=0; context->errorFlag=false; context->numBits=0; context->bytesWritten=0; prepareOutputBuffer(); // initialize health check if (!inmHealthCheckStart(PREDICTION_BITS, DESIGN_K, debug)) { context->message = "Can't initialize health checker"; return false; } // initialize USB if (!initializeUSB(&context->ftdic, &context->message, serial)) { // Sometimes have to do it twice - not sure why if (!initializeUSB(&context->ftdic, &context->message, serial)) { return false; } } // initialize keccak if (keccak) { KeccakInitialize(); KeccakInitializeState(keccakState); } // let healthcheck collect some data uint32_t maxWarmupRounds = 5000; uint32_t warmupRounds = 0; //bool errorFlag = false; while (!inmHealthCheckOkToUseData()) { readData(context, NULL, true, 1); warmupRounds++; } if (warmupRounds > maxWarmupRounds) { context->message = "Unable to collect enough entropy to initialize health checker."; return false; } return true; } uint8_t outBuf[BUFLEN]; void prepareOutputBuffer() { uint32_t i; // Endless loop: set SW1EN and SW2EN alternately for (i = 0u; i < BUFLEN; i++) { // Alternate Ph1 and Ph2 outBuf[i] = i & 1 ? (1 << SWEN2) : (1 << SWEN1); } } // 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. uint32_t extractBytes(uint8_t *bytes, uint8_t *inBuf, char **message, bool *errorFlag) { inmClearEntropyLevel(); uint32_t i; for (i = 0u; i < BUFLEN / 8u; i++) { uint32_t j; uint8_t byte = 0u; for (j = 0u; j < 8u; j++) { uint8_t val = inBuf[i * 8u + j]; uint8_t evenBit = (val >> COMP2) & 1u; uint8_t oddBit = (val >> COMP1) & 1u; bool even = j & 1u; // Use the even bit if j is odd uint8_t bit = even ? evenBit : oddBit; byte = (byte << 1u) | bit; // This is a good place to feed the bit from the INM to the health checker. if (!inmHealthCheckAddBit(evenBit, oddBit, even)) { *message = "Health check of Infinite Noise Multiplier failed!"; *errorFlag = true; return 0; } } bytes[i] = byte; } return inmGetEntropyLevel(); } // 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. If // outputMultiplier is 0, we output only as many bits as we measure in entropy. // This allows a user to generate hundreds of MiB per second if needed, for use // as cryptographic keys. uint32_t processBytes(uint8_t *bytes, uint8_t *result, uint32_t *entropy, uint32_t *numBits, uint32_t *bytesWritten, bool raw, uint32_t outputMultiplier) { //Use the lower of the measured entropy and the provable lower bound on //average entropy. if (*entropy > inmExpectedEntropyPerBit * BUFLEN / INM_ACCURACY) { *entropy = inmExpectedEntropyPerBit * BUFLEN / INM_ACCURACY; } if (raw) { // In raw mode, we just output raw data from the INM. if (result != NULL) { memcpy(result, bytes, BUFLEN / 8u * sizeof(uint8_t)); } return BUFLEN / 8u; } // 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 squeezing data out to ensure 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". uint8_t resultSize; if (outputMultiplier <= 2) { resultSize = 64u; } else { resultSize = 128u; } uint8_t dataOut[resultSize]; KeccakAbsorb(keccakState, bytes, BUFLEN / 64u); if (outputMultiplier == 0u) { // Output all the bytes of entropy we have KeccakExtract(keccakState, dataOut, (*entropy + 63u) / 64u); if (result != NULL) { memcpy(result, dataOut, *entropy / 8u * sizeof(uint8_t)); } return *entropy / 8u; } // Output 256*outputMultipler bits (in chunks of 1024) // only the first 1024 now, if (*numBits == 0u) { *numBits = outputMultiplier*256u; *bytesWritten = 0u; // Output up to 1024 bits at a time. uint32_t bytesToWrite = 1024u / 8u; if (bytesToWrite > *numBits / 8u) { bytesToWrite = *numBits / 8u; } KeccakExtract(keccakState, result, bytesToWrite / 8u); KeccakPermutation(keccakState); *bytesWritten = bytesToWrite; *numBits -= bytesToWrite * 8u; } return *bytesWritten; } // Return the difference in the times as a double in microseconds. 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 * 1.0e6 + nanoseconds / 1000.0; } bool isSuperUser(void) { return (geteuid() == 0); } // Return a list of all infinite noise multipliers found. devlist_node listUSBDevices(char **message) { struct ftdi_context ftdic; ftdi_init(&ftdic); struct ftdi_device_list *devlist; struct ftdi_device_list *curdev; char manufacturer[128], description[128], serial[128]; int i = 0; // search devices int rc = ftdi_usb_find_all(&ftdic, &devlist, INFNOISE_VENDOR_ID, INFNOISE_PRODUCT_ID); if (rc < 0) { if (!isSuperUser()) { *message = "Can't find Infinite Noise Multiplier. Try running as super user?"; } else { *message = "Can't find Infinite Noise Multiplier."; } return NULL; } devlist_node return_list = malloc(sizeof(struct infnoise_devlist_node)); devlist_node current_entry = return_list; for (curdev = devlist; curdev != NULL; i++) { rc = ftdi_usb_get_strings(&ftdic, curdev->dev, manufacturer, 128, description, 128, serial, 128); if (rc < 0) { if (!isSuperUser()) { *message = "Can't find Infinite Noise Multiplier. Try running as super user?"; } else { sprintf(*message, "ftdi_usb_get_strings failed: %d (%s)", rc, ftdi_get_error_string(&ftdic)); } return NULL; } current_entry->id = i; strcpy(current_entry->serial, serial); strcpy(current_entry->manufacturer, manufacturer); strcpy(current_entry->description, description); if (curdev->next) { current_entry->next = malloc(sizeof(struct infnoise_devlist_node)); current_entry = current_entry->next; } else { current_entry->next = NULL; } curdev = curdev->next; } return return_list; } // Initialize the Infinite Noise Multiplier USB interface. bool initializeUSB(struct ftdi_context *ftdic, char **message, char *serial) { ftdi_init(ftdic); struct ftdi_device_list *devlist; // search devices int rc = 0; if ((rc = ftdi_usb_find_all(ftdic, &devlist, INFNOISE_VENDOR_ID, INFNOISE_PRODUCT_ID)) < 0) { *message = "Can't find Infinite Noise Multiplier"; return false; } // only one found, or no serial given if (rc >= 0) { if (serial == NULL) { // more than one found AND no serial given if (rc >= 2) { *message = "Multiple Infnoise TRNGs found and serial not specified, using the first one!"; } if (ftdi_usb_open(ftdic, INFNOISE_VENDOR_ID, INFNOISE_PRODUCT_ID) < 0) { if (!isSuperUser()) { *message = "Can't open Infinite Noise Multiplier. Try running as super user?"; } else { #ifdef LINUX *message = "Can't open Infinite Noise Multiplier."; #endif #if defined(__APPLE__) *message = "Can't open Infinite Noise Multiplier. sudo kextunload -b com.FTDI.driver.FTDIUSBSerialDriver ? sudo kextunload -b com.apple.driver.AppleUSBFTDI ?"; #endif } return false; } } else { // serial specified rc = ftdi_usb_open_desc(ftdic, INFNOISE_VENDOR_ID, INFNOISE_PRODUCT_ID, NULL, serial); if (rc < 0) { if (!isSuperUser()) { *message = "Can't find Infinite Noise Multiplier. Try running as super user?"; } else { *message = "Can't find Infinite Noise Multiplier with given serial"; } return false; } } } // Set high baud rate rc = ftdi_set_baudrate(ftdic, 30000); if (rc == -1) { *message = "Invalid baud rate"; return false; } else if (rc == -2) { *message = "Setting baud rate failed"; return false; } else if (rc == -3) { *message = "Infinite Noise Multiplier unavailable"; return false; } rc = ftdi_set_bitmode(ftdic, MASK, BITMODE_SYNCBB); if (rc == -1) { *message = "Can't enable bit-bang mode"; return false; } else if (rc == -2) { *message = "Infinite Noise Multiplier unavailable\n"; return false; } // Just test to see that we can write and read. uint8_t buf[64u] = {0u,}; if (ftdi_write_data(ftdic, buf, 64) != 64) { *message = "USB write failed"; return false; } if (ftdi_read_data(ftdic, buf, 64) != 64) { *message = "USB read failed"; return false; } return true; } uint32_t readData(struct infnoise_context *context, uint8_t *result, bool raw, uint32_t outputMultiplier) { // check if data can be squeezed from the keccak sponge from previous state (or we need to collect some new entropy to get numBits >0) if (context->numBits > 0u) { // squeeze the sponge! // Output up to 1024 bits at a time. uint32_t bytesToWrite = 1024u / 8u; if (bytesToWrite > context->numBits / 8u) { bytesToWrite = context->numBits / 8u; } KeccakExtract(keccakState, result, bytesToWrite / 8u); KeccakPermutation(keccakState); context->bytesWritten += bytesToWrite; context->numBits -= bytesToWrite * 8u; return bytesToWrite; } else { // collect new entropy uint8_t inBuf[BUFLEN]; struct timespec start; clock_gettime(CLOCK_REALTIME, &start); // write clock signal if (ftdi_write_data(&context->ftdic, outBuf, BUFLEN) != BUFLEN) { context->message = "USB write failed"; context->errorFlag = true; } // and read 512 byte from the internal buffer (in synchronous bitbang mode) if (ftdi_read_data(&context->ftdic, inBuf, BUFLEN) != BUFLEN) { context->message = "USB read failed"; context->errorFlag = true; return 0; } struct timespec end; clock_gettime(CLOCK_REALTIME, &end); uint32_t us = diffTime(&start, &end); if (us <= MAX_MICROSEC_FOR_SAMPLES) { uint8_t bytes[BUFLEN / 8u]; context->entropyThisTime = extractBytes(bytes, inBuf, &context->message, &context->errorFlag); if (context->errorFlag) { // todo: message? return 0; } // call health check and return bytes if OK if (inmHealthCheckOkToUseData() && inmEntropyOnTarget(context->entropyThisTime, BUFLEN)) { return processBytes(bytes, result, &context->entropyThisTime, &context->numBits, &context->bytesWritten, raw, outputMultiplier); } } } return 0; }