Table for driving key data extraction.
[u/mdw/catacomb] / rspit.c
CommitLineData
ee33470f 1/* -*-c-*-
2 *
052b36d0 3 * $Id: rspit.c,v 1.3 2000/02/12 18:21:03 mdw Exp $
ee33470f 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 $
052b36d0 33 * Revision 1.3 2000/02/12 18:21:03 mdw
34 * Overhaul of key management (again).
35 *
87de7c73 36 * Revision 1.2 1999/12/22 15:59:51 mdw
37 * New prime-search system. Read BBS keys from key files.
38 *
ee33470f 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"
87de7c73 67#include "key.h"
ee33470f 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
89typedef struct gen {
90 const char *name;
91 grand *(*seed)(unsigned /*i*/);
92 unsigned i;
93 const char *help;
94} gen;
95
96static gen generators[];
97
98/*----- Miscellaneous static data -----------------------------------------*/
99
100static FILE *outfp = stdout;
101static size_t outsz = 0;
102
103static int argc;
104static char **argv;
105
106static unsigned flags = 0;
107
108enum {
109 f_progress = 1,
110 f_file = 2
111};
112
113/*----- Help options ------------------------------------------------------*/
114
115static void usage(FILE *fp)
116{
117 pquis(fp, "Usage: $ generator [options]\n");
118}
119
120static void version(FILE *fp)
121{
122 pquis(fp, "$, Catacomb version " VERSION "\n");
123}
124
125static void help(FILE *fp)
126{
127 version(fp);
128 fputc('\n', fp);
129 usage(fp);
130 pquis(fp, "\n\
131Emits a stream of random bytes suitable for, well, all sorts of things.\n\
132The primary objective is to be able to generate streams of input for\n\
133statistical tests, such as Diehard.\n\
134\n\
135Options are specific to the particular generator, although there's a\n\
136common 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
155static 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
175static 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
182static option_v optv = DA_INIT;
183static 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
195static 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
214static 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
289static 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
ee33470f 311static 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;
87de7c73 342 unsigned bits = 1024;
ee33470f 343 mp *m, *x;
344 unsigned show = 0;
87de7c73 345 const char *kfile = 0, *id = 0, *ktype = 0;
ee33470f 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' },
87de7c73 355 { "keyring", OPTF_ARGREQ, 0, 'k' },
356 { "id", OPTF_ARGREQ, 0, 'i' },
357 { "type", OPTF_ARGREQ, 0, 't' },
ee33470f 358 { 0, 0, 0, 0 }
359 };
360
87de7c73 361 addopts("m:gs:b:Sk:i:t:", opts);
ee33470f 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;
87de7c73 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;
ee33470f 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);
87de7c73 407 if (!m || *p || (m->v[0] & 3) != 1)
ee33470f 408 die(EXIT_FAILURE, "bad modulus `%s'", mt);
409 /* Unfortunately I don't know how to test for a Blum integer */
87de7c73 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);
ee33470f 446 } else {
87de7c73 447 bbs_param bp;
ee33470f 448
052b36d0 449 if (bbs_gen(&bp, bits, &rand_global, 0,
450 (flags & f_progress) ? pgen_ev : 0, 0))
87de7c73 451 die(EXIT_FAILURE, "modulus generation failed");
ee33470f 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
ee33470f 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
490static 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
541static 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
597static 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
607enum {
608#define E(c, x, y, z) c
609 OFBTAB
610#undef E
611};
612
613#undef OFBTAB
614
615static 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
688static 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
725static 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
763static grand *gen_opts(unsigned i)
764{
765 while (opt() >= 0)
766 ;
767 return (0);
768}
769
770/*----- Generators table --------------------------------------------------*/
771
772static 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,
87de7c73 792 "[-gS] [-s SEED] [-m MODULUS] [-b BITS] [-k KEYRING] [-i TAG] [-t TYPE]"
793 },
ee33470f 794 { 0, 0, 0, 0 },
795};
796
797static gen optsg = { "options", gen_opts, 0,
798 "This message shouldn't be printed." };
799
800/*----- Main code ---------------------------------------------------------*/
801
802int 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 -------------------------------------------------*/