progs/rspit.c: Handle large requested output.
[catacomb] / progs / rspit.c
CommitLineData
ee33470f 1/* -*-c-*-
2 *
ee33470f 3 * Spit out random numbers
4 *
5 * (c) 1999 Straylight/Edgeware
6 */
7
45c0fd36 8/*----- Licensing notice --------------------------------------------------*
ee33470f 9 *
10 * This file is part of Catacomb.
11 *
12 * Catacomb is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Library General Public License as
14 * published by the Free Software Foundation; either version 2 of the
15 * License, or (at your option) any later version.
45c0fd36 16 *
ee33470f 17 * Catacomb is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Library General Public License for more details.
45c0fd36 21 *
ee33470f 22 * You should have received a copy of the GNU Library General Public
23 * License along with Catacomb; if not, write to the Free
24 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25 * MA 02111-1307, USA.
26 */
27
ee33470f 28/*----- Header files ------------------------------------------------------*/
29
30#include "config.h"
31
bb78535e 32#include <assert.h>
ee33470f 33#include <errno.h>
bb78535e 34#include <math.h>
ee33470f 35#include <signal.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <time.h>
40
41#ifndef PORTABLE
42# include <unistd.h>
43#endif
44
45#include <mLib/darray.h>
46#include <mLib/dstr.h>
47#include <mLib/mdwopt.h>
48#include <mLib/quis.h>
49#include <mLib/report.h>
50#include <mLib/sub.h>
51
bafe8a7b
MW
52#include "mp.h"
53#include "mpint.h"
54#include "mplimits.h"
55#include "mptext.h"
56
bb78535e 57#include "fipstest.h"
ee33470f 58#include "grand.h"
bb78535e 59#include "maurer.h"
87de7c73 60#include "key.h"
ee33470f 61
62#include "lcrand.h"
63#include "fibrand.h"
64#include "rand.h"
65#include "noise.h"
66
67#include "bbs.h"
68#include "mprand.h"
69
70#include "rc4.h"
bb78535e 71#include "seal.h"
ee33470f 72
73#include "des-ofb.h"
74#include "des3-ofb.h"
bb78535e 75#include "rc2-ofb.h"
ee33470f 76#include "rc5-ofb.h"
b17f4ed5 77#include "mars-ofb.h"
265fb162 78#include "skipjack-ofb.h"
79#include "tea-ofb.h"
80#include "xtea-ofb.h"
ee33470f 81#include "blowfish-ofb.h"
bb78535e 82#include "twofish-ofb.h"
ee33470f 83#include "idea-ofb.h"
bb78535e 84#include "cast128-ofb.h"
85#include "cast256-ofb.h"
0ba18b90 86#include "noekeon-ofb.h"
bb78535e 87#include "rijndael-ofb.h"
2e8eb64a 88#include "rijndael192-ofb.h"
89#include "rijndael256-ofb.h"
b17f4ed5 90#include "safer-ofb.h"
91#include "safersk-ofb.h"
265fb162 92#include "square-ofb.h"
bb78535e 93#include "serpent-ofb.h"
94
95#include "des-counter.h"
96#include "des3-counter.h"
97#include "rc2-counter.h"
98#include "rc5-counter.h"
b17f4ed5 99#include "mars-counter.h"
265fb162 100#include "skipjack-counter.h"
101#include "tea-counter.h"
102#include "xtea-counter.h"
bb78535e 103#include "blowfish-counter.h"
104#include "twofish-counter.h"
105#include "idea-counter.h"
106#include "cast128-counter.h"
107#include "cast256-counter.h"
0ba18b90 108#include "noekeon-counter.h"
bb78535e 109#include "rijndael-counter.h"
2e8eb64a 110#include "rijndael192-counter.h"
111#include "rijndael256-counter.h"
b17f4ed5 112#include "safer-counter.h"
113#include "safersk-counter.h"
265fb162 114#include "square-counter.h"
bb78535e 115#include "serpent-counter.h"
116
d3187d77 117#include "md2-mgf.h"
bb78535e 118#include "md4-mgf.h"
119#include "md5-mgf.h"
120#include "sha-mgf.h"
265fb162 121#include "tiger-mgf.h"
122#include "rmd128-mgf.h"
bb78535e 123#include "rmd160-mgf.h"
265fb162 124#include "rmd256-mgf.h"
125#include "rmd320-mgf.h"
ee33470f 126
127#include "rmd160.h"
128
129/*----- Data structures ---------------------------------------------------*/
130
131typedef struct gen {
132 const char *name;
133 grand *(*seed)(unsigned /*i*/);
134 unsigned i;
135 const char *help;
136} gen;
137
f57c6d85 138extern gen generators[];
ee33470f 139
bb78535e 140#define CIPHERS \
141 E(DES, des) \
142 E(DES3, des3) \
143 E(RC2, rc2) \
144 E(RC5, rc5) \
b17f4ed5 145 E(MARS, mars) \
265fb162 146 E(SKIPJACK, skipjack) \
147 E(TEA, tea) \
148 E(XTEA, xtea) \
bb78535e 149 E(BLOWFISH, blowfish) \
150 E(TWOFISH, twofish) \
151 E(IDEA, idea) \
152 E(CAST128, cast128) \
153 E(CAST256, cast256) \
265fb162 154 E(SQUARE, square) \
b17f4ed5 155 E(SAFER, safer) \
156 E(SAFERSK, safersk) \
0ba18b90 157 E(NOEKEON, noekeon) \
bb78535e 158 E(RIJNDAEL, rijndael) \
2e8eb64a 159 E(RIJNDAEL192, rijndael192) \
160 E(RIJNDAEL256, rijndael256) \
bb78535e 161 E(SERPENT, serpent)
162
163#define HASHES \
d3187d77 164 E(MD2, md2) \
bb78535e 165 E(MD4, md4) \
166 E(MD5, md5) \
167 E(SHA, sha) \
265fb162 168 E(TIGER, tiger) \
169 E(RMD128, rmd128) \
170 E(RMD160, rmd160) \
171 E(RMD256, rmd256) \
172 E(RMD320, rmd320)
bb78535e 173
174#define E(PRE, pre) CIPHER_##PRE,
175enum { CIPHERS CIPHER__bogus };
176#undef E
177
178#define E(PRE, pre) HASH_##PRE,
179enum { HASHES HASH__bogus };
180#undef E
181
cbef819a 182static const struct {
bb78535e 183 const octet *keysz;
184 size_t blksz;
185 grand *(*ofb)(const void */*k*/, size_t /*sz*/);
186 grand *(*counter)(const void */*k*/, size_t /*sz*/);
187} ciphertab[] = {
188#define E(PRE, pre) \
189 { pre##_keysz, PRE##_BLKSZ, pre##_ofbrand, pre##_counterrand },
190 CIPHERS
191#undef E
192};
193
cbef819a 194static const struct {
bb78535e 195 const gchash *h;
196 const octet *keysz;
197 grand *(*mgf)(const void */*k*/, size_t /*sz*/);
198} hashtab[] = {
199#define E(PRE, pre) \
200 { &pre, pre##_mgfkeysz, pre##_mgfrand },
201 HASHES
202#undef E
203};
204
ee33470f 205/*----- Miscellaneous static data -----------------------------------------*/
206
c2181593 207static FILE *outfp;
bafe8a7b 208static mp *outsz = 0;
f0675f8a 209static unsigned maurer_lo = 5, maurer_hi = 8;
ee33470f 210
211static int argc;
212static char **argv;
213
214static unsigned flags = 0;
215
16efd15b 216#define f_progress 1u
217#define f_file 2u
218#define f_fips 4u
219#define f_maurer 8u
220#define f_timer 16u
221#define f_discard 32u
ee33470f 222
223/*----- Help options ------------------------------------------------------*/
224
225static void usage(FILE *fp)
226{
227 pquis(fp, "Usage: $ generator [options]\n");
228}
229
230static void version(FILE *fp)
231{
232 pquis(fp, "$, Catacomb version " VERSION "\n");
233}
234
235static void help(FILE *fp)
236{
237 version(fp);
238 fputc('\n', fp);
239 usage(fp);
240 pquis(fp, "\n\
241Emits a stream of random bytes suitable for, well, all sorts of things.\n\
242The primary objective is to be able to generate streams of input for\n\
243statistical tests, such as Diehard.\n\
244\n\
245Options are specific to the particular generator, although there's a\n\
246common core set:\n\
247\n\
248-h, --help Display this help message.\n\
249-v, --version Display the program's version number.\n\
250-u, --usage Display a useless usage message.\n\
251\n\
252-l, --list Show a list of the supported generators, with\n\
253 their options.\n\
bb78535e 254-f, --fipstest Run the FIPS 140-1 randomness test.\n\
33efa0ae 255-m, --maurer[=LO-HI] Run Maurer's universal statistical test.\n\
ee33470f 256-o, --output FILE Write output to FILE, not stdout.\n\
257-z, --size SIZE Emit SIZE bytes, not an unlimited number.\n\
258-p, --progress Show a little progress meter (on stderr).\n\
04d45209 259-T, --timer Keep track of the CPU time used by the generator.\n\
260-d, --discard Discard the generated output.\n\
ee33470f 261\n\
262(A SIZE may be followed by `g' for gigabytes, `m' for megabytes, or\n\
263`k' for kilobytes. If unqualified, an amount in bytes is assumed.)\n\
264");
265}
266
267/*----- Main options parser -----------------------------------------------*/
268
269static struct option opts[] = {
270
271 /* --- Standard GNU help options --- */
272
273 { "help", 0, 0, 'h' },
274 { "version", 0, 0, 'v' },
275 { "usage", 0, 0, 'u' },
276
277 /* --- Other useful things --- */
278
279 { "list", 0, 0, 'l' },
bb78535e 280 { "fipstest", 0, 0, 'f' },
f0675f8a 281 { "maurer", OPTF_ARGOPT, 0, 'm' },
ee33470f 282 { "output", OPTF_ARGREQ, 0, 'o' },
283 { "size", OPTF_ARGREQ, 0, 'z' },
284 { "progress", 0, 0, 'p' },
04d45209 285 { "timer", 0, 0, 'T' },
286 { "discard", 0, 0, 'd' },
ee33470f 287
288 /* --- End of main table --- */
289
290 { 0, 0, 0, 0 }
291};
292
04d45209 293static const char *sopts = "hvu lfm::o:z:pTd";
ee33470f 294
295#ifndef OPTION_V
296 DA_DECL(option_v, struct option);
297# define OPTION_V
298#endif
299
300static option_v optv = DA_INIT;
301static dstr optd = DSTR_INIT;
302
303/* --- @addopts@ --- *
304 *
305 * Arguments: @const char *s@ = pointer to short options
306 * @struct option *l@ = pointer to long options
307 *
308 * Returns: ---
309 *
310 * Use: Adds a collection of options to the table.
311 */
312
313static void addopts(const char *s, struct option *l)
314{
315 dstr_puts(&optd, s);
316 if (DA_LEN(&optv))
317 DA_SHRINK(&optv, 1);
318 while (l->name)
319 DA_PUSH(&optv, *l++);
320 DA_PUSH(&optv, *l);
321}
322
323/* --- @opt@ --- *
324 *
325 * Arguments: ---
326 *
327 * Returns: Next option from argument array.
328 *
329 * Use: Fetches options, handling the standard ones.
330 */
331
332static int opt(void)
333{
334 for (;;) {
335 int i = mdwopt(argc, argv, optd.buf, DA(&optv), 0, 0, 0);
336 switch (i) {
337 case 'h':
338 help(stdout);
339 exit(0);
340 case 'v':
341 version(stdout);
342 exit(0);
343 case 'u':
344 usage(stdout);
345 exit(0);
346 case 'l': {
347 gen *g;
348 puts("Generators supported:");
349 for (g = generators; g->name; g++)
350 printf(" %s %s\n", g->name, g->help);
351 exit(0);
352 } break;
bb78535e 353 case 'f':
354 flags |= f_fips;
355 break;
356 case 'm':
357 flags |= f_maurer;
f0675f8a 358 if (optarg) {
359 char *p;
360 unsigned long lo, hi;
361 lo = strtoul(optarg, &p, 0);
e3dc2d22 362 if (*p == '-' || *p == ',')
f0675f8a 363 hi = strtoul(p + 1, &p, 0);
364 else
365 hi = lo;
366 if (*p != 0 || hi < lo || lo == 0)
367 die(EXIT_FAILURE, "bad bit range `%s'", optarg);
368 maurer_lo = lo;
369 maurer_hi = hi;
370 }
bb78535e 371 break;
ee33470f 372 case 'o':
373 if (flags & f_file)
374 die(EXIT_FAILURE, "already set an output file");
375 if (strcmp(optarg, "-") == 0)
376 outfp = stdout;
377 else {
378 outfp = fopen(optarg, "w");
379 if (!outfp) {
380 die(EXIT_FAILURE, "couldn't open output file `%s': %s",
bb78535e 381 optarg, strerror(errno));
ee33470f 382 }
383 }
384 flags |= f_file;
385 break;
386 case 'z': {
387 char *p;
bafe8a7b
MW
388 outsz = mp_readstring(outsz, optarg, &p, 0);
389 if (!outsz || MP_NEGP(outsz))
ee33470f 390 die(EXIT_FAILURE, "bad number `%s'", optarg);
391 switch (*p) {
bafe8a7b
MW
392 case 'G': case 'g': outsz = mp_lsl(outsz, outsz, 10);
393 case 'M': case 'm': outsz = mp_lsl(outsz, outsz, 10);
394 case 'K': case 'k': outsz = mp_lsl(outsz, outsz, 10);
ee33470f 395 case 0:
396 break;
397 default:
398 die(EXIT_FAILURE, "bad suffix `%s'", p);
399 break;
400 }
401 if (*p && p[1] != 0)
402 die(EXIT_FAILURE, "bad suffix `%s'", p);
403 } break;
404 case 'p':
405 flags |= f_progress;
406 break;
04d45209 407 case 'T':
408 flags |= f_timer;
409 break;
410 case 'd':
411 flags |= f_discard;
412 break;
ee33470f 413 default:
414 return (i);
415 }
416 }
417}
418
419/*----- Manglers for seed strings -----------------------------------------*/
420
421/* --- @unhex@ --- *
422 *
423 * Arguments: @const char *p@ = pointer to input string
424 * @char **end@ = where the end goes
425 * @dstr *d@ = output buffer
426 *
427 * Returns: ---
428 *
429 * Use: Transforms a hex string into a chunk of binary data.
430 */
431
432static void unhex(const char *p, char **end, dstr *d)
433{
434 while (p[0] && p[1]) {
435 int x = p[0], y = p[1];
436 if ('0' <= x && x <= '9') x -= '0';
437 else if ('A' <= x && x <= 'F') x -= 'A' - 10;
438 else if ('a' <= x && x <= 'f') x -= 'a' - 10;
439 else x = 0;
440 if ('0' <= y && y <= '9') y -= '0';
441 else if ('A' <= y && y <= 'F') y -= 'A' - 10;
442 else if ('a' <= y && y <= 'f') y -= 'a' - 10;
443 else y = 0;
444 DPUTC(d, (x << 4) + y);
445 p += 2;
446 }
447 *end = (char *)p;
448}
449
bb78535e 450/* --- Generate a key --- */
451
452static void textkey(dstr *d, const char *p, const octet *ksz)
453{
454 size_t sz = strlen(p);
455
456 if (!sz)
457 die(EXIT_FAILURE, "zero-length key string");
458 if (keysz(sz, ksz) != sz)
459 DPUTM(d, p, sz);
460 else {
461 rmd160_mgfctx g;
462 rmd160_mgfinit(&g, p, sz);
463 sz = keysz(0, ksz);
464 dstr_ensure(d, sz);
465 rmd160_mgfencrypt(&g, 0, d->buf, sz);
466 d->len += sz;
467 }
468 assert(((void)"I can't seem to choose a good key size",
469 keysz(d->len, ksz) == d->len));
470}
471
472static void hexkey(dstr *d, const char *p, const octet *ksz)
473{
474 char *q;
475 unhex(optarg, &q, d);
476 if (*q)
477 die(EXIT_FAILURE, "bad hex key `%s'", p);
478 if (keysz(d->len, ksz) != d->len)
479 die(EXIT_FAILURE, "bad key length");
480}
481
482static void randkey(dstr *d, const octet *ksz)
483{
484 size_t sz = keysz(0, ksz);
485 dstr_ensure(d, sz);
486 rand_get(RAND_GLOBAL, d->buf, sz);
487 d->len += sz;
488}
489
ee33470f 490/*----- Generators --------------------------------------------------------*/
491
492/* --- Blum-Blum-Shub strong generator --- */
493
ee33470f 494static grand *gen_bbs(unsigned i)
495{
496 /* --- Default modulus --- *
497 *
498 * The factors of this number are
499 *
500 * @p = 1229936431484295969649886203367009966370895964206162032259292413@
45c0fd36 501 * @7754313537966036459299022912838407755462506416274551744201653277@
ee33470f 502 * @313130311731673973886822067@
503 *
504 * @q = 9798171783943489959487301695884963889684294764514008432498259742@
505 * @5374320073594018817245784145742769603334292182227671519041431067@
506 * @61344781426317516045890159@
507 *
bb78535e 508 * Both %$p$% and %$q$% are prime; %$(p - 1)/2$% and %$(q - 1)/2$% have no
ee33470f 509 * common factors. They were found using this program, with random
510 * starting points.
511 *
512 * I hope that, by publishing these factors, I'll dissuade people from
bb78535e 513 * actually using this modulus in an attempt to attain real security. The
514 * program is quite quick at finding Blum numbers, so there's no excuse for
515 * not generating your own.
ee33470f 516 */
517
518 const char *mt =
eaa515d8 519 "12051128439013574251357214209433471144307319411973256935382082"
520 "84356405274180923922403660880355098909699130818163691602989614"
521 "90135716255689660470370755013177656905237112577648090277537209"
522 "93607817155427455344810369808478266925293635284364998010510985"
523 "0503830397166360721262431179505917248447259735253684659338653";
ee33470f 524
525 /* --- Other things --- */
526
527 grand *r;
528 const char *xt = 0;
87de7c73 529 unsigned bits = 1024;
ee33470f 530 mp *m, *x;
531 unsigned show = 0;
87de7c73 532 const char *kfile = 0, *id = 0, *ktype = 0;
ee33470f 533
534 /* --- Parse options --- */
535
536 static struct option opts[] = {
bb78535e 537 { "modulus", OPTF_ARGREQ, 0, 'M' },
ee33470f 538 { "generate", 0, 0, 'g' },
539 { "seed", OPTF_ARGREQ, 0, 's' },
540 { "bits", OPTF_ARGREQ, 0, 'b' },
541 { "show", 0, 0, 'S' },
87de7c73 542 { "keyring", OPTF_ARGREQ, 0, 'k' },
543 { "id", OPTF_ARGREQ, 0, 'i' },
544 { "type", OPTF_ARGREQ, 0, 't' },
ee33470f 545 { 0, 0, 0, 0 }
546 };
547
bb78535e 548 addopts("M:gs:b:Sk:i:t:", opts);
ee33470f 549
550 for (;;) {
551 int o = opt();
552 if (o < 0)
553 break;
554 switch (o) {
bb78535e 555 case 'M':
ee33470f 556 mt = optarg;
557 break;
558 case 'g':
559 mt = 0;
560 break;
561 case 's':
562 xt = optarg;
563 break;
564 case 'b':
565 bits = strtoul(optarg, 0, 0);
566 if (bits == 0)
567 die(EXIT_FAILURE, "bad number of bits `%s'", optarg);
568 break;
569 case 'S':
570 show = 1;
571 break;
87de7c73 572 case 'k':
573 kfile = optarg;
574 mt = 0;
575 break;
576 case 'i':
577 id = optarg;
578 mt = 0;
579 break;
580 case 't':
581 ktype = optarg;
582 mt = 0;
583 break;
ee33470f 584 default:
585 return (0);
586 }
587 }
588
589 /* --- Generate a modulus if one is requested --- */
590
591 if (mt) {
592 char *p;
593 m = mp_readstring(MP_NEW, mt, &p, 0);
87de7c73 594 if (!m || *p || (m->v[0] & 3) != 1)
ee33470f 595 die(EXIT_FAILURE, "bad modulus `%s'", mt);
596 /* Unfortunately I don't know how to test for a Blum integer */
87de7c73 597 } else if (kfile || id || ktype) {
598 key_file kf;
599 key *kk;
600 key_data *kd;
601
602 /* --- Open the key file --- */
603
604 if (!kfile)
605 kfile = "keyring";
606 if (key_open(&kf, kfile, KOPEN_READ, key_moan, 0)) {
607 die(EXIT_FAILURE, "error opening key file `%s': %s",
608 kfile, strerror(errno));
609 }
610
611 /* --- Find the key --- */
612
613 if (id) {
614 if ((kk = key_bytag(&kf, id)) == 0)
615 die(EXIT_FAILURE, "key `%s' not found", id);
616 } else {
617 if (!ktype)
618 ktype = "bbs";
619 if ((kk = key_bytype(&kf, ktype)) == 0)
620 die(EXIT_FAILURE, "no suitable key with type `%s' found", ktype);
621 }
622
623 /* --- Read the key data --- */
624
ef13e9a4 625 if ((kk->k->e & KF_ENCMASK) != KENC_STRUCT)
87de7c73 626 die(EXIT_FAILURE, "key is not structured");
ef13e9a4 627 if ((kd = key_structfind(kk->k, "n")) == 0)
87de7c73 628 die(EXIT_FAILURE, "key has no subkey `n'");
629 if ((kd->e & KF_ENCMASK) != KENC_MP)
e2edda68 630 die(EXIT_FAILURE, "incompatible subkey encoding");
87de7c73 631 m = MP_COPY(kd->u.m);
632 key_close(&kf);
ee33470f 633 } else {
c2181593 634 bbs_priv bp;
ee33470f 635
052b36d0 636 if (bbs_gen(&bp, bits, &rand_global, 0,
637 (flags & f_progress) ? pgen_ev : 0, 0))
87de7c73 638 die(EXIT_FAILURE, "modulus generation failed");
ee33470f 639 m = bp.n;
640
641 if (show) {
642 fputs("p = ", stderr);
643 mp_writefile(bp.p, stderr, 10);
644 fputs("\nq = ", stderr);
645 mp_writefile(bp.q, stderr, 10);
646 fputs("\nn = ", stderr);
647 mp_writefile(bp.n, stderr, 10);
648 fputc('\n', stderr);
649 }
650
ee33470f 651 mp_drop(bp.p);
652 mp_drop(bp.q);
653 }
654
655 /* --- Set up a seed --- */
656
657 if (!xt)
658 x = mprand(MP_NEW, mp_bits(m) - 1, &rand_global, 1);
659 else {
660 char *p;
661 x = mp_readstring(MP_NEW, xt, &p, 0);
662 if (*p)
663 die(EXIT_FAILURE, "bad modulus `%s'", xt);
664 }
665
666 /* --- Right --- */
667
668 r = bbs_rand(m, x);
669
670 mp_drop(m);
671 mp_drop(x);
672 return (r);
673}
674
675/* --- Catacomb's random number generator --- */
676
677static grand *gen_rand(unsigned i)
678{
679 grand *r = rand_create();
680 dstr d = DSTR_INIT;
681
682 static struct option opts[] = {
683 { "key", OPTF_ARGREQ, 0, 'k' },
684 { "text", OPTF_ARGREQ, 0, 't' },
685 { "hex", OPTF_ARGREQ, 0, 'H' },
ee33470f 686 { 0, 0, 0, 0 }
687 };
688
689 addopts("k:t:H:n", opts);
690
bb78535e 691 r->ops->misc(r, RAND_NOISESRC, &noise_source);
692 r->ops->misc(r, RAND_SEED, 160);
693
ee33470f 694 for (;;) {
695 int o = opt();
696 if (o < 0)
697 break;
698 switch (o) {
bb78535e 699 case 'k':
700 DRESET(&d);
1d9ac42f 701 textkey(&d, optarg, rmd160_hmackeysz);
bb78535e 702 r->ops->misc(r, RAND_KEY, d.buf, d.len);
703 break;
ee33470f 704 case 't':
705 r->ops->misc(r, GRAND_SEEDBLOCK, optarg, strlen(optarg));
706 break;
bb78535e 707 case 'H':
ee33470f 708 DRESET(&d);
1d9ac42f 709 hexkey(&d, optarg, rmd160_hmackeysz);
ee33470f 710 r->ops->misc(r, GRAND_SEEDBLOCK, d.buf, d.len);
ee33470f 711 break;
712 }
713 }
714
715 dstr_destroy(&d);
716 return (r);
717}
718
719/* --- RC4 output --- */
720
721static grand *gen_rc4(unsigned i)
722{
723 grand *r;
724 dstr d = DSTR_INIT;
725
726 static struct option opts[] = {
727 { "key", OPTF_ARGREQ, 0, 'k' },
728 { "hex", OPTF_ARGREQ, 0, 'H' },
729 { 0, 0, 0, 0 }
730 };
731
732 addopts("k:H:", opts);
733
734 for (;;) {
735 int o = opt();
736 if (o < 0)
737 break;
738 switch (o) {
bb78535e 739 case 'k':
740 DRESET(&d);
741 textkey(&d, optarg, rc4_keysz);
742 break;
743 case 'H':
744 DRESET(&d);
745 hexkey(&d, optarg, rc4_keysz);
746 break;
ee33470f 747 default:
748 return (0);
749 }
750 }
751
bb78535e 752 if (!d.len)
753 randkey(&d, rc4_keysz);
ee33470f 754 r = rc4_rand(d.buf, d.len);
755 dstr_destroy(&d);
756 return (r);
757}
758
bb78535e 759/* --- SEAL output --- */
ee33470f 760
bb78535e 761static grand *gen_seal(unsigned i)
762{
763 grand *r;
764 dstr d = DSTR_INIT;
765 uint32 n = 0;
ee33470f 766
bb78535e 767 static struct option opts[] = {
768 { "key", OPTF_ARGREQ, 0, 'k' },
769 { "hex", OPTF_ARGREQ, 0, 'H' },
770 { "sequence", OPTF_ARGREQ, 0, 'n' },
771 { 0, 0, 0, 0 }
772 };
ee33470f 773
bb78535e 774 addopts("k:H:n:", opts);
ee33470f 775
bb78535e 776 for (;;) {
777 int o = opt();
778 if (o < 0)
779 break;
780 switch (o) {
781 case 'k':
782 DRESET(&d);
783 textkey(&d, optarg, seal_keysz);
784 break;
785 case 'H':
786 DRESET(&d);
787 hexkey(&d, optarg, seal_keysz);
788 break;
789 case 'n': {
790 char *p;
791 n = strtoul(optarg, &p, 0);
792 if (*p)
793 die(EXIT_FAILURE, "bad number `%s'", optarg);
794 } break;
795 default:
796 return (0);
797 }
798 }
799
800 if (!d.len)
801 randkey(&d, seal_keysz);
802 r = seal_rand(d.buf, d.len, n);
803 dstr_destroy(&d);
804 return (r);
805}
806
807/* --- Output feedback generators --- */
ee33470f 808
809static grand *gen_ofb(unsigned i)
810{
811 grand *r;
812 dstr d = DSTR_INIT;
813 dstr iv = DSTR_INIT;
814
815 static struct option opts[] = {
816 { "key", OPTF_ARGREQ, 0, 'k' },
817 { "hex", OPTF_ARGREQ, 0, 'H' },
818 { "iv", OPTF_ARGREQ, 0, 'i' },
819 { 0, 0, 0, 0 }
820 };
821
822 addopts("k:H:i:", opts);
823
824 for (;;) {
825 int o = opt();
826 if (o < 0)
827 break;
828 switch (o) {
bb78535e 829 case 'k':
830 DRESET(&d);
831 textkey(&d, optarg, ciphertab[i].keysz);
832 break;
833 case 'H':
834 DRESET(&d);
835 hexkey(&d, optarg, ciphertab[i].keysz);
836 break;
ee33470f 837 case 'i': {
838 char *p;
1cdc3a62 839 DRESET(&iv);
ee33470f 840 unhex(optarg, &p, &iv);
841 if (*p)
842 die(EXIT_FAILURE, "bad hex IV `%s'", optarg);
1cdc3a62
MW
843 if (iv.len != ciphertab[i].blksz) {
844 die(EXIT_FAILURE, "bad IV length %lu (must be %lu)",
845 (unsigned long)iv.len, (unsigned long)ciphertab[i].blksz);
846 }
ee33470f 847 } break;
848 default:
849 return (0);
850 }
851 }
852
bb78535e 853 if (!d.len)
854 randkey(&d, ciphertab[i].keysz);
855 r = ciphertab[i].ofb(d.buf, d.len);
1cdc3a62 856 if (iv.len)
bb78535e 857 r->ops->misc(r, GRAND_SEEDBLOCK, iv.buf);
ee33470f 858
bb78535e 859 dstr_destroy(&d);
860 dstr_destroy(&iv);
861 return (r);
862}
863
864/* --- Counter generators --- */
865
866static grand *gen_counter(unsigned i)
867{
868 grand *r;
869 dstr d = DSTR_INIT;
870 dstr iv = DSTR_INIT;
ee33470f 871
bb78535e 872 static struct option opts[] = {
873 { "key", OPTF_ARGREQ, 0, 'k' },
874 { "hex", OPTF_ARGREQ, 0, 'H' },
875 { "iv", OPTF_ARGREQ, 0, 'i' },
876 { 0, 0, 0, 0 }
877 };
878
879 addopts("k:H:i:", opts);
880
881 for (;;) {
882 int o = opt();
883 if (o < 0)
884 break;
885 switch (o) {
886 case 'k':
887 DRESET(&d);
888 textkey(&d, optarg, ciphertab[i].keysz);
889 break;
890 case 'H':
891 DRESET(&d);
892 hexkey(&d, optarg, ciphertab[i].keysz);
893 break;
894 case 'i': {
895 char *p;
1cdc3a62 896 DRESET(&iv);
bb78535e 897 unhex(optarg, &p, &iv);
898 if (*p)
899 die(EXIT_FAILURE, "bad hex IV `%s'", optarg);
1cdc3a62
MW
900 if (iv.len != ciphertab[i].blksz) {
901 die(EXIT_FAILURE, "bad IV length %lu (must be %lu)",
902 (unsigned long)iv.len, (unsigned long)ciphertab[i].blksz);
903 }
bb78535e 904 } break;
905 default:
906 return (0);
907 }
908 }
909
910 if (!d.len)
911 randkey(&d, ciphertab[i].keysz);
912 r = ciphertab[i].counter(d.buf, d.len);
1cdc3a62 913 if (iv.len)
ee33470f 914 r->ops->misc(r, GRAND_SEEDBLOCK, iv.buf);
ee33470f 915
916 dstr_destroy(&d);
917 dstr_destroy(&iv);
918 return (r);
919}
920
bb78535e 921/* --- Mask generators --- */
922
923static grand *gen_mgf(unsigned i)
924{
925 grand *r;
926 dstr d = DSTR_INIT;
927 uint32 c = 0;
928
929 static struct option opts[] = {
930 { "key", OPTF_ARGREQ, 0, 'k' },
931 { "hex", OPTF_ARGREQ, 0, 'H' },
932 { "index", OPTF_ARGREQ, 0, 'i' },
933 { 0, 0, 0, 0 }
934 };
935
936 addopts("k:H:i:", opts);
937
938 for (;;) {
939 int o = opt();
940 if (o < 0)
941 break;
942 switch (o) {
943 case 'k':
944 DRESET(&d);
945 textkey(&d, optarg, hashtab[i].keysz);
946 break;
947 case 'H':
948 DRESET(&d);
949 hexkey(&d, optarg, hashtab[i].keysz);
950 break;
951 case 'i': {
952 char *p;
953 c = strtoul(optarg, &p, 0);
954 if (*p)
955 die(EXIT_FAILURE, "bad index `%s'", optarg);
956 } break;
957 default:
958 return (0);
959 }
960 }
961
962 if (!d.len)
963 randkey(&d, hashtab[i].keysz);
964
965 r = hashtab[i].mgf(d.buf, d.len);
966 if (c)
967 r->ops->misc(r, GRAND_SEEDUINT32, c);
968
969 dstr_destroy(&d);
970 return (r);
971}
972
ee33470f 973/* --- Fibonacci generator --- */
974
975static grand *gen_fib(unsigned i)
976{
977 grand *r;
978 uint32 s = 0;
979 char *p;
980 unsigned set = 0;
981
982 static struct option opts[] = {
983 { "seed", OPTF_ARGREQ, 0, 's' },
984 { 0, 0, 0, 0 }
985 };
986
987 addopts("s:", opts);
988
989 for (;;) {
990 int o = opt();
991 if (o < 0)
992 break;
993 switch (o) {
994 case 's':
995 s = strtoul(optarg, &p, 0);
996 if (*p)
997 die(EXIT_FAILURE, "bad integer `%s'", optarg);
998 set = 1;
999 break;
1000 default:
1001 return (0);
1002 }
1003 }
1004 r = fibrand_create(s);
1005 if (!set)
1006 r->ops->misc(r, GRAND_SEEDRAND, &rand_global);
1007 return (r);
1008}
1009
1010/* --- LC generator --- */
1011
1012static grand *gen_lc(unsigned i)
1013{
1014 uint32 s = 0;
1015 char *p;
1016 unsigned set = 0;
1017
1018 static struct option opts[] = {
1019 { "seed", OPTF_ARGREQ, 0, 's' },
1020 { 0, 0, 0, 0 }
1021 };
1022
1023 addopts("s:", opts);
1024
1025 for (;;) {
1026 int o = opt();
1027 if (o < 0)
1028 break;
1029 switch (o) {
1030 case 's':
1031 s = strtoul(optarg, &p, 0);
1032 if (*p)
1033 die(EXIT_FAILURE, "bad integer `%s'", optarg);
1034 set = 1;
1035 break;
1036 default:
1037 return (0);
1038 }
1039 }
1040 if (!set) {
1041 do
1042 s = rand_global.ops->range(&rand_global, LCRAND_P);
1043 while (s == LCRAND_FIXEDPT);
1044 }
1045 return (lcrand_create(s));
1046}
1047
1048/* --- Basic options parser -- can't generate output --- */
1049
1050static grand *gen_opts(unsigned i)
1051{
1052 while (opt() >= 0)
1053 ;
1054 return (0);
1055}
1056
1057/*----- Generators table --------------------------------------------------*/
1058
f57c6d85 1059gen generators[] = {
ee33470f 1060 { "fibonacci", gen_fib, 0,
1061 "[-s SEED]" },
1062 { "lc", gen_lc, 0,
1063 "[-s SEED]" },
bb78535e 1064#define E(PRE, pre) \
1065 { #pre "-ofb", gen_ofb, CIPHER_##PRE, \
ee33470f 1066 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
bb78535e 1067 CIPHERS
1068#undef E
1069#define E(PRE, pre) \
1070 { #pre "-counter", gen_counter, CIPHER_##PRE, \
ee33470f 1071 "[-k KEY-PHRASE] [-H HEX-KEY] [-i HEX-IV]" },
bb78535e 1072 CIPHERS
f57c6d85 1073#undef E
bb78535e 1074#define E(PRE, pre) \
1075 { #pre "-mgf", gen_mgf, HASH_##PRE, \
1076 "[-k KEY-PHRASE] [-H HEX-KEY] [-i INDEX]" },
1077 HASHES
f57c6d85 1078#undef E
ee33470f 1079 { "rc4", gen_rc4, 0,
1080 "[-k KEY-PHRASE] [-H HEX-KEY]" },
bb78535e 1081 { "seal", gen_seal, 0,
1082 "[-k KEY-PHRASE] [-H HEX-KEY] [-n SEQ]" },
ee33470f 1083 { "rand", gen_rand, 0,
1084 "[-n] [-k KEY-PHRASE] [-t TEXT-BLOCK] [-H HEX-BLOCK]" },
1085 { "bbs", gen_bbs, 0,
bb78535e 1086 "[-gS] [-s SEED] [-M MODULUS] [-b BITS] [-k KEYRING] [-i TAG] [-t TYPE]"
87de7c73 1087 },
ee33470f 1088 { 0, 0, 0, 0 },
1089};
1090
1091static gen optsg = { "options", gen_opts, 0,
1092 "This message shouldn't be printed." };
1093
f0675f8a 1094/*----- Random number generation ------------------------------------------*/
ee33470f 1095
f0675f8a 1096static int genfile(const void *buf, size_t sz, void *p)
1097{
1098 FILE *fp = p;
1099 if (fwrite(buf, 1, sz, fp) != sz)
1100 die(EXIT_FAILURE, "error writing to file: %s", strerror(errno));
1101 return (0);
1102}
1103
1104static int genbuf(const void *buf, size_t sz, void *p)
1105{
1106 octet **pp = p;
1107 memcpy(*pp, buf, sz);
1108 *pp += sz;
1109 return (0);
1110}
1111
e3dc2d22 1112typedef struct genmaurer_ctx {
1113 size_t n;
1114 maurer_ctx *m;
1115} genmaurer_ctx;
1116
1117static int genmaurer(const void *buf, size_t sz, void *p)
1118{
1119 genmaurer_ctx *g = p;
1120 size_t i;
1121
1122 for (i = 0; i < g->n; i++)
1123 maurer_test(&g->m[i], buf, sz);
1124 return (0);
1125}
1126
bafe8a7b 1127static int generate(grand *r, mp *outsz,
f0675f8a 1128 int (*func)(const void *buf, size_t sz, void *p),
1129 void *p)
ee33470f 1130{
04d45209 1131 static char kmg[] = { ' ', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 0 };
1132
bb78535e 1133 unsigned percent = 0;
bafe8a7b
MW
1134 mp *kb = MP_ZERO, *t = MP_NEW;
1135 dstr d = DSTR_INIT;
ee33470f 1136 time_t last;
bb78535e 1137 static char baton[] = "-\\|/";
ee33470f 1138 char *bp;
f0675f8a 1139 int rc;
04d45209 1140 clock_t clk = 0;
f0675f8a 1141
1142 /* --- Spit out random data --- */
1143
1144 last = time(0);
1145 bp = baton;
1146 if (flags & f_progress) {
1147 char *errbuf = xmalloc(BUFSIZ);
1148 setvbuf(stderr, errbuf, _IOLBF, BUFSIZ);
1149 if (outsz)
1150 fprintf(stderr, "[%*s] 0%% 0\r[/\b", 50, "");
1151 else
1152 fputs("[ ] 0\r[/\b", stderr);
1153 fflush(stderr);
1154 }
1155
1156#ifdef SIGPIPE
1157 signal(SIGPIPE, SIG_IGN);
1158#endif
1159
bafe8a7b 1160 while (!outsz || MP_CMP(kb, <, outsz)) {
f0675f8a 1161 octet buf[BUFSIZ];
bafe8a7b 1162 size_t sz = sizeof(buf), left;
04d45209 1163 clock_t c_start, c_stop;
f0675f8a 1164
1165 /* --- Emit a bufferful (or less) of data --- */
1166
1167 if (outsz) {
bafe8a7b
MW
1168 t = mp_sub(t, outsz, kb);
1169 assert(!MP_NEGP(t));
1170 if (MP_CMP(t, <=, MP_SIZET_MAX)) {
1171 left = mp_tosizet(t);
1172 if (sz > left) sz = left;
1173 }
f0675f8a 1174 }
04d45209 1175 c_start = clock();
f0675f8a 1176 r->ops->fill(r, buf, sz);
04d45209 1177 c_stop = clock();
1178 clk += c_stop - c_start;
1179 if (func && (rc = func(buf, sz, p)) != 0)
f0675f8a 1180 return (rc);
bafe8a7b
MW
1181 t = mp_fromsizet(t, sz);
1182 kb = mp_add(kb, kb, t);
f0675f8a 1183
1184 /* --- Update the display --- */
1185
1186 if (flags & f_progress) {
bafe8a7b 1187 time_t now = time(0);
f0675f8a 1188 unsigned up = 0;
1189
1190 if (percent > 100)
1191 up = 1;
1192
1193 if (!outsz) {
bafe8a7b 1194 if (difftime(now, last) > 1.0) {
f0675f8a 1195 up = 1;
1196 }
1197 if (up)
1198 fputs(" ] ", stderr);
1199 } else {
bafe8a7b
MW
1200 unsigned pc;
1201 t = mp_fromulong(t, 100);
1202 t = mp_mul(t, t, kb);
1203 mp_div(&t, 0, t, outsz);
1204 assert(!MP_NEGP(t) && MP_CMP(t, <, MP_UINT_MAX));
1205 pc = mp_touint(t);
1206 if (pc > percent || percent > 100 || difftime(now, last) > 1.0) {
f0675f8a 1207 if (percent > 100)
1208 percent = 0;
1209 percent &= ~1;
1210 for (; percent < (pc & ~1); percent += 2)
1211 putc('.', stderr);
1212 percent = pc;
1213 for (; pc < 100; pc += 2)
1214 putc(' ', stderr);
1215 fprintf(stderr, "] %3i%% ", percent);
1216 up = 1;
1217 }
1218 }
1219
1220 if (up) {
04d45209 1221 char *kk = kmg;
bafe8a7b
MW
1222 t = mp_add(t, kb, MP_ZERO);
1223 while (mp_bits(t) >= 14) {
1224 t = mp_lsr(t, t, 10);
04d45209 1225 kk++;
f0675f8a 1226 }
bafe8a7b
MW
1227 DRESET(&d);
1228 mp_writedstr(t, &d, 10);
1229 fprintf(stderr, "%4s%c\r[", d.buf, *kk);
f0675f8a 1230 if (outsz) {
1231 unsigned pc;
1232 for (pc = 0; pc < (percent & ~1); pc += 2)
1233 putc('.', stderr);
1234 }
bafe8a7b 1235 last = now;
f0675f8a 1236 }
1237
1238 if (percent > 100)
1239 percent = 0;
1240
1241 if (percent < 100) {
1242 putc(*bp++, stderr);
1243 putc('\b', stderr);
1244 if (!*bp)
1245 bp = baton;
1246 }
1247 fflush(stderr);
1248 }
bafe8a7b 1249 }
f0675f8a 1250
1251 if (flags & f_progress)
1252 fputc('\n', stderr);
04d45209 1253 if (flags & f_timer) {
bafe8a7b
MW
1254 DRESET(&d);
1255 dstr_puts(&d, "generated ");
1256 mp_writedstr(kb, &d, 10);
1257 dstr_puts(&d, " bytes ");
e3dc2d22 1258 if (!clk)
bafe8a7b 1259 dstr_puts(&d, "too quickly to measure\n");
e3dc2d22 1260 else {
1261 char *kk;
bafe8a7b 1262 double out;
e3dc2d22 1263 double sec = (double)clk/CLOCKS_PER_SEC;
bafe8a7b
MW
1264 unsigned long sh;
1265 double bps;
1266
1267 MP_SHRINK(kb);
1268 switch (MP_LEN(kb)) {
1269 case 0: out = 0; break;
1270 case 1: out = kb->v[0]; break;
1271 default:
1272 sh = mp_bits(kb) - MPW_BITS;
1273 t = mp_lsr(t, kb, sh);
1274 out = ldexp(t->v[0], sh);
1275 break;
1276 }
1277 bps = (8*out)/sec;
e3dc2d22 1278 for (kk = kmg; bps > 1024 && kk[1]; kk++, bps /= 1024)
1279 ;
bafe8a7b
MW
1280 dstr_putf(&d, "in %g secs (%g %cb/s)\n", sec, bps, *kk);
1281 fwrite(d.buf, 1, d.len, stderr);
e3dc2d22 1282 }
04d45209 1283 }
bafe8a7b
MW
1284
1285 mp_drop(t);
1286 DDESTROY(&d);
f0675f8a 1287 return (0);
1288}
1289
1290/*----- Main code ---------------------------------------------------------*/
1291
1292int main(int ac, char *av[])
1293{
1294 gen *g = &optsg;
1295 grand *r;
ee33470f 1296
1297 /* --- Initialize mLib --- */
1298
1299 ego(av[0]);
1300 sub_init();
1301
1302 /* --- Set up the main Catacomb generator --- */
1303
1304 rand_noisesrc(RAND_GLOBAL, &noise_source);
bb78535e 1305 rand_seed(RAND_GLOBAL, 160);
ee33470f 1306
1307 /* --- Initialize the options table --- */
1308
1309 addopts(sopts, opts);
1310 argc = ac;
1311 argv = av;
c2181593 1312 outfp = stdout;
ee33470f 1313
1314 /* --- Read the generator out of the first argument --- */
1315
1316 if (argc > 1 && *argv[1] != '-') {
1317 const char *arg = av[1];
1318 size_t sz = strlen(arg);
bc97f84c 1319 gen *gg;
ee33470f 1320
1321 g = 0;
1322 for (gg = generators; gg->name; gg++) {
1323 if (strncmp(arg, gg->name, sz) == 0) {
1324 if (gg->name[sz] == 0) {
1325 g = gg;
1326 break;
1327 } else if (g)
1328 die(EXIT_FAILURE, "ambiguous generator name `%s'", arg);
1329 else
1330 g = gg;
1331 }
1332 }
1333 if (!g)
1334 die(EXIT_FAILURE, "unknown generator name `%s'", arg);
1335 argc--;
1336 argv++;
1337 }
1338
1339 /* --- Get a generic random number generator --- */
1340
1341 r = g->seed(g->i);
1342 if (!r || optind != ac - 1) {
1343 usage(stderr);
1344 exit(EXIT_FAILURE);
1345 }
1346
bb78535e 1347 /* --- Do the FIPS test --- */
1348
1349 if (flags & f_fips) {
1350 octet buf[FIPSTEST_BUFSZ];
1351 unsigned rc;
bafe8a7b 1352 mp *t;
f0675f8a 1353 octet *p = buf;
bb78535e 1354
bafe8a7b
MW
1355 t = mp_fromsizet(MP_NEW, sizeof(buf));
1356 generate(r, t, genbuf, &p);
1357 mp_drop(t);
bb78535e 1358 rc = fipstest(buf);
1359 if (rc & FIPSTEST_MONOBIT)
1360 moan("failed monobit test");
1361 if (rc & FIPSTEST_POKER)
1362 moan("failed poker test");
1363 if (rc & FIPSTEST_RUNS)
1364 moan("failed runs test");
1365 if (rc & FIPSTEST_LONGRUNS)
1366 moan("failed long runs test");
1367 if (!rc && (flags & f_progress))
04d45209 1368 fputs("test passed\n", stderr);
bb78535e 1369 return (rc ? EXIT_FAILURE : 0);
1370 }
1371
1372 /* --- Do Maurer's test --- */
1373
1374 if (flags & f_maurer) {
f0675f8a 1375 size_t bufsz;
bb78535e 1376 unsigned i;
1377 unsigned rc = 0;
bafe8a7b 1378 mp *t;
e3dc2d22 1379 genmaurer_ctx g;
bb78535e 1380
1381 static struct { double x; const char *sig; } sigtab[] = {
1382 { 3.2905, "1e-3" },
1383 { 3.0902, "2e-3" },
1384 { 2.8070, "5e-3" },
1385 { 2.5758, "1e-2" },
1386 { 0 , 0 }
1387 };
1388
e3dc2d22 1389 g.n = maurer_hi - maurer_lo + 1;
1390 g.m = xmalloc(g.n * sizeof(maurer_ctx));
1391 for (i = 0; i < g.n; i++)
1392 maurer_init(&g.m[i], i + maurer_lo);
f0675f8a 1393 bufsz = (100 * maurer_hi) << maurer_hi;
e3dc2d22 1394
bafe8a7b
MW
1395 t = mp_fromsizet(MP_NEW, bufsz);
1396 generate(r, t, genmaurer, &g);
1397 mp_drop(t);
f0675f8a 1398
1399 for (i = maurer_lo; i <= maurer_hi; i++) {
e3dc2d22 1400 double z = maurer_done(&g.m[i - maurer_lo]);
bb78535e 1401 double zz = fabs(z);
1402 unsigned j;
1403
1404 for (j = 0; sigtab[j].sig; j++) {
1405 if (zz > sigtab[j].x) {
bb78535e 1406 rc = EXIT_FAILURE;
1407 moan("failed, bits = %u, sig = %s, Z_u = %g",
f0675f8a 1408 i, sigtab[j].sig, z);
bb78535e 1409 break;
1410 }
1411 }
1412 if (flags & f_progress)
04d45209 1413 fprintf(stderr, "bits = %u, Z_u = %g\n", i, z);
bb78535e 1414 }
1415
e3dc2d22 1416 xfree(g.m);
bb78535e 1417 return (rc);
1418 }
1419
04d45209 1420 /* --- Discard --- */
1421
1422 if (flags & f_discard) {
1423 generate(r, outsz, 0, 0);
1424 return (0);
1425 }
1426
f0675f8a 1427 /* --- Write to a file --- */
bb78535e 1428
ee33470f 1429#ifndef PORTABLE
1430 if (!(flags & f_file) && isatty(STDOUT_FILENO))
1431 die(EXIT_FAILURE, "writing output to a terminal is a bad idea");
1432#endif
1433
f0675f8a 1434 generate(r, outsz, genfile, outfp);
ee33470f 1435
1436 /* --- Done --- */
1437
1438 r->ops->destroy(r);
ee33470f 1439 return (0);
1440}
1441
1442/*----- That's all, folks -------------------------------------------------*/