X-Git-Url: https://git.distorted.org.uk/u/mdw/catacomb/blobdiff_plain/ba6e6b64033b1f9de49feccb5c9cd438354481f7..0f00dc4c8eb47e67bc0f148c2dd109f73a451e0a:/progs/cc-enc.c?ds=sidebyside diff --git a/progs/cc-enc.c b/progs/cc-enc.c new file mode 100644 index 0000000..61d9805 --- /dev/null +++ b/progs/cc-enc.c @@ -0,0 +1,528 @@ +/* -*-c-*- + * + * 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 ------------------------------------------------------*/ + +#define _FILE_OFFSET_BITS 64 + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "cc.h" + +typedef int encbdryp(const char *, void *); + +/*----- Main code ---------------------------------------------------------*/ + +/* --- Binary --- */ + +static enc *bin_encinit(FILE *fp, const char *msg) + { enc *e = CREATE(enc); return (e); } +static enc *bin_decinit(FILE *fp, encbdryp *func, void *p) + { 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); +} + +int checkbdry(const char *b, void *p) { return (!p || strcmp(b, p) == 0); } + +static enc *pem_decinit(FILE *fp, encbdryp *func, void *p) +{ + 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 || (func && !func(buf + 6, p))) + 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 --- */ + +const encops enctab[] = { + { "binary", "rb", "wb", 1, 1, + bin_encinit, bin_decinit, + bin_read, bin_write, + bin_done, bin_done, + bin_destroy }, + { "pem", "r", "w", 3, 4, + 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 + * + * Returns: The encoder object. + * + * Use: Initializes an encoder. + */ + +enc *initenc(const encops *eo, FILE *fp, const char *msg) +{ + enc *e = eo->initenc(fp, msg); + e->ops = eo; + e->fp = fp; + return (e); +} + +/* --- @initdec@ --- * + * + * Arguments: @const encops *eo@ = operations (from @getenc@) + * @FILE *fp@ = file handle to attach + * @int (*func)(const char *, void *)@ = banner check function + * @void *p@ = argument for @func@ + * + * Returns: The encoder object. + * + * Use: Initializes an encoder. + */ + +enc *initdec(const encops *eo, FILE *fp, + int (*func)(const char *, void *), void *p) +{ + enc *e = eo->initdec(fp, func, p); + 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); } + +/*----- Encoding and decoding commands ------------------------------------*/ + +int cmd_encode(int argc, char *argv[]) +{ + const char *fn, *of = 0; + FILE *ofp = 0; + FILE *fp = 0; + const char *ef = "binary"; + const char *bd = "MESSAGE"; + fprogress ff; + int i; + size_t n; + char buf[4096]; + unsigned f = 0; + const encops *eo; + enc *e; + +#define f_bogus 1u +#define f_progress 2u + + for (;;) { + static const struct option opt[] = { + { "format", OPTF_ARGREQ, 0, 'f' }, + { "boundary", OPTF_ARGREQ, 0, 'b' }, + { "output", OPTF_ARGREQ, 0, 'o' }, + { "progress", 0, 0, 'p' }, + { 0, 0, 0, 0 } + }; + i = mdwopt(argc, argv, "f:b:o:p", 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; + case 'p': f |= f_progress; 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); + + fn = optind < argc ? argv[optind++] : "-"; + if (strcmp(fn, "-") == 0) + fp = stdin; + else if ((fp = fopen(fn, "rb")) == 0) { + die(EXIT_FAILURE, "couldn't open file `%s': %s", + fn, strerror(errno)); + } + + 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); + + if (f & f_progress) { + if (fprogress_init(&ff, fn, fp)) { + die(EXIT_FAILURE, "failed to initialize progress display: %s", + strerror(errno)); + } + } + + do { + n = fread(buf, 1, sizeof(buf), fp); + if (f & f_progress) fprogress_update(&ff, n); + if (e->ops->write(e, buf, n)) { + if (f & f_progress) fprogress_done(&ff); + die(EXIT_FAILURE, "error writing output: %s", strerror(errno)); + } + } while (n == sizeof(buf)); + if (f & f_progress) fprogress_done(&ff); + e->ops->encdone(e); + freeenc(e); + return (0); + +#undef f_bogus +#undef f_progress +} + +int cmd_decode(int argc, char *argv[]) +{ + const char *fn, *of = 0; + FILE *ofp = 0; + FILE *fp = 0; + const char *ef = "binary"; + const char *bd = 0; + fprogress ff; + int i; + char buf[4096]; + unsigned f = 0; + const encops *eo; + enc *e; + +#define f_bogus 1u +#define f_progress 2u + + for (;;) { + static const struct option opt[] = { + { "format", OPTF_ARGREQ, 0, 'f' }, + { "boundary", OPTF_ARGREQ, 0, 'b' }, + { "output", OPTF_ARGREQ, 0, 'o' }, + { "progress", 0, 0, 'p' }, + { 0, 0, 0, 0 } + }; + i = mdwopt(argc, argv, "f:b:o:p", 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; + case 'p': f |= f_progress; 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); + + fn = optind < argc ? argv[optind++] : "-"; + if (strcmp(fn, "-") == 0) + fp = stdin; + else if ((fp = fopen(fn, eo->rmode)) == 0) { + die(EXIT_FAILURE, "couldn't open file `%s': %s", + fn, strerror(errno)); + } + + 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 = initdec(eo, fp, checkbdry, (/*unconst*/ void *)bd); + + if (f & f_progress) { + if (fprogress_init(&ff, fn, fp)) { + die(EXIT_FAILURE, "failed to initialize progress display: %s", + strerror(errno)); + } + } + + do { + if ((i = e->ops->read(e, buf, sizeof(buf))) < 0) { + if (f & f_progress) fprogress_done(&ff); + die(EXIT_FAILURE, "error reading input: %s", strerror(errno)); + } + if (f & f_progress) + fprogress_update(&ff, i*e->ops->ncook/e->ops->nraw); + if (fwrite(buf, 1, i, ofp) < i) { + if (f & f_progress) fprogress_done(&ff); + die(EXIT_FAILURE, "error writing output: %s", strerror(errno)); + } + } while (i == sizeof(buf)); + e->ops->decdone(e); + if (f & f_progress) fprogress_done(&ff); + freeenc(e); + return (0); + +#undef f_bogus +#undef f_progress +} + +/*----- That's all, folks -------------------------------------------------*/