X-Git-Url: https://git.distorted.org.uk/u/mdw/catacomb/blobdiff_plain/72a6efc4d59568868fc6bbe05d343f777fc1a159..5c3f75ec49019d160806489824fc76652a2ef444:/cc-enc.c 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 -------------------------------------------------*/