3 * $Id: rspit.c,v 1.11 2000/10/08 12:10:32 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.11 2000/10/08 12:10:32 mdw
34 * Make table have external linkage to bodge around deficiency in C. The
35 * problem is that @static gen generators[];@ is considered to be a
36 * `tentative definition', and therefore mustn't have incomplete type,
37 * which it obviously has.
39 * Revision 1.10 2000/08/11 21:34:59 mdw
40 * New restartable interface to Maurer testing.
42 * Revision 1.9 2000/08/04 23:24:15 mdw
43 * Add a timer and a discard option.
45 * Revision 1.8 2000/07/29 22:05:47 mdw
46 * Fix error in help message about Maurer test syntax.
48 * Revision 1.7 2000/07/18 23:01:26 mdw
49 * Improve progress indications, and allow user to choose chunk sizes for
52 * Revision 1.6 2000/07/15 20:53:35 mdw
53 * Add a load of new ciphers and hashes.
55 * Revision 1.5 2000/07/01 11:27:03 mdw
56 * Portability fix: don't assume that `stdout' is a constant expression.
57 * Remove old type name `bbs_param'.
59 * Revision 1.4 2000/06/17 12:08:28 mdw
60 * Restructure handling of cipher-based generators. Add counter-mode
61 * ciphers and MGF-1 hash functions. Add FIPS 140-1 and Maurer's tests.
63 * Revision 1.3 2000/02/12 18:21:03 mdw
64 * Overhaul of key management (again).
66 * Revision 1.2 1999/12/22 15:59:51 mdw
67 * New prime-search system. Read BBS keys from key files.
69 * Revision 1.1 1999/12/10 23:29:13 mdw
70 * Emit random numbers for statistical tests.
74 /*----- Header files ------------------------------------------------------*/
91 #include <mLib/darray.h>
92 #include <mLib/dstr.h>
93 #include <mLib/mdwopt.h>
94 #include <mLib/quis.h>
95 #include <mLib/report.h>
115 #include "des3-ofb.h"
118 #include "skipjack-ofb.h"
120 #include "xtea-ofb.h"
121 #include "blowfish-ofb.h"
122 #include "twofish-ofb.h"
123 #include "idea-ofb.h"
124 #include "cast128-ofb.h"
125 #include "cast256-ofb.h"
126 #include "rijndael-ofb.h"
127 #include "square-ofb.h"
128 #include "serpent-ofb.h"
130 #include "des-counter.h"
131 #include "des3-counter.h"
132 #include "rc2-counter.h"
133 #include "rc5-counter.h"
134 #include "skipjack-counter.h"
135 #include "tea-counter.h"
136 #include "xtea-counter.h"
137 #include "blowfish-counter.h"
138 #include "twofish-counter.h"
139 #include "idea-counter.h"
140 #include "cast128-counter.h"
141 #include "cast256-counter.h"
142 #include "rijndael-counter.h"
143 #include "square-counter.h"
144 #include "serpent-counter.h"
149 #include "tiger-mgf.h"
150 #include "rmd128-mgf.h"
151 #include "rmd160-mgf.h"
152 #include "rmd256-mgf.h"
153 #include "rmd320-mgf.h"
157 /*----- Data structures ---------------------------------------------------*/
161 grand
*(*seed
)(unsigned /*i*/);
166 extern gen generators
[];
173 E(SKIPJACK, skipjack) \
176 E(BLOWFISH, blowfish) \
177 E(TWOFISH, twofish) \
179 E(CAST128, cast128) \
180 E(CAST256, cast256) \
182 E(RIJNDAEL, rijndael) \
195 #define E(PRE, pre) CIPHER_##PRE,
196 enum { CIPHERS CIPHER__bogus
};
199 #define E(PRE, pre) HASH_##PRE,
200 enum { HASHES HASH__bogus
};
206 grand
*(*ofb
)(const void */
*k*/
, size_t /*sz*/);
207 grand
*(*counter
)(const void */
*k*/
, size_t /*sz*/);
209 #define E(PRE, pre) \
210 { pre##_keysz, PRE##_BLKSZ, pre##_ofbrand, pre##_counterrand },
218 grand
*(*mgf
)(const void */
*k*/
, size_t /*sz*/);
220 #define E(PRE, pre) \
221 { &pre, pre##_mgfkeysz, pre##_mgfrand },
226 /*----- Miscellaneous static data -----------------------------------------*/
229 static size_t outsz
= 0;
230 static unsigned maurer_lo
= 5, maurer_hi
= 8;
235 static unsigned flags
= 0;
246 /*----- Help options ------------------------------------------------------*/
248 static void usage(FILE *fp
)
250 pquis(fp
, "Usage: $ generator [options]\n");
253 static void version(FILE *fp
)
255 pquis(fp
, "$, Catacomb version " VERSION
"\n");
258 static void help(FILE *fp
)
264 Emits a stream of random bytes suitable for, well, all sorts of things.\n\
265 The primary objective is to be able to generate streams of input for\n\
266 statistical tests, such as Diehard.\n\
268 Options are specific to the particular generator, although there's a\n\
271 -h, --help Display this help message.\n\
272 -v, --version Display the program's version number.\n\
273 -u, --usage Display a useless usage message.\n\
275 -l, --list Show a list of the supported generators, with\n\
277 -f, --fipstest Run the FIPS 140-1 randomness test.\n\
278 -m, --maurer[=LO-HI] Run Maurer's universal statistical test.\n\
279 -o, --output FILE Write output to FILE, not stdout.\n\
280 -z, --size SIZE Emit SIZE bytes, not an unlimited number.\n\
281 -p, --progress Show a little progress meter (on stderr).\n\
282 -T, --timer Keep track of the CPU time used by the generator.\n\
283 -d, --discard Discard the generated output.\n\
285 (A SIZE may be followed by `g' for gigabytes, `m' for megabytes, or\n\
286 `k' for kilobytes. If unqualified, an amount in bytes is assumed.)\n\
290 /*----- Main options parser -----------------------------------------------*/
292 static struct option opts
[] = {
294 /* --- Standard GNU help options --- */
296 { "help", 0, 0, 'h' },
297 { "version", 0, 0, 'v' },
298 { "usage", 0, 0, 'u' },
300 /* --- Other useful things --- */
302 { "list", 0, 0, 'l' },
303 { "fipstest", 0, 0, 'f' },
304 { "maurer", OPTF_ARGOPT
, 0, 'm' },
305 { "output", OPTF_ARGREQ
, 0, 'o' },
306 { "size", OPTF_ARGREQ
, 0, 'z' },
307 { "progress", 0, 0, 'p' },
308 { "timer", 0, 0, 'T' },
309 { "discard", 0, 0, 'd' },
311 /* --- End of main table --- */
316 static const char *sopts
= "hvu lfm::o:z:pTd";
319 DA_DECL(option_v
, struct option
);
323 static option_v optv
= DA_INIT
;
324 static dstr optd
= DSTR_INIT
;
326 /* --- @addopts@ --- *
328 * Arguments: @const char *s@ = pointer to short options
329 * @struct option *l@ = pointer to long options
333 * Use: Adds a collection of options to the table.
336 static void addopts(const char *s
, struct option
*l
)
342 DA_PUSH(&optv
, *l
++);
350 * Returns: Next option from argument array.
352 * Use: Fetches options, handling the standard ones.
358 int i
= mdwopt(argc
, argv
, optd
.buf
, DA(&optv
), 0, 0, 0);
371 puts("Generators supported:");
372 for (g
= generators
; g
->name
; g
++)
373 printf(" %s %s\n", g
->name
, g
->help
);
383 unsigned long lo
, hi
;
384 lo
= strtoul(optarg
, &p
, 0);
385 if (*p
== '-' || *p
== ',')
386 hi
= strtoul(p
+ 1, &p
, 0);
389 if (*p
!= 0 || hi
< lo
|| lo
== 0)
390 die(EXIT_FAILURE
, "bad bit range `%s'", optarg
);
397 die(EXIT_FAILURE
, "already set an output file");
398 if (strcmp(optarg
, "-") == 0)
401 outfp
= fopen(optarg
, "w");
403 die(EXIT_FAILURE
, "couldn't open output file `%s': %s",
404 optarg
, strerror(errno
));
411 outsz
= strtoul(optarg
, &p
, 0);
413 die(EXIT_FAILURE
, "bad number `%s'", optarg
);
415 case 'G': case 'g': outsz
*= 1024;
416 case 'M': case 'm': outsz
*= 1024;
417 case 'K': case 'k': outsz
*= 1024;
421 die(EXIT_FAILURE
, "bad suffix `%s'", p
);
425 die(EXIT_FAILURE
, "bad suffix `%s'", p
);
442 /*----- Manglers for seed strings -----------------------------------------*/
446 * Arguments: @const char *p@ = pointer to input string
447 * @char **end@ = where the end goes
448 * @dstr *d@ = output buffer
452 * Use: Transforms a hex string into a chunk of binary data.
455 static void unhex(const char *p
, char **end
, dstr
*d
)
457 while (p
[0] && p
[1]) {
458 int x
= p
[0], y
= p
[1];
459 if ('0' <= x
&& x
<= '9') x
-= '0';
460 else if ('A' <= x
&& x
<= 'F') x
-= 'A' - 10;
461 else if ('a' <= x
&& x
<= 'f') x
-= 'a' - 10;
463 if ('0' <= y
&& y
<= '9') y
-= '0';
464 else if ('A' <= y
&& y
<= 'F') y
-= 'A' - 10;
465 else if ('a' <= y
&& y
<= 'f') y
-= 'a' - 10;
467 DPUTC(d
, (x
<< 4) + y
);
473 /* --- Generate a key --- */
475 static void textkey(dstr
*d
, const char *p
, const octet
*ksz
)
477 size_t sz
= strlen(p
);
480 die(EXIT_FAILURE
, "zero-length key string");
481 if (keysz(sz
, ksz
) != sz
)
485 rmd160_mgfinit(&g
, p
, sz
);
488 rmd160_mgfencrypt(&g
, 0, d
->buf
, sz
);
491 assert(((void)"I can't seem to choose a good key size",
492 keysz(d
->len
, ksz
) == d
->len
));
495 static void hexkey(dstr
*d
, const char *p
, const octet
*ksz
)
498 unhex(optarg
, &q
, d
);
500 die(EXIT_FAILURE
, "bad hex key `%s'", p
);
501 if (keysz(d
->len
, ksz
) != d
->len
)
502 die(EXIT_FAILURE
, "bad key length");
505 static void randkey(dstr
*d
, const octet
*ksz
)
507 size_t sz
= keysz(0, ksz
);
509 rand_get(RAND_GLOBAL
, d
->buf
, sz
);
513 /*----- Generators --------------------------------------------------------*/
515 /* --- Blum-Blum-Shub strong generator --- */
517 static grand
*gen_bbs(unsigned i
)
519 /* --- Default modulus --- *
521 * The factors of this number are
523 * @p = 1229936431484295969649886203367009966370895964206162032259292413@
524 * @7754313537966036459299022912838407755462506416274551744201653277@
525 * @313130311731673973886822067@
527 * @q = 9798171783943489959487301695884963889684294764514008432498259742@
528 * @5374320073594018817245784145742769603334292182227671519041431067@
529 * @61344781426317516045890159@
531 * Both %$p$% and %$q$% are prime; %$(p - 1)/2$% and %$(q - 1)/2$% have no
532 * common factors. They were found using this program, with random
535 * I hope that, by publishing these factors, I'll dissuade people from
536 * actually using this modulus in an attempt to attain real security. The
537 * program is quite quick at finding Blum numbers, so there's no excuse for
538 * not generating your own.
542 "120511284390135742513572142094334711443073194119732569353820828435640527418092392240366088035509890969913081816369160298961490135716255689660470370755013177656905237112577648090277537209936078171554274553448103698084782669252936352843649980105109850503830397166360721262431179505917248447259735253684659338653";
544 /* --- Other things --- */
548 unsigned bits
= 1024;
551 const char *kfile
= 0, *id
= 0, *ktype
= 0;
553 /* --- Parse options --- */
555 static struct option opts
[] = {
556 { "modulus", OPTF_ARGREQ
, 0, 'M' },
557 { "generate", 0, 0, 'g' },
558 { "seed", OPTF_ARGREQ
, 0, 's' },
559 { "bits", OPTF_ARGREQ
, 0, 'b' },
560 { "show", 0, 0, 'S' },
561 { "keyring", OPTF_ARGREQ
, 0, 'k' },
562 { "id", OPTF_ARGREQ
, 0, 'i' },
563 { "type", OPTF_ARGREQ
, 0, 't' },
567 addopts("M:gs:b:Sk:i:t:", opts
);
584 bits
= strtoul(optarg
, 0, 0);
586 die(EXIT_FAILURE
, "bad number of bits `%s'", optarg
);
608 /* --- Generate a modulus if one is requested --- */
612 m
= mp_readstring(MP_NEW
, mt
, &p
, 0);
613 if (!m
|| *p
|| (m
->v
[0] & 3) != 1)
614 die(EXIT_FAILURE
, "bad modulus `%s'", mt
);
615 /* Unfortunately I don't know how to test for a Blum integer */
616 } else if (kfile
|| id
|| ktype
) {
621 /* --- Open the key file --- */
625 if (key_open(&kf
, kfile
, KOPEN_READ
, key_moan
, 0)) {
626 die(EXIT_FAILURE
, "error opening key file `%s': %s",
627 kfile
, strerror(errno
));
630 /* --- Find the key --- */
633 if ((kk
= key_bytag(&kf
, id
)) == 0)
634 die(EXIT_FAILURE
, "key `%s' not found", id
);
638 if ((kk
= key_bytype(&kf
, ktype
)) == 0)
639 die(EXIT_FAILURE
, "no suitable key with type `%s' found", ktype
);
642 /* --- Read the key data --- */
644 if ((kk
->k
.e
& KF_ENCMASK
) != KENC_STRUCT
)
645 die(EXIT_FAILURE
, "key is not structured");
646 if ((kd
= key_structfind(&kk
->k
, "n")) == 0)
647 die(EXIT_FAILURE
, "key has no subkey `n'");
648 if ((kd
->e
& KF_ENCMASK
) != KENC_MP
)
649 die(EXIT_FAILURE
, "incomatible subkey encoding");
650 m
= MP_COPY(kd
->u
.m
);
655 if (bbs_gen(&bp
, bits
, &rand_global
, 0,
656 (flags
& f_progress
) ? pgen_ev
: 0, 0))
657 die(EXIT_FAILURE
, "modulus generation failed");
661 fputs("p = ", stderr
);
662 mp_writefile(bp
.p
, stderr
, 10);
663 fputs("\nq = ", stderr
);
664 mp_writefile(bp
.q
, stderr
, 10);
665 fputs("\nn = ", stderr
);
666 mp_writefile(bp
.n
, stderr
, 10);
674 /* --- Set up a seed --- */
677 x
= mprand(MP_NEW
, mp_bits(m
) - 1, &rand_global
, 1);
680 x
= mp_readstring(MP_NEW
, xt
, &p
, 0);
682 die(EXIT_FAILURE
, "bad modulus `%s'", xt
);
694 /* --- Catacomb's random number generator --- */
696 static grand
*gen_rand(unsigned i
)
698 grand
*r
= rand_create();
701 static struct option opts
[] = {
702 { "key", OPTF_ARGREQ
, 0, 'k' },
703 { "text", OPTF_ARGREQ
, 0, 't' },
704 { "hex", OPTF_ARGREQ
, 0, 'H' },
708 addopts("k:t:H:n", opts
);
710 r
->ops
->misc(r
, RAND_NOISESRC
, &noise_source
);
711 r
->ops
->misc(r
, RAND_SEED
, 160);
720 textkey(&d
, optarg
, rmd160_mackeysz
);
721 r
->ops
->misc(r
, RAND_KEY
, d
.buf
, d
.len
);
724 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, optarg
, strlen(optarg
));
728 hexkey(&d
, optarg
, rmd160_mackeysz
);
729 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, d
.buf
, d
.len
);
738 /* --- RC4 output --- */
740 static grand
*gen_rc4(unsigned i
)
745 static struct option opts
[] = {
746 { "key", OPTF_ARGREQ
, 0, 'k' },
747 { "hex", OPTF_ARGREQ
, 0, 'H' },
751 addopts("k:H:", opts
);
760 textkey(&d
, optarg
, rc4_keysz
);
764 hexkey(&d
, optarg
, rc4_keysz
);
772 randkey(&d
, rc4_keysz
);
773 r
= rc4_rand(d
.buf
, d
.len
);
778 /* --- SEAL output --- */
780 static grand
*gen_seal(unsigned i
)
786 static struct option opts
[] = {
787 { "key", OPTF_ARGREQ
, 0, 'k' },
788 { "hex", OPTF_ARGREQ
, 0, 'H' },
789 { "sequence", OPTF_ARGREQ
, 0, 'n' },
793 addopts("k:H:n:", opts
);
802 textkey(&d
, optarg
, seal_keysz
);
806 hexkey(&d
, optarg
, seal_keysz
);
810 n
= strtoul(optarg
, &p
, 0);
812 die(EXIT_FAILURE
, "bad number `%s'", optarg
);
820 randkey(&d
, seal_keysz
);
821 r
= seal_rand(d
.buf
, d
.len
, n
);
826 /* --- Output feedback generators --- */
828 static grand
*gen_ofb(unsigned i
)
834 static struct option opts
[] = {
835 { "key", OPTF_ARGREQ
, 0, 'k' },
836 { "hex", OPTF_ARGREQ
, 0, 'H' },
837 { "iv", OPTF_ARGREQ
, 0, 'i' },
841 addopts("k:H:i:", opts
);
850 textkey(&d
, optarg
, ciphertab
[i
].keysz
);
854 hexkey(&d
, optarg
, ciphertab
[i
].keysz
);
858 unhex(optarg
, &p
, &iv
);
860 die(EXIT_FAILURE
, "bad hex IV `%s'", optarg
);
868 randkey(&d
, ciphertab
[i
].keysz
);
869 r
= ciphertab
[i
].ofb(d
.buf
, d
.len
);
871 if (iv
.len
!= ciphertab
[i
].blksz
) {
872 die(EXIT_FAILURE
, "bad IV length %lu (must be %lu)",
873 (unsigned long)iv
.len
, (unsigned long)ciphertab
[i
].blksz
);
875 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, iv
.buf
);
883 /* --- Counter generators --- */
885 static grand
*gen_counter(unsigned i
)
891 static struct option opts
[] = {
892 { "key", OPTF_ARGREQ
, 0, 'k' },
893 { "hex", OPTF_ARGREQ
, 0, 'H' },
894 { "iv", OPTF_ARGREQ
, 0, 'i' },
898 addopts("k:H:i:", opts
);
907 textkey(&d
, optarg
, ciphertab
[i
].keysz
);
911 hexkey(&d
, optarg
, ciphertab
[i
].keysz
);
915 unhex(optarg
, &p
, &iv
);
917 die(EXIT_FAILURE
, "bad hex IV `%s'", optarg
);
925 randkey(&d
, ciphertab
[i
].keysz
);
926 r
= ciphertab
[i
].counter(d
.buf
, d
.len
);
928 if (iv
.len
!= ciphertab
[i
].blksz
) {
929 die(EXIT_FAILURE
, "bad IV length %lu (must be %lu)",
930 (unsigned long)iv
.len
, (unsigned long)ciphertab
[i
].blksz
);
932 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, iv
.buf
);
940 /* --- Mask generators --- */
942 static grand
*gen_mgf(unsigned i
)
948 static struct option opts
[] = {
949 { "key", OPTF_ARGREQ
, 0, 'k' },
950 { "hex", OPTF_ARGREQ
, 0, 'H' },
951 { "index", OPTF_ARGREQ
, 0, 'i' },
955 addopts("k:H:i:", opts
);
964 textkey(&d
, optarg
, hashtab
[i
].keysz
);
968 hexkey(&d
, optarg
, hashtab
[i
].keysz
);
972 c
= strtoul(optarg
, &p
, 0);
974 die(EXIT_FAILURE
, "bad index `%s'", optarg
);
982 randkey(&d
, hashtab
[i
].keysz
);
984 r
= hashtab
[i
].mgf(d
.buf
, d
.len
);
986 r
->ops
->misc(r
, GRAND_SEEDUINT32
, c
);
992 /* --- Fibonacci generator --- */
994 static grand
*gen_fib(unsigned i
)
1001 static struct option opts
[] = {
1002 { "seed", OPTF_ARGREQ
, 0, 's' },
1006 addopts("s:", opts
);
1014 s
= strtoul(optarg
, &p
, 0);
1016 die(EXIT_FAILURE
, "bad integer `%s'", optarg
);
1023 r
= fibrand_create(s
);
1025 r
->ops
->misc(r
, GRAND_SEEDRAND
, &rand_global
);
1029 /* --- LC generator --- */
1031 static grand
*gen_lc(unsigned i
)
1037 static struct option opts
[] = {
1038 { "seed", OPTF_ARGREQ
, 0, 's' },
1042 addopts("s:", opts
);
1050 s
= strtoul(optarg
, &p
, 0);
1052 die(EXIT_FAILURE
, "bad integer `%s'", optarg
);
1061 s
= rand_global
.ops
->range(&rand_global
, LCRAND_P
);
1062 while (s
== LCRAND_FIXEDPT
);
1064 return (lcrand_create(s
));
1067 /* --- Basic options parser -- can't generate output --- */
1069 static grand
*gen_opts(unsigned i
)
1076 /*----- Generators table --------------------------------------------------*/
1078 gen generators
[] = {
1079 { "fibonacci", gen_fib
, 0,
1083 #define E(PRE, pre) \
1084 { #pre "-ofb", gen_ofb, CIPHER_##PRE, \
1085 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
1088 #define E(PRE, pre) \
1089 { #pre "-counter", gen_counter, CIPHER_##PRE, \
1090 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
1093 #define E(PRE, pre) \
1094 { #pre "-mgf", gen_mgf, HASH_##PRE, \
1095 "[-k KEY-PHRASE] [-H HEX-KEY] [-i INDEX]" },
1098 { "rc4", gen_rc4
, 0,
1099 "[-k KEY-PHRASE] [-H HEX-KEY]" },
1100 { "seal", gen_seal
, 0,
1101 "[-k KEY-PHRASE] [-H HEX-KEY] [-n SEQ]" },
1102 { "rand", gen_rand
, 0,
1103 "[-n] [-k KEY-PHRASE] [-t TEXT-BLOCK] [-H HEX-BLOCK]" },
1104 { "bbs", gen_bbs
, 0,
1105 "[-gS] [-s SEED] [-M MODULUS] [-b BITS] [-k KEYRING] [-i TAG] [-t TYPE]"
1110 static gen optsg
= { "options", gen_opts
, 0,
1111 "This message shouldn't be printed." };
1113 /*----- Random number generation ------------------------------------------*/
1115 static int genfile(const void *buf
, size_t sz
, void *p
)
1118 if (fwrite(buf
, 1, sz
, fp
) != sz
)
1119 die(EXIT_FAILURE
, "error writing to file: %s", strerror(errno
));
1123 static int genbuf(const void *buf
, size_t sz
, void *p
)
1126 memcpy(*pp
, buf
, sz
);
1131 typedef struct genmaurer_ctx
{
1136 static int genmaurer(const void *buf
, size_t sz
, void *p
)
1138 genmaurer_ctx
*g
= p
;
1141 for (i
= 0; i
< g
->n
; i
++)
1142 maurer_test(&g
->m
[i
], buf
, sz
);
1146 static int generate(grand
*r
, size_t outsz
,
1147 int (*func
)(const void *buf
, size_t sz
, void *p
),
1150 static char kmg
[] = { ' ', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 0 };
1152 unsigned percent
= 0;
1155 static char baton
[] = "-\\|/";
1160 /* --- Spit out random data --- */
1164 if (flags
& f_progress
) {
1165 char *errbuf
= xmalloc(BUFSIZ
);
1166 setvbuf(stderr
, errbuf
, _IOLBF
, BUFSIZ
);
1168 fprintf(stderr
, "[%*s] 0%% 0\r[/\b", 50, "");
1170 fputs("[ ] 0\r[/\b", stderr
);
1175 signal(SIGPIPE
, SIG_IGN
);
1180 size_t sz
= sizeof(buf
);
1181 clock_t c_start
, c_stop
;
1183 /* --- Emit a bufferful (or less) of data --- */
1186 if (sz
> outsz
- kb
)
1190 r
->ops
->fill(r
, buf
, sz
);
1192 clk
+= c_stop
- c_start
;
1193 if (func
&& (rc
= func(buf
, sz
, p
)) != 0)
1197 /* --- Update the display --- */
1199 if (flags
& f_progress
) {
1207 if (difftime(t
, last
) > 1.0) {
1211 fputs(" ] ", stderr
);
1213 unsigned pc
= kb
* 100.0 / outsz
;
1214 if (pc
> percent
|| percent
> 100 || difftime(t
, last
) > 1.0) {
1218 for (; percent
< (pc
& ~1); percent
+= 2)
1221 for (; pc
< 100; pc
+= 2)
1223 fprintf(stderr
, "] %3i%% ", percent
);
1231 while (q
> 8192 && kk
[1]) {
1235 fprintf(stderr
, "%4i%c\r[", q
, *kk
);
1238 for (pc
= 0; pc
< (percent
& ~1); pc
+= 2)
1247 if (percent
< 100) {
1248 putc(*bp
++, stderr
);
1256 /* --- Terminate the loop --- */
1258 } while (!outsz
|| kb
< outsz
);
1260 if (flags
& f_progress
)
1261 fputc('\n', stderr
);
1262 if (flags
& f_timer
) {
1263 fprintf(stderr
, "generated %lu bytes ", (unsigned long)outsz
);
1265 fputs("too quickly to measure\n", stderr
);
1268 double sec
= (double)clk
/CLOCKS_PER_SEC
;
1269 double bps
= (outsz
<< 3)/sec
;
1270 for (kk
= kmg
; bps
> 1024 && kk
[1]; kk
++, bps
/= 1024)
1272 fprintf(stderr
, "in %g secs (%g %cb/s)\n", sec
, bps
, *kk
);
1278 /*----- Main code ---------------------------------------------------------*/
1280 int main(int ac
, char *av
[])
1285 /* --- Initialize mLib --- */
1290 /* --- Set up the main Catacomb generator --- */
1292 rand_noisesrc(RAND_GLOBAL
, &noise_source
);
1293 rand_seed(RAND_GLOBAL
, 160);
1295 /* --- Initialize the options table --- */
1297 addopts(sopts
, opts
);
1302 /* --- Read the generator out of the first argument --- */
1304 if (argc
> 1 && *argv
[1] != '-') {
1305 const char *arg
= av
[1];
1306 size_t sz
= strlen(arg
);
1310 for (gg
= generators
; gg
->name
; gg
++) {
1311 if (strncmp(arg
, gg
->name
, sz
) == 0) {
1312 if (gg
->name
[sz
] == 0) {
1316 die(EXIT_FAILURE
, "ambiguous generator name `%s'", arg
);
1322 die(EXIT_FAILURE
, "unknown generator name `%s'", arg
);
1327 /* --- Get a generic random number generator --- */
1330 if (!r
|| optind
!= ac
- 1) {
1335 /* --- Do the FIPS test --- */
1337 if (flags
& f_fips
) {
1338 octet buf
[FIPSTEST_BUFSZ
];
1342 generate(r
, sizeof(buf
), genbuf
, &p
);
1344 if (rc
& FIPSTEST_MONOBIT
)
1345 moan("failed monobit test");
1346 if (rc
& FIPSTEST_POKER
)
1347 moan("failed poker test");
1348 if (rc
& FIPSTEST_RUNS
)
1349 moan("failed runs test");
1350 if (rc
& FIPSTEST_LONGRUNS
)
1351 moan("failed long runs test");
1352 if (!rc
&& (flags
& f_progress
))
1353 fputs("test passed\n", stderr
);
1354 return (rc ? EXIT_FAILURE
: 0);
1357 /* --- Do Maurer's test --- */
1359 if (flags
& f_maurer
) {
1365 static struct { double x
; const char *sig
; } sigtab
[] = {
1373 g
.n
= maurer_hi
- maurer_lo
+ 1;
1374 g
.m
= xmalloc(g
.n
* sizeof(maurer_ctx
));
1375 for (i
= 0; i
< g
.n
; i
++)
1376 maurer_init(&g
.m
[i
], i
+ maurer_lo
);
1377 bufsz
= (100 * maurer_hi
) << maurer_hi
;
1379 generate(r
, bufsz
, genmaurer
, &g
);
1381 for (i
= maurer_lo
; i
<= maurer_hi
; i
++) {
1382 double z
= maurer_done(&g
.m
[i
- maurer_lo
]);
1383 double zz
= fabs(z
);
1386 for (j
= 0; sigtab
[j
].sig
; j
++) {
1387 if (zz
> sigtab
[j
].x
) {
1389 moan("failed, bits = %u, sig = %s, Z_u = %g",
1390 i
, sigtab
[j
].sig
, z
);
1394 if (flags
& f_progress
)
1395 fprintf(stderr
, "bits = %u, Z_u = %g\n", i
, z
);
1402 /* --- Discard --- */
1404 if (flags
& f_discard
) {
1405 generate(r
, outsz
, 0, 0);
1409 /* --- Write to a file --- */
1412 if (!(flags
& f_file
) && isatty(STDOUT_FILENO
))
1413 die(EXIT_FAILURE
, "writing output to a terminal is a bad idea");
1416 generate(r
, outsz
, genfile
, outfp
);
1424 /*----- That's all, folks -------------------------------------------------*/