diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..669af4d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "software/tests/test_helper/bats-support"] + path = software/tests/test_helper/bats-support + url = https://github.com/ztombol/bats-support +[submodule "software/tests/test_helper/bats-assert"] + path = software/tests/test_helper/bats-assert + url = https://github.com/ztombol/bats-assert diff --git a/software/Makefile b/software/Makefile index 2479ef1..b74b382 100644 --- a/software/Makefile +++ b/software/Makefile @@ -4,11 +4,10 @@ GIT_DATE := $(firstword $(shell git --no-pager show --date=iso-strict --forma PREFIX = $(DESTDIR)/usr/local -CFLAGS = -Wall -Wextra -Werror -std=c99 -O3 -I Keccak -I /usr/include/libftdi1 \ +CFLAGS = -Wall -Wextra -Werror -std=c99 -O3 -fPIC -I Keccak -I /usr/include/libftdi1 \ -DGIT_VERSION=\"$(GIT_VERSION)\"\ -DGIT_COMMIT=\"$(GIT_COMMIT)\"\ -DGIT_DATE=\"$(GIT_DATE)\"\ - -DBUILD_DATE=\"$(BUILD_DATE)\" FOUND = $(shell /sbin/ldconfig -p | grep --silent libftdi.so && echo found) ifeq ($(FOUND), found) @@ -17,15 +16,42 @@ else FTDI= -lftdi1 endif -all: infnoise +all: libinfnoise.a libinfnoise.so infnoise -infnoise: infnoise.c infnoise.h healthcheck.c writeentropy.c daemon.c Keccak/KeccakF-1600-reference.c Keccak/brg_endian.h - $(CC) $(CFLAGS) -o infnoise infnoise.c healthcheck.c writeentropy.c daemon.c Keccak/KeccakF-1600-reference.c $(FTDI) -lm -lrt +infnoise: libinfnoise.a infnoise.o daemon.o + $(CC) $(CFLAGS) -o infnoise infnoise.o daemon.o libinfnoise.a $(FTDI) -lm -lrt -L. -linfnoise + +%.o: %.c infnoise.h libinfnoise.h + $(CC) -c -o $@ $< $(CFLAGS) + +KeccakF-1600-reference.o: Keccak/KeccakF-1600-reference.c Keccak/KeccakF-1600-interface.h Keccak/brg_endian.h + $(CC) -c -o $@ $< $(CFLAGS) + +# static lib compiled into infnoise binary +libinfnoise.o: libinfnoise.c libinfnoise.h libinfnoise_private.h healthcheck.c + $(CC) $(CFLAGS) -c libinfnoise.c + +libinfnoise.a: libinfnoise.o healthcheck.o KeccakF-1600-reference.o writeentropy.o + ar rcs libinfnoise.a libinfnoise.o healthcheck.o KeccakF-1600-reference.o writeentropy.o + ranlib libinfnoise.a + +# shared lib +libinfnoise.so: libinfnoise.o healthcheck.o KeccakF-1600-reference.o writeentropy.o + $(CC) $(CFLAGS) -fvisibility=hidden -o libinfnoise.so libinfnoise.o healthcheck.o KeccakF-1600-reference.o writeentropy.o -Wl,--version-script=libinfnoise.version $(FTDI) -lm -shared + +libs: libinfnoise.a clean: - $(RM) infnoise + $(RM) infnoise *.o *.a *.gch *.so libinfnoise-example -install: +install-lib: libinfnoise.so + install -d $(PREFIX)/include + install -m 0644 libinfnoise.h $(PREFIX)/include + install -d $(PREFIX)/lib + install -m 0644 libinfnoise.so $(PREFIX)/lib + ldconfig $(PREFIX)/lib + +install: infnoise install -d $(PREFIX)/sbin install -m 0755 infnoise $(PREFIX)/sbin/ install -d $(PREFIX)/lib/udev/rules.d/ diff --git a/software/build-scripts/README.md b/software/build-scripts/README.md index a7a8fe6..44cbf02 100644 --- a/software/build-scripts/README.md +++ b/software/build-scripts/README.md @@ -7,7 +7,7 @@ and can create packages of the following types: - RPM (CentOS, Fedora) - ArchLinux -The packages get signed afterwards and made available as Github releases +The packages are signed and made available as Github releases and via the apt repository described in software/README. During the build the GitHub release and version information are compiled into the binary. diff --git a/software/build-scripts/build.sh b/software/build-scripts/build.sh index 2accfba..b55de8c 100755 --- a/software/build-scripts/build.sh +++ b/software/build-scripts/build.sh @@ -59,3 +59,24 @@ echo "Architecture: $ARCH" >> build/DEBIAN/control dpkg -b build/ infnoise-tools_${VERSION}_${ARCH}.deb rm -rf build +cd .. + +### build libinfnoise ### +rm -rf build + +mkdir -p build/usr/lib +mkdir -p build/usr/include + +make libinfnoise.so + +cp libinfnoise.so build/usr/lib/ +cp libinfnoise.h build/usr/include + +mkdir -p build/DEBIAN +cp build-scripts/control.debian.lib build/DEBIAN/control +echo "Version: $VERSION" >> build/DEBIAN/control +echo "Architecture: $ARCH" >> build/DEBIAN/control + +dpkg -b build/ libinfnoise_${VERSION}_${ARCH}.deb + +rm -rf build diff --git a/software/build-scripts/control.debian.lib b/software/build-scripts/control.debian.lib new file mode 100644 index 0000000..a148847 --- /dev/null +++ b/software/build-scripts/control.debian.lib @@ -0,0 +1,8 @@ +Package: libinfnoise +Essential: no +Section: security +Depends: libftdi1 +Priority: optional +Maintainer: Manuel Domke +Installed-Size: 12K +Description: Infinite Noise TRNG library diff --git a/software/daemon.c b/software/daemon.c index 2fa1ff2..e7c9dd2 100644 --- a/software/daemon.c +++ b/software/daemon.c @@ -48,6 +48,3 @@ void startDaemon(struct opt_struct* opts) { // Child } -bool isSuperUser(void) { - return (geteuid() == 0); -} diff --git a/software/examples/README.md b/software/examples/README.md new file mode 100644 index 0000000..9fb063f --- /dev/null +++ b/software/examples/README.md @@ -0,0 +1,26 @@ +# Usage examples for Infinite Noise + +## Integrate the binary to python + +See the python examples `serial-numbers.py` and `randomserver.py` to see how you could integrate it with python. + +#### serial-numbers.py +An extended version of this script was used to generate the serial numbers of all Infinite Noise's made by 13-37.org. The numbers generated by this simpler one are not guaranteed to be unique, thats why I use the UNIQUE constraint of a database when storing them. + +This simple version just prints the serials to stdout. Call like this: + + $ ./serial-numbers.py -c 3 -l 8 + 0A8BBA15 + 0341F762 + 813DC0BD + +#### randomserver.py + +A simple webserver based on the web.py framework to serve random data via a REST interface. An example is hosted at https://rng.13-37.org (running on a Raspberry Pi in Amsterdam, thanks to pcextreme.nl!) + +It has only two resources: `/get` and `/status`. + +## libinfnoise + +TODO + diff --git a/software/examples/libinfnoise/Makefile b/software/examples/libinfnoise/Makefile new file mode 100644 index 0000000..1401560 --- /dev/null +++ b/software/examples/libinfnoise/Makefile @@ -0,0 +1,26 @@ +GIT_VERSION := $(shell git --no-pager describe --tags --always) +GIT_COMMIT := $(shell git rev-parse --verify HEAD) +GIT_DATE := $(firstword $(shell git --no-pager show --date=iso-strict --format="%ad" --name-only)) + +PREFIX = $(DESTDIR)/usr/local + +CFLAGS = -Wall -Wextra -Werror -std=c99 -O3 -fPIC -I Keccak -I /usr/include/libftdi1 \ + -DGIT_VERSION=\"$(GIT_VERSION)\"\ + -DGIT_COMMIT=\"$(GIT_COMMIT)\"\ + -DGIT_DATE=\"$(GIT_DATE)\"\ + +LDFLAGS= -shared + +FTDI := $(shell libftdi-config --libs) + +all: libinfnoise-example libinfnoise-example-raw + +libinfnoise-example: example.c + $(CC) $(CFLAGS) -o libinfnoise-example example.c $(FTDI) -lm -lrt -linfnoise + +libinfnoise-example-raw: example.c + $(CC) $(CFLAGS) -o libinfnoise-example-raw example-raw.c $(FTDI) -lm -lrt -linfnoise + +clean: + $(RM) libinfnoise-example* *.o + diff --git a/software/examples/libinfnoise/example-raw.c b/software/examples/libinfnoise/example-raw.c new file mode 100644 index 0000000..53d84d3 --- /dev/null +++ b/software/examples/libinfnoise/example-raw.c @@ -0,0 +1,46 @@ +/* +This is a very simple example to use libinfnoise in raw output mode. +*/ + +#include +#include +#include + +int main() +{ + // parameters + char *serial=NULL; // can be set to a specific serial, NULL uses the first found device + bool debug=true; // debug mode (health monitor writes to stderr) + + char *message; + bool errorFlag = false; + + // initialize hardware and health monitor + struct ftdi_context ftdic; + if (!initInfnoise(&ftdic, serial, &message, false, debug)) { + fputs(message, stderr); + return 1; // ERROR + } + + // read and print in a loop (until 1M) + uint32_t totalBytesWritten = 0; + while (totalBytesWritten < 1000000) { + uint8_t result[64];// result is always 64 bytes in raw mode + + // read data returns the number of bytes written to result array + uint64_t bytesWritten = readRawData(&ftdic, result, &message, &errorFlag); + totalBytesWritten += bytesWritten; + + // check for errors + // note: bytesWritten is also 0 in this case, but an errorFlag is needed as + // bytesWritten can also be 0 when data hasn't passed the health monitor. + if (errorFlag) { + fprintf(stderr, "Error: %s\n", message); + return 1; + } + + // print bytes to stdout + fwrite(result, 1, bytesWritten, stdout); + } + return 0; +} diff --git a/software/examples/libinfnoise/example.c b/software/examples/libinfnoise/example.c new file mode 100644 index 0000000..0344d39 --- /dev/null +++ b/software/examples/libinfnoise/example.c @@ -0,0 +1,57 @@ +/* +This is a simple example to use libinfnoise with whitened and multiplied output. +*/ + +#include +#include +#include + +int main() +{ + // parameters + char *serial=NULL; // can be set to a specific serial, NULL uses the first found device + bool initKeccak = true; // initialize Keccak sponge (used for whitening) + uint32_t multiplier = 10u; // multiplier for whitening + bool debug = false; // debug mode (health monitor writes to stderr) + + char *message; + bool errorFlag = false; + + // initialize hardware and health monitor + struct ftdi_context ftdic; + if (!initInfnoise(&ftdic, serial, &message, true, debug)) { + fputs(message, stderr); + 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 (multiplier == 0 || initKeccak == false) { + resultSize = BUFLEN/8u; + } else { + resultSize = multiplier*32u; + } + + // read and print in a loop (until 1M is read) + uint64_t totalBytesWritten = 0u; + while (totalBytesWritten < 1000000) { + uint8_t result[resultSize]; + + // readRawData returns the number of bytes written to result array + uint64_t bytesWritten = readData(&ftdic, result, &message, &errorFlag, multiplier); + totalBytesWritten += bytesWritten; + + // check for errors + // note: bytesWritten is also 0 in this case, but an errorFlag is needed as + // bytesWritten can also be 0 when data hasn't passed the health monitor. + if (errorFlag) { + fprintf(stderr, "Error: %s\n", message); + return 1; + } + + // print as many bytes + fwrite(result, 1, bytesWritten, stdout); + } + return 0; +} diff --git a/software/examples/random-server.py b/software/examples/random-server.py new file mode 100755 index 0000000..1e8e6ad --- /dev/null +++ b/software/examples/random-server.py @@ -0,0 +1,94 @@ +#!/usr/bin/python + +import shlex +import subprocess +from threading import Thread +import time +import web + +urls = ( + '/', 'help', + '/get', 'GetRandomData', +# '/reg', 'Register', + '/status', 'GetPoolStatus' +) + +RESPONSE_SIZE=8192 + +MIN_POOL_SIZE=1 # in MB +MAX_POOL_SIZE=32 # in MB + +class help: + def GET(self): + message= "" + message+= "Infinite Noise webservice" + message+= "

