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