--- /dev/null
+/* -*-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 <errno.h>
+#include <stdio.h>
+
+#include <mLib/alloc.h>
+#include <mLib/base64.h>
+#include <mLib/dstr.h>
+#include <mLib/mdwopt.h>
+#include <mLib/report.h>
+#include <mLib/sub.h>
+
+#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 -------------------------------------------------*/