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