X-Git-Url: https://git.distorted.org.uk/u/mdw/catacomb/blobdiff_plain/c65df27983057ec76ed0e72bb370f9a5ae7dad28..d32965008f328887b38d68d27665503ee875bfe5:/catcrypt.c diff --git a/catcrypt.c b/catcrypt.c index cc1ab39..4e54712 100644 --- a/catcrypt.c +++ b/catcrypt.c @@ -88,17 +88,16 @@ static const char *keyring = "keyring"; * the verification key-id if the message is signed. * * Next comes the key-encapsulation chunk. This is decrypted in some - * KEM-specific way to yield a secret hash. This hash is what is signed if - * the message is signed. The hash is expanded using an MGF (or similar) to - * make a symmetric encryption and MAC key. + * KEM-specific way to yield a secret hash. The hash is expanded using an + * MGF (or similar) to make a symmetric encryption and MAC key. * * If the message is signed, there comes a signature chunk. The signature is - * on the secret hash. This means that the recipient can modify the message - * and still have a valid signature, so it's not useful for proving things to - * other people; but it also means that the recipient knows that the message - * is from someone who knows the hash, which limits the possiblities to (a) - * whoever encrypted the message (good!) and (b) whoever knows the - * recipient's private key. + * on the further output of the MGF. This means that the recipient can + * modify the message and still have a valid signature, so it's not useful + * for proving things to other people; but it also means that the recipient + * knows that the message is from someone who knows the hash, which limits + * the possiblities to (a) whoever encrypted the message (good!) and (b) + * whoever knows the recipient's private key. * * Then come message chunks. Each one begins with a MAC over an implicit * sequence number and the ciphertext. The final chunk's ciphertext is @@ -226,16 +225,16 @@ static int encrypt(int argc, char *argv[]) dstr_reset(&d); key_fulltag(k, &d); - e = initenc(eo, ofp, "CATCRYPT ENCRYPTED MESSAGE", 1); + e = initenc(eo, ofp, "CATCRYPT ENCRYPTED MESSAGE"); km = getkem(k, "cckem", 0); if ((err = km->ops->check(km)) != 0) - moan("key `%s' fails check: %s", d.buf, err); + moan("key %s fails check: %s", d.buf, err); if (sk) { dstr_reset(&d); key_fulltag(sk, &d); s = getsig(sk, "ccsig", 1); if ((err = s->ops->check(s)) != 0) - moan("key `%s' fails check: %s", d.buf, err); + moan("key %s fails check: %s", d.buf, err); } /* --- Build the header chunk --- */ @@ -328,10 +327,11 @@ static int encrypt(int argc, char *argv[]) static int decrypt(int argc, char *argv[]) { const char *of = 0; - FILE *ofp = 0; + FILE *ofp = 0, *rfp = 0; FILE *fp = 0; const char *ef = "binary"; int i; + size_t n; dstr d = DSTR_INIT; buf b; key_file kf; @@ -353,21 +353,24 @@ static int decrypt(int argc, char *argv[]) enc *e; #define f_bogus 1u +#define f_buffer 2u for (;;) { static const struct option opt[] = { { "armour", 0, 0, 'a' }, { "armor", 0, 0, 'a' }, + { "buffer", 0, 0, 'b' }, { "verbose", 0, 0, 'v' }, { "quiet", 0, 0, 'q' }, { "format", OPTF_ARGREQ, 0, 'f' }, { "output", OPTF_ARGREQ, 0, 'o' }, { 0, 0, 0, 0 } }; - i = mdwopt(argc, argv, "af:o:qv", opt, 0, 0, 0); + i = mdwopt(argc, argv, "abf:o:qv", opt, 0, 0, 0); if (i < 0) break; switch (i) { case 'a': ef = "pem"; break; + case 'b': f |= f_buffer; break; case 'v': verb++; break; case 'q': if (verb) verb--; break; case 'f': ef = optarg; break; @@ -395,7 +398,7 @@ static int decrypt(int argc, char *argv[]) if (key_open(&kf, keyring, KOPEN_READ, keyreport, 0)) die(EXIT_FAILURE, "can't open keyring `%s'", keyring); - e = initenc(eo, fp, "CATCRYPT ENCRYPTED MESSAGE", 0); + e = initdec(eo, fp, checkbdry, "CATCRYPT ENCRYPTED MESSAGE"); /* --- Read the header chunk --- */ @@ -455,20 +458,25 @@ static int decrypt(int argc, char *argv[]) if (verb) { dstr_reset(&d); key_fulltag(sk, &d); - printf("INFO good signature from %s\n", d.buf); + printf("INFO good-signature %s\n", d.buf); } } else if (verb) - printf("INFO no signature packet\n"); + printf("INFO no-signature\n"); /* --- Now decrypt the main body --- */ if (!of || strcmp(of, "-") == 0) { ofp = stdout; - if (verb) printf("DATA\n"); - } else if ((ofp = fopen(of, "wb")) == 0) { - die(EXIT_FAILURE, "couldn't open file `%s' for output: %s", - ofp, strerror(errno)); + f |= f_buffer; } + if (!(f & f_buffer)) { + if ((ofp = fopen(of, "wb")) == 0) { + die(EXIT_FAILURE, "couldn't open file `%s' for output: %s", + ofp, strerror(errno)); + } + rfp = ofp; + } else if ((rfp = tmpfile()) == 0) + die(EXIT_FAILURE, "couldn't create temporary file: %s", strerror(errno)); seq = 0; dstr_ensure(&d, GC_CLASS(c)->blksz); @@ -484,32 +492,48 @@ static int decrypt(int argc, char *argv[]) seq++; chunk_read(e, &d, &b); if ((tag = buf_get(&b, GM_CLASS(m)->hashsz)) == 0) { - if (verb) printf("%sFAIL bad ciphertext chunk: no tag\n", - ofp == stdout ? "\n" : ""); + if (verb) printf("FAIL bad ciphertext chunk: no tag\n"); exit(EXIT_FAILURE); } GH_HASH(h, BCUR(&b), BLEFT(&b)); if (memcmp(tag, GH_DONE(h, 0), GM_CLASS(m)->hashsz) != 0) { if (verb) - printf("%sFAIL bad ciphertext chunk: authentication failure\n", - ofp == stdout ? "\n" : ""); + printf("FAIL bad ciphertext chunk: authentication failure\n"); exit(EXIT_FAILURE); } if (!BLEFT(&b)) break; GC_DECRYPT(c, BCUR(&b), BCUR(&b), BLEFT(&b)); - if (fwrite(BCUR(&b), 1, BLEFT(&b), ofp) != BLEFT(&b)) { - if (verb) printf("%sFAIL error writing output: %s\n", - ofp == stdout ? "\n" : "", strerror(errno)); + if (fwrite(BCUR(&b), 1, BLEFT(&b), rfp) != BLEFT(&b)) { + if (verb) printf("FAIL error writing output: %s\n", strerror(errno)); exit(EXIT_FAILURE); } } - if (fflush(ofp) || ferror(ofp)) { - if (verb) printf("%sFAIL error writing output: %s\n", - ofp == stdout ? "\n" : "", strerror(errno)); + if (fflush(rfp) || ferror(rfp)) { + if (verb) printf("FAIL error writing output: %s\n", strerror(errno)); exit(EXIT_FAILURE); } + if (f & f_buffer) { + if (!ofp && (ofp = fopen(of, "wb")) == 0) { + die(EXIT_FAILURE, "couldn't open file `%s' for output: %s", + of, strerror(errno)); + } + rewind(rfp); + dstr_reset(&d); + dstr_ensure(&d, 65536); + if (verb && ofp == stdout) printf("DATA\n"); + for (;;) { + n = fread(d.buf, 1, d.sz, rfp); + if (!n) break; + if (fwrite(d.buf, 1, n, ofp) < n) + die(EXIT_FAILURE, "error writing output: %s", strerror(errno)); + } + if (ferror(rfp) || fclose(rfp)) + die(EXIT_FAILURE, "error unbuffering output: %s", strerror(errno)); + } + if (ofp && (fflush(ofp) || ferror(ofp) || fclose(ofp))) + die(EXIT_FAILURE, "error writing output: %s", strerror(errno)); e->ops->decdone(e); if (verb && ofp != stdout) @@ -525,148 +549,7 @@ static int decrypt(int argc, char *argv[]) return (0); #undef f_bogus -} - -/*----- Test code ---------------------------------------------------------*/ - -static int encode(int argc, char *argv[]) -{ - const char *of = 0; - FILE *ofp = 0; - FILE *fp = 0; - const char *ef = "binary"; - const char *bd = "MESSAGE"; - int i; - size_t n; - char buf[4096]; - unsigned f = 0; - const encops *eo; - enc *e; - -#define f_bogus 1u - - for (;;) { - static const struct option opt[] = { - { "format", OPTF_ARGREQ, 0, 'f' }, - { "boundary", OPTF_ARGREQ, 0, 'b' }, - { "output", OPTF_ARGREQ, 0, 'o' }, - { 0, 0, 0, 0 } - }; - i = mdwopt(argc, argv, "f:b:o:", opt, 0, 0, 0); - if (i < 0) break; - switch (i) { - case 'f': ef = optarg; break; - case 'b': bd = optarg; break; - case 'o': of = optarg; break; - default: f |= f_bogus; break; - } - } - if (argc - optind > 1 || (f & f_bogus)) - die(EXIT_FAILURE, "Usage: encode [-OPTIONS] [FILE]"); - - if ((eo = getenc(ef)) == 0) - die(EXIT_FAILURE, "encoding `%s' not found", ef); - - if (optind == argc) - fp = stdin; - else if (strcmp(argv[optind], "-") == 0) { - fp = stdin; - optind++; - } else if ((fp = fopen(argv[optind], "rb")) == 0) { - die(EXIT_FAILURE, "couldn't open file `%s': %s", - argv[optind], strerror(errno)); - } else - optind++; - - if (!of || strcmp(of, "-") == 0) - ofp = stdout; - else if ((ofp = fopen(of, eo->wmode)) == 0) { - die(EXIT_FAILURE, "couldn't open file `%s' for output: %s", - ofp, strerror(errno)); - } - - e = initenc(eo, ofp, bd, 1); - - do { - n = fread(buf, 1, sizeof(buf), fp); - if (e->ops->write(e, buf, n)) - die(EXIT_FAILURE, "error writing output: %s", strerror(errno)); - } while (n == sizeof(buf)); - e->ops->encdone(e); - freeenc(e); - return (0); - -#undef f_bogus -} - -static int decode(int argc, char *argv[]) -{ - const char *of = 0; - FILE *ofp = 0; - FILE *fp = 0; - const char *ef = "binary"; - const char *bd = 0; - int i; - char buf[4096]; - unsigned f = 0; - const encops *eo; - enc *e; - -#define f_bogus 1u - - for (;;) { - static const struct option opt[] = { - { "format", OPTF_ARGREQ, 0, 'f' }, - { "boundary", OPTF_ARGREQ, 0, 'b' }, - { "output", OPTF_ARGREQ, 0, 'o' }, - { 0, 0, 0, 0 } - }; - i = mdwopt(argc, argv, "f:b:o:", opt, 0, 0, 0); - if (i < 0) break; - switch (i) { - case 'f': ef = optarg; break; - case 'b': bd = optarg; break; - case 'o': of = optarg; break; - default: f |= f_bogus; break; - } - } - if (argc - optind > 1 || (f & f_bogus)) - die(EXIT_FAILURE, "Usage: decode [-OPTIONS] [FILE]"); - - if ((eo = getenc(ef)) == 0) - die(EXIT_FAILURE, "encoding `%s' not found", ef); - - if (optind == argc) - fp = stdin; - else if (strcmp(argv[optind], "-") == 0) { - fp = stdin; - optind++; - } else if ((fp = fopen(argv[optind], eo->rmode)) == 0) { - die(EXIT_FAILURE, "couldn't open file `%s': %s", - argv[optind], strerror(errno)); - } else - optind++; - - if (!of || strcmp(of, "-") == 0) - ofp = stdout; - else if ((ofp = fopen(of, "wb")) == 0) { - die(EXIT_FAILURE, "couldn't open file `%s' for output: %s", - ofp, strerror(errno)); - } - - e = initenc(eo, fp, bd, 0); - - do { - if ((i = e->ops->read(e, buf, sizeof(buf))) < 0) - die(EXIT_FAILURE, "error reading input: %s", strerror(errno)); - if (fwrite(buf, 1, i, ofp) < i) - die(EXIT_FAILURE, "error writing output: %s", strerror(errno)); - } while (i == sizeof(buf)); - e->ops->decdone(e); - freeenc(e); - return (0); - -#undef f_bogus +#undef f_buffer } /*----- Main code ---------------------------------------------------------*/ @@ -699,24 +582,8 @@ static int cmd_help(int, char **); static cmd cmdtab[] = { { "help", cmd_help, "help [COMMAND...]" }, { "show", cmd_show, "show [ITEM...]" }, - { "encode", encode, - "encode [-f FORMAT] [-b LABEL] [-o OUTPUT] [FILE]", - "\ -Options:\n\ -\n\ --f, --format=FORMAT Encode to FORMAT.\n\ --b, --boundary=LABEL PEM boundary is LABEL.\n\ --o, --output=FILE Write output to FILE.\n\ -" }, - { "decode", decode, - "decode [-f FORMAT] [-b LABEL] [-o OUTPUT] [FILE]", - "\ -Options:\n\ -\n\ --f, --format=FORMAT Decode from FORMAT.\n\ --b, --boundary=LABEL PEM boundary is LABEL.\n\ --o, --output=FILE Write output to FILE.\n\ -" }, + CMD_ENCODE, + CMD_DECODE, { "encrypt", encrypt, "encrypt [-a] [-k TAG] [-s TAG] [-f FORMAT]\n\t\ [-o OUTPUT] [FILE]", "\ @@ -729,15 +596,16 @@ Options:\n\ -o, --output=FILE Write output to FILE.\n\ " }, { "decrypt", decrypt, - "decrypt [-aqv] [-f FORMAT] [-o OUTPUT] [FILE]", "\ + "decrypt [-abqv] [-f FORMAT] [-o OUTPUT] [FILE]", "\ Options:\n\ \n\ -a, --armour Same as `-f pem'.\n\ --v, --verbose Produce more verbose messages.\n\ --q, --quiet Produce fewer messages.\n\ +-b, --buffer Buffer output until we're sure we have it all.\n\ -f, --format=FORMAT Decode as FORMAT.\n\ -o, --output=FILE Write output to FILE.\n\ -" }, +-q, --quiet Produce fewer messages.\n\ +-v, --verbose Produce more verbose messages.\n\ +" }, /* ' emacs is confused */ { 0, 0, 0 } };