389 lines
14 KiB
C
389 lines
14 KiB
C
/* Driver for the Infinite Noise Multiplier USB stick */
|
|
|
|
// Required to include clock_gettime
|
|
#define _POSIX_C_SOURCE 200809L
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__FreeBSD__)
|
|
#include <fcntl.h>
|
|
#endif
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <sys/types.h>
|
|
#include <ftdi.h> // requires <sys/types.h>
|
|
#include <getopt.h>
|
|
#include "infnoise.h"
|
|
#include "libinfnoise.h"
|
|
#include "libinfnoise_private.h"
|
|
#include "KeccakF-1600-interface.h"
|
|
|
|
static void initOpts(struct opt_struct *opts) {
|
|
opts->outputMultiplier = 0u;
|
|
opts->daemon = false;
|
|
opts->debug = false;
|
|
opts->devRandom = false;
|
|
opts->noOutput = false;
|
|
opts->listDevices = false;
|
|
opts->raw = false;
|
|
opts->version = false;
|
|
opts->help = false;
|
|
opts->none = false;
|
|
opts->pidFileName =
|
|
opts->serial = NULL;
|
|
}
|
|
|
|
// getopt_long(3) options descriptor
|
|
|
|
static struct option longopts[] = {{"raw", no_argument, NULL, 'r'},
|
|
{"debug", no_argument, NULL, 'D'},
|
|
{"dev-random", no_argument, NULL, 'R'},
|
|
{"no-output", no_argument, NULL, 'n'},
|
|
{"multiplier", required_argument, NULL, 'm'},
|
|
{"pidfile", required_argument, NULL, 'p'},
|
|
{"serial", required_argument, NULL, 's'},
|
|
{"daemon", no_argument, NULL, 'd'},
|
|
{"list-devices", no_argument, NULL, 'l'},
|
|
{"version", no_argument, NULL, 'v'},
|
|
{"help", no_argument, NULL, 'h'},
|
|
{NULL, 0, NULL, 0}};
|
|
|
|
//
|
|
//// Write the bytes to either stdout, or /dev/random.
|
|
//bool outputBytes(uint8_t *bytes, uint32_t length, uint32_t entropy, bool writeDevRandom, char **message) {
|
|
// if(!writeDevRandom)
|
|
// {
|
|
// if(fwrite(bytes, 1, length, stdout) != length) {
|
|
// *message = "Unable to write output from Infinite Noise Multiplier";
|
|
// return false;
|
|
// }
|
|
// } else {
|
|
//#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__FreeBSD__)
|
|
// // quell compiler warning about unused variable.
|
|
// static int devRandomFD = -1;
|
|
// (void)entropy;
|
|
//
|
|
// if (devRandomFD < 0)
|
|
// devRandomFD = open("/dev/random",O_WRONLY);
|
|
// if (devRandomFD < 0) {
|
|
// *message = "Unable to open random(4)";
|
|
// return false;
|
|
// };
|
|
// // we are not trapping EINT and EAGAIN; as the random(4) driver seems
|
|
// // to not treat partial writes as not an error. So we think that comparing
|
|
// // to length is fine.
|
|
// //
|
|
// if (write(devRandomFD, bytes, length) != length) {
|
|
// *message = "Unable to write output from Infinite Noise Multiplier to random(4)";
|
|
// return false;
|
|
// }
|
|
//#endif
|
|
//#if defined(__APPLE__)
|
|
// *message = "macOS doesn't support writes to entropy pool";
|
|
// entropy = 0; // suppress warning
|
|
// return false;
|
|
//#endif
|
|
//#ifdef LINUX
|
|
// inmWaitForPoolToHaveRoom();
|
|
// inmWriteEntropyToPool(bytes, length, entropy);
|
|
//#endif
|
|
// }
|
|
// return true;
|
|
//}
|
|
//
|
|
//uint32_t processBytes(uint8_t *bytes, uint8_t *result, uint32_t entropy,
|
|
// bool raw, bool writeDevRandom, uint32_t outputMultiplier, bool noOutput,
|
|
// char **message, bool *errorFlag) {
|
|
// //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 (!noOutput) {
|
|
// if (!outputBytes(bytes, BUFLEN/8u, entropy, writeDevRandom, message)) {
|
|
// *errorFlag = true;
|
|
// return 0; // write failed
|
|
// }
|
|
// } else {
|
|
// 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".
|
|
// KeccakAbsorb(keccakState, bytes, BUFLEN/64u);
|
|
// uint8_t dataOut[16u*8u];
|
|
// if(outputMultiplier == 0u) {
|
|
// // Output all the bytes of entropy we have
|
|
// KeccakExtract(keccakState, dataOut, (entropy + 63u)/64u);
|
|
// if (!noOutput) {
|
|
// if (!outputBytes(dataOut, entropy/8u, entropy & 0x7u, writeDevRandom, message)) {
|
|
// *errorFlag = true;
|
|
// return 0;
|
|
// }
|
|
// } else {
|
|
// if (result != NULL) {
|
|
// memcpy(result, dataOut, entropy/8u * sizeof(uint8_t));
|
|
// }
|
|
// }
|
|
// return entropy/8u;
|
|
// }
|
|
//
|
|
// // Output 256*outputMultipler bits.
|
|
// uint32_t numBits = outputMultiplier*256u;
|
|
// uint32_t bytesWritten = 0u;
|
|
//
|
|
// while(numBits > 0u) {
|
|
// // Write up to 1024 bits at a time.
|
|
// uint32_t bytesToWrite = 1024u/8u;
|
|
// if(bytesToWrite > numBits/8u) {
|
|
// bytesToWrite = numBits/8u;
|
|
// }
|
|
// KeccakExtract(keccakState, dataOut, bytesToWrite/8u);
|
|
// uint32_t entropyThisTime = entropy;
|
|
// if(entropyThisTime > 8u*bytesToWrite) {
|
|
// entropyThisTime = 8u*bytesToWrite;
|
|
// }
|
|
// if (!noOutput) {
|
|
// if (!outputBytes(dataOut, bytesToWrite, entropyThisTime, writeDevRandom, message)) {
|
|
// *errorFlag = true;
|
|
// return 0;
|
|
// }
|
|
// } else {
|
|
// //memcpy(result + bytesWritten, dataOut, bytesToWrite * sizeof(uint8_t)); //doesn't work?
|
|
// // alternative: loop through dataOut and append array elements to result..
|
|
// if (result != NULL) {
|
|
// for (uint32_t i =0; i < bytesToWrite; i++ ) {
|
|
// result[bytesWritten + i] = dataOut[i];
|
|
// }
|
|
// }
|
|
// }
|
|
// bytesWritten += bytesToWrite;
|
|
// numBits -= bytesToWrite*8u;
|
|
// entropy -= entropyThisTime;
|
|
// if(numBits > 0u) {
|
|
// KeccakPermutation(keccakState);
|
|
// }
|
|
// }
|
|
// if(bytesWritten != outputMultiplier*(256u/8u)) {
|
|
// *message = "Internal error outputing bytes";
|
|
// *errorFlag = true;
|
|
// return 0;
|
|
// }
|
|
// return bytesWritten;
|
|
//}
|
|
|
|
int main(int argc, char **argv) {
|
|
struct ftdi_context ftdic;
|
|
struct opt_struct opts;
|
|
int ch;
|
|
bool multiplierAssigned = false;
|
|
|
|
initOpts(&opts);
|
|
|
|
// Process arguments
|
|
while ((ch = getopt_long(argc, argv, "rDRnm:p:s:dlvh", longopts, NULL)) !=
|
|
-1) {
|
|
switch (ch) {
|
|
case 'r':
|
|
opts.raw = true;
|
|
break;
|
|
case 'D':
|
|
opts.debug = true;
|
|
break;
|
|
case 'R':
|
|
opts.devRandom = true;
|
|
break;
|
|
case 'n':
|
|
opts.noOutput = true;
|
|
break;
|
|
case 'm':
|
|
multiplierAssigned = true;
|
|
int tmpOutputMult = atoi(optarg);
|
|
if (tmpOutputMult < 0) {
|
|
fputs("Multiplier must be >= 0\n", stderr);
|
|
return 1;
|
|
}
|
|
opts.outputMultiplier = tmpOutputMult;
|
|
break;
|
|
case 'p':
|
|
opts.pidFileName = optarg;
|
|
if (opts.pidFileName == NULL || !strcmp("", opts.pidFileName)) {
|
|
fputs("--pidfile without file name\n", stderr);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 's':
|
|
opts.serial = optarg;
|
|
if (opts.serial == NULL || !strcmp("", opts.serial)) {
|
|
fputs("--serial without value\n", stderr);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 'd':
|
|
opts.daemon = true;
|
|
break;
|
|
case 'l':
|
|
opts.listDevices = true;
|
|
break;
|
|
case 'v':
|
|
opts.version = true;
|
|
break;
|
|
case 'h':
|
|
opts.help = true;
|
|
break;
|
|
default:
|
|
opts.help = true;
|
|
opts.none = true;
|
|
}
|
|
}
|
|
|
|
if (opts.help) {
|
|
fputs("Usage: infnoise [options]\n"
|
|
"Options are:\n"
|
|
" -D, --debug - turn on some debug output\n"
|
|
" -R, --dev-random - write entropy to /dev/random instead of "
|
|
"stdout\n"
|
|
" -r, --raw - do not whiten the output\n"
|
|
" -m, --multiplier <value> - write 256 bits * value for each 512 bits written to\n"
|
|
" the Keccak sponge. Default of 0 means write all the entropy.\n"
|
|
" -n, --no-output - do not write random output data\n"
|
|
" -p, --pidfile <file> - write process ID to file\n"
|
|
" -d, --daemon - run in the background\n"
|
|
" -s, --serial <serial> - use specified device\n"
|
|
" -l, --list-devices - list available devices\n"
|
|
" -v, --version - show version information\n"
|
|
" -h, --help - this help output\n",
|
|
stdout);
|
|
if (opts.none) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// read environment variables, not overriding command line options
|
|
if (opts.serial == NULL) {
|
|
if (getenv("INFNOISE_SERIAL") != NULL) {
|
|
opts.serial = getenv("INFNOISE_SERIAL");
|
|
}
|
|
}
|
|
|
|
if (opts.debug == false) {
|
|
if (getenv("INFNOISE_DEBUG") != NULL) {
|
|
if (!strcmp("true",getenv("INFNOISE_DEBUG"))) {
|
|
opts.debug = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (multiplierAssigned == false) {
|
|
if (getenv("INFNOISE_MULTIPLIER") != NULL) {
|
|
int tmpOutputMult = atoi(getenv("INFNOISE_MULTIPLIER"));
|
|
if (tmpOutputMult < 0) {
|
|
fputs("Multiplier must be >= 0\n", stderr);
|
|
return 1;
|
|
}
|
|
multiplierAssigned = true;
|
|
opts.outputMultiplier = tmpOutputMult;
|
|
}
|
|
}
|
|
|
|
if(!multiplierAssigned && opts.devRandom) {
|
|
opts.outputMultiplier = 2u; // Don't throw away entropy when writing to /dev/random unless told to do so
|
|
}
|
|
|
|
if (opts.version) {
|
|
printf("GIT VERSION - %s\n", GIT_VERSION);
|
|
printf("GIT COMMIT - %s\n", GIT_COMMIT);
|
|
printf("GIT DATE - %s\n", GIT_DATE);
|
|
return 0;
|
|
}
|
|
|
|
char *message = "no data?";
|
|
bool errorFlag = false;
|
|
if (opts.listDevices) {
|
|
if(!listUSBDevices(&ftdic, &message)) {
|
|
fputs(message, stderr);
|
|
return 1;
|
|
}
|
|
//fputs(message, stdout); // todo: put list of devices to &message and print here, not in libinfnoise
|
|
return 0;
|
|
}
|
|
|
|
if (opts.devRandom) {
|
|
#ifdef LINUX
|
|
inmWriteEntropyStart(BUFLEN/8u, opts.debug); // todo: create method in libinfnoise.h for this?
|
|
// also todo: check superUser in this mode (it will fail silently if not :-/)
|
|
#endif
|
|
#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__FreeBSD__)
|
|
int devRandomFD = open("/dev/random", O_WRONLY);
|
|
if(devRandomFD < 0) {
|
|
fprintf(stderr, "Unable to open /dev/random\n");
|
|
exit(1);
|
|
}
|
|
close(devRandomFD);
|
|
#endif
|
|
#if defined(__APPLE__)
|
|
message = "dev/random not supported on macOS";
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
// Optionally run in the background and optionally write a PID-file
|
|
startDaemon(&opts);
|
|
|
|
// initialize USB device, health check and Keccak state (see libinfnoise)
|
|
if (!initInfnoise(&ftdic, opts.serial, &message, !opts.raw, opts.debug)) {
|
|
fprintf(stderr, "Error: %s\n", message);
|
|
return 1; // ERROR
|
|
}
|
|
|
|
// calculate output size based on the parameters:
|
|
// when using the multiplier, we need a result array of 32*MULTIPLIER - otherwise 64(BUFLEN/8) bytes
|
|
uint32_t resultSize;
|
|
if (opts.outputMultiplier == 0 || opts.raw == true) {
|
|
resultSize = BUFLEN/8u;
|
|
} else {
|
|
resultSize = opts.outputMultiplier*32u;
|
|
}
|
|
|
|
// endless loop
|
|
uint64_t totalBytesWritten = 0u;
|
|
while(true) {
|
|
uint8_t result[resultSize];
|
|
uint64_t prevTotalBytesWritten = totalBytesWritten;
|
|
|
|
if (opts.raw) {
|
|
totalBytesWritten = readRawData(&ftdic, result, &message, &errorFlag);
|
|
} else {
|
|
totalBytesWritten = readData(&ftdic, result, &message, &errorFlag, opts.outputMultiplier);
|
|
}
|
|
|
|
//totalBytesWritten += readData_private(&ftdic, NULL, &message, &errorFlag, opts.noOutput, opts.raw, opts.outputMultiplier, opts.devRandom); // calling libinfnoise's private readData method
|
|
|
|
if (errorFlag) {
|
|
fprintf(stderr, "Error: %s\n", message);
|
|
return 1;
|
|
}
|
|
|
|
if(opts.debug && (1u << 20u)*(totalBytesWritten/(1u << 20u)) > (1u << 20u)*(prevTotalBytesWritten/(1u << 20u))) {
|
|
fprintf(stderr, "Output %lu bytes\n", (unsigned long)totalBytesWritten);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|