3 * $Id: rspit.c,v 1.16 2001/04/29 18:11: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.16 2001/04/29 18:11:32 mdw
36 * Revision 1.15 2001/04/19 18:26:13 mdw
37 * Use the new MAC keysize names.
39 * Revision 1.14 2001/02/21 20:03:22 mdw
40 * Added support for MD2 hash function.
42 * Revision 1.13 2000/12/06 20:33:27 mdw
43 * Make flags be macros rather than enumerations, to ensure that they're
46 * Revision 1.12 2000/10/08 15:49:18 mdw
47 * Remove failed kludge for shutting up a warning.
49 * Revision 1.11 2000/10/08 12:10:32 mdw
50 * Make table have external linkage to bodge around deficiency in C. The
51 * problem is that @static gen generators[];@ is considered to be a
52 * `tentative definition', and therefore mustn't have incomplete type,
53 * which it obviously has.
55 * Revision 1.10 2000/08/11 21:34:59 mdw
56 * New restartable interface to Maurer testing.
58 * Revision 1.9 2000/08/04 23:24:15 mdw
59 * Add a timer and a discard option.
61 * Revision 1.8 2000/07/29 22:05:47 mdw
62 * Fix error in help message about Maurer test syntax.
64 * Revision 1.7 2000/07/18 23:01:26 mdw
65 * Improve progress indications, and allow user to choose chunk sizes for
68 * Revision 1.6 2000/07/15 20:53:35 mdw
69 * Add a load of new ciphers and hashes.
71 * Revision 1.5 2000/07/01 11:27:03 mdw
72 * Portability fix: don't assume that `stdout' is a constant expression.
73 * Remove old type name `bbs_param'.
75 * Revision 1.4 2000/06/17 12:08:28 mdw
76 * Restructure handling of cipher-based generators. Add counter-mode
77 * ciphers and MGF-1 hash functions. Add FIPS 140-1 and Maurer's tests.
79 * Revision 1.3 2000/02/12 18:21:03 mdw
80 * Overhaul of key management (again).
82 * Revision 1.2 1999/12/22 15:59:51 mdw
83 * New prime-search system. Read BBS keys from key files.
85 * Revision 1.1 1999/12/10 23:29:13 mdw
86 * Emit random numbers for statistical tests.
90 /*----- Header files ------------------------------------------------------*/
107 #include <mLib/darray.h>
108 #include <mLib/dstr.h>
109 #include <mLib/mdwopt.h>
110 #include <mLib/quis.h>
111 #include <mLib/report.h>
112 #include <mLib/sub.h>
114 #include "fipstest.h"
131 #include "des3-ofb.h"
134 #include "mars-ofb.h"
135 #include "skipjack-ofb.h"
137 #include "xtea-ofb.h"
138 #include "blowfish-ofb.h"
139 #include "twofish-ofb.h"
140 #include "idea-ofb.h"
141 #include "cast128-ofb.h"
142 #include "cast256-ofb.h"
143 #include "rijndael-ofb.h"
144 #include "safer-ofb.h"
145 #include "safersk-ofb.h"
146 #include "square-ofb.h"
147 #include "serpent-ofb.h"
149 #include "des-counter.h"
150 #include "des3-counter.h"
151 #include "rc2-counter.h"
152 #include "rc5-counter.h"
153 #include "mars-counter.h"
154 #include "skipjack-counter.h"
155 #include "tea-counter.h"
156 #include "xtea-counter.h"
157 #include "blowfish-counter.h"
158 #include "twofish-counter.h"
159 #include "idea-counter.h"
160 #include "cast128-counter.h"
161 #include "cast256-counter.h"
162 #include "rijndael-counter.h"
163 #include "safer-counter.h"
164 #include "safersk-counter.h"
165 #include "square-counter.h"
166 #include "serpent-counter.h"
172 #include "tiger-mgf.h"
173 #include "rmd128-mgf.h"
174 #include "rmd160-mgf.h"
175 #include "rmd256-mgf.h"
176 #include "rmd320-mgf.h"
180 /*----- Data structures ---------------------------------------------------*/
184 grand
*(*seed
)(unsigned /*i*/);
189 extern gen generators
[];
197 E(SKIPJACK, skipjack) \
200 E(BLOWFISH, blowfish) \
201 E(TWOFISH, twofish) \
203 E(CAST128, cast128) \
204 E(CAST256, cast256) \
207 E(SAFERSK, safersk) \
208 E(RIJNDAEL, rijndael) \
222 #define E(PRE, pre) CIPHER_##PRE,
223 enum { CIPHERS CIPHER__bogus
};
226 #define E(PRE, pre) HASH_##PRE,
227 enum { HASHES HASH__bogus
};
233 grand
*(*ofb
)(const void */
*k*/
, size_t /*sz*/);
234 grand
*(*counter
)(const void */
*k*/
, size_t /*sz*/);
236 #define E(PRE, pre) \
237 { pre##_keysz, PRE##_BLKSZ, pre##_ofbrand, pre##_counterrand },
245 grand
*(*mgf
)(const void */
*k*/
, size_t /*sz*/);
247 #define E(PRE, pre) \
248 { &pre, pre##_mgfkeysz, pre##_mgfrand },
253 /*----- Miscellaneous static data -----------------------------------------*/
256 static size_t outsz
= 0;
257 static unsigned maurer_lo
= 5, maurer_hi
= 8;
262 static unsigned flags
= 0;
264 #define f_progress 1u
269 #define f_discard 32u
271 /*----- Help options ------------------------------------------------------*/
273 static void usage(FILE *fp
)
275 pquis(fp
, "Usage: $ generator [options]\n");
278 static void version(FILE *fp
)
280 pquis(fp
, "$, Catacomb version " VERSION
"\n");
283 static void help(FILE *fp
)
289 Emits a stream of random bytes suitable for, well, all sorts of things.\n\
290 The primary objective is to be able to generate streams of input for\n\
291 statistical tests, such as Diehard.\n\
293 Options are specific to the particular generator, although there's a\n\
296 -h, --help Display this help message.\n\
297 -v, --version Display the program's version number.\n\
298 -u, --usage Display a useless usage message.\n\
300 -l, --list Show a list of the supported generators, with\n\
302 -f, --fipstest Run the FIPS 140-1 randomness test.\n\
303 -m, --maurer[=LO-HI] Run Maurer's universal statistical test.\n\
304 -o, --output FILE Write output to FILE, not stdout.\n\
305 -z, --size SIZE Emit SIZE bytes, not an unlimited number.\n\
306 -p, --progress Show a little progress meter (on stderr).\n\
307 -T, --timer Keep track of the CPU time used by the generator.\n\
308 -d, --discard Discard the generated output.\n\
310 (A SIZE may be followed by `g' for gigabytes, `m' for megabytes, or\n\
311 `k' for kilobytes. If unqualified, an amount in bytes is assumed.)\n\
315 /*----- Main options parser -----------------------------------------------*/
317 static struct option opts
[] = {
319 /* --- Standard GNU help options --- */
321 { "help", 0, 0, 'h' },
322 { "version", 0, 0, 'v' },
323 { "usage", 0, 0, 'u' },
325 /* --- Other useful things --- */
327 { "list", 0, 0, 'l' },
328 { "fipstest", 0, 0, 'f' },
329 { "maurer", OPTF_ARGOPT
, 0, 'm' },
330 { "output", OPTF_ARGREQ
, 0, 'o' },
331 { "size", OPTF_ARGREQ
, 0, 'z' },
332 { "progress", 0, 0, 'p' },
333 { "timer", 0, 0, 'T' },
334 { "discard", 0, 0, 'd' },
336 /* --- End of main table --- */
341 static const char *sopts
= "hvu lfm::o:z:pTd";
344 DA_DECL(option_v
, struct option
);
348 static option_v optv
= DA_INIT
;
349 static dstr optd
= DSTR_INIT
;
351 /* --- @addopts@ --- *
353 * Arguments: @const char *s@ = pointer to short options
354 * @struct option *l@ = pointer to long options
358 * Use: Adds a collection of options to the table.
361 static void addopts(const char *s
, struct option
*l
)
367 DA_PUSH(&optv
, *l
++);
375 * Returns: Next option from argument array.
377 * Use: Fetches options, handling the standard ones.
383 int i
= mdwopt(argc
, argv
, optd
.buf
, DA(&optv
), 0, 0, 0);
396 puts("Generators supported:");
397 for (g
= generators
; g
->name
; g
++)
398 printf(" %s %s\n", g
->name
, g
->help
);
408 unsigned long lo
, hi
;
409 lo
= strtoul(optarg
, &p
, 0);
410 if (*p
== '-' || *p
== ',')
411 hi
= strtoul(p
+ 1, &p
, 0);
414 if (*p
!= 0 || hi
< lo
|| lo
== 0)
415 die(EXIT_FAILURE
, "bad bit range `%s'", optarg
);
422 die(EXIT_FAILURE
, "already set an output file");
423 if (strcmp(optarg
, "-") == 0)
426 outfp
= fopen(optarg
, "w");
428 die(EXIT_FAILURE
, "couldn't open output file `%s': %s",
429 optarg
, strerror(errno
));
436 outsz
= strtoul(optarg
, &p
, 0);
438 die(EXIT_FAILURE
, "bad number `%s'", optarg
);
440 case 'G': case 'g': outsz
*= 1024;
441 case 'M': case 'm': outsz
*= 1024;
442 case 'K': case 'k': outsz
*= 1024;
446 die(EXIT_FAILURE
, "bad suffix `%s'", p
);
450 die(EXIT_FAILURE
, "bad suffix `%s'", p
);
467 /*----- Manglers for seed strings -----------------------------------------*/
471 * Arguments: @const char *p@ = pointer to input string
472 * @char **end@ = where the end goes
473 * @dstr *d@ = output buffer
477 * Use: Transforms a hex string into a chunk of binary data.
480 static void unhex(const char *p
, char **end
, dstr
*d
)
482 while (p
[0] && p
[1]) {
483 int x
= p
[0], y
= p
[1];
484 if ('0' <= x
&& x
<= '9') x
-= '0';
485 else if ('A' <= x
&& x
<= 'F') x
-= 'A' - 10;
486 else if ('a' <= x
&& x
<= 'f') x
-= 'a' - 10;
488 if ('0' <= y
&& y
<= '9') y
-= '0';
489 else if ('A' <= y
&& y
<= 'F') y
-= 'A' - 10;
490 else if ('a' <= y
&& y
<= 'f') y
-= 'a' - 10;
492 DPUTC(d
, (x
<< 4) + y
);
498 /* --- Generate a key --- */
500 static void textkey(dstr
*d
, const char *p
, const octet
*ksz
)
502 size_t sz
= strlen(p
);
505 die(EXIT_FAILURE
, "zero-length key string");
506 if (keysz(sz
, ksz
) != sz
)
510 rmd160_mgfinit(&g
, p
, sz
);
513 rmd160_mgfencrypt(&g
, 0, d
->buf
, sz
);
516 assert(((void)"I can't seem to choose a good key size",
517 keysz(d
->len
, ksz
) == d
->len
));
520 static void hexkey(dstr
*d
, const char *p
, const octet
*ksz
)
523 unhex(optarg
, &q
, d
);
525 die(EXIT_FAILURE
, "bad hex key `%s'", p
);
526 if (keysz(d
->len
, ksz
) != d
->len
)
527 die(EXIT_FAILURE
, "bad key length");
530 static void randkey(dstr
*d
, const octet
*ksz
)
532 size_t sz
= keysz(0, ksz
);
534 rand_get(RAND_GLOBAL
, d
->buf
, sz
);
538 /*----- Generators --------------------------------------------------------*/
540 /* --- Blum-Blum-Shub strong generator --- */
542 static grand
*gen_bbs(unsigned i
)
544 /* --- Default modulus --- *
546 * The factors of this number are
548 * @p = 1229936431484295969649886203367009966370895964206162032259292413@
549 * @7754313537966036459299022912838407755462506416274551744201653277@
550 * @313130311731673973886822067@
552 * @q = 9798171783943489959487301695884963889684294764514008432498259742@
553 * @5374320073594018817245784145742769603334292182227671519041431067@
554 * @61344781426317516045890159@
556 * Both %$p$% and %$q$% are prime; %$(p - 1)/2$% and %$(q - 1)/2$% have no
557 * common factors. They were found using this program, with random
560 * I hope that, by publishing these factors, I'll dissuade people from
561 * actually using this modulus in an attempt to attain real security. The
562 * program is quite quick at finding Blum numbers, so there's no excuse for
563 * not generating your own.
567 "120511284390135742513572142094334711443073194119732569353820828435640527418092392240366088035509890969913081816369160298961490135716255689660470370755013177656905237112577648090277537209936078171554274553448103698084782669252936352843649980105109850503830397166360721262431179505917248447259735253684659338653";
569 /* --- Other things --- */
573 unsigned bits
= 1024;
576 const char *kfile
= 0, *id
= 0, *ktype
= 0;
578 /* --- Parse options --- */
580 static struct option opts
[] = {
581 { "modulus", OPTF_ARGREQ
, 0, 'M' },
582 { "generate", 0, 0, 'g' },
583 { "seed", OPTF_ARGREQ
, 0, 's' },
584 { "bits", OPTF_ARGREQ
, 0, 'b' },
585 { "show", 0, 0, 'S' },
586 { "keyring", OPTF_ARGREQ
, 0, 'k' },
587 { "id", OPTF_ARGREQ
, 0, 'i' },
588 { "type", OPTF_ARGREQ
, 0, 't' },
592 addopts("M:gs:b:Sk:i:t:", opts
);
609 bits
= strtoul(optarg
, 0, 0);
611 die(EXIT_FAILURE
, "bad number of bits `%s'", optarg
);
633 /* --- Generate a modulus if one is requested --- */
637 m
= mp_readstring(MP_NEW
, mt
, &p
, 0);
638 if (!m
|| *p
|| (m
->v
[0] & 3) != 1)
639 die(EXIT_FAILURE
, "bad modulus `%s'", mt
);
640 /* Unfortunately I don't know how to test for a Blum integer */
641 } else if (kfile
|| id
|| ktype
) {
646 /* --- Open the key file --- */
650 if (key_open(&kf
, kfile
, KOPEN_READ
, key_moan
, 0)) {
651 die(EXIT_FAILURE
, "error opening key file `%s': %s",
652 kfile
, strerror(errno
));
655 /* --- Find the key --- */
658 if ((kk
= key_bytag(&kf
, id
)) == 0)
659 die(EXIT_FAILURE
, "key `%s' not found", id
);
663 if ((kk
= key_bytype(&kf
, ktype
)) == 0)
664 die(EXIT_FAILURE
, "no suitable key with type `%s' found", ktype
);
667 /* --- Read the key data --- */
669 if ((kk
->k
.e
& KF_ENCMASK
) != KENC_STRUCT
)
670 die(EXIT_FAILURE
, "key is not structured");
671 if ((kd
= key_structfind(&kk
->k
, "n")) == 0)
672 die(EXIT_FAILURE
, "key has no subkey `n'");
673 if ((kd
->e
& KF_ENCMASK
) != KENC_MP
)
674 die(EXIT_FAILURE
, "incomatible subkey encoding");
675 m
= MP_COPY(kd
->u
.m
);
680 if (bbs_gen(&bp
, bits
, &rand_global
, 0,
681 (flags
& f_progress
) ? pgen_ev
: 0, 0))
682 die(EXIT_FAILURE
, "modulus generation failed");
686 fputs("p = ", stderr
);
687 mp_writefile(bp
.p
, stderr
, 10);
688 fputs("\nq = ", stderr
);
689 mp_writefile(bp
.q
, stderr
, 10);
690 fputs("\nn = ", stderr
);
691 mp_writefile(bp
.n
, stderr
, 10);
699 /* --- Set up a seed --- */
702 x
= mprand(MP_NEW
, mp_bits(m
) - 1, &rand_global
, 1);
705 x
= mp_readstring(MP_NEW
, xt
, &p
, 0);
707 die(EXIT_FAILURE
, "bad modulus `%s'", xt
);
719 /* --- Catacomb's random number generator --- */
721 static grand
*gen_rand(unsigned i
)
723 grand
*r
= rand_create();
726 static struct option opts
[] = {
727 { "key", OPTF_ARGREQ
, 0, 'k' },
728 { "text", OPTF_ARGREQ
, 0, 't' },
729 { "hex", OPTF_ARGREQ
, 0, 'H' },
733 addopts("k:t:H:n", opts
);
735 r
->ops
->misc(r
, RAND_NOISESRC
, &noise_source
);
736 r
->ops
->misc(r
, RAND_SEED
, 160);
745 textkey(&d
, optarg
, rmd160_hmackeysz
);
746 r
->ops
->misc(r
, RAND_KEY
, d
.buf
, d
.len
);
749 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, optarg
, strlen(optarg
));
753 hexkey(&d
, optarg
, rmd160_hmackeysz
);
754 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, d
.buf
, d
.len
);
763 /* --- RC4 output --- */
765 static grand
*gen_rc4(unsigned i
)
770 static struct option opts
[] = {
771 { "key", OPTF_ARGREQ
, 0, 'k' },
772 { "hex", OPTF_ARGREQ
, 0, 'H' },
776 addopts("k:H:", opts
);
785 textkey(&d
, optarg
, rc4_keysz
);
789 hexkey(&d
, optarg
, rc4_keysz
);
797 randkey(&d
, rc4_keysz
);
798 r
= rc4_rand(d
.buf
, d
.len
);
803 /* --- SEAL output --- */
805 static grand
*gen_seal(unsigned i
)
811 static struct option opts
[] = {
812 { "key", OPTF_ARGREQ
, 0, 'k' },
813 { "hex", OPTF_ARGREQ
, 0, 'H' },
814 { "sequence", OPTF_ARGREQ
, 0, 'n' },
818 addopts("k:H:n:", opts
);
827 textkey(&d
, optarg
, seal_keysz
);
831 hexkey(&d
, optarg
, seal_keysz
);
835 n
= strtoul(optarg
, &p
, 0);
837 die(EXIT_FAILURE
, "bad number `%s'", optarg
);
845 randkey(&d
, seal_keysz
);
846 r
= seal_rand(d
.buf
, d
.len
, n
);
851 /* --- Output feedback generators --- */
853 static grand
*gen_ofb(unsigned i
)
859 static struct option opts
[] = {
860 { "key", OPTF_ARGREQ
, 0, 'k' },
861 { "hex", OPTF_ARGREQ
, 0, 'H' },
862 { "iv", OPTF_ARGREQ
, 0, 'i' },
866 addopts("k:H:i:", opts
);
875 textkey(&d
, optarg
, ciphertab
[i
].keysz
);
879 hexkey(&d
, optarg
, ciphertab
[i
].keysz
);
883 unhex(optarg
, &p
, &iv
);
885 die(EXIT_FAILURE
, "bad hex IV `%s'", optarg
);
893 randkey(&d
, ciphertab
[i
].keysz
);
894 r
= ciphertab
[i
].ofb(d
.buf
, d
.len
);
896 if (iv
.len
!= ciphertab
[i
].blksz
) {
897 die(EXIT_FAILURE
, "bad IV length %lu (must be %lu)",
898 (unsigned long)iv
.len
, (unsigned long)ciphertab
[i
].blksz
);
900 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, iv
.buf
);
908 /* --- Counter generators --- */
910 static grand
*gen_counter(unsigned i
)
916 static struct option opts
[] = {
917 { "key", OPTF_ARGREQ
, 0, 'k' },
918 { "hex", OPTF_ARGREQ
, 0, 'H' },
919 { "iv", OPTF_ARGREQ
, 0, 'i' },
923 addopts("k:H:i:", opts
);
932 textkey(&d
, optarg
, ciphertab
[i
].keysz
);
936 hexkey(&d
, optarg
, ciphertab
[i
].keysz
);
940 unhex(optarg
, &p
, &iv
);
942 die(EXIT_FAILURE
, "bad hex IV `%s'", optarg
);
950 randkey(&d
, ciphertab
[i
].keysz
);
951 r
= ciphertab
[i
].counter(d
.buf
, d
.len
);
953 if (iv
.len
!= ciphertab
[i
].blksz
) {
954 die(EXIT_FAILURE
, "bad IV length %lu (must be %lu)",
955 (unsigned long)iv
.len
, (unsigned long)ciphertab
[i
].blksz
);
957 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, iv
.buf
);
965 /* --- Mask generators --- */
967 static grand
*gen_mgf(unsigned i
)
973 static struct option opts
[] = {
974 { "key", OPTF_ARGREQ
, 0, 'k' },
975 { "hex", OPTF_ARGREQ
, 0, 'H' },
976 { "index", OPTF_ARGREQ
, 0, 'i' },
980 addopts("k:H:i:", opts
);
989 textkey(&d
, optarg
, hashtab
[i
].keysz
);
993 hexkey(&d
, optarg
, hashtab
[i
].keysz
);
997 c
= strtoul(optarg
, &p
, 0);
999 die(EXIT_FAILURE
, "bad index `%s'", optarg
);
1007 randkey(&d
, hashtab
[i
].keysz
);
1009 r
= hashtab
[i
].mgf(d
.buf
, d
.len
);
1011 r
->ops
->misc(r
, GRAND_SEEDUINT32
, c
);
1017 /* --- Fibonacci generator --- */
1019 static grand
*gen_fib(unsigned i
)
1026 static struct option opts
[] = {
1027 { "seed", OPTF_ARGREQ
, 0, 's' },
1031 addopts("s:", opts
);
1039 s
= strtoul(optarg
, &p
, 0);
1041 die(EXIT_FAILURE
, "bad integer `%s'", optarg
);
1048 r
= fibrand_create(s
);
1050 r
->ops
->misc(r
, GRAND_SEEDRAND
, &rand_global
);
1054 /* --- LC generator --- */
1056 static grand
*gen_lc(unsigned i
)
1062 static struct option opts
[] = {
1063 { "seed", OPTF_ARGREQ
, 0, 's' },
1067 addopts("s:", opts
);
1075 s
= strtoul(optarg
, &p
, 0);
1077 die(EXIT_FAILURE
, "bad integer `%s'", optarg
);
1086 s
= rand_global
.ops
->range(&rand_global
, LCRAND_P
);
1087 while (s
== LCRAND_FIXEDPT
);
1089 return (lcrand_create(s
));
1092 /* --- Basic options parser -- can't generate output --- */
1094 static grand
*gen_opts(unsigned i
)
1101 /*----- Generators table --------------------------------------------------*/
1103 gen generators
[] = {
1104 { "fibonacci", gen_fib
, 0,
1108 #define E(PRE, pre) \
1109 { #pre "-ofb", gen_ofb, CIPHER_##PRE, \
1110 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
1113 #define E(PRE, pre) \
1114 { #pre "-counter", gen_counter, CIPHER_##PRE, \
1115 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
1118 #define E(PRE, pre) \
1119 { #pre "-mgf", gen_mgf, HASH_##PRE, \
1120 "[-k KEY-PHRASE] [-H HEX-KEY] [-i INDEX]" },
1123 { "rc4", gen_rc4
, 0,
1124 "[-k KEY-PHRASE] [-H HEX-KEY]" },
1125 { "seal", gen_seal
, 0,
1126 "[-k KEY-PHRASE] [-H HEX-KEY] [-n SEQ]" },
1127 { "rand", gen_rand
, 0,
1128 "[-n] [-k KEY-PHRASE] [-t TEXT-BLOCK] [-H HEX-BLOCK]" },
1129 { "bbs", gen_bbs
, 0,
1130 "[-gS] [-s SEED] [-M MODULUS] [-b BITS] [-k KEYRING] [-i TAG] [-t TYPE]"
1135 static gen optsg
= { "options", gen_opts
, 0,
1136 "This message shouldn't be printed." };
1138 /*----- Random number generation ------------------------------------------*/
1140 static int genfile(const void *buf
, size_t sz
, void *p
)
1143 if (fwrite(buf
, 1, sz
, fp
) != sz
)
1144 die(EXIT_FAILURE
, "error writing to file: %s", strerror(errno
));
1148 static int genbuf(const void *buf
, size_t sz
, void *p
)
1151 memcpy(*pp
, buf
, sz
);
1156 typedef struct genmaurer_ctx
{
1161 static int genmaurer(const void *buf
, size_t sz
, void *p
)
1163 genmaurer_ctx
*g
= p
;
1166 for (i
= 0; i
< g
->n
; i
++)
1167 maurer_test(&g
->m
[i
], buf
, sz
);
1171 static int generate(grand
*r
, size_t outsz
,
1172 int (*func
)(const void *buf
, size_t sz
, void *p
),
1175 static char kmg
[] = { ' ', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 0 };
1177 unsigned percent
= 0;
1180 static char baton
[] = "-\\|/";
1185 /* --- Spit out random data --- */
1189 if (flags
& f_progress
) {
1190 char *errbuf
= xmalloc(BUFSIZ
);
1191 setvbuf(stderr
, errbuf
, _IOLBF
, BUFSIZ
);
1193 fprintf(stderr
, "[%*s] 0%% 0\r[/\b", 50, "");
1195 fputs("[ ] 0\r[/\b", stderr
);
1200 signal(SIGPIPE
, SIG_IGN
);
1205 size_t sz
= sizeof(buf
);
1206 clock_t c_start
, c_stop
;
1208 /* --- Emit a bufferful (or less) of data --- */
1211 if (sz
> outsz
- kb
)
1215 r
->ops
->fill(r
, buf
, sz
);
1217 clk
+= c_stop
- c_start
;
1218 if (func
&& (rc
= func(buf
, sz
, p
)) != 0)
1222 /* --- Update the display --- */
1224 if (flags
& f_progress
) {
1232 if (difftime(t
, last
) > 1.0) {
1236 fputs(" ] ", stderr
);
1238 unsigned pc
= kb
* 100.0 / outsz
;
1239 if (pc
> percent
|| percent
> 100 || difftime(t
, last
) > 1.0) {
1243 for (; percent
< (pc
& ~1); percent
+= 2)
1246 for (; pc
< 100; pc
+= 2)
1248 fprintf(stderr
, "] %3i%% ", percent
);
1256 while (q
> 8192 && kk
[1]) {
1260 fprintf(stderr
, "%4i%c\r[", q
, *kk
);
1263 for (pc
= 0; pc
< (percent
& ~1); pc
+= 2)
1272 if (percent
< 100) {
1273 putc(*bp
++, stderr
);
1281 /* --- Terminate the loop --- */
1283 } while (!outsz
|| kb
< outsz
);
1285 if (flags
& f_progress
)
1286 fputc('\n', stderr
);
1287 if (flags
& f_timer
) {
1288 fprintf(stderr
, "generated %lu bytes ", (unsigned long)outsz
);
1290 fputs("too quickly to measure\n", stderr
);
1293 double sec
= (double)clk
/CLOCKS_PER_SEC
;
1294 double bps
= (outsz
<< 3)/sec
;
1295 for (kk
= kmg
; bps
> 1024 && kk
[1]; kk
++, bps
/= 1024)
1297 fprintf(stderr
, "in %g secs (%g %cb/s)\n", sec
, bps
, *kk
);
1303 /*----- Main code ---------------------------------------------------------*/
1305 int main(int ac
, char *av
[])
1310 /* --- Initialize mLib --- */
1315 /* --- Set up the main Catacomb generator --- */
1317 rand_noisesrc(RAND_GLOBAL
, &noise_source
);
1318 rand_seed(RAND_GLOBAL
, 160);
1320 /* --- Initialize the options table --- */
1322 addopts(sopts
, opts
);
1327 /* --- Read the generator out of the first argument --- */
1329 if (argc
> 1 && *argv
[1] != '-') {
1330 const char *arg
= av
[1];
1331 size_t sz
= strlen(arg
);
1335 for (gg
= generators
; gg
->name
; gg
++) {
1336 if (strncmp(arg
, gg
->name
, sz
) == 0) {
1337 if (gg
->name
[sz
] == 0) {
1341 die(EXIT_FAILURE
, "ambiguous generator name `%s'", arg
);
1347 die(EXIT_FAILURE
, "unknown generator name `%s'", arg
);
1352 /* --- Get a generic random number generator --- */
1355 if (!r
|| optind
!= ac
- 1) {
1360 /* --- Do the FIPS test --- */
1362 if (flags
& f_fips
) {
1363 octet buf
[FIPSTEST_BUFSZ
];
1367 generate(r
, sizeof(buf
), genbuf
, &p
);
1369 if (rc
& FIPSTEST_MONOBIT
)
1370 moan("failed monobit test");
1371 if (rc
& FIPSTEST_POKER
)
1372 moan("failed poker test");
1373 if (rc
& FIPSTEST_RUNS
)
1374 moan("failed runs test");
1375 if (rc
& FIPSTEST_LONGRUNS
)
1376 moan("failed long runs test");
1377 if (!rc
&& (flags
& f_progress
))
1378 fputs("test passed\n", stderr
);
1379 return (rc ? EXIT_FAILURE
: 0);
1382 /* --- Do Maurer's test --- */
1384 if (flags
& f_maurer
) {
1390 static struct { double x
; const char *sig
; } sigtab
[] = {
1398 g
.n
= maurer_hi
- maurer_lo
+ 1;
1399 g
.m
= xmalloc(g
.n
* sizeof(maurer_ctx
));
1400 for (i
= 0; i
< g
.n
; i
++)
1401 maurer_init(&g
.m
[i
], i
+ maurer_lo
);
1402 bufsz
= (100 * maurer_hi
) << maurer_hi
;
1404 generate(r
, bufsz
, genmaurer
, &g
);
1406 for (i
= maurer_lo
; i
<= maurer_hi
; i
++) {
1407 double z
= maurer_done(&g
.m
[i
- maurer_lo
]);
1408 double zz
= fabs(z
);
1411 for (j
= 0; sigtab
[j
].sig
; j
++) {
1412 if (zz
> sigtab
[j
].x
) {
1414 moan("failed, bits = %u, sig = %s, Z_u = %g",
1415 i
, sigtab
[j
].sig
, z
);
1419 if (flags
& f_progress
)
1420 fprintf(stderr
, "bits = %u, Z_u = %g\n", i
, z
);
1427 /* --- Discard --- */
1429 if (flags
& f_discard
) {
1430 generate(r
, outsz
, 0, 0);
1434 /* --- Write to a file --- */
1437 if (!(flags
& f_file
) && isatty(STDOUT_FILENO
))
1438 die(EXIT_FAILURE
, "writing output to a terminal is a bad idea");
1441 generate(r
, outsz
, genfile
, outfp
);
1449 /*----- That's all, folks -------------------------------------------------*/