3 * $Id: rspit.c,v 1.3 2000/02/12 18:21:03 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.3 2000/02/12 18:21:03 mdw
34 * Overhaul of key management (again).
36 * Revision 1.2 1999/12/22 15:59:51 mdw
37 * New prime-search system. Read BBS keys from key files.
39 * Revision 1.1 1999/12/10 23:29:13 mdw
40 * Emit random numbers for statistical tests.
44 /*----- Header files ------------------------------------------------------*/
59 #include <mLib/darray.h>
60 #include <mLib/dstr.h>
61 #include <mLib/mdwopt.h>
62 #include <mLib/quis.h>
63 #include <mLib/report.h>
82 #include "blowfish-ofb.h"
87 /*----- Data structures ---------------------------------------------------*/
91 grand
*(*seed
)(unsigned /*i*/);
96 static gen generators
[];
98 /*----- Miscellaneous static data -----------------------------------------*/
100 static FILE *outfp
= stdout
;
101 static size_t outsz
= 0;
106 static unsigned flags
= 0;
113 /*----- Help options ------------------------------------------------------*/
115 static void usage(FILE *fp
)
117 pquis(fp
, "Usage: $ generator [options]\n");
120 static void version(FILE *fp
)
122 pquis(fp
, "$, Catacomb version " VERSION
"\n");
125 static void help(FILE *fp
)
131 Emits a stream of random bytes suitable for, well, all sorts of things.\n\
132 The primary objective is to be able to generate streams of input for\n\
133 statistical tests, such as Diehard.\n\
135 Options are specific to the particular generator, although there's a\n\
138 -h, --help Display this help message.\n\
139 -v, --version Display the program's version number.\n\
140 -u, --usage Display a useless usage message.\n\
142 -l, --list Show a list of the supported generators, with\n\
144 -o, --output FILE Write output to FILE, not stdout.\n\
145 -z, --size SIZE Emit SIZE bytes, not an unlimited number.\n\
146 -p, --progress Show a little progress meter (on stderr).\n\
148 (A SIZE may be followed by `g' for gigabytes, `m' for megabytes, or\n\
149 `k' for kilobytes. If unqualified, an amount in bytes is assumed.)\n\
153 /*----- Main options parser -----------------------------------------------*/
155 static struct option opts
[] = {
157 /* --- Standard GNU help options --- */
159 { "help", 0, 0, 'h' },
160 { "version", 0, 0, 'v' },
161 { "usage", 0, 0, 'u' },
163 /* --- Other useful things --- */
165 { "list", 0, 0, 'l' },
166 { "output", OPTF_ARGREQ
, 0, 'o' },
167 { "size", OPTF_ARGREQ
, 0, 'z' },
168 { "progress", 0, 0, 'p' },
170 /* --- End of main table --- */
175 static const char *sopts
= "hvu lo:z:p";
178 DA_DECL(option_v
, struct option
);
182 static option_v optv
= DA_INIT
;
183 static dstr optd
= DSTR_INIT
;
185 /* --- @addopts@ --- *
187 * Arguments: @const char *s@ = pointer to short options
188 * @struct option *l@ = pointer to long options
192 * Use: Adds a collection of options to the table.
195 static void addopts(const char *s
, struct option
*l
)
201 DA_PUSH(&optv
, *l
++);
209 * Returns: Next option from argument array.
211 * Use: Fetches options, handling the standard ones.
217 int i
= mdwopt(argc
, argv
, optd
.buf
, DA(&optv
), 0, 0, 0);
230 puts("Generators supported:");
231 for (g
= generators
; g
->name
; g
++)
232 printf(" %s %s\n", g
->name
, g
->help
);
237 die(EXIT_FAILURE
, "already set an output file");
238 if (strcmp(optarg
, "-") == 0)
241 outfp
= fopen(optarg
, "w");
243 die(EXIT_FAILURE
, "couldn't open output file `%s': %s",
251 outsz
= strtoul(optarg
, &p
, 0);
253 die(EXIT_FAILURE
, "bad number `%s'", optarg
);
255 case 'G': case 'g': outsz
*= 1024;
256 case 'M': case 'm': outsz
*= 1024;
257 case 'K': case 'k': outsz
*= 1024;
261 die(EXIT_FAILURE
, "bad suffix `%s'", p
);
265 die(EXIT_FAILURE
, "bad suffix `%s'", p
);
276 /*----- Manglers for seed strings -----------------------------------------*/
280 * Arguments: @const char *p@ = pointer to input string
281 * @char **end@ = where the end goes
282 * @dstr *d@ = output buffer
286 * Use: Transforms a hex string into a chunk of binary data.
289 static void unhex(const char *p
, char **end
, dstr
*d
)
291 while (p
[0] && p
[1]) {
292 int x
= p
[0], y
= p
[1];
293 if ('0' <= x
&& x
<= '9') x
-= '0';
294 else if ('A' <= x
&& x
<= 'F') x
-= 'A' - 10;
295 else if ('a' <= x
&& x
<= 'f') x
-= 'a' - 10;
297 if ('0' <= y
&& y
<= '9') y
-= '0';
298 else if ('A' <= y
&& y
<= 'F') y
-= 'A' - 10;
299 else if ('a' <= y
&& y
<= 'f') y
-= 'a' - 10;
301 DPUTC(d
, (x
<< 4) + y
);
307 /*----- Generators --------------------------------------------------------*/
309 /* --- Blum-Blum-Shub strong generator --- */
311 static grand
*gen_bbs(unsigned i
)
313 /* --- Default modulus --- *
315 * The factors of this number are
317 * @p = 1229936431484295969649886203367009966370895964206162032259292413@
318 * @7754313537966036459299022912838407755462506416274551744201653277@
319 * @313130311731673973886822067@
321 * @q = 9798171783943489959487301695884963889684294764514008432498259742@
322 * @5374320073594018817245784145742769603334292182227671519041431067@
323 * @61344781426317516045890159@
325 * Both %$p$% and %$q$% are prime; %$(p - 1)/2%$ and %$(q - 1)/2$% have no
326 * common factors. They were found using this program, with random
329 * I hope that, by publishing these factors, I'll dissuade people from
330 * actually using this modulus in attempt to actually attain real
331 * security. The program is quite quick at finding Blum numbers, so
332 * there's no excuse for not generating your own.
336 "120511284390135742513572142094334711443073194119732569353820828435640527418092392240366088035509890969913081816369160298961490135716255689660470370755013177656905237112577648090277537209936078171554274553448103698084782669252936352843649980105109850503830397166360721262431179505917248447259735253684659338653";
338 /* --- Other things --- */
342 unsigned bits
= 1024;
345 const char *kfile
= 0, *id
= 0, *ktype
= 0;
347 /* --- Parse options --- */
349 static struct option opts
[] = {
350 { "modulus", OPTF_ARGREQ
, 0, 'm' },
351 { "generate", 0, 0, 'g' },
352 { "seed", OPTF_ARGREQ
, 0, 's' },
353 { "bits", OPTF_ARGREQ
, 0, 'b' },
354 { "show", 0, 0, 'S' },
355 { "keyring", OPTF_ARGREQ
, 0, 'k' },
356 { "id", OPTF_ARGREQ
, 0, 'i' },
357 { "type", OPTF_ARGREQ
, 0, 't' },
361 addopts("m:gs:b:Sk:i:t:", opts
);
378 bits
= strtoul(optarg
, 0, 0);
380 die(EXIT_FAILURE
, "bad number of bits `%s'", optarg
);
402 /* --- Generate a modulus if one is requested --- */
406 m
= mp_readstring(MP_NEW
, mt
, &p
, 0);
407 if (!m
|| *p
|| (m
->v
[0] & 3) != 1)
408 die(EXIT_FAILURE
, "bad modulus `%s'", mt
);
409 /* Unfortunately I don't know how to test for a Blum integer */
410 } else if (kfile
|| id
|| ktype
) {
415 /* --- Open the key file --- */
419 if (key_open(&kf
, kfile
, KOPEN_READ
, key_moan
, 0)) {
420 die(EXIT_FAILURE
, "error opening key file `%s': %s",
421 kfile
, strerror(errno
));
424 /* --- Find the key --- */
427 if ((kk
= key_bytag(&kf
, id
)) == 0)
428 die(EXIT_FAILURE
, "key `%s' not found", id
);
432 if ((kk
= key_bytype(&kf
, ktype
)) == 0)
433 die(EXIT_FAILURE
, "no suitable key with type `%s' found", ktype
);
436 /* --- Read the key data --- */
438 if ((kk
->k
.e
& KF_ENCMASK
) != KENC_STRUCT
)
439 die(EXIT_FAILURE
, "key is not structured");
440 if ((kd
= key_structfind(&kk
->k
, "n")) == 0)
441 die(EXIT_FAILURE
, "key has no subkey `n'");
442 if ((kd
->e
& KF_ENCMASK
) != KENC_MP
)
443 die(EXIT_FAILURE
, "incomatible subkey encoding");
444 m
= MP_COPY(kd
->u
.m
);
449 if (bbs_gen(&bp
, bits
, &rand_global
, 0,
450 (flags
& f_progress
) ? pgen_ev
: 0, 0))
451 die(EXIT_FAILURE
, "modulus generation failed");
455 fputs("p = ", stderr
);
456 mp_writefile(bp
.p
, stderr
, 10);
457 fputs("\nq = ", stderr
);
458 mp_writefile(bp
.q
, stderr
, 10);
459 fputs("\nn = ", stderr
);
460 mp_writefile(bp
.n
, stderr
, 10);
468 /* --- Set up a seed --- */
471 x
= mprand(MP_NEW
, mp_bits(m
) - 1, &rand_global
, 1);
474 x
= mp_readstring(MP_NEW
, xt
, &p
, 0);
476 die(EXIT_FAILURE
, "bad modulus `%s'", xt
);
488 /* --- Catacomb's random number generator --- */
490 static grand
*gen_rand(unsigned i
)
492 grand
*r
= rand_create();
495 static struct option opts
[] = {
496 { "key", OPTF_ARGREQ
, 0, 'k' },
497 { "text", OPTF_ARGREQ
, 0, 't' },
498 { "hex", OPTF_ARGREQ
, 0, 'H' },
499 { "noise", 0, 0, 'n' },
503 addopts("k:t:H:n", opts
);
512 octet hash
[RMD160_HASHSZ
];
514 rmd160_hash(&c
, optarg
, strlen(optarg
));
515 rmd160_done(&c
, hash
);
516 r
->ops
->misc(r
, RAND_KEY
, hash
, sizeof(hash
));
519 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, optarg
, strlen(optarg
));
524 unhex(optarg
, &p
, &d
);
526 die(EXIT_FAILURE
, "bad hex key `%s'", optarg
);
527 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, d
.buf
, d
.len
);
530 r
->ops
->misc(r
, RAND_NOISESRC
, &noise_source
);
539 /* --- RC4 output --- */
541 static grand
*gen_rc4(unsigned i
)
546 static struct option opts
[] = {
547 { "key", OPTF_ARGREQ
, 0, 'k' },
548 { "hex", OPTF_ARGREQ
, 0, 'H' },
552 addopts("k:H:", opts
);
561 dstr_ensure(&d
, RMD160_HASHSZ
);
563 rmd160_hash(&c
, optarg
, strlen(optarg
));
564 rmd160_done(&c
, d
.buf
);
565 d
.len
+= RMD160_HASHSZ
;
569 unhex(optarg
, &p
, &d
);
571 die(EXIT_FAILURE
, "bad hex key `%s'", optarg
);
581 rand_getgood(RAND_GLOBAL
, d
.buf
, d
.len
);
583 r
= rc4_rand(d
.buf
, d
.len
);
588 /* --- Output feedback generators --- */
591 E(OFB_DES, DES_KEYSZ, DES_BLKSZ, des_ofbrand), \
592 E(OFB_DES3, DES3_KEYSZ, DES3_BLKSZ, des3_ofbrand), \
593 E(OFB_RC5, RC5_KEYSZ, RC5_BLKSZ, rc5_ofbrand), \
594 E(OFB_BLOWFISH, BLOWFISH_KEYSZ, BLOWFISH_BLKSZ, blowfish_ofbrand), \
595 E(OFB_IDEA, IDEA_KEYSZ, IDEA_BLKSZ, idea_ofbrand)
600 grand
*(*rand
)(const void */
*k*/
, size_t /*sz*/);
602 #define E(c, x, y, z) { x, y, z }
608 #define E(c, x, y, z) c
615 static grand
*gen_ofb(unsigned i
)
621 static struct option opts
[] = {
622 { "key", OPTF_ARGREQ
, 0, 'k' },
623 { "hex", OPTF_ARGREQ
, 0, 'H' },
624 { "iv", OPTF_ARGREQ
, 0, 'i' },
628 addopts("k:H:i:", opts
);
637 dstr_ensure(&d
, RMD160_HASHSZ
);
639 rmd160_hash(&c
, optarg
, strlen(optarg
));
640 rmd160_done(&c
, d
.buf
);
641 d
.len
+= RMD160_HASHSZ
;
645 unhex(optarg
, &p
, &d
);
647 die(EXIT_FAILURE
, "bad hex key `%s'", optarg
);
651 unhex(optarg
, &p
, &iv
);
653 die(EXIT_FAILURE
, "bad hex IV `%s'", optarg
);
661 size_t n
= ofbtab
[i
].keysz
;
666 rand_getgood(RAND_GLOBAL
, d
.buf
, d
.len
);
669 while (d
.len
< ofbtab
[i
].keysz
)
671 if (ofbtab
[i
].keysz
&& d
.len
> ofbtab
[i
].keysz
)
672 d
.len
= ofbtab
[i
].keysz
;
674 r
= ofbtab
[i
].rand(d
.buf
, d
.len
);
676 while (iv
.len
< ofbtab
[i
].blksz
)
678 r
->ops
->misc(r
, GRAND_SEEDBLOCK
, iv
.buf
);
686 /* --- Fibonacci generator --- */
688 static grand
*gen_fib(unsigned i
)
695 static struct option opts
[] = {
696 { "seed", OPTF_ARGREQ
, 0, 's' },
708 s
= strtoul(optarg
, &p
, 0);
710 die(EXIT_FAILURE
, "bad integer `%s'", optarg
);
717 r
= fibrand_create(s
);
719 r
->ops
->misc(r
, GRAND_SEEDRAND
, &rand_global
);
723 /* --- LC generator --- */
725 static grand
*gen_lc(unsigned i
)
731 static struct option opts
[] = {
732 { "seed", OPTF_ARGREQ
, 0, 's' },
744 s
= strtoul(optarg
, &p
, 0);
746 die(EXIT_FAILURE
, "bad integer `%s'", optarg
);
755 s
= rand_global
.ops
->range(&rand_global
, LCRAND_P
);
756 while (s
== LCRAND_FIXEDPT
);
758 return (lcrand_create(s
));
761 /* --- Basic options parser -- can't generate output --- */
763 static grand
*gen_opts(unsigned i
)
770 /*----- Generators table --------------------------------------------------*/
772 static gen generators
[] = {
773 { "fibonacci", gen_fib
, 0,
777 { "des-ofb", gen_ofb
, OFB_DES
,
778 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
779 { "3des-ofb", gen_ofb
, OFB_DES3
,
780 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
781 { "rc5-ofb", gen_ofb
, OFB_RC5
,
782 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
783 { "blowfish-ofb", gen_ofb
, OFB_BLOWFISH
,
784 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
785 { "idea-ofb", gen_ofb
, OFB_IDEA
,
786 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
788 "[-k KEY-PHRASE] [-H HEX-KEY]" },
789 { "rand", gen_rand
, 0,
790 "[-n] [-k KEY-PHRASE] [-t TEXT-BLOCK] [-H HEX-BLOCK]" },
792 "[-gS] [-s SEED] [-m MODULUS] [-b BITS] [-k KEYRING] [-i TAG] [-t TYPE]"
797 static gen optsg
= { "options", gen_opts
, 0,
798 "This message shouldn't be printed." };
800 /*----- Main code ---------------------------------------------------------*/
802 int main(int ac
, char *av
[])
806 unsigned percent
= -1;
809 static char baton
[] = "|/-\\";
812 /* --- Initialize mLib --- */
817 /* --- Set up the main Catacomb generator --- */
819 rand_noisesrc(RAND_GLOBAL
, &noise_source
);
821 /* --- Initialize the options table --- */
823 addopts(sopts
, opts
);
827 /* --- Read the generator out of the first argument --- */
829 if (argc
> 1 && *argv
[1] != '-') {
830 const char *arg
= av
[1];
831 size_t sz
= strlen(arg
);
835 for (gg
= generators
; gg
->name
; gg
++) {
836 if (strncmp(arg
, gg
->name
, sz
) == 0) {
837 if (gg
->name
[sz
] == 0) {
841 die(EXIT_FAILURE
, "ambiguous generator name `%s'", arg
);
847 die(EXIT_FAILURE
, "unknown generator name `%s'", arg
);
852 /* --- Get a generic random number generator --- */
855 if (!r
|| optind
!= ac
- 1) {
861 if (!(flags
& f_file
) && isatty(STDOUT_FILENO
))
862 die(EXIT_FAILURE
, "writing output to a terminal is a bad idea");
865 /* --- Spit out random data --- */
869 if (flags
& f_progress
) {
870 char *errbuf
= xmalloc(BUFSIZ
);
871 setvbuf(stderr
, errbuf
, _IOLBF
, BUFSIZ
);
877 signal(SIGPIPE
, SIG_IGN
);
882 size_t sz
= sizeof(buf
);
884 /* --- Emit a bufferful (or less) of data --- */
890 r
->ops
->fill(r
, buf
, sz
);
891 if (fwrite(buf
, 1, sz
, outfp
) != sz
) {
892 if (flags
& f_progress
)
894 die(EXIT_FAILURE
, "error writing data: %s", strerror(errno
));
898 /* --- Update the display --- */
900 if (flags
& f_progress
) {
908 if (difftime(t
, last
) > 1.0) {
912 fputs(" ] ", stderr
);
914 unsigned pc
= kb
* 100.0 / outsz
;
915 if (pc
> percent
|| percent
> 100 || difftime(t
, last
) > 1.0) {
919 for (; percent
< (pc
& ~1); percent
+= 2)
922 for (; pc
< 100; pc
+= 2)
924 fprintf(stderr
, "] %3i%% ", percent
);
932 while (q
> 8192 && suff
[1]) {
936 fprintf(stderr
, "%4i%c\r[", q
, *suff
);
939 for (pc
= 0; pc
< (percent
& ~1); pc
+= 2)
957 /* --- Terminate the loop --- */
959 if (outsz
&& kb
>= outsz
)
966 if (flags
& f_progress
)
971 /*----- That's all, folks -------------------------------------------------*/