From 5c3f75ec49019d160806489824fc76652a2ef444 Mon Sep 17 00:00:00 2001 From: mdw Date: Sat, 17 Apr 2004 09:58:37 +0000 Subject: [PATCH] Add simple public-key encryption program `catcrypt'. --- Makefile.m4 | 7 +- blkc.h | 14 +- catcrypt.c | 757 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ cbc-def.h | 32 ++- cc-enc.c | 330 +++++++++++++++++++++++++ cc-kem.c | 528 ++++++++++++++++++++++++++++++++++++++++ cc-sig.c | 651 +++++++++++++++++++++++++++++++++++++++++++++++++ cc.h | 216 ++++++++++++++++ cfb-def.h | 22 +- dsig.c | 599 ++------------------------------------------- ec-info.c | 22 +- ecb-def.h | 30 ++- g-ec.c | 4 +- group-stdops.c | 12 +- group.h | 8 +- 15 files changed, 2594 insertions(+), 638 deletions(-) create mode 100644 catcrypt.c create mode 100644 cc-enc.c create mode 100644 cc-kem.c create mode 100644 cc-sig.c create mode 100644 cc.h diff --git a/Makefile.m4 b/Makefile.m4 index bb976cc..066ef52 100644 --- a/Makefile.m4 +++ b/Makefile.m4 @@ -1,6 +1,6 @@ ## -*-m4-*- ## -## $Id: Makefile.m4,v 1.81 2004/04/08 16:17:32 mdw Exp $ +## $Id: Makefile.m4,v 1.82 2004/04/17 09:58:36 mdw Exp $ ## ## Makefile for Catacomb ## @@ -245,14 +245,15 @@ patsubst(MP_SOURCES, `\.c\>', `.lo') dsig.o keyutil.o rspit.o: \ ## --- Utility programs --- -bin_PROGRAMS = dsig key pixie rspit factorial hashsum mkphrase +bin_PROGRAMS = dsig key pixie rspit factorial hashsum mkphrase catcrypt bin_SCRIPTS = catacomb-config xpixie noinst_PROGRAMS = \ genprimes mptypes serpent-check bittest mpdump \ addsuffix(`gen_tables', `-mktab') LDADD = libcatacomb.la -dsig_SOURCES = dsig.c getdate.y getdate.h +dsig_SOURCES = dsig.c cc.h cc-sig.c getdate.y getdate.h +catcrypt_SOURCES = catcrypt.c cc.h cc-sig.c cc-kem.c cc-enc.c key_SOURCES = keyutil.c getdate.y getdate.h hashsum_SOURCES = hashsum.c rspit_SOURCES = rspit.c diff --git a/blkc.h b/blkc.h index dfbd4f7..77ecf0e 100644 --- a/blkc.h +++ b/blkc.h @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: blkc.h,v 1.7 2004/04/08 01:36:15 mdw Exp $ + * $Id: blkc.h,v 1.8 2004/04/17 09:58:36 mdw Exp $ * * Common definitions for block ciphers * @@ -101,6 +101,10 @@ BLKC_GLUE(BLKC_STEP_X_, BLKC_ENDIAN(PRE)) \ (PRE, w) +#define BLKC_ZERO(PRE, w) \ + BLKC_GLUE(BLKC_ZERO_, BLKC_TYPE(PRE)) \ + (PRE, w, BLKC_BITS(PRE)) + #define BLKC_SET(PRE, w, x) \ BLKC_GLUE(BLKC_SET_X_, BLKC_ENDIAN(PRE)) \ (PRE, w, x) @@ -154,6 +158,9 @@ BLKC_SKEL_X(PRE, BLKC_W(w); const BLKC_WX(wx);, \ *_w ^= *_wx; _w++; _wx++; ) \ +#define BLKC_ZERO_X(PRE, w, n) \ + BLKC_SKEL_X(PRE, BLKC_W(w);, *_w++ = 0;) + #define BLKC_STEP_X_B(PRE, w) do { \ unsigned _i = PRE##_BLKSZ / 4; BLKC_W(w); uint32 _x = 0; \ while (_i && !_x) { _i--; _w[_i] = _x = U32(_w[_i] + 1); } \ @@ -208,6 +215,7 @@ #define BLKC_XLOAD_GUTS(op, i) _w[i] ^= op(_p + 4 * i) #define BLKC_MOVE_GUTS(op, i) _w[i] = _wx[i] #define BLKC_XMOVE_GUTS(op, i) _w[i] ^= _wx[i] +#define BLKC_ZERO_GUTS(op, i) _w[i] = 0 #define BLKC_STORE_N(PRE, b, w, op, n) \ BLKC_GLUE(BLKC_SKEL_, n) \ @@ -230,6 +238,10 @@ BLKC_GLUE(BLKC_SKEL_, n) \ (PRE, BLKC_W(w); const BLKC_WX(wx);, op, BLKC_MOVE_GUTS) +#define BLKC_ZERO_N(PRE, w, n) \ + BLKC_GLUE(BLKC_SKEL_, n) \ + (PRE, BLKC_W(w); , op, BLKC_ZERO_GUTS) + #define BLKC_XMOVE_N(PRE, w, wx, n) \ BLKC_GLUE(BLKC_SKEL_, n) \ (PRE, BLKC_W(w); const BLKC_WX(wx);, op, BLKC_XMOVE_GUTS) diff --git a/catcrypt.c b/catcrypt.c new file mode 100644 index 0000000..1ffae77 --- /dev/null +++ b/catcrypt.c @@ -0,0 +1,757 @@ +/* -*-c-*- + * + * $Id: catcrypt.c,v 1.1 2004/04/17 09:58:36 mdw Exp $ + * + * Command-line encryption tool + * + * (c) 2004 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "buf.h" +#include "rand.h" +#include "noise.h" +#include "mprand.h" +#include "key.h" +#include "cc.h" + +/*----- Utilities ---------------------------------------------------------*/ + +/* --- @keyreport@ --- * + * + * Arguments: @const char *file@ = filename containing the error + * @int line@ = line number in file + * @const char *err@ = error text message + * @void *p@ = unimportant pointer + * + * Returns: --- + * + * Use: Reports errors during the opening of a key file. + */ + +static void keyreport(const char *file, int line, const char *err, void *p) +{ + moan("error in keyring `%s' at line `%s': %s", file, line, err); +} + +/*----- Static variables --------------------------------------------------*/ + +static const char *keyring = "keyring"; + +/*----- Data format -------------------------------------------------------*/ + +/* --- Overview --- * + * + * The encrypted message is divided into chunks, each preceded by a two-octet + * length. The chunks don't need to be large -- the idea is that we can + * stream the chunks in and out. + * + * The first chunk is a header. It contains the decryption key-id, and maybe + * 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. + * + * 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. + * + * Then come message chunks. Each one begins with a MAC over an implicit + * sequence number and the ciphertext. The final chunk's ciphertext is + * empty; no other chunk is empty. Thus can the correct end-of-file be + * discerned. + */ + +/*----- Chunk I/O ---------------------------------------------------------*/ + +static void chunk_write(enc *e, buf *b) +{ + octet l[2]; + size_t n = BLEN(b); + assert(n <= MASK16); + STORE16(l, n); + if (e->ops->write(e, l, 2) || + e->ops->write(e, BBASE(b), BLEN(b))) + die(EXIT_FAILURE, "error writing output: %s", strerror(errno)); +} + +static void chunk_read(enc *e, dstr *d, buf *b) +{ + octet l[2]; + size_t n; + + dstr_reset(d); + errno = 0; + if (e->ops->read(e, l, 2) != 2) + goto err; + n = LOAD16(l); + dstr_ensure(d, n); + if (e->ops->read(e, d->buf, n) != n) + goto err; + d->len = n; + buf_init(b, d->buf, d->len); + return; + +err: + if (!errno) die(EXIT_FAILURE, "unexpected end-of-file on input"); + else die(EXIT_FAILURE, "error reading input: %s", strerror(errno)); +} + +/*----- Encryption --------------------------------------------------------*/ + +static int encrypt(int argc, char *argv[]) +{ + const char *of = 0, *kn = "ccrypt"; + FILE *ofp = 0; + FILE *fp = 0; + const char *ef = "binary"; + const char *err; + int i; + size_t n; + dstr d = DSTR_INIT; + octet *tag, *ct; + buf b; + size_t seq; + char bb[16384]; + unsigned f = 0; + key_file kf; + key *k; + kem *km; + gcipher *cx, *c; + gmac *m; + ghash *h; + const encops *eo; + enc *e; + +#define f_bogus 1u + + for (;;) { + static const struct option opt[] = { + { "key", OPTF_ARGREQ, 0, 'k' }, + { "armour", 0, 0, 'a' }, + { "armor", 0, 0, 'a' }, + { "format", OPTF_ARGREQ, 0, 'f' }, + { "output", OPTF_ARGREQ, 0, 'o' }, + { 0, 0, 0, 0 } + }; + i = mdwopt(argc, argv, "k:af:o:", opt, 0, 0, 0); + if (i < 0) break; + switch (i) { + case 'k': kn = optarg; break; + case 'a': ef = "pem"; break; + case 'f': ef = optarg; break; + case 'o': of = optarg; break; + default: f |= f_bogus; break; + } + } + if (argc - optind > 1 || (f & f_bogus)) + die(EXIT_FAILURE, "Usage: encrypt [-options] [file]"); + + if (key_open(&kf, keyring, KOPEN_READ, keyreport, 0)) + die(EXIT_FAILURE, "can't open keyring `%s'", keyring); + if ((k = key_bytag(&kf, kn)) == 0) + die(EXIT_FAILURE, "key `%s' not found", kn); + + 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)); + } + + key_fulltag(k, &d); + e = initenc(eo, ofp, "CATCRYPT ENCRYPTED MESSAGE", 1); + km = getkem(k, "ccrypt", 0); + if ((err = km->ops->check(km)) != 0) + moan("key `%s' fails check: %s", d.buf, err); + + /* --- Build the header chunk --- */ + + dstr_reset(&d); + dstr_ensure(&d, 256); + buf_init(&b, d.buf, 256); + buf_putu32(&b, k->id); + assert(BOK(&b)); + chunk_write(e, &b); + + /* --- Build the KEM chunk --- */ + + dstr_reset(&d); + if (setupkem(km, &d, &cx, &c, &m)) + die(EXIT_FAILURE, "failed to encapsulate key"); + buf_init(&b, d.buf, d.len); + BSTEP(&b, d.len); + chunk_write(e, &b); + + /* --- Now do the main crypto --- */ + + assert(GC_CLASS(c)->blksz <= sizeof(bb)); + dstr_ensure(&d, sizeof(bb) + GM_CLASS(m)->hashsz); + seq = 0; + for (;;) { + h = GM_INIT(m); + STORE32(bb, seq); + GH_HASH(h, bb, 4); + seq++; + if (GC_CLASS(c)->blksz) { + GC_ENCRYPT(cx, 0, bb, GC_CLASS(c)->blksz); + GC_SETIV(c, bb); + } + n = fread(bb, 1, sizeof(bb), fp); + if (!n) break; + buf_init(&b, d.buf, d.sz); + tag = buf_get(&b, GM_CLASS(m)->hashsz); + ct = buf_get(&b, n); + assert(tag); assert(ct); + GC_ENCRYPT(c, bb, ct, n); + GH_HASH(h, ct, n); + GH_DONE(h, tag); + GH_DESTROY(h); + chunk_write(e, &b); + } + + /* --- Final terminator packet --- */ + + buf_init(&b, d.buf, d.sz); + tag = buf_get(&b, GM_CLASS(m)->hashsz); + assert(tag); + GH_DONE(h, tag); + GH_DESTROY(h); + chunk_write(e, &b); + + /* --- All done --- */ + + e->ops->encdone(e); + GM_DESTROY(m); + GC_DESTROY(c); + GC_DESTROY(cx); + freeenc(e); + freekem(km); + if (of) fclose(ofp); + key_close(&kf); + dstr_destroy(&d); + return (0); + +#undef f_bogus +} + +/*---- Decryption ---------------------------------------------------------*/ + +static int decrypt(int argc, char *argv[]) +{ + const char *of = 0; + FILE *ofp = 0; + FILE *fp = 0; + const char *ef = "binary"; + int i; + dstr d = DSTR_INIT; + buf b; + key_file kf; + size_t seq; + uint32 id; + key *k; + kem *km; + gcipher *cx; + gcipher *c; + ghash *h; + gmac *m; + octet *tag; + unsigned f = 0; + const encops *eo; + enc *e; + +#define f_bogus 1u + + for (;;) { + static const struct option opt[] = { + { "armour", 0, 0, 'a' }, + { "armor", 0, 0, 'a' }, + { "format", OPTF_ARGREQ, 0, 'f' }, + { "output", OPTF_ARGREQ, 0, 'o' }, + { 0, 0, 0, 0 } + }; + i = mdwopt(argc, argv, "af:o:", opt, 0, 0, 0); + if (i < 0) break; + switch (i) { + case 'a': ef = "pem"; break; + case 'f': ef = optarg; break; + case 'o': of = optarg; break; + default: f |= f_bogus; break; + } + } + if (argc - optind > 1 || (f & f_bogus)) + die(EXIT_FAILURE, "Usage: decrypt [-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 (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); + + /* --- Read the header chunk --- */ + + chunk_read(e, &d, &b); + if (buf_getu32(&b, &id)) + die(EXIT_FAILURE, "malformed header: missing keyid"); + if (BLEFT(&b)) + die(EXIT_FAILURE, "malformed header: junk at end"); + + /* --- Find the key --- */ + + if ((k = key_byid(&kf, id)) == 0) + die(EXIT_FAILURE, "key id %08lx not found", (unsigned long)id); + km = getkem(k, "ccrypt", 1); + + /* --- Read the KEM chunk --- */ + + chunk_read(e, &d, &b); + if (setupkem(km, &d, &cx, &c, &m)) + die(EXIT_FAILURE, "failed to decapsulate key"); + + /* --- Now decrypt the main body --- */ + + 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)); + } + + seq = 0; + dstr_ensure(&d, GC_CLASS(c)->blksz); + dstr_ensure(&d, 4); + for (;;) { + if (GC_CLASS(c)->blksz) { + GC_ENCRYPT(cx, 0, d.buf, GC_CLASS(c)->blksz); + GC_SETIV(c, d.buf); + } + h = GM_INIT(m); + STORE32(d.buf, seq); + GH_HASH(h, d.buf, 4); + seq++; + chunk_read(e, &d, &b); + if ((tag = buf_get(&b, GM_CLASS(m)->hashsz)) == 0) + die(EXIT_FAILURE, "bad ciphertext chunk: no tag"); + GH_HASH(h, BCUR(&b), BLEFT(&b)); + if (memcmp(tag, GH_DONE(h, 0), GM_CLASS(m)->hashsz) != 0) + die(EXIT_FAILURE, "bad ciphertext chunk: authentication failure"); + if (!BLEFT(&b)) + break; + GC_DECRYPT(c, BCUR(&b), BCUR(&b), BLEFT(&b)); + if (fwrite(BCUR(&b), 1, BLEFT(&b), ofp) != BLEFT(&b)) + die(EXIT_FAILURE, "error writing output: %s", strerror(errno)); + } + + if (fflush(ofp) || ferror(ofp)) + die(EXIT_FAILURE, "error writing output: %s", strerror(errno)); + + e->ops->decdone(e); + freeenc(e); + GC_DESTROY(c); + GC_DESTROY(cx); + GM_DESTROY(m); + freekem(km); + if (of) fclose(ofp); + key_close(&kf); + dstr_destroy(&d); + 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 +} + +/*----- Main code ---------------------------------------------------------*/ + +typedef struct cmd { + const char *name; + int (*func)(int /*argc*/, char */*argv*/[]); + const char *usage; + const char *help; +} cmd; + +static cmd cmdtab[] = { + { "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\ +" }, + { "encrypt", encrypt, + "encrypt [-a] [-k tag] [f format]] [-o output] [file]", + "\ +Options:\n\ +\n\ +-a, --armour Same as `-f pem'.\n\ +-f, --format=FORMAT Encode as FORMAT.\n\ +-k, --key=TAG Use public key named by TAG.\n\ +-o, --output=FILE Write output to FILE.\n\ +" }, + { "decrypt", decrypt, + "decrypt [-t] [-o output] [file]", "\ +Options:\n\ +\n\ +-t, --text Read PEM-encoded input.\n\ +-o, --output=FILE Write output to FILE.\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"); +} + +static void usage(FILE *fp) +{ + pquis(fp, "Usage: $ [-k keyring] command [args]\n"); +} + +static void help(FILE *fp, char **argv) +{ + cmd *c; + + 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->usage); + } +} + +/* --- @main@ --- * + * + * Arguments: @int argc@ = number of command line arguments + * @char *argv[]@ = vector of command line arguments + * + * Returns: Zero if successful, nonzero otherwise. + * + * Use: Signs or verifies signatures on lists of files. Useful for + * ensuring that a distribution is unmolested. + */ + +int main(int argc, char *argv[]) +{ + unsigned f = 0; + +#define f_bogus 1u + + /* --- Initialize the library --- */ + + ego(argv[0]); + sub_init(); + rand_noisesrc(RAND_GLOBAL, &noise_source); + rand_seed(RAND_GLOBAL, 160); + + /* --- Parse options --- */ + + for (;;) { + static struct option opts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "usage", 0, 0, 'u' }, + { "keyring", OPTF_ARGREQ, 0, 'k' }, + { 0, 0, 0, 0 } + }; + int i = mdwopt(argc, argv, "+hvu k:", opts, 0, 0, 0); + if (i < 0) + break; + switch (i) { + case 'h': + help(stdout, argv + optind); + exit(0); + break; + case 'v': + version(stdout); + exit(0); + break; + case 'u': + usage(stdout); + exit(0); + case 'k': + keyring = optarg; + break; + default: + f |= f_bogus; + break; + } + } + + argc -= optind; + argv += optind; + optind = 0; + if (f & f_bogus || argc < 1) { + usage(stderr); + exit(EXIT_FAILURE); + } + + /* --- Dispatch to the correct subcommand handler --- */ + + return (findcmd(argv[0])->func(argc, argv)); + +#undef f_bogus +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/cbc-def.h b/cbc-def.h index c0fc600..00695ae 100644 --- a/cbc-def.h +++ b/cbc-def.h @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: cbc-def.h,v 1.5 2004/04/08 01:36:15 mdw Exp $ + * $Id: cbc-def.h,v 1.6 2004/04/17 09:58:36 mdw Exp $ * * Definitions for cipher block chaining mode * @@ -179,8 +179,10 @@ void pre##_cbcencrypt(pre##_cbcctx *ctx, \ \ pre##_eblk(&ctx->ctx, ctx->iv, ctx->iv); \ BLKC_STORE(PRE, b, ctx->iv); \ - for (i = 0; i < sz; i++) \ - d[i] = b[i] ^ s[i]; \ + if (d) { \ + for (i = 0; i < sz; i++) \ + d[i] = b[i] ^ (s ? s[i] : 0); \ + } \ memmove(b, b + sz, PRE##_BLKSZ - sz); \ memcpy(b + PRE##_BLKSZ - sz, d, sz); \ BLKC_LOAD(PRE, ctx->iv, b); \ @@ -195,11 +197,15 @@ void pre##_cbcencrypt(pre##_cbcctx *ctx, \ */ \ \ while (sz >= 2 * PRE##_BLKSZ || sz == PRE##_BLKSZ) { \ - BLKC_XLOAD(PRE, ctx->iv, s); \ + if (s) { \ + BLKC_XLOAD(PRE, ctx->iv, s); \ + s += PRE##_BLKSZ; \ + } \ pre##_eblk(&ctx->ctx, ctx->iv, ctx->iv); \ - BLKC_STORE(PRE, d, ctx->iv); \ - s += PRE##_BLKSZ; \ - d += PRE##_BLKSZ; \ + if (d) { \ + BLKC_STORE(PRE, d, ctx->iv); \ + d += PRE##_BLKSZ; \ + } \ sz -= PRE##_BLKSZ; \ } \ \ @@ -224,7 +230,7 @@ void pre##_cbcencrypt(pre##_cbcctx *ctx, \ * block. \ */ \ \ - BLKC_XLOAD(PRE, ctx->iv, s); \ + if (s) BLKC_XLOAD(PRE, ctx->iv, s); \ pre##_eblk(&ctx->ctx, ctx->iv, ctx->iv); \ BLKC_STORE(PRE, b, ctx->iv); \ \ @@ -235,16 +241,16 @@ void pre##_cbcencrypt(pre##_cbcctx *ctx, \ * ciphertext block. \ */ \ \ - s += PRE##_BLKSZ; \ - d += PRE##_BLKSZ; \ + if (s) s += PRE##_BLKSZ; \ + if (d) d += PRE##_BLKSZ; \ for (i = 0; i < sz; i++) { \ register octet x = b[i]; \ - b[i] ^= s[i]; \ - d[i] = x; \ + if (s) b[i] ^= s[i]; \ + if (d) d[i] = x; \ } \ BLKC_LOAD(PRE, ctx->iv, b); \ pre##_eblk(&ctx->ctx, ctx->iv, ctx->iv); \ - BLKC_STORE(PRE, d - PRE##_BLKSZ, ctx->iv); \ + if (d) BLKC_STORE(PRE, d - PRE##_BLKSZ, ctx->iv); \ } \ \ /* --- Done --- */ \ diff --git a/cc-enc.c b/cc-enc.c new file mode 100644 index 0000000..f44bb33 --- /dev/null +++ b/cc-enc.c @@ -0,0 +1,330 @@ +/* -*-c-*- + * + * $Id: cc-enc.c,v 1.1 2004/04/17 09:58:37 mdw Exp $ + * + * Catcrypt data encoding + * + * (c) 2004 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include +#include +#include +#include +#include + +#include "cc.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- Binary --- */ + +static enc *bin_init(FILE *fp, const char *msg) + { enc *e = CREATE(enc); return (e); } + +static int bin_read(enc *e, void *p, size_t sz) +{ + size_t n; + + if (!sz) return (0); + n = fread(p, 1, sz, e->fp); + if (!n || ferror(e->fp)) return (-1); + return (n); +} + +static int bin_write(enc *e, const void *p, size_t sz) + { if (sz && fwrite(p, 1, sz, e->fp) < sz) return (-1); return (0); } + +static int bin_done(enc *e) { return (0); } + +static void bin_destroy(enc *e) { DESTROY(e); } + +/* --- PEM --- */ + +typedef struct pem_encctx { + enc e; + char *msg; + unsigned f; + base64_ctx b; + dstr d; + size_t n; +#define PEMF_NL 1u +#define PEMF_EOF 2u +} pem_encctx; + +static enc *pem_encinit(FILE *fp, const char *msg) +{ + pem_encctx *pe = CREATE(pem_encctx); + base64_init(&pe->b); + fprintf(fp, "-----BEGIN %s-----\n", msg); + pe->msg = xstrdup(msg); + dstr_create(&pe->d); + pe->n = 0; + pe->f = 0; + return (&pe->e); +} + +static enc *pem_decinit(FILE *fp, const char *msg) +{ + char buf[128]; + int i, d; + pem_encctx *pe; + int ch; + + /* --- Go until I find a newline and `-----' --- */ + +top: + d = 0; + for (;;) { + if ((ch = getc(fp)) == EOF) goto fail; + switch (ch) { + case '\n': d = 0; break; + case '-': if (d >= 0) { d++; if (d == 5) goto banner; }; break; + default: d = -1; break; + } + } + + /* --- See what the banner looks like --- */ + +banner: + i = d = 0; + for (;;) { + if ((ch = getc(fp)) == EOF) goto fail; + if (ch == '-') { d++; continue; } + if (ch == '\n') break; + if (i + d + 1 >= sizeof(buf)) goto top; + while (d) { buf[i++] = '-'; d--; } + buf[i++] = ch; + } + buf[i] = 0; + + /* --- Check we have the right framing --- */ + + if (d != 5) goto top; + if (strncmp(buf, "BEGIN ", 6) != 0 || + (msg && strcmp(buf + 6, msg) != 0)) + goto top; + + /* --- Ready --- */ + + pe = CREATE(pem_encctx); + base64_init(&pe->b); + pe->msg = xstrdup(buf + 6); + dstr_create(&pe->d); + pe->n = 0; + pe->f = PEMF_NL; + return (&pe->e); + + /* --- Failed --- */ + +fail: + die(EXIT_FAILURE, "initial encapsulation boundary not found"); + return (0); +} + +#define PEM_CHUNKSZ 4096 + +static int pem_read(enc *e, void *p, size_t sz) +{ + pem_encctx *pe = (pem_encctx *)e; + char buf[PEM_CHUNKSZ]; + char *pp = p; + int ch; + size_t n; + int rc = 0; + + for (;;) { + n = pe->d.len - pe->n; + if (n > sz) n = sz; + memcpy(pp, pe->d.buf + pe->n, n); + pe->n += n; + pp += n; + rc += n; + sz -= n; + if (!sz) break; + if (pe->f & PEMF_EOF) return (rc ? rc : -1); + dstr_reset(&pe->d); + n = 0; + for (;;) { + if ((ch = getc(pe->e.fp)) == EOF) return (-1); + if ((pe->f & PEMF_NL) && ch == '-') { + ungetc(ch, pe->e.fp); + pe->f |= PEMF_EOF; + break; + } + if (ch == '\n') { pe->f |= PEMF_NL; continue; } + pe->f &= ~PEMF_NL; + buf[n++] = ch; + if (n >= PEM_CHUNKSZ) break; + } + if (n) + base64_decode(&pe->b, buf, n, &pe->d); + if (pe->f & PEMF_EOF) + base64_decode(&pe->b, 0, 0, &pe->d); + pe->n = 0; + } + return (rc); +} + +static int pem_write(enc *e, const void *p, size_t sz) +{ + pem_encctx *pe = (pem_encctx *)e; + const char *pp = p; + size_t n; + + while (sz) { + n = PEM_CHUNKSZ; + if (n > sz) n = sz; + dstr_reset(&pe->d); + base64_encode(&pe->b, pp, n, &pe->d); + if (fwrite(pe->d.buf, 1, pe->d.len, pe->e.fp) < pe->d.len) + return (-1); + pp += n; + sz -= n; + } + return (0); +} + +static int pem_encdone(enc *e) +{ + pem_encctx *pe = (pem_encctx *)e; + dstr_reset(&pe->d); + base64_encode(&pe->b, 0, 0, &pe->d); + if (fwrite(pe->d.buf, 1, pe->d.len, pe->e.fp) < pe->d.len) + return (-1); + if (pe->b.lnlen) fputc('\n', pe->e.fp); + fprintf(pe->e.fp, "-----END %s-----\n", pe->msg); + return (0); +} + +static int pem_decdone(enc *e) +{ + pem_encctx *pe = (pem_encctx *)e; + char buf[128]; + int i, d; + int ch; + + for (d = 0; d < 5; d++) + if ((ch = getc(pe->e.fp)) != '-') goto fail; + i = d = 0; + for (;;) { + if ((ch = getc(pe->e.fp)) == EOF) goto fail; + if (ch == '-') { d++; continue; } + if (ch == '\n') break; + if (i + d + 1 >= sizeof(buf)) goto fail; + while (d) { buf[i++] = '-'; d--; } + buf[i++] = ch; + } + if (d != 5) goto fail; + buf[i] = 0; + if (strncmp(buf, "END ", 4) != 0 || strcmp(buf + 4, pe->msg) != 0) + goto fail; + return (0); + +fail: + die(EXIT_FAILURE, "final encapsulation boundary not found"); + return (-1); +} + +static void pem_destroy(enc *e) +{ + pem_encctx *pe = (pem_encctx *)e; + dstr_destroy(&pe->d); + xfree(pe->msg); + DESTROY(pe); +} + +/* --- Encoder table --- */ + +static const encops enctab[] = { + { "binary", "rb", "wb", + bin_init, bin_init, + bin_read, bin_write, + bin_done, bin_done, + bin_destroy }, + { "pem", "r", "w", + pem_encinit, pem_decinit, + pem_read, pem_write, + pem_encdone, pem_decdone, + pem_destroy }, + { 0 } +}; + +/* --- @getenc@ --- * + * + * Arguments: @const char *enc@ = name of wanted encoding + * + * Returns: Pointer to encoder operations. + * + * Use: Finds a named encoder or decoder. + */ + +const encops *getenc(const char *enc) +{ + const encops *eo; + + for (eo = enctab; eo->name; eo++) { + if (strcmp(eo->name, enc) == 0) + goto e_found; + } + die(EXIT_FAILURE, "couldn't find encoding `%s'", enc); +e_found: + return (eo); +} + +/* --- @initenc@ --- * + * + * Arguments: @const encops *eo@ = operations (from @getenc@) + * @FILE *fp@ = file handle to attach + * @const char *msg@ = banner message + * @int wantenc@ = nonzero if we want to encode + * + * Returns: The encoder object. + * + * Use: Initializes an encoder. + */ + +enc *initenc(const encops *eo, FILE *fp, const char *msg, int wantenc) +{ + enc *e = (wantenc ? eo->initenc : eo->initdec)(fp, msg); + e->ops = eo; + e->fp = fp; + return (e); +} + +/* --- @freeenc@ --- * + * + * Arguments: @enc *e@ = encoder object + * + * Returns: --- + * + * Use: Frees an encoder object. + */ + +void freeenc(enc *e) { e->ops->destroy(e); } + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/cc-kem.c b/cc-kem.c new file mode 100644 index 0000000..4f71d0d --- /dev/null +++ b/cc-kem.c @@ -0,0 +1,528 @@ +/* -*-c-*- + * + * $Id: cc-kem.c,v 1.1 2004/04/17 09:58:37 mdw Exp $ + * + * Catcrypt key-encapsulation + * + * (c) 2004 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include +#include +#include +#include + +#include "mprand.h" +#include "rand.h" + +#include "ec.h" +#include "ec-keys.h" +#include "dh.h" +#include "rsa.h" + +#include "rmd160.h" +#include "blowfish-cbc.h" + +#include "cc.h" + +/*----- Key encapsulation -------------------------------------------------*/ + +/* --- RSA --- */ + +typedef struct rsa_encctx { + kem k; + rsa_pubctx rp; +} rsa_encctx; + +static kem *rsa_encinit(key *k, void *kd) +{ + rsa_encctx *re = CREATE(rsa_encctx); + rsa_pubcreate(&re->rp, kd); + return (&re->k); +} + +static int rsa_encdoit(kem *k, dstr *d, ghash *h) +{ + rsa_encctx *re = (rsa_encctx *)k; + mp *x = mprand_range(MP_NEW, re->rp.rp->n, &rand_global, 0); + mp *y = rsa_pubop(&re->rp, MP_NEW, x); + size_t n = mp_octets(re->rp.rp->n); + dstr_ensure(d, n); + mp_storeb(x, d->buf, n); + GH_HASH(h, d->buf, n); + mp_storeb(y, d->buf, n); + d->len += n; + mp_drop(x); + mp_drop(y); + return (0); +} + +static const char *rsa_lengthcheck(mp *n) +{ + if (mp_bits(n) < 1020) return ("key too short"); + return (0); +} + +static const char *rsa_enccheck(kem *k) +{ + rsa_encctx *re = (rsa_encctx *)k; + const char *e; + if ((e = rsa_lengthcheck(re->rp.rp->n)) != 0) return (e); + return (0); +} + +static void rsa_encdestroy(kem *k) +{ + rsa_encctx *re = (rsa_encctx *)k; + rsa_pubdestroy(&re->rp); + DESTROY(re); +} + +static const kemops rsa_encops = { + rsa_pubfetch, sizeof(rsa_pub), + rsa_encinit, rsa_encdoit, rsa_enccheck, rsa_encdestroy +}; + +typedef struct rsa_decctx { + kem k; + rsa_privctx rp; +} rsa_decctx; + +static kem *rsa_decinit(key *k, void *kd) +{ + rsa_decctx *rd = CREATE(rsa_decctx); + rsa_privcreate(&rd->rp, kd, &rand_global); + return (&rd->k); +} + +static int rsa_decdoit(kem *k, dstr *d, ghash *h) +{ + rsa_decctx *rd = (rsa_decctx *)k; + mp *x = mp_loadb(MP_NEW, d->buf, d->len); + size_t n; + char *p; + + if (MP_CMP(x, >=, rd->rp.rp->n)) { + mp_drop(x); + return (-1); + } + n = mp_octets(rd->rp.rp->n); + p = xmalloc(n); + x = rsa_privop(&rd->rp, x, x); + mp_storeb(x, p, n); + GH_HASH(h, p, n); + mp_drop(x); + xfree(p); + return (0); +} + +static const char *rsa_deccheck(kem *k) +{ + rsa_decctx *rd = (rsa_decctx *)k; + const char *e; + if ((e = rsa_lengthcheck(rd->rp.rp->n)) != 0) return (e); + return (0); +} + +static void rsa_decdestroy(kem *k) +{ + rsa_decctx *rd = (rsa_decctx *)k; + rsa_privdestroy(&rd->rp); + DESTROY(rd); +} + +static const kemops rsa_decops = { + rsa_privfetch, sizeof(rsa_priv), + rsa_decinit, rsa_decdoit, rsa_deccheck, rsa_decdestroy +}; + +/* --- DH and EC --- */ + +typedef struct dh_encctx { + kem k; + group *g; + mp *x; + ge *y; +} dh_encctx; + +static dh_encctx *dh_doinit(key *k, const gprime_param *gp, mp *y) +{ + dh_encctx *de = CREATE(dh_encctx); + dstr t = DSTR_INIT; + + key_fulltag(k, &t); + if ((de->g = group_prime(gp)) == 0) + die(EXIT_FAILURE, "bad prime group in key `%s'", t.buf); + de->x = MP_NEW; + de->y = G_CREATE(de->g); + if (G_FROMINT(de->g, de->y, y)) + die(EXIT_FAILURE, "bad public key `%s'", t.buf); + dstr_destroy(&t); + return (de); +} + +static dh_encctx *ec_doinit(key *k, const char *cstr, const ec *y) +{ + dh_encctx *de = CREATE(dh_encctx); + ec_info ei; + const char *e; + dstr t = DSTR_INIT; + + key_fulltag(k, &t); + if ((e = ec_getinfo(&ei, cstr)) != 0 || + (de->g = group_ec(&ei)) == 0) + die(EXIT_FAILURE, "bad elliptic curve spec in key `%s': %s", t.buf, e); + de->x = MP_NEW; + de->y = G_CREATE(de->g); + if (G_FROMEC(de->g, de->y, y)) + die(EXIT_FAILURE, "bad public curve point `%s'", t.buf); + dstr_destroy(&t); + return (de); +} + +static kem *dh_encinit(key *k, void *kd) +{ + dh_pub *dp = kd; + dh_encctx *de = dh_doinit(k, &dp->dp, dp->y); + return (&de->k); +} + +static kem *ec_encinit(key *k, void *kd) +{ + ec_pub *ep = kd; + dh_encctx *de = ec_doinit(k, ep->cstr, &ep->p); + return (&de->k); +} + +static int dh_encdoit(kem *k, dstr *d, ghash *h) +{ + dh_encctx *de = (dh_encctx *)k; + mp *r = mprand_range(MP_NEW, de->g->r, &rand_global, 0); + ge *x = G_CREATE(de->g); + ge *y = G_CREATE(de->g); + size_t n = de->g->noctets; + buf b; + + G_EXP(de->g, x, de->g->g, r); + G_EXP(de->g, y, de->y, r); + dstr_ensure(d, n); + buf_init(&b, d->buf, n); + G_TORAW(de->g, &b, y); + GH_HASH(h, BBASE(&b), BLEN(&b)); + buf_init(&b, d->buf, n); + G_TORAW(de->g, &b, x); + GH_HASH(h, BBASE(&b), BLEN(&b)); + d->len += BLEN(&b); + mp_drop(r); + G_DESTROY(de->g, x); + G_DESTROY(de->g, y); + return (0); +} + +static const char *dh_enccheck(kem *k) +{ + dh_encctx *de = (dh_encctx *)k; + const char *e; + if ((e = G_CHECK(de->g, &rand_global)) != 0) + return (0); + if (group_check(de->g, de->y)) + return ("public key not in subgroup"); + return (0); +} + +static void dh_encdestroy(kem *k) +{ + dh_encctx *de = (dh_encctx *)k; + G_DESTROY(de->g, de->y); + mp_drop(de->x); + G_DESTROYGROUP(de->g); +} + +static const kemops dh_encops = { + dh_pubfetch, sizeof(dh_pub), + dh_encinit, dh_encdoit, dh_enccheck, dh_encdestroy +}; + +static const kemops ec_encops = { + ec_pubfetch, sizeof(ec_pub), + ec_encinit, dh_encdoit, dh_enccheck, dh_encdestroy +}; + +static kem *dh_decinit(key *k, void *kd) +{ + dh_priv *dp = kd; + dh_encctx *de = dh_doinit(k, &dp->dp, dp->y); + de->x = MP_COPY(dp->x); + return (&de->k); +} + +static kem *ec_decinit(key *k, void *kd) +{ + ec_priv *ep = kd; + dh_encctx *de = ec_doinit(k, ep->cstr, &ep->p); + de->x = MP_COPY(ep->x); + return (&de->k); +} + +static int dh_decdoit(kem *k, dstr *d, ghash *h) +{ + dh_encctx *de = (dh_encctx *)k; + ge *x = G_CREATE(de->g); + size_t n = de->g->noctets; + void *p = xmalloc(n); + buf b; + int rc = -1; + + buf_init(&b, d->buf, d->len); + if (G_FROMRAW(de->g, &b, x) || group_check(de->g, x)) + goto done; + G_EXP(de->g, x, x, de->x); + buf_init(&b, p, n); + G_TORAW(de->g, &b, x); + GH_HASH(h, BBASE(&b), BLEN(&b)); + GH_HASH(h, d->buf, d->len); + rc = 0; +done: + G_DESTROY(de->g, x); + xfree(p); + return (rc); +} + +static const kemops dh_decops = { + dh_privfetch, sizeof(dh_priv), + dh_decinit, dh_decdoit, dh_enccheck, dh_encdestroy +}; + +static const kemops ec_decops = { + ec_privfetch, sizeof(ec_priv), + ec_decinit, dh_decdoit, dh_enccheck, dh_encdestroy +}; + +/* --- The switch table --- */ + +static const struct kemtab { + const char *name; + const kemops *encops; + const kemops *decops; +} kemtab[] = { + { "rsa", &rsa_encops, &rsa_decops }, + { "dh", &dh_encops, &dh_decops }, + { "ec", &ec_encops, &ec_decops }, + { 0, 0, 0 } +}; + +/* --- @getkem@ --- * + * + * Arguments: @key *k@ = the key to load + * @const char *app@ = application name + * @int wantpriv@ = nonzero if we want to decrypt + * + * Returns: A key-encapsulating thing. + * + * Use: Loads a key. + */ + +kem *getkem(key *k, const char *app, int wantpriv) +{ + const char *kalg, *halg = 0, *calg = 0; + dstr d = DSTR_INIT; + dstr t = DSTR_INIT; + size_t n; + char *p = 0; + const char *q; + kem *kk; + const struct kemtab *kt; + const kemops *ko; + void *kd; + int e; + key_packdef *kp; + + /* --- Setup stuff --- */ + + key_fulltag(k, &t); + + /* --- Get the KEM name --- * + * + * Take the attribute if it's there; otherwise use the key type. + */ + + n = strlen(app); + if ((q = key_getattr(0, k, "kem")) != 0) { + dstr_puts(&d, q); + p = d.buf; + } else if (strncmp(k->type, app, n) == 0 && k->type[n] == '-') { + dstr_puts(&d, k->type); + p = d.buf + n + 1; + } else + die(EXIT_FAILURE, "no KEM for key `%s'", t.buf); + kalg = p; + + /* --- Grab the encryption scheme --- * + * + * Grab it from the KEM if it's there, but override it from the attribute. + */ + + if (p && (p = strchr(p, '/')) != 0) { + *p++ = 0; + calg = p; + } + if ((q = key_getattr(0, k, "cipher")) != 0) + calg = q; + + /* --- Grab the hash function --- */ + + if (p && (p = strchr(p, '/')) != 0) { + *p++ = 0; + halg = p; + } + if ((q = key_getattr(0, k, "hash")) != 0) + halg = q; + + /* --- Instantiate the KEM --- */ + + for (kt = kemtab; kt->name; kt++) { + if (strcmp(kt->name, kalg) == 0) + goto k_found; + } + die(EXIT_FAILURE, "key encapsulation mechanism `%s' not found in key `%s'", + kalg, t.buf); +k_found:; + ko = wantpriv ? kt->decops : kt->encops; + kd = xmalloc(ko->kdsz); + kp = key_fetchinit(ko->kf, 0, kd); + if ((e = key_fetch(kp, k)) != 0) + die(EXIT_FAILURE, "error fetching key `%s': %s", t.buf, key_strerror(e)); + kk = ko->init(k, kd); + kk->kp = kp; + kk->ops = ko; + kk->kd = kd; + + /* --- Set up the algorithms --- */ + + if (!halg) + kk->h = &rmd160; + else if ((kk->h = ghash_byname(halg)) == 0) { + die(EXIT_FAILURE, "hash algorithm `%s' not found in key `%s'", + halg, t.buf); + } + + if (!calg) + kk->c = &blowfish_cbc; + else if ((kk->c = gcipher_byname(calg)) == 0) { + die(EXIT_FAILURE, "encryption scheme `%s' not found in key `%s'", + calg, t.buf); + } + + dstr_reset(&d); + if ((q = key_getattr(0, k, "kdf")) == 0) { + dstr_putf(&d, "%s-mgf", kk->h->name); + q = d.buf; + } + if ((kk->cx = gcipher_byname(q)) == 0) { + die(EXIT_FAILURE, "encryption scheme (KDF) `%s' not found in key `%s'", + q, t.buf); + } + + dstr_reset(&d); + if ((q = key_getattr(0, k, "mac")) == 0) { + dstr_putf(&d, "%s-hmac", kk->h->name); + q = d.buf; + } + if ((kk->m = gmac_byname(q)) == 0) { + die(EXIT_FAILURE, + "message authentication code `%s' not found in key `%s'", + q, t.buf); + } + + /* --- Tidy up --- */ + + dstr_destroy(&d); + dstr_destroy(&t); + return (kk); +} + +/* --- @setupkem@ --- * + * + * Arguments: @kem *k@ = key-encapsulation thing + * @dstr *d@ = key-encapsulation data + * @gcipher **cx@ = key-expansion function (for IVs) + * @gcipher **c@ = where to put initialized encryption scheme + * @gmac **m@ = where to put initialized MAC + * + * Returns: Zero on success, nonzero on failure. + * + * Use: Initializes all the various symmetric things from a KEM. + */ + +int setupkem(kem *k, dstr *d, gcipher **cx, gcipher **c, gmac **m) +{ + octet *kd; + size_t n, cn, mn; + ghash *h; + int rc = 0; + + h = GH_INIT(k->h); + if (k->ops->doit(k, d, h)) + goto done; + n = keysz(GH_CLASS(h)->hashsz, k->cx->keysz); + if (!n) + goto done; + kd = GH_DONE(h, 0); + *cx = GC_INIT(k->cx, kd, n); + + cn = keysz(0, k->c->keysz); n = cn; + mn = keysz(0, k->m->keysz); if (mn > n) n = mn; + kd = xmalloc(n); + GC_ENCRYPT(*cx, 0, kd, cn); + *c = GC_INIT(k->c, kd, cn); + GC_ENCRYPT(*cx, 0, kd, mn); + *m = GM_KEY(k->m, kd, mn); + xfree(kd); + + rc = 0; +done: + GH_DESTROY(h); + return (rc); +} + +/* --- @freekem@ --- * + * + * Arguments: @kem *k@ = key-encapsulation thing + * + * Returns: --- + * + * Use: Frees up a key-encapsulation thing. + */ + +void freekem(kem *k) +{ + key_fetchdone(k->kp); + xfree(k->kd); + k->ops->destroy(k); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/cc-sig.c b/cc-sig.c new file mode 100644 index 0000000..2bfe7e9 --- /dev/null +++ b/cc-sig.c @@ -0,0 +1,651 @@ +/* -*-c-*- + * + * $Id: cc-sig.c,v 1.1 2004/04/17 09:58:37 mdw Exp $ + * + * Catcrypt signatures + * + * (c) 2004 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include + +#include "rand.h" +#include "sha.h" +#include "has160.h" + +#include "ec.h" +#include "ec-keys.h" +#include "dh.h" +#include "gdsa.h" +#include "gkcdsa.h" +#include "rsa.h" + +#include "cc.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- RSA PKCS1 --- */ + +typedef struct rsap1_sigctx { + sig s; + rsa_privctx rp; + pkcs1 p1; +} rsap1_sigctx; + +static sig *rsap1_siginit(key *k, void *kd, const gchash *hc) +{ + rsap1_sigctx *rs = CREATE(rsap1_sigctx); + rsa_privcreate(&rs->rp, kd, &rand_global); + rs->p1.r = &rand_global; + rs->p1.ep = hc->name; + rs->p1.epsz = strlen(hc->name) + 1; + rs->s.h = 0; + return (&rs->s); +} + +static int rsap1_sigdoit(sig *s, dstr *d) +{ + rsap1_sigctx *rs = (rsap1_sigctx *)s; + size_t n; + mp *m = rsa_sign(&rs->rp, MP_NEW, + GH_DONE(s->h, 0), GH_CLASS(s->h)->hashsz, + pkcs1_sigencode, &rs->p1); + if (!m) return (-1); + n = mp_octets(rs->rp.rp->n); dstr_ensure(d, n); mp_storeb(m, d->buf, n); + d->len += n; mp_drop(m); + return (0); +} + +static const char *rsa_lengthcheck(mp *n) +{ + if (mp_bits(n) < 1024) return ("key too short"); + return (0); +} + +static const char *rsap1_sigcheck(sig *s) +{ + rsap1_sigctx *rs = (rsap1_sigctx *)s; + const char *e; + if ((e = rsa_lengthcheck(rs->rp.rp->n)) != 0) return (e); + return (0); +} + +static void rsap1_sigdestroy(sig *s) +{ + rsap1_sigctx *rs = (rsap1_sigctx *)s; + rsa_privdestroy(&rs->rp); + DESTROY(rs); +} + +static const sigops rsap1_sig = { + rsa_privfetch, sizeof(rsa_priv), + rsap1_siginit, rsap1_sigdoit, rsap1_sigcheck, rsap1_sigdestroy +}; + +typedef struct rsap1_vrfctx { + sig s; + rsa_pubctx rp; + pkcs1 p1; +} rsap1_vrfctx; + +static sig *rsap1_vrfinit(key *k, void *kd, const gchash *hc) +{ + rsap1_vrfctx *rv = CREATE(rsap1_vrfctx); + rsa_pubcreate(&rv->rp, kd); + rv->p1.r = &rand_global; + rv->p1.ep = hc->name; + rv->p1.epsz = strlen(hc->name) + 1; + rv->s.h = 0; + return (&rv->s); +} + +static int rsap1_vrfdoit(sig *s, dstr *d) +{ + rsap1_vrfctx *rv = (rsap1_vrfctx *)s; + mp *m = mp_loadb(MP_NEW, d->buf, d->len); + int rc = rsa_verify(&rv->rp, m, + GH_DONE(s->h, 0), GH_CLASS(s->h)->hashsz, + 0, pkcs1_sigdecode, &rv->p1); + mp_drop(m); + return (rc); +} + +static const char *rsap1_vrfcheck(sig *s) +{ + rsap1_vrfctx *rv = (rsap1_vrfctx *)s; + const char *e; + if ((e = rsa_lengthcheck(rv->rp.rp->n)) != 0) return (e); + return (0); +} + +static void rsap1_vrfdestroy(sig *s) +{ + rsap1_vrfctx *rv = (rsap1_vrfctx *)s; + rsa_pubdestroy(&rv->rp); + DESTROY(rv); +} + +static const sigops rsap1_vrf = { + rsa_pubfetch, sizeof(rsa_pub), + rsap1_vrfinit, rsap1_vrfdoit, rsap1_vrfcheck, rsap1_vrfdestroy +}; + +/* --- RSA PSS --- */ + +static const gccipher *getmgf(key *k, const gchash *hc) +{ + dstr d = DSTR_INIT; + const gccipher *gc; + const char *mm; + + if ((mm = key_getattr(0, k, "mgf")) == 0) { + dstr_putf(&d, "%s-mgf", hc->name); + mm = d.buf; + } + if ((gc = gcipher_byname(mm)) == 0) + die(EXIT_FAILURE, "unknown encryption scheme `%s'", mm); + dstr_destroy(&d); + return (gc); +} + +typedef struct rsapss_sigctx { + sig s; + rsa_privctx rp; + pss p; +} rsapss_sigctx; + +static sig *rsapss_siginit(key *k, void *kd, const gchash *hc) +{ + rsapss_sigctx *rs = CREATE(rsapss_sigctx); + rsa_privcreate(&rs->rp, kd, &rand_global); + rs->p.r = &rand_global; + rs->p.cc = getmgf(k, hc); + rs->p.ch = hc; + rs->p.ssz = hc->hashsz; + rsa_privdestroy(&rs->rp); + return (&rs->s); +} + +static int rsapss_sigdoit(sig *s, dstr *d) +{ + rsapss_sigctx *rs = (rsapss_sigctx *)s; + size_t n; + mp *m = rsa_sign(&rs->rp, MP_NEW, + GH_DONE(s->h, 0), GH_CLASS(s->h)->hashsz, + pss_encode, &rs->p); + if (!m) return (-1); + n = mp_octets(rs->rp.rp->n); dstr_ensure(d, n); mp_storeb(m, d->buf, n); + d->len += n; mp_drop(m); + return (0); +} + +static const char *rsapss_sigcheck(sig *s) +{ + rsapss_sigctx *rs = (rsapss_sigctx *)s; + const char *e; + if ((e = rsa_lengthcheck(rs->rp.rp->n)) != 0) return (e); + return (0); +} + +static void rsapss_sigdestroy(sig *s) +{ + rsapss_sigctx *rs = (rsapss_sigctx *)s; + rsa_privdestroy(&rs->rp); + DESTROY(rs); +} + +static const sigops rsapss_sig = { + rsa_privfetch, sizeof(rsa_priv), + rsapss_siginit, rsapss_sigdoit, rsapss_sigcheck, rsapss_sigdestroy +}; + +typedef struct rsapss_vrfctx { + sig s; + rsa_pubctx rp; + pss p; +} rsapss_vrfctx; + +static sig *rsapss_vrfinit(key *k, void *kd, const gchash *hc) +{ + rsapss_vrfctx *rv = CREATE(rsapss_vrfctx); + rsa_pubcreate(&rv->rp, kd); + rv->p.r = &rand_global; + rv->p.cc = getmgf(k, hc); + rv->p.ch = hc; + rv->p.ssz = hc->hashsz; + return (&rv->s); +} + +static int rsapss_vrfdoit(sig *s, dstr *d) +{ + rsapss_vrfctx *rv = (rsapss_vrfctx *)s; + mp *m = mp_loadb(MP_NEW, d->buf, d->len); + int rc = rsa_verify(&rv->rp, m, + GH_DONE(s->h, 0), GH_CLASS(s->h)->hashsz, + 0, pss_decode, &rv->p); + mp_drop(m); + return (rc); +} + +static const char *rsapss_vrfcheck(sig *s) +{ + rsapss_vrfctx *rv = (rsapss_vrfctx *)s; + const char *e; + if ((e = rsa_lengthcheck(rv->rp.rp->n)) != 0) return (e); + return (0); +} + +static void rsapss_vrfdestroy(sig *s) +{ + rsapss_vrfctx *rv = (rsapss_vrfctx *)s; + rsa_pubdestroy(&rv->rp); + DESTROY(rv); +} + +static const sigops rsapss_vrf = { + rsa_pubfetch, sizeof(rsa_pub), + rsapss_vrfinit, rsapss_vrfdoit, rsapss_vrfcheck, rsapss_vrfdestroy +}; + +/* --- DSA and ECDSA --- */ + +typedef struct dsa_sigctx { + sig s; + gdsa g; +} dsa_sigctx; + +static void dsa_initcommon(dsa_sigctx *ds, const gchash *hc, + const char *ktag) +{ + ds->g.r = &rand_global; + ds->g.h = hc; + ds->g.u = MP_NEW; + ds->s.h = 0; +} + +static dsa_sigctx *dsa_doinit(key *k, const gprime_param *gp, + mp *y, const gchash *hc) +{ + dsa_sigctx *ds = CREATE(dsa_sigctx); + dstr t = DSTR_INIT; + + key_fulltag(k, &t); + if ((ds->g.g = group_prime(gp)) == 0) + die(EXIT_FAILURE, "bad prime group in key `%s'", t.buf); + ds->g.p = G_CREATE(ds->g.g); + if (G_FROMINT(ds->g.g, ds->g.p, y)) + die(EXIT_FAILURE, "bad public key in key `%s'", t.buf); + dsa_initcommon(ds, hc, t.buf); + dstr_destroy(&t); + return (ds); +} + +static dsa_sigctx *ecdsa_doinit(key *k, const char *cstr, + ec *y, const gchash *hc) +{ + dsa_sigctx *ds = CREATE(dsa_sigctx); + ec_info ei; + const char *e; + dstr t = DSTR_INIT; + + key_fulltag(k, &t); + if ((e = ec_getinfo(&ei, cstr)) != 0) + die(EXIT_FAILURE, "bad curve in key `%s': %s", t.buf, e); + ds->g.g = group_ec(&ei); + ds->g.p = G_CREATE(ds->g.g); + if (G_FROMEC(ds->g.g, ds->g.p, y)) + die(EXIT_FAILURE, "bad public key in key `%s'", t.buf); + dsa_initcommon(ds, hc, t.buf); + dstr_destroy(&t); + return (ds); +} + +static sig *dsa_siginit(key *k, void *kd, const gchash *hc) +{ + dh_priv *dp = kd; + dsa_sigctx *ds = dsa_doinit(k, &dp->dp, dp->y, hc); + ds->g.u = MP_COPY(dp->x); + return (&ds->s); +} + +static sig *ecdsa_siginit(key *k, void *kd, const gchash *hc) +{ + ec_priv *ep = kd; + dsa_sigctx *ds = ecdsa_doinit(k, ep->cstr, &ep->p, hc); + ds->g.u = MP_COPY(ep->x); + return (&ds->s); +} + +static int dsa_sigdoit(sig *s, dstr *d) +{ + dsa_sigctx *ds = (dsa_sigctx *)s; + gdsa_sig ss = GDSA_SIG_INIT; + size_t n = mp_octets(ds->g.g->r); + + gdsa_sign(&ds->g, &ss, GH_DONE(ds->s.h, 0), 0); + dstr_ensure(d, 2 * n); + mp_storeb(ss.r, d->buf, n); + mp_storeb(ss.s, d->buf + n, n); + d->len += 2 * n; + mp_drop(ss.r); mp_drop(ss.s); + return (0); +} + +static const char *dsa_sigcheck(sig *s) +{ + dsa_sigctx *ds = (dsa_sigctx *)s; + const char *e; + if ((e = G_CHECK(ds->g.g, &rand_global)) != 0) + return (0); + if (group_check(ds->g.g, ds->g.p)) + return ("public key not in subgroup"); + return (0); +} + +static void dsa_sigdestroy(sig *s) +{ + dsa_sigctx *ds = (dsa_sigctx *)s; + G_DESTROY(ds->g.g, ds->g.p); + mp_drop(ds->g.u); + G_DESTROYGROUP(ds->g.g); +} + +static const sigops dsa_sig = { + dh_privfetch, sizeof(dh_priv), + dsa_siginit, dsa_sigdoit, dsa_sigcheck, dsa_sigdestroy +}; + +static const sigops ecdsa_sig = { + ec_privfetch, sizeof(ec_priv), + ecdsa_siginit, dsa_sigdoit, dsa_sigcheck, dsa_sigdestroy +}; + +static sig *dsa_vrfinit(key *k, void *kd, const gchash *hc) +{ + dh_pub *dp = kd; + dsa_sigctx *ds = dsa_doinit(k, &dp->dp, dp->y, hc); + return (&ds->s); +} + +static sig *ecdsa_vrfinit(key *k, void *kd, const gchash *hc) +{ + ec_pub *ep = kd; + dsa_sigctx *ds = ecdsa_doinit(k, ep->cstr, &ep->p, hc); + return (&ds->s); +} + +static int dsa_vrfdoit(sig *s, dstr *d) +{ + dsa_sigctx *ds = (dsa_sigctx *)s; + gdsa_sig ss; + size_t n = d->len/2; + int rc; + + ss.r = mp_loadb(MP_NEW, d->buf, n); + ss.s = mp_loadb(MP_NEW, d->buf + n, d->len - n); + rc = gdsa_verify(&ds->g, &ss, GH_DONE(ds->s.h, 0)); + mp_drop(ss.r); mp_drop(ss.s); + return (rc); +} + +static const sigops dsa_vrf = { + dh_pubfetch, sizeof(dh_pub), + dsa_vrfinit, dsa_vrfdoit, dsa_sigcheck, dsa_sigdestroy +}; + +static const sigops ecdsa_vrf = { + ec_pubfetch, sizeof(ec_pub), + ecdsa_vrfinit, dsa_vrfdoit, dsa_sigcheck, dsa_sigdestroy +}; + +/* --- KCDSA and ECKCDSA --- */ + +static void kcdsa_privkey(dsa_sigctx *ds, mp *x) + { ds->g.u = mp_modinv(MP_NEW, x, ds->g.g->r); } + +static void kcdsa_sethash(dsa_sigctx *ds, const gchash *hc) + { ds->s.h = gkcdsa_beginhash(&ds->g); } + +static sig *kcdsa_siginit(key *k, void *kd, const gchash *hc) +{ + dh_priv *dp = kd; + dsa_sigctx *ds = dsa_doinit(k, &dp->dp, dp->y, hc); + kcdsa_privkey(ds, dp->x); + kcdsa_sethash(ds, hc); + return (&ds->s); +} + +static sig *eckcdsa_siginit(key *k, void *kd, const gchash *hc) +{ + ec_priv *ep = kd; + dsa_sigctx *ds = ecdsa_doinit(k, ep->cstr, &ep->p, hc); + kcdsa_privkey(ds, ep->x); + kcdsa_sethash(ds, hc); + return (&ds->s); +} + +static int kcdsa_sigdoit(sig *s, dstr *d) +{ + dsa_sigctx *ds = (dsa_sigctx *)s; + gkcdsa_sig ss = GKCDSA_SIG_INIT; + size_t hsz = ds->g.h->hashsz, n = mp_octets(ds->g.g->r); + + gkcdsa_sign(&ds->g, &ss, GH_DONE(ds->s.h, 0), 0); + dstr_ensure(d, hsz + n); + memcpy(d->buf, ss.r, hsz); + mp_storeb(ss.s, d->buf + hsz, n); + d->len += hsz + n; + xfree(ss.r); mp_drop(ss.s); + return (0); +} + +static const sigops kcdsa_sig = { + dh_privfetch, sizeof(dh_priv), + kcdsa_siginit, kcdsa_sigdoit, dsa_sigcheck, dsa_sigdestroy +}; + +static const sigops eckcdsa_sig = { + ec_privfetch, sizeof(ec_priv), + eckcdsa_siginit, kcdsa_sigdoit, dsa_sigcheck, dsa_sigdestroy +}; + +static sig *kcdsa_vrfinit(key *k, void *kd, const gchash *hc) +{ + dh_pub *dp = kd; + dsa_sigctx *ds = dsa_doinit(k, &dp->dp, dp->y, hc); + kcdsa_sethash(ds, hc); + return (&ds->s); +} + +static sig *eckcdsa_vrfinit(key *k, void *kd, const gchash *hc) +{ + ec_pub *ep = kd; + dsa_sigctx *ds = ecdsa_doinit(k, ep->cstr, &ep->p, hc); + kcdsa_sethash(ds, hc); + return (&ds->s); +} + +static int kcdsa_vrfdoit(sig *s, dstr *d) +{ + dsa_sigctx *ds = (dsa_sigctx *)s; + gkcdsa_sig ss; + size_t hsz = ds->g.h->hashsz, n = d->len - hsz; + int rc; + + if (d->len < hsz) + return (-1); + ss.r = (octet *)d->buf; + ss.s = mp_loadb(MP_NEW, d->buf + hsz, n); + rc = gkcdsa_verify(&ds->g, &ss, GH_DONE(ds->s.h, 0)); + mp_drop(ss.s); + return (rc); +} + +static const sigops kcdsa_vrf = { + dh_pubfetch, sizeof(dh_pub), + kcdsa_vrfinit, kcdsa_vrfdoit, dsa_sigcheck, dsa_sigdestroy +}; + +static const sigops eckcdsa_vrf = { + ec_pubfetch, sizeof(ec_pub), + eckcdsa_vrfinit, kcdsa_vrfdoit, dsa_sigcheck, dsa_sigdestroy +}; + +/* --- The switch table --- */ + +static const struct sigtab { + const char *name; + const sigops *signops; + const sigops *verifyops; + const gchash *ch; +} sigtab[] = { + { "rsapkcs1", &rsap1_sig, &rsap1_vrf, &sha }, + { "rsapss", &rsapss_sig, &rsapss_vrf, &sha }, + { "dsa", &dsa_sig, &dsa_vrf, &sha }, + { "ecdsa", &ecdsa_sig, &ecdsa_vrf, &sha }, + { "kcdsa", &kcdsa_sig, &kcdsa_vrf, &has160 }, + { "eckcdsa", &eckcdsa_sig, &eckcdsa_vrf, &has160 }, + { 0, 0, 0 } +}; + +/* --- @getsig@ --- * + * + * Arguments: @key *k@ = the key to load + * @const char *app@ = application name + * @int wantpriv@ = nonzero if we want to sign + * + * Returns: A signature-making thing. + * + * Use: Loads a key and starts hashing. + */ + +sig *getsig(key *k, const char *app, int wantpriv) +{ + const char *salg, *halg = 0; + dstr d = DSTR_INIT; + dstr t = DSTR_INIT; + char *p = 0; + const char *q; + sig *s; + size_t n; + const struct sigtab *st; + const sigops *so; + const gchash *ch; + void *kd; + int e; + key_packdef *kp; + + /* --- Setup stuff --- */ + + key_fulltag(k, &t); + + /* --- Get the signature algorithm --- * + * + * Take the attribute if it's there; otherwise use the key type. + */ + + n = strlen(app); + if ((q = key_getattr(0, k, "sig")) != 0) { + dstr_puts(&d, q); + p = d.buf; + } else if (strncmp(k->type, app, n) == 0 && k->type[n] == '-') { + dstr_puts(&d, k->type); + p = d.buf + n + 1; + } else + die(EXIT_FAILURE, "no signature algorithm for key `%s'", t.buf); + + /* --- Grab the hash algorithm --- * + * + * Grab it from the signature algorithm if it's there. But override that + * from the attribute. + */ + + salg = p; + if ((p = strchr(p, '/')) != 0) { + *p++ = 0; + halg = p; + } + if ((q = key_getattr(0, k, "hash")) != 0) + halg = q; + + /* --- Look up the algorithms in the table --- */ + + for (st = sigtab; st->name; st++) { + if (strcmp(st->name, salg) == 0) + goto s_found; + } + die(EXIT_FAILURE, "signature algorithm `%s' not found in key `%s'", + salg, t.buf); +s_found:; + if (!halg) + ch = st->ch; + else { + if ((ch = ghash_byname(halg)) == 0) { + die(EXIT_FAILURE, "hash algorithm `%s' not found in key `%s'", + halg, t.buf); + } + } + so = wantpriv ? st->signops : st->verifyops; + + /* --- Load the key --- */ + + kd = xmalloc(so->kdsz); + kp = key_fetchinit(so->kf, 0, kd); + if ((e = key_fetch(kp, k)) != 0) + die(EXIT_FAILURE, "error fetching key `%s': %s", t.buf, key_strerror(e)); + s = so->init(k, kd, ch); + if (!s->h) + s->h = GH_INIT(ch); + s->kp = kp; + s->ops = so; + s->kd = kd; + + /* --- Free stuff up --- */ + + dstr_destroy(&d); + dstr_destroy(&t); + return (s); +} + +/* --- @freesig@ --- * + * + * Arguments: @sig *s@ = signature-making thing + * + * Returns: --- + * + * Use: Frees up a signature-making thing + */ + +void freesig(sig *s) +{ + GH_DESTROY(s->h); + key_fetchdone(s->kp); + xfree(s->kd); + s->ops->destroy(s); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/cc.h b/cc.h new file mode 100644 index 0000000..7c2c092 --- /dev/null +++ b/cc.h @@ -0,0 +1,216 @@ +/* -*-c-*- + * + * $Id: cc.h,v 1.1 2004/04/17 09:58:37 mdw Exp $ + * + * Catcrypt common stuff + * + * (c) 2004 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#ifndef CATACOMB_CC_H +#define CATACOMB_CC_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include + +#include "key.h" +#include "gcipher.h" +#include "ghash.h" +#include "gmac.h" + +/*----- Data structures ---------------------------------------------------*/ + +/* --- Key encapsulation --- */ + +typedef struct kem { + const struct kemops *ops; + key_packdef *kp; + void *kd; + const gchash *h; + const gccipher *c, *cx; + const gcmac *m; +} kem; + +typedef struct kemops { + const key_fetchdef *kf; /* Key fetching structure */ + size_t kdsz; /* Size of the key-data structure */ + kem *(*init)(key */*k*/, void */*kd*/); + int (*doit)(kem */*k*/, dstr */*d*/, ghash */*h*/); + const char *(*check)(kem */*k*/); + void (*destroy)(kem */*k*/); +} kemops; + +/* --- Signing --- */ + +typedef struct sig { + const struct sigops *ops; + key_packdef *kp; + void *kd; + ghash *h; +} sig; + +typedef struct sigops { + const key_fetchdef *kf; /* Key fetching structure */ + size_t kdsz; /* Size of the key-data structure */ + sig *(*init)(key */*k*/, void */*kd*/, const gchash */*hc*/); + int (*doit)(sig */*s*/, dstr */*d*/); + const char *(*check)(sig */*s*/); + void (*destroy)(sig */*s*/); +} sigops; + +/* --- Data encoding --- */ + +typedef struct enc { + const struct encops *ops; + FILE *fp; +} enc; + +typedef struct encops { + const char *name; + const char *rmode, *wmode; + enc *(*initenc)(FILE */*fp*/, const char */*msg*/); + enc *(*initdec)(FILE */*fp*/, const char */*msg*/); + int (*read)(enc */*e*/, void */*p*/, size_t /*sz*/); + int (*write)(enc */*e*/, const void */*p*/, size_t /*sz*/); + int (*encdone)(enc */*e*/); + int (*decdone)(enc */*e*/); + void (*destroy)(enc */*e*/); +} encops; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @getkem@ --- * + * + * Arguments: @key *k@ = the key to load + * @const char *app@ = application name + * @int wantpriv@ = nonzero if we want to decrypt + * + * Returns: A key-encapsulating thing. + * + * Use: Loads a key. + */ + +extern kem *getkem(key */*k*/, const char */*app*/, int /*wantpriv*/); + +/* --- @setupkem@ --- * + * + * Arguments: @kem *k@ = key-encapsulation thing + * @dstr *d@ = key-encapsulation data + * @gcipher **cx@ = key-expansion function (for IVs) + * @gcipher **c@ = where to put initialized encryption scheme + * @gmac **m@ = where to put initialized MAC + * + * Returns: Zero for success, nonzero on faliure. + * + * Use: Initializes all the various symmetric things from a KEM. + */ + +extern int setupkem(kem */*k*/, dstr */*d*/, + gcipher **/*cx*/, gcipher **/*c*/, gmac **/*m*/); + +/* --- @freekem@ --- * + * + * Arguments: @kem *k@ = key-encapsulation thing + * + * Returns: --- + * + * Use: Frees up a key-encapsulation thing. + */ + +extern void freekem(kem */*k*/); + +/* --- @getsig@ --- * + * + * Arguments: @key *k@ = the key to load + * @const char *app@ = application name + * @int wantpriv@ = nonzero if we want to sign + * + * Returns: A signature-making thing. + * + * Use: Loads a key and starts hashing. + */ + +extern sig *getsig(key */*k*/, const char */*app*/, int /*wantpriv*/); + +/* --- @freesig@ --- * + * + * Arguments: @sig *s@ = signature-making thing + * + * Returns: --- + * + * Use: Frees up a signature-making thing + */ + +extern void freesig(sig */*s*/); + +/* --- @getenc@ --- * + * + * Arguments: @const char *enc@ = name of wanted encoding + * + * Returns: Pointer to encoder operations. + * + * Use: Finds a named encoder or decoder. + */ + +extern const encops *getenc(const char */*enc*/); + +/* --- @initenc@ --- * + * + * Arguments: @const encops *eo@ = operations (from @getenc@) + * @FILE *fp@ = file handle to attach + * @const char *msg@ = banner message + * @int wantenc@ = nonzero if we want to encode + * + * Returns: The encoder object. + * + * Use: Initializes an encoder. + */ + +extern enc *initenc(const encops */*eo*/, FILE */*fp*/, + const char */*msg*/, int /*wantenc*/); + +/* --- @freeenc@ --- * + * + * Arguments: @enc *e@ = encoder object + * + * Returns: --- + * + * Use: Frees an encoder object. + */ + +extern void freeenc(enc */*e*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/cfb-def.h b/cfb-def.h index 647aa26..8de17b3 100644 --- a/cfb-def.h +++ b/cfb-def.h @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: cfb-def.h,v 1.5 2004/04/08 01:36:15 mdw Exp $ + * $Id: cfb-def.h,v 1.6 2004/04/17 09:58:37 mdw Exp $ * * Definitions for ciphertext feedback mode * @@ -207,7 +207,9 @@ void pre##_cfbencrypt(pre##_cfbctx *ctx, \ \ while (off < PRE##_BLKSZ) { \ register octet x = *s++; \ - *d++ = ctx->iv[off++] ^= x; \ + ctx->iv[off] ^= x; \ + if (d) *d++ = ctx->iv[off]; \ + off++; \ sz--; \ } \ \ @@ -221,10 +223,14 @@ void pre##_cfbencrypt(pre##_cfbctx *ctx, \ pre##_eblk(&ctx->ctx, iv, iv); \ if (sz < PRE##_BLKSZ) \ break; \ - BLKC_XLOAD(PRE, iv, s); \ - BLKC_STORE(PRE, d, iv); \ - s += PRE##_BLKSZ; \ - d += PRE##_BLKSZ; \ + if (s) { \ + BLKC_XLOAD(PRE, iv, s); \ + s += PRE##_BLKSZ; \ + } \ + if (d) { \ + BLKC_STORE(PRE, d, iv); \ + d += PRE##_BLKSZ; \ + } \ sz -= PRE##_BLKSZ; \ } \ off = 0; \ @@ -237,7 +243,9 @@ void pre##_cfbencrypt(pre##_cfbctx *ctx, \ small: \ do { \ register octet x = *s++; \ - *d++ = ctx->iv[off++] ^= x; \ + ctx->iv[off] ^= x; \ + if (d) *d++ = ctx->iv[off]; \ + off++; \ sz--; \ } while (sz); \ } \ diff --git a/dsig.c b/dsig.c index 5a60444..2aeb9ef 100644 --- a/dsig.c +++ b/dsig.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: dsig.c,v 1.11 2004/04/08 16:27:49 mdw Exp $ + * $Id: dsig.c,v 1.12 2004/04/17 09:58:37 mdw Exp $ * * Verify signatures on distribuitions of files * @@ -45,580 +45,12 @@ #include #include "getdate.h" -#include "grand.h" +#include "rand.h" #include "ghash.h" #include "key.h" #include "key-data.h" #include "noise.h" - -#include "ec.h" -#include "ec-keys.h" -#include "dh.h" -#include "gdsa.h" -#include "gkcdsa.h" -#include "rsa.h" - -#include "sha.h" -#include "has160.h" - -/*----- Algorithm choice --------------------------------------------------*/ - -/* --- Relevant type operations --- */ - -typedef struct sig { - const struct sigops *ops; - key_packdef *kp; - ghash *h; -} sig; - -typedef struct sigops { - const key_fetchdef *kf; /* Key fetching structure */ - size_t kdsz; /* Size of the key-data structure */ - sig *(*init)(key */*k*/, void */*kd*/, const gchash */*hc*/); - int (*doit)(sig */*s*/, dstr */*d*/); - void (*destroy)(sig */*s*/); -} sigops; - -/* --- RSA PKCS1 --- */ - -typedef struct rsap1_sigctx { - sig s; - rsa_privctx rp; - pkcs1 p1; -} rsap1_sigctx; - -static sig *rsap1_siginit(key *k, void *kd, const gchash *hc) -{ - rsap1_sigctx *rs = CREATE(rsap1_sigctx); - rsa_privcreate(&rs->rp, kd, &rand_global); - rs->p1.r = &rand_global; - rs->p1.ep = hc->name; - rs->p1.epsz = strlen(hc->name) + 1; - rs->s.h = 0; - return (&rs->s); -} - -static int rsap1_sigdoit(sig *s, dstr *d) -{ - rsap1_sigctx *rs = (rsap1_sigctx *)s; - size_t n; - mp *m = rsa_sign(&rs->rp, MP_NEW, - GH_DONE(s->h, 0), GH_CLASS(s->h)->hashsz, - pkcs1_sigencode, &rs->p1); - if (!m) return (-1); - n = mp_octets(rs->rp.rp->n); dstr_ensure(d, n); mp_storeb(m, d->buf, n); - d->len += n; mp_drop(m); - return (0); -} - -static void rsap1_sigdestroy(sig *s) -{ - rsap1_sigctx *rs = (rsap1_sigctx *)s; - DESTROY(rs); -} - -static const sigops rsap1_sig = { - rsa_privfetch, sizeof(rsa_priv), - rsap1_siginit, rsap1_sigdoit, rsap1_sigdestroy -}; - -typedef struct rsap1_vrfctx { - sig s; - rsa_pubctx rp; - pkcs1 p1; -} rsap1_vrfctx; - -static sig *rsap1_vrfinit(key *k, void *kd, const gchash *hc) -{ - rsap1_vrfctx *rv = CREATE(rsap1_vrfctx); - rsa_pubcreate(&rv->rp, kd); - rv->p1.r = &rand_global; - rv->p1.ep = hc->name; - rv->p1.epsz = strlen(hc->name) + 1; - rv->s.h = 0; - return (&rv->s); -} - -static int rsap1_vrfdoit(sig *s, dstr *d) -{ - rsap1_vrfctx *rv = (rsap1_vrfctx *)s; - mp *m = mp_loadb(MP_NEW, d->buf, d->len); - int rc = rsa_verify(&rv->rp, m, - GH_DONE(s->h, 0), GH_CLASS(s->h)->hashsz, - 0, pkcs1_sigdecode, &rv->p1); - mp_drop(m); - return (rc); -} - -static void rsap1_vrfdestroy(sig *s) -{ - rsap1_vrfctx *rv = (rsap1_vrfctx *)s; - DESTROY(rv); -} - -static const sigops rsap1_vrf = { - rsa_pubfetch, sizeof(rsa_pub), - rsap1_vrfinit, rsap1_vrfdoit, rsap1_vrfdestroy -}; - -/* --- RSA PSS --- */ - -static const gccipher *getmgf(key *k, const gchash *hc) -{ - dstr d = DSTR_INIT; - const gccipher *gc; - const char *mm; - - if ((mm = key_getattr(0, k, "mgf-alg")) == 0) { - dstr_putf(&d, "%s-mgf", hc->name); - mm = d.buf; - } - if ((gc = gcipher_byname(mm)) == 0) - die(EXIT_FAILURE, "unknown encryption scheme `%s'", mm); - dstr_destroy(&d); - return (gc); -} - -typedef struct rsapss_sigctx { - sig s; - rsa_privctx rp; - pss p; -} rsapss_sigctx; - -static sig *rsapss_siginit(key *k, void *kd, const gchash *hc) -{ - rsapss_sigctx *rs = CREATE(rsapss_sigctx); - rsa_privcreate(&rs->rp, kd, &rand_global); - rs->p.r = &rand_global; - rs->p.cc = getmgf(k, hc); - rs->p.ch = hc; - rs->p.ssz = hc->hashsz; - return (&rs->s); -} - -static int rsapss_sigdoit(sig *s, dstr *d) -{ - rsapss_sigctx *rs = (rsapss_sigctx *)s; - size_t n; - mp *m = rsa_sign(&rs->rp, MP_NEW, - GH_DONE(s->h, 0), GH_CLASS(s->h)->hashsz, - pss_encode, &rs->p); - if (!m) return (-1); - n = mp_octets(rs->rp.rp->n); dstr_ensure(d, n); mp_storeb(m, d->buf, n); - d->len += n; mp_drop(m); - return (0); -} - -static void rsapss_sigdestroy(sig *s) -{ - rsapss_sigctx *rs = (rsapss_sigctx *)s; - DESTROY(rs); -} - -static const sigops rsapss_sig = { - rsa_privfetch, sizeof(rsa_priv), - rsapss_siginit, rsapss_sigdoit, rsapss_sigdestroy -}; - -typedef struct rsapss_vrfctx { - sig s; - rsa_pubctx rp; - pss p; -} rsapss_vrfctx; - -static sig *rsapss_vrfinit(key *k, void *kd, const gchash *hc) -{ - rsapss_vrfctx *rv = CREATE(rsapss_vrfctx); - rsa_pubcreate(&rv->rp, kd); - rv->p.r = &rand_global; - rv->p.cc = getmgf(k, hc); - rv->p.ch = hc; - rv->p.ssz = hc->hashsz; - return (&rv->s); -} - -static int rsapss_vrfdoit(sig *s, dstr *d) -{ - rsapss_vrfctx *rv = (rsapss_vrfctx *)s; - mp *m = mp_loadb(MP_NEW, d->buf, d->len); - int rc = rsa_verify(&rv->rp, m, - GH_DONE(s->h, 0), GH_CLASS(s->h)->hashsz, - 0, pss_decode, &rv->p); - mp_drop(m); - return (rc); -} - -static void rsapss_vrfdestroy(sig *s) -{ - rsapss_vrfctx *rv = (rsapss_vrfctx *)s; - DESTROY(rv); -} - -static const sigops rsapss_vrf = { - rsa_pubfetch, sizeof(rsa_pub), - rsapss_vrfinit, rsapss_vrfdoit, rsapss_vrfdestroy -}; - -/* --- DSA and ECDSA --- */ - -typedef struct dsa_sigctx { - sig s; - gdsa g; -} dsa_sigctx; - -static void dsa_initcommon(dsa_sigctx *ds, const gchash *hc, - const char *ktag) -{ - ds->g.r = &rand_global; - ds->g.h = hc; - ds->g.u = MP_NEW; - ds->s.h = 0; -} - -static dsa_sigctx *dsa_doinit(key *k, const gprime_param *gp, - mp *y, const gchash *hc) -{ - dsa_sigctx *ds = CREATE(dsa_sigctx); - dstr t = DSTR_INIT; - - key_fulltag(k, &t); - if ((ds->g.g = group_prime(gp)) == 0) - die(EXIT_FAILURE, "bad prime group in key `%s'", t.buf); - ds->g.p = G_CREATE(ds->g.g); - if (G_FROMINT(ds->g.g, ds->g.p, y)) - die(EXIT_FAILURE, "bad public key in key `%s'", t.buf); - dsa_initcommon(ds, hc, t.buf); - dstr_destroy(&t); - return (ds); -} - -static dsa_sigctx *ecdsa_doinit(key *k, const char *cstr, - ec *y, const gchash *hc) -{ - dsa_sigctx *ds = CREATE(dsa_sigctx); - ec_info ei; - const char *e; - dstr t = DSTR_INIT; - - key_fulltag(k, &t); - if ((e = ec_getinfo(&ei, cstr)) != 0) - die(EXIT_FAILURE, "bad curve in key `%s': %s", t.buf, e); - ds->g.g = group_ec(&ei); - ds->g.p = G_CREATE(ds->g.g); - if (G_FROMEC(ds->g.g, ds->g.p, y)) - die(EXIT_FAILURE, "bad public key in key `%s'", t.buf); - dsa_initcommon(ds, hc, t.buf); - dstr_destroy(&t); - return (ds); -} - -static sig *dsa_siginit(key *k, void *kd, const gchash *hc) -{ - dh_priv *dp = kd; - dsa_sigctx *ds = dsa_doinit(k, &dp->dp, dp->y, hc); - ds->g.u = MP_COPY(dp->x); - return (&ds->s); -} - -static sig *ecdsa_siginit(key *k, void *kd, const gchash *hc) -{ - ec_priv *ep = kd; - dsa_sigctx *ds = ecdsa_doinit(k, ep->cstr, &ep->p, hc); - ds->g.u = MP_COPY(ep->x); - return (&ds->s); -} - -static int dsa_sigdoit(sig *s, dstr *d) -{ - dsa_sigctx *ds = (dsa_sigctx *)s; - gdsa_sig ss = GDSA_SIG_INIT; - size_t n = mp_octets(ds->g.g->r); - - gdsa_sign(&ds->g, &ss, GH_DONE(ds->s.h, 0), 0); - dstr_ensure(d, 2 * n); - mp_storeb(ss.r, d->buf, n); - mp_storeb(ss.s, d->buf + n, n); - d->len += 2 * n; - mp_drop(ss.r); mp_drop(ss.s); - return (0); -} - -static void dsa_sigdestroy(sig *s) -{ - dsa_sigctx *ds = (dsa_sigctx *)s; - G_DESTROY(ds->g.g, ds->g.p); - mp_drop(ds->g.u); - G_DESTROYGROUP(ds->g.g); -} - -static const sigops dsa_sig = { - dh_privfetch, sizeof(dh_priv), - dsa_siginit, dsa_sigdoit, dsa_sigdestroy -}; - -static const sigops ecdsa_sig = { - ec_privfetch, sizeof(ec_priv), - ecdsa_siginit, dsa_sigdoit, dsa_sigdestroy -}; - -static sig *dsa_vrfinit(key *k, void *kd, const gchash *hc) -{ - dh_pub *dp = kd; - dsa_sigctx *ds = dsa_doinit(k, &dp->dp, dp->y, hc); - return (&ds->s); -} - -static sig *ecdsa_vrfinit(key *k, void *kd, const gchash *hc) -{ - ec_pub *ep = kd; - dsa_sigctx *ds = ecdsa_doinit(k, ep->cstr, &ep->p, hc); - return (&ds->s); -} - -static int dsa_vrfdoit(sig *s, dstr *d) -{ - dsa_sigctx *ds = (dsa_sigctx *)s; - gdsa_sig ss; - size_t n = d->len/2; - int rc; - - ss.r = mp_loadb(MP_NEW, d->buf, n); - ss.s = mp_loadb(MP_NEW, d->buf + n, d->len - n); - rc = gdsa_verify(&ds->g, &ss, GH_DONE(ds->s.h, 0)); - mp_drop(ss.r); mp_drop(ss.s); - return (rc); -} - -static const sigops dsa_vrf = { - dh_pubfetch, sizeof(dh_pub), - dsa_vrfinit, dsa_vrfdoit, dsa_sigdestroy -}; - -static const sigops ecdsa_vrf = { - ec_pubfetch, sizeof(ec_pub), - ecdsa_vrfinit, dsa_vrfdoit, dsa_sigdestroy -}; - -/* --- KCDSA and ECKCDSA --- */ - -static void kcdsa_privkey(dsa_sigctx *ds, mp *x) - { ds->g.u = mp_modinv(MP_NEW, x, ds->g.g->r); } - -static void kcdsa_sethash(dsa_sigctx *ds, const gchash *hc) - { ds->s.h = gkcdsa_beginhash(&ds->g); } - -static sig *kcdsa_siginit(key *k, void *kd, const gchash *hc) -{ - dh_priv *dp = kd; - dsa_sigctx *ds = dsa_doinit(k, &dp->dp, dp->y, hc); - kcdsa_privkey(ds, dp->x); - kcdsa_sethash(ds, hc); - return (&ds->s); -} - -static sig *eckcdsa_siginit(key *k, void *kd, const gchash *hc) -{ - ec_priv *ep = kd; - dsa_sigctx *ds = ecdsa_doinit(k, ep->cstr, &ep->p, hc); - kcdsa_privkey(ds, ep->x); - kcdsa_sethash(ds, hc); - return (&ds->s); -} - -static int kcdsa_sigdoit(sig *s, dstr *d) -{ - dsa_sigctx *ds = (dsa_sigctx *)s; - gkcdsa_sig ss = GKCDSA_SIG_INIT; - size_t hsz = ds->g.h->hashsz, n = mp_octets(ds->g.g->r); - - gkcdsa_sign(&ds->g, &ss, GH_DONE(ds->s.h, 0), 0); - dstr_ensure(d, hsz + n); - memcpy(d->buf, ss.r, hsz); - mp_storeb(ss.s, d->buf + hsz, n); - d->len += hsz + n; - xfree(ss.r); mp_drop(ss.s); - return (0); -} - -static const sigops kcdsa_sig = { - dh_privfetch, sizeof(dh_priv), - kcdsa_siginit, kcdsa_sigdoit, dsa_sigdestroy -}; - -static const sigops eckcdsa_sig = { - ec_privfetch, sizeof(ec_priv), - eckcdsa_siginit, kcdsa_sigdoit, dsa_sigdestroy -}; - -static sig *kcdsa_vrfinit(key *k, void *kd, const gchash *hc) -{ - dh_pub *dp = kd; - dsa_sigctx *ds = dsa_doinit(k, &dp->dp, dp->y, hc); - kcdsa_sethash(ds, hc); - return (&ds->s); -} - -static sig *eckcdsa_vrfinit(key *k, void *kd, const gchash *hc) -{ - ec_pub *ep = kd; - dsa_sigctx *ds = ecdsa_doinit(k, ep->cstr, &ep->p, hc); - kcdsa_sethash(ds, hc); - return (&ds->s); -} - -static int kcdsa_vrfdoit(sig *s, dstr *d) -{ - dsa_sigctx *ds = (dsa_sigctx *)s; - gkcdsa_sig ss; - size_t hsz = ds->g.h->hashsz, n = d->len - hsz; - int rc; - - if (d->len < hsz) - return (-1); - ss.r = (octet *)d->buf; - ss.s = mp_loadb(MP_NEW, d->buf + hsz, n); - rc = gkcdsa_verify(&ds->g, &ss, GH_DONE(ds->s.h, 0)); - mp_drop(ss.s); - return (rc); -} - -static const sigops kcdsa_vrf = { - dh_pubfetch, sizeof(dh_pub), - kcdsa_vrfinit, kcdsa_vrfdoit, dsa_sigdestroy -}; - -static const sigops eckcdsa_vrf = { - ec_pubfetch, sizeof(ec_pub), - eckcdsa_vrfinit, kcdsa_vrfdoit, dsa_sigdestroy -}; - -/* --- The switch table --- */ - -static const struct sigtab { - const char *name; - const sigops *signops; - const sigops *verifyops; - const gchash *ch; -} sigtab[] = { - { "rsapkcs1", &rsap1_sig, &rsap1_vrf, &sha }, - { "rsapss", &rsapss_sig, &rsapss_vrf, &sha }, - { "dsa", &dsa_sig, &dsa_vrf, &sha }, - { "ecdsa", &ecdsa_sig, &ecdsa_vrf, &sha }, - { "kcdsa", &kcdsa_sig, &kcdsa_vrf, &has160 }, - { "eckcdsa", &eckcdsa_sig, &eckcdsa_vrf, &has160 }, - { 0, 0, 0 } -}; - -/* --- @getsig@ --- * - * - * Arguments: @key *k@ = the key to load - * @int wantpriv@ = nonzero if we want to sign - * - * Returns: A signature-making thing. - * - * Use: Loads a key and starts hashing. - */ - -static sig *getsig(key *k, int wantpriv) -{ - const char *salg, *halg = 0; - dstr d = DSTR_INIT; - dstr t = DSTR_INIT; - char *p = 0; - const char *q; - sig *s; - const struct sigtab *st; - const sigops *so; - const gchash *ch; - void *kd; - int e; - key_packdef *kp; - - /* --- Setup stuff --- */ - - key_fulltag(k, &t); - - /* --- Get the signature algorithm --- * - * - * Take the attribute if it's there; otherwise use the key type. - */ - - if ((q = key_getattr(0, k, "sig-alg")) != 0) { - dstr_puts(&d, q); - p = d.buf; - } else if (strncmp(k->type, "dsig-", 5) == 0 && k->type[5]) { - dstr_puts(&d, k->type); - p = d.buf + 5; - } else - die(EXIT_FAILURE, "no signature algorithm for key `%s'", t.buf); - - /* --- Grab the hash algorithm --- * - * - * Grab it from the signature algorithm if it's there. But override that - * from the attribute. - */ - - salg = p; - if ((p = strchr(p, '-')) != 0) { - *p++ = 0; - halg = p; - } - if ((q = key_getattr(0, k, "hash-alg")) != 0) - halg = q; - - /* --- Look up the algorithms in the table --- */ - - for (st = sigtab; st->name; st++) { - if (strcmp(st->name, salg) == 0) - goto s_found; - } - die(EXIT_FAILURE, "signature algorithm `%s' not found in key `%s'", - salg, t.buf); -s_found:; - if (!halg) - ch = st->ch; - else { - if ((ch = ghash_byname(halg)) == 0) { - die(EXIT_FAILURE, "hash algorithm `%s' not found in key `%s'", - halg, t.buf); - } - } - so = wantpriv ? st->signops : st->verifyops; - - /* --- Load the key --- */ - - kd = xmalloc(so->kdsz); - kp = key_fetchinit(so->kf, 0, kd); - if ((e = key_fetch(kp, k)) != 0) - die(EXIT_FAILURE, "error fetching key `%s': %s", t.buf, key_strerror(e)); - s = so->init(k, kd, ch); - if (!s->h) - s->h = GH_INIT(ch); - s->kp = kp; - s->ops = so; - - /* --- Free stuff up --- */ - - dstr_destroy(&d); - dstr_destroy(&t); - return (s); -} - -/* --- @freesig@ --- * - * - * Arguments: @sig *s@ = signature-making thing - * - * Returns: --- - * - * Use: Frees up a signature-making thing - */ - -static void freesig(sig *s) -{ - GH_DESTROY(s->h); - key_fetchdone(s->kp); - s->ops->destroy(s); -} +#include "cc.h" /*----- Data formatting ---------------------------------------------------*/ @@ -1224,7 +656,6 @@ static void fhex(FILE *fp, const void *p, size_t sz) sz--; if (!sz) break; -/* putc(' ', fp); */ } } @@ -1246,6 +677,7 @@ static int sign(int argc, char *argv[]) const char *ifile = 0; const char *ofile = 0; const char *c = 0; + const char *err; FILE *ifp, *ofp; dstr d = DSTR_INIT; block b; @@ -1264,7 +696,7 @@ static int sign(int argc, char *argv[]) { "expire", OPTF_ARGREQ, 0, 'e' }, { 0, 0, 0, 0 } }; - int i = mdwopt(argc, argv, "+0vqb c: f:o: k:e:", opts, 0, 0, 0); + int i = mdwopt(argc, argv, "+0vqb" "c:" "f:o:" "k:e:", opts, 0, 0, 0); if (i < 0) break; switch (i) { @@ -1318,7 +750,12 @@ static int sign(int argc, char *argv[]) die(EXIT_FAILURE, "key `%s' expires: can't create nonexpiring signature", d.buf); } - s = getsig(k, 1); + s = getsig(k, "dsig", 1); + + /* --- Check the key --- */ + + if ((err = s->ops->check(s)) != 0) + moan("key `%s' fails check: %s", d.buf, err); /* --- Open files --- */ @@ -1455,6 +892,7 @@ static int verify(int argc, char *argv[]) key *k = 0; sig *s; dstr d = DSTR_INIT; + const char *err; FILE *fp; block b; int e; @@ -1546,14 +984,16 @@ static int verify(int argc, char *argv[]) /* --- Initialize the hash function and start reading hashed packets --- */ - s = getsig(k, 0); - if (!k) { if (verb) puts("FAIL no keyid packet found"); exit(EXIT_FAILURE); } + s = getsig(k, "dsig", 0); + if (verb && (err = s->ops->check(s)) != 0) + printf("WARN public key fails check: %s", err); + for (;;) { switch (e) { case T_COMMENT: @@ -1656,12 +1096,9 @@ typedef struct cmd { } cmd; static cmd cmdtab[] = { -/* { "manifest", manifest, */ -/* "manifest [-0] [-o output]" }, */ { "sign", sign, - "sign [-options]\n\ - [-0bqv] [-c comment] [-k tag] [-i keyid]\n\ - [-e expire] [-f file] [-o output]", "\ + "sign [-0bqv] [-c comment] [-k tag] [-e expire] [-f file] [-o output]", + "\ Options:\n\ \n\ -0, --null Read null-terminated filenames from stdin.\n\ diff --git a/ec-info.c b/ec-info.c index cf65584..4f852c2 100644 --- a/ec-info.c +++ b/ec-info.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: ec-info.c,v 1.6 2004/04/08 16:17:32 mdw Exp $ + * $Id: ec-info.c,v 1.7 2004/04/17 09:58:37 mdw Exp $ * * Elliptic curve information management * @@ -372,11 +372,6 @@ static const char *primecheck(const ec_info *ei, grand *gr) if (!pgen_primep(ei->r, gr)) return ("generator order not prime"); - /* --- Check %$0 < h \le 4$% --- */ - - if (MP_CMP(ei->h, <, MP_ONE) || MP_CMP(ei->h, >, MP_FOUR)) - return ("cofactor out of range"); - /* --- Check %$h = \lfloor (\sqrt{p} + 1)^2/r \rlfoor$% --- * * * This seems to work with the approximate-sqrt in the library, but might @@ -421,6 +416,11 @@ static const char *primecheck(const ec_info *ei, grand *gr) MP_DROP(x); if (i) return ("curve is weak"); + /* --- Check %$0 < h \le 4$% --- */ + + if (MP_CMP(ei->h, <, MP_ONE) || MP_CMP(ei->h, >, MP_FOUR)) + return ("cofactor out of range"); + /* --- Done --- */ return (0); @@ -459,11 +459,6 @@ static const char *bincheck(const ec_info *ei, grand *gr) if (!pgen_primep(ei->r, gr)) return ("generator order not prime"); - /* --- Check %$0 < h \le 4$% --- */ - - if (MP_CMP(ei->h, <, MP_ONE) || MP_CMP(ei->h, >, MP_FOUR)) - return ("cofactor out of range"); - /* --- Check %$h = \lfloor (\sqrt{2^m} + 1)^2/r \rlfoor$% --- * * * This seems to work with the approximate-sqrt in the library, but might @@ -505,6 +500,11 @@ static const char *bincheck(const ec_info *ei, grand *gr) MP_DROP(x); if (i) return ("curve is weak"); + /* --- Check %$0 < h \le 4$% --- */ + + if (MP_CMP(ei->h, <, MP_ONE) || MP_CMP(ei->h, >, MP_FOUR)) + return ("cofactor out of range"); + /* --- Done --- */ return (0); diff --git a/ecb-def.h b/ecb-def.h index 49a232b..7333310 100644 --- a/ecb-def.h +++ b/ecb-def.h @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: ecb-def.h,v 1.3 2004/04/08 01:36:15 mdw Exp $ + * $Id: ecb-def.h,v 1.4 2004/04/17 09:58:37 mdw Exp $ * * Definitions electronic code book mode * @@ -148,11 +148,17 @@ void pre##_ecbencrypt(pre##_ecbctx *ctx, \ \ while (sz >= 2 * PRE##_BLKSZ || sz == PRE##_BLKSZ) { \ uint32 x[PRE##_BLKSZ / 4]; \ - BLKC_LOAD(PRE, x, s); \ + if (!s) \ + BLKC_ZERO(PRE, x); \ + else { \ + BLKC_LOAD(PRE, x, s); \ + s += PRE##_BLKSZ; \ + } \ pre##_eblk(&ctx->ctx, x, x); \ - BLKC_STORE(PRE, d, x); \ - s += PRE##_BLKSZ; \ - d += PRE##_BLKSZ; \ + if (d) { \ + BLKC_STORE(PRE, d, x); \ + d += PRE##_BLKSZ; \ + } \ sz -= PRE##_BLKSZ; \ } \ \ @@ -177,7 +183,12 @@ void pre##_ecbencrypt(pre##_ecbctx *ctx, \ * out yet, because I've not read the partial plaintext block. \ */ \ \ - BLKC_LOAD(PRE, x, s); \ + if (!s) \ + BLKC_ZERO(PRE, x); \ + else { \ + BLKC_LOAD(PRE, x, s); \ + s += PRE##_BLKSZ; \ + } \ pre##_eblk(&ctx->ctx, x, x); \ BLKC_STORE(PRE, b, x); \ \ @@ -188,16 +199,15 @@ void pre##_ecbencrypt(pre##_ecbctx *ctx, \ * ciphertext block. \ */ \ \ - s += PRE##_BLKSZ; \ - d += PRE##_BLKSZ; \ + if (d) d += PRE##_BLKSZ; \ for (i = 0; i < sz; i++) { \ register octet y = b[i]; \ b[i] = s[i]; \ - d[i] = y; \ + if (d) d[i] = y; \ } \ BLKC_LOAD(PRE, x, b); \ pre##_eblk(&ctx->ctx, x, x); \ - BLKC_STORE(PRE, d - PRE##_BLKSZ, x); \ + if (d) BLKC_STORE(PRE, d - PRE##_BLKSZ, x); \ } \ \ /* --- Done --- */ \ diff --git a/g-ec.c b/g-ec.c index 1809e6c..32220ff 100644 --- a/g-ec.c +++ b/g-ec.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: g-ec.c,v 1.4 2004/04/08 01:36:15 mdw Exp $ + * $Id: g-ec.c,v 1.5 2004/04/17 09:58:37 mdw Exp $ * * Abstraction for elliptic curve groups * @@ -150,7 +150,7 @@ static int gfromint(group *gg, ec *d, mp *x) { static int gtoec(group *gg, ec *d, ec *x) { gctx *g = (gctx *)gg; EC_OUT(g->ei.c, d, x); return (0); } -static int gfromec(group *gg, ec *d, ec *x) { +static int gfromec(group *gg, ec *d, const ec *x) { gctx *g = (gctx *)gg; ec t = EC_INIT; int rc; EC_IN(g->ei.c, &t, x); rc = EC_CHECK(g->ei.c, &t); if (!rc) EC_COPY(d, &t); EC_DESTROY(&t); return (rc); diff --git a/group-stdops.c b/group-stdops.c index faa34f2..ec30b4d 100644 --- a/group-stdops.c +++ b/group-stdops.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: group-stdops.c,v 1.2 2004/04/08 01:36:15 mdw Exp $ + * $Id: group-stdops.c,v 1.3 2004/04/17 09:58:37 mdw Exp $ * * Standard group operations * @@ -42,7 +42,7 @@ * Returns: Zero on success, nonzero for failure. * * Use: Checks that @x@ is a valid group element. This may take a - * while, since it checks that %$x^h \ne 1$% and %$x^r = 1$%. + * while, since it checks that %$x \ne 1$% and %$x^r = 1$%. */ int group_check(group *g, ge *x) @@ -50,8 +50,8 @@ int group_check(group *g, ge *x) ge *d = G_CREATE(g); int rc; - G_EXP(g, d, x, g->h); rc = !G_IDENTP(g, d); - if (rc) { G_EXP(g, d, x, g->r); rc = G_IDENTP(g, d); } + G_EXP(g, d, x, g->r); + rc = (G_IDENTP(g, d) && !G_IDENTP(g, x)); G_DESTROY(g, d); if (!rc) return (-1); return (0); @@ -137,7 +137,7 @@ int group_stdtoec(group *g, ec *d, ge *x) { return (-1); } * * Arguments: @group *g@ = abstract group * @ge *d@ = destination pointer - * @ec *p@ = elliptic curve point + * @const ec *p@ = elliptic curve point * * Returns: Zero for success, @-1@ on failure. * @@ -145,7 +145,7 @@ int group_stdtoec(group *g, ec *d, ge *x) { return (-1); } * coordinate. */ -int group_stdfromec(group *g, ge *d, ec *p) +int group_stdfromec(group *g, ge *d, const ec *p) { if (EC_ATINF(p)) return (-1); return (G_FROMINT(g, d, p->x)); } /* --- @group_stdcheck@ --- * diff --git a/group.h b/group.h index 9211a58..9578f33 100644 --- a/group.h +++ b/group.h @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: group.h,v 1.4 2004/04/08 01:36:15 mdw Exp $ + * $Id: group.h,v 1.5 2004/04/17 09:58:37 mdw Exp $ * * General cyclic group abstraction * @@ -122,7 +122,7 @@ typedef struct group_ops { mp *(*toint)(group */*g*/, mp */*d*/, ge */*x*/); int (*fromint)(group */*g*/, ge */*d*/, mp */*x*/); int (*toec)(group */*g*/, ec */*d*/, ge */*x*/); - int (*fromec)(group */*g*/, ge */*d*/, ec */*p*/); + int (*fromec)(group */*g*/, ge */*d*/, const ec */*p*/); int (*tobuf)(group */*h*/, buf */*b*/, ge */*x*/); int (*frombuf)(group */*h*/, buf */*b*/, ge */*d*/); int (*toraw)(group */*h*/, buf */*b*/, ge */*x*/); @@ -305,7 +305,7 @@ extern int group_stdtoec(group */*g*/, ec */*d*/, ge */*x*/); * * Arguments: @group *g@ = abstract group * @ge *d@ = destination pointer - * @ec *p@ = elliptic curve point + * @const ec *p@ = elliptic curve point * * Returns: Zero for success, @-1@ on failure. * @@ -313,7 +313,7 @@ extern int group_stdtoec(group */*g*/, ec */*d*/, ge */*x*/); * coordinate. */ -extern int group_stdfromec(group */*g*/, ge */*d*/, ec */*p*/); +extern int group_stdfromec(group */*g*/, ge */*d*/, const ec */*p*/); /*----- Prime field subgroups ---------------------------------------------*/ -- 2.11.0