chiark / gitweb /
Overhaul of key management (again).
[catacomb] / rspit.c
1 /* -*-c-*-
2  *
3  * $Id: rspit.c,v 1.3 2000/02/12 18:21:03 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.3  2000/02/12 18:21:03  mdw
34  * Overhaul of key management (again).
35  *
36  * Revision 1.2  1999/12/22 15:59:51  mdw
37  * New prime-search system.  Read BBS keys from key files.
38  *
39  * Revision 1.1  1999/12/10 23:29:13  mdw
40  * Emit random numbers for statistical tests.
41  *
42  */
43
44 /*----- Header files ------------------------------------------------------*/
45
46 #include "config.h"
47
48 #include <errno.h>
49 #include <signal.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <time.h>
54
55 #ifndef PORTABLE
56 #  include <unistd.h>
57 #endif
58
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>
64 #include <mLib/sub.h>
65
66 #include "grand.h"
67 #include "key.h"
68
69 #include "lcrand.h"
70 #include "fibrand.h"
71 #include "rand.h"
72 #include "noise.h"
73
74 #include "bbs.h"
75 #include "mprand.h"
76
77 #include "rc4.h"
78
79 #include "des-ofb.h"
80 #include "des3-ofb.h"
81 #include "rc5-ofb.h"
82 #include "blowfish-ofb.h"
83 #include "idea-ofb.h"
84
85 #include "rmd160.h"
86
87 /*----- Data structures ---------------------------------------------------*/
88
89 typedef struct gen {
90   const char *name;
91   grand *(*seed)(unsigned /*i*/);
92   unsigned i;
93   const char *help;
94 } gen;
95
96 static gen generators[];
97
98 /*----- Miscellaneous static data -----------------------------------------*/
99
100 static FILE *outfp = stdout;
101 static size_t outsz = 0;
102
103 static int argc;
104 static char **argv;
105
106 static unsigned flags = 0;
107
108 enum {
109   f_progress = 1,
110   f_file = 2
111 };
112
113 /*----- Help options ------------------------------------------------------*/
114
115 static void usage(FILE *fp)
116 {
117   pquis(fp, "Usage: $ generator [options]\n");
118 }
119
120 static void version(FILE *fp)
121 {
122   pquis(fp, "$, Catacomb version " VERSION "\n");
123 }
124
125 static void help(FILE *fp)
126 {
127   version(fp);
128   fputc('\n', fp);
129   usage(fp);
130   pquis(fp, "\n\
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\
134 \n\
135 Options are specific to the particular generator, although there's a\n\
136 common core set:\n\
137 \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\
141 \n\
142 -l, --list              Show a list of the supported generators, with\n\
143                         their options.\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\
147 \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\
150 ");
151 }
152
153 /*----- Main options parser -----------------------------------------------*/
154
155 static struct option opts[] = {
156
157   /* --- Standard GNU help options --- */
158
159   { "help",     0,              0,      'h' },
160   { "version",  0,              0,      'v' },
161   { "usage",    0,              0,      'u' },
162
163   /* --- Other useful things --- */
164
165   { "list",     0,              0,      'l' },
166   { "output",   OPTF_ARGREQ,    0,      'o' },
167   { "size",     OPTF_ARGREQ,    0,      'z' },
168   { "progress", 0,              0,      'p' },
169
170   /* --- End of main table --- */
171
172   { 0,          0,              0,      0 }
173 };
174
175 static const char *sopts = "hvu lo:z:p";
176
177 #ifndef OPTION_V
178    DA_DECL(option_v, struct option);
179 #  define OPTION_V
180 #endif
181
182 static option_v optv = DA_INIT;
183 static dstr optd = DSTR_INIT;
184
185 /* --- @addopts@ --- *
186  *
187  * Arguments:   @const char *s@ = pointer to short options
188  *              @struct option *l@ = pointer to long options
189  *
190  * Returns:     ---
191  *
192  * Use:         Adds a collection of options to the table.
193  */
194
195 static void addopts(const char *s, struct option *l)
196 {
197   dstr_puts(&optd, s);
198   if (DA_LEN(&optv))
199     DA_SHRINK(&optv, 1);
200   while (l->name)
201     DA_PUSH(&optv, *l++);
202   DA_PUSH(&optv, *l);
203 }
204
205 /* --- @opt@ --- *
206  *
207  * Arguments:   ---
208  *
209  * Returns:     Next option from argument array.
210  *
211  * Use:         Fetches options, handling the standard ones.
212  */
213
214 static int opt(void)
215 {
216   for (;;) {
217     int i = mdwopt(argc, argv, optd.buf, DA(&optv), 0, 0, 0);
218     switch (i) {
219       case 'h':
220         help(stdout);
221         exit(0);
222       case 'v':
223         version(stdout);
224         exit(0);
225       case 'u':
226         usage(stdout);
227         exit(0);
228       case 'l': {
229         gen *g;
230         puts("Generators supported:");
231         for (g = generators; g->name; g++)
232           printf("  %s %s\n", g->name, g->help);
233         exit(0);
234       } break;
235       case 'o':
236         if (flags & f_file)
237           die(EXIT_FAILURE, "already set an output file");
238         if (strcmp(optarg, "-") == 0)
239           outfp = stdout;
240         else {
241           outfp = fopen(optarg, "w");
242           if (!outfp) {
243             die(EXIT_FAILURE, "couldn't open output file `%s': %s",
244                 strerror(errno));
245           }
246         }
247         flags |= f_file;
248         break;
249       case 'z': {
250         char *p;
251         outsz = strtoul(optarg, &p, 0);
252         if (!outsz)
253           die(EXIT_FAILURE, "bad number `%s'", optarg);
254         switch (*p) {
255           case 'G': case 'g': outsz *= 1024;
256           case 'M': case 'm': outsz *= 1024;
257           case 'K': case 'k': outsz *= 1024;
258           case 0:
259             break;
260           default:
261             die(EXIT_FAILURE, "bad suffix `%s'", p);
262             break;
263         }
264         if (*p && p[1] != 0)
265           die(EXIT_FAILURE, "bad suffix `%s'", p);
266       } break;
267       case 'p':
268         flags |= f_progress;
269         break;
270       default:
271         return (i);
272     }
273   }
274 }
275
276 /*----- Manglers for seed strings -----------------------------------------*/
277
278 /* --- @unhex@ --- *
279  *
280  * Arguments:   @const char *p@ = pointer to input string
281  *              @char **end@ = where the end goes
282  *              @dstr *d@ = output buffer
283  *
284  * Returns:     ---
285  *
286  * Use:         Transforms a hex string into a chunk of binary data.
287  */
288
289 static void unhex(const char *p, char **end, dstr *d)
290 {
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;
296     else x = 0;
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;
300     else y = 0;
301     DPUTC(d, (x << 4) + y);
302     p += 2;
303   }
304   *end = (char *)p;
305 }
306
307 /*----- Generators --------------------------------------------------------*/
308
309 /* --- Blum-Blum-Shub strong generator --- */
310
311 static grand *gen_bbs(unsigned i)
312 {
313   /* --- Default modulus --- *
314    *
315    * The factors of this number are
316    *
317    *  @p = 1229936431484295969649886203367009966370895964206162032259292413@
318    *      @7754313537966036459299022912838407755462506416274551744201653277@
319    *      @313130311731673973886822067@
320    *
321    *  @q = 9798171783943489959487301695884963889684294764514008432498259742@
322    *      @5374320073594018817245784145742769603334292182227671519041431067@
323    *      @61344781426317516045890159@
324    *
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
327    * starting points.
328    *
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.
333    */
334
335   const char *mt =
336   "120511284390135742513572142094334711443073194119732569353820828435640527418092392240366088035509890969913081816369160298961490135716255689660470370755013177656905237112577648090277537209936078171554274553448103698084782669252936352843649980105109850503830397166360721262431179505917248447259735253684659338653";
337
338   /* --- Other things --- */
339
340   grand *r;
341   const char *xt = 0;
342   unsigned bits = 1024;
343   mp *m, *x;
344   unsigned show = 0;
345   const char *kfile = 0, *id = 0, *ktype = 0;
346
347   /* --- Parse options --- */
348
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' },
358     { 0,                0,              0,      0 }
359   };
360
361   addopts("m:gs:b:Sk:i:t:", opts);
362
363   for (;;) {
364     int o = opt();
365     if (o < 0)
366       break;
367     switch (o) {
368       case 'm':
369         mt = optarg;
370         break;
371       case 'g':
372         mt = 0;
373         break;
374       case 's':
375         xt = optarg;
376         break;
377       case 'b':
378         bits = strtoul(optarg, 0, 0);
379         if (bits == 0)
380           die(EXIT_FAILURE, "bad number of bits `%s'", optarg);
381         break;
382       case 'S':
383         show = 1;
384         break;
385       case 'k':
386         kfile = optarg;
387         mt = 0;
388         break;
389       case 'i':
390         id = optarg;
391         mt = 0;
392         break;
393       case 't':
394         ktype = optarg;
395         mt = 0;
396         break;
397       default:
398         return (0);
399     }
400   }
401
402   /* --- Generate a modulus if one is requested --- */
403
404   if (mt) {
405     char *p;
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) {
411     key_file kf;
412     key *kk;
413     key_data *kd;
414
415     /* --- Open the key file --- */
416
417     if (!kfile)
418       kfile = "keyring";
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));
422     }
423
424     /* --- Find the key --- */
425
426     if (id) {
427       if ((kk = key_bytag(&kf, id)) == 0)
428         die(EXIT_FAILURE, "key `%s' not found", id);
429     } else {
430       if (!ktype)
431         ktype = "bbs";
432       if ((kk = key_bytype(&kf, ktype)) == 0)
433         die(EXIT_FAILURE, "no suitable key with type `%s' found", ktype);
434     }
435
436     /* --- Read the key data --- */
437
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);
445     key_close(&kf);
446   } else {
447     bbs_param bp;
448
449     if (bbs_gen(&bp, bits, &rand_global, 0,
450                 (flags & f_progress) ? pgen_ev : 0, 0))
451       die(EXIT_FAILURE, "modulus generation failed");
452     m = bp.n;
453
454     if (show) {
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);
461       fputc('\n', stderr);
462     }
463
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 -------------------------------------------------*/