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