chiark / gitweb /
progs/key.c: Multiple output presentations for fingerprints.
authorMark Wooding <mdw@distorted.org.uk>
Fri, 29 May 2015 17:54:29 +0000 (18:54 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Fri, 29 May 2015 19:02:32 +0000 (20:02 +0100)
Add a new presentation for base32 output, separated into groups of six
characters.

Also document the presentation styles better; include some other missing
options in usage messages; and patch an irrelevant memory leak.

progs/key.1
progs/key.c

index f301391f9f25cea6c0bafcd5c996755d92614eaa..a2ac3376b9f2670c36a3287f0b3c5280a3a98406 100644 (file)
@@ -117,6 +117,8 @@ is one of:
 .B fingerprint
 .RB [ \-f
 .IR filter ]
+.RB [ \-p
+.IR style ]
 .RB [ \-a
 .IR hash ]
 .RI [ tag ...]
@@ -124,6 +126,8 @@ is one of:
 .B verify
 .RB [ \-f
 .IR filter ]
+.RB [ \-p
+.IR style ]
 .RB [ \-a
 .IR hash ]
 .I tag
@@ -340,6 +344,13 @@ The pseudorandom generators which are acceptable to the
 option of the
 .B add
 command.
+.TP
+.B fpres
+Fingerprint presentation styles, as used by the
+.B fingerprint
+and
+.B verify
+commands.
 .SS add
 The
 .B add
@@ -919,6 +930,11 @@ Specifies a filter.  Only keys and key components which match the filter
 are fingerprinted.  The default is to only fingerprint nonsecret
 components.
 .TP
+.BI "\-p, \-\-presentation " style
+Write fingerprints in the given
+.IR style .
+See below for a list of presentation styles.
+.TP
 .BI "\-a, \-\-algorithm " hash
 Names the hashing algorithm.  Run
 .B key show hash
@@ -930,6 +946,18 @@ command line arguments.  If no key tags are given, all keys which match
 the filter are fingerprinted.  See
 .BR keyring (5)
 for a description of how key fingerprints are computed.
+.PP
+The fingerprint may be shown in the following styles.
+.TP
+.B hex
+Lowercase hexadecimal, with groups of eight digits separated by hyphens
+(`\-').  This is the default presentation style.  (On input, colons are
+also permitted as separators.)
+.TP
+.B base32
+Lowercase Base32 encoding, without `=' padding, with groups of six
+digits separated by colons (`:').  (On input, padding characters are
+ignored.)
 .SS "verify"
 Check a key's fingerprint against a reference copy.  The following
 options are supported:
@@ -939,15 +967,29 @@ Specifies a filter.  Only key components which match the filter are
 hashed.  The default is to only fingerprint nonsecret components.  An
 error is reported if no part of the key matches.
 .TP
+.BI "\-p, \-\-presentation " style
+Expect the
+.I fingerprint
+to be in the given presentation
+.IR style .
+These match the styles produced by the
+.B fingerprint
+command described above.
+.TP
 .BI "\-a, \-\-algorithm " hash
 Names the hashing algorithm.  Run
 .B key show hash
 for a list of hashing algorithms.  The default is
 .BR rmd160 .
 .PP
-The reference fingerprint is given as hex, in upper or lower case.  The
-hash may contain hyphens, colons and whitespace.  Other characters are
-not permitted.
+The fingerprint should be provided in the form printed by the
+.B fingerprint
+command, using the same presentation
+.IR style .
+A little flexibility is permitted: separators may be placed anywhere (or
+not at all) and are ignored; whitespace is permitted and ignored; and
+case is ignored in presentation styles which don't make use of both
+upper- and lower-case characters.
 .SS "tidy"
 Simply reads the keyring from file and writes it back again.  This has
 the effect of removing any deleted keys from the file.
index 4530d00e59b5bda3da26f0ee4616c2827fbc4c97..05494691405afc04b4c01b62b9953a73a3ec98c8 100644 (file)
@@ -39,7 +39,9 @@
 #include <time.h>
 
 #include <mLib/codec.h>
+#include <mLib/base32.h>
 #include <mLib/base64.h>
+#include <mLib/hex.h>
 #include <mLib/mdwopt.h>
 #include <mLib/quis.h>
 #include <mLib/report.h>
@@ -1682,42 +1684,67 @@ static int cmd_getattr(int argc, char *argv[])
 
 /* --- @cmd_finger@ --- */
 
-static void fingerprint(key *k, const gchash *ch, const key_filter *kf)
+static const struct fpres {
+  const char *name;
+  const codec_class *cdc;
+  unsigned short ival;
+  const char *sep;
+} fprestab[] = {
+  { "hex", &hex_class, 8, "-:" },
+  { "base32", &base32_class, 6, ":" },
+  { 0, 0 }
+};
+
+static void fingerprint(key *k, const struct fpres *fpres,
+                       const gchash *ch, const key_filter *kf)
 {
   ghash *h;
-  dstr d = DSTR_INIT;
+  dstr d = DSTR_INIT, dd = DSTR_INIT;
   const octet *p;
   size_t i;
+  codec *c;
 
   h = GH_INIT(ch);
   if (key_fingerprint(k, h, kf)) {
     p = GH_DONE(h, 0);
-    key_fulltag(k, &d);
-    for (i = 0; i < ch->hashsz; i++) {
-      if (i && i % 4 == 0)
-       putchar('-');
-      printf("%02x", p[i]);
+    c = fpres->cdc->encoder(CDCF_LOWERC | CDCF_NOEQPAD, "", 0);
+    c->ops->code(c, p, ch->hashsz, &dd); c->ops->code(c, 0, 0, &dd);
+    c->ops->destroy(c);
+    for (i = 0; i < dd.len; i++) {
+      if (i && i%fpres->ival == 0) dstr_putc(&d, fpres->sep[0]);
+      dstr_putc(&d, dd.buf[i]);
     }
-    printf(" %s\n", d.buf);
+    dstr_putc(&d, ' '); key_fulltag(k, &d); dstr_putc(&d, '\n');
+    dstr_write(&d, stdout);
   }
-  dstr_destroy(&d);
+  dstr_destroy(&d); dstr_destroy(&dd);
   GH_DESTROY(h);
 }
 
+static const struct fpres *lookup_fpres(const char *name)
+{
+  const struct fpres *fpres;
+  for (fpres = fprestab; fpres->name; fpres++)
+    if (strcmp(fpres->name, name) == 0) return (fpres);
+  die(EXIT_FAILURE, "unknown presentation syle `%s'", name);
+}
+
 static int cmd_finger(int argc, char *argv[])
 {
   key_file f;
   int rc = 0;
+  const struct fpres *fpres = fprestab;
   const gchash *ch = &rmd160;
   key_filter kf = { KF_NONSECRET, KF_NONSECRET };
 
   for (;;) {
     static struct option opt[] = {
       { "filter",      OPTF_ARGREQ,    0,      'f' },
+      { "presentation",        OPTF_ARGREQ,    0,      'p' },
       { "algorithm",   OPTF_ARGREQ,    0,      'a' },
       { 0,             0,              0,      0 }
     };
-    int i = mdwopt(argc, argv, "+f:a:", opt, 0, 0, 0);
+    int i = mdwopt(argc, argv, "+f:a:p:", opt, 0, 0, 0);
     if (i < 0)
       break;
     switch (i) {
@@ -1727,6 +1754,9 @@ static int cmd_finger(int argc, char *argv[])
        if (err || *p)
          die(EXIT_FAILURE, "bad filter string `%s'", optarg);
       } break;
+      case 'p':
+       fpres = lookup_fpres(optarg);
+       break;
       case 'a':
        if ((ch = ghash_byname(optarg)) == 0)
          die(EXIT_FAILURE, "unknown hash algorithm `%s'", optarg);
@@ -1738,8 +1768,10 @@ static int cmd_finger(int argc, char *argv[])
   }
 
   argv += optind; argc -= optind;
-  if (rc)
-    die(EXIT_FAILURE, "Usage: fingerprint [-f FILTER] [TAG...]");
+  if (rc) {
+    die(EXIT_FAILURE,
+       "Usage: fingerprint [-a HASHALG] [-p STYLE] [-f FILTER] [TAG...]");
+  }
 
   doopen(&f, KOPEN_READ);
 
@@ -1748,7 +1780,7 @@ static int cmd_finger(int argc, char *argv[])
     for (i = 0; i < argc; i++) {
       key *k = key_bytag(&f, argv[i]);
       if (k)
-       fingerprint(k, ch, &kf);
+       fingerprint(k, fpres, ch, &kf);
       else {
        rc = 1;
        moan("key `%s' not found", argv[i]);
@@ -1758,50 +1790,13 @@ static int cmd_finger(int argc, char *argv[])
     key_iter i;
     key *k;
     for (key_mkiter(&i, &f); (k = key_next(&i)) != 0; )
-      fingerprint(k, ch, &kf);
+      fingerprint(k, fpres, ch, &kf);
   }
   return (rc);
 }
 
 /* --- @cmd_verify@ --- */
 
-static unsigned xdigit(char c)
-{
-  if ('A' <= c && c <= 'Z') return (c + 10 - 'A');
-  if ('a' <= c && c <= 'z') return (c + 10 - 'a');
-  if ('0' <= c && c <= '9') return (c - '0');
-  return (~0u);
-}
-
-static void unhexify(octet *q, char *p, size_t n)
-{
-  unsigned a = 0;
-  int i = 0;
-
-  for (;;) {
-    if (*p == '-' || *p == ':' || isspace((unsigned char)*p)) {
-      p++;
-      continue;
-    }
-    if (!n && !*p)
-      break;
-    if (!*p)
-      die(EXIT_FAILURE, "hex string too short");
-    if (!isxdigit((unsigned char)*p))
-      die(EXIT_FAILURE, "bad hex string");
-    if (!n)
-      die(EXIT_FAILURE, "hex string too long");
-    a = (a << 4) | xdigit(*p++);
-    i++;
-    if (i == 2) {
-      *q++ = U8(a);
-      a = 0;
-      i = 0;
-      n--;
-    }
-  }
-}
-
 static int cmd_verify(int argc, char *argv[])
 {
   key_file f;
@@ -1809,17 +1804,21 @@ static int cmd_verify(int argc, char *argv[])
   const gchash *ch = &rmd160;
   ghash *h;
   key *k;
-  octet *buf;
   const octet *fpr;
+  dstr d = DSTR_INIT, dd = DSTR_INIT;
+  codec *c;
+  const char *p;
+  const struct fpres *fpres = fprestab;
   key_filter kf = { KF_NONSECRET, KF_NONSECRET };
 
   for (;;) {
     static struct option opt[] = {
       { "filter",      OPTF_ARGREQ,    0,      'f' },
+      { "presentation",        OPTF_ARGREQ,    0,      'p' },
       { "algorithm",   OPTF_ARGREQ,    0,      'a' },
       { 0,             0,              0,      0 }
     };
-    int i = mdwopt(argc, argv, "+f:a:", opt, 0, 0, 0);
+    int i = mdwopt(argc, argv, "+f:a:p:", opt, 0, 0, 0);
     if (i < 0)
       break;
     switch (i) {
@@ -1829,6 +1828,9 @@ static int cmd_verify(int argc, char *argv[])
        if (err || *p)
          die(EXIT_FAILURE, "bad filter string `%s'", optarg);
       } break;
+      case 'p':
+       fpres = lookup_fpres(optarg);
+       break;
       case 'a':
        if ((ch = ghash_byname(optarg)) == 0)
          die(EXIT_FAILURE, "unknown hash algorithm `%s'", optarg);
@@ -1840,21 +1842,36 @@ static int cmd_verify(int argc, char *argv[])
   }
 
   argv += optind; argc -= optind;
-  if (rc || argc != 2)
-    die(EXIT_FAILURE, "Usage: verify [-f FILTER] TAG FINGERPRINT");
+  if (rc || argc != 2) {
+    die(EXIT_FAILURE,
+       "Usage: verify [-a HASHALG] [-p STYLE] [-f FILTER] TAG FINGERPRINT");
+  }
 
   doopen(&f, KOPEN_READ);
 
   if ((k = key_bytag(&f, argv[0])) == 0)
     die(EXIT_FAILURE, "key `%s' not found", argv[0]);
-  buf = xmalloc(ch->hashsz);
-  unhexify(buf, argv[1], ch->hashsz);
+  for (p = argv[1]; *p; p++) {
+    if (strchr(fpres->sep, *p)) continue;
+    dstr_putc(&dd, *p);
+  }
+  c = fpres->cdc->decoder(CDCF_IGNCASE | CDCF_IGNEQPAD |
+                         CDCF_IGNSPC | CDCF_IGNNEWL);
+  if ((rc = c->ops->code(c, dd.buf, dd.len, &d)) != 0 ||
+      (rc = c->ops->code(c, 0, 0, &d)) != 0)
+    die(EXIT_FAILURE, "invalid fingerprint: %s", codec_strerror(rc));
+  c->ops->destroy(c);
+  if (d.len != ch->hashsz) {
+    die(EXIT_FAILURE, "incorrect fingerprint length (%lu != %lu)",
+       (unsigned long)d.len, (unsigned long)ch->hashsz);
+  }
   h = GH_INIT(ch);
   if (!key_fingerprint(k, h, &kf))
     die(EXIT_FAILURE, "key has no fingerprintable components (as filtered)");
   fpr = GH_DONE(h, 0);
-  if (memcmp(fpr, buf, ch->hashsz) != 0)
+  if (memcmp(fpr, d.buf, ch->hashsz) != 0)
     die(EXIT_FAILURE, "key fingerprint mismatch");
+  dstr_destroy(&d); dstr_destroy(&dd);
   doclose(&f);
   return (0);
 }
@@ -2090,7 +2107,9 @@ static int cmd_merge(int argc, char *argv[])
   LI("Key-generation algorithms", keygen,                              \
      algtab[i].name, algtab[i].name)                                   \
   LI("Random seeding algorithms", seed,                                        \
-     seedtab[i].p, seedtab[i].p)
+     seedtab[i].p, seedtab[i].p)                                       \
+  LI("Fingerprint presentation styles", fpres,                         \
+     fprestab[i].name, fprestab[i].name)
 
 MAKELISTTAB(listtab, LISTS)
 
@@ -2113,17 +2132,21 @@ Options:\n\
 -q, --quiet            Show less information.\n\
 -v, --verbose          Show more information.\n\
 " },
-  { "fingerprint", cmd_finger, "fingerprint [-f FILTER] [TAG...]", "\
+  { "fingerprint", cmd_finger,
+    "fingerprint [-a HASHALG] [-p STYLE] [-f FILTER] [TAG...]", "\
 Options:\n\
 \n\
 -f, --filter=FILT      Only hash key components matching FILT.\n\
+-p, --presentation=STYLE Use STYLE for presenting fingerprints.\n\
 -a, --algorithm=HASH   Use the named HASH algorithm.\n\
                          ($ show hash for list.)\n\
 " },
-  { "verify", cmd_verify, "verify [-f FILTER] TAG FINGERPRINT", "\
+  { "verify", cmd_verify,
+    "verify [-a HASH] [-p STYLE] [-f FILTER] TAG FINGERPRINT", "\
 Options:\n\
 \n\
 -f, --filter=FILT      Only hash key components matching FILT.\n\
+-p, --presentation=STYLE Expect FINGERPRINT in the given STYLE.\n\
 -a, --algorithm=HASH   Use the named HASH algorithm.\n\
                          ($ show hash for list.)\n\
 " },