]> git.ipfire.org Git - thirdparty/rng-tools.git/commitdiff
Import rng-tools from private subversion repo.
authorJeff Garzik <jgarzik@pobox.com>
Tue, 25 Oct 2005 06:49:12 +0000 (02:49 -0400)
committerJeff Garzik <jgarzik@pobox.com>
Tue, 25 Oct 2005 06:49:12 +0000 (02:49 -0400)
25 files changed:
.gitignore [new file with mode: 0644]
AUTHORS [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
NEWS [new file with mode: 0644]
README [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
configure.ac [new file with mode: 0644]
contrib/Makefile.am [new file with mode: 0644]
contrib/randstat.c [new file with mode: 0644]
contrib/rngtest.c [new file with mode: 0644]
exits.h [new file with mode: 0644]
fips.c [new file with mode: 0644]
fips.h [new file with mode: 0644]
rngd.8.in [new file with mode: 0644]
rngd.c [new file with mode: 0644]
rngd.h [new file with mode: 0644]
rngd_entsource.c [new file with mode: 0644]
rngd_entsource.h [new file with mode: 0644]
rngd_linux.c [new file with mode: 0644]
rngd_linux.h [new file with mode: 0644]
rngtest.1.in [new file with mode: 0644]
rngtest.c [new file with mode: 0644]
stats.c [new file with mode: 0644]
stats.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..4f48cc9
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
index 0000000..d0cec2d
--- /dev/null
@@ -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 (file)
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 (file)
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 (executable)
index 0000000..9f98ef8
--- /dev/null
@@ -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 (file)
index 0000000..763f548
--- /dev/null
@@ -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 (file)
index 0000000..18c4fbc
--- /dev/null
@@ -0,0 +1,3 @@
+
+EXTRA_DIST = randstat.c
+
diff --git a/contrib/randstat.c b/contrib/randstat.c
new file mode 100644 (file)
index 0000000..992f23c
--- /dev/null
@@ -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 (file)
index 0000000..26917d1
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..86d6646
--- /dev/null
@@ -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 (file)
index 0000000..5c9b350
--- /dev/null
@@ -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 (file)
index 0000000..13ea618
--- /dev/null
@@ -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 (file)
index 0000000..5c65dd5
--- /dev/null
@@ -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 (file)
index 0000000..a116caa
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 */