Update manuals.
[u/mdw/catacomb] / keyutil.c
CommitLineData
d03ab969 1/* -*-c-*-
2 *
3563e365 3 * $Id: keyutil.c,v 1.19 2004/04/03 03:31:01 mdw Exp $
d03ab969 4 *
5 * Simple key manager program
6 *
252c122d 7 * (c) 1999 Straylight/Edgeware
d03ab969 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: keyutil.c,v $
3563e365 33 * Revision 1.19 2004/04/03 03:31:01 mdw
34 * Allow explicit group parameters for DH groups.
35 *
34e4f738 36 * Revision 1.18 2004/04/01 12:50:09 mdw
37 * Add cyclic group abstraction, with test code. Separate off exponentation
38 * functions for better static linking. Fix a buttload of bugs on the way.
39 * Generally ensure that negative exponents do inversion correctly. Add
40 * table of standard prime-field subgroups. (Binary field subgroups are
41 * currently unimplemented but easy to add if anyone ever finds a good one.)
42 *
1ba83484 43 * Revision 1.17 2004/03/28 01:58:47 mdw
44 * Generate, store and retreive elliptic curve keys.
45 *
1e7fadef 46 * Revision 1.16 2003/10/15 09:31:45 mdw
47 * Fix help message.
48 *
72f13799 49 * Revision 1.15 2003/05/15 23:23:24 mdw
50 * Fix behaviour with bogus trailing attributes.
51 *
99dde2f4 52 * Revision 1.14 2001/02/23 09:03:27 mdw
53 * Simplify usage message by removing nonexistant options.
54 *
ce70ec47 55 * Revision 1.13 2001/02/21 20:04:27 mdw
56 * Provide help on individual commands (some need it desparately). Allow
57 * atomic retagging of keys.
58 *
f4f8e305 59 * Revision 1.12 2001/02/03 11:58:22 mdw
60 * Store the correct seed information and count for DSA keys now that it's
61 * available.
62 *
16efd15b 63 * Revision 1.11 2000/12/06 20:33:27 mdw
64 * Make flags be macros rather than enumerations, to ensure that they're
65 * unsigned.
66 *
4b536f42 67 * Revision 1.10 2000/10/08 12:02:21 mdw
68 * Use @MP_EQ@ instead of @MP_CMP@.
69 *
dce3fb0d 70 * Revision 1.9 2000/08/15 21:40:49 mdw
71 * Minor formatting change in listing attributes.
72 *
40d5a112 73 * Revision 1.8 2000/07/29 09:59:13 mdw
74 * Support Lim-Lee primes in Diffie-Hellman parameter generation.
75 *
ab0ca95f 76 * Revision 1.7 2000/07/01 11:18:51 mdw
77 * Use new interfaces for key manipulation.
78 *
9219a5d6 79 * Revision 1.6 2000/06/17 11:28:22 mdw
80 * Use secure memory interface from MP library. `rand_getgood' is
81 * deprecated.
82 *
052b36d0 83 * Revision 1.5 2000/02/12 18:21:03 mdw
84 * Overhaul of key management (again).
85 *
252c122d 86 * Revision 1.4 1999/12/22 15:48:10 mdw
87 * Track new key-management changes. Support new key generation
88 * algorithms.
89 *
ce1f2631 90 * Revision 1.3 1999/11/02 15:23:24 mdw
91 * Fix newlines in keyring list.
92 *
cc321283 93 * Revision 1.2 1999/10/15 21:05:28 mdw
94 * In `key list', show timezone for local times, and support `-u' option
95 * for UTC output.
96 *
d03ab969 97 * Revision 1.1 1999/09/03 08:41:12 mdw
98 * Initial import.
99 *
100 */
101
102/*----- Header files ------------------------------------------------------*/
103
104#include "config.h"
105
106#include <errno.h>
107#include <stdio.h>
108#include <stdlib.h>
109#include <string.h>
110#include <time.h>
111
252c122d 112#include <mLib/base64.h>
d03ab969 113#include <mLib/mdwopt.h>
114#include <mLib/quis.h>
115#include <mLib/report.h>
116#include <mLib/sub.h>
117
118#include <noise.h>
119#include <rand.h>
120
252c122d 121#include "bbs.h"
052b36d0 122#include "dh.h"
252c122d 123#include "dsa.h"
34e4f738 124#include "dsarand.h"
1ba83484 125#include "ec.h"
126#include "ec-keys.h"
127#include "ectab.h"
252c122d 128#include "fibrand.h"
d03ab969 129#include "getdate.h"
130#include "key.h"
252c122d 131#include "mp.h"
132#include "mpmont.h"
133#include "mprand.h"
134#include "mptext.h"
135#include "pgen.h"
3563e365 136#include "ptab.h"
252c122d 137#include "rsa.h"
d03ab969 138
34e4f738 139#include "sha-mgf.h"
140#include "sha256-mgf.h"
141#include "sha224-mgf.h"
142#include "sha384-mgf.h"
143#include "sha512-mgf.h"
144#include "tiger-mgf.h"
145#include "rmd128-mgf.h"
146#include "rmd160-mgf.h"
147#include "rmd256-mgf.h"
148#include "rmd320-mgf.h"
149#include "md5-mgf.h"
150#include "dsarand.h"
151
d03ab969 152/*----- Handy global state ------------------------------------------------*/
153
154static const char *keyfile = "keyring";
155
156/*----- Useful shared functions -------------------------------------------*/
157
158/* --- @doopen@ --- *
159 *
160 * Arguments: @key_file *f@ = pointer to key file block
161 * @unsigned how@ = method to open file with
162 *
163 * Returns: ---
164 *
165 * Use: Opens a key file and handles errors by panicking
166 * appropriately.
167 */
168
169static void doopen(key_file *f, unsigned how)
170{
252c122d 171 if (key_open(f, keyfile, how, key_moan, 0))
052b36d0 172 die(1, "couldn't open keyring `%s': %s", keyfile, strerror(errno));
d03ab969 173}
174
175/* --- @doclose@ --- *
176 *
177 * Arguments: @key_file *f@ = pointer to key file block
178 *
179 * Returns: ---
180 *
181 * Use: Closes a key file and handles errors by panicking
182 * appropriately.
183 */
184
185static void doclose(key_file *f)
186{
187 switch (key_close(f)) {
188 case KWRITE_FAIL:
189 die(EXIT_FAILURE, "couldn't write file `%s': %s",
190 keyfile, strerror(errno));
191 case KWRITE_BROKEN:
192 die(EXIT_FAILURE, "keyring file `%s' broken: %s (repair manually)",
193 keyfile, strerror(errno));
194 }
195}
196
197/* --- @setattr@ --- *
198 *
199 * Arguments: @key_file *f@ = pointer to key file block
200 * @key *k@ = pointer to key block
201 * @char *v[]@ = array of assignments (overwritten!)
202 *
203 * Returns: ---
204 *
205 * Use: Applies the attribute assignments to the key.
206 */
207
208static void setattr(key_file *f, key *k, char *v[])
209{
210 while (*v) {
252c122d 211 int err;
d03ab969 212 char *p = *v;
213 size_t eq = strcspn(p, "=");
72f13799 214 if (!p[eq]) {
215 moan("invalid assignment: `%s' (ignored)", p);
216 v++;
217 continue;
218 }
d03ab969 219 p[eq] = 0;
220 p += eq + 1;
252c122d 221 if ((err = key_putattr(f, k, *v, *p ? p : 0)) != 0)
222 die(EXIT_FAILURE, "couldn't set attributes: %s", key_strerror(err));
d03ab969 223 v++;
224 }
225}
226
34e4f738 227/*----- Seeding -----------------------------------------------------------*/
228
229const struct seedalg { const char *p; grand *(*gen)(const void *, size_t); }
230seedtab[] = {
231 { "dsarand", dsarand_create },
232 { "rmd128-mgf", rmd128_mgfrand },
233 { "rmd160-mgf", rmd160_mgfrand },
234 { "rmd256-mgf", rmd256_mgfrand },
235 { "rmd320-mgf", rmd320_mgfrand },
236 { "sha-mgf", sha_mgfrand },
237 { "sha224-mgf", sha224_mgfrand },
238 { "sha256-mgf", sha256_mgfrand },
239 { "sha384-mgf", sha384_mgfrand },
240 { "sha512-mgf", sha512_mgfrand },
241 { "tiger-mgf", tiger_mgfrand },
242 { 0, 0 }
243};
244
245#define SEEDALG_DEFAULT (seedtab + 2)
246
252c122d 247/*----- Key generation ----------------------------------------------------*/
248
249/* --- Key generation parameters --- */
250
251typedef struct keyopts {
252 key_file *kf; /* Pointer to key file */
253 key *k; /* Pointer to the actual key */
254 dstr tag; /* Full tag name for the key */
255 unsigned f; /* Flags for the new key */
256 unsigned bits, qbits; /* Bit length for the new key */
1ba83484 257 const char *curve; /* Elliptic curve name/info */
34e4f738 258 grand *r; /* Random number source */
252c122d 259 key *p; /* Parameters key-data */
260} keyopts;
261
16efd15b 262#define f_bogus 1u /* Error in parsing */
263#define f_lock 2u /* Passphrase-lock private key */
264#define f_quiet 4u /* Don't show a progress indicator */
265#define f_limlee 8u /* Generate Lim-Lee primes */
266#define f_subgroup 16u /* Generate a subgroup */
ce70ec47 267#define f_retag 32u /* Remove any existing tag */
252c122d 268
269/* --- @dolock@ --- *
270 *
271 * Arguments: @keyopts *k@ = key generation options
272 * @key_data *kd@ = pointer to key data to lock
273 * @const char *t@ = tag suffix or null
274 *
275 * Returns: ---
276 *
277 * Use: Does passphrase locking on new keys.
278 */
279
280static void dolock(keyopts *k, key_data *kd, const char *t)
281{
282 if (!(k->f & f_lock))
283 return;
284 if (t)
285 dstr_putf(&k->tag, ".%s", t);
286 if (key_plock(k->tag.buf, kd, kd))
287 die(EXIT_FAILURE, "couldn't lock key");
288}
289
290/* --- @mpkey@ --- *
291 *
292 * Arguments: @key_data *kd@ = pointer to parent key block
293 * @const char *tag@ = pointer to tag string
294 * @mp *m@ = integer to store
295 * @unsigned f@ = flags to set
296 *
297 * Returns: ---
298 *
299 * Use: Sets a multiprecision integer subkey.
300 */
301
302static void mpkey(key_data *kd, const char *tag, mp *m, unsigned f)
303{
304 key_data *kkd = key_structcreate(kd, tag);
305 key_mp(kkd, m);
306 kkd->e |= f;
307}
308
309/* --- @copyparam@ --- *
310 *
311 * Arguments: @keyopts *k@ = pointer to key options
312 * @const char **pp@ = checklist of parameters
313 *
314 * Returns: Nonzero if parameters copied; zero if you have to generate
315 * them.
316 *
317 * Use: Copies parameters from a source key to the current one.
318 */
319
320static int copyparam(keyopts *k, const char **pp)
321{
322 key_filter kf;
323
324 /* --- Quick check if no parameters supplied --- */
325
326 if (!k->p)
327 return (0);
328
329 /* --- Run through the checklist --- */
330
331 while (*pp) {
332 key_data *kd = key_structfind(&k->p->k, *pp);
333 if (!kd)
334 die(EXIT_FAILURE, "bad parameter key: parameter `%s' not found", *pp);
335 if ((kd->e & KF_CATMASK) != KCAT_SHARE)
336 die(EXIT_FAILURE, "bad parameter key: subkey `%s' is not shared", *pp);
337 pp++;
338 }
339
340 /* --- Copy over the parameters --- */
341
342 kf.f = KCAT_SHARE;
343 kf.m = KF_CATMASK;
344 if (!key_copy(&k->k->k, &k->p->k, &kf))
345 die(EXIT_FAILURE, "unexpected failure while copying parameters");
346 return (1);
347}
348
349/* --- @getmp@ --- *
350 *
351 * Arguments: @key_data *k@ = pointer to key data block
352 * @const char *tag@ = tag string to use
353 *
354 * Returns: Pointer to multiprecision integer key item.
355 *
356 * Use: Fetches an MP key component.
357 */
358
359static mp *getmp(key_data *k, const char *tag)
360{
361 k = key_structfind(k, tag);
362 if (!k)
363 die(EXIT_FAILURE, "unexpected failure looking up subkey `%s'", tag);
364 if ((k->e & KF_ENCMASK) != KENC_MP)
365 die(EXIT_FAILURE, "subkey `%s' has an incompatible type");
366 return (k->u.m);
367}
368
052b36d0 369/* --- @keyrand@ --- *
370 *
371 * Arguments: @key_file *kf@ = pointer to key file
372 * @const char *id@ = pointer to key id (or null)
373 *
374 * Returns: ---
375 *
376 * Use: Keys the random number generator.
377 */
378
379static void keyrand(key_file *kf, const char *id)
380{
381 key *k;
382
383 /* --- Find the key --- */
384
385 if (id) {
386 if ((k = key_bytag(kf, id)) == 0)
387 die(EXIT_FAILURE, "key `%s' not found", id);
388 } else
389 k = key_bytype(kf, "catacomb-rand");
390
391 if (k) {
392 key_data *kd = &k->k, kkd;
393
394 again:
395 switch (kd->e & KF_ENCMASK) {
396 case KENC_BINARY:
397 break;
398 case KENC_ENCRYPT: {
399 dstr d = DSTR_INIT;
400 key_fulltag(k, &d);
401 if (key_punlock(d.buf, kd, &kkd))
402 die(EXIT_FAILURE, "error unlocking key `%s'", d.buf);
403 dstr_destroy(&d);
404 kd = &kkd;
405 } goto again;
406 default: {
407 dstr d = DSTR_INIT;
408 key_fulltag(k, &d);
409 die(EXIT_FAILURE, "bad encoding type for key `%s'", d.buf);
410 } break;
411 }
412
413 /* --- Key the generator --- */
414
415 rand_key(RAND_GLOBAL, kd->u.k.k, kd->u.k.sz);
416 if (kd == &kkd)
417 key_destroy(&kkd);
418 }
419}
420
252c122d 421/* --- Key generation algorithms --- */
422
423static void alg_binary(keyopts *k)
424{
425 unsigned sz;
426 unsigned m;
427 octet *p;
428
429 if (!k->bits)
430 k->bits = 128;
431 if (k->p)
432 die(EXIT_FAILURE, "no shared parameters for binary keys");
433
434 sz = (k->bits + 7) >> 3;
435 p = sub_alloc(sz);
436 m = (1 << (((k->bits - 1) & 7) + 1)) - 1;
34e4f738 437 k->r->ops->fill(k->r, p, sz);
252c122d 438 *p &= m;
439 key_binary(&k->k->k, p, sz);
440 k->k->k.e |= KCAT_SYMM | KF_BURN;
441 memset(p, 0, sz);
442 sub_free(p, sz);
443 dolock(k, &k->k->k, 0);
444}
445
446static void alg_des(keyopts *k)
447{
448 unsigned sz;
449 octet *p;
450 int i;
451
452 if (!k->bits)
1ba83484 453 k->bits = 168;
252c122d 454 if (k->p)
455 die(EXIT_FAILURE, "no shared parameters for DES keys");
456 if (k->bits % 56 || k->bits > 168)
457 die(EXIT_FAILURE, "DES keys must be 56, 112 or 168 bits long");
458
459 sz = k->bits / 7;
460 p = sub_alloc(sz);
34e4f738 461 k->r->ops->fill(k->r, p, sz);
252c122d 462 for (i = 0; i < sz; i++) {
052b36d0 463 octet x = p[i] | 0x01;
252c122d 464 x = x ^ (x >> 4);
465 x = x ^ (x >> 2);
052b36d0 466 x = x ^ (x >> 1);
252c122d 467 p[i] = (p[i] & 0xfe) | (x & 0x01);
468 }
469 key_binary(&k->k->k, p, sz);
470 k->k->k.e |= KCAT_SYMM | KF_BURN;
471 memset(p, 0, sz);
472 sub_free(p, sz);
473 dolock(k, &k->k->k, 0);
474}
475
476static void alg_rsa(keyopts *k)
477{
ab0ca95f 478 rsa_priv rp;
252c122d 479 key_data *kd;
480
481 /* --- Sanity checking --- */
482
483 if (k->p)
484 die(EXIT_FAILURE, "no shared parameters for RSA keys");
485 if (!k->bits)
486 k->bits = 1024;
487
488 /* --- Generate the RSA parameters --- */
489
34e4f738 490 if (rsa_gen(&rp, k->bits, k->r, 0,
252c122d 491 (k->f & f_quiet) ? 0 : pgen_ev, 0))
492 die(EXIT_FAILURE, "RSA key generation failed");
493
494 /* --- Run a test encryption --- */
495
496 {
34e4f738 497 grand *g = fibrand_create(k->r->ops->word(k->r));
ab0ca95f 498 rsa_pub rpp;
252c122d 499 mp *m = mprand_range(MP_NEW, rp.n, g, 0);
500 mp *c;
501
ab0ca95f 502 rpp.n = rp.n;
503 rpp.e = rp.e;
504 c = rsa_qpubop(&rpp, MP_NEW, m);
505 c = rsa_qprivop(&rp, c, c, g);
252c122d 506
4b536f42 507 if (!MP_EQ(c, m))
252c122d 508 die(EXIT_FAILURE, "test encryption failed");
509 mp_drop(c);
510 mp_drop(m);
511 g->ops->destroy(g);
512 }
513
514 /* --- Allrighty then --- */
515
516 kd = &k->k->k;
517 key_structure(kd);
518 mpkey(kd, "n", rp.n, KCAT_PUB);
519 mpkey(kd, "e", rp.e, KCAT_PUB);
520
521 kd = key_structcreate(kd, "private");
522 key_structure(kd);
523 mpkey(kd, "d", rp.d, KCAT_PRIV | KF_BURN);
524 mpkey(kd, "p", rp.p, KCAT_PRIV | KF_BURN);
525 mpkey(kd, "q", rp.q, KCAT_PRIV | KF_BURN);
526 mpkey(kd, "q-inv", rp.q_inv, KCAT_PRIV | KF_BURN);
527 mpkey(kd, "d-mod-p", rp.dp, KCAT_PRIV | KF_BURN);
528 mpkey(kd, "d-mod-q", rp.dq, KCAT_PRIV | KF_BURN);
529 dolock(k, kd, "private");
530
ab0ca95f 531 rsa_privfree(&rp);
252c122d 532}
533
534static void alg_dsaparam(keyopts *k)
535{
536 static const char *pl[] = { "q", "p", "g", 0 };
537 if (!copyparam(k, pl)) {
538 dsa_param dp;
539 octet *p;
540 size_t sz;
541 dstr d = DSTR_INIT;
542 base64_ctx c;
543 key_data *kd = &k->k->k;
f4f8e305 544 dsa_seed ds;
252c122d 545
546 /* --- Choose appropriate bit lengths if necessary --- */
547
548 if (!k->qbits)
549 k->qbits = 160;
550 if (!k->bits)
551 k->bits = 768;
552
553 /* --- Allocate a seed block --- */
554
555 sz = (k->qbits + 7) >> 3;
556 p = sub_alloc(sz);
34e4f738 557 k->r->ops->fill(k->r, p, sz);
252c122d 558
559 /* --- Allocate the parameters --- */
560
f4f8e305 561 if (dsa_gen(&dp, k->qbits, k->bits, 0, p, sz, &ds,
40d5a112 562 (k->f & f_quiet) ? 0 : pgen_ev, 0))
252c122d 563 die(EXIT_FAILURE, "DSA parameter generation failed");
564
565 /* --- Store the parameters --- */
566
567 key_structure(kd);
568 mpkey(kd, "q", dp.q, KCAT_SHARE);
569 mpkey(kd, "p", dp.p, KCAT_SHARE);
570 mpkey(kd, "g", dp.g, KCAT_SHARE);
571 mp_drop(dp.q);
572 mp_drop(dp.p);
573 mp_drop(dp.g);
574
575 /* --- Store the seed for future verification --- */
576
577 base64_init(&c);
578 c.maxline = 0;
579 c.indent = "";
f4f8e305 580 base64_encode(&c, ds.p, ds.sz, &d);
252c122d 581 base64_encode(&c, 0, 0, &d);
582 key_putattr(k->kf, k->k, "seed", d.buf);
f4f8e305 583 DRESET(&d);
584 dstr_putf(&d, "%u", ds.count);
585 key_putattr(k->kf, k->k, "count", d.buf);
586 xfree(ds.p);
252c122d 587 sub_free(p, sz);
588 dstr_destroy(&d);
589 }
590}
591
592static void alg_dsa(keyopts *k)
593{
594 mp *q, *p, *g;
595 mp *x, *y;
596 mpmont mm;
597 key_data *kd = &k->k->k;
598
599 /* --- Get the shared parameters --- */
600
601 alg_dsaparam(k);
602 q = getmp(kd, "q");
603 p = getmp(kd, "p");
604 g = getmp(kd, "g");
605
606 /* --- Choose a private key --- */
607
34e4f738 608 x = mprand_range(MP_NEWSEC, q, k->r, 0);
252c122d 609 mpmont_create(&mm, p);
610 y = mpmont_exp(&mm, MP_NEW, g, x);
611
612 /* --- Store everything away --- */
613
614 mpkey(kd, "y", y, KCAT_PUB);
615
616 kd = key_structcreate(kd, "private");
617 key_structure(kd);
618 mpkey(kd, "x", x, KCAT_PRIV | KF_BURN);
619 dolock(k, kd, "private");
052b36d0 620
621 mp_drop(x); mp_drop(y);
252c122d 622}
623
624static void alg_dhparam(keyopts *k)
625{
052b36d0 626 static const char *pl[] = { "p", "q", "g", 0 };
34e4f738 627 key_data *kd = &k->k->k;
252c122d 628 if (!copyparam(k, pl)) {
052b36d0 629 dh_param dp;
40d5a112 630 int rc;
252c122d 631
3563e365 632 if (k->curve) {
633 qd_parse qd;
634
635 if (strcmp(k->curve, "list") == 0) {
636 const pentry *pe;
637 printf("Built-in prime groups:\n");
638 for (pe = ptab; pe->name; pe++)
639 printf(" %s\n", pe->name);
640 exit(0);
641 }
642 qd.p = k->curve;
643 if (dh_parse(&qd, &dp))
644 die(EXIT_FAILURE, "error in group spec: %s", qd.e);
645 goto done;
646 }
647
252c122d 648 if (!k->bits)
649 k->bits = 1024;
650
651 /* --- Choose a large safe prime number --- */
652
40d5a112 653 if (k->f & f_limlee) {
654 mp **f;
655 size_t nf;
656 if (!k->qbits)
657 k->qbits = 256;
658 rc = dh_limlee(&dp, k->qbits, k->bits,
659 (k->f & f_subgroup) ? DH_SUBGROUP : 0,
34e4f738 660 0, k->r, (k->f & f_quiet) ? 0 : pgen_ev, 0,
40d5a112 661 (k->f & f_quiet) ? 0 : pgen_evspin, 0, &nf, &f);
662 if (!rc) {
663 dstr d = DSTR_INIT;
664 size_t i;
665 for (i = 0; i < nf; i++) {
666 if (i)
667 dstr_puts(&d, ", ");
668 mp_writedstr(f[i], &d, 10);
669 mp_drop(f[i]);
670 }
671 key_putattr(k->kf, k->k, "factors", d.buf);
672 dstr_destroy(&d);
673 }
674 } else
34e4f738 675 rc = dh_gen(&dp, k->qbits, k->bits, 0, k->r,
40d5a112 676 (k->f & f_quiet) ? 0 : pgen_ev, 0);
677
678 if (rc)
252c122d 679 die(EXIT_FAILURE, "Diffie-Hellman parameter generation failed");
680
3563e365 681 done:
252c122d 682 key_structure(kd);
052b36d0 683 mpkey(kd, "p", dp.p, KCAT_SHARE);
684 mpkey(kd, "q", dp.q, KCAT_SHARE);
685 mpkey(kd, "g", dp.g, KCAT_SHARE);
686 mp_drop(dp.q);
687 mp_drop(dp.p);
688 mp_drop(dp.g);
34e4f738 689 }
252c122d 690}
691
692static void alg_dh(keyopts *k)
693{
694 mp *x, *y;
052b36d0 695 mp *p, *q, *g;
252c122d 696 mpmont mm;
697 key_data *kd = &k->k->k;
698
699 /* --- Get the shared parameters --- */
700
701 alg_dhparam(k);
702 p = getmp(kd, "p");
052b36d0 703 q = getmp(kd, "q");
252c122d 704 g = getmp(kd, "g");
705
706 /* --- Choose a suitable private key --- *
707 *
708 * Since %$g$% has order %$q$%, choose %$x < q$%.
709 */
710
34e4f738 711 x = mprand_range(MP_NEWSEC, q, k->r, 0);
252c122d 712
713 /* --- Compute the public key %$y = g^x \bmod p$% --- */
714
715 mpmont_create(&mm, p);
052b36d0 716 y = mpmont_exp(&mm, MP_NEW, g, x);
252c122d 717 mpmont_destroy(&mm);
718
719 /* --- Store everything away --- */
720
721 mpkey(kd, "y", y, KCAT_PUB);
722
723 kd = key_structcreate(kd, "private");
724 key_structure(kd);
725 mpkey(kd, "x", x, KCAT_PRIV | KF_BURN);
726 dolock(k, kd, "private");
052b36d0 727
728 mp_drop(x); mp_drop(y);
252c122d 729}
730
731static void alg_bbs(keyopts *k)
732{
ab0ca95f 733 bbs_priv bp;
252c122d 734 key_data *kd;
252c122d 735
736 /* --- Sanity checking --- */
737
738 if (k->p)
739 die(EXIT_FAILURE, "no shared parameters for Blum-Blum-Shub keys");
740 if (!k->bits)
741 k->bits = 1024;
742
743 /* --- Generate the BBS parameters --- */
744
34e4f738 745 if (bbs_gen(&bp, k->bits, k->r, 0,
052b36d0 746 (k->f & f_quiet) ? 0 : pgen_ev, 0))
252c122d 747 die(EXIT_FAILURE, "Blum-Blum-Shub key generation failed");
252c122d 748
749 /* --- Allrighty then --- */
750
751 kd = &k->k->k;
752 key_structure(kd);
753 mpkey(kd, "n", bp.n, KCAT_PUB);
754
755 kd = key_structcreate(kd, "private");
756 key_structure(kd);
757 mpkey(kd, "p", bp.p, KCAT_PRIV | KF_BURN);
758 mpkey(kd, "q", bp.q, KCAT_PRIV | KF_BURN);
759 dolock(k, kd, "private");
760
ab0ca95f 761 bbs_privfree(&bp);
252c122d 762}
763
1ba83484 764static void alg_ecparam(keyopts *k)
765{
766 static const char *pl[] = { "curve", 0 };
767 if (!copyparam(k, pl)) {
768 ec_info ei;
769 const char *e;
770 key_data *kd = &k->k->k;
771
772 /* --- Decide on a curve --- */
773
774 if (!k->bits) k->bits = 256;
3563e365 775 if (k->curve && strcmp(k->curve, "list") == 0) {
776 const ecentry *ee;
777 printf("Built-in elliptic curves:\n");
778 for (ee = ectab; ee->name; ee++)
779 printf(" %s\n", ee->name);
780 exit(0);
781 }
1ba83484 782 if (!k->curve) {
783 if (k->bits <= 56) k->curve = "secp112r1";
784 else if (k->bits <= 64) k->curve = "secp128r1";
785 else if (k->bits <= 80) k->curve = "secp160r1";
786 else if (k->bits <= 96) k->curve = "secp192r1";
787 else if (k->bits <= 112) k->curve = "secp224r1";
788 else if (k->bits <= 128) k->curve = "secp256r1";
789 else if (k->bits <= 192) k->curve = "secp384r1";
790 else if (k->bits <= 256) k->curve = "secp521r1";
791 else
792 die(EXIT_FAILURE, "no built-in curves provide %u-bit security",
793 k->bits);
794 }
795
796 /* --- Check it --- */
797
798 if ((e = ec_getinfo(&ei, k->curve)) != 0)
799 die(EXIT_FAILURE, "error in curve spec: %s", e);
34e4f738 800 if (!(k->f & f_quiet) && (e = ec_checkinfo(&ei, k->r)) != 0)
1ba83484 801 moan("WARNING! curve check failed: %s", e);
802 ec_freeinfo(&ei);
803
804 /* --- Write out the answer --- */
805
806 key_structure(kd);
807 kd = key_structcreate(kd, "curve");
808 key_string(kd, k->curve);
809 kd->e |= KCAT_SHARE;
810 }
811}
812
813static void alg_ec(keyopts *k)
814{
815 key_data *kd = &k->k->k;
816 key_data *kkd;
817 mp *x = MP_NEW;
818 ec p = EC_INIT;
819 const char *e;
820 ec_info ei;
821
822 /* --- Get the curve --- */
823
824 alg_ecparam(k);
825 if ((kkd = key_structfind(kd, "curve")) == 0)
826 die(EXIT_FAILURE, "unexpected failure looking up subkey `curve')");
827 if ((kkd->e & KF_ENCMASK) != KENC_STRING)
828 die(EXIT_FAILURE, "subkey `curve' is not a string");
829 if ((e = ec_getinfo(&ei, kkd->u.p)) != 0)
830 die(EXIT_FAILURE, "error in curve spec: %s", e);
831
832 /* --- Invent a private exponent and compute the public key --- */
833
34e4f738 834 x = mprand_range(MP_NEWSEC, ei.r, k->r, 0);
1ba83484 835 ec_mul(ei.c, &p, &ei.g, x);
836
837 /* --- Store everything away --- */
838
839 kkd = key_structcreate(kd, "p");
840 key_ec(kkd, &p);
841 kkd->e |= KCAT_PUB;
842 kkd = key_structcreate(kd, "private");
843 key_structure(kkd);
844 mpkey(kkd, "x", x, KCAT_PRIV | KF_BURN);
845
846 /* --- Done --- */
847
848 ec_freeinfo(&ei);
849 mp_drop(x);
850}
851
252c122d 852/* --- The algorithm tables --- */
853
854typedef struct keyalg {
855 const char *name;
856 void (*proc)(keyopts *o);
857 const char *help;
858} keyalg;
859
860static keyalg algtab[] = {
861 { "binary", alg_binary, "Plain binary data" },
862 { "des", alg_des, "Binary with DES-style parity" },
863 { "rsa", alg_rsa, "RSA public-key encryption" },
864 { "dsa", alg_dsa, "DSA digital signatures" },
865 { "dsa-param", alg_dsaparam, "DSA shared parameters" },
866 { "dh", alg_dh, "Diffie-Hellman key exchange" },
867 { "dh-param", alg_dhparam, "Diffie-Hellman parameters" },
868 { "bbs", alg_bbs, "Blum-Blum-Shub generator" },
1ba83484 869 { "ec-param", alg_ecparam, "Elliptic curve parameters" },
870 { "ec", alg_ec, "Elliptic curve crypto" },
252c122d 871 { 0, 0 }
872};
d03ab969 873
874/* --- @cmd_add@ --- */
875
876static int cmd_add(int argc, char *argv[])
877{
878 key_file f;
d03ab969 879 time_t exp = KEXP_EXPIRE;
252c122d 880 const char *tag = 0, *ptag = 0;
d03ab969 881 const char *c = 0;
252c122d 882 keyalg *alg = algtab;
052b36d0 883 const char *rtag = 0;
34e4f738 884 const struct seedalg *sa = SEEDALG_DEFAULT;
885 keyopts k = { 0, 0, DSTR_INIT, 0, 0, 0, 0, 0 };
886 const char *seed = 0;
887 k.r = &rand_global;
d03ab969 888
889 /* --- Parse options for the subcommand --- */
890
891 for (;;) {
892 static struct option opt[] = {
252c122d 893 { "algorithm", OPTF_ARGREQ, 0, 'a' },
d03ab969 894 { "bits", OPTF_ARGREQ, 0, 'b' },
252c122d 895 { "qbits", OPTF_ARGREQ, 0, 'B' },
896 { "parameters", OPTF_ARGREQ, 0, 'p' },
d03ab969 897 { "expire", OPTF_ARGREQ, 0, 'e' },
898 { "comment", OPTF_ARGREQ, 0, 'c' },
252c122d 899 { "tag", OPTF_ARGREQ, 0, 't' },
ce70ec47 900 { "rand-id", OPTF_ARGREQ, 0, 'R' },
1ba83484 901 { "curve", OPTF_ARGREQ, 0, 'C' },
34e4f738 902 { "seedalg", OPTF_ARGREQ, 0, 'A' },
903 { "seed", OPTF_ARGREQ, 0, 's' },
904 { "newseed", OPTF_ARGREQ, 0, 'n' },
252c122d 905 { "lock", 0, 0, 'l' },
906 { "quiet", 0, 0, 'q' },
40d5a112 907 { "lim-lee", 0, 0, 'L' },
908 { "subgroup", 0, 0, 'S' },
d03ab969 909 { 0, 0, 0, 0 }
910 };
34e4f738 911 int i = mdwopt(argc, argv, "+a:b:B:p:e:c:t:R:C:A:s:n:lqrLS", opt, 0, 0, 0);
d03ab969 912 if (i < 0)
913 break;
914
915 /* --- Handle the various options --- */
916
917 switch (i) {
918
252c122d 919 /* --- Read an algorithm name --- */
920
921 case 'a': {
922 keyalg *a;
923 size_t sz = strlen(optarg);
924
925 if (strcmp(optarg, "list") == 0) {
926 for (a = algtab; a->name; a++)
927 printf("%-10s %s\n", a->name, a->help);
928 return (0);
929 }
930
931 alg = 0;
932 for (a = algtab; a->name; a++) {
933 if (strncmp(optarg, a->name, sz) == 0) {
934 if (a->name[sz] == 0) {
935 alg = a;
936 break;
937 } else if (alg)
938 die(EXIT_FAILURE, "ambiguous algorithm name `%s'", optarg);
939 else
940 alg = a;
941 }
942 }
943 if (!alg)
944 die(EXIT_FAILURE, "unknown algorithm name `%s'", optarg);
945 } break;
946
d03ab969 947 /* --- Bits must be nonzero and a multiple of 8 --- */
948
252c122d 949 case 'b': {
950 char *p;
951 k.bits = strtoul(optarg, &p, 0);
952 if (k.bits == 0 || *p != 0)
953 die(EXIT_FAILURE, "bad bitlength `%s'", optarg);
954 } break;
955
956 case 'B': {
957 char *p;
958 k.qbits = strtoul(optarg, &p, 0);
959 if (k.qbits == 0 || *p != 0)
960 die(EXIT_FAILURE, "bad bitlength `%s'", optarg);
961 } break;
962
963 /* --- Parameter selection --- */
964
965 case 'p':
966 ptag = optarg;
d03ab969 967 break;
968
969 /* --- Expiry dates get passed to @get_date@ for parsing --- */
970
971 case 'e':
252c122d 972 if (strncmp(optarg, "forever", strlen(optarg)) == 0)
d03ab969 973 exp = KEXP_FOREVER;
974 else {
975 exp = get_date(optarg, 0);
976 if (exp == -1)
252c122d 977 die(EXIT_FAILURE, "bad expiry date `%s'", optarg);
d03ab969 978 }
979 break;
980
981 /* --- Store comments without interpretation --- */
982
983 case 'c':
252c122d 984 if (key_chkcomment(optarg))
985 die(EXIT_FAILURE, "bad comment string `%s'", optarg);
d03ab969 986 c = optarg;
987 break;
988
1ba83484 989 /* --- Elliptic curve parameters --- */
990
991 case 'C':
1ba83484 992 k.curve = optarg;
993 break;
994
252c122d 995 /* --- Store tags --- */
996
997 case 't':
998 if (key_chkident(optarg))
999 die(EXIT_FAILURE, "bad tag string `%s'", optarg);
1000 tag = optarg;
1001 break;
ce70ec47 1002 case 'r':
1003 k.f |= f_retag;
1004 break;
252c122d 1005
34e4f738 1006 /* --- Seeding --- */
1007
1008 case 'A': {
1009 const struct seedalg *ss;
1010 if (strcmp(optarg, "list") == 0) {
1011 printf("Seed algorithms:\n");
1012 for (ss = seedtab; ss->p; ss++)
1013 printf(" %s\n", ss->p);
1014 exit(0);
1015 }
1016 if (seed) die(EXIT_FAILURE, "seed already set -- put -A first");
1017 sa = 0;
1018 for (ss = seedtab; ss->p; ss++) {
1019 if (strcmp(optarg, ss->p) == 0)
1020 sa = ss;
1021 }
1022 if (!sa)
1023 die(EXIT_FAILURE, "seed algorithm `%s' not known", optarg);
1024 } break;
1025
1026 case 's': {
1027 base64_ctx b;
1028 dstr d = DSTR_INIT;
1029 if (seed) die(EXIT_FAILURE, "seed already set");
1030 base64_init(&b);
1031 base64_decode(&b, optarg, strlen(optarg), &d);
1032 base64_decode(&b, 0, 0, &d);
1033 k.r = sa->gen(d.buf, d.len);
1034 seed = optarg;
1035 dstr_destroy(&d);
1036 } break;
1037
1038 case 'n': {
1039 base64_ctx b;
1040 dstr d = DSTR_INIT;
1041 char *p;
1042 unsigned n = strtoul(optarg, &p, 0);
1043 if (n == 0 || *p != 0 || n % 8 != 0)
1044 die(EXIT_FAILURE, "bad seed length `%s'", optarg);
1045 if (seed) die(EXIT_FAILURE, "seed already set");
1046 n /= 8;
1047 p = xmalloc(n);
1048 rand_get(RAND_GLOBAL, p, n);
1049 base64_init(&b);
1050 base64_encode(&b, p, n, &d);
1051 base64_encode(&b, 0, 0, &d);
1052 seed = d.buf;
1053 k.r = sa->gen(p, n);
1054 } break;
1055
252c122d 1056 /* --- Other flags --- */
1057
ce70ec47 1058 case 'R':
052b36d0 1059 rtag = optarg;
1060 break;
252c122d 1061 case 'l':
1062 k.f |= f_lock;
1063 break;
1064 case 'q':
1065 k.f |= f_quiet;
1066 break;
40d5a112 1067 case 'L':
1068 k.f |= f_limlee;
1069 break;
1070 case 'S':
1071 k.f |= f_subgroup;
1072 break;
252c122d 1073
d03ab969 1074 /* --- Other things are bogus --- */
1075
1076 default:
252c122d 1077 k.f |= f_bogus;
d03ab969 1078 break;
1079 }
1080 }
1081
252c122d 1082 /* --- Various sorts of bogosity --- */
d03ab969 1083
252c122d 1084 if ((k.f & f_bogus) || optind + 1 > argc) {
d03ab969 1085 die(EXIT_FAILURE,
252c122d 1086 "Usage: add [options] type [attr...]");
d03ab969 1087 }
252c122d 1088 if (key_chkident(argv[optind]))
1089 die(EXIT_FAILURE, "bad key type `%s'", argv[optind]);
1090
1091 /* --- Set up various bits of the state --- */
1092
d03ab969 1093 if (exp == KEXP_EXPIRE)
1094 exp = time(0) + 14 * 24 * 60 * 60;
1095
252c122d 1096 /* --- Open the file and create the basic key block --- *
1097 *
1098 * Keep on generating keyids until one of them doesn't collide.
1099 */
d03ab969 1100
252c122d 1101 doopen(&f, KOPEN_WRITE);
1102 k.kf = &f;
1103
052b36d0 1104 /* --- Key the generator --- */
1105
1106 keyrand(&f, rtag);
1107
252c122d 1108 for (;;) {
1109 uint32 id = rand_global.ops->word(&rand_global);
1110 int err;
1111 k.k = key_new(&f, id, argv[optind], exp, &err);
1112 if (k.k)
1113 break;
1114 if (err != KERR_DUPID)
1115 die(EXIT_FAILURE, "error adding new key: %s", key_strerror(err));
1116 }
d03ab969 1117
252c122d 1118 /* --- Set various simple attributes --- */
d03ab969 1119
252c122d 1120 if (tag) {
ce70ec47 1121 int err;
1122 key *kk;
1123 if (k.f & f_retag) {
1124 if ((kk = key_bytag(&f, tag)) != 0 && strcmp(kk->tag, tag) == 0)
1125 key_settag(&f, kk, 0);
1126 }
1127 if ((err = key_settag(&f, k.k, tag)) != 0)
252c122d 1128 die(EXIT_FAILURE, "error setting key tag: %s", key_strerror(err));
1129 }
d03ab969 1130
252c122d 1131 if (c) {
1132 int err = key_setcomment(&f, k.k, c);
1133 if (err)
1134 die(EXIT_FAILURE, "error setting key comment: %s", key_strerror(err));
1135 }
1136
1137 setattr(&f, k.k, argv + optind + 1);
34e4f738 1138 if (seed) {
1139 key_putattr(&f, k.k, "genseed", seed);
1140 key_putattr(&f, k.k, "seedalg", sa->p);
1141 }
252c122d 1142
1143 key_fulltag(k.k, &k.tag);
1144
1145 /* --- Find the parameter key --- */
1146
1147 if (ptag) {
1148 if ((k.p = key_bytag(&f, ptag)) == 0)
1149 die(EXIT_FAILURE, "parameter key `%s' not found", ptag);
1150 if ((k.p->k.e & KF_ENCMASK) != KENC_STRUCT)
1151 die(EXIT_FAILURE, "parameter key `%s' is not structured", ptag);
1152 }
1153
1154 /* --- Now generate the actual key data --- */
1155
1156 alg->proc(&k);
1157
1158 /* --- Done --- */
d03ab969 1159
d03ab969 1160 doclose(&f);
1161 return (0);
1162}
1163
252c122d 1164/*----- Key listing -------------------------------------------------------*/
1165
1166/* --- Listing options --- */
1167
1168typedef struct listopts {
1169 const char *tfmt; /* Date format (@strftime@-style) */
1170 int v; /* Verbosity level */
1171 unsigned f; /* Various flags */
1172 time_t t; /* Time now (for key expiry) */
1173 key_filter kf; /* Filter for matching keys */
1174} listopts;
1175
1176/* --- Listing flags --- */
1177
16efd15b 1178#define f_newline 2u /* Write newline before next entry */
1179#define f_attr 4u /* Written at least one attribute */
1180#define f_utc 8u /* Emit UTC time, not local time */
252c122d 1181
1182/* --- @showkeydata@ --- *
1183 *
1184 * Arguments: @key_data *k@ = pointer to key to write
1185 * @int ind@ = indentation level
1186 * @listopts *o@ = listing options
1187 * @dstr *d@ = tag string for this subkey
1188 *
1189 * Returns: ---
1190 *
1191 * Use: Emits a piece of key data in a human-readable format.
1192 */
1193
1194static void showkeydata(key_data *k, int ind, listopts *o, dstr *d)
1195{
1196#define INDENT(i) do { \
1197 int _i; \
1198 for (_i = 0; _i < (i); _i++) { \
1199 putchar(' '); \
1200 } \
1201} while (0)
1202
1203 switch (k->e & KF_ENCMASK) {
1204
1205 /* --- Binary key data --- *
1206 *
1207 * Emit as a simple hex dump.
1208 */
1209
1210 case KENC_BINARY: {
1211 const octet *p = k->u.k.k;
1212 const octet *l = p + k->u.k.sz;
1213 size_t sz = 0;
1214
1215 fputs(" {", stdout);
1216 while (p < l) {
1217 if (sz % 16 == 0) {
1218 putchar('\n');
1219 INDENT(ind + 2);
1220 } else if (sz % 8 == 0)
1221 fputs(" ", stdout);
1222 else
1223 putc(' ', stdout);
1224 printf("%02x", *p++);
1225 sz++;
1226 }
1227 putchar('\n');
1228 INDENT(ind);
1229 fputs("}\n", stdout);
1230 } break;
1231
1232 /* --- Encrypted data --- *
1233 *
1234 * If the user is sufficiently keen, ask for a passphrase and decrypt the
1235 * key. Otherwise just say that it's encrypted and move on.
1236 */
1237
1238 case KENC_ENCRYPT:
1239 if (o->v <= 3)
1240 fputs(" encrypted\n", stdout);
1241 else {
1242 key_data kd;
1243 if (key_punlock(d->buf, k, &kd))
1244 printf(" <failed to unlock %s>\n", d->buf);
1245 else {
1246 fputs(" encrypted", stdout);
1247 showkeydata(&kd, ind, o, d);
1248 key_destroy(&kd);
1249 }
1250 }
1251 break;
1252
1253 /* --- Integer keys --- *
1254 *
1255 * Emit as a large integer in decimal. This makes using the key in
1256 * `calc' or whatever easier.
1257 */
1258
1259 case KENC_MP:
1260 putchar(' ');
1261 mp_writefile(k->u.m, stdout, 10);
1262 putchar('\n');
1263 break;
1264
1ba83484 1265 /* --- Strings --- */
1266
1267 case KENC_STRING:
1268 printf(" `%s'\n", k->u.p);
1269 break;
1270
1271 /* --- Elliptic curve points --- */
1272
1273 case KENC_EC:
1274 fputs(" 0x", stdout); mp_writefile(k->u.e.x, stdout, 16);
1275 fputs(", 0x", stdout); mp_writefile(k->u.e.y, stdout, 16);
1276 putchar('\n');
1277 break;
1278
252c122d 1279 /* --- Structured keys --- *
1280 *
1281 * Just iterate over the subkeys.
1282 */
1283
1284 case KENC_STRUCT: {
1285 sym_iter i;
1286 key_struct *ks;
1287 size_t n = d->len;
1288
1289 fputs(" {\n", stdout);
1290 for (sym_mkiter(&i, &k->u.s); (ks = sym_next(&i)) != 0; ) {
1291 if (!key_match(&ks->k, &o->kf))
1292 continue;
1293 INDENT(ind + 2);
1294 printf("%s =", SYM_NAME(ks));
1295 d->len = n;
1296 dstr_putf(d, ".%s", SYM_NAME(ks));
1297 showkeydata(&ks->k, ind + 2, o, d);
1298 }
1299 INDENT(ind);
1300 fputs("}\n", stdout);
1301 } break;
1302 }
1303
1304#undef INDENT
1305}
1306
1307/* --- @showkey@ --- *
1308 *
1309 * Arguments: @key *k@ = pointer to key to show
1310 * @listopts *o@ = pointer to listing options
1311 *
1312 * Returns: ---
1313 *
1314 * Use: Emits a listing of a particular key.
1315 */
1316
1317static void showkey(key *k, listopts *o)
1318{
1319 char ebuf[24], dbuf[24];
1320 struct tm *tm;
1321
1322 /* --- Skip the key if the filter doesn't match --- */
1323
1324 if (!key_match(&k->k, &o->kf))
1325 return;
1326
1327 /* --- Sort out the expiry and deletion times --- */
1328
1329 if (KEY_EXPIRED(o->t, k->exp))
1330 strcpy(ebuf, "expired");
1331 else if (k->exp == KEXP_FOREVER)
1332 strcpy(ebuf, "forever");
1333 else {
1334 tm = (o->f & f_utc) ? gmtime(&k->exp) : localtime(&k->exp);
1335 strftime(ebuf, sizeof(ebuf), o->tfmt, tm);
1336 }
1337
1338 if (KEY_EXPIRED(o->t, k->del))
1339 strcpy(dbuf, "deleted");
1340 else if (k->del == KEXP_FOREVER)
1341 strcpy(dbuf, "forever");
1342 else {
1343 tm = (o->f & f_utc) ? gmtime(&k->del) : localtime(&k->del);
1344 strftime(dbuf, sizeof(dbuf), o->tfmt, tm);
1345 }
1346
1347 /* --- If in compact format, just display and quit --- */
1348
1349 if (!o->v) {
1350 if (!(o->f & f_newline)) {
1351 printf("%8s %-20s %-20s %-10s %-10s\n",
1352 "Id", "Tag", "Type", "Expire", "Delete");
1353 }
1354 printf("%08lx %-20s %-20s %-10s %-10s\n",
1355 (unsigned long)k->id, k->tag ? k->tag : "<none>",
1356 k->type, ebuf, dbuf);
1357 o->f |= f_newline;
1358 return;
1359 }
1360
1361 /* --- Display the standard header --- */
1362
1363 if (o->f & f_newline)
1364 fputc('\n', stdout);
1365 printf("keyid: %08lx\n", (unsigned long)k->id);
1366 printf("tag: %s\n", k->tag ? k->tag : "<none>");
1367 printf("type: %s\n", k->type);
1368 printf("expiry: %s\n", ebuf);
1369 printf("delete: %s\n", dbuf);
1370 printf("comment: %s\n", k->c ? k->c : "<none>");
1371
1372 /* --- Display the attributes --- */
1373
1374 if (o->v > 1) {
1375 key_attriter i;
1376 const char *av, *an;
1377
1378 o->f &= ~f_attr;
1379 printf("attributes:");
1380 for (key_mkattriter(&i, k); key_nextattr(&i, &an, &av); ) {
dce3fb0d 1381 printf("\n %s = %s", an, av);
252c122d 1382 o->f |= f_attr;
1383 }
1384 if (o->f & f_attr)
1385 fputc('\n', stdout);
1386 else
1387 puts(" <none>");
1388 }
1389
1390 /* --- If dumping requested, dump the raw key data --- */
1391
1392 if (o->v > 2) {
1393 dstr d = DSTR_INIT;
1394 fputs("key:", stdout);
1395 key_fulltag(k, &d);
1396 showkeydata(&k->k, 0, o, &d);
1397 dstr_destroy(&d);
1398 }
1399
1400 o->f |= f_newline;
1401}
1402
1403/* --- @cmd_list@ --- */
1404
1405static int cmd_list(int argc, char *argv[])
1406{
1407 key_file f;
1408 key *k;
1409 listopts o = { 0, 0, 0, 0, { 0, 0 } };
1410
1411 /* --- Parse subcommand options --- */
1412
1413 for (;;) {
1414 static struct option opt[] = {
1415 { "quiet", 0, 0, 'q' },
1416 { "verbose", 0, 0, 'v' },
1417 { "utc", 0, 0, 'u' },
1418 { "filter", OPTF_ARGREQ, 0, 'f' },
1419 { 0, 0, 0, 0 }
1420 };
1421 int i = mdwopt(argc, argv, "+uqvf:", opt, 0, 0, 0);
1422 if (i < 0)
1423 break;
1424
1425 switch (i) {
1426 case 'u':
1427 o.f |= f_utc;
1428 break;
1429 case 'q':
1430 if (o.v)
1431 o.v--;
1432 break;
1433 case 'v':
1434 o.v++;
1435 break;
1436 case 'f': {
1437 char *p;
1438 int e = key_readflags(optarg, &p, &o.kf.f, &o.kf.m);
1439 if (e || *p)
1440 die(EXIT_FAILURE, "bad filter string `%s'", optarg);
1441 } break;
1442 default:
1443 o.f |= f_bogus;
1444 break;
1445 }
1446 }
1447
1448 if (o.f & f_bogus)
1449 die(EXIT_FAILURE, "Usage: list [-uqv] [-f filter] [tag...]");
1450
1451 /* --- Open the key file --- */
1452
1453 doopen(&f, KOPEN_READ);
1454 o.t = time(0);
1455
1456 /* --- Set up the time format --- */
1457
1458 if (!o.v)
1459 o.tfmt = "%Y-%m-%d";
1460 else if (o.f & f_utc)
1461 o.tfmt = "%Y-%m-%d %H:%M:%S UTC";
1462 else
1463 o.tfmt = "%Y-%m-%d %H:%M:%S %Z";
1464
1465 /* --- If specific keys were requested use them, otherwise do all --- *
1466 *
1467 * Some day, this might turn into a wildcard match.
1468 */
1469
1470 if (optind < argc) {
1471 do {
1472 if ((k = key_bytag(&f, argv[optind])) != 0)
1473 showkey(k, &o);
1474 else {
1475 moan("key `%s' not found", argv[optind]);
1476 o.f |= f_bogus;
1477 }
1478 optind++;
1479 } while (optind < argc);
1480 } else {
1481 key_iter i;
1482 for (key_mkiter(&i, &f); (k = key_next(&i)) != 0; )
1483 showkey(k, &o);
1484 }
1485
1486 /* --- Done --- */
1487
1488 doclose(&f);
1489 if (o.f & f_bogus)
1490 return (EXIT_FAILURE);
1491 else
1492 return (0);
1493}
1494
1495/*----- Command implementation --------------------------------------------*/
1496
d03ab969 1497/* --- @cmd_expire@ --- */
1498
1499static int cmd_expire(int argc, char *argv[])
1500{
1501 key_file f;
1502 key *k;
d03ab969 1503 int i;
1504 int rc = 0;
1505
1506 if (argc < 2)
252c122d 1507 die(EXIT_FAILURE, "Usage: expire tag...");
d03ab969 1508 doopen(&f, KOPEN_WRITE);
1509 for (i = 1; i < argc; i++) {
252c122d 1510 if ((k = key_bytag(&f, argv[i])) != 0)
d03ab969 1511 key_expire(&f, k);
1512 else {
252c122d 1513 moan("key `%s' not found", argv[i]);
d03ab969 1514 rc = 1;
1515 }
1516 }
1517 doclose(&f);
1518 return (rc);
1519}
1520
1521/* --- @cmd_delete@ --- */
1522
1523static int cmd_delete(int argc, char *argv[])
1524{
1525 key_file f;
1526 key *k;
d03ab969 1527 int i;
1528 int rc = 0;
1529
1530 if (argc < 2)
252c122d 1531 die(EXIT_FAILURE, "Usage: delete tag...");
d03ab969 1532 doopen(&f, KOPEN_WRITE);
1533 for (i = 1; i < argc; i++) {
252c122d 1534 if ((k = key_bytag(&f, argv[i])) != 0)
d03ab969 1535 key_delete(&f, k);
1536 else {
252c122d 1537 moan("key `%s' not found", argv[i]);
d03ab969 1538 rc = 1;
1539 }
1540 }
1541 doclose(&f);
1542 return (rc);
1543}
1544
1545/* --- @cmd_setattr@ --- */
1546
1547static int cmd_setattr(int argc, char *argv[])
1548{
1549 key_file f;
1550 key *k;
d03ab969 1551
1552 if (argc < 3)
252c122d 1553 die(EXIT_FAILURE, "Usage: setattr tag attr...");
d03ab969 1554 doopen(&f, KOPEN_WRITE);
252c122d 1555 if ((k = key_bytag(&f, argv[1])) == 0)
1556 die(EXIT_FAILURE, "key `%s' not found", argv[1]);
d03ab969 1557 setattr(&f, k, argv + 2);
1558 doclose(&f);
1559 return (0);
1560}
1561
252c122d 1562/* --- @cmd_finger@ --- */
d03ab969 1563
252c122d 1564static void fingerprint(key *k, const key_filter *kf)
d03ab969 1565{
252c122d 1566 rmd160_ctx r;
1567 octet hash[RMD160_HASHSZ];
1568 dstr d = DSTR_INIT;
1569 int i;
d03ab969 1570
052b36d0 1571 if (!key_encode(&k->k, &d, kf))
252c122d 1572 return;
1573 rmd160_init(&r);
052b36d0 1574 rmd160_hash(&r, d.buf, d.len);
252c122d 1575 rmd160_done(&r, hash);
052b36d0 1576
1577 DRESET(&d);
252c122d 1578 key_fulltag(k, &d);
1579 for (i = 0; i < sizeof(hash); i++) {
1580 if (i && i % 4 == 0)
1581 putchar('-');
1582 printf("%02x", hash[i]);
1583 }
1584 printf(" %s\n", d.buf);
1585 dstr_destroy(&d);
1586}
d03ab969 1587
252c122d 1588static int cmd_finger(int argc, char *argv[])
1589{
1590 key_file f;
1591 int rc = 0;
1592 key_filter kf = { KF_NONSECRET, KF_NONSECRET };
d03ab969 1593
1594 for (;;) {
1595 static struct option opt[] = {
252c122d 1596 { "filter", OPTF_ARGREQ, 0, 'f' },
1597 { 0, 0, 0, 0 }
d03ab969 1598 };
ce70ec47 1599 int i = mdwopt(argc, argv, "+f:", opt, 0, 0, 0);
d03ab969 1600 if (i < 0)
1601 break;
d03ab969 1602 switch (i) {
252c122d 1603 case 'f': {
1604 char *p;
1605 int err = key_readflags(optarg, &p, &kf.f, &kf.m);
1606 if (err || *p)
1607 die(EXIT_FAILURE, "bad filter string `%s'", optarg);
1608 } break;
d03ab969 1609 default:
252c122d 1610 rc = 1;
d03ab969 1611 break;
1612 }
1613 }
1614
252c122d 1615 argv += optind; argc -= optind;
1616 if (rc)
1617 die(EXIT_FAILURE, "Usage: fingerprint [-f filter] [tag...]");
d03ab969 1618
1619 doopen(&f, KOPEN_READ);
d03ab969 1620
252c122d 1621 if (argc) {
1622 int i;
1623 for (i = 0; i < argc; i++) {
1624 key *k = key_bytag(&f, argv[i]);
1625 if (k)
1626 fingerprint(k, &kf);
1627 else {
1628 rc = 1;
1629 moan("key `%s' not found", argv[i]);
1630 }
1631 }
1632 } else {
1633 key_iter i;
1634 key *k;
1635 for (key_mkiter(&i, &f); (k = key_next(&i)) != 0; )
1636 fingerprint(k, &kf);
1637 }
1638 return (rc);
1639}
d03ab969 1640
252c122d 1641/* --- @cmd_comment@ --- */
d03ab969 1642
252c122d 1643static int cmd_comment(int argc, char *argv[])
1644{
1645 key_file f;
1646 key *k;
1647 int err;
d03ab969 1648
252c122d 1649 if (argc < 2 || argc > 3)
1650 die(EXIT_FAILURE, "Usage: comment tag [comment]");
1651 doopen(&f, KOPEN_WRITE);
1652 if ((k = key_bytag(&f, argv[1])) == 0)
1653 die(EXIT_FAILURE, "key `%s' not found", argv[1]);
1654 if ((err = key_setcomment(&f, k, argv[2])) != 0)
1655 die(EXIT_FAILURE, "bad comment `%s': %s", argv[2], key_strerror(err));
1656 doclose(&f);
1657 return (0);
1658}
d03ab969 1659
252c122d 1660/* --- @cmd_tag@ --- */
d03ab969 1661
252c122d 1662static int cmd_tag(int argc, char *argv[])
1663{
1664 key_file f;
1665 key *k;
1666 int err;
ce70ec47 1667 unsigned flags = 0;
1668 int rc = 0;
d03ab969 1669
ce70ec47 1670 for (;;) {
1671 static struct option opt[] = {
1672 { "retag", 0, 0, 'r' },
1673 { 0, 0, 0, 0 }
1674 };
1675 int i = mdwopt(argc, argv, "+r", opt, 0, 0, 0);
1676 if (i < 0)
1677 break;
1678 switch (i) {
1679 case 'r':
1680 flags |= f_retag;
1681 break;
1682 default:
1683 rc = 1;
1684 break;
1685 }
1686 }
1687
1688 argv += optind; argc -= optind;
1689 if (argc < 1 || argc > 2 || rc)
1690 die(EXIT_FAILURE, "Usage: tag [-r] tag [new-tag]");
252c122d 1691 doopen(&f, KOPEN_WRITE);
ce70ec47 1692 if (flags & f_retag) {
1693 if ((k = key_bytag(&f, argv[1])) != 0 && strcmp(k->tag, argv[1]) == 0)
1694 key_settag(&f, k, 0);
1695 }
1696 if ((k = key_bytag(&f, argv[0])) == 0)
1697 die(EXIT_FAILURE, "key `%s' not found", argv[0]);
1698 if ((err = key_settag(&f, k, argv[1])) != 0)
1699 die(EXIT_FAILURE, "bad tag `%s': %s", argv[1], key_strerror(err));
252c122d 1700 doclose(&f);
1701 return (0);
1702}
d03ab969 1703
252c122d 1704/* --- @cmd_lock@ --- */
d03ab969 1705
252c122d 1706static int cmd_lock(int argc, char *argv[])
1707{
1708 key_file f;
1709 key *k;
1710 key_data *kd;
1711 dstr d = DSTR_INIT;
d03ab969 1712
252c122d 1713 if (argc != 2)
1714 die(EXIT_FAILURE, "Usage: lock qtag");
1715 doopen(&f, KOPEN_WRITE);
1716 if (key_qtag(&f, argv[1], &d, &k, &kd))
1717 die(EXIT_FAILURE, "key `%s' not found", argv[1]);
1718 if (kd->e == KENC_ENCRYPT && key_punlock(d.buf, kd, kd))
1719 die(EXIT_FAILURE, "couldn't unlock key `%s'", d.buf);
1720 if (key_plock(d.buf, kd, kd))
1721 die(EXIT_FAILURE, "failed to lock key `%s'", d.buf);
1722 f.f |= KF_MODIFIED;
1723 doclose(&f);
1724 return (0);
1725}
d03ab969 1726
252c122d 1727/* --- @cmd_unlock@ --- */
d03ab969 1728
252c122d 1729static int cmd_unlock(int argc, char *argv[])
1730{
1731 key_file f;
1732 key *k;
1733 key_data *kd;
1734 dstr d = DSTR_INIT;
d03ab969 1735
252c122d 1736 if (argc != 2)
1737 die(EXIT_FAILURE, "Usage: unlock qtag");
1738 doopen(&f, KOPEN_WRITE);
1739 if (key_qtag(&f, argv[1], &d, &k, &kd))
1740 die(EXIT_FAILURE, "key `%s' not found", argv[1]);
1741 if (kd->e != KENC_ENCRYPT)
1742 die(EXIT_FAILURE, "key `%s' is not encrypted", d.buf);
1743 if (kd->e == KENC_ENCRYPT && key_punlock(d.buf, kd, kd))
1744 die(EXIT_FAILURE, "couldn't unlock key `%s'", d.buf);
1745 f.f |= KF_MODIFIED;
d03ab969 1746 doclose(&f);
1747 return (0);
1748}
1749
1750/* --- @cmd_extract@ --- */
1751
1752static int cmd_extract(int argc, char *argv[])
1753{
1754 key_file f;
1755 key *k;
d03ab969 1756 int i;
1757 int rc = 0;
252c122d 1758 key_filter kf = { 0, 0 };
d03ab969 1759 FILE *fp;
1760
252c122d 1761 for (;;) {
1762 static struct option opt[] = {
1763 { "filter", OPTF_ARGREQ, 0, 'f' },
1764 { 0, 0, 0, 0 }
1765 };
1766 int i = mdwopt(argc, argv, "f:", opt, 0, 0, 0);
1767 if (i < 0)
1768 break;
1769 switch (i) {
1770 case 'f': {
1771 char *p;
1772 int err = key_readflags(optarg, &p, &kf.f, &kf.m);
1773 if (err || *p)
1774 die(EXIT_FAILURE, "bad filter string `%s'", optarg);
1775 } break;
1776 default:
1777 rc = 1;
1778 break;
1779 }
1780 }
1781
1782 argv += optind; argc -= optind;
1783 if (rc || argc < 1)
1784 die(EXIT_FAILURE, "Usage: extract [-f filter] file [tag...]");
1785 if (strcmp(*argv, "-") == 0)
d03ab969 1786 fp = stdout;
252c122d 1787 else if (!(fp = fopen(*argv, "w"))) {
d03ab969 1788 die(EXIT_FAILURE, "couldn't open `%s' for writing: %s",
252c122d 1789 *argv, strerror(errno));
d03ab969 1790 }
1791
252c122d 1792 doopen(&f, KOPEN_READ);
1793 if (argc < 2) {
1794 key_iter i;
1795 key *k;
1796 for (key_mkiter(&i, &f); (k = key_next(&i)) != 0; )
1797 key_extract(&f, k, fp, &kf);
1798 } else {
1799 for (i = 1; i < argc; i++) {
1800 if ((k = key_bytag(&f, argv[i])) != 0)
1801 key_extract(&f, k, fp, &kf);
1802 else {
1803 moan("key `%s' not found", argv[i]);
1804 rc = 1;
1805 }
d03ab969 1806 }
1807 }
252c122d 1808 if (fclose(fp))
1809 die(EXIT_FAILURE, "error writing file: %s", strerror(errno));
d03ab969 1810 doclose(&f);
1811 return (rc);
1812}
1813
1814/* --- @cmd_tidy@ --- */
1815
1816static int cmd_tidy(int argc, char *argv[])
1817{
1818 key_file f;
1819 if (argc != 1)
1820 die(EXIT_FAILURE, "usage: tidy");
1821 doopen(&f, KOPEN_WRITE);
1822 f.f |= KF_MODIFIED; /* Nasty hack */
1823 doclose(&f);
1824 return (0);
1825}
1826
1827/* --- @cmd_merge@ --- */
1828
1829static int cmd_merge(int argc, char *argv[])
1830{
1831 key_file f;
1832 FILE *fp;
1833
1834 if (argc != 2)
252c122d 1835 die(EXIT_FAILURE, "Usage: merge file");
d03ab969 1836 if (strcmp(argv[1], "-") == 0)
1837 fp = stdin;
1838 else if (!(fp = fopen(argv[1], "r"))) {
1839 die(EXIT_FAILURE, "couldn't open `%s' for writing: %s",
1840 argv[1], strerror(errno));
1841 }
1842
1843 doopen(&f, KOPEN_WRITE);
252c122d 1844 key_merge(&f, argv[1], fp, key_moan, 0);
d03ab969 1845 doclose(&f);
1846 return (0);
1847}
1848
1849/*----- Main command table ------------------------------------------------*/
1850
1851static struct cmd {
1852 const char *name;
1853 int (*cmd)(int /*argc*/, char */*argv*/[]);
ce70ec47 1854 const char *usage;
d03ab969 1855 const char *help;
1856} cmds[] = {
1857 { "add", cmd_add,
252c122d 1858 "add [options] type [attr...]\n\
ce70ec47 1859 Options: [-lqrLS] [-a alg] [-bB bits] [-p param] [-R tag]\n\
1860 [-e expire] [-t tag] [-c comment]", "\
1861Options:\n\
1862\n\
1863-a, --algorithm=ALG Generate keys suitable for ALG.\n\
1864-b, --bits=N Generate an N-bit key.\n\
1865-B, --qbits=N Use an N-bit subgroup or factors.\n\
1866-p, --parameters=TAG Get group parameters from TAG.\n\
1ba83484 1867-C, --curve=CURVE Use elliptic curve CURVE.\n\
ce70ec47 1868-e, --expire=TIME Make the key expire after TIME.\n\
1869-c, --comment=STRING Attach the command STRING to the key.\n\
1870-t, --tag=TAG Tag the key with the name TAG.\n\
1871-r, --retag Untag any key currently with that tag.\n\
1872-R, --rand-id=TAG Use key named TAG for the random number generator.\n\
1873-l, --lock Lock the generated key with a passphrase.\n\
1874-q, --quiet Don't give progress indicators while working.\n\
1875-L, --lim-lee Generate Lim-Lee primes for Diffie-Hellman groups.\n\
1876-S, --subgroup Use a prime-order subgroup for Diffie-Hellman.\n\
1877" },
252c122d 1878 { "expire", cmd_expire, "expire tag..." },
1879 { "delete", cmd_delete, "delete tag..." },
ce70ec47 1880 { "tag", cmd_tag, "tag [-r] tag [new-tag]", "\
1881Options:\n\
1882\n\
1883-r, --retag Untag any key currently called new-tag.\n\
1884" },
252c122d 1885 { "setattr", cmd_setattr, "setattr tag attr..." },
1886 { "comment", cmd_comment, "comment tag [comment]" },
1887 { "lock", cmd_lock, "lock qtag" },
1888 { "unlock", cmd_unlock, "unlock qtag" },
ce70ec47 1889 { "list", cmd_list, "list [-uqv] [-f filter] [tag...]", "\
1890Options:\n\
1891\n\
1892-u, --utc Display expiry times etc. in UTC, not local time.\n\
1893-q, --quiet Show less information.\n\
1894-v, --verbose Show more information.\n\
1895" },
1896 { "fingerprint", cmd_finger, "fingerprint [-f filter] [tag...]", "\
1897Options:\n\
1898\n\
1899-f, --filter=FILT Only hash key components matching FILT.\n\
1900" },
d03ab969 1901 { "tidy", cmd_tidy, "tidy" },
ce70ec47 1902 { "extract", cmd_extract, "extract [-f filter] file [tag...]", "\
1903Options:\n\
1904\n\
1905-f, --filter=FILT Only extract key components matching FILT.\n\
1906" },
252c122d 1907 { "merge", cmd_merge, "merge file" },
d03ab969 1908 { 0, 0, 0 }
1909};
1910
1911typedef struct cmd cmd;
1912
1913/*----- Main code ---------------------------------------------------------*/
1914
ce70ec47 1915/* --- @findcmd@ --- *
1916 *
1917 * Arguments: @const char *name@ = a command name
1918 *
1919 * Returns: Pointer to the command structure.
1920 *
1921 * Use: Looks up a command by name. If the command isn't found, an
1922 * error is reported and the program is terminated.
1923 */
1924
1925static cmd *findcmd(const char *name)
1926{
1927 cmd *c, *chosen = 0;
1928 size_t sz = strlen(name);
1929
1930 for (c = cmds; c->name; c++) {
1931 if (strncmp(name, c->name, sz) == 0) {
1932 if (c->name[sz] == 0) {
1933 chosen = c;
1934 break;
1935 } else if (chosen)
1936 die(EXIT_FAILURE, "ambiguous command name `%s'", name);
1937 else
1938 chosen = c;
1939 }
1940 }
1941 if (!chosen)
1942 die(EXIT_FAILURE, "unknown command name `%s'", name);
1943 return (chosen);
1944}
1945
d03ab969 1946/* --- Helpful GNUy functions --- */
1947
1948void usage(FILE *fp)
1949{
99dde2f4 1950 pquis(fp, "Usage: $ [-k keyring] command [args]\n");
d03ab969 1951}
1952
1953void version(FILE *fp)
1954{
052b36d0 1955 pquis(fp, "$, Catacomb version " VERSION "\n");
d03ab969 1956}
1957
ce70ec47 1958void help(FILE *fp, char **argv)
d03ab969 1959{
1960 cmd *c;
ce70ec47 1961
d03ab969 1962 version(fp);
1963 fputc('\n', fp);
ce70ec47 1964 if (*argv) {
1965 c = findcmd(*argv);
99dde2f4 1966 fprintf(fp, "Usage: %s [-k keyring] %s\n", QUIS, c->usage);
ce70ec47 1967 if (c->help) {
1968 fputc('\n', fp);
1969 fputs(c->help, fp);
1970 }
1971 } else {
ce70ec47 1972 usage(fp);
1973 fputs("\n\
d03ab969 1974Performs various simple key management operations. Command line options\n\
1975recognized are:\n\
1976\n\
ce70ec47 1977-h, --help [COMMAND] Display this help text (or help for COMMAND).\n\
d03ab969 1978-v, --version Display version number.\n\
1979-u, --usage Display short usage summary.\n\
1980\n\
1981-k, --keyring=FILE Read and write keys in FILE.\n\
052b36d0 1982-i, --id=TAG Use key TAG for random number generator.\n\
1983-t, --type=TYPE Use key TYPE for random number generator.\n\
d03ab969 1984\n\
1985The following commands are understood:\n\n",
ce70ec47 1986 fp);
1987 for (c = cmds; c->name; c++)
1988 fprintf(fp, "%s\n", c->usage);
1989 }
d03ab969 1990}
1991
1992/* --- @main@ --- *
1993 *
1994 * Arguments: @int argc@ = number of command line arguments
1995 * @char *argv[]@ = array of command line arguments
1996 *
1997 * Returns: Nonzero on failure.
1998 *
1999 * Use: Main program. Performs simple key management functions.
2000 */
2001
2002int main(int argc, char *argv[])
2003{
2004 unsigned f = 0;
2005
16efd15b 2006#define f_bogus 1u
d03ab969 2007
2008 /* --- Initialization --- */
2009
2010 ego(argv[0]);
2011 sub_init();
2012
2013 /* --- Parse command line options --- */
2014
2015 for (;;) {
2016 static struct option opt[] = {
2017
2018 /* --- Standard GNUy help options --- */
2019
2020 { "help", 0, 0, 'h' },
2021 { "version", 0, 0, 'v' },
2022 { "usage", 0, 0, 'u' },
2023
2024 /* --- Real live useful options --- */
2025
2026 { "keyring", OPTF_ARGREQ, 0, 'k' },
2027
2028 /* --- Magic terminator --- */
2029
2030 { 0, 0, 0, 0 }
2031 };
99dde2f4 2032 int i = mdwopt(argc, argv, "+hvu k:", opt, 0, 0, 0);
d03ab969 2033
2034 if (i < 0)
2035 break;
2036 switch (i) {
2037
2038 /* --- GNU help options --- */
ce70ec47 2039
d03ab969 2040 case 'h':
ce70ec47 2041 help(stdout, argv + optind);
d03ab969 2042 exit(0);
2043 case 'v':
2044 version(stdout);
2045 exit(0);
2046 case 'u':
2047 usage(stdout);
2048 exit(0);
2049
2050 /* --- Real useful options --- */
2051
2052 case 'k':
2053 keyfile = optarg;
2054 break;
2055
2056 /* --- Bogosity --- */
2057
2058 default:
2059 f |= f_bogus;
2060 break;
2061 }
2062 }
2063
2064 /* --- Complain about excessive bogons --- */
2065
2066 if (f & f_bogus || optind == argc) {
2067 usage(stderr);
2068 exit(1);
2069 }
2070
052b36d0 2071 /* --- Initialize the Catacomb random number generator --- */
2072
052b36d0 2073 rand_noisesrc(RAND_GLOBAL, &noise_source);
9219a5d6 2074 rand_seed(RAND_GLOBAL, 160);
052b36d0 2075
d03ab969 2076 /* --- Dispatch to appropriate command handler --- */
2077
2078 argc -= optind;
2079 argv += optind;
2080 optind = 0;
ce70ec47 2081 return (findcmd(argv[0])->cmd(argc, argv));
d03ab969 2082}
2083
2084/*----- That's all, folks -------------------------------------------------*/