Add an internal-representation no-op function.
[u/mdw/catacomb] / dsig.c
diff --git a/dsig.c b/dsig.c
index 9818367..c5eb5af 100644 (file)
--- a/dsig.c
+++ b/dsig.c
@@ -1,6 +1,6 @@
 /* -*-c-*-
  *
- * $Id: dsig.c,v 1.1 2000/06/17 10:54:29 mdw Exp $
+ * $Id: dsig.c,v 1.7 2001/02/23 09:04:17 mdw Exp $
  *
  * Verify signatures on distribuitions of files
  *
 /*----- Revision history --------------------------------------------------* 
  *
  * $Log: dsig.c,v $
+ * Revision 1.7  2001/02/23 09:04:17  mdw
+ * Add new hash functions.  Provide full help for subcommands.  Run the
+ * hash function over parts of the header in a canonical order.
+ *
+ * Revision 1.6  2000/12/06 20:33:27  mdw
+ * Make flags be macros rather than enumerations, to ensure that they're
+ * unsigned.
+ *
+ * Revision 1.5  2000/10/08 12:12:09  mdw
+ * Shut up some warnings.
+ *
+ * Revision 1.4  2000/08/04 23:23:44  mdw
+ * Various <ctype.h> fixes.
+ *
+ * Revision 1.3  2000/07/15 20:53:23  mdw
+ * More hash functions.  Bug fix in getstring.
+ *
+ * Revision 1.2  2000/07/01 11:27:22  mdw
+ * Use new PKCS#1 padding functions rather than rolling by hand.
+ *
  * Revision 1.1  2000/06/17 10:54:29  mdw
  * Program to generate and verify signatures on multiple files.
  *
 #include <mLib/report.h>
 #include <mLib/sub.h>
 
-#include "dsa.h"
 #include "getdate.h"
 #include "grand.h"
 #include "ghash.h"
 #include "key.h"
 #include "key-data.h"
-#include "md5.h"
 #include "noise.h"
-#include "rmd160.h"
+
+#include "dsa.h"
 #include "rsa.h"
+#include "pkcs1.h"
+
+#include "md2.h"
+#include "md4.h"
+#include "md5.h"
+#include "rmd128.h"
+#include "rmd160.h"
+#include "rmd256.h"
+#include "rmd320.h"
 #include "sha.h"
+#include "sha256.h"
+#include "sha384.h"
+#include "sha512.h"
+#include "tiger.h"
 
 /*----- Digital signature algorithm ---------------------------------------*/
 
@@ -81,8 +113,10 @@ static int dsasign(key *k, const void *m, size_t msz, dstr *d)
     return (e);
   }
   sz = mp_octets(dp.dp.q);
+  if (sz < msz)
+    die(EXIT_FAILURE, "hash function too wide for this signing key");
   DENSURE(d, sz * 2);
-  p = d->buf + d->len;
+  p = (octet *)d->buf + d->len;
   rand_get(RAND_GLOBAL, p, sz);
   dsa_sign(&dp.dp, dp.x, m, msz, p, sz, p, sz, p + sz, sz);
   d->len += sz * 2;
@@ -97,6 +131,7 @@ static int dsaverify(key *k, const void *m, size_t msz,
   key_packstruct ks[DSA_PUBFETCHSZ];
   key_packdef *kp;
   size_t sz;
+  const octet *p = s;
   int e;
 
   kp = key_fetchinit(dsa_pubfetch, ks, &dp);
@@ -105,7 +140,7 @@ static int dsaverify(key *k, const void *m, size_t msz,
     return (e);
   }
   sz = ssz / 2;
-  e = dsa_verify(&dp.dp, dp.y, m, msz, s, sz, s + sz, sz);
+  e = dsa_verify(&dp.dp, dp.y, m, msz, p, sz, p + sz, sz);
   key_fetchdone(kp);
   return (e);  
 }
