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