3 * $Id: rspit.c,v 1.12 2000/10/08 15:49:18 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.12 2000/10/08 15:49:18 mdw
34 * Remove failed kludge for shutting up a warning.
36 * Revision 1.11 2000/10/08 12:10:32 mdw
37 * Make table have external linkage to bodge around deficiency in C. The
38 * problem is that @static gen generators[];@ is considered to be a
39 * `tentative definition', and therefore mustn't have incomplete type,
40 * which it obviously has.
42 * Revision 1.10 2000/08/11 21:34:59 mdw
43 * New restartable interface to Maurer testing.
45 * Revision 1.9 2000/08/04 23:24:15 mdw
46 * Add a timer and a discard option.
48 * Revision 1.8 2000/07/29 22:05:47 mdw
49 * Fix error in help message about Maurer test syntax.
51 * Revision 1.7 2000/07/18 23:01:26 mdw
52 * Improve progress indications, and allow user to choose chunk sizes for
55 * Revision 1.6 2000/07/15 20:53:35 mdw
56 * Add a load of new ciphers and hashes.
58 * Revision 1.5 2000/07/01 11:27:03 mdw
59 * Portability fix: don't assume that `stdout' is a constant expression.
60 * Remove old type name `bbs_param'.
62 * Revision 1.4 2000/06/17 12:08:28 mdw
63 * Restructure handling of cipher-based generators. Add counter-mode
64 * ciphers and MGF-1 hash functions. Add FIPS 140-1 and Maurer's tests.
66 * Revision 1.3 2000/02/12 18:21:03 mdw
67 * Overhaul of key management (again).
69 * Revision 1.2 1999/12/22 15:59:51 mdw
70 * New prime-search system. Read BBS keys from key files.
72 * Revision 1.1 1999/12/10 23:29:13 mdw
73 * Emit random numbers for statistical tests.
77 /*----- Header files ------------------------------------------------------*/
94 #include <mLib/darray.h>
95 #include <mLib/dstr.h>
96 #include <mLib/mdwopt.h>
97 #include <mLib/quis.h>
98 #include <mLib/report.h>
101 #include "fipstest.h"
118 #include "des3-ofb.h"
121 #include "skipjack-ofb.h"
123 #include "xtea-ofb.h"
124 #include "blowfish-ofb.h"
125 #include "twofish-ofb.h"
126 #include "idea-ofb.h"
127 #include "cast128-ofb.h"
128 #include "cast256-ofb.h"
129 #include "rijndael-ofb.h"
130 #include "square-ofb.h"
131 #include "serpent-ofb.h"
133 #include "des-counter.h"
134 #include "des3-counter.h"
135 #include "rc2-counter.h"
136 #include "rc5-counter.h"
137 #include "skipjack-counter.h"
138 #include "tea-counter.h"
139 #include "xtea-counter.h"
140 #include "blowfish-counter.h"
141 #include "twofish-counter.h"
142 #include "idea-counter.h"
143 #include "cast128-counter.h"
144 #include "cast256-counter.h"
145 #include "rijndael-counter.h"
146 #include "square-counter.h"
147 #include "serpent-counter.h"
152 #include "tiger-mgf.h"
153 #include "rmd128-mgf.h"
154 #include "rmd160-mgf.h"
155 #include "rmd256-mgf.h"
156 #include "rmd320-mgf.h"
160 /*----- Data structures ---------------------------------------------------*/
164 grand
*(*seed
)(unsigned /*i*/);
169 extern gen generators
[];
176 E(SKIPJACK, skipjack) \
179 E(BLOWFISH, blowfish) \
180 E(TWOFISH, twofish) \
182 E(CAST128, cast128) \
183 E(CAST256, cast256) \
185 E(RIJNDAEL, rijndael) \
198 #define E(PRE, pre) CIPHER_##PRE,
199 enum { CIPHERS CIPHER__bogus
};
202 #define E(PRE, pre) HASH_##PRE,
203 enum { HASHES HASH__bogus
};
209 grand
*(*ofb
)(const void */
*k*/
, size_t /*sz*/);
210 grand
*(*counter
)(const void */
*k*/
, size_t /*sz*/);
212 #define E(PRE, pre) \
213 { pre##_keysz, PRE##_BLKSZ, pre##_ofbrand, pre##_counterrand },
221 grand
*(*mgf
)(const void */
*k*/
, size_t /*sz*/);
223 #define E(PRE, pre) \
224 { &pre, pre##_mgfkeysz, pre##_mgfrand },
229 /*----- Miscellaneous static data -----------------------------------------*/
232 static size_t outsz
= 0;
233 static unsigned maurer_lo
= 5, maurer_hi
= 8;
238 static unsigned flags
= 0;
249 /*----- Help options ------------------------------------------------------*/
251 static void usage(FILE *fp
)
253 pquis(fp
, "Usage: $ generator [options]\n");
256 static void version(FILE *fp
)
258 pquis(fp
, "$, Catacomb version " VERSION
"\n");
261 static void help(FILE *fp
)
267 Emits a stream of random bytes suitable for, well, all sorts of things.\n\
268 The primary objective is to be able to generate streams of input for\n\
269 statistical tests, such as Diehard.\n\
271 Options are specific to the particular generator, although there's a\n\
274 -h, --help Display this help message.\n\
275 -v, --version Display the program's version number.\n\
276 -u, --usage Display a useless usage message.\n\
278 -l, --list Show a list of the supported generators, with\n\
280 -f, --fipstest Run the FIPS 140-1 randomness test.\n\
281 -m, --maurer[=LO-HI] Run Maurer's universal statistical test.\n\
282 -o, --output FILE Write output to FILE, not stdout.\n\
283 -z, --size SIZE Emit SIZE bytes, not an unlimited number.\n\
284 -p, --progress Show a little progress meter (on stderr).\n\
285 -T, --timer Keep track of the CPU time used by the generator.\n\
286 -d, --discard Discard the generated output.\n\
288 (A SIZE may be followed by `g' for gigabytes, `m' for megabytes, or\n\
289 `k' for kilobytes. If unqualified, an amount in bytes is assumed.)\n\
293 /*----- Main options parser -----------------------------------------------*/
295 static struct option opts
[] = {
297 /* --- Standard GNU help options --- */
299 { "help", 0, 0, 'h' },
300 { "version", 0, 0, 'v' },
301 { "usage", 0, 0, 'u' },
303 /* --- Other useful things --- */
305 { "list", 0, 0, 'l' },
306 { "fipstest", 0, 0, 'f' },
307 { "maurer", OPTF_ARGOPT
, 0, 'm' },
308 { "output", OPTF_ARGREQ
, 0, 'o' },
309 { "size", OPTF_ARGREQ
, 0, 'z' },
310 { "progress", 0, 0, 'p' },
311 { "timer", 0, 0, 'T' },
312 { "discard", 0, 0, 'd' },
314 /* --- End of main table --- */
319 static const char *sopts
= "hvu lfm::o:z:pTd";
322 DA_DECL(option_v
, struct option
);
326 static option_v optv
= DA_INIT
;
327 static dstr optd
= DSTR_INIT
;
329 /* --- @addopts@ --- *
331 * Arguments: @const char *s@ = pointer to short options
332 * @struct option *l@ = pointer to long options
336 * Use: Adds a collection of options to the table.
339 static void addopts(const char *s
, struct option
*l
)
345 DA_PUSH(&optv
, *l
++);
353 * Returns: Next option from argument array.
355 * Use: Fetches options, handling the standard ones.
361 int i
= mdwopt(argc
, argv
, optd
.buf
, DA(&optv
), 0, 0, 0);
374 puts("Generators supported:");
375 for (g
= generators
; g
->name
; g
++)
376 printf(" %s %s\n", g
->name
, g
->help
);
386 unsigned long lo
, hi
;
387 lo
= strtoul(optarg
, &p
, 0);
388 if (*p
== '-' || *p
== ',')
389 hi
= strtoul(p
+ 1, &p
, 0);
392 if (*p
!= 0 || hi
< lo
|| lo
== 0)
393 die(EXIT_FAILURE
, "bad bit range `%s'", optarg
);
400 die(EXIT_FAILURE
, "already set an output file");
401 if (strcmp(optarg
, "-") == 0)
404 outfp
= fopen(optarg
, "w");
406 die(EXIT_FAILURE
, "couldn't open output file `%s': %s",
407 optarg
, strerror(errno
));
414 outsz
= strtoul(optarg
, &p
, 0);
416 die(EXIT_FAILURE
, "bad number `%s'", optarg
);
418 case 'G': case 'g': outsz
*= 1024;
419 case 'M': case 'm': outsz
*= 1024;
420 case 'K': case 'k': outsz
*= 1024;
424 die(EXIT_FAILURE
, "bad suffix `%s'", p
);
428 die(EXIT_FAILURE
, "bad suffix `%s'", p
);
445 /*----- Manglers for seed strings -----------------------------------------*/
449 * Arguments: @const char *p@ = pointer to input string
450 * @char **end@ = where the end goes
451 * @dstr *d@ = output buffer
455 * Use: Transforms a hex string into a chunk of binary data.
458 static void unhex(const char *p
, char **end
, dstr
*d
)
460 while (p
[0] && p
[1]) {
461 int x
= p
[0], y
= p
[1];
462 if ('0' <= x
&& x
<= '9') x
-= '0';
463 else if ('A' <= x
&& x
<= 'F') x
-= 'A' - 10;
464 else if ('a' <= x
&& x
<= 'f') x
-= 'a' - 10;
466 if ('0' <= y
&& y
<= '9') y
-= '0';
467 else if ('A' <= y
&& y
<= 'F') y
-= 'A' - 10;
468 else if ('a' <= y
&& y
<= 'f') y
-= 'a' - 10;
470 DPUTC(d
, (x
<< 4) + y
);
476 /* --- Generate a key --- */
478 static void textkey(dstr
*d
, const char *p
, const octet
*ksz
)
480 size_t sz
= strlen(p
);
483 die(EXIT_FAILURE
, "zero-length key string");
484 if (keysz(sz
, ksz
) != sz
)
488 rmd160_mgfinit(&g
, p
, sz
);
491 rmd160_mgfencrypt(&g
, 0, d
->buf
, sz
);
494 assert(((void)"I can't seem to choose a good key size",
495 keysz(d
->len
, ksz
) == d
->len
));
498 static void hexkey(dstr
*d
, const char *p
, const octet
*ksz
)
501 unhex(optarg
, &q
, d
);
503 die(EXIT_FAILURE
, "bad hex key `%s'", p
);
504 if (keysz(d
->len
, ksz
) != d
->len
)
505 die(EXIT_FAILURE
, "bad key length");
508 static void randkey(dstr
*d
, const octet
*ksz
)
510 size_t sz
= keysz(0, ksz
);
512 rand_get(RAND_GLOBAL
, d
->buf
, sz
);
516 /*----- Generators --------------------------------------------------------*/
518 /* --- Blum-Blum-Shub strong generator --- */
520 static grand
*gen_bbs(unsigned i
)
522 /* --- Default modulus --- *
524 * The factors of this number are
526 * @p = 1229936431484295969649886203367009966370895964206162032259292413@
527 * @7754313537966036459299022912838407755462506416274551744201653277@
528 * @313130311731673973886822067@
530 * @q = 9798171783943489959487301695884963889684294764514008432498259742@
531 * @5374320073594018817245784145742769603334292182227671519041431067@
532 * @61344781426317516045890159@
534 * Both %$p$% and %$q$% are prime; %$(p - 1)/2$% and %$(q - 1)/2$% have no
535 * common factors. They were found using this program, with random
538 * I hope that, by publishing these factors, I'll dissuade people from
539 * actually using this modulus in an attempt to attain real security. The
540 * program is quite quick at finding Blum numbers, so there's no excuse for
541 * not generating your own.
545 "120511284390135742513572142094334711443073194119732569353820828435640527418092392240366088035509890969913081816369160298961490135716255689660470370755013177656905237112577648090277537209936078171554274553448103698084782669252936352843649980105109850503830397166360721262431179505917248447259735253684659338653";
547 /* --- Other things --- */
551 unsigned bits
= 1024;
554 const char *kfile
= 0, *id
= 0, *ktype
= 0;
556 /* --- Parse options --- */
558 static struct option opts
[] = {
559 { "modulus", OPTF_ARGREQ
, 0, 'M' },
560 { "generate", 0, 0, 'g' },
561 { "seed", OPTF_ARGREQ
, 0, 's' },
562 { "bits", OPTF_ARGREQ
, 0, 'b' },
563 { "show", 0, 0, 'S' },
564 { "keyring", OPTF_ARGREQ
, 0, 'k' },
565 { "id", OPTF_ARGREQ
, 0, 'i' },
566 { "type", OPTF_ARGREQ
, 0, 't' },
570 addopts("M:gs:b:Sk:i:t:", opts
);
587 bits
= strtoul(optarg
, 0, 0);
589 die(EXIT_FAILURE
, "bad number of bits `%s'", optarg
);
611 /* --- Generate a modulus if one is requested --- */
615 m
= mp_readstring(MP_NEW
, mt
, &p
, 0);
616 if (!m
|| *p
|| (m
->v
[0] & 3) != 1)
617 die(EXIT_FAILURE
, "bad modulus `%s'", mt
);
618 /* Unfortunately I don't know how to test for a Blum integer */
619 } else if (kfile
|| id
|| ktype
) {
624 /* --- Open the key file --- */
628 if (key_open(&kf
, kfile
, KOPEN_READ
, key_moan
, 0)) {
629 die(EXIT_FAILURE
, "error opening key file `%s': %s",
630 kfile
, strerror(errno
));
633 /* --- Find the key --- */
636 if ((kk
= key_bytag(&kf
, id
)) == 0)
637 die(EXIT_FAILURE
, "key `%s' not found", id
);
641 if ((kk
= key_bytype(&kf
, ktype
)) == 0)
642 die(EXIT_FAILURE
, "no suitable key with type `%s' found", ktype
);
645 /* --- Read the key data --- */
647 if ((kk
->k
.e
& KF_ENCMASK
) != KENC_STRUCT
)
648 die(EXIT_FAILURE
, "key is not structured");
649 if ((kd
= key_structfind(&kk
->k
, "n")) == 0)
650 die(EXIT_FAILURE
, "key has no subkey `n'");
651 if ((kd
->e
& KF_ENCMASK
) != KENC_MP
)
652 die(EXIT_FAILURE
, "incomatible subkey encoding");
653 m
= MP_COPY(kd
->u
.m
);
658 if (bbs_gen(&bp
, bits
, &rand_global
, 0,
659 (flags
& f_progress
) ? pgen_ev
: 0, 0))
660 die(EXIT_FAILURE
, "modulus generation failed");
664 fputs("p = ", stderr
);
665 mp_writefile(bp
.p
, stderr
, 10);
666 fputs("\nq = ", stderr
);
667 mp_writefile(bp
.q
, stderr
, 10);
668 fputs("\nn = ", stderr
);
669 mp_writefile(bp
.n
, stderr
, 10);
677 /* --- Set up a seed --- */
680 x
= mprand(MP_NEW
, mp_bits(m
) - 1, &rand_global
, 1);
683 x
= mp_readstring(MP_NEW
, xt
, &p
, 0);
685 die(EXIT_FAILURE
, "bad modulus `%s'", xt
);
697 /* --- Catacomb's random number generator --- */
699 static grand
*gen_rand(unsigned i
)
701 grand
*r
= rand_create();
704 static struct option opts
[] = {
705 { "key", OPTF_ARGREQ
, 0, 'k' },
706 { "text", OPTF_ARGREQ
, 0, 't' },
707 { "hex", OPTF_ARGREQ
, 0, 'H' },
711 addopts("k:t:H:n", opts
);
713 r
->ops
->misc(r
, RAND_NOISESRC
, &noise_source
);
714 r
->ops
->misc(r
, RAND_SEED
, 160);
723 textkey(&d
, optarg
, rmd160_mackeysz
);
724 r
->ops
->misc(r
, RAND_KEY
, d
.buf
, d
.len
);
727 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, optarg
, strlen(optarg
));
731 hexkey(&d
, optarg
, rmd160_mackeysz
);
732 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, d
.buf
, d
.len
);
741 /* --- RC4 output --- */
743 static grand
*gen_rc4(unsigned i
)
748 static struct option opts
[] = {
749 { "key", OPTF_ARGREQ
, 0, 'k' },
750 { "hex", OPTF_ARGREQ
, 0, 'H' },
754 addopts("k:H:", opts
);
763 textkey(&d
, optarg
, rc4_keysz
);
767 hexkey(&d
, optarg
, rc4_keysz
);
775 randkey(&d
, rc4_keysz
);
776 r
= rc4_rand(d
.buf
, d
.len
);
781 /* --- SEAL output --- */
783 static grand
*gen_seal(unsigned i
)
789 static struct option opts
[] = {
790 { "key", OPTF_ARGREQ
, 0, 'k' },
791 { "hex", OPTF_ARGREQ
, 0, 'H' },
792 { "sequence", OPTF_ARGREQ
, 0, 'n' },
796 addopts("k:H:n:", opts
);
805 textkey(&d
, optarg
, seal_keysz
);
809 hexkey(&d
, optarg
, seal_keysz
);
813 n
= strtoul(optarg
, &p
, 0);
815 die(EXIT_FAILURE
, "bad number `%s'", optarg
);
823 randkey(&d
, seal_keysz
);
824 r
= seal_rand(d
.buf
, d
.len
, n
);
829 /* --- Output feedback generators --- */
831 static grand
*gen_ofb(unsigned i
)
837 static struct option opts
[] = {
838 { "key", OPTF_ARGREQ
, 0, 'k' },
839 { "hex", OPTF_ARGREQ
, 0, 'H' },
840 { "iv", OPTF_ARGREQ
, 0, 'i' },
844 addopts("k:H:i:", opts
);
853 textkey(&d
, optarg
, ciphertab
[i
].keysz
);
857 hexkey(&d
, optarg
, ciphertab
[i
].keysz
);
861 unhex(optarg
, &p
, &iv
);
863 die(EXIT_FAILURE
, "bad hex IV `%s'", optarg
);
871 randkey(&d
, ciphertab
[i
].keysz
);
872 r
= ciphertab
[i
].ofb(d
.buf
, d
.len
);
874 if (iv
.len
!= ciphertab
[i
].blksz
) {
875 die(EXIT_FAILURE
, "bad IV length %lu (must be %lu)",
876 (unsigned long)iv
.len
, (unsigned long)ciphertab
[i
].blksz
);
878 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, iv
.buf
);
886 /* --- Counter generators --- */
888 static grand
*gen_counter(unsigned i
)
894 static struct option opts
[] = {
895 { "key", OPTF_ARGREQ
, 0, 'k' },
896 { "hex", OPTF_ARGREQ
, 0, 'H' },
897 { "iv", OPTF_ARGREQ
, 0, 'i' },
901 addopts("k:H:i:", opts
);
910 textkey(&d
, optarg
, ciphertab
[i
].keysz
);
914 hexkey(&d
, optarg
, ciphertab
[i
].keysz
);
918 unhex(optarg
, &p
, &iv
);
920 die(EXIT_FAILURE
, "bad hex IV `%s'", optarg
);
928 randkey(&d
, ciphertab
[i
].keysz
);
929 r
= ciphertab
[i
].counter(d
.buf
, d
.len
);
931 if (iv
.len
!= ciphertab
[i
].blksz
) {
932 die(EXIT_FAILURE
, "bad IV length %lu (must be %lu)",
933 (unsigned long)iv
.len
, (unsigned long)ciphertab
[i
].blksz
);
935 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, iv
.buf
);
943 /* --- Mask generators --- */
945 static grand
*gen_mgf(unsigned i
)
951 static struct option opts
[] = {
952 { "key", OPTF_ARGREQ
, 0, 'k' },
953 { "hex", OPTF_ARGREQ
, 0, 'H' },
954 { "index", OPTF_ARGREQ
, 0, 'i' },
958 addopts("k:H:i:", opts
);
967 textkey(&d
, optarg
, hashtab
[i
].keysz
);
971 hexkey(&d
, optarg
, hashtab
[i
].keysz
);
975 c
= strtoul(optarg
, &p
, 0);
977 die(EXIT_FAILURE
, "bad index `%s'", optarg
);
985 randkey(&d
, hashtab
[i
].keysz
);
987 r
= hashtab
[i
].mgf(d
.buf
, d
.len
);
989 r
->ops
->misc(r
, GRAND_SEEDUINT32
, c
);
995 /* --- Fibonacci generator --- */
997 static grand
*gen_fib(unsigned i
)
1004 static struct option opts
[] = {
1005 { "seed", OPTF_ARGREQ
, 0, 's' },
1009 addopts("s:", opts
);
1017 s
= strtoul(optarg
, &p
, 0);
1019 die(EXIT_FAILURE
, "bad integer `%s'", optarg
);
1026 r
= fibrand_create(s
);
1028 r
->ops
->misc(r
, GRAND_SEEDRAND
, &rand_global
);
1032 /* --- LC generator --- */
1034 static grand
*gen_lc(unsigned i
)
1040 static struct option opts
[] = {
1041 { "seed", OPTF_ARGREQ
, 0, 's' },
1045 addopts("s:", opts
);
1053 s
= strtoul(optarg
, &p
, 0);
1055 die(EXIT_FAILURE
, "bad integer `%s'", optarg
);
1064 s
= rand_global
.ops
->range(&rand_global
, LCRAND_P
);
1065 while (s
== LCRAND_FIXEDPT
);
1067 return (lcrand_create(s
));
1070 /* --- Basic options parser -- can't generate output --- */
1072 static grand
*gen_opts(unsigned i
)
1079 /*----- Generators table --------------------------------------------------*/
1081 gen generators
[] = {
1082 { "fibonacci", gen_fib
, 0,
1086 #define E(PRE, pre) \
1087 { #pre "-ofb", gen_ofb, CIPHER_##PRE, \
1088 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
1091 #define E(PRE, pre) \
1092 { #pre "-counter", gen_counter, CIPHER_##PRE, \
1093 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
1096 #define E(PRE, pre) \
1097 { #pre "-mgf", gen_mgf, HASH_##PRE, \
1098 "[-k KEY-PHRASE] [-H HEX-KEY] [-i INDEX]" },
1101 { "rc4", gen_rc4
, 0,
1102 "[-k KEY-PHRASE] [-H HEX-KEY]" },
1103 { "seal", gen_seal
, 0,
1104 "[-k KEY-PHRASE] [-H HEX-KEY] [-n SEQ]" },
1105 { "rand", gen_rand
, 0,
1106 "[-n] [-k KEY-PHRASE] [-t TEXT-BLOCK] [-H HEX-BLOCK]" },
1107 { "bbs", gen_bbs
, 0,
1108 "[-gS] [-s SEED] [-M MODULUS] [-b BITS] [-k KEYRING] [-i TAG] [-t TYPE]"
1113 static gen optsg
= { "options", gen_opts
, 0,
1114 "This message shouldn't be printed." };
1116 /*----- Random number generation ------------------------------------------*/
1118 static int genfile(const void *buf
, size_t sz
, void *p
)
1121 if (fwrite(buf
, 1, sz
, fp
) != sz
)
1122 die(EXIT_FAILURE
, "error writing to file: %s", strerror(errno
));
1126 static int genbuf(const void *buf
, size_t sz
, void *p
)
1129 memcpy(*pp
, buf
, sz
);
1134 typedef struct genmaurer_ctx
{
1139 static int genmaurer(const void *buf
, size_t sz
, void *p
)
1141 genmaurer_ctx
*g
= p
;
1144 for (i
= 0; i
< g
->n
; i
++)
1145 maurer_test(&g
->m
[i
], buf
, sz
);
1149 static int generate(grand
*r
, size_t outsz
,
1150 int (*func
)(const void *buf
, size_t sz
, void *p
),
1153 static char kmg
[] = { ' ', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 0 };
1155 unsigned percent
= 0;
1158 static char baton
[] = "-\\|/";
1163 /* --- Spit out random data --- */
1167 if (flags
& f_progress
) {
1168 char *errbuf
= xmalloc(BUFSIZ
);
1169 setvbuf(stderr
, errbuf
, _IOLBF
, BUFSIZ
);
1171 fprintf(stderr
, "[%*s] 0%% 0\r[/\b", 50, "");
1173 fputs("[ ] 0\r[/\b", stderr
);
1178 signal(SIGPIPE
, SIG_IGN
);
1183 size_t sz
= sizeof(buf
);
1184 clock_t c_start
, c_stop
;
1186 /* --- Emit a bufferful (or less) of data --- */
1189 if (sz
> outsz
- kb
)
1193 r
->ops
->fill(r
, buf
, sz
);
1195 clk
+= c_stop
- c_start
;
1196 if (func
&& (rc
= func(buf
, sz
, p
)) != 0)
1200 /* --- Update the display --- */
1202 if (flags
& f_progress
) {
1210 if (difftime(t
, last
) > 1.0) {
1214 fputs(" ] ", stderr
);
1216 unsigned pc
= kb
* 100.0 / outsz
;
1217 if (pc
> percent
|| percent
> 100 || difftime(t
, last
) > 1.0) {
1221 for (; percent
< (pc
& ~1); percent
+= 2)
1224 for (; pc
< 100; pc
+= 2)
1226 fprintf(stderr
, "] %3i%% ", percent
);
1234 while (q
> 8192 && kk
[1]) {
1238 fprintf(stderr
, "%4i%c\r[", q
, *kk
);
1241 for (pc
= 0; pc
< (percent
& ~1); pc
+= 2)
1250 if (percent
< 100) {
1251 putc(*bp
++, stderr
);
1259 /* --- Terminate the loop --- */
1261 } while (!outsz
|| kb
< outsz
);
1263 if (flags
& f_progress
)
1264 fputc('\n', stderr
);
1265 if (flags
& f_timer
) {
1266 fprintf(stderr
, "generated %lu bytes ", (unsigned long)outsz
);
1268 fputs("too quickly to measure\n", stderr
);
1271 double sec
= (double)clk
/CLOCKS_PER_SEC
;
1272 double bps
= (outsz
<< 3)/sec
;
1273 for (kk
= kmg
; bps
> 1024 && kk
[1]; kk
++, bps
/= 1024)
1275 fprintf(stderr
, "in %g secs (%g %cb/s)\n", sec
, bps
, *kk
);
1281 /*----- Main code ---------------------------------------------------------*/
1283 int main(int ac
, char *av
[])
1288 /* --- Initialize mLib --- */
1293 /* --- Set up the main Catacomb generator --- */
1295 rand_noisesrc(RAND_GLOBAL
, &noise_source
);
1296 rand_seed(RAND_GLOBAL
, 160);
1298 /* --- Initialize the options table --- */
1300 addopts(sopts
, opts
);
1305 /* --- Read the generator out of the first argument --- */
1307 if (argc
> 1 && *argv
[1] != '-') {
1308 const char *arg
= av
[1];
1309 size_t sz
= strlen(arg
);
1313 for (gg
= generators
; gg
->name
; gg
++) {
1314 if (strncmp(arg
, gg
->name
, sz
) == 0) {
1315 if (gg
->name
[sz
] == 0) {
1319 die(EXIT_FAILURE
, "ambiguous generator name `%s'", arg
);
1325 die(EXIT_FAILURE
, "unknown generator name `%s'", arg
);
1330 /* --- Get a generic random number generator --- */
1333 if (!r
|| optind
!= ac
- 1) {
1338 /* --- Do the FIPS test --- */
1340 if (flags
& f_fips
) {
1341 octet buf
[FIPSTEST_BUFSZ
];
1345 generate(r
, sizeof(buf
), genbuf
, &p
);
1347 if (rc
& FIPSTEST_MONOBIT
)
1348 moan("failed monobit test");
1349 if (rc
& FIPSTEST_POKER
)
1350 moan("failed poker test");
1351 if (rc
& FIPSTEST_RUNS
)
1352 moan("failed runs test");
1353 if (rc
& FIPSTEST_LONGRUNS
)
1354 moan("failed long runs test");
1355 if (!rc
&& (flags
& f_progress
))
1356 fputs("test passed\n", stderr
);
1357 return (rc ? EXIT_FAILURE
: 0);
1360 /* --- Do Maurer's test --- */
1362 if (flags
& f_maurer
) {
1368 static struct { double x
; const char *sig
; } sigtab
[] = {
1376 g
.n
= maurer_hi
- maurer_lo
+ 1;
1377 g
.m
= xmalloc(g
.n
* sizeof(maurer_ctx
));
1378 for (i
= 0; i
< g
.n
; i
++)
1379 maurer_init(&g
.m
[i
], i
+ maurer_lo
);
1380 bufsz
= (100 * maurer_hi
) << maurer_hi
;
1382 generate(r
, bufsz
, genmaurer
, &g
);
1384 for (i
= maurer_lo
; i
<= maurer_hi
; i
++) {
1385 double z
= maurer_done(&g
.m
[i
- maurer_lo
]);
1386 double zz
= fabs(z
);
1389 for (j
= 0; sigtab
[j
].sig
; j
++) {
1390 if (zz
> sigtab
[j
].x
) {
1392 moan("failed, bits = %u, sig = %s, Z_u = %g",
1393 i
, sigtab
[j
].sig
, z
);
1397 if (flags
& f_progress
)
1398 fprintf(stderr
, "bits = %u, Z_u = %g\n", i
, z
);
1405 /* --- Discard --- */
1407 if (flags
& f_discard
) {
1408 generate(r
, outsz
, 0, 0);
1412 /* --- Write to a file --- */
1415 if (!(flags
& f_file
) && isatty(STDOUT_FILENO
))
1416 die(EXIT_FAILURE
, "writing output to a terminal is a bad idea");
1419 generate(r
, outsz
, genfile
, outfp
);
1427 /*----- That's all, folks -------------------------------------------------*/