Usage:

" + message+= "" + message+= "" + message+= "" +# print "/regtodo" + message+= "
requesthelp
/statusshow current buffer status
/getget " + str(RESPONSE_SIZE) + "bytes (binary format)
" + message+= "" + return message + +class GetRandomData: + def GET(self): + return POOL.getData() + +class GetPoolStatus: + def GET(self): + return POOL.getStatus() + +class RandPool(object): + minPoolSize = (MIN_POOL_SIZE * 1024 * 1024) / RESPONSE_SIZE + maxPoolSize = (MAX_POOL_SIZE * 1024 * 1024) / RESPONSE_SIZE + poolFillmark = -1 + randomPool = list() + process = None + do_run = True + + def __init__(self): + command = "sudo /usr/sbin/infnoise --multiplier 10" + self.process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE) + for i in range(0, self.minPoolSize): + self.randomPool.append(self.process.stdout.read(RESPONSE_SIZE)) + self.poolFillmark+= 1 + thread2 = Thread(target=self.monitorPool) + thread2.start() + + def monitorPool(self): + while getattr(self, "do_run", True): + if self.poolFillmark < self.maxPoolSize: # keep pool at 100% + print(str(self.poolFillmark)+ "/" + str(self.maxPoolSize)) + self.__refill_pool__() + time.sleep(1) + print("Stopping as you wish.") + self.process.kill() + + def __refill_pool__(self): + print("refilling pool - current level: " + str(self.poolFillmark)) + for i in range(self.poolFillmark, self.maxPoolSize): + self.randomPool.append(self.process.stdout.read(RESPONSE_SIZE)) + self.poolFillmark += 1 + self.randomPool.reverse() + print("refilled pool - new level: " + str(self.poolFillmark)) + + def getData(self): + if self.poolFillmark <= 0: + web.ctx.status = '503 Service currently unavailable - Out of entropy error' + return "0" + value = self.randomPool[self.poolFillmark] + self.randomPool[self.poolFillmark] = None + self.poolFillmark -= 1 + return value + + def getStatus(self): + return str(self.poolFillmark) + "/" + str(self.maxPoolSize) + +POOL = RandPool() + +if __name__ == '__main__': + app = web.application(urls, globals()) + app.run() + POOL.do_run = False + diff --git a/software/examples/serial-numbers.py b/software/examples/serial-numbers.py new file mode 100755 index 0000000..eeae404 --- /dev/null +++ b/software/examples/serial-numbers.py @@ -0,0 +1,36 @@ +#!/usr/bin/python + +import subprocess +import argparse + +parser = argparse.ArgumentParser(description='Generate random serial numbers') +parser.add_argument('-l', dest='length', action='store', default=8, + help='length of each serial number', type=int) +parser.add_argument('-c', dest='count', action='store', default=10, + help='length of each serial number', type=int) + +def main(): + args = parser.parse_args() # parse arguments + unique_serials_generated = 0 # counter for serials inserted successfully. + while unique_serials_generated < args.count: # loop until enough unique serials are created (because some could fail) + serials_list = readHexStrings(args.count - unique_serials_generated, args.length) # read $count hex-strings of fixed $length + for serial in serials_list: + if ("\n" in serial) or (" " in serial): + continue # dont use serials with newlines or spaces + unique_serials_generated += 1 # counter for serials + serial = serial.upper() + print(serial) + +def readHexStrings(count, length): + list = [] + infnoise_proc = subprocess.Popen(["infnoise"], stdout=subprocess.PIPE) # run infinite noise driver and + bin2hex_proc = subprocess.Popen(["infnoise-bin2hex"], stdin=infnoise_proc.stdout, # feed its stdout to the bin2hex utility, + stdout=subprocess.PIPE) # you could also skip this and do the bin2hex conversion in python + while len(list) < count and infnoise_proc.poll() == None: + list.append(bin2hex_proc.stdout.read(length)) + infnoise_proc.terminate() + bin2hex_proc.terminate() + return list + +if __name__ == '__main__': + main() diff --git a/software/healthcheck.c b/software/healthcheck.c index 6e3c27d..98f7773 100644 --- a/software/healthcheck.c +++ b/software/healthcheck.c @@ -24,7 +24,7 @@ confirmed. #include #include #include -#include "infnoise.h" +#include "libinfnoise_private.h" #define INM_MIN_DATA 80000u #define INM_MIN_SAMPLE_SIZE 100u @@ -91,11 +91,11 @@ static void resetStats(void) { // Initialize the health check. N is the number of bits used to predict the next bit. // At least 8 bits must be used, and no more than 30. In general, we should use bits // large enough so that INM output will be uncorrelated with bits N samples back in time. -bool inmHealthCheckStart(uint8_t N, double K, struct opt_struct *opts) { +bool inmHealthCheckStart(uint8_t N, double K, bool debug) { if(N < 1u || N > 30u) { return false; } - inmDebug = opts->debug; + inmDebug = debug; inmNumBitsOfEntropy = 0u; inmCurrentProbability = 1.0; inmK = K; @@ -297,6 +297,7 @@ bool inmEntropyOnTarget(uint32_t entropy, uint32_t numBits) { } #ifdef TEST_HEALTHCHECK +#include "infnoise.h" // Compare the ability to predict with 1 fewer bits and see how much less accurate we are. static void checkLSBStatsForNBits(uint8_t N) { @@ -375,7 +376,7 @@ int main() { //double K = sqrt(2.0); double K = 1.82; uint8_t N = 16u; - inmHealthCheckStart(N, K, &opts); + inmHealthCheckStart(N, K, false); srand(time(NULL)); double A = (double)rand()/RAND_MAX; // Simulating INM double noiseAmplitude = 1.0/(1u << 10); diff --git a/software/infnoise.c b/software/infnoise.c index bc0f7c7..1770af7 100644 --- a/software/infnoise.c +++ b/software/infnoise.c @@ -3,9 +3,6 @@ // Required to include clock_gettime #define _POSIX_C_SOURCE 200809L -#define INFNOISE_VENDOR_ID 0x0403 -#define INFNOISE_PRODUCT_ID 0x6015 - #include #include #include @@ -16,229 +13,17 @@ #include #include // requires #include "infnoise.h" +#include "libinfnoise.h" +#include "libinfnoise_private.h" #include "KeccakF-1600-interface.h" -// 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) { - 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)) { - fputs("Health check of Infinite Noise Multiplier failed!\n", stderr); - exit(1); - } - } - bytes[i] = byte; - } - return inmGetEntropyLevel(); -} - -// Write the bytes to either stdout, or /dev/random. -static void outputBytes(uint8_t *bytes, uint32_t length, uint32_t entropy, struct opt_struct *opts) { - if(!opts->devRandom) { - if(fwrite(bytes, 1, length, stdout) != length) { - fputs("Unable to write output from Infinite Noise Multiplier\n", stderr); - exit(1); - } - } else { - inmWaitForPoolToHaveRoom(); - inmWriteEntropyToPool(bytes, length, entropy); - } -} - -// 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 cryptogrpahic keys. -static uint32_t processBytes(uint8_t *keccakState, uint8_t *bytes, uint32_t entropy, struct opt_struct* opts) { - //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(opts->raw) { - // In raw mode, we just output raw data from the INM. - outputBytes(bytes, BUFLEN/8u, entropy, opts); - return BUFLEN/8u; - } - // Note that BUFLEN has to be less than 1600 by enough to make the sponge secure, - // since outputing 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/64u); - uint8_t dataOut[16u*8u]; - if(opts->outputMultiplier == 0u) { - // Output all the bytes of entropy we have - KeccakExtract(keccakState, dataOut, (entropy + 63u)/64u); - outputBytes(dataOut, entropy/8u, entropy & 0x7u, opts); - return entropy/8u; - } - - // Output 256*outputMultipler bytes. - uint32_t numBits = opts->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; - } - outputBytes(dataOut, bytesToWrite, entropyThisTime, opts); - bytesWritten += bytesToWrite; - numBits -= bytesToWrite*8u; - entropy -= entropyThisTime; - if(numBits > 0u) { - KeccakPermutation(keccakState); - } - } - if(bytesWritten != opts->outputMultiplier*(256u/8u)) { - fprintf(stderr, "Internal error outputing bytes\n"); - exit(1); - } - return bytesWritten; -} - -// Return a list of all infinite noise multipliers found. -static bool listUSBDevices(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()) { - fprintf(stderr, "Can't find Infinite Noise Multiplier. Try running as super user?\n"); - } else { - fprintf(stderr, "Can't find Infinite Noise Multiplier\n"); - } - } - for (curdev = devlist; curdev != NULL; i++) { - printf("Device: %d, ", i); - rc = ftdi_usb_get_strings(ftdic, curdev->dev, manufacturer, 128, description, 128, serial, 128); - if (rc < 0) { - if (!isSuperUser()) { - fprintf(stderr, "Can't find Infinite Noise Multiplier. Try running as super user?\n"); - } - fprintf(stderr, "ftdi_usb_get_strings failed: %d (%s)\n", rc, ftdi_get_error_string(ftdic)); - return false; - } - printf("Manufacturer: %s, Description: %s, Serial: %s\n", manufacturer, description, serial); - curdev = curdev->next; - } - return true; -} - - -// Initialize the Infinite Noise Multiplier USB interface. -static 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\n"; - 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) { - fprintf(stderr,"Multiple Infnoise TRNGs found and serial not specified, using the first one!\n"); - } - 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?\n"; - } else { - *message = "Can't open Infinite Noise Multiplier\n"; - } - 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?\n"; - } else { - *message = "Can't find Infinite Noise Multiplier with given serial\n"; - } - return false; - } - } - } - - // Set high baud rate - rc = ftdi_set_baudrate(ftdic, 30000); - if(rc == -1) { - *message = "Invalid baud rate\n"; - return false; - } else if(rc == -2) { - *message = "Setting baud rate failed\n"; - return false; - } else if(rc == -3) { - *message = "Infinite Noise Multiplier unavailable\n"; - return false; - } - rc = ftdi_set_bitmode(ftdic, MASK, BITMODE_SYNCBB); - if(rc == -1) { - *message = "Can't enable bit-bang mode\n"; - 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\n"; - return false; - } - if(ftdi_read_data(ftdic, buf, 64) != 64) { - *message = "USB read failed\n"; - return false; - } - return true; -} - static void initOpts(struct opt_struct *opts) { opts->outputMultiplier = 0u; - opts->daemon = - opts->debug = - opts->devRandom = - opts->noOutput = - opts->listDevices = + opts->daemon = false; + opts->debug = false; + opts->devRandom = false; + opts->noOutput = false; + opts->listDevices = false; opts->raw = false; opts->version = false; opts->help = false; @@ -247,13 +32,6 @@ static void initOpts(struct opt_struct *opts) { opts->serial = NULL; } -// Return the differnece 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*1.0e6 + nanoseconds/1000.0; -} - int main(int argc, char **argv) { struct ftdi_context ftdic; @@ -332,7 +110,6 @@ int main(int argc, char **argv) } } - // read environment variables, not overriding command line options if (opts.serial == NULL) { if (getenv("INFNOISE_SERIAL") != NULL) { @@ -371,69 +148,44 @@ int main(int argc, char **argv) return 0; } + char *message = "no data?"; + bool errorFlag = false; if (opts.listDevices) { - listUSBDevices(&ftdic); + 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) { + 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 :-/) + } + // Optionally run in the background and optionally write a PID-file startDaemon(&opts); - if(opts.devRandom) { - inmWriteEntropyStart(BUFLEN/8u, &opts); - } - - if(!inmHealthCheckStart(PREDICTION_BITS, DESIGN_K, &opts)) { - fputs("Can't intialize health checker\n", stderr); - return 1; - } - KeccakInitialize(); - uint8_t keccakState[KeccakPermutationSizeInBytes]; - KeccakInitializeState(keccakState); - - char *message; - if(!initializeUSB(&ftdic, &message, opts.serial)) { - // Sometimes have to do it twice - not sure why - if(!initializeUSB(&ftdic, &message, opts.serial)) { - fputs(message, stderr); - return 1; - } - } - - // Endless loop: set SW1EN and SW2EN alternately - uint32_t i; - uint8_t outBuf[BUFLEN], inBuf[BUFLEN]; - for(i = 0u; i < BUFLEN; i++) { - // Alternate Ph1 and Ph2 - outBuf[i] = i & 1? (1 << SWEN2) : (1 << SWEN1); + // initialize USB device, health check and Keccak state (see libinfnoise) + if (!initInfnoise(&ftdic, opts.serial, &message, !opts.raw, opts.debug)) { + fputs(message, stderr); + return 1; // ERROR } + // endless loop uint64_t totalBytesWritten = 0u; while(true) { - struct timespec start; - clock_gettime(CLOCK_REALTIME, &start); + uint64_t prevTotalBytesWritten = totalBytesWritten; + totalBytesWritten += readData_private(&ftdic, NULL, &message, &errorFlag, opts.noOutput, opts.raw, opts.outputMultiplier, opts.devRandom); // calling libinfnoise's private readData method - if(ftdi_write_data(&ftdic, outBuf, BUFLEN) != BUFLEN) { - fputs("USB write failed\n", stderr); + if (errorFlag) { + fprintf(stderr, "Error: %s\n", message); return 1; } - if(ftdi_read_data(&ftdic, inBuf, BUFLEN) != BUFLEN) { - fputs("USB read failed\n", stderr); - return 1; - } - 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]; - uint32_t entropy = extractBytes(bytes, inBuf); - if(!opts.noOutput && inmHealthCheckOkToUseData() && inmEntropyOnTarget(entropy, BUFLEN)) { - uint64_t prevTotalBytesWritten = totalBytesWritten; - totalBytesWritten += processBytes(keccakState, bytes, entropy, &opts); - if(opts.debug && (1u << 20u)*(totalBytesWritten/(1u << 20u)) > (1u << 20u)*(prevTotalBytesWritten/(1u << 20u))) { - fprintf(stderr, "Output %lu bytes\n", (unsigned long)totalBytesWritten); - } - } + + if(opts.debug && (1u << 20u)*(totalBytesWritten/(1u << 20u)) > (1u << 20u)*(prevTotalBytesWritten/(1u << 20u))) { + fprintf(stderr, "Output %lu bytes\n", (unsigned long)totalBytesWritten); } } return 0; diff --git a/software/infnoise.h b/software/infnoise.h index 3646ab5..aea1bd2 100644 --- a/software/infnoise.h +++ b/software/infnoise.h @@ -1,34 +1,7 @@ #include #include #include - -// Required accuracy of estimated vs measured entropy in health monitor -#define INM_ACCURACY 1.03 - -// The FT240X has a 512 byte buffer. Must be multiple of 64 -// We also write this in one go to the Keccak sponge, which is at most 1600 bits -#define BUFLEN 512u - -// This is how many previous bits are used to predict the next bit from the INM -#define PREDICTION_BITS 14u - -// This is the maximum time we allow to pass to perform the I/O operations, since long -// delays can reduce entropy from the INM. -#define MAX_MICROSEC_FOR_SAMPLES 5000u - -// This is the gain of each of the two op-amp stages in the INM -#define DESIGN_K 1.84 - -#define BITMODE_SYNCBB 0x4 - -// This defines which pins on the FT240X are used -#define COMP1 1u -#define COMP2 4u -#define SWEN1 2u -#define SWEN2 0u - -// All data bus bits of the FT240X are outputs, except COMP1 and COMP2 -#define MASK (0xffu & ~(1u << COMP1) & ~(1u << COMP2)) +#include // Structure for parsed command line options struct opt_struct { @@ -46,20 +19,4 @@ struct opt_struct { char *serial; // Name of selected device }; -bool inmHealthCheckStart(uint8_t N, double K, struct opt_struct *opts); -void inmHealthCheckStop(void); -bool inmHealthCheckAddBit(bool evenBit, bool oddBit, bool even); -bool inmHealthCheckOkToUseData(void); -double inmHealthCheckEstimateK(void); -double inmHealthCheckEstimateEntropyPerBit(void); -uint32_t inmGetEntropyLevel(void); -void inmClearEntropyLevel(void); -bool inmEntropyOnTarget(uint32_t entropy, uint32_t bits); -void inmWriteEntropyStart(uint32_t bufLen, struct opt_struct *opts); -void inmWriteEntropyToPool(uint8_t *bytes, uint32_t length, uint32_t entropy); -void inmWaitForPoolToHaveRoom(void); -void inmDumpStats(void); void startDaemon(struct opt_struct *opts); -bool isSuperUser(void); - -extern double inmK, inmExpectedEntropyPerBit; diff --git a/software/libinfnoise.c b/software/libinfnoise.c new file mode 100644 index 0000000..0dc654d --- /dev/null +++ b/software/libinfnoise.c @@ -0,0 +1,418 @@ +/* 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 "libinfnoise_private.h" +#include "libinfnoise.h" +#include "KeccakF-1600-interface.h" + +uint8_t keccakState[KeccakPermutationSizeInBytes]; +bool initInfnoise(struct ftdi_context *ftdic,char *serial, char **message, bool keccak, bool debug) { + prepareOutputBuffer(); + + // initialize health check + if (!inmHealthCheckStart(PREDICTION_BITS, DESIGN_K, debug)) { + *message="Can't initialize health checker"; + return false; + } + + // initialize USB + if(!initializeUSB(ftdic, message, serial)) { + // Sometimes have to do it twice - not sure why + if(!initializeUSB(ftdic, message, serial)) { + return false; + } + } + + // initialize keccak + if (keccak) { + KeccakInitialize(); + KeccakInitializeState(keccakState); + } + + // let healthcheck collect some data + uint32_t maxWarmupRounds = 500; + uint32_t warmupRounds = 0; + bool errorFlag = false; + while(!inmHealthCheckOkToUseData()) { + readData_private(ftdic, NULL, message, &errorFlag, false, true, 0, false); + warmupRounds++; + } + if (warmupRounds > maxWarmupRounds) { + *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(); +} + +// 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; +} + +// 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 { + inmWaitForPoolToHaveRoom(); + inmWriteEntropyToPool(bytes, length, entropy); + } + return true; +} + +bool isSuperUser(void) { + return (geteuid() == 0); +} + +// 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, + 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; +} + +// Return a list of all infinite noise multipliers found. +bool listUSBDevices(struct ftdi_context *ftdic, char** message) { + 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"; + } + } + + for (curdev = devlist; curdev != NULL; i++) { + //printf("Device: %d, ", 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?"; + return false; + } + //*message = "ftdi_usb_get_strings failed: %d (%s)\n", rc, ftdi_get_error_string(ftdic)); + return false; + } + + // print to stdout + printf("Manufacturer: %s, Description: %s, Serial: %s", manufacturer, description, serial); + curdev = curdev->next; + } + + return true; +} + +// 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 { + *message = "Can't open Infinite Noise Multiplier"; + } + 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 readRawData(struct ftdi_context *ftdic, uint8_t *result, char **message, bool *errorFlag) { + return readData_private(ftdic, result, message, errorFlag, false, true, 0, false); +} + +uint32_t readData(struct ftdi_context *ftdic, uint8_t *result, char **message, bool *errorFlag, uint32_t outputMultiplier) { + return readData_private(ftdic, result, message, errorFlag, false, false, outputMultiplier, false); +} + +uint32_t readData_private(struct ftdi_context *ftdic, uint8_t *result, char **message, bool *errorFlag, + bool noOutput, bool raw, uint32_t outputMultiplier, bool devRandom) { + uint8_t inBuf[BUFLEN]; + struct timespec start; + clock_gettime(CLOCK_REALTIME, &start); + + // write clock signal + if(ftdi_write_data(ftdic, outBuf, BUFLEN) != BUFLEN) { + *message = "USB write failed"; + *errorFlag = true; + } + + // and read 512 byte from the internal buffer (in synchronous bitbang mode) + if(ftdi_read_data(ftdic, inBuf, BUFLEN) != BUFLEN) { + *message = "USB read failed"; + *errorFlag = true; + } + + 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]; + uint32_t entropy = extractBytes(bytes, inBuf, message, errorFlag); + + // call health check and process bytes if OK + if (inmHealthCheckOkToUseData() && inmEntropyOnTarget(entropy, BUFLEN)) { + uint32_t byteswritten = processBytes(bytes, result, entropy, raw, devRandom, outputMultiplier, noOutput, message, errorFlag); + return byteswritten; + } + } + return 0; +} + +#ifdef LIB_EXAMPLE_PROGRAM +// example use of libinfnoise - with keccak +int main() { + char *serial=NULL; // use any device, can be set to a specific serial + + // initialize USB + struct ftdi_context ftdic; + initInfnoise(&ftdic, serial); + + // parameters for readData(..): + bool rawOutput = true; + uint32_t multiplier = 10u; + + // 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 (multiplier == 0 || rawOutput == true) { + resultSize = BUFLEN/8u; + } else { + resultSize = multiplier*32u; + } + + uint64_t totalBytesWritten = 0u; + + // read and print in a loop + while (totalBytesWritten < 100000) { + uint8_t result[resultSize]; + uint64_t bytesWritten = 0u; + bytesWritten = readData(&ftdic, keccakState, result, multiplier); + + // check for -1, indicating an error + totalBytesWritten += bytesWritten; + + // make sure to only read as many bytes as readData returned. Only those have passed the health check in this round (usually all) + fwrite(result, 1, bytesWritten, stdout); + } +} +#endif diff --git a/software/libinfnoise.h b/software/libinfnoise.h new file mode 100644 index 0000000..9246504 --- /dev/null +++ b/software/libinfnoise.h @@ -0,0 +1,17 @@ +#include +#include +#include +#include +#include + +// The FT240X has a 512 byte buffer. Must be multiple of 64 +// We also write this in one go to the Keccak sponge, which is at most 1600 bits +#define BUFLEN 512u + +bool listUSBDevices(struct ftdi_context *ftdic, char **message); + +bool initInfnoise(struct ftdi_context *ftdic, char *serial, char **message, bool keccak, bool debug); + +uint32_t readRawData(struct ftdi_context *ftdic, uint8_t *result, char **message, bool *errorFlag); + +uint32_t readData(struct ftdi_context *ftdic, uint8_t *result, char **message, bool *errorFlag, uint32_t outputMultiplier); diff --git a/software/libinfnoise.version b/software/libinfnoise.version new file mode 100644 index 0000000..c4c671a --- /dev/null +++ b/software/libinfnoise.version @@ -0,0 +1,5 @@ +libinfnoise { + global: listUSBDevices; initInfnoise; readRawData; readData; # explicitly list symbols to be exported + local: *; # hide everything else +}; + diff --git a/software/libinfnoise_private.h b/software/libinfnoise_private.h new file mode 100644 index 0000000..80478ef --- /dev/null +++ b/software/libinfnoise_private.h @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include + + +#define INFNOISE_VENDOR_ID 0x0403 +#define INFNOISE_PRODUCT_ID 0x6015 + +// Required accuracy of estimated vs measured entropy in health monitor +#define INM_ACCURACY 1.03 + +// This is how many previous bits are used to predict the next bit from the INM +#define PREDICTION_BITS 14u + +// This is the maximum time we allow to pass to perform the I/O operations, since long +// delays can reduce entropy from the INM. +#define MAX_MICROSEC_FOR_SAMPLES 5000u + +// This is the gain of each of the two op-amp stages in the INM +#define DESIGN_K 1.84 + +#define BITMODE_SYNCBB 0x4 + +// This defines which pins on the FT240X are used +#define COMP1 1u +#define COMP2 4u +#define SWEN1 2u +#define SWEN2 0u + +// All data bus bits of the FT240X are outputs, except COMP1 and COMP2 +#define MASK (0xffu & ~(1u << COMP1) & ~(1u << COMP2)) + + + +bool inmHealthCheckStart(uint8_t N, double K, bool debug); +void inmHealthCheckStop(void); +bool inmHealthCheckAddBit(bool evenBit, bool oddBit, bool even); +bool inmHealthCheckOkToUseData(void); +double inmHealthCheckEstimateK(void); +double inmHealthCheckEstimateEntropyPerBit(void); +uint32_t inmGetEntropyLevel(void); +void inmClearEntropyLevel(void); +bool inmEntropyOnTarget(uint32_t entropy, uint32_t bits); +void inmWriteEntropyStart(uint32_t bufLen, bool debug); +void inmWriteEntropyToPool(uint8_t *bytes, uint32_t length, uint32_t entropy); +void inmWaitForPoolToHaveRoom(void); +void inmDumpStats(void); + +extern double inmK, inmExpectedEntropyPerBit; + +bool initializeUSB(struct ftdi_context *ftdic, char **message, char *serial); +void prepareOutputBuffer(); + +struct timespec; +double diffTime(struct timespec *start, struct timespec *end); +uint32_t extractBytes(uint8_t *bytes, uint8_t *inBuf, char **message, bool *errorFlag); + +bool outputBytes(uint8_t *bytes, uint32_t length, uint32_t entropy, bool writeDevRandom, char **message); +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); + +uint32_t readData_private(struct ftdi_context *ftdic, uint8_t *result, char **message, bool *errorFlag, bool noOutput, bool raw, uint32_t outputMultiplier, bool devRandom); diff --git a/software/tests/README b/software/tests/README new file mode 100644 index 0000000..888bb81 --- /dev/null +++ b/software/tests/README @@ -0,0 +1 @@ +bats tests for the binary driver. diff --git a/software/tests/infnoise.bats b/software/tests/infnoise.bats new file mode 100644 index 0000000..2bebe98 --- /dev/null +++ b/software/tests/infnoise.bats @@ -0,0 +1,87 @@ +#!/usr/bin/env bats + +load 'test_helper/bats-support/load' +load 'test_helper/bats-assert/load' + +# Tests for the infnoise binary + +@test "test --raw output for expected entropy" { + # capture some data + TMP_FILE=`mktemp -u $BATS_TMPDIR/infnoise-test-XXXXXXX` + timeout 5s ./infnoise --raw > $TMP_FILE || true + + # run ent + run ent $TMP_FILE + + assert_line --index 0 --regexp '^Entropy = 7.2[5-9][0-9]+ bits per byte.$' + + # cleanup + rm $TMP_FILE +} + +@test "test whitened output for expected entropy" { + # capture some data + TMP_FILE=`mktemp -u $BATS_TMPDIR/infnoise-test-XXXXXXX` + timeout 5s ./infnoise > $TMP_FILE || true + + # run ent + run ent $TMP_FILE + + # check ent's result + assert_line --index 0 --regexp '^Entropy = 7.99[0-9]+ bits per byte.$' + + # cleanup + rm $TMP_FILE +} + +@test "test whitened output (multiplier=10) for expected entropy" { + # capture some data + TMP_FILE=`mktemp -u $BATS_TMPDIR/infnoise-test-XXXXXXX` + timeout 5s ./infnoise --multiplier 10 > $TMP_FILE || true + + # run ent + run ent $TMP_FILE + + assert_line --index 0 --regexp '^Entropy = 7.99[0-9]+ bits per byte.$' + + # cleanup + rm $TMP_FILE +} + +@test "test --no-output --debug" { + # capture some data + TMP_FILE=`mktemp -u $BATS_TMPDIR/infnoise-test-XXXXXXX` + run timeout 5s ./infnoise --no-output --debug + + echo $output + [ "$status" -eq 124 ] + + assert_line --index 0 --regexp '^Generated 1048576 bits. OK to use data. Estimated entropy per bit: 0\.[0-8][6-8][0-9]+, estimated K: 1\.8[1-5][0-9]+$' + assert_line --index 1 --regexp '^num1s:50.[0-9]+%, even misfires:0.[0-1][0-9]+%, odd misfires:0.[0-1][0-9]+%$' +} + +@test "test --list-devices" { + run ./infnoise --list-devices + echo $output + [ "$status" -eq 0 ] + + # FTDI serial: + assert_line --index 1 --regexp '^Manufacturer: FTDI, Description: FT240X USB FIFO, Serial: [0-9A-Z]+$' + + # 13-37.org serial: + assert_line --index 0 --regexp '^Manufacturer: 13-37.org, Description: Infinite Noise TRNG, Serial: [0-9A-F]+$' +} + +@test "test --serial with not connected serial (results in error)" { + run ./infnoise --serial 4711 + echo $output + [ "$status" -eq 1 ] + [ "${lines[0]}" = "Can't find Infinite Noise Multiplier. Try running as super user?" ] +} + +@test "test --help" { + run ./infnoise --help + echo $output + [ "$status" -eq 0 ] + [ "${lines[0]}" = "Usage: infnoise [options]" ] +} diff --git a/software/tests/test_helper/bats-assert b/software/tests/test_helper/bats-assert new file mode 160000 index 0000000..9f88b42 --- /dev/null +++ b/software/tests/test_helper/bats-assert @@ -0,0 +1 @@ +Subproject commit 9f88b4207da750093baabc4e3f41bf68f0dd3630 diff --git a/software/tests/test_helper/bats-support b/software/tests/test_helper/bats-support new file mode 160000 index 0000000..004e707 --- /dev/null +++ b/software/tests/test_helper/bats-support @@ -0,0 +1 @@ +Subproject commit 004e707638eedd62e0481e8cdc9223ad471f12ee diff --git a/software/tools/passgen.c b/software/tools/passgen.c index 4abd49e..a656df8 100644 --- a/software/tools/passgen.c +++ b/software/tools/passgen.c @@ -35,10 +35,10 @@ int main(int argc, char **argv) { uint32_t i; printf("password:"); for (i = 0u; i < keys; i++) { - uint32_t randVal = rollDie(26u, file); + uint32_t randVal = rollDie(32u, file); putchar('a' + randVal); } - printf("\nThis password has %.2f bits of entropy\n", log(pow(26.0, keys))/log(2)); + printf("\nThis password has %.2f bits of entropy\n", log(pow(32.0, keys))/log(2)); fclose(file); return 0; } diff --git a/software/writeentropy.c b/software/writeentropy.c index 4ecf044..33c832a 100644 --- a/software/writeentropy.c +++ b/software/writeentropy.c @@ -8,7 +8,7 @@ #include #include #include -#include "infnoise.h" +#include "libinfnoise.h" #define SIZE_PROC_FILENAME "/proc/sys/kernel/random/poolsize" #define FILL_PROC_FILENAME "/proc/sys/kernel/random/write_wakeup_threshold" @@ -40,9 +40,10 @@ static uint32_t readNumberFromFile(char *fileName) { } // Open /dev/random -void inmWriteEntropyStart(uint32_t bufLen, struct opt_struct* opts) { +void inmWriteEntropyStart(uint32_t bufLen, bool debug) { inmBufLen = bufLen; - inmDebug = opts->debug; + inmDebug = debug; + //inmDevRandomFD = open("/dev/random", O_WRONLY); inmDevRandomFD = open("/dev/random", O_RDWR); if(inmDevRandomFD < 0) {