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