key_file kf;
key *k;
sig *s;
+ fhashstate fh;
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[] = {
{ "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) {
case 'f':
ifile = optarg;
break;
+ case 'h':
+ hfile = optarg;
+ break;
case 'o':
ofile = optarg;
break;
}
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 --- */
/* --- Open files --- */
+ if (hfile) ifile = hfile;
if (!ifile || strcmp(ifile, "-") == 0)
ifp = stdin;
else if ((ifp = fopen(ifile, (f & f_bin) ? "rb" : "r")) == 0) {
/* --- 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);
+ fhash_init(&fh, GH_CLASS(s->h), f | FHF_BINARY);
+ 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(&fh, 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);
+ fhash_free(&fh);
}
}
/*----- Signature verification --------------------------------------------*/
+static int checkjunk(const char *path, const struct stat *st, void *p)
+{
+ if (!st) printf("JUNK (error %s) %s\n", strerror(errno), path);
+ else printf("JUNK %s %s\n", describefile(st), path);
+ return (0);
+}
+
static int verify(int argc, char *argv[])
{
#define f_bogus 1u
sig *s;
dstr d = DSTR_INIT;
const char *err;
+ fhashstate fh;
FILE *fp;
block b;
int e;
{ "progress", 0, 0, 'p' },
{ "quiet", 0, 0, 'q' },
{ "nocheck", 0, 0, 'C' },
+ { "junk", 0, 0, 'j' },
{ 0, 0, 0, 0 }
};
- int i = mdwopt(argc, argv, "+vpqC", opts, 0, 0, 0);
+ int i = mdwopt(argc, argv, "+vpqCj", opts, 0, 0, 0);
if (i < 0)
break;
switch (i) {
case 'C':
f |= f_nocheck;
break;
+ case 'j':
+ f |= FHF_JUNK;
+ break;
default:
f |= f_bogus;
break;
if (!(f & f_nocheck) && verb && (err = s->ops->check(s)) != 0)
printf("WARN public key fails check: %s", err);
+ fhash_init(&fh, GH_CLASS(s->h), f | FHF_BINARY);
for (;;) {
switch (e) {
case T_COMMENT:
case T_FILE:
DRESET(&d);
DENSURE(&d, GH_CLASS(s->h)->hashsz);
- if (fhash(GH_CLASS(s->h), f | FHF_BINARY, b.d.buf, d.buf)) {
+ if (fhash(&fh, b.d.buf, d.buf)) {
if (verb > 1) {
printf("BAD error reading file `%s': %s\n",
b.d.buf, strerror(errno));
}
}
done:
+ if ((f & FHF_JUNK) && fhash_junk(&fh, checkjunk, 0))
+ f |= f_bogus;
+ fhash_free(&fh);
bdestroy(&b);
dstr_destroy(&d);
freesig(s);
{ "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\
-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\