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