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