3 * $Id: rspit.c,v 1.1 1999/12/10 23:29:13 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.1 1999/12/10 23:29:13 mdw
34 * Emit random numbers for statistical tests.
38 /*----- Header files ------------------------------------------------------*/
53 #include <mLib/darray.h>
54 #include <mLib/dstr.h>
55 #include <mLib/mdwopt.h>
56 #include <mLib/quis.h>
57 #include <mLib/report.h>
75 #include "blowfish-ofb.h"
80 /*----- Data structures ---------------------------------------------------*/
84 grand
*(*seed
)(unsigned /*i*/);
89 static gen generators
[];
91 /*----- Miscellaneous static data -----------------------------------------*/
93 static FILE *outfp
= stdout
;
94 static size_t outsz
= 0;
99 static unsigned flags
= 0;
106 /*----- Help options ------------------------------------------------------*/
108 static void usage(FILE *fp
)
110 pquis(fp
, "Usage: $ generator [options]\n");
113 static void version(FILE *fp
)
115 pquis(fp
, "$, Catacomb version " VERSION
"\n");
118 static void help(FILE *fp
)
124 Emits a stream of random bytes suitable for, well, all sorts of things.\n\
125 The primary objective is to be able to generate streams of input for\n\
126 statistical tests, such as Diehard.\n\
128 Options are specific to the particular generator, although there's a\n\
131 -h, --help Display this help message.\n\
132 -v, --version Display the program's version number.\n\
133 -u, --usage Display a useless usage message.\n\
135 -l, --list Show a list of the supported generators, with\n\
137 -o, --output FILE Write output to FILE, not stdout.\n\
138 -z, --size SIZE Emit SIZE bytes, not an unlimited number.\n\
139 -p, --progress Show a little progress meter (on stderr).\n\
141 (A SIZE may be followed by `g' for gigabytes, `m' for megabytes, or\n\
142 `k' for kilobytes. If unqualified, an amount in bytes is assumed.)\n\
146 /*----- Main options parser -----------------------------------------------*/
148 static struct option opts
[] = {
150 /* --- Standard GNU help options --- */
152 { "help", 0, 0, 'h' },
153 { "version", 0, 0, 'v' },
154 { "usage", 0, 0, 'u' },
156 /* --- Other useful things --- */
158 { "list", 0, 0, 'l' },
159 { "output", OPTF_ARGREQ
, 0, 'o' },
160 { "size", OPTF_ARGREQ
, 0, 'z' },
161 { "progress", 0, 0, 'p' },
163 /* --- End of main table --- */
168 static const char *sopts
= "hvu lo:z:p";
171 DA_DECL(option_v
, struct option
);
175 static option_v optv
= DA_INIT
;
176 static dstr optd
= DSTR_INIT
;
178 /* --- @addopts@ --- *
180 * Arguments: @const char *s@ = pointer to short options
181 * @struct option *l@ = pointer to long options
185 * Use: Adds a collection of options to the table.
188 static void addopts(const char *s
, struct option
*l
)
194 DA_PUSH(&optv
, *l
++);
202 * Returns: Next option from argument array.
204 * Use: Fetches options, handling the standard ones.
210 int i
= mdwopt(argc
, argv
, optd
.buf
, DA(&optv
), 0, 0, 0);
223 puts("Generators supported:");
224 for (g
= generators
; g
->name
; g
++)
225 printf(" %s %s\n", g
->name
, g
->help
);
230 die(EXIT_FAILURE
, "already set an output file");
231 if (strcmp(optarg
, "-") == 0)
234 outfp
= fopen(optarg
, "w");
236 die(EXIT_FAILURE
, "couldn't open output file `%s': %s",
244 outsz
= strtoul(optarg
, &p
, 0);
246 die(EXIT_FAILURE
, "bad number `%s'", optarg
);
248 case 'G': case 'g': outsz
*= 1024;
249 case 'M': case 'm': outsz
*= 1024;
250 case 'K': case 'k': outsz
*= 1024;
254 die(EXIT_FAILURE
, "bad suffix `%s'", p
);
258 die(EXIT_FAILURE
, "bad suffix `%s'", p
);
269 /*----- Manglers for seed strings -----------------------------------------*/
273 * Arguments: @const char *p@ = pointer to input string
274 * @char **end@ = where the end goes
275 * @dstr *d@ = output buffer
279 * Use: Transforms a hex string into a chunk of binary data.
282 static void unhex(const char *p
, char **end
, dstr
*d
)
284 while (p
[0] && p
[1]) {
285 int x
= p
[0], y
= p
[1];
286 if ('0' <= x
&& x
<= '9') x
-= '0';
287 else if ('A' <= x
&& x
<= 'F') x
-= 'A' - 10;
288 else if ('a' <= x
&& x
<= 'f') x
-= 'a' - 10;
290 if ('0' <= y
&& y
<= '9') y
-= '0';
291 else if ('A' <= y
&& y
<= 'F') y
-= 'A' - 10;
292 else if ('a' <= y
&& y
<= 'f') y
-= 'a' - 10;
294 DPUTC(d
, (x
<< 4) + y
);
300 /*----- Generators --------------------------------------------------------*/
302 /* --- Blum-Blum-Shub strong generator --- */
304 static int bbsev(int ev
, mp
*m
, void *p
)
308 fputs("Searching for p: ", stderr
);
312 fputs("Searching for q: ", stderr
);
334 static grand
*gen_bbs(unsigned i
)
336 /* --- Default modulus --- *
338 * The factors of this number are
340 * @p = 1229936431484295969649886203367009966370895964206162032259292413@
341 * @7754313537966036459299022912838407755462506416274551744201653277@
342 * @313130311731673973886822067@
344 * @q = 9798171783943489959487301695884963889684294764514008432498259742@
345 * @5374320073594018817245784145742769603334292182227671519041431067@
346 * @61344781426317516045890159@
348 * Both %$p$% and %$q$% are prime; %$(p - 1)/2%$ and %$(q - 1)/2$% have no
349 * common factors. They were found using this program, with random
352 * I hope that, by publishing these factors, I'll dissuade people from
353 * actually using this modulus in attempt to actually attain real
354 * security. The program is quite quick at finding Blum numbers, so
355 * there's no excuse for not generating your own.
359 "120511284390135742513572142094334711443073194119732569353820828435640527418092392240366088035509890969913081816369160298961490135716255689660470370755013177656905237112577648090277537209936078171554274553448103698084782669252936352843649980105109850503830397166360721262431179505917248447259735253684659338653";
361 /* --- Other things --- */
369 /* --- Parse options --- */
371 static struct option opts
[] = {
372 { "modulus", OPTF_ARGREQ
, 0, 'm' },
373 { "generate", 0, 0, 'g' },
374 { "seed", OPTF_ARGREQ
, 0, 's' },
375 { "bits", OPTF_ARGREQ
, 0, 'b' },
376 { "show", 0, 0, 'S' },
380 addopts("m:gs:b:S", opts
);
397 bits
= strtoul(optarg
, 0, 0);
399 die(EXIT_FAILURE
, "bad number of bits `%s'", optarg
);
409 /* --- Generate a modulus if one is requested --- */
413 m
= mp_readstring(MP_NEW
, mt
, &p
, 0);
415 die(EXIT_FAILURE
, "bad modulus `%s'", mt
);
416 /* Unfortunately I don't know how to test for a Blum integer */
418 mp
*p
= mprand(MP_NEW
, bits
/ 2, &rand_global
, 3);
419 mp
*q
= mprand(MP_NEW
, bits
- bits
/ 2, &rand_global
, 3);
423 if ((err
= bbs_gen(&bp
, p
, q
, 0,
424 (flags
& f_progress
) ? bbsev
: 0, 0)) != 0)
425 die(EXIT_FAILURE
, "modulus generation failed (reason = %i)", err
);
429 fputs("p = ", stderr
);
430 mp_writefile(bp
.p
, stderr
, 10);
431 fputs("\nq = ", stderr
);
432 mp_writefile(bp
.q
, stderr
, 10);
433 fputs("\nn = ", stderr
);
434 mp_writefile(bp
.n
, stderr
, 10);
444 /* --- Set up a seed --- */
447 x
= mprand(MP_NEW
, mp_bits(m
) - 1, &rand_global
, 1);
450 x
= mp_readstring(MP_NEW
, xt
, &p
, 0);
452 die(EXIT_FAILURE
, "bad modulus `%s'", xt
);
464 /* --- Catacomb's random number generator --- */
466 static grand
*gen_rand(unsigned i
)
468 grand
*r
= rand_create();
471 static struct option opts
[] = {
472 { "key", OPTF_ARGREQ
, 0, 'k' },
473 { "text", OPTF_ARGREQ
, 0, 't' },
474 { "hex", OPTF_ARGREQ
, 0, 'H' },
475 { "noise", 0, 0, 'n' },
479 addopts("k:t:H:n", opts
);
488 octet hash
[RMD160_HASHSZ
];
490 rmd160_hash(&c
, optarg
, strlen(optarg
));
491 rmd160_done(&c
, hash
);
492 r
->ops
->misc(r
, RAND_KEY
, hash
, sizeof(hash
));
495 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, optarg
, strlen(optarg
));
500 unhex(optarg
, &p
, &d
);
502 die(EXIT_FAILURE
, "bad hex key `%s'", optarg
);
503 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, d
.buf
, d
.len
);
506 r
->ops
->misc(r
, RAND_NOISESRC
, &noise_source
);
515 /* --- RC4 output --- */
517 static grand
*gen_rc4(unsigned i
)
522 static struct option opts
[] = {
523 { "key", OPTF_ARGREQ
, 0, 'k' },
524 { "hex", OPTF_ARGREQ
, 0, 'H' },
528 addopts("k:H:", opts
);
537 dstr_ensure(&d
, RMD160_HASHSZ
);
539 rmd160_hash(&c
, optarg
, strlen(optarg
));
540 rmd160_done(&c
, d
.buf
);
541 d
.len
+= RMD160_HASHSZ
;
545 unhex(optarg
, &p
, &d
);
547 die(EXIT_FAILURE
, "bad hex key `%s'", optarg
);
557 rand_getgood(RAND_GLOBAL
, d
.buf
, d
.len
);
559 r
= rc4_rand(d
.buf
, d
.len
);
564 /* --- Output feedback generators --- */
567 E(OFB_DES, DES_KEYSZ, DES_BLKSZ, des_ofbrand), \
568 E(OFB_DES3, DES3_KEYSZ, DES3_BLKSZ, des3_ofbrand), \
569 E(OFB_RC5, RC5_KEYSZ, RC5_BLKSZ, rc5_ofbrand), \
570 E(OFB_BLOWFISH, BLOWFISH_KEYSZ, BLOWFISH_BLKSZ, blowfish_ofbrand), \
571 E(OFB_IDEA, IDEA_KEYSZ, IDEA_BLKSZ, idea_ofbrand)
576 grand
*(*rand
)(const void */
*k*/
, size_t /*sz*/);
578 #define E(c, x, y, z) { x, y, z }
584 #define E(c, x, y, z) c
591 static grand
*gen_ofb(unsigned i
)
597 static struct option opts
[] = {
598 { "key", OPTF_ARGREQ
, 0, 'k' },
599 { "hex", OPTF_ARGREQ
, 0, 'H' },
600 { "iv", OPTF_ARGREQ
, 0, 'i' },
604 addopts("k:H:i:", opts
);
613 dstr_ensure(&d
, RMD160_HASHSZ
);
615 rmd160_hash(&c
, optarg
, strlen(optarg
));
616 rmd160_done(&c
, d
.buf
);
617 d
.len
+= RMD160_HASHSZ
;
621 unhex(optarg
, &p
, &d
);
623 die(EXIT_FAILURE
, "bad hex key `%s'", optarg
);
627 unhex(optarg
, &p
, &iv
);
629 die(EXIT_FAILURE
, "bad hex IV `%s'", optarg
);
637 size_t n
= ofbtab
[i
].keysz
;
642 rand_getgood(RAND_GLOBAL
, d
.buf
, d
.len
);
645 while (d
.len
< ofbtab
[i
].keysz
)
647 if (ofbtab
[i
].keysz
&& d
.len
> ofbtab
[i
].keysz
)
648 d
.len
= ofbtab
[i
].keysz
;
650 r
= ofbtab
[i
].rand(d
.buf
, d
.len
);
652 while (iv
.len
< ofbtab
[i
].blksz
)
654 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, iv
.buf
);
662 /* --- Fibonacci generator --- */
664 static grand
*gen_fib(unsigned i
)
671 static struct option opts
[] = {
672 { "seed", OPTF_ARGREQ
, 0, 's' },
684 s
= strtoul(optarg
, &p
, 0);
686 die(EXIT_FAILURE
, "bad integer `%s'", optarg
);
693 r
= fibrand_create(s
);
695 r
->ops
->misc(r
, GRAND_SEEDRAND
, &rand_global
);
699 /* --- LC generator --- */
701 static grand
*gen_lc(unsigned i
)
707 static struct option opts
[] = {
708 { "seed", OPTF_ARGREQ
, 0, 's' },
720 s
= strtoul(optarg
, &p
, 0);
722 die(EXIT_FAILURE
, "bad integer `%s'", optarg
);
731 s
= rand_global
.ops
->range(&rand_global
, LCRAND_P
);
732 while (s
== LCRAND_FIXEDPT
);
734 return (lcrand_create(s
));
737 /* --- Basic options parser -- can't generate output --- */
739 static grand
*gen_opts(unsigned i
)
746 /*----- Generators table --------------------------------------------------*/
748 static gen generators
[] = {
749 { "fibonacci", gen_fib
, 0,
753 { "des-ofb", gen_ofb
, OFB_DES
,
754 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
755 { "3des-ofb", gen_ofb
, OFB_DES3
,
756 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
757 { "rc5-ofb", gen_ofb
, OFB_RC5
,
758 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
759 { "blowfish-ofb", gen_ofb
, OFB_BLOWFISH
,
760 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
761 { "idea-ofb", gen_ofb
, OFB_IDEA
,
762 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
764 "[-k KEY-PHRASE] [-H HEX-KEY]" },
765 { "rand", gen_rand
, 0,
766 "[-n] [-k KEY-PHRASE] [-t TEXT-BLOCK] [-H HEX-BLOCK]" },
768 "[-gS] [-s SEED] [-m MODULUS] [-b BITS" },
772 static gen optsg
= { "options", gen_opts
, 0,
773 "This message shouldn't be printed." };
775 /*----- Main code ---------------------------------------------------------*/
777 int main(int ac
, char *av
[])
781 unsigned percent
= -1;
784 static char baton
[] = "|/-\\";
787 /* --- Initialize mLib --- */
792 /* --- Set up the main Catacomb generator --- */
794 rand_noisesrc(RAND_GLOBAL
, &noise_source
);
796 /* --- Initialize the options table --- */
798 addopts(sopts
, opts
);
802 /* --- Read the generator out of the first argument --- */
804 if (argc
> 1 && *argv
[1] != '-') {
805 const char *arg
= av
[1];
806 size_t sz
= strlen(arg
);
810 for (gg
= generators
; gg
->name
; gg
++) {
811 if (strncmp(arg
, gg
->name
, sz
) == 0) {
812 if (gg
->name
[sz
] == 0) {
816 die(EXIT_FAILURE
, "ambiguous generator name `%s'", arg
);
822 die(EXIT_FAILURE
, "unknown generator name `%s'", arg
);
827 /* --- Get a generic random number generator --- */
830 if (!r
|| optind
!= ac
- 1) {
836 if (!(flags
& f_file
) && isatty(STDOUT_FILENO
))
837 die(EXIT_FAILURE
, "writing output to a terminal is a bad idea");
840 /* --- Spit out random data --- */
844 if (flags
& f_progress
) {
845 char *errbuf
= xmalloc(BUFSIZ
);
846 setvbuf(stderr
, errbuf
, _IOLBF
, BUFSIZ
);
852 signal(SIGPIPE
, SIG_IGN
);
857 size_t sz
= sizeof(buf
);
859 /* --- Emit a bufferful (or less) of data --- */
865 r
->ops
->fill(r
, buf
, sz
);
866 if (fwrite(buf
, 1, sz
, outfp
) != sz
) {
867 if (flags
& f_progress
)
869 die(EXIT_FAILURE
, "error writing data: %s", strerror(errno
));
873 /* --- Update the display --- */
875 if (flags
& f_progress
) {
883 if (difftime(t
, last
) > 1.0) {
887 fputs(" ] ", stderr
);
889 unsigned pc
= kb
* 100.0 / outsz
;
890 if (pc
> percent
|| percent
> 100 || difftime(t
, last
) > 1.0) {
894 for (; percent
< (pc
& ~1); percent
+= 2)
897 for (; pc
< 100; pc
+= 2)
899 fprintf(stderr
, "] %3i%% ", percent
);
907 while (q
> 8192 && suff
[1]) {
911 fprintf(stderr
, "%4i%c\r[", q
, *suff
);
914 for (pc
= 0; pc
< (percent
& ~1); pc
+= 2)
932 /* --- Terminate the loop --- */
934 if (outsz
&& kb
>= outsz
)
941 if (flags
& f_progress
)
946 /*----- That's all, folks -------------------------------------------------*/