chiark / gitweb /
New prime-search system. Read BBS keys from key files.
[catacomb] / rspit.c
1 /* -*-c-*-
2  *
3  * $Id: rspit.c,v 1.2 1999/12/22 15:59:51 mdw Exp $
4  *
5  * Spit out random numbers
6  *
7  * (c) 1999 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of Catacomb.
13  *
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.
18  * 
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.
23  * 
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,
27  * MA 02111-1307, USA.
28  */
29
30 /*----- Revision history --------------------------------------------------* 
31  *
32  * $Log: rspit.c,v $
33  * Revision 1.2  1999/12/22 15:59:51  mdw
34  * New prime-search system.  Read BBS keys from key files.
35  *
36  * Revision 1.1  1999/12/10 23:29:13  mdw
37  * Emit random numbers for statistical tests.
38  *
39  */
40
41 /*----- Header files ------------------------------------------------------*/
42
43 #include "config.h"
44
45 #include <errno.h>
46 #include <signal.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <time.h>
51
52 #ifndef PORTABLE
53 #  include <unistd.h>
54 #endif
55
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>
61 #include <mLib/sub.h>
62
63 #include "grand.h"
64 #include "key.h"
65
66 #include "lcrand.h"
67 #include "fibrand.h"
68 #include "rand.h"
69 #include "noise.h"
70
71 #include "bbs.h"
72 #include "mprand.h"
73
74 #include "rc4.h"
75
76 #include "des-ofb.h"
77 #include "des3-ofb.h"
78 #include "rc5-ofb.h"
79 #include "blowfish-ofb.h"
80 #include "idea-ofb.h"
81
82 #include "rmd160.h"
83
84 /*----- Data structures ---------------------------------------------------*/
85
86 typedef struct gen {
87   const char *name;
88   grand *(*seed)(unsigned /*i*/);
89   unsigned i;
90   const char *help;
91 } gen;
92
93 static gen generators[];
94
95 /*----- Miscellaneous static data -----------------------------------------*/
96
97 static FILE *outfp = stdout;
98 static size_t outsz = 0;
99
100 static int argc;
101 static char **argv;
102
103 static unsigned flags = 0;
104
105 enum {
106   f_progress = 1,
107   f_file = 2
108 };
109
110 /*----- Help options ------------------------------------------------------*/
111
112 static void usage(FILE *fp)
113 {
114   pquis(fp, "Usage: $ generator [options]\n");
115 }
116
117 static void version(FILE *fp)
118 {
119   pquis(fp, "$, Catacomb version " VERSION "\n");
120 }
121
122 static void help(FILE *fp)
123 {
124   version(fp);
125   fputc('\n', fp);
126   usage(fp);
127   pquis(fp, "\n\
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\
131 \n\
132 Options are specific to the particular generator, although there's a\n\
133 common core set:\n\
134 \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\
138 \n\
139 -l, --list              Show a list of the supported generators, with\n\
140                         their options.\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\
144 \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\
147 ");
148 }
149
150 /*----- Main options parser -----------------------------------------------*/
151
152 static struct option opts[] = {
153
154   /* --- Standard GNU help options --- */
155
156   { "help",     0,              0,      'h' },
157   { "version",  0,              0,      'v' },
158   { "usage",    0,              0,      'u' },
159
160   /* --- Other useful things --- */
161
162   { "list",     0,              0,      'l' },
163   { "output",   OPTF_ARGREQ,    0,      'o' },
164   { "size",     OPTF_ARGREQ,    0,      'z' },
165   { "progress", 0,              0,      'p' },
166
167   /* --- End of main table --- */
168
169   { 0,          0,              0,      0 }
170 };
171
172 static const char *sopts = "hvu lo:z:p";
173
174 #ifndef OPTION_V
175    DA_DECL(option_v, struct option);
176 #  define OPTION_V
177 #endif
178
179 static option_v optv = DA_INIT;
180 static dstr optd = DSTR_INIT;
181
182 /* --- @addopts@ --- *
183  *
184  * Arguments:   @const char *s@ = pointer to short options
185  *              @struct option *l@ = pointer to long options
186  *
187  * Returns:     ---
188  *
189  * Use:         Adds a collection of options to the table.
190  */
191
192 static void addopts(const char *s, struct option *l)
193 {
194   dstr_puts(&optd, s);
195   if (DA_LEN(&optv))
196     DA_SHRINK(&optv, 1);
197   while (l->name)
198     DA_PUSH(&optv, *l++);
199   DA_PUSH(&optv, *l);
200 }
201
202 /* --- @opt@ --- *
203  *
204  * Arguments:   ---
205  *
206  * Returns:     Next option from argument array.
207  *
208  * Use:         Fetches options, handling the standard ones.
209  */
210
211 static int opt(void)
212 {
213   for (;;) {
214     int i = mdwopt(argc, argv, optd.buf, DA(&optv), 0, 0, 0);
215     switch (i) {
216       case 'h':
217         help(stdout);
218         exit(0);
219       case 'v':
220         version(stdout);
221         exit(0);
222       case 'u':
223         usage(stdout);
224         exit(0);
225       case 'l': {
226         gen *g;
227         puts("Generators supported:");
228         for (g = generators; g->name; g++)
229           printf("  %s %s\n", g->name, g->help);
230         exit(0);
231       } break;
232       case 'o':
233         if (flags & f_file)
234           die(EXIT_FAILURE, "already set an output file");
235         if (strcmp(optarg, "-") == 0)
236           outfp = stdout;
237         else {
238           outfp = fopen(optarg, "w");
239           if (!outfp) {
240             die(EXIT_FAILURE, "couldn't open output file `%s': %s",
241                 strerror(errno));
242           }
243         }
244         flags |= f_file;
245         break;
246       case 'z': {
247         char *p;
248         outsz = strtoul(optarg, &p, 0);
249         if (!outsz)
250           die(EXIT_FAILURE, "bad number `%s'", optarg);
251         switch (*p) {
252           case 'G': case 'g': outsz *= 1024;
253           case 'M': case 'm': outsz *= 1024;
254           case 'K': case 'k': outsz *= 1024;
255           case 0:
256             break;
257           default:
258             die(EXIT_FAILURE, "bad suffix `%s'", p);
259             break;
260         }
261         if (*p && p[1] != 0)
262           die(EXIT_FAILURE, "bad suffix `%s'", p);
263       } break;
264       case 'p':
265         flags |= f_progress;
266         break;
267       default:
268         return (i);
269     }
270   }
271 }
272
273 /*----- Manglers for seed strings -----------------------------------------*/
274
275 /* --- @unhex@ --- *
276  *
277  * Arguments:   @const char *p@ = pointer to input string
278  *              @char **end@ = where the end goes
279  *              @dstr *d@ = output buffer
280  *
281  * Returns:     ---
282  *
283  * Use:         Transforms a hex string into a chunk of binary data.
284  */
285
286 static void unhex(const char *p, char **end, dstr *d)
287 {
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;
293     else x = 0;
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;
297     else y = 0;
298     DPUTC(d, (x << 4) + y);
299     p += 2;
300   }
301   *end = (char *)p;
302 }
303
304 /*----- Generators --------------------------------------------------------*/
305
306 /* --- Blum-Blum-Shub strong generator --- */
307
308 static grand *gen_bbs(unsigned i)
309 {
310   /* --- Default modulus --- *
311    *
312    * The factors of this number are
313    *
314    *  @p = 1229936431484295969649886203367009966370895964206162032259292413@
315    *      @7754313537966036459299022912838407755462506416274551744201653277@
316    *      @313130311731673973886822067@
317    *
318    *  @q = 9798171783943489959487301695884963889684294764514008432498259742@
319    *      @5374320073594018817245784145742769603334292182227671519041431067@
320    *      @61344781426317516045890159@
321    *
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
324    * starting points.
325    *
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.
330    */
331
332   const char *mt =
333   "120511284390135742513572142094334711443073194119732569353820828435640527418092392240366088035509890969913081816369160298961490135716255689660470370755013177656905237112577648090277537209936078171554274553448103698084782669252936352843649980105109850503830397166360721262431179505917248447259735253684659338653";
334
335   /* --- Other things --- */
336
337   grand *r;
338   const char *xt = 0;
339   unsigned bits = 1024;
340   mp *m, *x;
341   unsigned show = 0;
342   const char *kfile = 0, *id = 0, *ktype = 0;
343
344   /* --- Parse options --- */
345
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' },
355     { 0,                0,              0,      0 }
356   };
357
358   addopts("m:gs:b:Sk:i:t:", opts);
359
360   for (;;) {
361     int o = opt();
362     if (o < 0)
363       break;
364     switch (o) {
365       case 'm':
366         mt = optarg;
367         break;
368       case 'g':
369         mt = 0;
370         break;
371       case 's':
372         xt = optarg;
373         break;
374       case 'b':
375         bits = strtoul(optarg, 0, 0);
376         if (bits == 0)
377           die(EXIT_FAILURE, "bad number of bits `%s'", optarg);
378         break;
379       case 'S':
380         show = 1;
381         break;
382       case 'k':
383         kfile = optarg;
384         mt = 0;
385         break;
386       case 'i':
387         id = optarg;
388         mt = 0;
389         break;
390       case 't':
391         ktype = optarg;
392         mt = 0;
393         break;
394       default:
395         return (0);
396     }
397   }
398
399   /* --- Generate a modulus if one is requested --- */
400
401   if (mt) {
402     char *p;
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) {
408     key_file kf;
409     key *kk;
410     key_data *kd;
411
412     /* --- Open the key file --- */
413
414     if (!kfile)
415       kfile = "keyring";
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));
419     }
420
421     /* --- Find the key --- */
422
423     if (id) {
424       if ((kk = key_bytag(&kf, id)) == 0)
425         die(EXIT_FAILURE, "key `%s' not found", id);
426     } else {
427       if (!ktype)
428         ktype = "bbs";
429       if ((kk = key_bytype(&kf, ktype)) == 0)
430         die(EXIT_FAILURE, "no suitable key with type `%s' found", ktype);
431     }
432
433     /* --- Read the key data --- */
434
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);
442     key_close(&kf);
443   } else {
444     mp *p = mprand(MP_NEW, bits / 2, &rand_global, 3);
445     mp *q = mprand(MP_NEW, bits - bits / 2, &rand_global, 3);
446     bbs_param bp;
447
448     if (bbs_gen(&bp, p, q, 0, (flags & f_progress) ? pgen_ev : 0, 0))
449       die(EXIT_FAILURE, "modulus generation failed");
450     m = bp.n;
451
452     if (show) {
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);
459       fputc('\n', stderr);
460     }
461
462     mp_drop(p);
463     mp_drop(q);
464     mp_drop(bp.p);
465     mp_drop(bp.q);
466   }
467
468   /* --- Set up a seed --- */
469
470   if (!xt)
471     x = mprand(MP_NEW, mp_bits(m) - 1, &rand_global, 1);
472   else {
473     char *p;
474     x = mp_readstring(MP_NEW, xt, &p, 0);
475     if (*p)
476       die(EXIT_FAILURE, "bad modulus `%s'", xt);
477   }
478
479   /* --- Right --- */
480
481   r = bbs_rand(m, x);
482
483   mp_drop(m);
484   mp_drop(x);
485   return (r);
486 }
487
488 /* --- Catacomb's random number generator --- */
489
490 static grand *gen_rand(unsigned i)
491 {
492   grand *r = rand_create();
493   dstr d = DSTR_INIT;
494
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' },
500     { 0,                0,              0,      0 }
501   };
502
503   addopts("k:t:H:n", opts);
504
505   for (;;) {
506     int o = opt();
507     if (o < 0)
508       break;
509     switch (o) {
510       case 'k': {
511         rmd160_ctx c;
512         octet hash[RMD160_HASHSZ];
513         rmd160_init(&c);
514         rmd160_hash(&c, optarg, strlen(optarg));
515         rmd160_done(&c, hash);
516         r->ops->misc(r, RAND_KEY, hash, sizeof(hash));
517       } break;
518       case 't':
519         r->ops->misc(r, GRAND_SEEDBLOCK, optarg, strlen(optarg));
520         break;
521       case 'H': {
522         char *p;
523         DRESET(&d);
524         unhex(optarg, &p, &d);
525         if (*p)
526           die(EXIT_FAILURE, "bad hex key `%s'", optarg);
527         r->ops->misc(r, GRAND_SEEDBLOCK, d.buf, d.len);
528       } break;
529       case 'n':
530         r->ops->misc(r, RAND_NOISESRC, &noise_source);
531         break;
532     }
533   }
534
535   dstr_destroy(&d);
536   return (r);
537 }
538
539 /* --- RC4 output --- */
540
541 static grand *gen_rc4(unsigned i)
542 {
543   grand *r;
544   dstr d = DSTR_INIT;
545
546   static struct option opts[] = {
547     { "key",            OPTF_ARGREQ,    0,      'k' },
548     { "hex",            OPTF_ARGREQ,    0,      'H' },
549     { 0,                0,              0,      0 }
550   };
551
552   addopts("k:H:", opts);
553
554   for (;;) {
555     int o = opt();
556     if (o < 0)
557       break;
558     switch (o) {
559       case 'k': {
560         rmd160_ctx c;
561         dstr_ensure(&d, RMD160_HASHSZ);
562         rmd160_init(&c);
563         rmd160_hash(&c, optarg, strlen(optarg));
564         rmd160_done(&c, d.buf);
565         d.len += RMD160_HASHSZ;
566       } break;
567       case 'H': {
568         char *p;
569         unhex(optarg, &p, &d);
570         if (*p)
571           die(EXIT_FAILURE, "bad hex key `%s'", optarg);
572       } break;
573       default:
574         return (0);
575     }
576   }
577
578   if (!d.len) {
579     dstr_ensure(&d, 16);
580     d.len = 16;
581     rand_getgood(RAND_GLOBAL, d.buf, d.len);
582   }
583   r = rc4_rand(d.buf, d.len);
584   dstr_destroy(&d);
585   return (r);
586 }
587
588 /* --- Output feedback generators --- */
589
590 #define OFBTAB                                                          \
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)
596
597 static struct {
598   size_t keysz;
599   size_t blksz;
600   grand *(*rand)(const void */*k*/, size_t /*sz*/);
601 } ofbtab[] = {
602 #define E(c, x, y, z) { x, y, z }
603   OFBTAB
604 #undef E
605 };
606
607 enum {
608 #define E(c, x, y, z) c
609   OFBTAB
610 #undef E
611 };
612
613 #undef OFBTAB
614
615 static grand *gen_ofb(unsigned i)
616 {
617   grand *r;
618   dstr d = DSTR_INIT;
619   dstr iv = DSTR_INIT;
620
621   static struct option opts[] = {
622     { "key",            OPTF_ARGREQ,    0,      'k' },
623     { "hex",            OPTF_ARGREQ,    0,      'H' },
624     { "iv",             OPTF_ARGREQ,    0,      'i' },
625     { 0,                0,              0,      0 }
626   };
627
628   addopts("k:H:i:", opts);
629
630   for (;;) {
631     int o = opt();
632     if (o < 0)
633       break;
634     switch (o) {
635       case 'k': {
636         rmd160_ctx c;
637         dstr_ensure(&d, RMD160_HASHSZ);
638         rmd160_init(&c);
639         rmd160_hash(&c, optarg, strlen(optarg));
640         rmd160_done(&c, d.buf);
641         d.len += RMD160_HASHSZ;
642       } break;
643       case 'H': {
644         char *p;
645         unhex(optarg, &p, &d);
646         if (*p)
647           die(EXIT_FAILURE, "bad hex key `%s'", optarg);
648       } break;
649       case 'i': {
650         char *p;
651         unhex(optarg, &p, &iv);
652         if (*p)
653           die(EXIT_FAILURE, "bad hex IV `%s'", optarg);
654       } break;
655       default:
656         return (0);
657     }
658   }
659
660   if (!d.len) {
661     size_t n = ofbtab[i].keysz;
662     if (!n)
663       n = 16;
664     dstr_ensure(&d, n);
665     d.len = n;
666     rand_getgood(RAND_GLOBAL, d.buf, d.len);
667   }
668
669   while (d.len < ofbtab[i].keysz)
670     DPUTD(&d, &d);
671   if (ofbtab[i].keysz && d.len > ofbtab[i].keysz)
672     d.len = ofbtab[i].keysz;
673
674   r = ofbtab[i].rand(d.buf, d.len);
675   if (iv.len) {
676     while (iv.len < ofbtab[i].blksz)
677       DPUTD(&iv, &iv);
678     r->ops->misc(r, GRAND_SEEDBLOCK, iv.buf);
679   }
680
681   dstr_destroy(&d);
682   dstr_destroy(&iv);
683   return (r);
684 }
685
686 /* --- Fibonacci generator --- */
687
688 static grand *gen_fib(unsigned i)
689 {
690   grand *r;
691   uint32 s = 0;
692   char *p;
693   unsigned set = 0;
694
695   static struct option opts[] = {
696     { "seed",           OPTF_ARGREQ,    0,      's' },
697     { 0,                0,              0,      0 }
698   };
699
700   addopts("s:", opts);
701
702   for (;;) {
703     int o = opt();
704     if (o < 0)
705       break;
706     switch (o) {
707       case 's':
708         s = strtoul(optarg, &p, 0);
709         if (*p)
710           die(EXIT_FAILURE, "bad integer `%s'", optarg);
711         set = 1;
712         break;
713       default:
714         return (0);
715     }
716   }
717   r = fibrand_create(s);
718   if (!set)
719     r->ops->misc(r, GRAND_SEEDRAND, &rand_global);
720   return (r);
721 }
722
723 /* --- LC generator --- */
724
725 static grand *gen_lc(unsigned i)
726 {
727   uint32 s = 0;
728   char *p;
729   unsigned set = 0;
730
731   static struct option opts[] = {
732     { "seed",           OPTF_ARGREQ,    0,      's' },
733     { 0,                0,              0,      0 }
734   };
735
736   addopts("s:", opts);
737
738   for (;;) {
739     int o = opt();
740     if (o < 0)
741       break;
742     switch (o) {
743       case 's':
744         s = strtoul(optarg, &p, 0);
745         if (*p)
746           die(EXIT_FAILURE, "bad integer `%s'", optarg);
747         set = 1;
748         break;
749       default:
750         return (0);
751     }
752   }
753   if (!set) {
754     do
755       s = rand_global.ops->range(&rand_global, LCRAND_P);
756     while (s == LCRAND_FIXEDPT);
757   }
758   return (lcrand_create(s));
759 }
760
761 /* --- Basic options parser -- can't generate output --- */
762
763 static grand *gen_opts(unsigned i)
764 {
765   while (opt() >= 0)
766     ;
767   return (0);
768 }
769
770 /*----- Generators table --------------------------------------------------*/
771
772 static gen generators[] = {
773   { "fibonacci",        gen_fib,        0,
774     "[-s SEED]" },
775   { "lc",               gen_lc,         0,
776     "[-s SEED]" },
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]" },
787   { "rc4",              gen_rc4,        0,
788     "[-k KEY-PHRASE] [-H HEX-KEY]" },
789   { "rand",             gen_rand,       0,
790     "[-n] [-k KEY-PHRASE] [-t TEXT-BLOCK] [-H HEX-BLOCK]" },
791   { "bbs",              gen_bbs,        0,
792     "[-gS] [-s SEED] [-m MODULUS] [-b BITS] [-k KEYRING] [-i TAG] [-t TYPE]"
793   },
794   { 0,                  0,              0, 0 },
795 };
796
797 static gen optsg = { "options", gen_opts, 0,
798                      "This message shouldn't be printed." };
799
800 /*----- Main code ---------------------------------------------------------*/
801
802 int main(int ac, char *av[])
803 {
804   gen *g = &optsg;
805   grand *r;
806   unsigned percent = -1;
807   size_t kb = 0;
808   time_t last;
809   static char baton[] = "|/-\\";
810   char *bp;
811
812   /* --- Initialize mLib --- */
813
814   ego(av[0]);
815   sub_init();
816
817   /* --- Set up the main Catacomb generator --- */
818
819   rand_noisesrc(RAND_GLOBAL, &noise_source);
820
821   /* --- Initialize the options table --- */
822
823   addopts(sopts, opts);
824   argc = ac;
825   argv = av;
826
827   /* --- Read the generator out of the first argument --- */
828
829   if (argc > 1 && *argv[1] != '-') {
830     const char *arg = av[1];
831     size_t sz = strlen(arg);
832     gen *gg;
833
834     g = 0;
835     for (gg = generators; gg->name; gg++) {
836       if (strncmp(arg, gg->name, sz) == 0) {
837         if (gg->name[sz] == 0) {
838           g = gg;
839           break;
840         } else if (g)
841           die(EXIT_FAILURE, "ambiguous generator name `%s'", arg);
842         else
843           g = gg;
844       }
845     }
846     if (!g)
847       die(EXIT_FAILURE, "unknown generator name `%s'", arg);
848     argc--;
849     argv++;
850   }
851
852   /* --- Get a generic random number generator --- */
853
854   r = g->seed(g->i);
855   if (!r || optind != ac - 1) {
856     usage(stderr);
857     exit(EXIT_FAILURE);
858   }
859
860 #ifndef PORTABLE
861   if (!(flags & f_file) && isatty(STDOUT_FILENO))
862     die(EXIT_FAILURE, "writing output to a terminal is a bad idea");
863 #endif
864
865   /* --- Spit out random data --- */
866
867   last = time(0);
868   bp = baton;
869   if (flags & f_progress) {
870     char *errbuf = xmalloc(BUFSIZ);
871     setvbuf(stderr, errbuf, _IOLBF, BUFSIZ);
872     fputc('[', stderr);
873     fflush(stderr);
874   }
875
876 #ifdef SIGPIPE
877   signal(SIGPIPE, SIG_IGN);
878 #endif
879
880   for (;;) {
881     octet buf[BUFSIZ];
882     size_t sz = sizeof(buf);
883
884     /* --- Emit a bufferful (or less) of data --- */
885
886     if (outsz) {
887       if (sz > outsz - kb)
888         sz = outsz - kb;
889     }
890     r->ops->fill(r, buf, sz);
891     if (fwrite(buf, 1, sz, outfp) != sz) {
892       if (flags & f_progress)
893         fputc('\n', stderr);
894       die(EXIT_FAILURE, "error writing data: %s", strerror(errno));
895     }
896     kb += sz;
897
898     /* --- Update the display --- */
899
900     if (flags & f_progress) {
901       time_t t = time(0);
902       unsigned up = 0;
903
904       if (percent > 100)
905         up = 1;
906
907       if (!outsz) {
908         if (difftime(t, last) > 1.0) {
909           up = 1;
910         }
911         if (up)
912           fputs(" ] ", stderr);
913       } else {
914         unsigned pc = kb * 100.0 / outsz;
915         if (pc > percent || percent > 100 || difftime(t, last) > 1.0) {
916           if (percent > 100)
917             percent = 0;
918           percent &= ~1;
919           for (; percent < (pc & ~1); percent += 2)
920             putc('.', stderr);
921           percent = pc;
922           for (; pc < 100; pc += 2)
923             putc(' ', stderr);
924           fprintf(stderr, "] %3i%% ", percent);
925           up = 1;
926         }
927       }
928
929       if (up) {
930         size_t q = kb;
931         char *suff = " KMG";
932         while (q > 8192 && suff[1]) {
933           q >>= 10;
934           suff++;
935         }
936         fprintf(stderr, "%4i%c\r[", q, *suff);
937         if (outsz) {
938           unsigned pc;
939           for (pc = 0; pc < (percent & ~1); pc += 2)
940             putc('.', stderr);
941         }
942         last = t;
943       }
944
945       if (percent > 100)
946         percent = 0;
947
948       if (percent < 100) {
949         putc(*bp++, stderr);
950         putc('\b', stderr);
951         if (!*bp)
952           bp = baton;
953       }
954       fflush(stderr);
955     }
956
957     /* --- Terminate the loop --- */
958
959     if (outsz && kb >= outsz)
960       break;
961   }
962
963   /* --- Done --- */
964
965   r->ops->destroy(r);
966   if (flags & f_progress)
967     fputc('\n', stderr);
968   return (0);
969 }
970
971 /*----- That's all, folks -------------------------------------------------*/