diff options
author | Jeff Garzik <jgarzik@pobox.com> | 2005-10-25 02:49:12 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@pobox.com> | 2005-10-25 02:49:12 -0400 |
commit | 61af3de31a9a4d4928ebd468ce645b9596f46184 (patch) | |
tree | 5a3d700aa4ba2b70546a0614d3bcf1c9af2841cb | |
download | rng-tools-61af3de31a9a4d4928ebd468ce645b9596f46184.tar.gz |
Import rng-tools from private subversion repo.
-rw-r--r-- | .gitignore | 20 | ||||
-rw-r--r-- | AUTHORS | 3 | ||||
-rw-r--r-- | ChangeLog | 160 | ||||
-rw-r--r-- | Makefile.am | 23 | ||||
-rw-r--r-- | NEWS | 13 | ||||
-rw-r--r-- | README | 1 | ||||
-rwxr-xr-x | autogen.sh | 11 | ||||
-rw-r--r-- | configure.ac | 59 | ||||
-rw-r--r-- | contrib/Makefile.am | 3 | ||||
-rw-r--r-- | contrib/randstat.c | 29 | ||||
-rw-r--r-- | contrib/rngtest.c | 160 | ||||
-rw-r--r-- | exits.h | 31 | ||||
-rw-r--r-- | fips.c | 201 | ||||
-rw-r--r-- | fips.h | 72 | ||||
-rw-r--r-- | rngd.8.in | 82 | ||||
-rw-r--r-- | rngd.c | 214 | ||||
-rw-r--r-- | rngd.h | 65 | ||||
-rw-r--r-- | rngd_entsource.c | 106 | ||||
-rw-r--r-- | rngd_entsource.h | 42 | ||||
-rw-r--r-- | rngd_linux.c | 105 | ||||
-rw-r--r-- | rngd_linux.h | 44 | ||||
-rw-r--r-- | rngtest.1.in | 88 | ||||
-rw-r--r-- | rngtest.c | 424 | ||||
-rw-r--r-- | stats.c | 152 | ||||
-rw-r--r-- | stats.h | 71 |
25 files changed, 2179 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4f48cc9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +*.o + +Makefile +Makefile.in + +COPYING +INSTALL +missing +depcomp +install-sh +stamp-h1 +config.* +aclocal.m4 +configure +*.tar.gz +push + +.dotest +autom4te.cache +.deps @@ -0,0 +1,3 @@ +Philipp Rumpf +Jeff Garzik <jgarzik@pobox.com> +Henrique de Moraes Holschuh <hmh@debian.org> diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..07c57da --- /dev/null +++ b/ChangeLog @@ -0,0 +1,160 @@ +Tue May 09 2004 Henrique de Moraes Holschuh <hmh@debian.org> + + * rngd.h, rngd.c, rngd_linux.c, rngd.8.in: Let the user + set the fill watermark explicitly, using the new -W + command line option. This gets rid of RNDGETPOOL + usage, and of a hardcoded (yuck) setting of 50%. + This change will make rngd work right with 2.6 kernels. + +Thu Apr 15 2004 Jeff Garzik <jgarzik@pobox.com> + + * Makefile.am, configure.ac: put common code in a lib + +Tue Apr 6 2004 Henrique de Moraes Holschuh <hmh@debian.org> + + * rngd.c, rngtest.c: Add Copyright and license notices + to --version output, as per the GNU guidelines; + Improve --help output a little. + * rngtest.c: Cleanup logging, and exit with status 1 + when input drains before the first block is tested. + * rngtest.1.in: Minor text change. + + Preparatory cleanup for merging the multithreaded + code later: + * rngd.c: split globals to rngd.h; split linux + /dev/random functionality to rngd_linux.c; + split entropy source (/dev/hwrandom) functionality + to rngd_entsource.c. + * rngd.h, rngd_linux.h, rngd_linux.c, + rngd_entsource.h, rngd_entsource.c: add + +Tue Apr 6 2004 Jeff Garzik <jgarzik@pobox.com> + + * Release version 1.1. + +Fri Apr 5 2004 Henrique de Moraes Holschuh <hmh@debian.org> + + Add rngtest application: + * Makefile.am: build rngtest. + * configure.ac: process rngtest.1.in. + * stats.h/stats.c: add. Statistics based on ideas + from mtrngd.cpp by Martin Peck <coderman@peertech.org> + * exits.h: add. + * rngtest.c: add. + * contrib/Makefile.am: remove rngtest.c. + * contrib/rngtest.c: remove. + * AUTHORS: add myself + +Fri Apr 5 2004 Henrique de Moraes Holschuh <hmh@debian.org> + + * rngd.c: use C99 initializers syntax, and stop + compilation if build env. is incomplete + * fips.c, fips.h: s/FIPS_TESTS/N_FIPS_TESTS/ and + remove uneeded includes + * fips.c: reword error message when build env. is + incomplete, and also reorder some includes + * AUTHORS, configure.ac, fips.c, fips.h, rngd.c, + rngd.8.in: Update Jeff Garzik's email address, remove + outdated email address for Philipp Rumpf, on request + by Jeff Garzik. + * configure.ac, rngd.c: Change bugreport address to + Jeff Garzik's. + +Fri Apr 4 2004 Henrique de Moraes Holschuh <hmh@debian.org> + * Makefile.am: Add header and CVS Id tag; Do some cosmetic + reformating; Add rngd_SOURCES. + * rngd.c: move all FIPS test code to fips.h/fips.c + * fips.h, fips.c: add. + + Update comments with more FIPS 140-2 trivia. + + Use a context structure to hold the FIPS test data. + + Implement FIPS 140-2 4.9 Continuous Run test. + + Add constants with the test names and bitmask for + easier statistic reporting later. + +Fri Apr 3 2004 Henrique de Moraes Holschuh <hmh@debian.org> + + * autogen.sh: Add comments with the required versions + of the tools. Call aclocal before autoheader. + Use --copy for automake invocation. + Identify as version 1.1-devel. + * configure.in: rename to configure.ac + * configure.ac: Add GPL header. Convert to autoconf + 2.50 macros, enable AM_MAINTAINER_MODE and disable + useless cross-platform compatiblilty glue that + isn't used anywere + * acconfig.h: remove + * .cvsignore: update for new autotools + +Sat Jul 5 2003 Jeff Garzik <jgarzik@pobox.com> + + * contrib/rngtest.c, rngd.8.in: s/intel_rng/hwrandom/ + + Noticed by Olivier NICOLAS. + +Sat Jul 5 2003 Sami Farin <safari@users.sourceforge.net> + + "updated" to FIPS140-2 standard, it has a bit more + strict constraints on randomness.. about one out of + 1000 blocks read from /dev/urandom causes a failure. + + also a bugfix: + checks for EINTR in xread (maybe not necessary with + i810 driver?) + +Sat Jul 5 2003 Jeff Garzik <jgarzik@pobox.com> + + Rename to rng-tools, release version 1.0. + Rename input device to /dev/hwrandom in code and docs. + Rename config.h to rng-tools-config.h. + +Tue Mar 27 2001 Jeff Garzik <jgarzik@mandrakesoft.com> + + * rngd.c: Include config.h, pick up VERSION from + configure.in, via config.h. + +Mon Mar 26 2001 Philipp Rumpf <prumpf@mandrakesoft.com> + + * rngd.c: fail before the daemon() call if we + can't open /dev/random or /dev/intel_rng + +Mon Mar 26 2001 Philipp Rumpf <prumpf@mandrakesoft.com> + + * rngd.c: bugfixes, allow --timeout=0 to disable + periodical writes. + +Fri Mar 23 2001 Jeff Garzik <jgarzik@mandrakesoft.com> + + * rngd.c: Remove unused var. + Include stdlib.h for exit(3). + +Fri Mar 23 2001 Philipp Rumpf <prumpf@mandrakesoft.com> + + * rngd.c: fix argp_parse arguments + +Fri Mar 23 2001 Jeff Garzik <jgarzik@mandrakesoft.com> + + * configure.in: Change version in cvs + + * Makefile.am, configure.in, rngd.8.in: Add man page. + + * rngd.c: Update --help output, listing defaults. + Move 'arguments' local to top of file, call it + default_arguments. + +Fri Mar 23 2001 Philipp Rumpf <prumpf@mandrakesoft.com> + + * rngd.c: fix mixed-up options + +Fri Mar 23 2001 Philipp Rumpf <prumpf@mandrakesoft.com> + + * rngd.c: add argp support + * rngd.c: make random write granularity, poll timeout + command line options + * rngd.c: optionally daemonize + +Fri Mar 23 2001 Jeff Garzik <jgarzik@mandrakesoft.com> + + * Makefile.am, configure.in, contrib/Makefile.am, + autogen.sh, NEWS, ChangeLog, AUTHORS, README, + contrib/Makefile.am: Add autoconf/automake support. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..d0cec2d --- /dev/null +++ b/Makefile.am @@ -0,0 +1,23 @@ +## +## Toplevel Makefile.am for rng-tools +## + +SUBDIRS = contrib + +sbin_PROGRAMS = rngd +bin_PROGRAMS = rngtest +man_MANS = rngd.8 rngtest.1 +noinst_LIBRARIES = librngd.a + +rngd_SOURCES = rngd.h rngd.c rngd_entsource.h rngd_entsource.c \ + rngd_linux.h rngd_linux.c +rngd_LDADD = librngd.a + +rngtest_SOURCES = exits.h stats.h stats.c rngtest.c +rngtest_LDADD = librngd.a + +librngd_a_SOURCES = fips.h fips.c + + +EXTRA_DIST = autogen.sh + @@ -0,0 +1,13 @@ + +Version 2 (August 24, 2004): + +* Fixes and cleanups from Debian (Henrique de Moraes Holschuh) + Assures rngd works with 2.6 kernels. + +Version 1.1 (April 6, 2004): + +* update to recent autoconf/automake +* add new rngtest program +* various minor cleanups +* much better FIPS testing + @@ -0,0 +1 @@ +No documentation is good documentation. diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..9f98ef8 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# You need autoconf 2.5x, preferably 2.57 or later +# You need automake 1.7 or later. 1.6 might work. + +set -e + +aclocal +autoheader +automake --gnu --add-missing --copy +autoconf diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..763f548 --- /dev/null +++ b/configure.ac @@ -0,0 +1,59 @@ +dnl Process this file with autoconf 2.52+ to produce a configure script. +dnl +dnl Copyright (C) 2001 Philipp Rumpf +dnl Copyright (C) 2004 Henrique de Moraes Holschuh <hmh@debian.org> +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2 of the License, or +dnl (at your option) any later version. +dnl +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +AC_INIT(rng-tools, 2, [Jeff Garzik <jgarzik@pobox.com>]) +AC_PREREQ(2.52) +AC_CONFIG_SRCDIR([rngd.c]) +AM_INIT_AUTOMAKE([gnu]) +AC_CONFIG_HEADERS([rng-tools-config.h]) + +dnl Make sure anyone changing configure.ac/Makefile.am has a clue +AM_MAINTAINER_MODE + +dnl Checks for programs +AC_PROG_CC +AC_PROG_RANLIB +AC_PROG_GCC_TRADITIONAL + +dnl Checks for header files. +dnl AC_HEADER_STDC +dnl AC_CHECK_HEADERS(sys/ioctl.h unistd.h) + +dnl Checks for typedefs, structures, and compiler characteristics. +dnl AC_TYPE_SIZE_T +dnl AC_TYPE_PID_T + +dnl ----------------------------- +dnl Checks for required libraries +dnl ----------------------------- + +dnl ------------------------------------- +dnl Checks for optional library functions +dnl ------------------------------------- + +dnl ----------------- +dnl Configure options +dnl ----------------- + +dnl -------------------------- +dnl autoconf output generation +dnl -------------------------- + +AC_CONFIG_FILES([Makefile contrib/Makefile rngd.8 rngtest.1]) +AC_OUTPUT diff --git a/contrib/Makefile.am b/contrib/Makefile.am new file mode 100644 index 0000000..18c4fbc --- /dev/null +++ b/contrib/Makefile.am @@ -0,0 +1,3 @@ + +EXTRA_DIST = randstat.c + diff --git a/contrib/randstat.c b/contrib/randstat.c new file mode 100644 index 0000000..992f23c --- /dev/null +++ b/contrib/randstat.c @@ -0,0 +1,29 @@ + + +#include <sys/fcntl.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include <linux/types.h> +#include <linux/random.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> + +int main(int argc, char **argv) +{ + int random_fd; + int ent_count; + + random_fd = open("/dev/random", O_RDONLY); + + if (random_fd < 0) + return 1; + + if (ioctl(random_fd, RNDGETENTCNT, &ent_count) != 0) + return 1; + + printf("%d\n", ent_count); + + return 0; +} + diff --git a/contrib/rngtest.c b/contrib/rngtest.c new file mode 100644 index 0000000..26917d1 --- /dev/null +++ b/contrib/rngtest.c @@ -0,0 +1,160 @@ +/* + These are the Random Number Generator tests suggested by the + FIPS 140-1 spec section 4.11.1 (http://csrc.nist.gov/fips/fips1401.htm) + The Monobit, Poker, Runs, and Long Runs tests are implemented below. +*/ +#define RNG_DEVICE "/dev/hwrandom" +#define RNG_LOOPS 25 + + +#include <stdio.h> + +FILE *dev; + +char read_rng_byte() { + char random; + fscanf(dev,"%c",&random); + return random; +} + +int do_test() { + +unsigned char rbyte = 0; +int poker[16],runs[12],i,j; +double pokertest; +int longrun = 0; +int current = 0; +int rlength = 0; +int ones = 0; + +for(i=0; i<16; i++) { + poker[i] = 0; +} + +for(i=0; i<12; i++) { + runs[i] = 0; +} + +rlength = 999; +ones = 0; +for(i=0; i<2500; i++) { + rbyte = read_rng_byte(); + + // printf("%d: %x\n",i,rbyte); + + ones += rbyte & 1; + ones += (rbyte & 2)>>1; + ones += (rbyte & 4)>>2; + ones += (rbyte & 8)>>3; + ones += (rbyte & 16)>>4; + ones += (rbyte & 32)>>5; + ones += (rbyte & 64)>>6; + ones += (rbyte & 128)>>7; + + poker[rbyte>>4] += 1; + poker[rbyte & 15] += 1; + + /* Trick to make sure current != the first bit so we don't screw + up the first runlength */ + if(rlength == 999) { + current = !((rbyte & 128)>>7); + rlength = 1; + } + for(j=7; j>=0; j--) { + // printf("%d %d %d\n",rlength,current,((rbyte & 1<<j)>>j) ); + if(((rbyte & 1<<j)>>j) == current) { + rlength++; + + } + else { + /* If runlength is 1-6 count it in correct bucket. 0's go in + runs[0-5] 1's go in runs[6-11] hence the 6*current below */ + if(rlength < 6) { + runs[rlength - 1 + (6*current)]++; + } + if(rlength >= 6) { + runs[5 + (6*current)]++; + } + /* Check if we just failed longrun test */ + if(rlength > longrun) { + longrun = rlength; + } + rlength=1; + /* flip the current run type */ + current = (rbyte & 1<<j)>>j; + } + } +} + +/* add in the last (possibly incomplete) run */ +if(rlength <= 6) { + runs[rlength - 1 + (6*current)]++; +} +if(rlength > longrun) { + longrun = rlength; +} + +/* To poker test */ +pokertest = 0; +for(i=0; i<16; i++) { + // printf("P%d: %d\n",i,poker[i]); + pokertest += (double)(poker[i] * poker[i]); +} +pokertest = (16.0/5000.0) * pokertest - 5000.0; + +/* Data is all gathered, do the tests */ + printf("Ones: %d\nPokertest: %f\nRuns: %d %d %d %d %d %d\n %d %d %d %d %d %d\nLong Run: %d\n\n",ones,(float)pokertest,runs[0],runs[1],runs[2],runs[3],runs[4],runs[5],runs[6],runs[7],runs[8],runs[9],runs[10],runs[11],longrun); + + +if(! ((ones < 10346) && (ones > 9654)) ){ + printf(" RNG failed Monobit test.\n"); + return 1; +} + +if(! ((pokertest < 57.4) && (pokertest > 1.03)) ){ + printf(" RNG failed Poker test.\n"); + return 1; +} + +if(! ((runs[0] >= 2267) && (runs[0] <= 2733) && + (runs[1] >= 1079) && (runs[1] <= 1421) && + (runs[2] >= 502) && (runs[2] <= 748) && + (runs[3] >= 223) && (runs[3] <= 402) && + (runs[4] >= 90) && (runs[4] <= 223) && + (runs[5] >= 90) && (runs[5] <= 223) && + (runs[6] >= 2267) && (runs[6] <= 2733) && + (runs[7] >= 1079) && (runs[7] <= 1421) && + (runs[8] >= 502) && (runs[8] <= 748) && + (runs[9] >= 223) && (runs[9] <= 402) && + (runs[10] >= 90) && (runs[10] <= 223) && + (runs[11] >= 90) && (runs[11] <= 223)) ) { + printf(" RNG failed Runs test.\n"); + return 1; +} + +if(longrun >= 34) { + printf(" RNG failed LongRun test.\n"); + return 1; +} + + return 0; +} + +int main(int argv,char **argc) { + unsigned char random; + int i=0; + + if(! (dev = fopen(RNG_DEVICE,"r"))) { + printf("Open of "RNG_DEVICE" failed\n"); + exit(1); + } + + for(i=0; i<RNG_LOOPS; i++){ + printf("Test: %d\n",(i+1)); + if(do_test()) { + printf("Failed on test %d\n",i); + } + } + printf("RNG correctly completed %d FIPS tests.\n",i); + return 0; +} @@ -0,0 +1,31 @@ +/* + * exits.h -- Exit status + * + * Copyright (C) 2004 Henrique M. Holschuh <hmh@debian.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EXITS__H +#define EXITS__H + +/* Exit status */ +#define EXIT_FAIL 1 /* Exit due to error */ +#define EXIT_USAGE 10 /* Exit due to user error */ +#define EXIT_IOERR 11 /* Exit due to I/O error */ +#define EXIT_OSERR 12 /* Exit due to operating system error, + resource starvation, or another + non-app error */ +#endif /* EXITS__H */ @@ -0,0 +1,201 @@ +/* + * fips.c -- Performs FIPS 140-1/140-2 RNG tests + * + * Copyright (C) 2001 Philipp Rumpf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE + +#ifndef HAVE_CONFIG_H +#error Invalid or missing autoconf build environment +#endif + +#include "rng-tools-config.h" + +#include <unistd.h> +#include <string.h> + +#include "fips.h" + +/* + * Names for the FIPS tests, and bitmask + */ +const char *fips_test_names[N_FIPS_TESTS] = { + "FIPS 140-2(2001-10-10) Monobit", + "FIPS 140-2(2001-10-10) Poker", + "FIPS 140-2(2001-10-10) Runs", + "FIPS 140-2(2001-10-10) Long run", + "FIPS 140-2(2001-10-10) Continuous run" +}; +const unsigned int fips_test_mask[N_FIPS_TESTS] = { + FIPS_RNG_MONOBIT, FIPS_RNG_POKER, FIPS_RNG_RUNS, + FIPS_RNG_LONGRUN, FIPS_RNG_CONTINUOUS_RUN +}; + + +/* These are the startup tests suggested by the FIPS 140-1 spec section +* 4.11.1 (http://csrc.nist.gov/fips/fips1401.htm), and updated by FIPS +* 140-2 4.9, errata of 2001-10-10. FIPS 140-2, errata of 2002-12-03 +* removed all requirements for non-deterministic RNGs, and thus most of +* the tests we need are not mentioned in FIPS 140-2 anymore. We also +* implement FIPS 140-1 4.11.2/FIPS 140-2 4.9 Continuous Run test. +* +* The Monobit, Poker, Runs, and Long Runs tests are implemented below. +* This test must be run at periodic intervals to verify data is +* sufficiently random. If the tests are failed the RNG module shall +* no longer submit data to the entropy pool, but the tests shall +* continue to run at the given interval. If at a later time the RNG +* passes all tests it shall be re-enabled for the next period. +* +* The reason for this is that it is not unlikely that at some time +* during normal operation one of the tests will fail. This does not +* necessarily mean the RNG is not operating properly, it is just a +* statistically rare event. In that case we don't want to forever +* disable the RNG, we will just leave it disabled for the period of +* time until the tests are rerun and passed. +* +* For the continuous run test, we need to check all bits of data, so +* "periodic" above shall be read as "for every back-to-back block of +* 20000 bits". We verify 32 bits to accomodate the AMD TRNG, and +* to reduce false positives with other TRNGs. +*/ + + +/* + * fips_test_store - store 8 bits of entropy in FIPS + * internal test data pool + */ +static void fips_test_store(fips_ctx_t *ctx, unsigned int rng_data) +{ + int j; + + ctx->poker[rng_data >> 4]++; + ctx->poker[rng_data & 15]++; + + /* Note in the loop below rlength is always one less than the actual + run length. This makes things easier. */ + for (j = 7; j >= 0; j--) { + ctx->ones += ctx->current_bit = ((rng_data >> j) & 1); + if (ctx->current_bit != ctx->last_bit) { + /* If runlength is 1-6 count it in correct bucket. 0's go in + runs[0-5] 1's go in runs[6-11] hence the 6*current_bit below */ + if (ctx->rlength < 5) { + ctx->runs[ctx->rlength + + (6 * ctx->current_bit)]++; + } else { + ctx->runs[5 + (6 * ctx->current_bit)]++; + } + + /* Check if we just failed longrun test */ + if (ctx->rlength >= 25) + ctx->longrun = 1; + ctx->rlength = 0; + /* flip the current run type */ + ctx->last_bit = ctx->current_bit; + } else { + ctx->rlength++; + } + } +} + +int fips_run_rng_test (fips_ctx_t *ctx, const void *buf) +{ + int i, j; + int rng_test = 0; + unsigned char *rngdatabuf; + + if (!ctx) return -1; + if (!buf) return -1; + rngdatabuf = (unsigned char *)buf; + + for (i=0; i<FIPS_RNG_BUFFER_SIZE; i += 4) { + int new32 = rngdatabuf[i] | + ( rngdatabuf[i+1] << 8 ) | + ( rngdatabuf[i+2] << 16 ) | + ( rngdatabuf[i+3] << 24 ); + if (new32 == ctx->last32) rng_test |= FIPS_RNG_CONTINUOUS_RUN; + ctx->last32 = new32; + fips_test_store(ctx, rngdatabuf[i]); + fips_test_store(ctx, rngdatabuf[i+1]); + fips_test_store(ctx, rngdatabuf[i+2]); + fips_test_store(ctx, rngdatabuf[i+3]); + } + + /* add in the last (possibly incomplete) run */ + if (ctx->rlength < 5) + ctx->runs[ctx->rlength + (6 * ctx->current_bit)]++; + else { + ctx->runs[5 + (6 * ctx->current_bit)]++; + if (ctx->rlength >= 25) + rng_test |= FIPS_RNG_LONGRUN; + } + + if (ctx->longrun) { + rng_test |= FIPS_RNG_LONGRUN; + ctx->longrun = 0; + } + + /* Ones test */ + if ((ctx->ones >= 10275) || (ctx->ones <= 9725)) + rng_test |= FIPS_RNG_MONOBIT; + /* Poker calcs */ + for (i = 0, j = 0; i < 16; i++) + j += ctx->poker[i] * ctx->poker[i]; + /* 16/5000*1563176-5000 = 2.1632 */ + /* 16/5000*1576928-5000 = 46.1696 */ + if ((j > 1576928) || (j < 1563176)) + rng_test |= FIPS_RNG_POKER; + + if ((ctx->runs[0] < 2315) || (ctx->runs[0] > 2685) || + (ctx->runs[1] < 1114) || (ctx->runs[1] > 1386) || + (ctx->runs[2] < 527) || (ctx->runs[2] > 723) || + (ctx->runs[3] < 240) || (ctx->runs[3] > 384) || + (ctx->runs[4] < 103) || (ctx->runs[4] > 209) || + (ctx->runs[5] < 103) || (ctx->runs[5] > 209) || + (ctx->runs[6] < 2315) || (ctx->runs[6] > 2685) || + (ctx->runs[7] < 1114) || (ctx->runs[7] > 1386) || + (ctx->runs[8] < 527) || (ctx->runs[8] > 723) || + (ctx->runs[9] < 240) || (ctx->runs[9] > 384) || + (ctx->runs[10] < 103) || (ctx->runs[10] > 209) || + (ctx->runs[11] < 103) || (ctx->runs[11] > 209)) { + rng_test |= FIPS_RNG_RUNS; + } + + /* finally, clear out FIPS variables for start of next run */ + memset (ctx->poker, 0, sizeof (ctx->poker)); + memset (ctx->runs, 0, sizeof (ctx->runs)); + ctx->ones = 0; + ctx->rlength = -1; + ctx->current_bit = 0; + + return rng_test; +} + +void fips_init(fips_ctx_t *ctx, unsigned int last32) +{ + if (ctx) { + memset (ctx->poker, 0, sizeof (ctx->poker)); + memset (ctx->runs, 0, sizeof (ctx->runs)); + ctx->longrun = 0; + ctx->ones = 0; + ctx->rlength = -1; + ctx->current_bit = 0; + ctx->last_bit = 0; + ctx->last32 = last32; + } +} + @@ -0,0 +1,72 @@ +/* + * fips.h -- Performs FIPS 140-1/140-2 tests for RNGs + * + * Copyright (C) 2001 Philipp Rumpf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef FIPS__H +#define FIPS__H + +/* Size of a FIPS test buffer, do not change this */ +#define FIPS_RNG_BUFFER_SIZE 2500 + +/* Context for running FIPS tests */ +struct fips_ctx { + int poker[16], runs[12]; + int ones, rlength, current_bit, last_bit, longrun; + unsigned int last32; +}; +typedef struct fips_ctx fips_ctx_t; + +/* Initializes the context for FIPS tests. last32 contains + * 32 bits of RNG data to init the continuous run test */ +extern void fips_init(fips_ctx_t *ctx, unsigned int last32); + +/* + * Return values for fips_run_rng_test. These values are OR'ed together + * for all tests that failed. + */ +#define FIPS_RNG_MONOBIT 0x0001 /* FIPS 140-2 2001-10-10 monobit */ +#define FIPS_RNG_POKER 0x0002 /* FIPS 140-2 2001-10-10 poker */ +#define FIPS_RNG_RUNS 0x0004 /* FIPS 140-2 2001-10-10 runs */ +#define FIPS_RNG_LONGRUN 0x0008 /* FIPS 140-2 2001-10-10 long run */ +#define FIPS_RNG_CONTINUOUS_RUN 0x0010 /* FIPS 140-2 continuous run */ + +/* + * Names for the FIPS tests, and bitmask + */ +#define N_FIPS_TESTS 5 +extern const char *fips_test_names[N_FIPS_TESTS]; +extern const unsigned int fips_test_mask[N_FIPS_TESTS]; + +/* + * Runs the FIPS 140-1 4.11.1 and 4.11.2 tests, as updated by + * FIPS 140-2 4.9, errata from 2001-10-10 (which set more strict + * intervals for the tests to pass), on a buffer of size + * FIPS_RNG_BUFFER_SIZE, using the given context. + * + * FIPS 140-2, errata of 2002-12-03 removed tests for non-deterministic + * RNGs, other than Continuous Run test. + * + * This funtion returns 0 if all tests passed, or a bitmask + * with bits set for every test that failed. + * + * It returns -1 if either fips_ctx or buf is NULL. + */ +extern int fips_run_rng_test(fips_ctx_t *ctx, const void *buf); + +#endif /* FIPS__H */ diff --git a/rngd.8.in b/rngd.8.in new file mode 100644 index 0000000..3575784 --- /dev/null +++ b/rngd.8.in @@ -0,0 +1,82 @@ +.\" Copyright (C) 2001 Jeff Garzik -- jgarzik@pobox.com +.\" +.TH RNGD 8 "March 2001" "@PACKAGE@ @VERSION@" + +.SH NAME +rngd \- Check and feed random data from hardware device to kernel random device + +.SH SYNOPSIS +.B rngd +[\fB\-b\fR, \fB\-\-background\fR] +[\fB\-f\fR, \fB\-\-foreground\fR] +[\fB\-o\fR, \fB\-\-random-device=\fIfile\fR] +[\fB\-r\fR, \fB\-\-rng-device=\fIfile\fR] +[\fB\-s\fR, \fB\-\-random-step=\fInnn\fR] +[\fB\-W\fR, \fB\-\-fill-watermark=\fInnn\fR] +[\fB\-t\fR, \fB\-\-timeout=\fInnn\fR] +[\fB\-?\fR, \fB\-\-help\fR] +[\fB\-V\fR, \fB\-\-version\fR] +.RI + +.SH DESCRIPTION +This daemon feeds data from a random number generator to the kernel's +random number entropy pool, after first checking the data to ensure that +it is properly random. +.PP +The \fB\-f\fR or \fB\-\-foreground\fR options can be used to tell +\fBrngd\fR to avoid forking on startup. This is typically used for +debugging. The \fB\-f\fR or \fB\-\-foreground\fR options, which fork and put +\fBrngd\fR into the background automatically, are the default. +.PP +The \fB\-r\fR or \fB\-\-rng-device\fR options can be used to select an +alternate source of input, besides the default /dev/hwrandom. +The \fB\-o\fR or \fB\-\-random-device\fR options can be used to select +an alternate entropy output device, besides the default /dev/random. +Note that this device must support the Linux kernel /dev/random +ioctl API. +.PP +FIXME: document random-step and timeout + +.SH OPTIONS +.TP +\fB\-b\fR, \fB\-\-background\fR +Become a daemon (default) +.TP +\fB\-f\fR, \fB\-\-foreground\fR +Do not fork and become a daemon +.TP +\fB\-o\fI file\fR, \fB\-\-random-device=\fIfile\fR +Kernel device used for random number output +(default: /dev/random) +.TP +\fB\-r\fI file\fR, \fB\-\-rng-device=\fIfile\fR +Kernel device used for random number input +(default: /dev/hwrandom) +.TP +\fB\-s\fI nnn\fR, \fB\-\-random-step=\fInnn\fR +Number of bytes written to random-device at a time (default: 64) +.TP +\fB\-W\fI n\fR, \fB\-\-fill\-watermark=\fInnn\fR +Once we start doing it, feed entropy to \fIrandom-device\fR until at least +\fIfill-watermark\fR bits of entropy are available in its entropy pool (default: 2048). +Setting this too high will cause \fIrngd\fR to dominate the contents of the +entropy pool. Low values will hurt system performance during entropy +starves. Do not set \fIfill-watermark\fR above the size of the +entropy pool (usually 4096 bits). +.TP +\fB\-t\fI nnn\fR, \fB\-\-timeout=\fInnn\fR +Interval written to random-device when the entropy pool is full, in seconds, or 0 to disable (default: 60) +.TP +\fB\-?\fR, \fB\-\-help\fR +Give a short summary of all program options. +.TP +\fB\-V\fR, \fB\-\-version\fR +Print program version + +.SH AUTHORS +Philipp Rumpf +.br +Jeff Garzik \- jgarzik@pobox.com +.br +Matt Sottek + @@ -0,0 +1,214 @@ +/* + * rngd.c -- Random Number Generator daemon + * + * rngd reads data from a hardware random number generator, verifies it + * looks like random data, and adds it to /dev/random's entropy store. + * + * In theory, this should allow you to read very quickly from + * /dev/random; rngd also adds bytes to the entropy store periodically + * when it's full, which makes predicting the entropy store's contents + * harder. + * + * Copyright (C) 2001 Philipp Rumpf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE + +#ifndef HAVE_CONFIG_H +#error Invalid or missing autoconf build environment +#endif + +#include "rng-tools-config.h" + +#include <unistd.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <argp.h> +#include <syslog.h> + +#include "rngd.h" +#include "fips.h" +#include "exits.h" +#include "rngd_entsource.h" +#include "rngd_linux.h" + +/* + * Globals + */ + +/* Background/daemon mode */ +int am_daemon; /* Nonzero if we went daemon */ + +/* Command line arguments and processing */ +const char *argp_program_version = + "rngd " VERSION "\n" + "Copyright 2001-2004 Jeff Garzik\n" + "Copyright (c) 2001 by Philipp Rumpf\n" + "This is free software; see the source for copying conditions. There is NO " + "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."; + +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + +static char doc[] = + "Check and feed random data from hardware device to kernel entropy pool.\n"; + +static struct argp_option options[] = { + { "foreground", 'f', 0, 0, "Do not fork and become a daemon" }, + + { "background", 'b', 0, 0, "Become a daemon (default)" }, + + { "random-device", 'o', "file", 0, + "Kernel device used for random number output (default: /dev/random)" }, + + { "rng-device", 'r', "file", 0, + "Kernel device used for random number input (default: /dev/hwrandom)" }, + + { "random-step", 's', "nnn", 0, + "Number of bytes written to random-device at a time (default: 64)" }, + + { "fill-watermark", 'W', "n", 0, + "Do not stop feeding entropy to random-device until at least n bits of entropy are available in the pool (default: 2048), 0 <= n <= 4096" }, + + { "timeout", 't', "nnn", 0, + "Interval written to random-device when the entropy pool is full, in seconds (default: 60)" }, + + { 0 }, +}; + +static struct arguments default_arguments = { + .rng_name = "/dev/hwrandom", + .random_name = "/dev/random", + .poll_timeout = 60, + .random_step = 64, + .fill_watermark = 2048, + .daemon = 1, +}; +struct arguments *arguments = &default_arguments; + + +/* + * command line processing + */ +static error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + struct arguments *arguments = state->input; + + switch(key) { + case 'o': + arguments->random_name = arg; + break; + case 'r': + arguments->rng_name = arg; + break; + case 't': { + float f; + if (sscanf(arg, "%f", &f) == 0) + argp_usage(state); + else + arguments->poll_timeout = f; + break; + } + + case 'f': + arguments->daemon = 0; + break; + case 'b': + arguments->daemon = 1; + break; + case 's': + if (sscanf(arg, "%i", &arguments->random_step) == 0) + argp_usage(state); + break; + case 'W': { + int n; + if ((sscanf(arg, "%i", &n) == 0) || (n < 0) || (n > 4096)) + argp_usage(state); + else + arguments->fill_watermark = n; + break; + } + + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +static struct argp argp = { options, parse_opt, NULL, doc }; + + +static void do_loop(int random_step, + double poll_timeout) +{ + unsigned char buf[FIPS_RNG_BUFFER_SIZE]; + unsigned char *p; + int fips; + + for (;;) { + xread(buf, sizeof buf); + + fips = fips_run_rng_test(&fipsctx, buf); + + if (fips) { + message(LOG_DAEMON|LOG_ERR, "failed fips test\n"); + sleep(1); + continue; + } + + for (p = buf; p + random_step <= &buf[sizeof buf]; + p += random_step) { + random_add_entropy(p, random_step); + random_sleep(poll_timeout); + } + } +} + + +int main(int argc, char **argv) +{ + argp_parse(&argp, argc, argv, 0, 0, arguments); + + /* Init entropy source, and open TRNG device */ + init_entropy_source(arguments->rng_name); + + /* Init entropy sink and open random device */ + init_kernel_rng(arguments->random_name); + + if (arguments->daemon) { + am_daemon = 1; + + if (daemon(0, 0) < 0) { + fprintf(stderr, "can't daemonize: %s\n", + strerror(errno)); + return 1; + } + + openlog("rngd", 0, LOG_DAEMON); + } + + do_loop(arguments->random_step, + arguments->poll_timeout ? : -1.0); + + return 0; +} @@ -0,0 +1,65 @@ +/* + * rngd.h -- rngd globals + * + * Copyright (C) 2001 Philipp Rumpf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef RNGD__H +#define RNGD__H + +#define _GNU_SOURCE + +#include "rng-tools-config.h" + +#include <unistd.h> +#include <stdint.h> +#include <stdio.h> +#include <syslog.h> + +#include "fips.h" + +/* Command line arguments and processing */ +struct arguments { + char *random_name; + char *rng_name; + + int random_step; + int fill_watermark; + double poll_timeout; + + int daemon; +}; +extern struct arguments *arguments; + +/* Background/daemon mode */ +extern int am_daemon; /* Nonzero if we went daemon */ + + +/* + * Routines and macros + */ +#define message(priority,fmt,args...) do { \ + if (am_daemon) { \ + syslog((priority), fmt, ##args); \ + } else { \ + fprintf(stderr, fmt, ##args); \ + fprintf(stderr, "\n"); \ + } \ +} while (0) + +#endif /* RNGD__H */ + diff --git a/rngd_entsource.c b/rngd_entsource.c new file mode 100644 index 0000000..86d6646 --- /dev/null +++ b/rngd_entsource.c @@ -0,0 +1,106 @@ +/* + * rngd_entsource.c -- Entropy source and conditioning + * + * Copyright (C) 2001 Philipp Rumpf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE + +#ifndef HAVE_CONFIG_H +#error Invalid or missing autoconf build environment +#endif + +#include "rng-tools-config.h" + +#include <unistd.h> +#include <stdint.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <syslog.h> +#include <string.h> + +#include "rngd.h" +#include "fips.h" +#include "exits.h" +#include "rngd_entsource.h" + + +/* Logic and contexts */ +static int rng_fd; /* rng data source */ +fips_ctx_t fipsctx; /* Context for the FIPS tests */ + + +/* Read data from the entropy source */ +void xread(void *buf, size_t size) +{ + size_t off = 0; + ssize_t r; + + while (size > 0) { + do { + r = read(rng_fd, buf + off, size); + } while ((r == -1) && (errno == EINTR)); + if (r <= 0) + break; + off += r; + size -= r; + } + + if (size) { + message(LOG_DAEMON|LOG_ERR, "read error\n"); + exit(1); + } +} + +/* Initialize entropy source */ +static int discard_initial_data(void) +{ + /* Trash 32 bits of what is probably stale (non-random) + * initial state from the RNG. For Intel's, 8 bits would + * be enough, but since AMD's generates 32 bits at a time... + * + * The kernel drivers should be doing this at device powerup, + * but at least up to 2.4.24, it doesn't. */ + unsigned char tempbuf[4]; + xread(tempbuf, sizeof tempbuf); + + /* Return 32 bits of bootstrap data */ + xread(tempbuf, sizeof tempbuf); + + return tempbuf[0] | (tempbuf[1] << 8) | + (tempbuf[2] << 16) | (tempbuf[3] << 24); +} + +/* + * Open entropy source, and initialize it + */ +void init_entropy_source(const char* sourcedev) +{ + rng_fd = open(sourcedev, O_RDONLY); + if (rng_fd == -1) { + message(LOG_DAEMON|LOG_ERR, "can't open %s: %s", + sourcedev, strerror(errno)); + exit(EXIT_FAIL); + } + + /* Bootstrap FIPS tests */ + fips_init(&fipsctx, discard_initial_data()); +} + diff --git a/rngd_entsource.h b/rngd_entsource.h new file mode 100644 index 0000000..5c9b350 --- /dev/null +++ b/rngd_entsource.h @@ -0,0 +1,42 @@ +/* + * rngd_source.h -- Entropy source and conditioning + * + * Copyright (C) 2001 Philipp Rumpf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef RNGD_ENTSOURCE__H +#define RNGD_ENTSOURCE__H + +#include "rng-tools-config.h" + +#include <unistd.h> +#include <stdint.h> + +/* Logic and contexts */ +extern fips_ctx_t fipsctx; /* Context for the FIPS tests */ + +/* + * Initialize entropy source and entropy conditioning + * + * sourcedev is the path to the entropy source + */ +extern void init_entropy_source(const char* sourcedev); + +/* Read data from the entropy source */ +void xread(void *buf, size_t size); + +#endif /* RNGD_ENTSOURCE__H */ diff --git a/rngd_linux.c b/rngd_linux.c new file mode 100644 index 0000000..13ea618 --- /dev/null +++ b/rngd_linux.c @@ -0,0 +1,105 @@ +/* + * rngd_linux.c -- Entropy sink for the Linux Kernel (/dev/random) + * + * Copyright (C) 2001 Philipp Rumpf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE + +#ifndef HAVE_CONFIG_H +#error Invalid or missing autoconf build environment +#endif + +#include "rng-tools-config.h" + +#include <unistd.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <syslog.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/time.h> +#include <time.h> +#include <linux/types.h> +#include <linux/random.h> +#include <string.h> + +#include "rngd.h" +#include "fips.h" +#include "exits.h" +#include "rngd_linux.h" + + +/* Kernel output device */ +static int random_fd; + + +/* + * Initialize the interface to the Linux Kernel + * entropy pool (through /dev/random) + * + * randomdev is the path to the random device + */ +void init_kernel_rng(const char* randomdev) +{ + random_fd = open(randomdev, O_RDWR); + if (random_fd == -1) { + message(LOG_DAEMON|LOG_ERR, "can't open %s: %s", + randomdev, strerror(errno)); + exit(EXIT_USAGE); + } +} + +void random_add_entropy(void *buf, size_t size) +{ + struct { + int ent_count; + int size; + unsigned char data[size]; + } entropy; + + entropy.ent_count = size * 8; + entropy.size = size; + memcpy(entropy.data, buf, size); + + if (ioctl(random_fd, RNDADDENTROPY, &entropy) != 0) { + message(LOG_DAEMON|LOG_ERR, "RNDADDENTROPY failed: %s\n", + strerror(errno)); + exit(1); + } +} + +void random_sleep(double poll_timeout) +{ + int ent_count; + struct pollfd pfd = { + fd: random_fd, + events: POLLOUT, + }; + + if (ioctl(random_fd, RNDGETENTCNT, &ent_count) == 0 && + ent_count < arguments->fill_watermark) + return; + + poll(&pfd, 1, 1000.0 * poll_timeout); +} + diff --git a/rngd_linux.h b/rngd_linux.h new file mode 100644 index 0000000..5c65dd5 --- /dev/null +++ b/rngd_linux.h @@ -0,0 +1,44 @@ +/* + * rngd_linux.h -- Entropy sink for the Linux Kernel (/dev/random) + * + * Copyright (C) 2001 Philipp Rumpf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef RNGD_LINUX__H +#define RNGD_LINUX__H + +#include "rng-tools-config.h" + +#include <unistd.h> +#include <stdint.h> + +/* + * Initialize the interface to the Linux Kernel + * entropy pool (through /dev/random) + * + * randomdev is the path to the random device + */ +extern void init_kernel_rng(const char* randomdev); + +/* Send entropy to the kernel */ +extern void random_add_entropy(void *buf, size_t size); + +/* Sleep until the kernel is hungry for entropy */ +extern void random_sleep(double poll_timeout); + +#endif /* RNGD_LINUX__H */ + diff --git a/rngtest.1.in b/rngtest.1.in new file mode 100644 index 0000000..a116caa --- /dev/null +++ b/rngtest.1.in @@ -0,0 +1,88 @@ +.\" Copyright (c) 2004 Henrique de Moraes Holschuh -- hmh@debian.org +.\" +.TH RNGTEST 1 "March 2004" "@PACKAGE@ @VERSION@" + +.SH NAME +rngtest \- Check the randomness of data using FIPS 140-2 tests + +.SH SYNOPSIS +.B rngtest +[\fB\-c\fR \fIn\fR | \fB\-\-blockcount=\fIn\fR] +[\fB\-b\fR \fIn\fR | \fB\-\-blockstats=\fIn\fR] +[\fB\-t\fR \fIn\fR | \fB\-\-timedstats=\fIn\fR] +[\fB\-p\fR | \fB\-\-pipe\fR] +[\fB\-?\fR] [\fB\-\-help\fR] +[\fB\-V\fR] [\fB\-\-version\fR] +.RI + +.SH DESCRIPTION +\fIrngtest\fR works on blocks of 20000 bits at a time, using the FIPS 140-2 +(errata of 2001-10-10) tests to verify the randomness of the block of data. +.PP +It takes input from \fIstdin\fR, and outputs statistics to \fIstderr\fR, +optionally echoing blocks that passed the FIPS tests to \fIstdout\fR +(when operating in \fIpipe mode\fR). Errors are sent to \fIstderr\fR. +.PP +At startup, \fIrngtest\fR will trow away the first 32 bits of data when +operating in \fIpipe mode\fR. It will use the next 32 bits of data to +bootstrap the FIPS tests (even when not operating in \fIpipe mode\fR). +These bits are not tested for randomness. +.PP +Statistics are dumped to \fIstderr\fR when the program exits. + +.SH OPTIONS +.TP +\fB\-p\fR, \fB\-\-pipe\fR +Enable \fIpipe mode\fR. All data blocks that pass the FIPS tests are +echoed to \fIstdout\fR, and \fIrngtest\fR operates in silent mode. +.TP +\fB\-c\fR \fIn\fR, \fB\-\-blockcount=\fIn\fR (default: 0) +Exit after processing n input blocks, if n is not zero. +.TP +\fB\-b\fR \fIn\fR, \fB\-\-blockstats=\fIn\fR (default: 0) +Dump statistics every n blocks, if n is not zero. +.TP +\fB\-t\fR \fIn\fR, \fB\-\-timedstats=\fIn\fR (default: 0) +Dump statistics every n secods, if n is not zero. +.TP +\fB\-?\fR, \fB\-\-help\fR +Give a short summary of all program options. +.TP +\fB\-V\fR, \fB\-\-version\fR +Print program version + +.SH STATISTICS +\fIrngtest\fR will dump statistics to \fIstderr\fR when it exits, and +when told to by \fIblockstats\fR or \fItimedstats\fR. +.PP +\fBFIPS 140-2 successes\fR and \fBFIPS 140-2 failures\fR counts the number of +20000-bit blocks either accepted or rejected by the FIPS 140-2 tests. The +other statistics show a breakdown of the FIPS 140-2 failures by FIPS +140-2 test. See the FIPS 140-2 document for more information (note that these +tests are defined on FIPS 140-1 and FIPS 140-2 errata of 2001-10-10. They +were removed in FIPS 140-2 errata of 2002-12-03). +.PP +The speed statistics are taken for every 20000-bit block trasferred or +processed. + +.SH EXIT STATUS +.TP +\fB0\fR if no errors happen, and no blocks fail the FIPS tests. +.TP +\fB1\fR if no errors happen, but at least one block fails the FIPS tests. +.TP +\fB10\fR if there are problems with the parameters. +.TP +\fB11\fR if an input/output error happens. +.TP +\fB12\fR if an operating system or resource starvation error happens. + +.SH SEE ALSO +random(4), rngd(8) +.TP +FIPS PUB 140-2 Security Requirements for Cryptographic Modules, NIST, +http://csrc.nist.gov/cryptval/140-2.htm + +.SH AUTHORS +Henrique de Moraes Holschuh <hmh@debian.org> + diff --git a/rngtest.c b/rngtest.c new file mode 100644 index 0000000..9be57ed --- /dev/null +++ b/rngtest.c @@ -0,0 +1,424 @@ +/* + * rngtest.c -- Random Number Generator FIPS 140-1/140-2 tests + * + * This program tests the input stream in stdin for randomness, + * using the tests defined by FIPS 140-1/140-2 2001-10-10. + * + * Copyright (C) 2004 Henrique de Moraes Holschuh <hmh@debian.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE + +#ifndef HAVE_CONFIG_H +#error Invalid or missing autoconf build environment +#endif + +#include "rng-tools-config.h" + +#include <unistd.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/time.h> +#include <time.h> +#include <string.h> +#include <argp.h> +#include <signal.h> + +#include "fips.h" +#include "stats.h" +#include "exits.h" + +#define PROGNAME "rngtest" +const char* logprefix = PROGNAME ": "; + +/* + * argp stuff + */ + +const char *argp_program_version = + PROGNAME " " VERSION "\n" + "Copyright (c) 2004 by Henrique de Moraes Holschuh\n" + "This is free software; see the source for copying conditions. There is NO " + "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."; + +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +error_t argp_err_exit_status = EXIT_USAGE; + +static char doc[] = + "Check the randomness of data using FIPS 140-2 RNG tests.\n" + "\v" + "FIPS tests operate on 20000-bit blocks. Data is read from stdin. Statistics " + "and messages are sent to stderr.\n\n" + "If no errors happen nor any blocks fail the FIPS tests, the program will return " + "exit status 0. If any blocks fail the tests, the exit status will be 1.\n"; + +static struct argp_option options[] = { + { "blockcount", 'c', "n", 0, + "Exit after processing n blocks (default: 0)" }, + + { "pipe", 'p', 0, 0, + "Enable pipe mode: work silently, and echo to stdout all good blocks" }, + + { "timedstats", 't', "n", 0, + "Dump statistics every n secods (default: 0)" }, + + { "blockstats", 'b', "n", 0, + "Dump statistics every n blocks (default: 0)" }, + + { 0 }, +}; + +struct arguments { + int blockstats; + uint64_t timedstats; /* microseconds */ + int pipemode; + int blockcount; +}; + +static struct arguments default_arguments = { + .blockstats = 0, + .timedstats = 0, + .pipemode = 0, + .blockcount = 0, +}; + +static error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + struct arguments *arguments = state->input; + + switch(key) { + case 'c': { + int n; + if ((sscanf(arg, "%i", &n) == 0) || (n < 0)) + argp_usage(state); + else + arguments->blockcount = n; + break; + } + case 'b': { + int n; + if ((sscanf(arg, "%i", &n) == 0) || (n < 0)) + argp_usage(state); + else + arguments->blockstats = n; + break; + } + case 't': { + int n; + if ((sscanf(arg, "%i", &n) == 0) || (n < 0)) + argp_usage(state); + else + arguments->timedstats = 1000000ULL * n; + break; + } + + case 'p': + arguments->pipemode = 1; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +/* + * Globals + */ + +/* RNG Buffers */ +unsigned char rng_buffer[FIPS_RNG_BUFFER_SIZE]; + +/* Statistics */ +struct { + /* simple counters */ + uint64_t bad_fips_blocks; /* Blocks reproved by FIPS 140-2 */ + uint64_t good_fips_blocks; /* Blocks approved by FIPS 140-2 */ + uint64_t fips_failures[N_FIPS_TESTS]; /* Breakdown of block + failures per FIPS test */ + + uint64_t bytes_received; /* Bytes read from input */ + uint64_t bytes_sent; /* Bytes sent to output */ + + /* performance timers */ + struct rng_stat source_blockfill; /* Block-receive time */ + struct rng_stat fips_blockfill; /* FIPS run time */ + struct rng_stat sink_blockfill; /* Block-send time */ + + struct timeval progstart; /* Program start time */ +} rng_stats; + +/* Logic and contexts */ +static fips_ctx_t fipsctx; /* Context for the FIPS tests */ +static int exitstatus = EXIT_SUCCESS; /* Exit status */ + +/* Command line arguments and processing */ +struct arguments *arguments = &default_arguments; +static struct argp argp = { options, parse_opt, NULL, doc }; + +/* signals */ +static volatile int gotsigterm = 0; /* Received SIGTERM/SIGINT */ + + +/* + * Signal handling + */ +static void sigterm_handler(int sig) +{ + gotsigterm = sig; +} + +static void init_sighandlers(void) +{ + struct sigaction action; + + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + action.sa_handler = sigterm_handler; + + /* Handle SIGTERM and SIGINT the same way */ + if (sigaction(SIGTERM, &action, NULL) < 0) { + fprintf(stderr, + "unable to install signal handler for SIGTERM: %s", + strerror(errno)); + exit(EXIT_OSERR); + } + if (sigaction(SIGINT, &action, NULL) < 0) { + fprintf(stderr, + "unable to install signal handler for SIGINT: %s", + strerror(errno)); + exit(EXIT_OSERR); + } +} + + +static int xread(void *buf, size_t size) +{ + size_t off = 0; + ssize_t r; + + while (size) { + r = read(0, buf + off, size); + if (r < 0) { + if (gotsigterm) return -1; + if ((errno == EAGAIN) || (errno == EINTR)) continue; + break; + } else if (!r) { + if (!arguments->pipemode) + fprintf(stderr, + "%sentropy source drained\n", + logprefix); + return -1; + } + off += r; + size -= r; + rng_stats.bytes_received += r; + } + + if (size) { + fprintf(stderr, + "%serror reading input: %s\n", logprefix, + strerror(errno)); + exitstatus = EXIT_IOERR; + return -1; + } + return 0; +} + +static int xwrite(void *buf, size_t size) +{ + size_t off = 0; + ssize_t r; + + while (size) { + r = write(1, buf + off, size); + if (r < 0) { + if (gotsigterm) return -1; + if ((errno == EAGAIN) || (errno == EINTR)) continue; + break; + } else if (!r) { + fprintf(stderr, + "%swrite channel stuck\n", logprefix); + exitstatus = EXIT_IOERR; + return -1; + } + off += r; + size -= r; + rng_stats.bytes_sent += r; + } + + if (size) { + fprintf(stderr, + "%serror writing to output: %s\n", logprefix, + strerror(errno)); + exitstatus = EXIT_IOERR; + return -1; + } + return 0; +} + + +static void init_rng_stats(void) +{ + memset(&rng_stats, 0, sizeof(rng_stats)); + gettimeofday(&rng_stats.progstart, 0); + set_stat_prefix(logprefix); +} + +static void dump_rng_stats(void) +{ + int j; + char buf[256]; + struct timeval now; + + fprintf(stderr, "%s\n", dump_stat_counter(buf, sizeof(buf), + "bits received from input", + rng_stats.bytes_received * 8)); + if (arguments->pipemode) + fprintf(stderr, "%s\n", dump_stat_counter(buf, sizeof(buf), + "bits sent to output", + rng_stats.bytes_sent * 8)); + fprintf(stderr, "%s\n", dump_stat_counter(buf, sizeof(buf), + "FIPS 140-2 successes", + rng_stats.good_fips_blocks)); + fprintf(stderr, "%s\n", dump_stat_counter(buf, sizeof(buf), + "FIPS 140-2 failures", + rng_stats.bad_fips_blocks)); + for (j = 0; j < N_FIPS_TESTS; j++) + fprintf(stderr, "%s\n", dump_stat_counter(buf, sizeof(buf), + fips_test_names[j], + rng_stats.fips_failures[j])); + fprintf(stderr, "%s\n", dump_stat_bw(buf, sizeof(buf), + "input channel speed", "bits", + &rng_stats.source_blockfill, FIPS_RNG_BUFFER_SIZE*8)); + fprintf(stderr, "%s\n", dump_stat_bw(buf, sizeof(buf), + "FIPS tests speed", "bits", + &rng_stats.fips_blockfill, FIPS_RNG_BUFFER_SIZE*8)); + if (arguments->pipemode) + fprintf(stderr, "%s\n", dump_stat_bw(buf, sizeof(buf), + "output channel speed", "bits", + &rng_stats.sink_blockfill, FIPS_RNG_BUFFER_SIZE*8)); + + gettimeofday(&now, 0); + fprintf(stderr, "%sProgram run time: %llu microseconds\n", + logprefix, elapsed_time(&rng_stats.progstart, &now)); +} + +/* Return 32 bits of bootstrap data */ +static int discard_initial_data(void) +{ + unsigned char tempbuf[4]; + + /* Do full startup discards when in pipe mode */ + if (arguments->pipemode) + if (xread(tempbuf, sizeof tempbuf)) exit(EXIT_FAIL); + + /* Bootstrap data for FIPS tests */ + if (xread(tempbuf, sizeof tempbuf)) exit(EXIT_FAIL); + + return tempbuf[0] | (tempbuf[1] << 8) | + (tempbuf[2] << 16) | (tempbuf[3] << 24); +} + +static void do_rng_fips_test_loop( void ) +{ + int j; + int fips_result; + struct timeval start, stop, statdump, now; + int statruns, runs; + + runs = statruns = 0; + gettimeofday(&statdump, 0); + while (!gotsigterm) { + gettimeofday(&start, 0); + if (xread(rng_buffer, sizeof(rng_buffer))) return; + gettimeofday(&stop, 0); + update_usectimer_stat(&rng_stats.source_blockfill, + &start, &stop); + + gettimeofday(&start, 0); + fips_result = fips_run_rng_test(&fipsctx, &rng_buffer); + gettimeofday (&stop, 0); + update_usectimer_stat(&rng_stats.fips_blockfill, + &start, &stop); + + if (fips_result) { + rng_stats.bad_fips_blocks++; + for (j = 0; j < N_FIPS_TESTS; j++) + if (fips_result & fips_test_mask[j]) + rng_stats.fips_failures[j]++; + } else { + rng_stats.good_fips_blocks++; + if (arguments->pipemode) { + gettimeofday(&start, 0); + if (xwrite(rng_buffer, sizeof(rng_buffer))) + return; + gettimeofday (&stop, 0); + update_usectimer_stat( + &rng_stats.sink_blockfill, + &start, &stop); + } + } + + if (arguments->blockcount && + (++runs >= arguments->blockcount)) break; + + gettimeofday(&now, 0); + if ((arguments->blockstats && + (++statruns >= arguments->blockstats)) || + (arguments->timedstats && + (elapsed_time(&statdump, &now) > arguments->timedstats))) { + dump_rng_stats(); + gettimeofday(&statdump, 0); + statruns = 0; + } + } +} + +int main(int argc, char **argv) +{ + argp_parse(&argp, argc, argv, 0, 0, arguments); + + if (!arguments->pipemode) + fprintf(stderr, "%s\n\n", + argp_program_version); + + init_sighandlers(); + + /* Init data structures */ + init_rng_stats(); + + if (!arguments->pipemode) + fprintf(stderr, "%sstarting FIPS tests...\n", + logprefix); + + /* Bootstrap FIPS tests */ + fips_init(&fipsctx, discard_initial_data()); + + do_rng_fips_test_loop(); + + dump_rng_stats(); + + if ((exitstatus == EXIT_SUCCESS) && + (rng_stats.bad_fips_blocks || !rng_stats.good_fips_blocks)) { + exitstatus = EXIT_FAIL; + } + + exit(exitstatus); +} @@ -0,0 +1,152 @@ +/* + * stats.c -- Statistics helpers + * + * Copyright (C) 2004 Henrique de Moraes Holschuh <hmh@debian.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE + +#ifndef HAVE_CONFIG_H +#error Invalid or missing autoconf build environment +#endif + +#include "rng-tools-config.h" + +#include <unistd.h> +#include <stdint.h> +#include <stdio.h> +#include <sys/time.h> +#include <time.h> +#include <string.h> + +#include "fips.h" +#include "stats.h" + + +static char stat_prefix[20] = ""; + +void set_stat_prefix(const char* prefix) +{ + stat_prefix[sizeof(stat_prefix)-1] = 0; + strncpy(stat_prefix, prefix, sizeof(stat_prefix)-1); +} + +static void scale_mult_unit(char *unit, int unitsize, + const char *baseunit, + double *value_min, + double *value_avg, + double *value_max) +{ + int mult = 0; + char multchar[] = "KMGTPE"; + + while ((*value_min >= 1024.0) && (*value_avg >= 1024.0) && + (*value_max >= 1024.0) && (mult < sizeof(multchar))) { + mult++; + *value_min = *value_min / 1024.0; + *value_max = *value_max / 1024.0; + *value_avg = *value_avg / 1024.0; + } + unit[unitsize-1] = 0; + if (mult) + snprintf(unit, unitsize, "%ci%s", multchar[mult-1], baseunit); + else + strncpy(unit, baseunit, unitsize); +} + +/* Computes elapsed time in microseconds */ +uint64_t elapsed_time(struct timeval *start, + struct timeval *stop) +{ + uint64_t diff; + + if (stop->tv_sec < start->tv_sec) return 0; + + diff = (stop->tv_sec - start->tv_sec) * 1000000ULL; + if (stop->tv_usec > start->tv_usec) { + diff += stop->tv_usec - start->tv_usec; + } else { + diff -= start->tv_usec - stop->tv_usec; + } + + return diff; +} + +/* Updates min-max stat */ +void update_stat(struct rng_stat *stat, uint64_t value) +{ + uint64_t overflow = stat->num_samples; + + if ((stat->min == 0 ) || (value < stat->min)) stat->min = value; + if (value > stat->max) stat->max = value; + if (++stat->num_samples > overflow) { + stat->sum += value; + } else { + stat->sum = value; + stat->num_samples = 1; + } +} + +char *dump_stat_counter(char *buf, int size, + const char *msg, uint64_t value) +{ + buf[size-1] = 0; + snprintf(buf, size-1, "%s%s: %llu", stat_prefix, msg, value); + + return buf; +} + +char *dump_stat_stat(char *buf, int size, + const char *msg, const char *unit, struct rng_stat *stat) +{ + double avg = 0.0; + + if (stat->num_samples > 0) + avg = (double)stat->sum / stat->num_samples; + + buf[size-1] = 0; + snprintf(buf, size-1, "%s%s: (min=%llu; avg=%.3f; max=%llu)%s", + stat_prefix, msg, stat->min, avg, stat->max, unit); + + return buf; +} + +char *dump_stat_bw(char *buf, int size, + const char *msg, const char *unit, + struct rng_stat *stat, + uint64_t blocksize) +{ + char unitscaled[20]; + double bw_avg = 0.0, bw_min = 0.0, bw_max = 0.0; + + if (stat->max > 0) + bw_min = (1000000.0 * blocksize) / stat->max; + if (stat->min > 0) + bw_max = (1000000.0 * blocksize) / stat->min; + if (stat->num_samples > 0) + bw_avg = (1000000.0 * blocksize * stat->num_samples) / stat->sum; + + scale_mult_unit(unitscaled, sizeof(unitscaled), unit, + &bw_min, &bw_avg, &bw_max); + + buf[size-1] = 0; + snprintf(buf, size-1, "%s%s: (min=%.3f; avg=%.3f; max=%.3f)%s/s", + stat_prefix, msg, bw_min, bw_avg, bw_max, unitscaled); + + return buf; +} + @@ -0,0 +1,71 @@ +/* + * stats.h -- Statistics helpers + * + * Copyright (C) 2004 Henrique M. Holschuh <hmh@debian.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef STATS__H +#define STATS__H + +#include <unistd.h> +#include <stdint.h> + +/* Min-Max stat */ +struct rng_stat { + uint64_t max; /* Highest value seen */ + uint64_t min; /* Lowest value seen */ + uint64_t num_samples; /* Number of samples */ + uint64_t sum; /* Sum of all samples */ +}; + +/* Sets a prefix for all stat dumps. Maximum length is 19 chars */ +extern void set_stat_prefix(const char* prefix); + +/* Computes elapsed time in microseconds */ +extern uint64_t elapsed_time(struct timeval *start, + struct timeval *stop); + +/* Updates min-max stat */ +extern void update_stat(struct rng_stat *stat, uint64_t value); + +/* Updates min-max microseconds timer stat */ +#define update_usectimer_stat(STAT, START, STOP) \ + update_stat(STAT, elapsed_time(START, STOP)) + +/* + * The following functions format a stat dump on buf, and + * return a pointer to the start of buf + */ + +/* Dump simple counter */ +extern char *dump_stat_counter(char *buf, int size, + const char *msg, uint64_t value); + +/* Dump min-max time stat */ +extern char *dump_stat_stat(char *buf, int size, + const char *msg, const char *unit, + struct rng_stat *stat); + +/* + * Dump min-max speed stat, base time unit is a microsecond + */ +extern char *dump_stat_bw(char *buf, int size, + const char *msg, const char *unit, + struct rng_stat *stat, + uint64_t blocksize); + +#endif /* STATS__H */ |