dsig.c: Allow precomputed hashes to be read from a file.
[u/mdw/catacomb] / dsig.c
diff --git a/dsig.c b/dsig.c
index 2707f45..5006423 100644 (file)
--- a/dsig.c
+++ b/dsig.c
@@ -469,14 +469,15 @@ static int sign(int argc, char *argv[])
   sig *s;
   time_t exp = KEXP_EXPIRE;
   unsigned verb = 0;
-  const char *ifile = 0;
+  const char *ifile = 0, *hfile = 0;
   const char *ofile = 0;
   const char *c = 0;
   const char *err;
   FILE *ifp, *ofp;
   dstr d = DSTR_INIT;
+  hfpctx hfp;
   block b;
-  int e;
+  int e, hf, n;
 
   for (;;) {
     static struct option opts[] = {
@@ -487,13 +488,15 @@ static int sign(int argc, char *argv[])
       { "quiet",       0,              0,      'q' },
       { "comment",     OPTF_ARGREQ,    0,      'c' },
       { "file",                OPTF_ARGREQ,    0,      'f' },
+      { "hashes",      OPTF_ARGREQ,    0,      'h' },
       { "output",      OPTF_ARGREQ,    0,      'o' },
       { "key",         OPTF_ARGREQ,    0,      'k' },
       { "expire",      OPTF_ARGREQ,    0,      'e' },
       { "nocheck",     OPTF_ARGREQ,    0,      'C' },
       { 0,             0,              0,      0 }
     };
-    int i = mdwopt(argc, argv, "+0vpqbC" "c:" "f:o:" "k:e:", opts, 0, 0, 0);
+    int i = mdwopt(argc, argv, "+0vpqbC" "c:" "f:h:o:" "k:e:",
+                  opts, 0, 0, 0);
     if (i < 0)
       break;
     switch (i) {
@@ -522,6 +525,9 @@ static int sign(int argc, char *argv[])
       case 'f':
        ifile = optarg;
        break;
+      case 'h':
+       hfile = optarg;
+       break;
       case 'o':
        ofile = optarg;
        break;
@@ -541,6 +547,8 @@ static int sign(int argc, char *argv[])
   }
   if (optind != argc || (f & f_bogus))
     die(EXIT_FAILURE, "Usage: sign [-OPTIONS]");
+  if (hfile && ifile)
+    die(EXIT_FAILURE, "Inconsistent options `-h' and `-f'");
 
   /* --- Locate the signing key --- */
 
@@ -562,6 +570,7 @@ static int sign(int argc, char *argv[])
 
   /* --- Open files --- */
 
+  if (hfile) ifile = hfile;
   if (!ifile || strcmp(ifile, "-") == 0)
     ifp = stdin;
   else if ((ifp = fopen(ifile, (f & f_bin) ? "rb" : "r")) == 0) {
@@ -605,32 +614,70 @@ static int sign(int argc, char *argv[])
 
   /* --- Now hash the various files --- */
 
-  for (;;) {
+  if (hfile) {
+    hfp.f = f;
+    hfp.fp = ifp;
+    hfp.ee = &encodingtab[ENC_HEX];
+    hfp.gch = GH_CLASS(s->h);
+    hfp.dline = &d;
+    hfp.dfile = &b.d;
+
+    n = 0;
+    for (;;) {
+      breset(&b);
+      DENSURE(&b.b, hfp.gch->hashsz);
+      hfp.hbuf = (octet *)b.b.buf;
+      if (ferror(ofp)) { f |= f_bogus; break; }
+      if ((hf = hfparse(&hfp)) == HF_EOF) break;
+      n++;
+
+      switch (hf) {
+       case HF_HASH:
+         if (hfp.gch != GH_CLASS(s->h)) {
+           moan("%s:%d: incorrect hash function `%s' (should be `%s')",
+                hfile, n, hfp.gch->name, GH_CLASS(s->h)->name);
+           f |= f_bogus;
+         }
+         break;
+       case HF_BAD:
+         moan("%s:%d: invalid hash-file line", hfile, n);
+         f |= f_bogus;
+         break;
+       case HF_FILE:
+         b.tag = T_FILE;
+         b.b.len += hfp.gch->hashsz;
+         bemit(&b, ofp, s->h, f & f_bin);
+         break;
+      }
+    }
+  } else {
+    for (;;) {
 
-    /* --- Stop on an output error --- */
+      /* --- Stop on an output error --- */
 
-    if (ferror(ofp)) {
-      f |= f_bogus;
-      break;
-    }
+      if (ferror(ofp)) {
+       f |= f_bogus;
+       break;
+      }
 
-    /* --- Read the next filename to hash --- */
+      /* --- Read the next filename to hash --- */
 
-    breset(&b);
-    if (getstring(ifp, &b.d, GSF_FILE | f))
-      break;
-    b.tag = T_FILE;
-    DENSURE(&b.b, GH_CLASS(s->h)->hashsz);
-    if (fhash(GH_CLASS(s->h), f | FHF_BINARY, b.d.buf, b.b.buf)) {
-      moan("Error reading `%s': %s", b.d.buf, strerror(errno));
-      f |= f_bogus;
-    } else {
-      b.b.len += GH_CLASS(s->h)->hashsz;
-      if (verb) {
-       fhex(stderr, b.b.buf, b.b.len);
-       fprintf(stderr, " %s\n", b.d.buf);
+      breset(&b);
+      if (getstring(ifp, &b.d, GSF_FILE | f))
+       break;
+      b.tag = T_FILE;
+      DENSURE(&b.b, GH_CLASS(s->h)->hashsz);
+      if (fhash(GH_CLASS(s->h), f | FHF_BINARY, b.d.buf, b.b.buf)) {
+       moan("error reading `%s': %s", b.d.buf, strerror(errno));
+       f |= f_bogus;
+      } else {
+       b.b.len += GH_CLASS(s->h)->hashsz;
+       if (verb) {
+         fhex(stderr, b.b.buf, b.b.len);
+         fprintf(stderr, " %s\n", b.d.buf);
+       }
+       bemit(&b, ofp, s->h, f & f_bin);
       }
-      bemit(&b, ofp, s->h, f & f_bin);
     }
   }
 
@@ -923,7 +970,7 @@ static cmd cmdtab[] = {
   { "show", cmd_show, "show [ITEM...]" },
   { "sign", sign,
     "sign [-0bpqvC] [-c COMMENT] [-k TAG] [-e EXPIRE]\n\t\
-[-f FILE] [-o OUTPUT]",
+[-f FILE] [-h FILE] [-o OUTPUT]",
     "\
 Options:\n\
 \n\
@@ -935,6 +982,7 @@ Options:\n\
 -C, --nocheck          Don't check the private key.\n\
 -c, --comment=COMMENT  Include COMMENT in the output file.\n\
 -f, --file=FILE                Read filenames to hash from FILE.\n\
+-h, --hashes=FILE      Read precomputed hashes from FILE.\n\
 -o, --output=FILE      Write the signed result to FILE.\n\
 -k, --key=TAG          Use a key named by TAG.\n\
 -e, --expire=TIME      The signature should expire after TIME.\n\