aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Garzik <jgarzik@pobox.com>2005-10-25 02:49:12 -0400
committerJeff Garzik <jgarzik@pobox.com>2005-10-25 02:49:12 -0400
commit61af3de31a9a4d4928ebd468ce645b9596f46184 (patch)
tree5a3d700aa4ba2b70546a0614d3bcf1c9af2841cb
downloadrng-tools-61af3de31a9a4d4928ebd468ce645b9596f46184.tar.gz
Import rng-tools from private subversion repo.
-rw-r--r--.gitignore20
-rw-r--r--AUTHORS3
-rw-r--r--ChangeLog160
-rw-r--r--Makefile.am23
-rw-r--r--NEWS13
-rw-r--r--README1
-rwxr-xr-xautogen.sh11
-rw-r--r--configure.ac59
-rw-r--r--contrib/Makefile.am3
-rw-r--r--contrib/randstat.c29
-rw-r--r--contrib/rngtest.c160
-rw-r--r--exits.h31
-rw-r--r--fips.c201
-rw-r--r--fips.h72
-rw-r--r--rngd.8.in82
-rw-r--r--rngd.c214
-rw-r--r--rngd.h65
-rw-r--r--rngd_entsource.c106
-rw-r--r--rngd_entsource.h42
-rw-r--r--rngd_linux.c105
-rw-r--r--rngd_linux.h44
-rw-r--r--rngtest.1.in88
-rw-r--r--rngtest.c424
-rw-r--r--stats.c152
-rw-r--r--stats.h71
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
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..877532b
--- /dev/null
+++ b/AUTHORS
@@ -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
+
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..b0f991b
--- /dev/null
+++ b/NEWS
@@ -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
+
diff --git a/README b/README
new file mode 100644
index 0000000..62bb1b8
--- /dev/null
+++ b/README
@@ -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;
+}
diff --git a/exits.h b/exits.h
new file mode 100644
index 0000000..e1432fc
--- /dev/null
+++ b/exits.h
@@ -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 */
diff --git a/fips.c b/fips.c
new file mode 100644
index 0000000..04d2b97
--- /dev/null
+++ b/fips.c
@@ -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;
+ }
+}
+
diff --git a/fips.h b/fips.h
new file mode 100644
index 0000000..679776e
--- /dev/null
+++ b/fips.h
@@ -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
+
diff --git a/rngd.c b/rngd.c
new file mode 100644
index 0000000..70c14e1
--- /dev/null
+++ b/rngd.c
@@ -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;
+}
diff --git a/rngd.h b/rngd.h
new file mode 100644
index 0000000..dbc4419
--- /dev/null
+++ b/rngd.h
@@ -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);
+}
diff --git a/stats.c b/stats.c
new file mode 100644
index 0000000..dbdac5d
--- /dev/null
+++ b/stats.c
@@ -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;
+}
+
diff --git a/stats.h b/stats.h
new file mode 100644
index 0000000..9807192
--- /dev/null
+++ b/stats.h
@@ -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 */