]> git.ipfire.org Git - thirdparty/rng-tools.git/blame - rngtest.c
Release version 5.
[thirdparty/rng-tools.git] / rngtest.c
CommitLineData
61af3de3
JG
1/*
2 * rngtest.c -- Random Number Generator FIPS 140-1/140-2 tests
3 *
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.
d1b4c504 6 *
61af3de3
JG
7 * Copyright (C) 2004 Henrique de Moraes Holschuh <hmh@debian.org>
8 *
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.
d1b4c504 13 *
61af3de3
JG
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.
18 *
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
d32709d1 21 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
61af3de3
JG
22 */
23
24#define _GNU_SOURCE
25
26#ifndef HAVE_CONFIG_H
27#error Invalid or missing autoconf build environment
28#endif
29
30#include "rng-tools-config.h"
31
32#include <unistd.h>
33#include <stdint.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <sys/time.h>
37#include <time.h>
38#include <string.h>
39#include <argp.h>
40#include <signal.h>
41
42#include "fips.h"
43#include "stats.h"
44#include "exits.h"
45
46#define PROGNAME "rngtest"
47const char* logprefix = PROGNAME ": ";
48
49/*
50 * argp stuff
51 */
52
53const 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.";
58
59const char *argp_program_bug_address = PACKAGE_BUGREPORT;
60error_t argp_err_exit_status = EXIT_USAGE;
61
62static char doc[] =
63 "Check the randomness of data using FIPS 140-2 RNG tests.\n"
64 "\v"
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";
69
70static struct argp_option options[] = {
71 { "blockcount", 'c', "n", 0,
72 "Exit after processing n blocks (default: 0)" },
73
74 { "pipe", 'p', 0, 0,
75 "Enable pipe mode: work silently, and echo to stdout all good blocks" },
76
77 { "timedstats", 't', "n", 0,
78 "Dump statistics every n secods (default: 0)" },
79
80 { "blockstats", 'b', "n", 0,
81 "Dump statistics every n blocks (default: 0)" },
82
83 { 0 },
84};
85
86struct arguments {
87 int blockstats;
88 uint64_t timedstats; /* microseconds */
89 int pipemode;
90 int blockcount;
91};
92
93static struct arguments default_arguments = {
94 .blockstats = 0,
95 .timedstats = 0,
96 .pipemode = 0,
97 .blockcount = 0,
98};
99
100static error_t parse_opt (int key, char *arg, struct argp_state *state)
101{
102 struct arguments *arguments = state->input;
d1b4c504 103
61af3de3
JG
104 switch(key) {
105 case 'c': {
106 int n;
107 if ((sscanf(arg, "%i", &n) == 0) || (n < 0))
108 argp_usage(state);
109 else
110 arguments->blockcount = n;
111 break;
112 }
113 case 'b': {
114 int n;
115 if ((sscanf(arg, "%i", &n) == 0) || (n < 0))
116 argp_usage(state);
117 else
118 arguments->blockstats = n;
119 break;
120 }
121 case 't': {
122 int n;
123 if ((sscanf(arg, "%i", &n) == 0) || (n < 0))
124 argp_usage(state);
125 else
126 arguments->timedstats = 1000000ULL * n;
127 break;
128 }
129
130 case 'p':
131 arguments->pipemode = 1;
132 break;
133
134 default:
135 return ARGP_ERR_UNKNOWN;
136 }
137
138 return 0;
139}
140
141/*
142 * Globals
143 */
144
145/* RNG Buffers */
146unsigned char rng_buffer[FIPS_RNG_BUFFER_SIZE];
147
148/* Statistics */
149struct {
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 */
d1b4c504 155
61af3de3
JG
156 uint64_t bytes_received; /* Bytes read from input */
157 uint64_t bytes_sent; /* Bytes sent to output */
158
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 */
163
164 struct timeval progstart; /* Program start time */
165} rng_stats;
166
167/* Logic and contexts */
168static fips_ctx_t fipsctx; /* Context for the FIPS tests */
169static int exitstatus = EXIT_SUCCESS; /* Exit status */
170
171/* Command line arguments and processing */
172struct arguments *arguments = &default_arguments;
173static struct argp argp = { options, parse_opt, NULL, doc };
174
175/* signals */
176static volatile int gotsigterm = 0; /* Received SIGTERM/SIGINT */
177
178
179/*
180 * Signal handling
181 */
182static void sigterm_handler(int sig)
183{
184 gotsigterm = sig;
185}
186
187static void init_sighandlers(void)
188{
189 struct sigaction action;
190
191 sigemptyset(&action.sa_mask);
192 action.sa_flags = 0;
193 action.sa_handler = sigterm_handler;
194
195 /* Handle SIGTERM and SIGINT the same way */
196 if (sigaction(SIGTERM, &action, NULL) < 0) {
d1b4c504 197 fprintf(stderr,
61af3de3
JG
198 "unable to install signal handler for SIGTERM: %s",
199 strerror(errno));
200 exit(EXIT_OSERR);
201 }
202 if (sigaction(SIGINT, &action, NULL) < 0) {
203 fprintf(stderr,
204 "unable to install signal handler for SIGINT: %s",
205 strerror(errno));
206 exit(EXIT_OSERR);
207 }
208}
209
210
211static int xread(void *buf, size_t size)
212{
213 size_t off = 0;
214 ssize_t r;
215
216 while (size) {
217 r = read(0, buf + off, size);
218 if (r < 0) {
219 if (gotsigterm) return -1;
220 if ((errno == EAGAIN) || (errno == EINTR)) continue;
221 break;
222 } else if (!r) {
223 if (!arguments->pipemode)
d1b4c504
JG
224 fprintf(stderr,
225 "%sentropy source drained\n",
61af3de3
JG
226 logprefix);
227 return -1;
228 }
229 off += r;
230 size -= r;
231 rng_stats.bytes_received += r;
232 }
233
234 if (size) {
235 fprintf(stderr,
236 "%serror reading input: %s\n", logprefix,
237 strerror(errno));
238 exitstatus = EXIT_IOERR;
239 return -1;
240 }
241 return 0;
242}
243
244static int xwrite(void *buf, size_t size)
245{
246 size_t off = 0;
247 ssize_t r;
248
249 while (size) {
250 r = write(1, buf + off, size);
251 if (r < 0) {
252 if (gotsigterm) return -1;
253 if ((errno == EAGAIN) || (errno == EINTR)) continue;
254 break;
255 } else if (!r) {
d1b4c504 256 fprintf(stderr,
61af3de3
JG
257 "%swrite channel stuck\n", logprefix);
258 exitstatus = EXIT_IOERR;
259 return -1;
260 }
261 off += r;
262 size -= r;
263 rng_stats.bytes_sent += r;
264 }
265
266 if (size) {
267 fprintf(stderr,
268 "%serror writing to output: %s\n", logprefix,
269 strerror(errno));
270 exitstatus = EXIT_IOERR;
271 return -1;
272 }
273 return 0;
274}
275
276
277static void init_rng_stats(void)
278{
279 memset(&rng_stats, 0, sizeof(rng_stats));
280 gettimeofday(&rng_stats.progstart, 0);
281 set_stat_prefix(logprefix);
282}
283
284static void dump_rng_stats(void)
285{
286 int j;
287 char buf[256];
288 struct timeval now;
289
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),
305 fips_test_names[j],
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));
317
318 gettimeofday(&now, 0);
319 fprintf(stderr, "%sProgram run time: %llu microseconds\n",
92ee2129
JG
320 logprefix,
321 (unsigned long long) elapsed_time(&rng_stats.progstart, &now));
61af3de3
JG
322}
323
324/* Return 32 bits of bootstrap data */
325static int discard_initial_data(void)
326{
327 unsigned char tempbuf[4];
328
329 /* Do full startup discards when in pipe mode */
330 if (arguments->pipemode)
331 if (xread(tempbuf, sizeof tempbuf)) exit(EXIT_FAIL);
332
333 /* Bootstrap data for FIPS tests */
334 if (xread(tempbuf, sizeof tempbuf)) exit(EXIT_FAIL);
335
d1b4c504 336 return tempbuf[0] | (tempbuf[1] << 8) |
61af3de3
JG
337 (tempbuf[2] << 16) | (tempbuf[3] << 24);
338}
339
340static void do_rng_fips_test_loop( void )
341{
342 int j;
343 int fips_result;
344 struct timeval start, stop, statdump, now;
345 int statruns, runs;
346
347 runs = statruns = 0;
348 gettimeofday(&statdump, 0);
349 while (!gotsigterm) {
350 gettimeofday(&start, 0);
351 if (xread(rng_buffer, sizeof(rng_buffer))) return;
352 gettimeofday(&stop, 0);
d1b4c504 353 update_usectimer_stat(&rng_stats.source_blockfill,
61af3de3
JG
354 &start, &stop);
355
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,
360 &start, &stop);
361
362 if (fips_result) {
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]++;
367 } else {
368 rng_stats.good_fips_blocks++;
369 if (arguments->pipemode) {
370 gettimeofday(&start, 0);
371 if (xwrite(rng_buffer, sizeof(rng_buffer)))
372 return;
373 gettimeofday (&stop, 0);
374 update_usectimer_stat(
375 &rng_stats.sink_blockfill,
376 &start, &stop);
377 }
378 }
379
380 if (arguments->blockcount &&
381 (++runs >= arguments->blockcount)) break;
382
383 gettimeofday(&now, 0);
d1b4c504 384 if ((arguments->blockstats &&
61af3de3
JG
385 (++statruns >= arguments->blockstats)) ||
386 (arguments->timedstats &&
387 (elapsed_time(&statdump, &now) > arguments->timedstats))) {
388 dump_rng_stats();
389 gettimeofday(&statdump, 0);
390 statruns = 0;
391 }
392 }
393}
394
395int main(int argc, char **argv)
396{
397 argp_parse(&argp, argc, argv, 0, 0, arguments);
398
399 if (!arguments->pipemode)
400 fprintf(stderr, "%s\n\n",
401 argp_program_version);
402
403 init_sighandlers();
404
405 /* Init data structures */
406 init_rng_stats();
407
408 if (!arguments->pipemode)
409 fprintf(stderr, "%sstarting FIPS tests...\n",
410 logprefix);
411
412 /* Bootstrap FIPS tests */
413 fips_init(&fipsctx, discard_initial_data());
414
415 do_rng_fips_test_loop();
d1b4c504 416
61af3de3
JG
417 dump_rng_stats();
418
d1b4c504 419 if ((exitstatus == EXIT_SUCCESS) &&
61af3de3
JG
420 (rng_stats.bad_fips_blocks || !rng_stats.good_fips_blocks)) {
421 exitstatus = EXIT_FAIL;
422 }
423
424 exit(exitstatus);
425}