3 * $Id: rspit.c,v 1.2 1999/12/22 15:59:51 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.2 1999/12/22 15:59:51 mdw
34 * New prime-search system. Read BBS keys from key files.
36 * Revision 1.1 1999/12/10 23:29:13 mdw
37 * Emit random numbers for statistical tests.
41 /*----- Header files ------------------------------------------------------*/
56 #include <mLib/darray.h>
57 #include <mLib/dstr.h>
58 #include <mLib/mdwopt.h>
59 #include <mLib/quis.h>
60 #include <mLib/report.h>
79 #include "blowfish-ofb.h"
84 /*----- Data structures ---------------------------------------------------*/
88 grand
*(*seed
)(unsigned /*i*/);
93 static gen generators
[];
95 /*----- Miscellaneous static data -----------------------------------------*/
97 static FILE *outfp
= stdout
;
98 static size_t outsz
= 0;
103 static unsigned flags
= 0;
110 /*----- Help options ------------------------------------------------------*/
112 static void usage(FILE *fp
)
114 pquis(fp
, "Usage: $ generator [options]\n");
117 static void version(FILE *fp
)
119 pquis(fp
, "$, Catacomb version " VERSION
"\n");
122 static void help(FILE *fp
)
128 Emits a stream of random bytes suitable for, well, all sorts of things.\n\
129 The primary objective is to be able to generate streams of input for\n\
130 statistical tests, such as Diehard.\n\
132 Options are specific to the particular generator, although there's a\n\
135 -h, --help Display this help message.\n\
136 -v, --version Display the program's version number.\n\
137 -u, --usage Display a useless usage message.\n\
139 -l, --list Show a list of the supported generators, with\n\
141 -o, --output FILE Write output to FILE, not stdout.\n\
142 -z, --size SIZE Emit SIZE bytes, not an unlimited number.\n\
143 -p, --progress Show a little progress meter (on stderr).\n\
145 (A SIZE may be followed by `g' for gigabytes, `m' for megabytes, or\n\
146 `k' for kilobytes. If unqualified, an amount in bytes is assumed.)\n\
150 /*----- Main options parser -----------------------------------------------*/
152 static struct option opts
[] = {
154 /* --- Standard GNU help options --- */
156 { "help", 0, 0, 'h' },
157 { "version", 0, 0, 'v' },
158 { "usage", 0, 0, 'u' },
160 /* --- Other useful things --- */
162 { "list", 0, 0, 'l' },
163 { "output", OPTF_ARGREQ
, 0, 'o' },
164 { "size", OPTF_ARGREQ
, 0, 'z' },
165 { "progress", 0, 0, 'p' },
167 /* --- End of main table --- */
172 static const char *sopts
= "hvu lo:z:p";
175 DA_DECL(option_v
, struct option
);
179 static option_v optv
= DA_INIT
;
180 static dstr optd
= DSTR_INIT
;
182 /* --- @addopts@ --- *
184 * Arguments: @const char *s@ = pointer to short options
185 * @struct option *l@ = pointer to long options
189 * Use: Adds a collection of options to the table.
192 static void addopts(const char *s
, struct option
*l
)
198 DA_PUSH(&optv
, *l
++);
206 * Returns: Next option from argument array.
208 * Use: Fetches options, handling the standard ones.
214 int i
= mdwopt(argc
, argv
, optd
.buf
, DA(&optv
), 0, 0, 0);
227 puts("Generators supported:");
228 for (g
= generators
; g
->name
; g
++)
229 printf(" %s %s\n", g
->name
, g
->help
);
234 die(EXIT_FAILURE
, "already set an output file");
235 if (strcmp(optarg
, "-") == 0)
238 outfp
= fopen(optarg
, "w");
240 die(EXIT_FAILURE
, "couldn't open output file `%s': %s",
248 outsz
= strtoul(optarg
, &p
, 0);
250 die(EXIT_FAILURE
, "bad number `%s'", optarg
);
252 case 'G': case 'g': outsz
*= 1024;
253 case 'M': case 'm': outsz
*= 1024;
254 case 'K': case 'k': outsz
*= 1024;
258 die(EXIT_FAILURE
, "bad suffix `%s'", p
);
262 die(EXIT_FAILURE
, "bad suffix `%s'", p
);
273 /*----- Manglers for seed strings -----------------------------------------*/
277 * Arguments: @const char *p@ = pointer to input string
278 * @char **end@ = where the end goes
279 * @dstr *d@ = output buffer
283 * Use: Transforms a hex string into a chunk of binary data.
286 static void unhex(const char *p
, char **end
, dstr
*d
)
288 while (p
[0] && p
[1]) {
289 int x
= p
[0], y
= p
[1];
290 if ('0' <= x
&& x
<= '9') x
-= '0';
291 else if ('A' <= x
&& x
<= 'F') x
-= 'A' - 10;
292 else if ('a' <= x
&& x
<= 'f') x
-= 'a' - 10;
294 if ('0' <= y
&& y
<= '9') y
-= '0';
295 else if ('A' <= y
&& y
<= 'F') y
-= 'A' - 10;
296 else if ('a' <= y
&& y
<= 'f') y
-= 'a' - 10;
298 DPUTC(d
, (x
<< 4) + y
);
304 /*----- Generators --------------------------------------------------------*/
306 /* --- Blum-Blum-Shub strong generator --- */
308 static grand
*gen_bbs(unsigned i
)
310 /* --- Default modulus --- *
312 * The factors of this number are
314 * @p = 1229936431484295969649886203367009966370895964206162032259292413@
315 * @7754313537966036459299022912838407755462506416274551744201653277@
316 * @313130311731673973886822067@
318 * @q = 9798171783943489959487301695884963889684294764514008432498259742@
319 * @5374320073594018817245784145742769603334292182227671519041431067@
320 * @61344781426317516045890159@
322 * Both %$p$% and %$q$% are prime; %$(p - 1)/2%$ and %$(q - 1)/2$% have no
323 * common factors. They were found using this program, with random
326 * I hope that, by publishing these factors, I'll dissuade people from
327 * actually using this modulus in attempt to actually attain real
328 * security. The program is quite quick at finding Blum numbers, so
329 * there's no excuse for not generating your own.
333 "120511284390135742513572142094334711443073194119732569353820828435640527418092392240366088035509890969913081816369160298961490135716255689660470370755013177656905237112577648090277537209936078171554274553448103698084782669252936352843649980105109850503830397166360721262431179505917248447259735253684659338653";
335 /* --- Other things --- */
339 unsigned bits
= 1024;
342 const char *kfile
= 0, *id
= 0, *ktype
= 0;
344 /* --- Parse options --- */
346 static struct option opts
[] = {
347 { "modulus", OPTF_ARGREQ
, 0, 'm' },
348 { "generate", 0, 0, 'g' },
349 { "seed", OPTF_ARGREQ
, 0, 's' },
350 { "bits", OPTF_ARGREQ
, 0, 'b' },
351 { "show", 0, 0, 'S' },
352 { "keyring", OPTF_ARGREQ
, 0, 'k' },
353 { "id", OPTF_ARGREQ
, 0, 'i' },
354 { "type", OPTF_ARGREQ
, 0, 't' },
358 addopts("m:gs:b:Sk:i:t:", opts
);
375 bits
= strtoul(optarg
, 0, 0);
377 die(EXIT_FAILURE
, "bad number of bits `%s'", optarg
);
399 /* --- Generate a modulus if one is requested --- */
403 m
= mp_readstring(MP_NEW
, mt
, &p
, 0);
404 if (!m
|| *p
|| (m
->v
[0] & 3) != 1)
405 die(EXIT_FAILURE
, "bad modulus `%s'", mt
);
406 /* Unfortunately I don't know how to test for a Blum integer */
407 } else if (kfile
|| id
|| ktype
) {
412 /* --- Open the key file --- */
416 if (key_open(&kf
, kfile
, KOPEN_READ
, key_moan
, 0)) {
417 die(EXIT_FAILURE
, "error opening key file `%s': %s",
418 kfile
, strerror(errno
));
421 /* --- Find the key --- */
424 if ((kk
= key_bytag(&kf
, id
)) == 0)
425 die(EXIT_FAILURE
, "key `%s' not found", id
);
429 if ((kk
= key_bytype(&kf
, ktype
)) == 0)
430 die(EXIT_FAILURE
, "no suitable key with type `%s' found", ktype
);
433 /* --- Read the key data --- */
435 if ((kk
->k
.e
& KF_ENCMASK
) != KENC_STRUCT
)
436 die(EXIT_FAILURE
, "key is not structured");
437 if ((kd
= key_structfind(&kk
->k
, "n")) == 0)
438 die(EXIT_FAILURE
, "key has no subkey `n'");
439 if ((kd
->e
& KF_ENCMASK
) != KENC_MP
)
440 die(EXIT_FAILURE
, "incomatible subkey encoding");
441 m
= MP_COPY(kd
->u
.m
);
444 mp
*p
= mprand(MP_NEW
, bits
/ 2, &rand_global
, 3);
445 mp
*q
= mprand(MP_NEW
, bits
- bits
/ 2, &rand_global
, 3);
448 if (bbs_gen(&bp
, p
, q
, 0, (flags
& f_progress
) ? pgen_ev
: 0, 0))
449 die(EXIT_FAILURE
, "modulus generation failed");
453 fputs("p = ", stderr
);
454 mp_writefile(bp
.p
, stderr
, 10);
455 fputs("\nq = ", stderr
);
456 mp_writefile(bp
.q
, stderr
, 10);
457 fputs("\nn = ", stderr
);
458 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 -------------------------------------------------*/