3 * $Id: rspit.c,v 1.19 2001/06/16 23:42:17 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.19 2001/06/16 23:42:17 mdw
36 * Revision 1.18 2001/05/08 22:17:41 mdw
37 * New cipher Noekeon added.
39 * Revision 1.17 2001/05/07 17:33:19 mdw
40 * New Rijndael block sizes.
42 * Revision 1.16 2001/04/29 18:11:32 mdw
45 * Revision 1.15 2001/04/19 18:26:13 mdw
46 * Use the new MAC keysize names.
48 * Revision 1.14 2001/02/21 20:03:22 mdw
49 * Added support for MD2 hash function.
51 * Revision 1.13 2000/12/06 20:33:27 mdw
52 * Make flags be macros rather than enumerations, to ensure that they're
55 * Revision 1.12 2000/10/08 15:49:18 mdw
56 * Remove failed kludge for shutting up a warning.
58 * Revision 1.11 2000/10/08 12:10:32 mdw
59 * Make table have external linkage to bodge around deficiency in C. The
60 * problem is that @static gen generators[];@ is considered to be a
61 * `tentative definition', and therefore mustn't have incomplete type,
62 * which it obviously has.
64 * Revision 1.10 2000/08/11 21:34:59 mdw
65 * New restartable interface to Maurer testing.
67 * Revision 1.9 2000/08/04 23:24:15 mdw
68 * Add a timer and a discard option.
70 * Revision 1.8 2000/07/29 22:05:47 mdw
71 * Fix error in help message about Maurer test syntax.
73 * Revision 1.7 2000/07/18 23:01:26 mdw
74 * Improve progress indications, and allow user to choose chunk sizes for
77 * Revision 1.6 2000/07/15 20:53:35 mdw
78 * Add a load of new ciphers and hashes.
80 * Revision 1.5 2000/07/01 11:27:03 mdw
81 * Portability fix: don't assume that `stdout' is a constant expression.
82 * Remove old type name `bbs_param'.
84 * Revision 1.4 2000/06/17 12:08:28 mdw
85 * Restructure handling of cipher-based generators. Add counter-mode
86 * ciphers and MGF-1 hash functions. Add FIPS 140-1 and Maurer's tests.
88 * Revision 1.3 2000/02/12 18:21:03 mdw
89 * Overhaul of key management (again).
91 * Revision 1.2 1999/12/22 15:59:51 mdw
92 * New prime-search system. Read BBS keys from key files.
94 * Revision 1.1 1999/12/10 23:29:13 mdw
95 * Emit random numbers for statistical tests.
99 /*----- Header files ------------------------------------------------------*/
116 #include <mLib/darray.h>
117 #include <mLib/dstr.h>
118 #include <mLib/mdwopt.h>
119 #include <mLib/quis.h>
120 #include <mLib/report.h>
121 #include <mLib/sub.h>
123 #include "fipstest.h"
140 #include "des3-ofb.h"
143 #include "mars-ofb.h"
144 #include "skipjack-ofb.h"
146 #include "xtea-ofb.h"
147 #include "blowfish-ofb.h"
148 #include "twofish-ofb.h"
149 #include "idea-ofb.h"
150 #include "cast128-ofb.h"
151 #include "cast256-ofb.h"
152 #include "noekeon-ofb.h"
153 #include "rijndael-ofb.h"
154 #include "rijndael192-ofb.h"
155 #include "rijndael256-ofb.h"
156 #include "safer-ofb.h"
157 #include "safersk-ofb.h"
158 #include "square-ofb.h"
159 #include "serpent-ofb.h"
161 #include "des-counter.h"
162 #include "des3-counter.h"
163 #include "rc2-counter.h"
164 #include "rc5-counter.h"
165 #include "mars-counter.h"
166 #include "skipjack-counter.h"
167 #include "tea-counter.h"
168 #include "xtea-counter.h"
169 #include "blowfish-counter.h"
170 #include "twofish-counter.h"
171 #include "idea-counter.h"
172 #include "cast128-counter.h"
173 #include "cast256-counter.h"
174 #include "noekeon-counter.h"
175 #include "rijndael-counter.h"
176 #include "rijndael192-counter.h"
177 #include "rijndael256-counter.h"
178 #include "safer-counter.h"
179 #include "safersk-counter.h"
180 #include "square-counter.h"
181 #include "serpent-counter.h"
187 #include "tiger-mgf.h"
188 #include "rmd128-mgf.h"
189 #include "rmd160-mgf.h"
190 #include "rmd256-mgf.h"
191 #include "rmd320-mgf.h"
195 /*----- Data structures ---------------------------------------------------*/
199 grand
*(*seed
)(unsigned /*i*/);
204 extern gen generators
[];
212 E(SKIPJACK, skipjack) \
215 E(BLOWFISH, blowfish) \
216 E(TWOFISH, twofish) \
218 E(CAST128, cast128) \
219 E(CAST256, cast256) \
222 E(SAFERSK, safersk) \
223 E(NOEKEON, noekeon) \
224 E(RIJNDAEL, rijndael) \
225 E(RIJNDAEL192, rijndael192) \
226 E(RIJNDAEL256, rijndael256) \
240 #define E(PRE, pre) CIPHER_##PRE,
241 enum { CIPHERS CIPHER__bogus
};
244 #define E(PRE, pre) HASH_##PRE,
245 enum { HASHES HASH__bogus
};
251 grand
*(*ofb
)(const void */
*k*/
, size_t /*sz*/);
252 grand
*(*counter
)(const void */
*k*/
, size_t /*sz*/);
254 #define E(PRE, pre) \
255 { pre##_keysz, PRE##_BLKSZ, pre##_ofbrand, pre##_counterrand },
263 grand
*(*mgf
)(const void */
*k*/
, size_t /*sz*/);
265 #define E(PRE, pre) \
266 { &pre, pre##_mgfkeysz, pre##_mgfrand },
271 /*----- Miscellaneous static data -----------------------------------------*/
274 static size_t outsz
= 0;
275 static unsigned maurer_lo
= 5, maurer_hi
= 8;
280 static unsigned flags
= 0;
282 #define f_progress 1u
287 #define f_discard 32u
289 /*----- Help options ------------------------------------------------------*/
291 static void usage(FILE *fp
)
293 pquis(fp
, "Usage: $ generator [options]\n");
296 static void version(FILE *fp
)
298 pquis(fp
, "$, Catacomb version " VERSION
"\n");
301 static void help(FILE *fp
)
307 Emits a stream of random bytes suitable for, well, all sorts of things.\n\
308 The primary objective is to be able to generate streams of input for\n\
309 statistical tests, such as Diehard.\n\
311 Options are specific to the particular generator, although there's a\n\
314 -h, --help Display this help message.\n\
315 -v, --version Display the program's version number.\n\
316 -u, --usage Display a useless usage message.\n\
318 -l, --list Show a list of the supported generators, with\n\
320 -f, --fipstest Run the FIPS 140-1 randomness test.\n\
321 -m, --maurer[=LO-HI] Run Maurer's universal statistical test.\n\
322 -o, --output FILE Write output to FILE, not stdout.\n\
323 -z, --size SIZE Emit SIZE bytes, not an unlimited number.\n\
324 -p, --progress Show a little progress meter (on stderr).\n\
325 -T, --timer Keep track of the CPU time used by the generator.\n\
326 -d, --discard Discard the generated output.\n\
328 (A SIZE may be followed by `g' for gigabytes, `m' for megabytes, or\n\
329 `k' for kilobytes. If unqualified, an amount in bytes is assumed.)\n\
333 /*----- Main options parser -----------------------------------------------*/
335 static struct option opts
[] = {
337 /* --- Standard GNU help options --- */
339 { "help", 0, 0, 'h' },
340 { "version", 0, 0, 'v' },
341 { "usage", 0, 0, 'u' },
343 /* --- Other useful things --- */
345 { "list", 0, 0, 'l' },
346 { "fipstest", 0, 0, 'f' },
347 { "maurer", OPTF_ARGOPT
, 0, 'm' },
348 { "output", OPTF_ARGREQ
, 0, 'o' },
349 { "size", OPTF_ARGREQ
, 0, 'z' },
350 { "progress", 0, 0, 'p' },
351 { "timer", 0, 0, 'T' },
352 { "discard", 0, 0, 'd' },
354 /* --- End of main table --- */
359 static const char *sopts
= "hvu lfm::o:z:pTd";
362 DA_DECL(option_v
, struct option
);
366 static option_v optv
= DA_INIT
;
367 static dstr optd
= DSTR_INIT
;
369 /* --- @addopts@ --- *
371 * Arguments: @const char *s@ = pointer to short options
372 * @struct option *l@ = pointer to long options
376 * Use: Adds a collection of options to the table.
379 static void addopts(const char *s
, struct option
*l
)
385 DA_PUSH(&optv
, *l
++);
393 * Returns: Next option from argument array.
395 * Use: Fetches options, handling the standard ones.
401 int i
= mdwopt(argc
, argv
, optd
.buf
, DA(&optv
), 0, 0, 0);
414 puts("Generators supported:");
415 for (g
= generators
; g
->name
; g
++)
416 printf(" %s %s\n", g
->name
, g
->help
);
426 unsigned long lo
, hi
;
427 lo
= strtoul(optarg
, &p
, 0);
428 if (*p
== '-' || *p
== ',')
429 hi
= strtoul(p
+ 1, &p
, 0);
432 if (*p
!= 0 || hi
< lo
|| lo
== 0)
433 die(EXIT_FAILURE
, "bad bit range `%s'", optarg
);
440 die(EXIT_FAILURE
, "already set an output file");
441 if (strcmp(optarg
, "-") == 0)
444 outfp
= fopen(optarg
, "w");
446 die(EXIT_FAILURE
, "couldn't open output file `%s': %s",
447 optarg
, strerror(errno
));
454 outsz
= strtoul(optarg
, &p
, 0);
456 die(EXIT_FAILURE
, "bad number `%s'", optarg
);
458 case 'G': case 'g': outsz
*= 1024;
459 case 'M': case 'm': outsz
*= 1024;
460 case 'K': case 'k': outsz
*= 1024;
464 die(EXIT_FAILURE
, "bad suffix `%s'", p
);
468 die(EXIT_FAILURE
, "bad suffix `%s'", p
);
485 /*----- Manglers for seed strings -----------------------------------------*/
489 * Arguments: @const char *p@ = pointer to input string
490 * @char **end@ = where the end goes
491 * @dstr *d@ = output buffer
495 * Use: Transforms a hex string into a chunk of binary data.
498 static void unhex(const char *p
, char **end
, dstr
*d
)
500 while (p
[0] && p
[1]) {
501 int x
= p
[0], y
= p
[1];
502 if ('0' <= x
&& x
<= '9') x
-= '0';
503 else if ('A' <= x
&& x
<= 'F') x
-= 'A' - 10;
504 else if ('a' <= x
&& x
<= 'f') x
-= 'a' - 10;
506 if ('0' <= y
&& y
<= '9') y
-= '0';
507 else if ('A' <= y
&& y
<= 'F') y
-= 'A' - 10;
508 else if ('a' <= y
&& y
<= 'f') y
-= 'a' - 10;
510 DPUTC(d
, (x
<< 4) + y
);
516 /* --- Generate a key --- */
518 static void textkey(dstr
*d
, const char *p
, const octet
*ksz
)
520 size_t sz
= strlen(p
);
523 die(EXIT_FAILURE
, "zero-length key string");
524 if (keysz(sz
, ksz
) != sz
)
528 rmd160_mgfinit(&g
, p
, sz
);
531 rmd160_mgfencrypt(&g
, 0, d
->buf
, sz
);
534 assert(((void)"I can't seem to choose a good key size",
535 keysz(d
->len
, ksz
) == d
->len
));
538 static void hexkey(dstr
*d
, const char *p
, const octet
*ksz
)
541 unhex(optarg
, &q
, d
);
543 die(EXIT_FAILURE
, "bad hex key `%s'", p
);
544 if (keysz(d
->len
, ksz
) != d
->len
)
545 die(EXIT_FAILURE
, "bad key length");
548 static void randkey(dstr
*d
, const octet
*ksz
)
550 size_t sz
= keysz(0, ksz
);
552 rand_get(RAND_GLOBAL
, d
->buf
, sz
);
556 /*----- Generators --------------------------------------------------------*/
558 /* --- Blum-Blum-Shub strong generator --- */
560 static grand
*gen_bbs(unsigned i
)
562 /* --- Default modulus --- *
564 * The factors of this number are
566 * @p = 1229936431484295969649886203367009966370895964206162032259292413@
567 * @7754313537966036459299022912838407755462506416274551744201653277@
568 * @313130311731673973886822067@
570 * @q = 9798171783943489959487301695884963889684294764514008432498259742@
571 * @5374320073594018817245784145742769603334292182227671519041431067@
572 * @61344781426317516045890159@
574 * Both %$p$% and %$q$% are prime; %$(p - 1)/2$% and %$(q - 1)/2$% have no
575 * common factors. They were found using this program, with random
578 * I hope that, by publishing these factors, I'll dissuade people from
579 * actually using this modulus in an attempt to attain real security. The
580 * program is quite quick at finding Blum numbers, so there's no excuse for
581 * not generating your own.
585 "12051128439013574251357214209433471144307319411973256935382082"
586 "84356405274180923922403660880355098909699130818163691602989614"
587 "90135716255689660470370755013177656905237112577648090277537209"
588 "93607817155427455344810369808478266925293635284364998010510985"
589 "0503830397166360721262431179505917248447259735253684659338653";
591 /* --- Other things --- */
595 unsigned bits
= 1024;
598 const char *kfile
= 0, *id
= 0, *ktype
= 0;
600 /* --- Parse options --- */
602 static struct option opts
[] = {
603 { "modulus", OPTF_ARGREQ
, 0, 'M' },
604 { "generate", 0, 0, 'g' },
605 { "seed", OPTF_ARGREQ
, 0, 's' },
606 { "bits", OPTF_ARGREQ
, 0, 'b' },
607 { "show", 0, 0, 'S' },
608 { "keyring", OPTF_ARGREQ
, 0, 'k' },
609 { "id", OPTF_ARGREQ
, 0, 'i' },
610 { "type", OPTF_ARGREQ
, 0, 't' },
614 addopts("M:gs:b:Sk:i:t:", opts
);
631 bits
= strtoul(optarg
, 0, 0);
633 die(EXIT_FAILURE
, "bad number of bits `%s'", optarg
);
655 /* --- Generate a modulus if one is requested --- */
659 m
= mp_readstring(MP_NEW
, mt
, &p
, 0);
660 if (!m
|| *p
|| (m
->v
[0] & 3) != 1)
661 die(EXIT_FAILURE
, "bad modulus `%s'", mt
);
662 /* Unfortunately I don't know how to test for a Blum integer */
663 } else if (kfile
|| id
|| ktype
) {
668 /* --- Open the key file --- */
672 if (key_open(&kf
, kfile
, KOPEN_READ
, key_moan
, 0)) {
673 die(EXIT_FAILURE
, "error opening key file `%s': %s",
674 kfile
, strerror(errno
));
677 /* --- Find the key --- */
680 if ((kk
= key_bytag(&kf
, id
)) == 0)
681 die(EXIT_FAILURE
, "key `%s' not found", id
);
685 if ((kk
= key_bytype(&kf
, ktype
)) == 0)
686 die(EXIT_FAILURE
, "no suitable key with type `%s' found", ktype
);
689 /* --- Read the key data --- */
691 if ((kk
->k
.e
& KF_ENCMASK
) != KENC_STRUCT
)
692 die(EXIT_FAILURE
, "key is not structured");
693 if ((kd
= key_structfind(&kk
->k
, "n")) == 0)
694 die(EXIT_FAILURE
, "key has no subkey `n'");
695 if ((kd
->e
& KF_ENCMASK
) != KENC_MP
)
696 die(EXIT_FAILURE
, "incomatible subkey encoding");
697 m
= MP_COPY(kd
->u
.m
);
702 if (bbs_gen(&bp
, bits
, &rand_global
, 0,
703 (flags
& f_progress
) ? pgen_ev
: 0, 0))
704 die(EXIT_FAILURE
, "modulus generation failed");
708 fputs("p = ", stderr
);
709 mp_writefile(bp
.p
, stderr
, 10);
710 fputs("\nq = ", stderr
);
711 mp_writefile(bp
.q
, stderr
, 10);
712 fputs("\nn = ", stderr
);
713 mp_writefile(bp
.n
, stderr
, 10);
721 /* --- Set up a seed --- */
724 x
= mprand(MP_NEW
, mp_bits(m
) - 1, &rand_global
, 1);
727 x
= mp_readstring(MP_NEW
, xt
, &p
, 0);
729 die(EXIT_FAILURE
, "bad modulus `%s'", xt
);
741 /* --- Catacomb's random number generator --- */
743 static grand
*gen_rand(unsigned i
)
745 grand
*r
= rand_create();
748 static struct option opts
[] = {
749 { "key", OPTF_ARGREQ
, 0, 'k' },
750 { "text", OPTF_ARGREQ
, 0, 't' },
751 { "hex", OPTF_ARGREQ
, 0, 'H' },
755 addopts("k:t:H:n", opts
);
757 r
->ops
->misc(r
, RAND_NOISESRC
, &noise_source
);
758 r
->ops
->misc(r
, RAND_SEED
, 160);
767 textkey(&d
, optarg
, rmd160_hmackeysz
);
768 r
->ops
->misc(r
, RAND_KEY
, d
.buf
, d
.len
);
771 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, optarg
, strlen(optarg
));
775 hexkey(&d
, optarg
, rmd160_hmackeysz
);
776 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, d
.buf
, d
.len
);
785 /* --- RC4 output --- */
787 static grand
*gen_rc4(unsigned i
)
792 static struct option opts
[] = {
793 { "key", OPTF_ARGREQ
, 0, 'k' },
794 { "hex", OPTF_ARGREQ
, 0, 'H' },
798 addopts("k:H:", opts
);
807 textkey(&d
, optarg
, rc4_keysz
);
811 hexkey(&d
, optarg
, rc4_keysz
);
819 randkey(&d
, rc4_keysz
);
820 r
= rc4_rand(d
.buf
, d
.len
);
825 /* --- SEAL output --- */
827 static grand
*gen_seal(unsigned i
)
833 static struct option opts
[] = {
834 { "key", OPTF_ARGREQ
, 0, 'k' },
835 { "hex", OPTF_ARGREQ
, 0, 'H' },
836 { "sequence", OPTF_ARGREQ
, 0, 'n' },
840 addopts("k:H:n:", opts
);
849 textkey(&d
, optarg
, seal_keysz
);
853 hexkey(&d
, optarg
, seal_keysz
);
857 n
= strtoul(optarg
, &p
, 0);
859 die(EXIT_FAILURE
, "bad number `%s'", optarg
);
867 randkey(&d
, seal_keysz
);
868 r
= seal_rand(d
.buf
, d
.len
, n
);
873 /* --- Output feedback generators --- */
875 static grand
*gen_ofb(unsigned i
)
881 static struct option opts
[] = {
882 { "key", OPTF_ARGREQ
, 0, 'k' },
883 { "hex", OPTF_ARGREQ
, 0, 'H' },
884 { "iv", OPTF_ARGREQ
, 0, 'i' },
888 addopts("k:H:i:", opts
);
897 textkey(&d
, optarg
, ciphertab
[i
].keysz
);
901 hexkey(&d
, optarg
, ciphertab
[i
].keysz
);
905 unhex(optarg
, &p
, &iv
);
907 die(EXIT_FAILURE
, "bad hex IV `%s'", optarg
);
915 randkey(&d
, ciphertab
[i
].keysz
);
916 r
= ciphertab
[i
].ofb(d
.buf
, d
.len
);
918 if (iv
.len
!= ciphertab
[i
].blksz
) {
919 die(EXIT_FAILURE
, "bad IV length %lu (must be %lu)",
920 (unsigned long)iv
.len
, (unsigned long)ciphertab
[i
].blksz
);
922 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, iv
.buf
);
930 /* --- Counter generators --- */
932 static grand
*gen_counter(unsigned i
)
938 static struct option opts
[] = {
939 { "key", OPTF_ARGREQ
, 0, 'k' },
940 { "hex", OPTF_ARGREQ
, 0, 'H' },
941 { "iv", OPTF_ARGREQ
, 0, 'i' },
945 addopts("k:H:i:", opts
);
954 textkey(&d
, optarg
, ciphertab
[i
].keysz
);
958 hexkey(&d
, optarg
, ciphertab
[i
].keysz
);
962 unhex(optarg
, &p
, &iv
);
964 die(EXIT_FAILURE
, "bad hex IV `%s'", optarg
);
972 randkey(&d
, ciphertab
[i
].keysz
);
973 r
= ciphertab
[i
].counter(d
.buf
, d
.len
);
975 if (iv
.len
!= ciphertab
[i
].blksz
) {
976 die(EXIT_FAILURE
, "bad IV length %lu (must be %lu)",
977 (unsigned long)iv
.len
, (unsigned long)ciphertab
[i
].blksz
);
979 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, iv
.buf
);
987 /* --- Mask generators --- */
989 static grand
*gen_mgf(unsigned i
)
995 static struct option opts
[] = {
996 { "key", OPTF_ARGREQ
, 0, 'k' },
997 { "hex", OPTF_ARGREQ
, 0, 'H' },
998 { "index", OPTF_ARGREQ
, 0, 'i' },
1002 addopts("k:H:i:", opts
);
1011 textkey(&d
, optarg
, hashtab
[i
].keysz
);
1015 hexkey(&d
, optarg
, hashtab
[i
].keysz
);
1019 c
= strtoul(optarg
, &p
, 0);
1021 die(EXIT_FAILURE
, "bad index `%s'", optarg
);
1029 randkey(&d
, hashtab
[i
].keysz
);
1031 r
= hashtab
[i
].mgf(d
.buf
, d
.len
);
1033 r
->ops
->misc(r
, GRAND_SEEDUINT32
, c
);
1039 /* --- Fibonacci generator --- */
1041 static grand
*gen_fib(unsigned i
)
1048 static struct option opts
[] = {
1049 { "seed", OPTF_ARGREQ
, 0, 's' },
1053 addopts("s:", opts
);
1061 s
= strtoul(optarg
, &p
, 0);
1063 die(EXIT_FAILURE
, "bad integer `%s'", optarg
);
1070 r
= fibrand_create(s
);
1072 r
->ops
->misc(r
, GRAND_SEEDRAND
, &rand_global
);
1076 /* --- LC generator --- */
1078 static grand
*gen_lc(unsigned i
)
1084 static struct option opts
[] = {
1085 { "seed", OPTF_ARGREQ
, 0, 's' },
1089 addopts("s:", opts
);
1097 s
= strtoul(optarg
, &p
, 0);
1099 die(EXIT_FAILURE
, "bad integer `%s'", optarg
);
1108 s
= rand_global
.ops
->range(&rand_global
, LCRAND_P
);
1109 while (s
== LCRAND_FIXEDPT
);
1111 return (lcrand_create(s
));
1114 /* --- Basic options parser -- can't generate output --- */
1116 static grand
*gen_opts(unsigned i
)
1123 /*----- Generators table --------------------------------------------------*/
1125 gen generators
[] = {
1126 { "fibonacci", gen_fib
, 0,
1130 #define E(PRE, pre) \
1131 { #pre "-ofb", gen_ofb, CIPHER_##PRE, \
1132 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
1135 #define E(PRE, pre) \
1136 { #pre "-counter", gen_counter, CIPHER_##PRE, \
1137 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
1140 #define E(PRE, pre) \
1141 { #pre "-mgf", gen_mgf, HASH_##PRE, \
1142 "[-k KEY-PHRASE] [-H HEX-KEY] [-i INDEX]" },
1145 { "rc4", gen_rc4
, 0,
1146 "[-k KEY-PHRASE] [-H HEX-KEY]" },
1147 { "seal", gen_seal
, 0,
1148 "[-k KEY-PHRASE] [-H HEX-KEY] [-n SEQ]" },
1149 { "rand", gen_rand
, 0,
1150 "[-n] [-k KEY-PHRASE] [-t TEXT-BLOCK] [-H HEX-BLOCK]" },
1151 { "bbs", gen_bbs
, 0,
1152 "[-gS] [-s SEED] [-M MODULUS] [-b BITS] [-k KEYRING] [-i TAG] [-t TYPE]"
1157 static gen optsg
= { "options", gen_opts
, 0,
1158 "This message shouldn't be printed." };
1160 /*----- Random number generation ------------------------------------------*/
1162 static int genfile(const void *buf
, size_t sz
, void *p
)
1165 if (fwrite(buf
, 1, sz
, fp
) != sz
)
1166 die(EXIT_FAILURE
, "error writing to file: %s", strerror(errno
));
1170 static int genbuf(const void *buf
, size_t sz
, void *p
)
1173 memcpy(*pp
, buf
, sz
);
1178 typedef struct genmaurer_ctx
{
1183 static int genmaurer(const void *buf
, size_t sz
, void *p
)
1185 genmaurer_ctx
*g
= p
;
1188 for (i
= 0; i
< g
->n
; i
++)
1189 maurer_test(&g
->m
[i
], buf
, sz
);
1193 static int generate(grand
*r
, size_t outsz
,
1194 int (*func
)(const void *buf
, size_t sz
, void *p
),
1197 static char kmg
[] = { ' ', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 0 };
1199 unsigned percent
= 0;
1202 static char baton
[] = "-\\|/";
1207 /* --- Spit out random data --- */
1211 if (flags
& f_progress
) {
1212 char *errbuf
= xmalloc(BUFSIZ
);
1213 setvbuf(stderr
, errbuf
, _IOLBF
, BUFSIZ
);
1215 fprintf(stderr
, "[%*s] 0%% 0\r[/\b", 50, "");
1217 fputs("[ ] 0\r[/\b", stderr
);
1222 signal(SIGPIPE
, SIG_IGN
);
1227 size_t sz
= sizeof(buf
);
1228 clock_t c_start
, c_stop
;
1230 /* --- Emit a bufferful (or less) of data --- */
1233 if (sz
> outsz
- kb
)
1237 r
->ops
->fill(r
, buf
, sz
);
1239 clk
+= c_stop
- c_start
;
1240 if (func
&& (rc
= func(buf
, sz
, p
)) != 0)
1244 /* --- Update the display --- */
1246 if (flags
& f_progress
) {
1254 if (difftime(t
, last
) > 1.0) {
1258 fputs(" ] ", stderr
);
1260 unsigned pc
= kb
* 100.0 / outsz
;
1261 if (pc
> percent
|| percent
> 100 || difftime(t
, last
) > 1.0) {
1265 for (; percent
< (pc
& ~1); percent
+= 2)
1268 for (; pc
< 100; pc
+= 2)
1270 fprintf(stderr
, "] %3i%% ", percent
);
1278 while (q
> 8192 && kk
[1]) {
1282 fprintf(stderr
, "%4i%c\r[", q
, *kk
);
1285 for (pc
= 0; pc
< (percent
& ~1); pc
+= 2)
1294 if (percent
< 100) {
1295 putc(*bp
++, stderr
);
1303 /* --- Terminate the loop --- */
1305 } while (!outsz
|| kb
< outsz
);
1307 if (flags
& f_progress
)
1308 fputc('\n', stderr
);
1309 if (flags
& f_timer
) {
1310 fprintf(stderr
, "generated %lu bytes ", (unsigned long)outsz
);
1312 fputs("too quickly to measure\n", stderr
);
1315 double sec
= (double)clk
/CLOCKS_PER_SEC
;
1316 double bps
= (outsz
<< 3)/sec
;
1317 for (kk
= kmg
; bps
> 1024 && kk
[1]; kk
++, bps
/= 1024)
1319 fprintf(stderr
, "in %g secs (%g %cb/s)\n", sec
, bps
, *kk
);
1325 /*----- Main code ---------------------------------------------------------*/
1327 int main(int ac
, char *av
[])
1332 /* --- Initialize mLib --- */
1337 /* --- Set up the main Catacomb generator --- */
1339 rand_noisesrc(RAND_GLOBAL
, &noise_source
);
1340 rand_seed(RAND_GLOBAL
, 160);
1342 /* --- Initialize the options table --- */
1344 addopts(sopts
, opts
);
1349 /* --- Read the generator out of the first argument --- */
1351 if (argc
> 1 && *argv
[1] != '-') {
1352 const char *arg
= av
[1];
1353 size_t sz
= strlen(arg
);
1357 for (gg
= generators
; gg
->name
; gg
++) {
1358 if (strncmp(arg
, gg
->name
, sz
) == 0) {
1359 if (gg
->name
[sz
] == 0) {
1363 die(EXIT_FAILURE
, "ambiguous generator name `%s'", arg
);
1369 die(EXIT_FAILURE
, "unknown generator name `%s'", arg
);
1374 /* --- Get a generic random number generator --- */
1377 if (!r
|| optind
!= ac
- 1) {
1382 /* --- Do the FIPS test --- */
1384 if (flags
& f_fips
) {
1385 octet buf
[FIPSTEST_BUFSZ
];
1389 generate(r
, sizeof(buf
), genbuf
, &p
);
1391 if (rc
& FIPSTEST_MONOBIT
)
1392 moan("failed monobit test");
1393 if (rc
& FIPSTEST_POKER
)
1394 moan("failed poker test");
1395 if (rc
& FIPSTEST_RUNS
)
1396 moan("failed runs test");
1397 if (rc
& FIPSTEST_LONGRUNS
)
1398 moan("failed long runs test");
1399 if (!rc
&& (flags
& f_progress
))
1400 fputs("test passed\n", stderr
);
1401 return (rc ? EXIT_FAILURE
: 0);
1404 /* --- Do Maurer's test --- */
1406 if (flags
& f_maurer
) {
1412 static struct { double x
; const char *sig
; } sigtab
[] = {
1420 g
.n
= maurer_hi
- maurer_lo
+ 1;
1421 g
.m
= xmalloc(g
.n
* sizeof(maurer_ctx
));
1422 for (i
= 0; i
< g
.n
; i
++)
1423 maurer_init(&g
.m
[i
], i
+ maurer_lo
);
1424 bufsz
= (100 * maurer_hi
) << maurer_hi
;
1426 generate(r
, bufsz
, genmaurer
, &g
);
1428 for (i
= maurer_lo
; i
<= maurer_hi
; i
++) {
1429 double z
= maurer_done(&g
.m
[i
- maurer_lo
]);
1430 double zz
= fabs(z
);
1433 for (j
= 0; sigtab
[j
].sig
; j
++) {
1434 if (zz
> sigtab
[j
].x
) {
1436 moan("failed, bits = %u, sig = %s, Z_u = %g",
1437 i
, sigtab
[j
].sig
, z
);
1441 if (flags
& f_progress
)
1442 fprintf(stderr
, "bits = %u, Z_u = %g\n", i
, z
);
1449 /* --- Discard --- */
1451 if (flags
& f_discard
) {
1452 generate(r
, outsz
, 0, 0);
1456 /* --- Write to a file --- */
1459 if (!(flags
& f_file
) && isatty(STDOUT_FILENO
))
1460 die(EXIT_FAILURE
, "writing output to a terminal is a bad idea");
1463 generate(r
, outsz
, genfile
, outfp
);
1471 /*----- That's all, folks -------------------------------------------------*/