X-Git-Url: https://git.distorted.org.uk/u/mdw/catacomb/blobdiff_plain/16efd15b675c87d22a4fd9fb12d7bb26f2a36031..36f6c82c7146764fa65bf8d08fd99b8f12547f83:/keyutil.c diff --git a/keyutil.c b/keyutil.c index fac8813..a691e79 100644 --- a/keyutil.c +++ b/keyutil.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: keyutil.c,v 1.11 2000/12/06 20:33:27 mdw Exp $ + * $Id: keyutil.c,v 1.21 2004/04/03 15:15:19 mdw Exp $ * * Simple key manager program * @@ -30,6 +30,42 @@ /*----- Revision history --------------------------------------------------* * * $Log: keyutil.c,v $ + * Revision 1.21 2004/04/03 15:15:19 mdw + * Fix stupid error in previous rashly-committed version. + * + * Revision 1.20 2004/04/03 15:14:28 mdw + * Handle points at infinity properly in listings. + * + * Revision 1.19 2004/04/03 03:31:01 mdw + * Allow explicit group parameters for DH groups. + * + * Revision 1.18 2004/04/01 12:50:09 mdw + * Add cyclic group abstraction, with test code. Separate off exponentation + * functions for better static linking. Fix a buttload of bugs on the way. + * Generally ensure that negative exponents do inversion correctly. Add + * table of standard prime-field subgroups. (Binary field subgroups are + * currently unimplemented but easy to add if anyone ever finds a good one.) + * + * Revision 1.17 2004/03/28 01:58:47 mdw + * Generate, store and retreive elliptic curve keys. + * + * Revision 1.16 2003/10/15 09:31:45 mdw + * Fix help message. + * + * Revision 1.15 2003/05/15 23:23:24 mdw + * Fix behaviour with bogus trailing attributes. + * + * Revision 1.14 2001/02/23 09:03:27 mdw + * Simplify usage message by removing nonexistant options. + * + * Revision 1.13 2001/02/21 20:04:27 mdw + * Provide help on individual commands (some need it desparately). Allow + * atomic retagging of keys. + * + * Revision 1.12 2001/02/03 11:58:22 mdw + * Store the correct seed information and count for DSA keys now that it's + * available. + * * Revision 1.11 2000/12/06 20:33:27 mdw * Make flags be macros rather than enumerations, to ensure that they're * unsigned. @@ -91,6 +127,10 @@ #include "bbs.h" #include "dh.h" #include "dsa.h" +#include "dsarand.h" +#include "ec.h" +#include "ec-keys.h" +#include "ectab.h" #include "fibrand.h" #include "getdate.h" #include "key.h" @@ -99,8 +139,22 @@ #include "mprand.h" #include "mptext.h" #include "pgen.h" +#include "ptab.h" #include "rsa.h" +#include "sha-mgf.h" +#include "sha256-mgf.h" +#include "sha224-mgf.h" +#include "sha384-mgf.h" +#include "sha512-mgf.h" +#include "tiger-mgf.h" +#include "rmd128-mgf.h" +#include "rmd160-mgf.h" +#include "rmd256-mgf.h" +#include "rmd320-mgf.h" +#include "md5-mgf.h" +#include "dsarand.h" + /*----- Handy global state ------------------------------------------------*/ static const char *keyfile = "keyring"; @@ -163,8 +217,11 @@ static void setattr(key_file *f, key *k, char *v[]) int err; char *p = *v; size_t eq = strcspn(p, "="); - if (p[eq] == 0) - moan("invalid assignment: `%s'", p); + if (!p[eq]) { + moan("invalid assignment: `%s' (ignored)", p); + v++; + continue; + } p[eq] = 0; p += eq + 1; if ((err = key_putattr(f, k, *v, *p ? p : 0)) != 0) @@ -173,6 +230,26 @@ static void setattr(key_file *f, key *k, char *v[]) } } +/*----- Seeding -----------------------------------------------------------*/ + +const struct seedalg { const char *p; grand *(*gen)(const void *, size_t); } +seedtab[] = { + { "dsarand", dsarand_create }, + { "rmd128-mgf", rmd128_mgfrand }, + { "rmd160-mgf", rmd160_mgfrand }, + { "rmd256-mgf", rmd256_mgfrand }, + { "rmd320-mgf", rmd320_mgfrand }, + { "sha-mgf", sha_mgfrand }, + { "sha224-mgf", sha224_mgfrand }, + { "sha256-mgf", sha256_mgfrand }, + { "sha384-mgf", sha384_mgfrand }, + { "sha512-mgf", sha512_mgfrand }, + { "tiger-mgf", tiger_mgfrand }, + { 0, 0 } +}; + +#define SEEDALG_DEFAULT (seedtab + 2) + /*----- Key generation ----------------------------------------------------*/ /* --- Key generation parameters --- */ @@ -183,6 +260,8 @@ typedef struct keyopts { dstr tag; /* Full tag name for the key */ unsigned f; /* Flags for the new key */ unsigned bits, qbits; /* Bit length for the new key */ + const char *curve; /* Elliptic curve name/info */ + grand *r; /* Random number source */ key *p; /* Parameters key-data */ } keyopts; @@ -191,6 +270,7 @@ typedef struct keyopts { #define f_quiet 4u /* Don't show a progress indicator */ #define f_limlee 8u /* Generate Lim-Lee primes */ #define f_subgroup 16u /* Generate a subgroup */ +#define f_retag 32u /* Remove any existing tag */ /* --- @dolock@ --- * * @@ -360,7 +440,7 @@ static void alg_binary(keyopts *k) sz = (k->bits + 7) >> 3; p = sub_alloc(sz); m = (1 << (((k->bits - 1) & 7) + 1)) - 1; - rand_get(RAND_GLOBAL, p, sz); + k->r->ops->fill(k->r, p, sz); *p &= m; key_binary(&k->k->k, p, sz); k->k->k.e |= KCAT_SYMM | KF_BURN; @@ -376,7 +456,7 @@ static void alg_des(keyopts *k) int i; if (!k->bits) - k->bits = 112; + k->bits = 168; if (k->p) die(EXIT_FAILURE, "no shared parameters for DES keys"); if (k->bits % 56 || k->bits > 168) @@ -384,7 +464,7 @@ static void alg_des(keyopts *k) sz = k->bits / 7; p = sub_alloc(sz); - rand_get(RAND_GLOBAL, p, sz); /* Too much work done here! */ + k->r->ops->fill(k->r, p, sz); for (i = 0; i < sz; i++) { octet x = p[i] | 0x01; x = x ^ (x >> 4); @@ -413,14 +493,14 @@ static void alg_rsa(keyopts *k) /* --- Generate the RSA parameters --- */ - if (rsa_gen(&rp, k->bits, &rand_global, 0, + if (rsa_gen(&rp, k->bits, k->r, 0, (k->f & f_quiet) ? 0 : pgen_ev, 0)) die(EXIT_FAILURE, "RSA key generation failed"); /* --- Run a test encryption --- */ { - grand *g = fibrand_create(rand_global.ops->word(&rand_global)); + grand *g = fibrand_create(k->r->ops->word(k->r)); rsa_pub rpp; mp *m = mprand_range(MP_NEW, rp.n, g, 0); mp *c; @@ -467,6 +547,7 @@ static void alg_dsaparam(keyopts *k) dstr d = DSTR_INIT; base64_ctx c; key_data *kd = &k->k->k; + dsa_seed ds; /* --- Choose appropriate bit lengths if necessary --- */ @@ -479,11 +560,11 @@ static void alg_dsaparam(keyopts *k) sz = (k->qbits + 7) >> 3; p = sub_alloc(sz); - rand_get(RAND_GLOBAL, p, sz); + k->r->ops->fill(k->r, p, sz); /* --- Allocate the parameters --- */ - if (dsa_gen(&dp, k->qbits, k->bits, 0, p, sz, + if (dsa_gen(&dp, k->qbits, k->bits, 0, p, sz, &ds, (k->f & f_quiet) ? 0 : pgen_ev, 0)) die(EXIT_FAILURE, "DSA parameter generation failed"); @@ -502,9 +583,13 @@ static void alg_dsaparam(keyopts *k) base64_init(&c); c.maxline = 0; c.indent = ""; - base64_encode(&c, p, sz, &d); + base64_encode(&c, ds.p, ds.sz, &d); base64_encode(&c, 0, 0, &d); key_putattr(k->kf, k->k, "seed", d.buf); + DRESET(&d); + dstr_putf(&d, "%u", ds.count); + key_putattr(k->kf, k->k, "count", d.buf); + xfree(ds.p); sub_free(p, sz); dstr_destroy(&d); } @@ -526,7 +611,7 @@ static void alg_dsa(keyopts *k) /* --- Choose a private key --- */ - x = mprand_range(MP_NEWSEC, q, &rand_global, 0); + x = mprand_range(MP_NEWSEC, q, k->r, 0); mpmont_create(&mm, p); y = mpmont_exp(&mm, MP_NEW, g, x); @@ -545,11 +630,27 @@ static void alg_dsa(keyopts *k) static void alg_dhparam(keyopts *k) { static const char *pl[] = { "p", "q", "g", 0 }; + key_data *kd = &k->k->k; if (!copyparam(k, pl)) { dh_param dp; - key_data *kd = &k->k->k; int rc; + if (k->curve) { + qd_parse qd; + + if (strcmp(k->curve, "list") == 0) { + const pentry *pe; + printf("Built-in prime groups:\n"); + for (pe = ptab; pe->name; pe++) + printf(" %s\n", pe->name); + exit(0); + } + qd.p = k->curve; + if (dh_parse(&qd, &dp)) + die(EXIT_FAILURE, "error in group spec: %s", qd.e); + goto done; + } + if (!k->bits) k->bits = 1024; @@ -562,7 +663,7 @@ static void alg_dhparam(keyopts *k) k->qbits = 256; rc = dh_limlee(&dp, k->qbits, k->bits, (k->f & f_subgroup) ? DH_SUBGROUP : 0, - 0, &rand_global, (k->f & f_quiet) ? 0 : pgen_ev, 0, + 0, k->r, (k->f & f_quiet) ? 0 : pgen_ev, 0, (k->f & f_quiet) ? 0 : pgen_evspin, 0, &nf, &f); if (!rc) { dstr d = DSTR_INIT; @@ -577,12 +678,13 @@ static void alg_dhparam(keyopts *k) dstr_destroy(&d); } } else - rc = dh_gen(&dp, k->qbits, k->bits, 0, &rand_global, + rc = dh_gen(&dp, k->qbits, k->bits, 0, k->r, (k->f & f_quiet) ? 0 : pgen_ev, 0); if (rc) die(EXIT_FAILURE, "Diffie-Hellman parameter generation failed"); + done: key_structure(kd); mpkey(kd, "p", dp.p, KCAT_SHARE); mpkey(kd, "q", dp.q, KCAT_SHARE); @@ -590,7 +692,7 @@ static void alg_dhparam(keyopts *k) mp_drop(dp.q); mp_drop(dp.p); mp_drop(dp.g); - } + } } static void alg_dh(keyopts *k) @@ -612,7 +714,7 @@ static void alg_dh(keyopts *k) * Since %$g$% has order %$q$%, choose %$x < q$%. */ - x = mprand_range(MP_NEWSEC, q, &rand_global, 0); + x = mprand_range(MP_NEWSEC, q, k->r, 0); /* --- Compute the public key %$y = g^x \bmod p$% --- */ @@ -646,7 +748,7 @@ static void alg_bbs(keyopts *k) /* --- Generate the BBS parameters --- */ - if (bbs_gen(&bp, k->bits, &rand_global, 0, + if (bbs_gen(&bp, k->bits, k->r, 0, (k->f & f_quiet) ? 0 : pgen_ev, 0)) die(EXIT_FAILURE, "Blum-Blum-Shub key generation failed"); @@ -665,6 +767,94 @@ static void alg_bbs(keyopts *k) bbs_privfree(&bp); } +static void alg_ecparam(keyopts *k) +{ + static const char *pl[] = { "curve", 0 }; + if (!copyparam(k, pl)) { + ec_info ei; + const char *e; + key_data *kd = &k->k->k; + + /* --- Decide on a curve --- */ + + if (!k->bits) k->bits = 256; + if (k->curve && strcmp(k->curve, "list") == 0) { + const ecentry *ee; + printf("Built-in elliptic curves:\n"); + for (ee = ectab; ee->name; ee++) + printf(" %s\n", ee->name); + exit(0); + } + if (!k->curve) { + if (k->bits <= 56) k->curve = "secp112r1"; + else if (k->bits <= 64) k->curve = "secp128r1"; + else if (k->bits <= 80) k->curve = "secp160r1"; + else if (k->bits <= 96) k->curve = "secp192r1"; + else if (k->bits <= 112) k->curve = "secp224r1"; + else if (k->bits <= 128) k->curve = "secp256r1"; + else if (k->bits <= 192) k->curve = "secp384r1"; + else if (k->bits <= 256) k->curve = "secp521r1"; + else + die(EXIT_FAILURE, "no built-in curves provide %u-bit security", + k->bits); + } + + /* --- Check it --- */ + + if ((e = ec_getinfo(&ei, k->curve)) != 0) + die(EXIT_FAILURE, "error in curve spec: %s", e); + if (!(k->f & f_quiet) && (e = ec_checkinfo(&ei, k->r)) != 0) + moan("WARNING! curve check failed: %s", e); + ec_freeinfo(&ei); + + /* --- Write out the answer --- */ + + key_structure(kd); + kd = key_structcreate(kd, "curve"); + key_string(kd, k->curve); + kd->e |= KCAT_SHARE; + } +} + +static void alg_ec(keyopts *k) +{ + key_data *kd = &k->k->k; + key_data *kkd; + mp *x = MP_NEW; + ec p = EC_INIT; + const char *e; + ec_info ei; + + /* --- Get the curve --- */ + + alg_ecparam(k); + if ((kkd = key_structfind(kd, "curve")) == 0) + die(EXIT_FAILURE, "unexpected failure looking up subkey `curve')"); + if ((kkd->e & KF_ENCMASK) != KENC_STRING) + die(EXIT_FAILURE, "subkey `curve' is not a string"); + if ((e = ec_getinfo(&ei, kkd->u.p)) != 0) + die(EXIT_FAILURE, "error in curve spec: %s", e); + + /* --- Invent a private exponent and compute the public key --- */ + + x = mprand_range(MP_NEWSEC, ei.r, k->r, 0); + ec_mul(ei.c, &p, &ei.g, x); + + /* --- Store everything away --- */ + + kkd = key_structcreate(kd, "p"); + key_ec(kkd, &p); + kkd->e |= KCAT_PUB; + kkd = key_structcreate(kd, "private"); + key_structure(kkd); + mpkey(kkd, "x", x, KCAT_PRIV | KF_BURN); + + /* --- Done --- */ + + ec_freeinfo(&ei); + mp_drop(x); +} + /* --- The algorithm tables --- */ typedef struct keyalg { @@ -682,6 +872,8 @@ static keyalg algtab[] = { { "dh", alg_dh, "Diffie-Hellman key exchange" }, { "dh-param", alg_dhparam, "Diffie-Hellman parameters" }, { "bbs", alg_bbs, "Blum-Blum-Shub generator" }, + { "ec-param", alg_ecparam, "Elliptic curve parameters" }, + { "ec", alg_ec, "Elliptic curve crypto" }, { 0, 0 } }; @@ -695,7 +887,10 @@ static int cmd_add(int argc, char *argv[]) const char *c = 0; keyalg *alg = algtab; const char *rtag = 0; - keyopts k = { 0, 0, DSTR_INIT, 0, 0, 0, 0 }; + const struct seedalg *sa = SEEDALG_DEFAULT; + keyopts k = { 0, 0, DSTR_INIT, 0, 0, 0, 0, 0 }; + const char *seed = 0; + k.r = &rand_global; /* --- Parse options for the subcommand --- */ @@ -708,14 +903,18 @@ static int cmd_add(int argc, char *argv[]) { "expire", OPTF_ARGREQ, 0, 'e' }, { "comment", OPTF_ARGREQ, 0, 'c' }, { "tag", OPTF_ARGREQ, 0, 't' }, - { "rand-id", OPTF_ARGREQ, 0, 'r' }, + { "rand-id", OPTF_ARGREQ, 0, 'R' }, + { "curve", OPTF_ARGREQ, 0, 'C' }, + { "seedalg", OPTF_ARGREQ, 0, 'A' }, + { "seed", OPTF_ARGREQ, 0, 's' }, + { "newseed", OPTF_ARGREQ, 0, 'n' }, { "lock", 0, 0, 'l' }, { "quiet", 0, 0, 'q' }, { "lim-lee", 0, 0, 'L' }, { "subgroup", 0, 0, 'S' }, { 0, 0, 0, 0 } }; - int i = mdwopt(argc, argv, "+a:b:B:p:e:c:t:r:lqLS", opt, 0, 0, 0); + int i = mdwopt(argc, argv, "+a:b:B:p:e:c:t:R:C:A:s:n:lqrLS", opt, 0, 0, 0); if (i < 0) break; @@ -793,6 +992,12 @@ static int cmd_add(int argc, char *argv[]) c = optarg; break; + /* --- Elliptic curve parameters --- */ + + case 'C': + k.curve = optarg; + break; + /* --- Store tags --- */ case 't': @@ -800,10 +1005,63 @@ static int cmd_add(int argc, char *argv[]) die(EXIT_FAILURE, "bad tag string `%s'", optarg); tag = optarg; break; + case 'r': + k.f |= f_retag; + break; + + /* --- Seeding --- */ + case 'A': { + const struct seedalg *ss; + if (strcmp(optarg, "list") == 0) { + printf("Seed algorithms:\n"); + for (ss = seedtab; ss->p; ss++) + printf(" %s\n", ss->p); + exit(0); + } + if (seed) die(EXIT_FAILURE, "seed already set -- put -A first"); + sa = 0; + for (ss = seedtab; ss->p; ss++) { + if (strcmp(optarg, ss->p) == 0) + sa = ss; + } + if (!sa) + die(EXIT_FAILURE, "seed algorithm `%s' not known", optarg); + } break; + + case 's': { + base64_ctx b; + dstr d = DSTR_INIT; + if (seed) die(EXIT_FAILURE, "seed already set"); + base64_init(&b); + base64_decode(&b, optarg, strlen(optarg), &d); + base64_decode(&b, 0, 0, &d); + k.r = sa->gen(d.buf, d.len); + seed = optarg; + dstr_destroy(&d); + } break; + + case 'n': { + base64_ctx b; + dstr d = DSTR_INIT; + char *p; + unsigned n = strtoul(optarg, &p, 0); + if (n == 0 || *p != 0 || n % 8 != 0) + die(EXIT_FAILURE, "bad seed length `%s'", optarg); + if (seed) die(EXIT_FAILURE, "seed already set"); + n /= 8; + p = xmalloc(n); + rand_get(RAND_GLOBAL, p, n); + base64_init(&b); + base64_encode(&b, p, n, &d); + base64_encode(&b, 0, 0, &d); + seed = d.buf; + k.r = sa->gen(p, n); + } break; + /* --- Other flags --- */ - case 'r': + case 'R': rtag = optarg; break; case 'l': @@ -866,8 +1124,13 @@ static int cmd_add(int argc, char *argv[]) /* --- Set various simple attributes --- */ if (tag) { - int err = key_settag(&f, k.k, tag); - if (err) + int err; + key *kk; + if (k.f & f_retag) { + if ((kk = key_bytag(&f, tag)) != 0 && strcmp(kk->tag, tag) == 0) + key_settag(&f, kk, 0); + } + if ((err = key_settag(&f, k.k, tag)) != 0) die(EXIT_FAILURE, "error setting key tag: %s", key_strerror(err)); } @@ -878,6 +1141,10 @@ static int cmd_add(int argc, char *argv[]) } setattr(&f, k.k, argv + optind + 1); + if (seed) { + key_putattr(&f, k.k, "genseed", seed); + key_putattr(&f, k.k, "seedalg", sa->p); + } key_fulltag(k.k, &k.tag); @@ -1001,6 +1268,24 @@ static void showkeydata(key_data *k, int ind, listopts *o, dstr *d) putchar('\n'); break; + /* --- Strings --- */ + + case KENC_STRING: + printf(" `%s'\n", k->u.p); + break; + + /* --- Elliptic curve points --- */ + + case KENC_EC: + if (EC_ATINF(&k->u.e)) + fputs("inf\n", stdout); + else { + fputs(" 0x", stdout); mp_writefile(k->u.e.x, stdout, 16); + fputs(", 0x", stdout); mp_writefile(k->u.e.y, stdout, 16); + putchar('\n'); + } + break; + /* --- Structured keys --- * * * Just iterate over the subkeys. @@ -1321,7 +1606,7 @@ static int cmd_finger(int argc, char *argv[]) { "filter", OPTF_ARGREQ, 0, 'f' }, { 0, 0, 0, 0 } }; - int i = mdwopt(argc, argv, "f:", opt, 0, 0, 0); + int i = mdwopt(argc, argv, "+f:", opt, 0, 0, 0); if (i < 0) break; switch (i) { @@ -1389,14 +1674,39 @@ static int cmd_tag(int argc, char *argv[]) key_file f; key *k; int err; + unsigned flags = 0; + int rc = 0; - if (argc < 2 || argc > 3) - die(EXIT_FAILURE, "Usage: tag tag [new-tag]"); + for (;;) { + static struct option opt[] = { + { "retag", 0, 0, 'r' }, + { 0, 0, 0, 0 } + }; + int i = mdwopt(argc, argv, "+r", opt, 0, 0, 0); + if (i < 0) + break; + switch (i) { + case 'r': + flags |= f_retag; + break; + default: + rc = 1; + break; + } + } + + argv += optind; argc -= optind; + if (argc < 1 || argc > 2 || rc) + die(EXIT_FAILURE, "Usage: tag [-r] tag [new-tag]"); doopen(&f, KOPEN_WRITE); - if ((k = key_bytag(&f, argv[1])) == 0) - die(EXIT_FAILURE, "key `%s' not found", argv[1]); - if ((err = key_settag(&f, k, argv[2])) != 0) - die(EXIT_FAILURE, "bad tag `%s': %s", argv[2], key_strerror(err)); + if (flags & f_retag) { + if ((k = key_bytag(&f, argv[1])) != 0 && strcmp(k->tag, argv[1]) == 0) + key_settag(&f, k, 0); + } + if ((k = key_bytag(&f, argv[0])) == 0) + die(EXIT_FAILURE, "key `%s' not found", argv[0]); + if ((err = key_settag(&f, k, argv[1])) != 0) + die(EXIT_FAILURE, "bad tag `%s': %s", argv[1], key_strerror(err)); doclose(&f); return (0); } @@ -1551,24 +1861,59 @@ static int cmd_merge(int argc, char *argv[]) static struct cmd { const char *name; int (*cmd)(int /*argc*/, char */*argv*/[]); + const char *usage; const char *help; } cmds[] = { { "add", cmd_add, "add [options] type [attr...]\n\ - Options: [-lqLS] [-a alg] [-b|-B bits] [-p param] [-r tag]\n\ - [-e expire] [-t tag] [-c comment]" - }, + Options: [-lqrLS] [-a alg] [-bB bits] [-p param] [-R tag]\n\ + [-e expire] [-t tag] [-c comment]", "\ +Options:\n\ +\n\ +-a, --algorithm=ALG Generate keys suitable for ALG.\n\ +-b, --bits=N Generate an N-bit key.\n\ +-B, --qbits=N Use an N-bit subgroup or factors.\n\ +-p, --parameters=TAG Get group parameters from TAG.\n\ +-C, --curve=CURVE Use elliptic curve CURVE.\n\ +-e, --expire=TIME Make the key expire after TIME.\n\ +-c, --comment=STRING Attach the command STRING to the key.\n\ +-t, --tag=TAG Tag the key with the name TAG.\n\ +-r, --retag Untag any key currently with that tag.\n\ +-R, --rand-id=TAG Use key named TAG for the random number generator.\n\ +-l, --lock Lock the generated key with a passphrase.\n\ +-q, --quiet Don't give progress indicators while working.\n\ +-L, --lim-lee Generate Lim-Lee primes for Diffie-Hellman groups.\n\ +-S, --subgroup Use a prime-order subgroup for Diffie-Hellman.\n\ +" }, { "expire", cmd_expire, "expire tag..." }, { "delete", cmd_delete, "delete tag..." }, - { "tag", cmd_tag, "tag tag [new-tag]" }, + { "tag", cmd_tag, "tag [-r] tag [new-tag]", "\ +Options:\n\ +\n\ +-r, --retag Untag any key currently called new-tag.\n\ +" }, { "setattr", cmd_setattr, "setattr tag attr..." }, { "comment", cmd_comment, "comment tag [comment]" }, { "lock", cmd_lock, "lock qtag" }, { "unlock", cmd_unlock, "unlock qtag" }, - { "list", cmd_list, "list [-uqv] [-f filter] [tag...]" }, - { "fingerprint", cmd_finger, "fingerprint [-f filter] [tag...]" }, + { "list", cmd_list, "list [-uqv] [-f filter] [tag...]", "\ +Options:\n\ +\n\ +-u, --utc Display expiry times etc. in UTC, not local time.\n\ +-q, --quiet Show less information.\n\ +-v, --verbose Show more information.\n\ +" }, + { "fingerprint", cmd_finger, "fingerprint [-f filter] [tag...]", "\ +Options:\n\ +\n\ +-f, --filter=FILT Only hash key components matching FILT.\n\ +" }, { "tidy", cmd_tidy, "tidy" }, - { "extract", cmd_extract, "extract [-f filter] file [tag...]" }, + { "extract", cmd_extract, "extract [-f filter] file [tag...]", "\ +Options:\n\ +\n\ +-f, --filter=FILT Only extract key components matching FILT.\n\ +" }, { "merge", cmd_merge, "merge file" }, { 0, 0, 0 } }; @@ -1577,11 +1922,42 @@ typedef struct cmd cmd; /*----- Main code ---------------------------------------------------------*/ +/* --- @findcmd@ --- * + * + * Arguments: @const char *name@ = a command name + * + * Returns: Pointer to the command structure. + * + * Use: Looks up a command by name. If the command isn't found, an + * error is reported and the program is terminated. + */ + +static cmd *findcmd(const char *name) +{ + cmd *c, *chosen = 0; + size_t sz = strlen(name); + + for (c = cmds; c->name; c++) { + if (strncmp(name, c->name, sz) == 0) { + if (c->name[sz] == 0) { + chosen = c; + break; + } else if (chosen) + die(EXIT_FAILURE, "ambiguous command name `%s'", name); + else + chosen = c; + } + } + if (!chosen) + die(EXIT_FAILURE, "unknown command name `%s'", name); + return (chosen); +} + /* --- Helpful GNUy functions --- */ void usage(FILE *fp) { - pquis(fp, "Usage: $ [-k file] [-i tag] [-t type] command [args]\n"); + pquis(fp, "Usage: $ [-k keyring] command [args]\n"); } void version(FILE *fp) @@ -1589,17 +1965,26 @@ void version(FILE *fp) pquis(fp, "$, Catacomb version " VERSION "\n"); } -void help(FILE *fp) +void help(FILE *fp, char **argv) { cmd *c; + version(fp); fputc('\n', fp); - usage(fp); - fputs("\n\ + if (*argv) { + c = findcmd(*argv); + fprintf(fp, "Usage: %s [-k keyring] %s\n", QUIS, c->usage); + if (c->help) { + fputc('\n', fp); + fputs(c->help, fp); + } + } else { + usage(fp); + fputs("\n\ Performs various simple key management operations. Command line options\n\ recognized are:\n\ \n\ --h, --help Display this help text.\n\ +-h, --help [COMMAND] Display this help text (or help for COMMAND).\n\ -v, --version Display version number.\n\ -u, --usage Display short usage summary.\n\ \n\ @@ -1608,9 +1993,10 @@ recognized are:\n\ -t, --type=TYPE Use key TYPE for random number generator.\n\ \n\ The following commands are understood:\n\n", - fp); - for (c = cmds; c->name; c++) - fprintf(fp, "%s\n", c->help); + fp); + for (c = cmds; c->name; c++) + fprintf(fp, "%s\n", c->usage); + } } /* --- @main@ --- * @@ -1648,22 +2034,21 @@ int main(int argc, char *argv[]) /* --- Real live useful options --- */ { "keyring", OPTF_ARGREQ, 0, 'k' }, - { "id", OPTF_ARGREQ, 0, 'i' }, - { "type", OPTF_ARGREQ, 0, 't' }, /* --- Magic terminator --- */ { 0, 0, 0, 0 } }; - int i = mdwopt(argc, argv, "+hvu k:i:t:", opt, 0, 0, 0); + int i = mdwopt(argc, argv, "+hvu k:", opt, 0, 0, 0); if (i < 0) break; switch (i) { /* --- GNU help options --- */ + case 'h': - help(stdout); + help(stdout, argv + optind); exit(0); case 'v': version(stdout); @@ -1703,26 +2088,7 @@ int main(int argc, char *argv[]) argc -= optind; argv += optind; optind = 0; - - { - cmd *c, *chosen = 0; - size_t sz = strlen(argv[0]); - - for (c = cmds; c->name; c++) { - if (strncmp(argv[0], c->name, sz) == 0) { - if (c->name[sz] == 0) { - chosen = c; - break; - } else if (chosen) - die(EXIT_FAILURE, "ambiguous command name `%s'", argv[0]); - else - chosen = c; - } - } - if (!chosen) - die(EXIT_FAILURE, "unknown command name `%s'", argv[0]); - return (chosen->cmd(argc, argv)); - } + return (findcmd(argv[0])->cmd(argc, argv)); } /*----- That's all, folks -------------------------------------------------*/