3 * $Id: rspit.c,v 1.14 2001/02/21 20:03:22 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.14 2001/02/21 20:03:22 mdw
34 * Added support for MD2 hash function.
36 * Revision 1.13 2000/12/06 20:33:27 mdw
37 * Make flags be macros rather than enumerations, to ensure that they're
40 * Revision 1.12 2000/10/08 15:49:18 mdw
41 * Remove failed kludge for shutting up a warning.
43 * Revision 1.11 2000/10/08 12:10:32 mdw
44 * Make table have external linkage to bodge around deficiency in C. The
45 * problem is that @static gen generators[];@ is considered to be a
46 * `tentative definition', and therefore mustn't have incomplete type,
47 * which it obviously has.
49 * Revision 1.10 2000/08/11 21:34:59 mdw
50 * New restartable interface to Maurer testing.
52 * Revision 1.9 2000/08/04 23:24:15 mdw
53 * Add a timer and a discard option.
55 * Revision 1.8 2000/07/29 22:05:47 mdw
56 * Fix error in help message about Maurer test syntax.
58 * Revision 1.7 2000/07/18 23:01:26 mdw
59 * Improve progress indications, and allow user to choose chunk sizes for
62 * Revision 1.6 2000/07/15 20:53:35 mdw
63 * Add a load of new ciphers and hashes.
65 * Revision 1.5 2000/07/01 11:27:03 mdw
66 * Portability fix: don't assume that `stdout' is a constant expression.
67 * Remove old type name `bbs_param'.
69 * Revision 1.4 2000/06/17 12:08:28 mdw
70 * Restructure handling of cipher-based generators. Add counter-mode
71 * ciphers and MGF-1 hash functions. Add FIPS 140-1 and Maurer's tests.
73 * Revision 1.3 2000/02/12 18:21:03 mdw
74 * Overhaul of key management (again).
76 * Revision 1.2 1999/12/22 15:59:51 mdw
77 * New prime-search system. Read BBS keys from key files.
79 * Revision 1.1 1999/12/10 23:29:13 mdw
80 * Emit random numbers for statistical tests.
84 /*----- Header files ------------------------------------------------------*/
101 #include <mLib/darray.h>
102 #include <mLib/dstr.h>
103 #include <mLib/mdwopt.h>
104 #include <mLib/quis.h>
105 #include <mLib/report.h>
106 #include <mLib/sub.h>
108 #include "fipstest.h"
125 #include "des3-ofb.h"
128 #include "skipjack-ofb.h"
130 #include "xtea-ofb.h"
131 #include "blowfish-ofb.h"
132 #include "twofish-ofb.h"
133 #include "idea-ofb.h"
134 #include "cast128-ofb.h"
135 #include "cast256-ofb.h"
136 #include "rijndael-ofb.h"
137 #include "square-ofb.h"
138 #include "serpent-ofb.h"
140 #include "des-counter.h"
141 #include "des3-counter.h"
142 #include "rc2-counter.h"
143 #include "rc5-counter.h"
144 #include "skipjack-counter.h"
145 #include "tea-counter.h"
146 #include "xtea-counter.h"
147 #include "blowfish-counter.h"
148 #include "twofish-counter.h"
149 #include "idea-counter.h"
150 #include "cast128-counter.h"
151 #include "cast256-counter.h"
152 #include "rijndael-counter.h"
153 #include "square-counter.h"
154 #include "serpent-counter.h"
160 #include "tiger-mgf.h"
161 #include "rmd128-mgf.h"
162 #include "rmd160-mgf.h"
163 #include "rmd256-mgf.h"
164 #include "rmd320-mgf.h"
168 /*----- Data structures ---------------------------------------------------*/
172 grand
*(*seed
)(unsigned /*i*/);
177 extern gen generators
[];
184 E(SKIPJACK, skipjack) \
187 E(BLOWFISH, blowfish) \
188 E(TWOFISH, twofish) \
190 E(CAST128, cast128) \
191 E(CAST256, cast256) \
193 E(RIJNDAEL, rijndael) \
207 #define E(PRE, pre) CIPHER_##PRE,
208 enum { CIPHERS CIPHER__bogus
};
211 #define E(PRE, pre) HASH_##PRE,
212 enum { HASHES HASH__bogus
};
218 grand
*(*ofb
)(const void */
*k*/
, size_t /*sz*/);
219 grand
*(*counter
)(const void */
*k*/
, size_t /*sz*/);
221 #define E(PRE, pre) \
222 { pre##_keysz, PRE##_BLKSZ, pre##_ofbrand, pre##_counterrand },
230 grand
*(*mgf
)(const void */
*k*/
, size_t /*sz*/);
232 #define E(PRE, pre) \
233 { &pre, pre##_mgfkeysz, pre##_mgfrand },
238 /*----- Miscellaneous static data -----------------------------------------*/
241 static size_t outsz
= 0;
242 static unsigned maurer_lo
= 5, maurer_hi
= 8;
247 static unsigned flags
= 0;
249 #define f_progress 1u
254 #define f_discard 32u
256 /*----- Help options ------------------------------------------------------*/
258 static void usage(FILE *fp
)
260 pquis(fp
, "Usage: $ generator [options]\n");
263 static void version(FILE *fp
)
265 pquis(fp
, "$, Catacomb version " VERSION
"\n");
268 static void help(FILE *fp
)
274 Emits a stream of random bytes suitable for, well, all sorts of things.\n\
275 The primary objective is to be able to generate streams of input for\n\
276 statistical tests, such as Diehard.\n\
278 Options are specific to the particular generator, although there's a\n\
281 -h, --help Display this help message.\n\
282 -v, --version Display the program's version number.\n\
283 -u, --usage Display a useless usage message.\n\
285 -l, --list Show a list of the supported generators, with\n\
287 -f, --fipstest Run the FIPS 140-1 randomness test.\n\
288 -m, --maurer[=LO-HI] Run Maurer's universal statistical test.\n\
289 -o, --output FILE Write output to FILE, not stdout.\n\
290 -z, --size SIZE Emit SIZE bytes, not an unlimited number.\n\
291 -p, --progress Show a little progress meter (on stderr).\n\
292 -T, --timer Keep track of the CPU time used by the generator.\n\
293 -d, --discard Discard the generated output.\n\
295 (A SIZE may be followed by `g' for gigabytes, `m' for megabytes, or\n\
296 `k' for kilobytes. If unqualified, an amount in bytes is assumed.)\n\
300 /*----- Main options parser -----------------------------------------------*/
302 static struct option opts
[] = {
304 /* --- Standard GNU help options --- */
306 { "help", 0, 0, 'h' },
307 { "version", 0, 0, 'v' },
308 { "usage", 0, 0, 'u' },
310 /* --- Other useful things --- */
312 { "list", 0, 0, 'l' },
313 { "fipstest", 0, 0, 'f' },
314 { "maurer", OPTF_ARGOPT
, 0, 'm' },
315 { "output", OPTF_ARGREQ
, 0, 'o' },
316 { "size", OPTF_ARGREQ
, 0, 'z' },
317 { "progress", 0, 0, 'p' },
318 { "timer", 0, 0, 'T' },
319 { "discard", 0, 0, 'd' },
321 /* --- End of main table --- */
326 static const char *sopts
= "hvu lfm::o:z:pTd";
329 DA_DECL(option_v
, struct option
);
333 static option_v optv
= DA_INIT
;
334 static dstr optd
= DSTR_INIT
;
336 /* --- @addopts@ --- *
338 * Arguments: @const char *s@ = pointer to short options
339 * @struct option *l@ = pointer to long options
343 * Use: Adds a collection of options to the table.
346 static void addopts(const char *s
, struct option
*l
)
352 DA_PUSH(&optv
, *l
++);
360 * Returns: Next option from argument array.
362 * Use: Fetches options, handling the standard ones.
368 int i
= mdwopt(argc
, argv
, optd
.buf
, DA(&optv
), 0, 0, 0);
381 puts("Generators supported:");
382 for (g
= generators
; g
->name
; g
++)
383 printf(" %s %s\n", g
->name
, g
->help
);
393 unsigned long lo
, hi
;
394 lo
= strtoul(optarg
, &p
, 0);
395 if (*p
== '-' || *p
== ',')
396 hi
= strtoul(p
+ 1, &p
, 0);
399 if (*p
!= 0 || hi
< lo
|| lo
== 0)
400 die(EXIT_FAILURE
, "bad bit range `%s'", optarg
);
407 die(EXIT_FAILURE
, "already set an output file");
408 if (strcmp(optarg
, "-") == 0)
411 outfp
= fopen(optarg
, "w");
413 die(EXIT_FAILURE
, "couldn't open output file `%s': %s",
414 optarg
, strerror(errno
));
421 outsz
= strtoul(optarg
, &p
, 0);
423 die(EXIT_FAILURE
, "bad number `%s'", optarg
);
425 case 'G': case 'g': outsz
*= 1024;
426 case 'M': case 'm': outsz
*= 1024;
427 case 'K': case 'k': outsz
*= 1024;
431 die(EXIT_FAILURE
, "bad suffix `%s'", p
);
435 die(EXIT_FAILURE
, "bad suffix `%s'", p
);
452 /*----- Manglers for seed strings -----------------------------------------*/
456 * Arguments: @const char *p@ = pointer to input string
457 * @char **end@ = where the end goes
458 * @dstr *d@ = output buffer
462 * Use: Transforms a hex string into a chunk of binary data.
465 static void unhex(const char *p
, char **end
, dstr
*d
)
467 while (p
[0] && p
[1]) {
468 int x
= p
[0], y
= p
[1];
469 if ('0' <= x
&& x
<= '9') x
-= '0';
470 else if ('A' <= x
&& x
<= 'F') x
-= 'A' - 10;
471 else if ('a' <= x
&& x
<= 'f') x
-= 'a' - 10;
473 if ('0' <= y
&& y
<= '9') y
-= '0';
474 else if ('A' <= y
&& y
<= 'F') y
-= 'A' - 10;
475 else if ('a' <= y
&& y
<= 'f') y
-= 'a' - 10;
477 DPUTC(d
, (x
<< 4) + y
);
483 /* --- Generate a key --- */
485 static void textkey(dstr
*d
, const char *p
, const octet
*ksz
)
487 size_t sz
= strlen(p
);
490 die(EXIT_FAILURE
, "zero-length key string");
491 if (keysz(sz
, ksz
) != sz
)
495 rmd160_mgfinit(&g
, p
, sz
);
498 rmd160_mgfencrypt(&g
, 0, d
->buf
, sz
);
501 assert(((void)"I can't seem to choose a good key size",
502 keysz(d
->len
, ksz
) == d
->len
));
505 static void hexkey(dstr
*d
, const char *p
, const octet
*ksz
)
508 unhex(optarg
, &q
, d
);
510 die(EXIT_FAILURE
, "bad hex key `%s'", p
);
511 if (keysz(d
->len
, ksz
) != d
->len
)
512 die(EXIT_FAILURE
, "bad key length");
515 static void randkey(dstr
*d
, const octet
*ksz
)
517 size_t sz
= keysz(0, ksz
);
519 rand_get(RAND_GLOBAL
, d
->buf
, sz
);
523 /*----- Generators --------------------------------------------------------*/
525 /* --- Blum-Blum-Shub strong generator --- */
527 static grand
*gen_bbs(unsigned i
)
529 /* --- Default modulus --- *
531 * The factors of this number are
533 * @p = 1229936431484295969649886203367009966370895964206162032259292413@
534 * @7754313537966036459299022912838407755462506416274551744201653277@
535 * @313130311731673973886822067@
537 * @q = 9798171783943489959487301695884963889684294764514008432498259742@
538 * @5374320073594018817245784145742769603334292182227671519041431067@
539 * @61344781426317516045890159@
541 * Both %$p$% and %$q$% are prime; %$(p - 1)/2$% and %$(q - 1)/2$% have no
542 * common factors. They were found using this program, with random
545 * I hope that, by publishing these factors, I'll dissuade people from
546 * actually using this modulus in an attempt to attain real security. The
547 * program is quite quick at finding Blum numbers, so there's no excuse for
548 * not generating your own.
552 "120511284390135742513572142094334711443073194119732569353820828435640527418092392240366088035509890969913081816369160298961490135716255689660470370755013177656905237112577648090277537209936078171554274553448103698084782669252936352843649980105109850503830397166360721262431179505917248447259735253684659338653";
554 /* --- Other things --- */
558 unsigned bits
= 1024;
561 const char *kfile
= 0, *id
= 0, *ktype
= 0;
563 /* --- Parse options --- */
565 static struct option opts
[] = {
566 { "modulus", OPTF_ARGREQ
, 0, 'M' },
567 { "generate", 0, 0, 'g' },
568 { "seed", OPTF_ARGREQ
, 0, 's' },
569 { "bits", OPTF_ARGREQ
, 0, 'b' },
570 { "show", 0, 0, 'S' },
571 { "keyring", OPTF_ARGREQ
, 0, 'k' },
572 { "id", OPTF_ARGREQ
, 0, 'i' },
573 { "type", OPTF_ARGREQ
, 0, 't' },
577 addopts("M:gs:b:Sk:i:t:", opts
);
594 bits
= strtoul(optarg
, 0, 0);
596 die(EXIT_FAILURE
, "bad number of bits `%s'", optarg
);
618 /* --- Generate a modulus if one is requested --- */
622 m
= mp_readstring(MP_NEW
, mt
, &p
, 0);
623 if (!m
|| *p
|| (m
->v
[0] & 3) != 1)
624 die(EXIT_FAILURE
, "bad modulus `%s'", mt
);
625 /* Unfortunately I don't know how to test for a Blum integer */
626 } else if (kfile
|| id
|| ktype
) {
631 /* --- Open the key file --- */
635 if (key_open(&kf
, kfile
, KOPEN_READ
, key_moan
, 0)) {
636 die(EXIT_FAILURE
, "error opening key file `%s': %s",
637 kfile
, strerror(errno
));
640 /* --- Find the key --- */
643 if ((kk
= key_bytag(&kf
, id
)) == 0)
644 die(EXIT_FAILURE
, "key `%s' not found", id
);
648 if ((kk
= key_bytype(&kf
, ktype
)) == 0)
649 die(EXIT_FAILURE
, "no suitable key with type `%s' found", ktype
);
652 /* --- Read the key data --- */
654 if ((kk
->k
.e
& KF_ENCMASK
) != KENC_STRUCT
)
655 die(EXIT_FAILURE
, "key is not structured");
656 if ((kd
= key_structfind(&kk
->k
, "n")) == 0)
657 die(EXIT_FAILURE
, "key has no subkey `n'");
658 if ((kd
->e
& KF_ENCMASK
) != KENC_MP
)
659 die(EXIT_FAILURE
, "incomatible subkey encoding");
660 m
= MP_COPY(kd
->u
.m
);
665 if (bbs_gen(&bp
, bits
, &rand_global
, 0,
666 (flags
& f_progress
) ? pgen_ev
: 0, 0))
667 die(EXIT_FAILURE
, "modulus generation failed");
671 fputs("p = ", stderr
);
672 mp_writefile(bp
.p
, stderr
, 10);
673 fputs("\nq = ", stderr
);
674 mp_writefile(bp
.q
, stderr
, 10);
675 fputs("\nn = ", stderr
);
676 mp_writefile(bp
.n
, stderr
, 10);
684 /* --- Set up a seed --- */
687 x
= mprand(MP_NEW
, mp_bits(m
) - 1, &rand_global
, 1);
690 x
= mp_readstring(MP_NEW
, xt
, &p
, 0);
692 die(EXIT_FAILURE
, "bad modulus `%s'", xt
);
704 /* --- Catacomb's random number generator --- */
706 static grand
*gen_rand(unsigned i
)
708 grand
*r
= rand_create();
711 static struct option opts
[] = {
712 { "key", OPTF_ARGREQ
, 0, 'k' },
713 { "text", OPTF_ARGREQ
, 0, 't' },
714 { "hex", OPTF_ARGREQ
, 0, 'H' },
718 addopts("k:t:H:n", opts
);
720 r
->ops
->misc(r
, RAND_NOISESRC
, &noise_source
);
721 r
->ops
->misc(r
, RAND_SEED
, 160);
730 textkey(&d
, optarg
, rmd160_mackeysz
);
731 r
->ops
->misc(r
, RAND_KEY
, d
.buf
, d
.len
);
734 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, optarg
, strlen(optarg
));
738 hexkey(&d
, optarg
, rmd160_mackeysz
);
739 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, d
.buf
, d
.len
);
748 /* --- RC4 output --- */
750 static grand
*gen_rc4(unsigned i
)
755 static struct option opts
[] = {
756 { "key", OPTF_ARGREQ
, 0, 'k' },
757 { "hex", OPTF_ARGREQ
, 0, 'H' },
761 addopts("k:H:", opts
);
770 textkey(&d
, optarg
, rc4_keysz
);
774 hexkey(&d
, optarg
, rc4_keysz
);
782 randkey(&d
, rc4_keysz
);
783 r
= rc4_rand(d
.buf
, d
.len
);
788 /* --- SEAL output --- */
790 static grand
*gen_seal(unsigned i
)
796 static struct option opts
[] = {
797 { "key", OPTF_ARGREQ
, 0, 'k' },
798 { "hex", OPTF_ARGREQ
, 0, 'H' },
799 { "sequence", OPTF_ARGREQ
, 0, 'n' },
803 addopts("k:H:n:", opts
);
812 textkey(&d
, optarg
, seal_keysz
);
816 hexkey(&d
, optarg
, seal_keysz
);
820 n
= strtoul(optarg
, &p
, 0);
822 die(EXIT_FAILURE
, "bad number `%s'", optarg
);
830 randkey(&d
, seal_keysz
);
831 r
= seal_rand(d
.buf
, d
.len
, n
);
836 /* --- Output feedback generators --- */
838 static grand
*gen_ofb(unsigned i
)
844 static struct option opts
[] = {
845 { "key", OPTF_ARGREQ
, 0, 'k' },
846 { "hex", OPTF_ARGREQ
, 0, 'H' },
847 { "iv", OPTF_ARGREQ
, 0, 'i' },
851 addopts("k:H:i:", opts
);
860 textkey(&d
, optarg
, ciphertab
[i
].keysz
);
864 hexkey(&d
, optarg
, ciphertab
[i
].keysz
);
868 unhex(optarg
, &p
, &iv
);
870 die(EXIT_FAILURE
, "bad hex IV `%s'", optarg
);
878 randkey(&d
, ciphertab
[i
].keysz
);
879 r
= ciphertab
[i
].ofb(d
.buf
, d
.len
);
881 if (iv
.len
!= ciphertab
[i
].blksz
) {
882 die(EXIT_FAILURE
, "bad IV length %lu (must be %lu)",
883 (unsigned long)iv
.len
, (unsigned long)ciphertab
[i
].blksz
);
885 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, iv
.buf
);
893 /* --- Counter generators --- */
895 static grand
*gen_counter(unsigned i
)
901 static struct option opts
[] = {
902 { "key", OPTF_ARGREQ
, 0, 'k' },
903 { "hex", OPTF_ARGREQ
, 0, 'H' },
904 { "iv", OPTF_ARGREQ
, 0, 'i' },
908 addopts("k:H:i:", opts
);
917 textkey(&d
, optarg
, ciphertab
[i
].keysz
);
921 hexkey(&d
, optarg
, ciphertab
[i
].keysz
);
925 unhex(optarg
, &p
, &iv
);
927 die(EXIT_FAILURE
, "bad hex IV `%s'", optarg
);
935 randkey(&d
, ciphertab
[i
].keysz
);
936 r
= ciphertab
[i
].counter(d
.buf
, d
.len
);
938 if (iv
.len
!= ciphertab
[i
].blksz
) {
939 die(EXIT_FAILURE
, "bad IV length %lu (must be %lu)",
940 (unsigned long)iv
.len
, (unsigned long)ciphertab
[i
].blksz
);
942 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, iv
.buf
);
950 /* --- Mask generators --- */
952 static grand
*gen_mgf(unsigned i
)
958 static struct option opts
[] = {
959 { "key", OPTF_ARGREQ
, 0, 'k' },
960 { "hex", OPTF_ARGREQ
, 0, 'H' },
961 { "index", OPTF_ARGREQ
, 0, 'i' },
965 addopts("k:H:i:", opts
);
974 textkey(&d
, optarg
, hashtab
[i
].keysz
);
978 hexkey(&d
, optarg
, hashtab
[i
].keysz
);
982 c
= strtoul(optarg
, &p
, 0);
984 die(EXIT_FAILURE
, "bad index `%s'", optarg
);
992 randkey(&d
, hashtab
[i
].keysz
);
994 r
= hashtab
[i
].mgf(d
.buf
, d
.len
);
996 r
->ops
->misc(r
, GRAND_SEEDUINT32
, c
);
1002 /* --- Fibonacci generator --- */
1004 static grand
*gen_fib(unsigned i
)
1011 static struct option opts
[] = {
1012 { "seed", OPTF_ARGREQ
, 0, 's' },
1016 addopts("s:", opts
);
1024 s
= strtoul(optarg
, &p
, 0);
1026 die(EXIT_FAILURE
, "bad integer `%s'", optarg
);
1033 r
= fibrand_create(s
);
1035 r
->ops
->misc(r
, GRAND_SEEDRAND
, &rand_global
);
1039 /* --- LC generator --- */
1041 static grand
*gen_lc(unsigned i
)
1047 static struct option opts
[] = {
1048 { "seed", OPTF_ARGREQ
, 0, 's' },
1052 addopts("s:", opts
);
1060 s
= strtoul(optarg
, &p
, 0);
1062 die(EXIT_FAILURE
, "bad integer `%s'", optarg
);
1071 s
= rand_global
.ops
->range(&rand_global
, LCRAND_P
);
1072 while (s
== LCRAND_FIXEDPT
);
1074 return (lcrand_create(s
));
1077 /* --- Basic options parser -- can't generate output --- */
1079 static grand
*gen_opts(unsigned i
)
1086 /*----- Generators table --------------------------------------------------*/
1088 gen generators
[] = {
1089 { "fibonacci", gen_fib
, 0,
1093 #define E(PRE, pre) \
1094 { #pre "-ofb", gen_ofb, CIPHER_##PRE, \
1095 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
1098 #define E(PRE, pre) \
1099 { #pre "-counter", gen_counter, CIPHER_##PRE, \
1100 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
1103 #define E(PRE, pre) \
1104 { #pre "-mgf", gen_mgf, HASH_##PRE, \
1105 "[-k KEY-PHRASE] [-H HEX-KEY] [-i INDEX]" },
1108 { "rc4", gen_rc4
, 0,
1109 "[-k KEY-PHRASE] [-H HEX-KEY]" },
1110 { "seal", gen_seal
, 0,
1111 "[-k KEY-PHRASE] [-H HEX-KEY] [-n SEQ]" },
1112 { "rand", gen_rand
, 0,
1113 "[-n] [-k KEY-PHRASE] [-t TEXT-BLOCK] [-H HEX-BLOCK]" },
1114 { "bbs", gen_bbs
, 0,
1115 "[-gS] [-s SEED] [-M MODULUS] [-b BITS] [-k KEYRING] [-i TAG] [-t TYPE]"
1120 static gen optsg
= { "options", gen_opts
, 0,
1121 "This message shouldn't be printed." };
1123 /*----- Random number generation ------------------------------------------*/
1125 static int genfile(const void *buf
, size_t sz
, void *p
)
1128 if (fwrite(buf
, 1, sz
, fp
) != sz
)
1129 die(EXIT_FAILURE
, "error writing to file: %s", strerror(errno
));
1133 static int genbuf(const void *buf
, size_t sz
, void *p
)
1136 memcpy(*pp
, buf
, sz
);
1141 typedef struct genmaurer_ctx
{
1146 static int genmaurer(const void *buf
, size_t sz
, void *p
)
1148 genmaurer_ctx
*g
= p
;
1151 for (i
= 0; i
< g
->n
; i
++)
1152 maurer_test(&g
->m
[i
], buf
, sz
);
1156 static int generate(grand
*r
, size_t outsz
,
1157 int (*func
)(const void *buf
, size_t sz
, void *p
),
1160 static char kmg
[] = { ' ', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 0 };
1162 unsigned percent
= 0;
1165 static char baton
[] = "-\\|/";
1170 /* --- Spit out random data --- */
1174 if (flags
& f_progress
) {
1175 char *errbuf
= xmalloc(BUFSIZ
);
1176 setvbuf(stderr
, errbuf
, _IOLBF
, BUFSIZ
);
1178 fprintf(stderr
, "[%*s] 0%% 0\r[/\b", 50, "");
1180 fputs("[ ] 0\r[/\b", stderr
);
1185 signal(SIGPIPE
, SIG_IGN
);
1190 size_t sz
= sizeof(buf
);
1191 clock_t c_start
, c_stop
;
1193 /* --- Emit a bufferful (or less) of data --- */
1196 if (sz
> outsz
- kb
)
1200 r
->ops
->fill(r
, buf
, sz
);
1202 clk
+= c_stop
- c_start
;
1203 if (func
&& (rc
= func(buf
, sz
, p
)) != 0)
1207 /* --- Update the display --- */
1209 if (flags
& f_progress
) {
1217 if (difftime(t
, last
) > 1.0) {
1221 fputs(" ] ", stderr
);
1223 unsigned pc
= kb
* 100.0 / outsz
;
1224 if (pc
> percent
|| percent
> 100 || difftime(t
, last
) > 1.0) {
1228 for (; percent
< (pc
& ~1); percent
+= 2)
1231 for (; pc
< 100; pc
+= 2)
1233 fprintf(stderr
, "] %3i%% ", percent
);
1241 while (q
> 8192 && kk
[1]) {
1245 fprintf(stderr
, "%4i%c\r[", q
, *kk
);
1248 for (pc
= 0; pc
< (percent
& ~1); pc
+= 2)
1257 if (percent
< 100) {
1258 putc(*bp
++, stderr
);
1266 /* --- Terminate the loop --- */
1268 } while (!outsz
|| kb
< outsz
);
1270 if (flags
& f_progress
)
1271 fputc('\n', stderr
);
1272 if (flags
& f_timer
) {
1273 fprintf(stderr
, "generated %lu bytes ", (unsigned long)outsz
);
1275 fputs("too quickly to measure\n", stderr
);
1278 double sec
= (double)clk
/CLOCKS_PER_SEC
;
1279 double bps
= (outsz
<< 3)/sec
;
1280 for (kk
= kmg
; bps
> 1024 && kk
[1]; kk
++, bps
/= 1024)
1282 fprintf(stderr
, "in %g secs (%g %cb/s)\n", sec
, bps
, *kk
);
1288 /*----- Main code ---------------------------------------------------------*/
1290 int main(int ac
, char *av
[])
1295 /* --- Initialize mLib --- */
1300 /* --- Set up the main Catacomb generator --- */
1302 rand_noisesrc(RAND_GLOBAL
, &noise_source
);
1303 rand_seed(RAND_GLOBAL
, 160);
1305 /* --- Initialize the options table --- */
1307 addopts(sopts
, opts
);
1312 /* --- Read the generator out of the first argument --- */
1314 if (argc
> 1 && *argv
[1] != '-') {
1315 const char *arg
= av
[1];
1316 size_t sz
= strlen(arg
);
1320 for (gg
= generators
; gg
->name
; gg
++) {
1321 if (strncmp(arg
, gg
->name
, sz
) == 0) {
1322 if (gg
->name
[sz
] == 0) {
1326 die(EXIT_FAILURE
, "ambiguous generator name `%s'", arg
);
1332 die(EXIT_FAILURE
, "unknown generator name `%s'", arg
);
1337 /* --- Get a generic random number generator --- */
1340 if (!r
|| optind
!= ac
- 1) {
1345 /* --- Do the FIPS test --- */
1347 if (flags
& f_fips
) {
1348 octet buf
[FIPSTEST_BUFSZ
];
1352 generate(r
, sizeof(buf
), genbuf
, &p
);
1354 if (rc
& FIPSTEST_MONOBIT
)
1355 moan("failed monobit test");
1356 if (rc
& FIPSTEST_POKER
)
1357 moan("failed poker test");
1358 if (rc
& FIPSTEST_RUNS
)
1359 moan("failed runs test");
1360 if (rc
& FIPSTEST_LONGRUNS
)
1361 moan("failed long runs test");
1362 if (!rc
&& (flags
& f_progress
))
1363 fputs("test passed\n", stderr
);
1364 return (rc ? EXIT_FAILURE
: 0);
1367 /* --- Do Maurer's test --- */
1369 if (flags
& f_maurer
) {
1375 static struct { double x
; const char *sig
; } sigtab
[] = {
1383 g
.n
= maurer_hi
- maurer_lo
+ 1;
1384 g
.m
= xmalloc(g
.n
* sizeof(maurer_ctx
));
1385 for (i
= 0; i
< g
.n
; i
++)
1386 maurer_init(&g
.m
[i
], i
+ maurer_lo
);
1387 bufsz
= (100 * maurer_hi
) << maurer_hi
;
1389 generate(r
, bufsz
, genmaurer
, &g
);
1391 for (i
= maurer_lo
; i
<= maurer_hi
; i
++) {
1392 double z
= maurer_done(&g
.m
[i
- maurer_lo
]);
1393 double zz
= fabs(z
);
1396 for (j
= 0; sigtab
[j
].sig
; j
++) {
1397 if (zz
> sigtab
[j
].x
) {
1399 moan("failed, bits = %u, sig = %s, Z_u = %g",
1400 i
, sigtab
[j
].sig
, z
);
1404 if (flags
& f_progress
)
1405 fprintf(stderr
, "bits = %u, Z_u = %g\n", i
, z
);
1412 /* --- Discard --- */
1414 if (flags
& f_discard
) {
1415 generate(r
, outsz
, 0, 0);
1419 /* --- Write to a file --- */
1422 if (!(flags
& f_file
) && isatty(STDOUT_FILENO
))
1423 die(EXIT_FAILURE
, "writing output to a terminal is a bad idea");
1426 generate(r
, outsz
, genfile
, outfp
);
1434 /*----- That's all, folks -------------------------------------------------*/