@@ -115,11 +150,10 @@ static int dsaverify(key *k, const void *m, size_t msz,
 static int rsasign(key *k, const void *m, size_t msz, dstr *d)
 {
   rsa_priv rp;
+  rsa_privctx rpc;
+  pkcs1 pk = { 0, 0, 0 };
   key_packstruct ks[RSA_PRIVFETCHSZ];
   key_packdef *kp;
-  octet *p;
-  size_t sz;
-  mp *x;
   int e;
 
   kp = key_fetchinit(rsa_privfetch, ks, &rp);
@@ -127,22 +161,10 @@ static int rsasign(key *k, const void *m, size_t msz, dstr *d)
     key_fetchdone(kp);
     return (e);
   }
-  sz = mp_octets(rp.n);
-  if (sz < msz + 11)
-    die(EXIT_FAILURE, "key too small (help!)");
-  DENSURE(d, sz);
-  p = d->buf + d->len;
-  memcpy(p + sz - msz, m, msz);
-  p[0] = 0;
-  p[1] = 1;
-  memset(p + 2, 0xff, sz - msz - 3);
-  p[sz - msz - 1] = 0;
-  memcpy(p + sz - msz, m, msz);
-  x = mp_loadb(MP_NEW, p, sz);
-  x = rsa_decrypt(&rp, x, x, &rand_global);
-  mp_storeb(x, p, sz);
-  d->len += sz;
-  mp_drop(x);
+  rsa_privcreate(&rpc, &rp, &rand_global);
+  if (rsa_sign(&rpc, m, msz, d, pkcs1_sigencode, &pk) < 0)
+    die(EXIT_FAILURE, "internal error in rsasign (key too small?)");
+  rsa_privdestroy(&rpc);
   key_fetchdone(kp);
   return (0);
 }
@@ -151,41 +173,26 @@ static int rsaverify(key *k, const void *m, size_t msz,
                     const void *s, size_t ssz)
 {
   rsa_pub rp;
+  rsa_pubctx rpc;
+  pkcs1 pk = { 0, 0, 0 };
   key_packstruct ks[RSA_PUBFETCHSZ];
   key_packdef *kp;
-  octet *p;
-  size_t sz;
-  mp *x;
-  mpmont mm;
   int ok = 0;
+  dstr d = DSTR_INIT;
   int e;
-  int i;
 
   kp = key_fetchinit(rsa_pubfetch, ks, &rp);
   if ((e = key_fetch(kp, k)) != 0) {
     key_fetchdone(kp);
     return (e);
   }
-  x = mp_loadb(MP_NEW, s, ssz);
-  mpmont_create(&mm, rp.n);
-  x = mpmont_exp(&mm, x, x, rp.e);
-  mpmont_destroy(&mm);
-  sz = mp_octets(rp.n);
-  p = xmalloc(sz);
-  mp_storeb(x, p, sz);
-  mp_drop(x);
+  rsa_pubcreate(&rpc, &rp);
+  if (rsa_verify(&rpc, s, ssz, &d, pkcs1_sigdecode, &pk) > 0 &&
+      msz == d.len && memcmp(d.buf, m, msz) == 0)
+    ok = 1;
+  dstr_destroy(&d);
+  rsa_pubdestroy(&rpc);
   key_fetchdone(kp);
-  if (msz + 11 >= sz || p[0] != 0 || p[1] != 1 || p[sz - msz - 1] != 0)
-    goto done;
-  for (i = 2; i < sz - msz - 1; i++) {
-    if (p[i] != 0xff)
-      goto done;
-  }
-  if (memcmp(m, p + sz - msz, msz) != 0)
-    goto done;
-  ok = 1;
-done:
-  free(p);
   return (ok);
 }  
 
@@ -199,7 +206,9 @@ typedef struct sig {
                const void */*s*/, size_t /*ssz*/);
 } sig;
 
