2 * rngtest.c -- Random Number Generator FIPS 140-1/140-2 tests
4 * This program tests the input stream in stdin for randomness,
5 * using the tests defined by FIPS 140-1/140-2 2001-10-10.
7 * Copyright (C) 2004 Henrique de Moraes Holschuh <hmh@debian.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
27 #error Invalid or missing autoconf build environment
30 #include "rng-tools-config.h"
46 #define PROGNAME "rngtest"
47 const char* logprefix
= PROGNAME
": ";
53 const char *argp_program_version
=
54 PROGNAME
" " VERSION
"\n"
55 "Copyright (c) 2004 by Henrique de Moraes Holschuh\n"
56 "This is free software; see the source for copying conditions. There is NO "
57 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.";
59 const char *argp_program_bug_address
= PACKAGE_BUGREPORT
;
60 error_t argp_err_exit_status
= EXIT_USAGE
;
63 "Check the randomness of data using FIPS 140-2 RNG tests.\n"
65 "FIPS tests operate on 20000-bit blocks. Data is read from stdin. Statistics "
66 "and messages are sent to stderr.\n\n"
67 "If no errors happen nor any blocks fail the FIPS tests, the program will return "
68 "exit status 0. If any blocks fail the tests, the exit status will be 1.\n";
70 static struct argp_option options
[] = {
71 { "blockcount", 'c', "n", 0,
72 "Exit after processing n blocks (default: 0)" },
75 "Enable pipe mode: work silently, and echo to stdout all good blocks" },
77 { "timedstats", 't', "n", 0,
78 "Dump statistics every n secods (default: 0)" },
80 { "blockstats", 'b', "n", 0,
81 "Dump statistics every n blocks (default: 0)" },
88 uint64_t timedstats
; /* microseconds */
93 static struct arguments default_arguments
= {
100 static error_t
parse_opt (int key
, char *arg
, struct argp_state
*state
)
102 struct arguments
*arguments
= state
->input
;
107 if ((sscanf(arg
, "%i", &n
) == 0) || (n
< 0))
110 arguments
->blockcount
= n
;
115 if ((sscanf(arg
, "%i", &n
) == 0) || (n
< 0))
118 arguments
->blockstats
= n
;
123 if ((sscanf(arg
, "%i", &n
) == 0) || (n
< 0))
126 arguments
->timedstats
= 1000000ULL * n
;
131 arguments
->pipemode
= 1;
135 return ARGP_ERR_UNKNOWN
;
146 unsigned char rng_buffer
[FIPS_RNG_BUFFER_SIZE
];
150 /* simple counters */
151 uint64_t bad_fips_blocks
; /* Blocks reproved by FIPS 140-2 */
152 uint64_t good_fips_blocks
; /* Blocks approved by FIPS 140-2 */
153 uint64_t fips_failures
[N_FIPS_TESTS
]; /* Breakdown of block
154 failures per FIPS test */
156 uint64_t bytes_received
; /* Bytes read from input */
157 uint64_t bytes_sent
; /* Bytes sent to output */
159 /* performance timers */
160 struct rng_stat source_blockfill
; /* Block-receive time */
161 struct rng_stat fips_blockfill
; /* FIPS run time */
162 struct rng_stat sink_blockfill
; /* Block-send time */
164 struct timeval progstart
; /* Program start time */
167 /* Logic and contexts */
168 static fips_ctx_t fipsctx
; /* Context for the FIPS tests */
169 static int exitstatus
= EXIT_SUCCESS
; /* Exit status */
171 /* Command line arguments and processing */
172 struct arguments
*arguments
= &default_arguments
;
173 static struct argp argp
= { options
, parse_opt
, NULL
, doc
};
176 static volatile int gotsigterm
= 0; /* Received SIGTERM/SIGINT */
182 static void sigterm_handler(int sig
)
187 static void init_sighandlers(void)
189 struct sigaction action
;
191 sigemptyset(&action
.sa_mask
);
193 action
.sa_handler
= sigterm_handler
;
195 /* Handle SIGTERM and SIGINT the same way */
196 if (sigaction(SIGTERM
, &action
, NULL
) < 0) {
198 "unable to install signal handler for SIGTERM: %s",
202 if (sigaction(SIGINT
, &action
, NULL
) < 0) {
204 "unable to install signal handler for SIGINT: %s",
211 static int xread(void *buf
, size_t size
)
217 r
= read(0, buf
+ off
, size
);
219 if (gotsigterm
) return -1;
220 if ((errno
== EAGAIN
) || (errno
== EINTR
)) continue;
223 if (!arguments
->pipemode
)
225 "%sentropy source drained\n",
231 rng_stats
.bytes_received
+= r
;
236 "%serror reading input: %s\n", logprefix
,
238 exitstatus
= EXIT_IOERR
;
244 static int xwrite(void *buf
, size_t size
)
250 r
= write(1, buf
+ off
, size
);
252 if (gotsigterm
) return -1;
253 if ((errno
== EAGAIN
) || (errno
== EINTR
)) continue;
257 "%swrite channel stuck\n", logprefix
);
258 exitstatus
= EXIT_IOERR
;
263 rng_stats
.bytes_sent
+= r
;
268 "%serror writing to output: %s\n", logprefix
,
270 exitstatus
= EXIT_IOERR
;
277 static void init_rng_stats(void)
279 memset(&rng_stats
, 0, sizeof(rng_stats
));
280 gettimeofday(&rng_stats
.progstart
, 0);
281 set_stat_prefix(logprefix
);
284 static void dump_rng_stats(void)
290 fprintf(stderr
, "%s\n", dump_stat_counter(buf
, sizeof(buf
),
291 "bits received from input",
292 rng_stats
.bytes_received
* 8));
293 if (arguments
->pipemode
)
294 fprintf(stderr
, "%s\n", dump_stat_counter(buf
, sizeof(buf
),
295 "bits sent to output",
296 rng_stats
.bytes_sent
* 8));
297 fprintf(stderr
, "%s\n", dump_stat_counter(buf
, sizeof(buf
),
298 "FIPS 140-2 successes",
299 rng_stats
.good_fips_blocks
));
300 fprintf(stderr
, "%s\n", dump_stat_counter(buf
, sizeof(buf
),
301 "FIPS 140-2 failures",
302 rng_stats
.bad_fips_blocks
));
303 for (j
= 0; j
< N_FIPS_TESTS
; j
++)
304 fprintf(stderr
, "%s\n", dump_stat_counter(buf
, sizeof(buf
),
306 rng_stats
.fips_failures
[j
]));
307 fprintf(stderr
, "%s\n", dump_stat_bw(buf
, sizeof(buf
),
308 "input channel speed", "bits",
309 &rng_stats
.source_blockfill
, FIPS_RNG_BUFFER_SIZE
*8));
310 fprintf(stderr
, "%s\n", dump_stat_bw(buf
, sizeof(buf
),
311 "FIPS tests speed", "bits",
312 &rng_stats
.fips_blockfill
, FIPS_RNG_BUFFER_SIZE
*8));
313 if (arguments
->pipemode
)
314 fprintf(stderr
, "%s\n", dump_stat_bw(buf
, sizeof(buf
),
315 "output channel speed", "bits",
316 &rng_stats
.sink_blockfill
, FIPS_RNG_BUFFER_SIZE
*8));
318 gettimeofday(&now
, 0);
319 fprintf(stderr
, "%sProgram run time: %llu microseconds\n",
321 (unsigned long long) elapsed_time(&rng_stats
.progstart
, &now
));
324 /* Return 32 bits of bootstrap data */
325 static int discard_initial_data(void)
327 unsigned char tempbuf
[4];
329 /* Do full startup discards when in pipe mode */
330 if (arguments
->pipemode
)
331 if (xread(tempbuf
, sizeof tempbuf
)) exit(EXIT_FAIL
);
333 /* Bootstrap data for FIPS tests */
334 if (xread(tempbuf
, sizeof tempbuf
)) exit(EXIT_FAIL
);
336 return tempbuf
[0] | (tempbuf
[1] << 8) |
337 (tempbuf
[2] << 16) | (tempbuf
[3] << 24);
340 static void do_rng_fips_test_loop( void )
344 struct timeval start
, stop
, statdump
, now
;
348 gettimeofday(&statdump
, 0);
349 while (!gotsigterm
) {
350 gettimeofday(&start
, 0);
351 if (xread(rng_buffer
, sizeof(rng_buffer
))) return;
352 gettimeofday(&stop
, 0);
353 update_usectimer_stat(&rng_stats
.source_blockfill
,
356 gettimeofday(&start
, 0);
357 fips_result
= fips_run_rng_test(&fipsctx
, &rng_buffer
);
358 gettimeofday (&stop
, 0);
359 update_usectimer_stat(&rng_stats
.fips_blockfill
,
363 rng_stats
.bad_fips_blocks
++;
364 for (j
= 0; j
< N_FIPS_TESTS
; j
++)
365 if (fips_result
& fips_test_mask
[j
])
366 rng_stats
.fips_failures
[j
]++;
368 rng_stats
.good_fips_blocks
++;
369 if (arguments
->pipemode
) {
370 gettimeofday(&start
, 0);
371 if (xwrite(rng_buffer
, sizeof(rng_buffer
)))
373 gettimeofday (&stop
, 0);
374 update_usectimer_stat(
375 &rng_stats
.sink_blockfill
,
380 if (arguments
->blockcount
&&
381 (++runs
>= arguments
->blockcount
)) break;
383 gettimeofday(&now
, 0);
384 if ((arguments
->blockstats
&&
385 (++statruns
>= arguments
->blockstats
)) ||
386 (arguments
->timedstats
&&
387 (elapsed_time(&statdump
, &now
) > arguments
->timedstats
))) {
389 gettimeofday(&statdump
, 0);
395 int main(int argc
, char **argv
)
397 argp_parse(&argp
, argc
, argv
, 0, 0, arguments
);
399 if (!arguments
->pipemode
)
400 fprintf(stderr
, "%s\n\n",
401 argp_program_version
);
405 /* Init data structures */
408 if (!arguments
->pipemode
)
409 fprintf(stderr
, "%sstarting FIPS tests...\n",
412 /* Bootstrap FIPS tests */
413 fips_init(&fipsctx
, discard_initial_data());
415 do_rng_fips_test_loop();
419 if ((exitstatus
== EXIT_SUCCESS
) &&
420 (rng_stats
.bad_fips_blocks
|| !rng_stats
.good_fips_blocks
)) {
421 exitstatus
= EXIT_FAIL
;