chiark / gitweb /
server/{keymgmt.c,tripe-admin.5.in}: Improve key-management errors.
authorMark Wooding <mdw@distorted.org.uk>
Wed, 25 Jan 2012 00:31:28 +0000 (00:31 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Mon, 7 May 2012 14:35:10 +0000 (15:35 +0100)
Many of the errors are rather vague and not well documented.  Overhaul
the way that errors are reported and propagated in the keymgmt subsystem
and document all of the various error conditions in detail.

server/keymgmt.c
server/tripe-admin.5.in

index ae456c37ed7d72d5f0dac43d7c1a02de90ce3ccd..c7d0c6bc1e52c4373e710f874dc745d8b69a9eca 100644 (file)
@@ -46,112 +46,132 @@ static fwatch w_priv, w_pub;
 
 typedef struct kgops {
   const char *ty;
-  const char *(*loadpriv)(key_data *, group **, mp **, dstr *);
-  const char *(*loadpub)(key_data *, group **, ge **, dstr *);
+  int (*loadpriv)(key_data *, group **, mp **, dstr *, dstr *);
+  int (*loadpub)(key_data *, group **, ge **, dstr *, dstr *);
 } kgops;
 
 /* --- Diffie-Hellman --- */
 
-static const char *kgdh_priv(key_data *kd, group **g, mp **x, dstr *t)
+static int kgdh_priv(key_data *kd, group **g, mp **x, dstr *t, dstr *e)
 {
   key_packstruct kps[DH_PRIVFETCHSZ];
   key_packdef *kp;
   dh_priv dp;
-  const char *e;
   int rc;
 
   kp = key_fetchinit(dh_privfetch, kps, &dp);
   if ((rc = key_unpack(kp, kd, t)) != 0) {
-    e = key_strerror(rc);
-    goto done;
+    a_format(e, "unpack-failed", "%s", key_strerror(rc), A_END);
+    goto fail_0;
   }
   *g = group_prime(&dp.dp);
   *x = MP_COPY(dp.x);
-  e = 0;
+  rc = 0;
+  goto done;
+fail_0:
+  rc = -1;
 done:
   key_fetchdone(kp);
-  return (e);
+  return (rc);
 }
 
-static const char *kgdh_pub(key_data *kd, group **g, ge **p, dstr *t)
+static int kgdh_pub(key_data *kd, group **g, ge **p, dstr *t, dstr *e)
 {
   key_packstruct kps[DH_PUBFETCHSZ];
   key_packdef *kp;
   dh_pub dp;
-  const char *e;
   int rc;
 
   kp = key_fetchinit(dh_pubfetch, kps, &dp);
   if ((rc = key_unpack(kp, kd, t)) != 0) {
-    e = key_strerror(rc);
-    goto done;
+    a_format(e, "unpack-failed", "%s", key_strerror(rc), A_END);
+    goto fail_0;
   }
   *g = group_prime(&dp.dp);
   *p = G_CREATE(*g);
   if (G_FROMINT(*g, *p, dp.y)) {
-    e = "bad public value";
-    goto done;
+    a_format(e, "bad-public-vector", A_END);
+    goto fail_1;
   }
-  e = 0;
+  rc = 0;
+  goto done;
+fail_1:
+  G_DESTROY(*g, *p);
+  G_DESTROYGROUP(*g);
+fail_0:
+  rc = -1;
 done:
   key_fetchdone(kp);
-  return (e);
+  return (rc);
 }
 
 static const kgops kgdh_ops = { "dh", kgdh_priv, kgdh_pub };
 
 /* --- Elliptic curve --- */
 
-static const char *kgec_priv(key_data *kd, group **g, mp **x, dstr *t)
+static int kgec_priv(key_data *kd, group **g, mp **x, dstr *t, dstr *e)
 {
   key_packstruct kps[EC_PRIVFETCHSZ];
   key_packdef *kp;
   ec_priv ep;
   ec_info ei;
-  const char *e;
+  const char *err;
   int rc;
 
   kp = key_fetchinit(ec_privfetch, kps, &ep);
   if ((rc = key_unpack(kp, kd, t)) != 0) {
-    e = key_strerror(rc);
-    goto done;
+    a_format(e, "unpack-failed", "%s", key_strerror(rc), A_END);
+    goto fail_0;
+  }
+  if ((err = ec_getinfo(&ei, ep.cstr)) != 0) {
+    a_format(e, "decode-failed", "%s", err, A_END);
+    goto fail_0;
   }
-  if ((e = ec_getinfo(&ei, ep.cstr)) != 0)
-    goto done;
   *g = group_ec(&ei);
   *x = MP_COPY(ep.x);
-  e = 0;
+  rc = 0;
+  goto done;
+fail_0:
+  rc = -1;
 done:
   key_fetchdone(kp);
-  return (e);
+  return (rc);
 }
 
-static const char *kgec_pub(key_data *kd, group **g, ge **p, dstr *t)
+static int kgec_pub(key_data *kd, group **g, ge **p, dstr *t, dstr *e)
 {
   key_packstruct kps[EC_PUBFETCHSZ];
   key_packdef *kp;
   ec_pub ep;
   ec_info ei;
-  const char *e;
+  const char *err;
   int rc;
 
   kp = key_fetchinit(ec_pubfetch, kps, &ep);
   if ((rc = key_unpack(kp, kd, t)) != 0) {
-    e = key_strerror(rc);
-    goto done;
+    a_format(e, "unpack-failed", "%s", key_strerror(rc), A_END);
+    goto fail_0;
+  }
+  if ((err = ec_getinfo(&ei, ep.cstr)) != 0) {
+    a_format(e, "decode-failed", "%s", err, A_END);
+    goto fail_0;
   }
-  if ((e = ec_getinfo(&ei, ep.cstr)) != 0)
-    goto done;
   *g = group_ec(&ei);
   *p = G_CREATE(*g);
   if (G_FROMEC(*g, *p, &ep.p)) {
-    e = "bad public point";
-    goto done;
+    a_format(e, "bad-public-vector", A_END);
+    goto fail_1;
   }
-  e = 0;
+  rc = 0;
+  goto done;
+fail_1:
+  G_DESTROY(*g, *p);
+  G_DESTROYGROUP(*g);
+fail_0:
+  rc = -1;
 done:
   key_fetchdone(kp);
-  return (e);
+  return (rc);
 }
 
 static const kgops kgec_ops = { "ec", kgec_priv, kgec_pub };
@@ -165,76 +185,98 @@ static const kgops *kgtab[] = { &kgdh_ops, &kgec_ops, 0 };
 /* --- @algs_get@ --- *
  *
  * Arguments:  @algswitch *a@ = where to put the algorithms
- *             @key_file *kf@ = key file (for some stupid reason)
+ *             @dstr *e@ = where to write errror tokens
+ *             @key_file *kf@ = key file
  *             @key *k@ = key to inspect
  *
- * Returns:    Null if OK, or an error message.
+ * Returns:    Zero if OK; nonzero on error.
  *
  * Use:                Extracts an algorithm choice from a key.
  */
 
-static const char *algs_get(algswitch *a, key_file *kf, key *k)
+static int algs_get(algswitch *a, dstr *e, key_file *kf, key *k)
 {
   const char *p;
-  char *q;
+  char *q, *qq;
   dstr d = DSTR_INIT;
-  const char *e;
+  int rc = -1;
 
-#define FAIL(msg) do { e = msg; goto done; } while (0)
+  /* --- Symmetric encryption for bulk data --- */
 
-  if ((p = key_getattr(kf, k, "cipher")) == 0)
-    p = "blowfish-cbc";
-  if ((a->c = gcipher_byname(p)) == 0)
-    FAIL("unknown-cipher");
+  if ((p = key_getattr(kf, k, "cipher")) == 0) p = "blowfish-cbc";
+  if ((a->c = gcipher_byname(p)) == 0) {
+    a_format(e, "unknown-cipher", "%s", p, A_END);
+    goto done;
+  }
+
+  /* --- Hash function --- */
+
+  if ((p = key_getattr(kf, k, "hash")) == 0) p = "rmd160";
+  if ((a->h = ghash_byname(p)) == 0) {
+    a_format(e, "unknown-hash", "%s", p, A_END);
+    goto done;
+  }
 
-  if ((p = key_getattr(kf, k, "hash")) == 0)
-    p = "rmd160";
-  if ((a->h = ghash_byname(p)) == 0)
-    FAIL("unknown-hash");
+  /* --- Symmetric encryption for key derivation --- */
 
   if ((p = key_getattr(kf, k, "mgf")) == 0) {
     dstr_reset(&d);
     dstr_putf(&d, "%s-mgf", a->h->name);
     p = d.buf;
   }
-  if ((a->mgf = gcipher_byname(p)) == 0)
-    FAIL("unknown-mgf-cipher");
+  if ((a->mgf = gcipher_byname(p)) == 0) {
+    a_format(e, "unknown-mgf-cipher", "%s", p, A_END);
+    goto done;
+  }
+
+  /* --- Message authentication for bulk data --- */
 
   if ((p = key_getattr(kf, k, "mac")) != 0) {
     dstr_reset(&d);
     dstr_puts(&d, p);
     if ((q = strchr(d.buf, '/')) != 0)
       *q++ = 0;
-    if ((a->m = gmac_byname(d.buf)) == 0)
-      FAIL("unknown-mac");
+    if ((a->m = gmac_byname(d.buf)) == 0) {
+      a_format(e, "unknown-mac", "%s", d.buf, A_END);
+      goto done;
+    }
     if (!q)
       a->tagsz = a->m->hashsz;
     else {
-      unsigned long n = strtoul(q, &q, 0);
-      if (*q) FAIL("bad-tag-length-string");
-      if (n%8 || n > ~(size_t)0) FAIL("bad-tag-length");
+      unsigned long n = strtoul(q, &qq, 0);
+      if (*qq)  {
+       a_format(e, "bad-tag-length-string", "%s", q, A_END);
+       goto done;
+      }
+      if (n%8 || n/8 > a->m->hashsz) {
+       a_format(e, "bad-tag-length", "%lu", n, A_END);
+       goto done;
+      }
       a->tagsz = n/8;
     }
   } else {
     dstr_reset(&d);
     dstr_putf(&d, "%s-hmac", a->h->name);
-    if ((a->m = gmac_byname(d.buf)) == 0)
-      FAIL("no-hmac-for-hash");
+    if ((a->m = gmac_byname(d.buf)) == 0) {
+      a_format(e, "no-hmac-for-hash", "%s", a->h->name, A_END);
+      goto done;
+    }
     a->tagsz = a->h->hashsz/2;
   }
 
-  e = 0;
+  rc = 0;
 done:
   dstr_destroy(&d);
-  return (e);
+  return (rc);
 }
 
 /* --- @algs_check@ --- *
  *
  * Arguments:  @algswitch *a@ = a choice of algorithms
+ *             @dstr *e@ = where to write error tokens
  *             @const group *g@ = the group we're working in
  *
- * Returns:    Null if OK, or an error message.
+ * Returns:    Zero if OK; nonzero on error.
  *
  * Use:                Checks an algorithm choice for sensibleness.  This also
  *             derives some useful information from the choices, and you
@@ -242,7 +284,7 @@ done:
  *             for use by @keyset@ functions.
  */
 
-static const char *algs_check(algswitch *a, const group *g)
+static int algs_check(algswitch *a, dstr *e, const group *g)
 {
   /* --- Derive the key sizes --- *
    *
@@ -252,24 +294,32 @@ static const char *algs_check(algswitch *a, const group *g)
    */
 
   a->hashsz = a->h->hashsz;
-  if ((a->cksz = keysz(a->hashsz, a->c->keysz)) == 0)
-    return ("no key size found for cipher");
-  if ((a->mksz = keysz(a->hashsz, a->m->keysz)) == 0)
-    return ("no key size found for MAC");
+  if ((a->cksz = keysz(a->hashsz, a->c->keysz)) == 0) {
+    a_format(e, "cipher", "%s", a->c->name,
+            "no-key-size", "%lu", (unsigned long)a->hashsz,
+            A_END);
+    return (-1);
+  }
+  if ((a->mksz = keysz(a->hashsz, a->m->keysz)) == 0) {
+    a_format(e, "mac", "%s", a->m->name,
+            "no-key-size", "%lu", (unsigned long)a->hashsz,
+            A_END);
+    return (-1);
+  }
 
   /* --- Derive the data limit --- */
 
   if (a->c->blksz < 16) a->expsz = MEG(64);
   else a->expsz = MEG(2048);
 
-  /* --- Ensure that the tag size is sane --- */
-
-  if (a->tagsz > a->m->hashsz) return ("tag length too large");
-
   /* --- Ensure the MGF accepts hashes as keys --- */
 
-  if (keysz(a->hashsz, a->mgf->keysz) != a->hashsz)
-    return ("MGF not suitable -- restrictive key schedule");
+  if (keysz(a->hashsz, a->mgf->keysz) != a->hashsz) {
+    a_format(e, "mgf", "%s", a->mgf->name,
+            "restrictive-key-schedule",
+            A_END);
+    return (-1);
+  }
 
   /* --- All ship-shape and Bristol-fashion --- */
 
@@ -299,7 +349,7 @@ static int algs_samep(const algswitch *a, const algswitch *aa)
  * Arguments:  @const char *file@ = name of the file
  *             @int line@ = line number in file
  *             @const char *msg@ = error message
- *             @void *p@ = argument pointer
+ *             @void *p@ = argument pointer (indicates which keyring)
  *
  * Returns:    ---
  *
@@ -308,11 +358,15 @@ static int algs_samep(const algswitch *a, const algswitch *aa)
 
 static void keymoan(const char *file, int line, const char *msg, void *p)
 {
-  a_warn("KEYMGMT",
-        "key-file-error",
-        "%s:%i", file, line,
-        "%s", msg,
-        A_END);
+  const char *kind = p;
+
+  if (!line) {
+    a_warn("KEYMGMT", "%s-keyring", kind, "%s", file,
+          "io-error", "?ERRNO", A_END);
+  } else {
+    a_warn("KEYMGMT", "%s-keyring", kind, "%s", file, "line", "%d", line,
+          "%s", msg, A_END);
+  }
 }
 
 /* --- @keykg@ --- *
@@ -359,26 +413,24 @@ static const kgops *keykg(key_file *kf, key *k, const char **tyr)
  * Use:                Loads the private key from its keyfile.
  */
 
-static int loadpriv(dstr *d)
+static int loadpriv(void)
 {
   key_file kf;
   key *k;
   key_data **kd;
   dstr t = DSTR_INIT;
+  dstr e = DSTR_INIT;
   group *g = 0;
   mp *x = 0;
   int rc = -1;
   const kgops *ko;
-  const char *e, *tag, *ty;
+  const char *err, *tag, *ty;
   algswitch a;
 
   /* --- Open the private key file --- */
 
-  if (key_open(&kf, kr_priv, KOPEN_READ, keymoan, 0)) {
-    dstr_putf(d, "error reading private keyring `%s': %s",
-             kr_priv, strerror(errno));
+  if (key_open(&kf, kr_priv, KOPEN_READ, keymoan, "private"))
     goto done_0;
-  }
 
   /* --- Find the private key --- */
 
@@ -386,37 +438,45 @@ static int loadpriv(dstr *d)
       key_qtag(&kf, tag = tag_priv, &t, &k, &kd) :
       key_qtag(&kf, tag = "tripe", &t, &k, &kd) &&
        key_qtag(&kf, tag = "tripe-dh", &t, &k, &kd)) {
-    dstr_putf(d, "private key `%s' not found in keyring `%s'", tag, kr_priv);
+    a_warn("KEYMGMT", "private-keyring", "%s", kr_priv,
+          "key-not-found", "%s", tag, A_END);
     goto done_1;
   }
 
   /* --- Look up the key type in the table --- */
 
   if ((ko = keykg(&kf, k, &ty)) == 0) {
-    dstr_putf(d, "private key `%s' has unknown type `%s'", t.buf, ty);
+    a_warn("KEYMGMT", "private-keyring",
+          "%s", kr_priv, "key", "%s", t.buf,
+          "unknown-group-type", "%s", ty, A_END);
     goto done_1;
   }
 
   /* --- Load the key --- */
 
-  if ((e = ko->loadpriv(*kd, &g, &x, &t)) != 0) {
-    dstr_putf(d, "error reading private key `%s': %s", t.buf, e);
+  if (ko->loadpriv(*kd, &g, &x, &t, &e)) {
+    a_warn("KEYMGMT", "private-keyring",
+          "%s", kr_priv, "key", "%s", t.buf,
+          "*%s", e.buf, A_END);
     goto done_1;
   }
 
   /* --- Check that the key is sensible --- */
 
-  if ((e = G_CHECK(g, &rand_global)) != 0) {
-    dstr_putf(d, "bad group in private key `%s': %s", t.buf, e);
+  if ((err = G_CHECK(g, &rand_global)) != 0) {
+    a_warn("KEYMGMT", "private-keyring",
+          "%s", kr_priv, "key", "%s", t.buf,
+          "bad-group", "%s", err, A_END);
     goto done_1;
   }
 
   /* --- Collect the algorithms --- */
 
-  if ((e = algs_get(&a, &kf, k)) != 0 ||
-      (e = algs_check(&a, g)) != 0) {
-    dstr_putf(d, "bad symmetric algorithm selection in private key `%s': %s",
-             t.buf, e);
+  if (algs_get(&a, &e, &kf, k) ||
+      algs_check(&a, &e, g)) {
+    a_warn("KEYMGMT", "private-keyring",
+          "%s", kr_priv, "key", "%s", t.buf,
+          "*%s", e.buf, A_END);
     goto done_1;
   }
 
@@ -430,7 +490,9 @@ static int loadpriv(dstr *d)
 
   if (gg) {
     if (!group_samep(g, gg)) {
-      dstr_putf(d, "private key `%s' has different group", t.buf);
+      a_warn("KEYMGMT", "private-keyring",
+            "%s", kr_priv, "key", "%s", t.buf,
+            "changed-group", A_END);
       goto done_1;
     }
     G_DESTROYGROUP(gg);
@@ -473,6 +535,7 @@ done_1:
   key_close(&kf);
 done_0:
   dstr_destroy(&t);
+  dstr_destroy(&e);
   if (x) mp_drop(x);
   if (g) G_DESTROYGROUP(g);
   return (rc);
@@ -487,13 +550,11 @@ done_0:
  * Use:                Reloads the public keyring.
  */
 
-static int loadpub(dstr *d)
+static int loadpub(void)
 {
   key_file *kf = CREATE(key_file);
 
-  if (key_open(kf, kr_pub, KOPEN_READ, keymoan, 0)) {
-    dstr_putf(d, "error reading public keyring `%s': %s",
-             kr_pub, strerror(errno));
+  if (key_open(kf, kr_pub, KOPEN_READ, keymoan, "public")) {
     DESTROY(kf);
     return (-1);
   }
@@ -513,7 +574,6 @@ static int loadpub(dstr *d)
 
 int km_reload(void)
 {
-  dstr d = DSTR_INIT;
   key_file *kf;
   int reload = 0;
 
@@ -521,10 +581,7 @@ int km_reload(void)
 
   if (fwatch_update(&w_priv, kr_priv)) {
     T( trace(T_KEYMGMT, "keymgmt: private keyring updated: reloading..."); )
-    DRESET(&d);
-    if (loadpriv(&d))
-      a_warn("KEYMGMT", "bad-private-key", "%s", d.buf, A_END);
-    else
+    if (!loadpriv())
       reload = 1;
   }
 
@@ -533,10 +590,7 @@ int km_reload(void)
   if (fwatch_update(&w_pub, kr_pub)) {
     T( trace(T_KEYMGMT, "keymgmt: public keyring updated: reloading..."); )
     kf = kf_pub;
-    DRESET(&d);
-    if (loadpub(&d))
-      a_warn("KEYMGMT", "bad-public-keyring", "%s", d.buf, A_END);
-    else {
+    if (!loadpub()) {
       reload = 1;
       key_close(kf);
       DESTROY(kf);
@@ -561,7 +615,6 @@ int km_reload(void)
 
 void km_init(const char *priv, const char *pub, const char *tag)
 {
-  dstr d = DSTR_INIT;
   const gchash *const *hh;
 
   kr_priv = priv;
@@ -577,11 +630,8 @@ void km_init(const char *priv, const char *pub, const char *tag)
     }
   }
 
-  DRESET(&d);
-  if (loadpriv(&d))
-    die(EXIT_FAILURE, "%s", d.buf);
-  if (loadpub(&d))
-    die(EXIT_FAILURE, "%s", d.buf);
+  if (loadpriv() || loadpub())
+    exit(EXIT_FAILURE);
 }
 
 /* --- @km_getpubkey@ --- *
@@ -600,8 +650,9 @@ int km_getpubkey(const char *tag, ge *kpub, time_t *t_exp)
   key *k;
   key_data **kd;
   dstr t = DSTR_INIT;
+  dstr e = DSTR_INIT;
   const kgops *ko;
-  const char *e, *ty;
+  const char *ty;
   group *g = 0;
   ge *p = 0;
   algswitch a;
@@ -610,24 +661,26 @@ int km_getpubkey(const char *tag, ge *kpub, time_t *t_exp)
   /* --- Find the key --- */
 
   if (key_qtag(kf_pub, tag, &t, &k, &kd)) {
-    a_warn("KEYMGMT", "public-key", "%s", tag, "not-found", A_END);
+    a_warn("KEYMGMT", "public-keyring", "%s", kr_pub,
+          "key-not-found", "%s", tag, A_END);
     goto done;
   }
 
   /* --- Look up the key type in the table --- */
 
   if ((ko = keykg(kf_pub, k, &ty)) == 0) {
-    a_warn("KEYMGMT",
-          "public-key", "%s", t.buf,
-          "unknown-type", "%s", ty,
-          A_END);
+    a_warn("KEYMGMT", "public-keyring",
+          "%s", kr_pub, "key", "%s", t.buf,
+          "unknown-group-type", "%s", ty, A_END);
     goto done;
   }
 
   /* --- Load the key --- */
 
-  if ((e = ko->loadpub(*kd, &g, &p, &t)) != 0) {
-    a_warn("KEYMGMT", "public-key", "%s", t.buf, "bad", "%s", e, A_END);
+  if (ko->loadpub(*kd, &g, &p, &t, &e)) {
+    a_warn("KEYMGMT", "public-keyring",
+          "%s", kr_pub, "key", "%s", t.buf,
+          "*%s", e.buf, A_END);
     goto done;
   }
 
@@ -638,15 +691,17 @@ int km_getpubkey(const char *tag, ge *kpub, time_t *t_exp)
    */
 
   if (!group_samep(gg, g)) {
-    a_warn("KEYMGMT", "public-key", "%s", t.buf, "incorrect-group", A_END);
+    a_warn("KEYMGMT", "public-keyring",
+          "%s", kr_pub, "key", "%s", t.buf,
+          "*%s", e.buf, A_END);
     goto done;
   }
 
   /* --- Check the public group element --- */
 
   if (group_check(gg, p)) {
-    a_warn("KEYMGMT",
-          "public-key", "%s", t.buf,
+    a_warn("KEYMGMT", "public-keyring",
+          "%s", kr_pub, "key", "%s", t.buf,
           "bad-public-group-element",
           A_END);
     goto done;
@@ -654,18 +709,16 @@ int km_getpubkey(const char *tag, ge *kpub, time_t *t_exp)
 
   /* --- Check the algorithms --- */
 
-  if ((e = algs_get(&a, kf_pub, k)) != 0) {
-    a_warn("KEYMGMT",
-          "public-key", "%s", t.buf,
-          "bad-algorithm-selection", e,
-          A_END);
+  if (algs_get(&a, &e, kf_pub, k)) {
+    a_warn("KEYMGMT", "public-keyring",
+          "%s", kr_pub, "key", "%s", t.buf,
+          "*%s", e.buf, A_END);
     goto done;
   }
   if (!algs_samep(&a, &algs)) {
-    a_warn("KEYMGMT",
-          "public-key", "%s", t.buf,
-          "algorithm-mismatch",
-          A_END);
+    a_warn("KEYMGMT", "public-keyring",
+          "%s", kr_pub, "key", "%s", t.buf,
+          "algorithm-mismatch", A_END);
     goto done;
   }
 
@@ -688,6 +741,7 @@ done:
   if (p) G_DESTROY(g, p);
   if (g) G_DESTROYGROUP(g);
   dstr_destroy(&t);
+  dstr_destroy(&e);
   return (rc);
 }
 
index 5b0fe3bef03ed66a46ebc810a11c4e7a374d37e0..0ab6711bdedc71862e80f58a2e4f11ea3ec70356 100644 (file)
@@ -1192,58 +1192,101 @@ up to something!
 Challenge received was old, but maybe not actually a replay.  Try again.
 .SS "KEYMGMT warnings"
 These indicate a problem with the keyring files, or the keys stored in
-them.
-.SP
-.BI "KEYMGMT bad-private-key " message
-The private key could not be read, or failed a consistency check.  If
-there was a problem with the file, usually there will have been
-.B key-file-error
-warnings before this.
-.SP
-.BI "KEYMGMT bad-public-keyring " message
-The public keyring couldn't be read.  Usually, there will have been
-.B key-file-error
-warnings before this.
-.SP
-.BI "KEYMGMT key-file-error " file ":" line " " message
-Reports a specific error with the named keyring file.  This probably
-indicates a bug in
-.BR key (1).
-.SP
-.BI "KEYMGMT public-key " tag " " tokens\fR...
-These messages all indicate a problem with the public key named
-.IR tag .
-.SP
-.BI "KEYMGMT public-key " tag " algorithm-mismatch"
-The algorithms specified on the public key don't match the ones for our
-private key.  All the peers in a network have to use the same
-algorithms.
-.SP
-.BI "KEYMGMT public-key " tag " bad " message
-The public key couldn't be read, or is invalid.
-.SP
-.BI "KEYMGMT public-key " tag " bad-public-group-element"
-The public key is invalid.  This may indicate a malicious attempt to
-introduce a bogus key.
-.SP
-.BI "KEYMGMT public-key " tag " bad-algorithm-selection"
-The algorithms listed on the public key couldn't be understood.  The
-algorithm selection attributes are probably malformed and need fixing.
+them.  The first token is either
+.B private-keyring
+or
+.B public-keyring
+(notated
+.IB which -keyring
+in the descriptions below) indicating which keyring file is problematic,
+and the second token is the filename of the keyring.  Frequently a key
+tag may be given next, preceded by the token
+.BR key .
+.SP
+.BI "KEYMGMT public-keyring " file " key " tag " algorithm-mismatch"
+A peer's public key doesn't request the same algorithms as our private
+key.
+.SP
+.BI "KEYMGMT " which "-keyring " file " key " tag " bad-tag-length " len
+The key attributes specify the length of MAC tag as
+.I len
+but this is an invalid value \(en either too large or not a multiple of
+eight.
+.SP
+.BI "KEYMGMT " which "-keyring " file " key " tag " bad-tag-length-string " str
+The key attributes contain
+.I str
+where a MAC tag length was expected.  The key was generated wrongly.
+.SP
+.BI "KEYMGMT private-keyring " file " key " tag " changed-group"
+The private keyring has been changed, but the new private key can't be
+used because it uses a different group for Diffie\(enHellman key
+exchange.
+.SP
+.BI "KEYMGMT " which "-keyring " file " io-error " ecode " " message
+A system error occurred while opening or reading the keyring file.
+.SP
+.BI "KEYMGMT " which "-keyring " file " key " tag " unknown-cipher " cipher
+The key specifies the use of an unknown symmetric encryption algorithm
+.IR cipher .
+Maybe the key was generated wrongly, or maybe the version of
+Catacomb installed is too old.
+.SP
+.BI "KEYMGMT " which "-keyring " file " key " tag " unknown-group-type " type
+The key specifies the use of a Diffie\(enHellman group of an unknown
+.IR type .
+Maybe the key was generated wrongly, or maybe the version of
+.BR tripe (8)
+is too old.
+.SP
+.BI "KEYMGMT " which "-keyring " file " key " tag " unknown-hash " hash
+The key specifies the use of an unknown hash function
+.IR hash .
+Maybe the key was generated wrongly, or maybe the version of Catacomb
+installed is too old.
+.SP
+.BI "KEYMGMT " which "-keyring " file " key " tag " unknown-mac " mac
+The key specifies the use of an unknown message authentication code
+.IR mac .
+Maybe the key was generated wrongly, or maybe the version of Catacomb
+installed is too old.
+.SP
+.BI "KEYMGMT " which "-keyring " file " key " tag " unknown-mgf-cipher " mgf
+The key specifies the use of an unknown symmetric encryption function
+.I mgf
+for mask generation.  Maybe the key was generated wrongly, or maybe the
+version of Catacomb installed is too old.
+.SP
+.BI "KEYMGMT " which "-keyring " file " key " tag " no-hmac-for-hash " hash
+No message authentication code was given explicitly, and there's no
+implementation of HMAC for the selected hash function
+.IR hash .
+.SP
+.BI "KEYMGMT " which "-keyring " file " key " tag " " alg " " name " no-key-size " hashsz
+The
+.I alg
+token is either
+.B cipher
+or
+.BR mac .
+The named algorithm requires more key material than the hash function
+can provide.  You must change either the hash function, or the cipher or
+MAC.
 .SP
-.BI "KEYMGMT public-key " tag " incorrect-group"
-The public key doesn't use the same group as our private key.  All the
-peers in a network have to use the same group.
+.BI "KEYMGMT " which "-keyring " file " key " tag " mgf " mgf " restrictive-key-schedule"
+The cipher selected for mask-generation is unsuitable because it can't
+accept arbitrary-sized keys.
 .SP
-.BI "KEYMGMT public-key " tag " not-found"
-The public key for peer
+.BI "KEYMGMT " which "-keyring " file " key-not-found " tag
+A key named
 .I tag
-wasn't in the public keyring.
+couldn't be found in the keyring.
 .SP
-.BI "KEYMGMT public-key " tag " unknown-type"
-The type of the public key isn't understood.  Maybe you need to upgrade
-your copy of
-.BR tripe .
-(Even if you do, you'll have to regenerate your keys.)
+.BI "KEYMGMT " which "-keyring " file " line " line " " message
+The contents of the keyring file are invalid.  There may well be a bug
+in the
+.BR key (1)
+program.
 .SS "KX warnings"
 These indicate problems during key-exchange.  Many indicate either a bug
 in the server (either yours or the remote one), or some kind of attack