-static const gchash *hashtab[] = { &rmd160, &sha, &md5, 0 };
+static const gchash *hashtab[] = {
+  &rmd160, &tiger, &sha, &sha256, &sha384, &sha512,
+  &rmd128, &rmd256, &rmd320, &md5, &md4, &md2, 0 };
 static sig sigtab[] = {
   { "dsa", "dsig-dsa", dsasign, dsaverify },
   { "rsa", "dsig-rsa", rsasign, rsaverify },
@@ -357,7 +366,7 @@ static int getstring(FILE *fp, dstr *d, unsigned raw)
 
 again:
   ch = getc(fp);
-  while (isspace((unsigned char)ch))
+  while (isspace(ch))
     ch = getc(fp);
   if (ch == '#') {
     do ch = getc(fp); while (ch != '\n' && ch != EOF);
@@ -389,7 +398,7 @@ again:
       if (ch == EOF)
        break;
       switch (ch) {
-       case 'a': ch = '\n'; break;
+       case 'a': ch = '\a'; break;
        case 'b': ch = '\b'; break;
        case 'f': ch = '\f'; break;
        case 'n': ch = '\n'; break;
@@ -398,6 +407,7 @@ again:
        case 'v': ch = '\v'; break;
       }
       DPUTC(d, ch);
+      ch = getc(fp);
       continue;
     }
 
@@ -823,14 +833,14 @@ static void keyreport(const char *file, int line, const char *err, void *p)
  *
  * Arguments:  @const gchash *c@ = pointer to hash class
  *             @const char *file@ = file to hash
- *             @octet *b@ = pointer to output buffer
+ *             @void *b@ = pointer to output buffer
  *
  * Returns:    Zero if it worked, or nonzero for a system error.
  *
  * Use:                Hashes a file.
  */
 
-static int fhash(const gchash *c, const char *file, octet *b)
+static int fhash(const gchash *c, const char *file, void *b)
 {
   FILE *fp = fopen(file, "rb");
   ghash *h = c->init();
@@ -879,11 +889,9 @@ static void fhex(FILE *fp, const void *p, size_t sz)
 
 static int sign(int argc, char *argv[])
 {
-  enum {
-    f_raw = 1,
-    f_bin = 2,
-    f_bogus = 4
-  };
+#define f_raw 1u
+#define f_bin 2u
+#define f_bogus 4u
 
   unsigned f = 0;
   const char *kt = 0;
@@ -1014,6 +1022,8 @@ static int sign(int argc, char *argv[])
        ofile, strerror(errno));
   }
 
+  h = gch->init();
+
   /* --- Emit the start of the output --- */
 
   binit(&b); b.tag = T_IDENT;
@@ -1021,20 +1031,19 @@ static int sign(int argc, char *argv[])
   bemit(&b, ofp, 0, f & f_bin);
   
   breset(&b); b.tag = T_SIGALG; DPUTS(&b.d, s->name);
-  bemit(&b, ofp, 0, f & f_bin);
+  bemit(&b, ofp, h, f & f_bin);
 
   breset(&b); b.tag = T_HASHALG; DPUTS(&b.d, gch->name);
-  bemit(&b, ofp, 0, f & f_bin);
+  bemit(&b, ofp, h, f & f_bin);
 
   breset(&b); b.tag = T_KEYID; b.k = k->id;
-  bemit(&b, ofp, 0, f & f_bin);
+  bemit(&b, ofp, h, f & f_bin);
 
   /* --- Start hashing, and emit the datestamps and things --- */
 
   {
     time_t now = time(0);
 
-    h = gch->init();
     breset(&b); b.tag = T_DATE; b.t = now; bemit(&b, ofp, h, f & f_bin);
     if (exp == KEXP_EXPIRE)
       exp = now + 86400 * 28;
@@ -1121,17 +1130,19 @@ static int sign(int argc, char *argv[])
   if (f & f_bogus)
     die(EXIT_FAILURE, "error(s) occurred while creating signature");
   return (EXIT_SUCCESS);
+
+#undef f_raw
+#undef f_bin
+#undef f_bogus
 }
 
 /*----- Signature verification --------------------------------------------*/
 
 static int verify(int argc, char *argv[])
 {
-  enum {
-    f_bogus = 1,
-    f_bin = 2,
-    f_ok = 4
-  };
+#define f_bogus 1u
+#define f_bin 2u
+#define f_ok 4u
 
   unsigned f = 0;
   unsigned verb = 1;
@@ -1140,7 +1151,7 @@ static int verify(int argc, char *argv[])
   sig *s = sigtab;
   const gchash *gch = &rmd160;
   dstr d = DSTR_INIT;
-  ghash *h;
+  ghash *h = 0;
   FILE *fp;
   block b;
   int e;
@@ -1251,11 +1262,25 @@ static int verify(int argc, char *argv[])
   /* --- Initialize the hash function and start reading hashed packets --- */
 
   h = gch->init();
+
   if (!k) {
     if (verb)
       puts("FAIL no keyid packet found");
     exit(EXIT_FAILURE);
   }
+
+  {
+    block bb;
+    binit(&bb);
+    breset(&bb); bb.tag = T_SIGALG; DPUTS(&bb.d, s->name);
+    bemit(&bb, 0, h, 0);
+    breset(&bb); bb.tag = T_HASHALG; DPUTS(&bb.d, gch->name);
+    bemit(&bb, 0, h, 0);
+    breset(&bb); bb.tag = T_KEYID; bb.k = k->id;
+    bemit(&bb, 0, h, 0);
+    bdestroy(&bb);
+  }
+
   for (;;) {
     switch (e) {
       case T_COMMENT:
@@ -1350,6 +1375,10 @@ done:
       puts("OK signature verified");
   }
   return (f & f_bogus ? EXIT_FAILURE : EXIT_SUCCESS);
+
+#undef f_bogus
+#undef f_bin
+#undef f_ok
 }
 
 /*----- Main code ---------------------------------------------------------*/
@@ -1357,6 +1386,7 @@ done:
 typedef struct cmd {
   const char *name;
   int (*func)(int /*argc*/, char */*argv*/[]);
+  const char *usage;
   const char *help;
 } cmd;
 
@@ -1365,13 +1395,64 @@ static cmd cmdtab[] = {
 /*     "manifest [-0] [-o output]" }, */
   { "sign", sign,
     "sign [-options]\n\
-       [-0v] [-a alg] [-h hash] [-t keytype] [-i keyid]\n\
-       [-e expire] [-f file] [-o output]" },
+       [-0bqv] [-a alg] [-h hash] [-t keytype] [-i keyid]\n\
+       [-e expire] [-f file] [-o output]", "\
+Options:\n\
+\n\
+-0, --null             Read null-terminated filenames from stdin.\n\
+-b, --binary           Produce a binary output file.\n\
+-q, --quiet            Produce fewer messages while working.\n\
+-v, --verbose          Produce more messages while working.\n\
+-a, --algorithm=SIGALG Use the SIGALG signature algorithm.\n\
+-h, --hash=HASHASL     Use the HASHALG message digest algorithm.\n\
+-c, --comment=COMMENT  Include COMMENT in the output file.\n\
+-f, --file=FILE                Read filenames to hash from FILE.\n\
+-o, --output=FILE      Write the signed result to FILE.\n\
+-t, --keytype=TYPE     Use a key with type TYPE to do the signing.\n\
+-i, --keyid=ID         Use the key with the given ID to do the signing.\n\
+-e, --expire=TIME      The signature should expire after TIME.\n\
+" },
   { "verify", verify,
-    "verify [-qv] [file]" },
+    "verify [-qv] [file]", "\
+Options:\n\
+\n\
+-q, --quiet            Produce fewer messages while working.\n\
+-v, --verbose          Produce more messages while working.\n\
+" },
   { 0, 0, 0 }
 };
 
+/* --- @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 = cmdtab; 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);
+}
+
 static void version(FILE *fp)
 {
   pquis(fp, "$, Catacomb version " VERSION "\n");
@@ -1382,17 +1463,27 @@ static void usage(FILE *fp)
   pquis(fp, "Usage: $ [-k keyring] command [args]\n");
 }
 
-static void help(FILE *fp)
+static 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 {
+    version(fp);
+    fputc('\n', fp);
+    usage(fp);
+    fputs("\n\
 Create and verify signatures on lists of files.\n\
 \n", fp);
-  for (c = cmdtab; c->name; c++)
-    fprintf(fp, "%s\n", c->help);
+    for (c = cmdtab; c->name; c++)
+      fprintf(fp, "%s\n", c->usage);
+  }
 }
 
 /* --- @main@ --- *
@@ -1409,12 +1500,8 @@ Create and verify signatures on lists of files.\n\
 int main(int argc, char *argv[])
 {
   unsigned f = 0;
-  cmd *c = 0, *cc = 0;
-  size_t n;
 
-  enum {
-    f_bogus = 1
-  };
+#define f_bogus 1u
 
   /* --- Initialize the library --- */
 
@@ -1438,7 +1525,7 @@ int main(int argc, char *argv[])
       break;
     switch (i) {
       case 'h':
-       help(stdout);
+       help(stdout, argv + optind);
        exit(0);
        break;
       case 'v':
@@ -1467,21 +1554,9 @@ int main(int argc, char *argv[])
 
   /* --- Dispatch to the correct subcommand handler --- */
 
-  n = strlen(argv[0]);
-  for (c = cmdtab; c->name; c++) {
-    if (strncmp(argv[0], c->name, n) == 0) {
-      if (c->name[n] == 0) {
-       cc = c;
-       break;
-      } else if (cc)
-       die(EXIT_FAILURE, "ambiguous command name `%s'", argv[0]);
-      else
-       cc = c;
-    }
-  }
-  if (!cc)
-    die(EXIT_FAILURE, "unknown command `%s'", argv[0]);
-  return (cc->func(argc, argv));
+  return (findcmd(argv[0])->func(argc, argv));
+
+#undef f_bogus
 }
 
 /*----- That's all, folks -------------------------------------------------*/