Only produce initialization message if verbose.
[u/mdw/catacomb] / rspit.c
CommitLineData
ee33470f 1/* -*-c-*-
2 *
87de7c73 3 * $Id: rspit.c,v 1.2 1999/12/22 15:59:51 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 $
87de7c73 33 * Revision 1.2 1999/12/22 15:59:51 mdw
34 * New prime-search system. Read BBS keys from key files.
35 *
ee33470f 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"
87de7c73 64#include "key.h"
ee33470f 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
86typedef struct gen {
87 const char *name;
88 grand *(*seed)(unsigned /*i*/);
89 unsigned i;
90 const char *help;
91} gen;
92
93static gen generators[];
94
95/*----- Miscellaneous static data -----------------------------------------*/
96
97static FILE *outfp = stdout;
98static size_t outsz = 0;
99
100static int argc;
101static char **argv;
102
103static unsigned flags = 0;
104
105enum {
106 f_progress = 1,
107 f_file = 2
108};
109
110/*----- Help options ------------------------------------------------------*/
111
112static void usage(FILE *fp)
113{
114 pquis(fp, "Usage: $ generator [options]\n");
115}
116
117static void version(FILE *fp)
118{
119 pquis(fp, "$, Catacomb version " VERSION "\n");
120}
121
122static void help(FILE *fp)
123{
124 version(fp);
125 fputc('\n', fp);
126 usage(fp);
127 pquis(fp, "\n\
128Emits a stream of random bytes suitable for, well, all sorts of things.\n\
129The primary objective is to be able to generate streams of input for\n\
130statistical tests, such as Diehard.\n\
131\n\
132Options are specific to the particular generator, although there's a\n\
133common 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
152static 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
172static 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
179static option_v optv = DA_INIT;
180static 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
192static 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
211static 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
286static 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
ee33470f 308static 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;
87de7c73 339 unsigned bits = 1024;
ee33470f 340 mp *m, *x;
341 unsigned show = 0;
87de7c73 342 const char *kfile = 0, *id = 0, *ktype = 0;
ee33470f 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' },
87de7c73 352 { "keyring", OPTF_ARGREQ, 0, 'k' },
353 { "id", OPTF_ARGREQ, 0, 'i' },
354 { "type", OPTF_ARGREQ, 0, 't' },
ee33470f 355 { 0, 0, 0, 0 }
356 };
357
87de7c73 358 addopts("m:gs:b:Sk:i:t:", opts);
ee33470f 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;
87de7c73 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;
ee33470f 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);
87de7c73 404 if (!m || *p || (m->v[0] & 3) != 1)
ee33470f 405 die(EXIT_FAILURE, "bad modulus `%s'", mt);
406 /* Unfortunately I don't know how to test for a Blum integer */
87de7c73 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);
ee33470f 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);
87de7c73 446 bbs_param bp;
ee33470f 447
87de7c73 448 if (bbs_gen(&bp, p, q, 0, (flags & f_progress) ? pgen_ev : 0, 0))
449 die(EXIT_FAILURE, "modulus generation failed");
ee33470f 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
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 -------------------------------------------------*/