f4083f6d9f97570256156d930c746da831e2a29e
3 * $Id: rspit.c,v 1.15 2001/04/19 18:26:13 mdw Exp $
5 * Spit out random numbers
7 * (c) 1999 Straylight/Edgeware
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of Catacomb.
14 * Catacomb is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Library General Public License as
16 * published by the Free Software Foundation; either version 2 of the
17 * License, or (at your option) any later version.
19 * Catacomb is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
24 * You should have received a copy of the GNU Library General Public
25 * License along with Catacomb; if not, write to the Free
26 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
30 /*----- Revision history --------------------------------------------------*
33 * Revision 1.15 2001/04/19 18:26:13 mdw
34 * Use the new MAC keysize names.
36 * Revision 1.14 2001/02/21 20:03:22 mdw
37 * Added support for MD2 hash function.
39 * Revision 1.13 2000/12/06 20:33:27 mdw
40 * Make flags be macros rather than enumerations, to ensure that they're
43 * Revision 1.12 2000/10/08 15:49:18 mdw
44 * Remove failed kludge for shutting up a warning.
46 * Revision 1.11 2000/10/08 12:10:32 mdw
47 * Make table have external linkage to bodge around deficiency in C. The
48 * problem is that @static gen generators[];@ is considered to be a
49 * `tentative definition', and therefore mustn't have incomplete type,
50 * which it obviously has.
52 * Revision 1.10 2000/08/11 21:34:59 mdw
53 * New restartable interface to Maurer testing.
55 * Revision 1.9 2000/08/04 23:24:15 mdw
56 * Add a timer and a discard option.
58 * Revision 1.8 2000/07/29 22:05:47 mdw
59 * Fix error in help message about Maurer test syntax.
61 * Revision 1.7 2000/07/18 23:01:26 mdw
62 * Improve progress indications, and allow user to choose chunk sizes for
65 * Revision 1.6 2000/07/15 20:53:35 mdw
66 * Add a load of new ciphers and hashes.
68 * Revision 1.5 2000/07/01 11:27:03 mdw
69 * Portability fix: don't assume that `stdout' is a constant expression.
70 * Remove old type name `bbs_param'.
72 * Revision 1.4 2000/06/17 12:08:28 mdw
73 * Restructure handling of cipher-based generators. Add counter-mode
74 * ciphers and MGF-1 hash functions. Add FIPS 140-1 and Maurer's tests.
76 * Revision 1.3 2000/02/12 18:21:03 mdw
77 * Overhaul of key management (again).
79 * Revision 1.2 1999/12/22 15:59:51 mdw
80 * New prime-search system. Read BBS keys from key files.
82 * Revision 1.1 1999/12/10 23:29:13 mdw
83 * Emit random numbers for statistical tests.
87 /*----- Header files ------------------------------------------------------*/
104 #include <mLib/darray.h>
105 #include <mLib/dstr.h>
106 #include <mLib/mdwopt.h>
107 #include <mLib/quis.h>
108 #include <mLib/report.h>
109 #include <mLib/sub.h>
111 #include "fipstest.h"
128 #include "des3-ofb.h"
131 #include "skipjack-ofb.h"
133 #include "xtea-ofb.h"
134 #include "blowfish-ofb.h"
135 #include "twofish-ofb.h"
136 #include "idea-ofb.h"
137 #include "cast128-ofb.h"
138 #include "cast256-ofb.h"
139 #include "rijndael-ofb.h"
140 #include "square-ofb.h"
141 #include "serpent-ofb.h"
143 #include "des-counter.h"
144 #include "des3-counter.h"
145 #include "rc2-counter.h"
146 #include "rc5-counter.h"
147 #include "skipjack-counter.h"
148 #include "tea-counter.h"
149 #include "xtea-counter.h"
150 #include "blowfish-counter.h"
151 #include "twofish-counter.h"
152 #include "idea-counter.h"
153 #include "cast128-counter.h"
154 #include "cast256-counter.h"
155 #include "rijndael-counter.h"
156 #include "square-counter.h"
157 #include "serpent-counter.h"
163 #include "tiger-mgf.h"
164 #include "rmd128-mgf.h"
165 #include "rmd160-mgf.h"
166 #include "rmd256-mgf.h"
167 #include "rmd320-mgf.h"
171 /*----- Data structures ---------------------------------------------------*/
175 grand
*(*seed
)(unsigned /*i*/);
180 extern gen generators
[];
187 E(SKIPJACK, skipjack) \
190 E(BLOWFISH, blowfish) \
191 E(TWOFISH, twofish) \
193 E(CAST128, cast128) \
194 E(CAST256, cast256) \
196 E(RIJNDAEL, rijndael) \
210 #define E(PRE, pre) CIPHER_##PRE,
211 enum { CIPHERS CIPHER__bogus
};
214 #define E(PRE, pre) HASH_##PRE,
215 enum { HASHES HASH__bogus
};
221 grand
*(*ofb
)(const void */
*k*/
, size_t /*sz*/);
222 grand
*(*counter
)(const void */
*k*/
, size_t /*sz*/);
224 #define E(PRE, pre) \
225 { pre##_keysz, PRE##_BLKSZ, pre##_ofbrand, pre##_counterrand },
233 grand
*(*mgf
)(const void */
*k*/
, size_t /*sz*/);
235 #define E(PRE, pre) \
236 { &pre, pre##_mgfkeysz, pre##_mgfrand },
241 /*----- Miscellaneous static data -----------------------------------------*/
244 static size_t outsz
= 0;
245 static unsigned maurer_lo
= 5, maurer_hi
= 8;
250 static unsigned flags
= 0;
252 #define f_progress 1u
257 #define f_discard 32u
259 /*----- Help options ------------------------------------------------------*/
261 static void usage(FILE *fp
)
263 pquis(fp
, "Usage: $ generator [options]\n");
266 static void version(FILE *fp
)
268 pquis(fp
, "$, Catacomb version " VERSION
"\n");
271 static void help(FILE *fp
)
277 Emits a stream of random bytes suitable for, well, all sorts of things.\n\
278 The primary objective is to be able to generate streams of input for\n\
279 statistical tests, such as Diehard.\n\
281 Options are specific to the particular generator, although there's a\n\
284 -h, --help Display this help message.\n\
285 -v, --version Display the program's version number.\n\
286 -u, --usage Display a useless usage message.\n\
288 -l, --list Show a list of the supported generators, with\n\
290 -f, --fipstest Run the FIPS 140-1 randomness test.\n\
291 -m, --maurer[=LO-HI] Run Maurer's universal statistical test.\n\
292 -o, --output FILE Write output to FILE, not stdout.\n\
293 -z, --size SIZE Emit SIZE bytes, not an unlimited number.\n\
294 -p, --progress Show a little progress meter (on stderr).\n\
295 -T, --timer Keep track of the CPU time used by the generator.\n\
296 -d, --discard Discard the generated output.\n\
298 (A SIZE may be followed by `g' for gigabytes, `m' for megabytes, or\n\
299 `k' for kilobytes. If unqualified, an amount in bytes is assumed.)\n\
303 /*----- Main options parser -----------------------------------------------*/
305 static struct option opts
[] = {
307 /* --- Standard GNU help options --- */
309 { "help", 0, 0, 'h' },
310 { "version", 0, 0, 'v' },
311 { "usage", 0, 0, 'u' },
313 /* --- Other useful things --- */
315 { "list", 0, 0, 'l' },
316 { "fipstest", 0, 0, 'f' },
317 { "maurer", OPTF_ARGOPT
, 0, 'm' },
318 { "output", OPTF_ARGREQ
, 0, 'o' },
319 { "size", OPTF_ARGREQ
, 0, 'z' },
320 { "progress", 0, 0, 'p' },
321 { "timer", 0, 0, 'T' },
322 { "discard", 0, 0, 'd' },
324 /* --- End of main table --- */
329 static const char *sopts
= "hvu lfm::o:z:pTd";
332 DA_DECL(option_v
, struct option
);
336 static option_v optv
= DA_INIT
;
337 static dstr optd
= DSTR_INIT
;
339 /* --- @addopts@ --- *
341 * Arguments: @const char *s@ = pointer to short options
342 * @struct option *l@ = pointer to long options
346 * Use: Adds a collection of options to the table.
349 static void addopts(const char *s
, struct option
*l
)
355 DA_PUSH(&optv
, *l
++);
363 * Returns: Next option from argument array.
365 * Use: Fetches options, handling the standard ones.
371 int i
= mdwopt(argc
, argv
, optd
.buf
, DA(&optv
), 0, 0, 0);
384 puts("Generators supported:");
385 for (g
= generators
; g
->name
; g
++)
386 printf(" %s %s\n", g
->name
, g
->help
);
396 unsigned long lo
, hi
;
397 lo
= strtoul(optarg
, &p
, 0);
398 if (*p
== '-' || *p
== ',')
399 hi
= strtoul(p
+ 1, &p
, 0);
402 if (*p
!= 0 || hi
< lo
|| lo
== 0)
403 die(EXIT_FAILURE
, "bad bit range `%s'", optarg
);
410 die(EXIT_FAILURE
, "already set an output file");
411 if (strcmp(optarg
, "-") == 0)
414 outfp
= fopen(optarg
, "w");
416 die(EXIT_FAILURE
, "couldn't open output file `%s': %s",
417 optarg
, strerror(errno
));
424 outsz
= strtoul(optarg
, &p
, 0);
426 die(EXIT_FAILURE
, "bad number `%s'", optarg
);
428 case 'G': case 'g': outsz
*= 1024;
429 case 'M': case 'm': outsz
*= 1024;
430 case 'K': case 'k': outsz
*= 1024;
434 die(EXIT_FAILURE
, "bad suffix `%s'", p
);
438 die(EXIT_FAILURE
, "bad suffix `%s'", p
);
455 /*----- Manglers for seed strings -----------------------------------------*/
459 * Arguments: @const char *p@ = pointer to input string
460 * @char **end@ = where the end goes
461 * @dstr *d@ = output buffer
465 * Use: Transforms a hex string into a chunk of binary data.
468 static void unhex(const char *p
, char **end
, dstr
*d
)
470 while (p
[0] && p
[1]) {
471 int x
= p
[0], y
= p
[1];
472 if ('0' <= x
&& x
<= '9') x
-= '0';
473 else if ('A' <= x
&& x
<= 'F') x
-= 'A' - 10;
474 else if ('a' <= x
&& x
<= 'f') x
-= 'a' - 10;
476 if ('0' <= y
&& y
<= '9') y
-= '0';
477 else if ('A' <= y
&& y
<= 'F') y
-= 'A' - 10;
478 else if ('a' <= y
&& y
<= 'f') y
-= 'a' - 10;
480 DPUTC(d
, (x
<< 4) + y
);
486 /* --- Generate a key --- */
488 static void textkey(dstr
*d
, const char *p
, const octet
*ksz
)
490 size_t sz
= strlen(p
);
493 die(EXIT_FAILURE
, "zero-length key string");
494 if (keysz(sz
, ksz
) != sz
)
498 rmd160_mgfinit(&g
, p
, sz
);
501 rmd160_mgfencrypt(&g
, 0, d
->buf
, sz
);
504 assert(((void)"I can't seem to choose a good key size",
505 keysz(d
->len
, ksz
) == d
->len
));
508 static void hexkey(dstr
*d
, const char *p
, const octet
*ksz
)
511 unhex(optarg
, &q
, d
);
513 die(EXIT_FAILURE
, "bad hex key `%s'", p
);
514 if (keysz(d
->len
, ksz
) != d
->len
)
515 die(EXIT_FAILURE
, "bad key length");
518 static void randkey(dstr
*d
, const octet
*ksz
)
520 size_t sz
= keysz(0, ksz
);
522 rand_get(RAND_GLOBAL
, d
->buf
, sz
);
526 /*----- Generators --------------------------------------------------------*/
528 /* --- Blum-Blum-Shub strong generator --- */
530 static grand
*gen_bbs(unsigned i
)
532 /* --- Default modulus --- *
534 * The factors of this number are
536 * @p = 1229936431484295969649886203367009966370895964206162032259292413@
537 * @7754313537966036459299022912838407755462506416274551744201653277@
538 * @313130311731673973886822067@
540 * @q = 9798171783943489959487301695884963889684294764514008432498259742@
541 * @5374320073594018817245784145742769603334292182227671519041431067@
542 * @61344781426317516045890159@
544 * Both %$p$% and %$q$% are prime; %$(p - 1)/2$% and %$(q - 1)/2$% have no
545 * common factors. They were found using this program, with random
548 * I hope that, by publishing these factors, I'll dissuade people from
549 * actually using this modulus in an attempt to attain real security. The
550 * program is quite quick at finding Blum numbers, so there's no excuse for
551 * not generating your own.
555 "120511284390135742513572142094334711443073194119732569353820828435640527418092392240366088035509890969913081816369160298961490135716255689660470370755013177656905237112577648090277537209936078171554274553448103698084782669252936352843649980105109850503830397166360721262431179505917248447259735253684659338653";
557 /* --- Other things --- */
561 unsigned bits
= 1024;
564 const char *kfile
= 0, *id
= 0, *ktype
= 0;
566 /* --- Parse options --- */
568 static struct option opts
[] = {
569 { "modulus", OPTF_ARGREQ
, 0, 'M' },
570 { "generate", 0, 0, 'g' },
571 { "seed", OPTF_ARGREQ
, 0, 's' },
572 { "bits", OPTF_ARGREQ
, 0, 'b' },
573 { "show", 0, 0, 'S' },
574 { "keyring", OPTF_ARGREQ
, 0, 'k' },
575 { "id", OPTF_ARGREQ
, 0, 'i' },
576 { "type", OPTF_ARGREQ
, 0, 't' },
580 addopts("M:gs:b:Sk:i:t:", opts
);
597 bits
= strtoul(optarg
, 0, 0);
599 die(EXIT_FAILURE
, "bad number of bits `%s'", optarg
);
621 /* --- Generate a modulus if one is requested --- */
625 m
= mp_readstring(MP_NEW
, mt
, &p
, 0);
626 if (!m
|| *p
|| (m
->v
[0] & 3) != 1)
627 die(EXIT_FAILURE
, "bad modulus `%s'", mt
);
628 /* Unfortunately I don't know how to test for a Blum integer */
629 } else if (kfile
|| id
|| ktype
) {
634 /* --- Open the key file --- */
638 if (key_open(&kf
, kfile
, KOPEN_READ
, key_moan
, 0)) {
639 die(EXIT_FAILURE
, "error opening key file `%s': %s",
640 kfile
, strerror(errno
));
643 /* --- Find the key --- */
646 if ((kk
= key_bytag(&kf
, id
)) == 0)
647 die(EXIT_FAILURE
, "key `%s' not found", id
);
651 if ((kk
= key_bytype(&kf
, ktype
)) == 0)
652 die(EXIT_FAILURE
, "no suitable key with type `%s' found", ktype
);
655 /* --- Read the key data --- */
657 if ((kk
->k
.e
& KF_ENCMASK
) != KENC_STRUCT
)
658 die(EXIT_FAILURE
, "key is not structured");
659 if ((kd
= key_structfind(&kk
->k
, "n")) == 0)
660 die(EXIT_FAILURE
, "key has no subkey `n'");
661 if ((kd
->e
& KF_ENCMASK
) != KENC_MP
)
662 die(EXIT_FAILURE
, "incomatible subkey encoding");
663 m
= MP_COPY(kd
->u
.m
);
668 if (bbs_gen(&bp
, bits
, &rand_global
, 0,
669 (flags
& f_progress
) ? pgen_ev
: 0, 0))
670 die(EXIT_FAILURE
, "modulus generation failed");
674 fputs("p = ", stderr
);
675 mp_writefile(bp
.p
, stderr
, 10);
676 fputs("\nq = ", stderr
);
677 mp_writefile(bp
.q
, stderr
, 10);
678 fputs("\nn = ", stderr
);
679 mp_writefile(bp
.n
, stderr
, 10);
687 /* --- Set up a seed --- */
690 x
= mprand(MP_NEW
, mp_bits(m
) - 1, &rand_global
, 1);
693 x
= mp_readstring(MP_NEW
, xt
, &p
, 0);
695 die(EXIT_FAILURE
, "bad modulus `%s'", xt
);
707 /* --- Catacomb's random number generator --- */
709 static grand
*gen_rand(unsigned i
)
711 grand
*r
= rand_create();
714 static struct option opts
[] = {
715 { "key", OPTF_ARGREQ
, 0, 'k' },
716 { "text", OPTF_ARGREQ
, 0, 't' },
717 { "hex", OPTF_ARGREQ
, 0, 'H' },
721 addopts("k:t:H:n", opts
);
723 r
->ops
->misc(r
, RAND_NOISESRC
, &noise_source
);
724 r
->ops
->misc(r
, RAND_SEED
, 160);
733 textkey(&d
, optarg
, rmd160_hmackeysz
);
734 r
->ops
->misc(r
, RAND_KEY
, d
.buf
, d
.len
);
737 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, optarg
, strlen(optarg
));
741 hexkey(&d
, optarg
, rmd160_hmackeysz
);
742 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, d
.buf
, d
.len
);
751 /* --- RC4 output --- */
753 static grand
*gen_rc4(unsigned i
)
758 static struct option opts
[] = {
759 { "key", OPTF_ARGREQ
, 0, 'k' },
760 { "hex", OPTF_ARGREQ
, 0, 'H' },
764 addopts("k:H:", opts
);
773 textkey(&d
, optarg
, rc4_keysz
);
777 hexkey(&d
, optarg
, rc4_keysz
);
785 randkey(&d
, rc4_keysz
);
786 r
= rc4_rand(d
.buf
, d
.len
);
791 /* --- SEAL output --- */
793 static grand
*gen_seal(unsigned i
)
799 static struct option opts
[] = {
800 { "key", OPTF_ARGREQ
, 0, 'k' },
801 { "hex", OPTF_ARGREQ
, 0, 'H' },
802 { "sequence", OPTF_ARGREQ
, 0, 'n' },
806 addopts("k:H:n:", opts
);
815 textkey(&d
, optarg
, seal_keysz
);
819 hexkey(&d
, optarg
, seal_keysz
);
823 n
= strtoul(optarg
, &p
, 0);
825 die(EXIT_FAILURE
, "bad number `%s'", optarg
);
833 randkey(&d
, seal_keysz
);
834 r
= seal_rand(d
.buf
, d
.len
, n
);
839 /* --- Output feedback generators --- */
841 static grand
*gen_ofb(unsigned i
)
847 static struct option opts
[] = {
848 { "key", OPTF_ARGREQ
, 0, 'k' },
849 { "hex", OPTF_ARGREQ
, 0, 'H' },
850 { "iv", OPTF_ARGREQ
, 0, 'i' },
854 addopts("k:H:i:", opts
);
863 textkey(&d
, optarg
, ciphertab
[i
].keysz
);
867 hexkey(&d
, optarg
, ciphertab
[i
].keysz
);
871 unhex(optarg
, &p
, &iv
);
873 die(EXIT_FAILURE
, "bad hex IV `%s'", optarg
);
881 randkey(&d
, ciphertab
[i
].keysz
);
882 r
= ciphertab
[i
].ofb(d
.buf
, d
.len
);
884 if (iv
.len
!= ciphertab
[i
].blksz
) {
885 die(EXIT_FAILURE
, "bad IV length %lu (must be %lu)",
886 (unsigned long)iv
.len
, (unsigned long)ciphertab
[i
].blksz
);
888 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, iv
.buf
);
896 /* --- Counter generators --- */
898 static grand
*gen_counter(unsigned i
)
904 static struct option opts
[] = {
905 { "key", OPTF_ARGREQ
, 0, 'k' },
906 { "hex", OPTF_ARGREQ
, 0, 'H' },
907 { "iv", OPTF_ARGREQ
, 0, 'i' },
911 addopts("k:H:i:", opts
);
920 textkey(&d
, optarg
, ciphertab
[i
].keysz
);
924 hexkey(&d
, optarg
, ciphertab
[i
].keysz
);
928 unhex(optarg
, &p
, &iv
);
930 die(EXIT_FAILURE
, "bad hex IV `%s'", optarg
);
938 randkey(&d
, ciphertab
[i
].keysz
);
939 r
= ciphertab
[i
].counter(d
.buf
, d
.len
);
941 if (iv
.len
!= ciphertab
[i
].blksz
) {
942 die(EXIT_FAILURE
, "bad IV length %lu (must be %lu)",
943 (unsigned long)iv
.len
, (unsigned long)ciphertab
[i
].blksz
);
945 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, iv
.buf
);
953 /* --- Mask generators --- */
955 static grand
*gen_mgf(unsigned i
)
961 static struct option opts
[] = {
962 { "key", OPTF_ARGREQ
, 0, 'k' },
963 { "hex", OPTF_ARGREQ
, 0, 'H' },
964 { "index", OPTF_ARGREQ
, 0, 'i' },
968 addopts("k:H:i:", opts
);
977 textkey(&d
, optarg
, hashtab
[i
].keysz
);
981 hexkey(&d
, optarg
, hashtab
[i
].keysz
);
985 c
= strtoul(optarg
, &p
, 0);
987 die(EXIT_FAILURE
, "bad index `%s'", optarg
);
995 randkey(&d
, hashtab
[i
].keysz
);
997 r
= hashtab
[i
].mgf(d
.buf
, d
.len
);
999 r
->ops
->misc(r
, GRAND_SEEDUINT32
, c
);
1005 /* --- Fibonacci generator --- */
1007 static grand
*gen_fib(unsigned i
)
1014 static struct option opts
[] = {
1015 { "seed", OPTF_ARGREQ
, 0, 's' },
1019 addopts("s:", opts
);
1027 s
= strtoul(optarg
, &p
, 0);
1029 die(EXIT_FAILURE
, "bad integer `%s'", optarg
);
1036 r
= fibrand_create(s
);
1038 r
->ops
->misc(r
, GRAND_SEEDRAND
, &rand_global
);
1042 /* --- LC generator --- */
1044 static grand
*gen_lc(unsigned i
)
1050 static struct option opts
[] = {
1051 { "seed", OPTF_ARGREQ
, 0, 's' },
1055 addopts("s:", opts
);
1063 s
= strtoul(optarg
, &p
, 0);
1065 die(EXIT_FAILURE
, "bad integer `%s'", optarg
);
1074 s
= rand_global
.ops
->range(&rand_global
, LCRAND_P
);
1075 while (s
== LCRAND_FIXEDPT
);
1077 return (lcrand_create(s
));
1080 /* --- Basic options parser -- can't generate output --- */
1082 static grand
*gen_opts(unsigned i
)
1089 /*----- Generators table --------------------------------------------------*/
1091 gen generators
[] = {
1092 { "fibonacci", gen_fib
, 0,
1096 #define E(PRE, pre) \
1097 { #pre "-ofb", gen_ofb, CIPHER_##PRE, \
1098 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
1101 #define E(PRE, pre) \
1102 { #pre "-counter", gen_counter, CIPHER_##PRE, \
1103 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
1106 #define E(PRE, pre) \
1107 { #pre "-mgf", gen_mgf, HASH_##PRE, \
1108 "[-k KEY-PHRASE] [-H HEX-KEY] [-i INDEX]" },
1111 { "rc4", gen_rc4
, 0,
1112 "[-k KEY-PHRASE] [-H HEX-KEY]" },
1113 { "seal", gen_seal
, 0,
1114 "[-k KEY-PHRASE] [-H HEX-KEY] [-n SEQ]" },
1115 { "rand", gen_rand
, 0,
1116 "[-n] [-k KEY-PHRASE] [-t TEXT-BLOCK] [-H HEX-BLOCK]" },
1117 { "bbs", gen_bbs
, 0,
1118 "[-gS] [-s SEED] [-M MODULUS] [-b BITS] [-k KEYRING] [-i TAG] [-t TYPE]"
1123 static gen optsg
= { "options", gen_opts
, 0,
1124 "This message shouldn't be printed." };
1126 /*----- Random number generation ------------------------------------------*/
1128 static int genfile(const void *buf
, size_t sz
, void *p
)
1131 if (fwrite(buf
, 1, sz
, fp
) != sz
)
1132 die(EXIT_FAILURE
, "error writing to file: %s", strerror(errno
));
1136 static int genbuf(const void *buf
, size_t sz
, void *p
)
1139 memcpy(*pp
, buf
, sz
);
1144 typedef struct genmaurer_ctx
{
1149 static int genmaurer(const void *buf
, size_t sz
, void *p
)
1151 genmaurer_ctx
*g
= p
;
1154 for (i
= 0; i
< g
->n
; i
++)
1155 maurer_test(&g
->m
[i
], buf
, sz
);
1159 static int generate(grand
*r
, size_t outsz
,
1160 int (*func
)(const void *buf
, size_t sz
, void *p
),
1163 static char kmg
[] = { ' ', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 0 };
1165 unsigned percent
= 0;
1168 static char baton
[] = "-\\|/";
1173 /* --- Spit out random data --- */
1177 if (flags
& f_progress
) {
1178 char *errbuf
= xmalloc(BUFSIZ
);
1179 setvbuf(stderr
, errbuf
, _IOLBF
, BUFSIZ
);
1181 fprintf(stderr
, "[%*s] 0%% 0\r[/\b", 50, "");
1183 fputs("[ ] 0\r[/\b", stderr
);
1188 signal(SIGPIPE
, SIG_IGN
);
1193 size_t sz
= sizeof(buf
);
1194 clock_t c_start
, c_stop
;
1196 /* --- Emit a bufferful (or less) of data --- */
1199 if (sz
> outsz
- kb
)
1203 r
->ops
->fill(r
, buf
, sz
);
1205 clk
+= c_stop
- c_start
;
1206 if (func
&& (rc
= func(buf
, sz
, p
)) != 0)
1210 /* --- Update the display --- */
1212 if (flags
& f_progress
) {
1220 if (difftime(t
, last
) > 1.0) {
1224 fputs(" ] ", stderr
);
1226 unsigned pc
= kb
* 100.0 / outsz
;
1227 if (pc
> percent
|| percent
> 100 || difftime(t
, last
) > 1.0) {
1231 for (; percent
< (pc
& ~1); percent
+= 2)
1234 for (; pc
< 100; pc
+= 2)
1236 fprintf(stderr
, "] %3i%% ", percent
);
1244 while (q
> 8192 && kk
[1]) {
1248 fprintf(stderr
, "%4i%c\r[", q
, *kk
);
1251 for (pc
= 0; pc
< (percent
& ~1); pc
+= 2)
1260 if (percent
< 100) {
1261 putc(*bp
++, stderr
);
1269 /* --- Terminate the loop --- */
1271 } while (!outsz
|| kb
< outsz
);
1273 if (flags
& f_progress
)
1274 fputc('\n', stderr
);
1275 if (flags
& f_timer
) {
1276 fprintf(stderr
, "generated %lu bytes ", (unsigned long)outsz
);
1278 fputs("too quickly to measure\n", stderr
);
1281 double sec
= (double)clk
/CLOCKS_PER_SEC
;
1282 double bps
= (outsz
<< 3)/sec
;
1283 for (kk
= kmg
; bps
> 1024 && kk
[1]; kk
++, bps
/= 1024)
1285 fprintf(stderr
, "in %g secs (%g %cb/s)\n", sec
, bps
, *kk
);
1291 /*----- Main code ---------------------------------------------------------*/
1293 int main(int ac
, char *av
[])
1298 /* --- Initialize mLib --- */
1303 /* --- Set up the main Catacomb generator --- */
1305 rand_noisesrc(RAND_GLOBAL
, &noise_source
);
1306 rand_seed(RAND_GLOBAL
, 160);
1308 /* --- Initialize the options table --- */
1310 addopts(sopts
, opts
);
1315 /* --- Read the generator out of the first argument --- */
1317 if (argc
> 1 && *argv
[1] != '-') {
1318 const char *arg
= av
[1];
1319 size_t sz
= strlen(arg
);
1323 for (gg
= generators
; gg
->name
; gg
++) {
1324 if (strncmp(arg
, gg
->name
, sz
) == 0) {
1325 if (gg
->name
[sz
] == 0) {
1329 die(EXIT_FAILURE
, "ambiguous generator name `%s'", arg
);
1335 die(EXIT_FAILURE
, "unknown generator name `%s'", arg
);
1340 /* --- Get a generic random number generator --- */
1343 if (!r
|| optind
!= ac
- 1) {
1348 /* --- Do the FIPS test --- */
1350 if (flags
& f_fips
) {
1351 octet buf
[FIPSTEST_BUFSZ
];
1355 generate(r
, sizeof(buf
), genbuf
, &p
);
1357 if (rc
& FIPSTEST_MONOBIT
)
1358 moan("failed monobit test");
1359 if (rc
& FIPSTEST_POKER
)
1360 moan("failed poker test");
1361 if (rc
& FIPSTEST_RUNS
)
1362 moan("failed runs test");
1363 if (rc
& FIPSTEST_LONGRUNS
)
1364 moan("failed long runs test");
1365 if (!rc
&& (flags
& f_progress
))
1366 fputs("test passed\n", stderr
);
1367 return (rc ? EXIT_FAILURE
: 0);
1370 /* --- Do Maurer's test --- */
1372 if (flags
& f_maurer
) {
1378 static struct { double x
; const char *sig
; } sigtab
[] = {
1386 g
.n
= maurer_hi
- maurer_lo
+ 1;
1387 g
.m
= xmalloc(g
.n
* sizeof(maurer_ctx
));
1388 for (i
= 0; i
< g
.n
; i
++)
1389 maurer_init(&g
.m
[i
], i
+ maurer_lo
);
1390 bufsz
= (100 * maurer_hi
) << maurer_hi
;
1392 generate(r
, bufsz
, genmaurer
, &g
);
1394 for (i
= maurer_lo
; i
<= maurer_hi
; i
++) {
1395 double z
= maurer_done(&g
.m
[i
- maurer_lo
]);
1396 double zz
= fabs(z
);
1399 for (j
= 0; sigtab
[j
].sig
; j
++) {
1400 if (zz
> sigtab
[j
].x
) {
1402 moan("failed, bits = %u, sig = %s, Z_u = %g",
1403 i
, sigtab
[j
].sig
, z
);
1407 if (flags
& f_progress
)
1408 fprintf(stderr
, "bits = %u, Z_u = %g\n", i
, z
);
1415 /* --- Discard --- */
1417 if (flags
& f_discard
) {
1418 generate(r
, outsz
, 0, 0);
1422 /* --- Write to a file --- */
1425 if (!(flags
& f_file
) && isatty(STDOUT_FILENO
))
1426 die(EXIT_FAILURE
, "writing output to a terminal is a bad idea");
1429 generate(r
, outsz
, genfile
, outfp
);
1437 /*----- That's all, folks -------------------------------------------------*/