5 * Catcrypt data encoding
7 * (c) 2004 Straylight/Edgeware
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of Catacomb.
14 * Catacomb is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Library General Public License as
16 * published by the Free Software Foundation; either version 2 of the
17 * License, or (at your option) any later version.
19 * Catacomb is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
24 * You should have received a copy of the GNU Library General Public
25 * License along with Catacomb; if not, write to the Free
26 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
30 /*----- Header files ------------------------------------------------------*/
35 #include <mLib/alloc.h>
36 #include <mLib/base64.h>
37 #include <mLib/dstr.h>
38 #include <mLib/mdwopt.h>
39 #include <mLib/report.h>
44 typedef int encbdryp(const char *, void *);
46 /*----- Main code ---------------------------------------------------------*/
50 static enc *bin_encinit(FILE *fp, const char *msg)
51 { enc *e = CREATE(enc); return (e); }
52 static enc *bin_decinit(FILE *fp, encbdryp *func, void *p)
53 { enc *e = CREATE(enc); return (e); }
55 static int bin_read(enc *e, void *p, size_t sz)
60 n = fread(p, 1, sz, e->fp);
61 if (!n || ferror(e->fp)) return (-1);
65 static int bin_write(enc *e, const void *p, size_t sz)
66 { if (sz && fwrite(p, 1, sz, e->fp) < sz) return (-1); return (0); }
68 static int bin_done(enc *e) { return (0); }
70 static void bin_destroy(enc *e) { DESTROY(e); }
74 typedef struct pem_encctx {
85 static enc *pem_encinit(FILE *fp, const char *msg)
87 pem_encctx *pe = CREATE(pem_encctx);
89 fprintf(fp, "-----BEGIN %s-----\n", msg);
90 pe->msg = xstrdup(msg);
97 int checkbdry(const char *b, void *p) { return (!p || strcmp(b, p) == 0); }
99 static enc *pem_decinit(FILE *fp, encbdryp *func, void *p)
106 /* --- Go until I find a newline and `-----' --- */
111 if ((ch = getc(fp)) == EOF) goto fail;
113 case '\n': d = 0; break;
114 case '-': if (d >= 0) { d++; if (d == 5) goto banner; }; break;
115 default: d = -1; break;
119 /* --- See what the banner looks like --- */
124 if ((ch = getc(fp)) == EOF) goto fail;
125 if (ch == '-') { d++; continue; }
126 if (ch == '\n') break;
127 if (i + d + 1 >= sizeof(buf)) goto top;
128 while (d) { buf[i++] = '-'; d--; }
133 /* --- Check we have the right framing --- */
135 if (d != 5) goto top;
136 if (strncmp(buf, "BEGIN ", 6) != 0 || (func && !func(buf + 6, p)))
141 pe = CREATE(pem_encctx);
143 pe->msg = xstrdup(buf + 6);
152 die(EXIT_FAILURE, "initial encapsulation boundary not found");
156 #define PEM_CHUNKSZ 4096
158 static int pem_read(enc *e, void *p, size_t sz)
160 pem_encctx *pe = (pem_encctx *)e;
161 char buf[PEM_CHUNKSZ];
168 n = pe->d.len - pe->n;
170 memcpy(pp, pe->d.buf + pe->n, n);
176 if (pe->f & PEMF_EOF) return (rc ? rc : -1);
180 if ((ch = getc(pe->e.fp)) == EOF) return (-1);
181 if ((pe->f & PEMF_NL) && ch == '-') {
182 ungetc(ch, pe->e.fp);
186 if (ch == '\n') { pe->f |= PEMF_NL; continue; }
189 if (n >= PEM_CHUNKSZ) break;
192 base64_decode(&pe->b, buf, n, &pe->d);
193 if (pe->f & PEMF_EOF)
194 base64_decode(&pe->b, 0, 0, &pe->d);
200 static int pem_write(enc *e, const void *p, size_t sz)
202 pem_encctx *pe = (pem_encctx *)e;
210 base64_encode(&pe->b, pp, n, &pe->d);
211 if (fwrite(pe->d.buf, 1, pe->d.len, pe->e.fp) < pe->d.len)
219 static int pem_encdone(enc *e)
221 pem_encctx *pe = (pem_encctx *)e;
223 base64_encode(&pe->b, 0, 0, &pe->d);
224 if (fwrite(pe->d.buf, 1, pe->d.len, pe->e.fp) < pe->d.len)
226 if (pe->b.lnlen) fputc('\n', pe->e.fp);
227 fprintf(pe->e.fp, "-----END %s-----\n", pe->msg);
231 static int pem_decdone(enc *e)
233 pem_encctx *pe = (pem_encctx *)e;
238 for (d = 0; d < 5; d++)
239 if ((ch = getc(pe->e.fp)) != '-') goto fail;
242 if ((ch = getc(pe->e.fp)) == EOF) goto fail;
243 if (ch == '-') { d++; continue; }
244 if (ch == '\n') break;
245 if (i + d + 1 >= sizeof(buf)) goto fail;
246 while (d) { buf[i++] = '-'; d--; }
249 if (d != 5) goto fail;
251 if (strncmp(buf, "END ", 4) != 0 || strcmp(buf + 4, pe->msg) != 0)
256 die(EXIT_FAILURE, "final encapsulation boundary not found");
260 static void pem_destroy(enc *e)
262 pem_encctx *pe = (pem_encctx *)e;
263 dstr_destroy(&pe->d);
268 /* --- Encoder table --- */
270 const encops enctab[] = {
271 { "binary", "rb", "wb",
272 bin_encinit, bin_decinit,
277 pem_encinit, pem_decinit,
279 pem_encdone, pem_decdone,
284 /* --- @getenc@ --- *
286 * Arguments: @const char *enc@ = name of wanted encoding
288 * Returns: Pointer to encoder operations.
290 * Use: Finds a named encoder or decoder.
293 const encops *getenc(const char *enc)
297 for (eo = enctab; eo->name; eo++) {
298 if (strcmp(eo->name, enc) == 0)
301 die(EXIT_FAILURE, "couldn't find encoding `%s'", enc);
306 /* --- @initenc@ --- *
308 * Arguments: @const encops *eo@ = operations (from @getenc@)
309 * @FILE *fp@ = file handle to attach
310 * @const char *msg@ = banner message
312 * Returns: The encoder object.
314 * Use: Initializes an encoder.
317 enc *initenc(const encops *eo, FILE *fp, const char *msg)
319 enc *e = eo->initenc(fp, msg);
325 /* --- @initdec@ --- *
327 * Arguments: @const encops *eo@ = operations (from @getenc@)
328 * @FILE *fp@ = file handle to attach
329 * @int (*func)(const char *, void *)@ = banner check function
330 * @void *p@ = argument for @func@
332 * Returns: The encoder object.
334 * Use: Initializes an encoder.
337 enc *initdec(const encops *eo, FILE *fp,
338 int (*func)(const char *, void *), void *p)
340 enc *e = eo->initdec(fp, func, p);
346 /* --- @freeenc@ --- *
348 * Arguments: @enc *e@ = encoder object
352 * Use: Frees an encoder object.
355 void freeenc(enc *e) { e->ops->destroy(e); }
357 /*----- Encoding and decoding commands ------------------------------------*/
359 int cmd_encode(int argc, char *argv[])
364 const char *ef = "binary";
365 const char *bd = "MESSAGE";
376 static const struct option opt[] = {
377 { "format", OPTF_ARGREQ, 0, 'f' },
378 { "boundary", OPTF_ARGREQ, 0, 'b' },
379 { "output", OPTF_ARGREQ, 0, 'o' },
382 i = mdwopt(argc, argv, "f:b:o:", opt, 0, 0, 0);
385 case 'f': ef = optarg; break;
386 case 'b': bd = optarg; break;
387 case 'o': of = optarg; break;
388 default: f |= f_bogus; break;
391 if (argc - optind > 1 || (f & f_bogus))
392 die(EXIT_FAILURE, "Usage: encode [-OPTIONS] [FILE]");
394 if ((eo = getenc(ef)) == 0)
395 die(EXIT_FAILURE, "encoding `%s' not found", ef);
399 else if (strcmp(argv[optind], "-") == 0) {
402 } else if ((fp = fopen(argv[optind], "rb")) == 0) {
403 die(EXIT_FAILURE, "couldn't open file `%s': %s",
404 argv[optind], strerror(errno));
408 if (!of || strcmp(of, "-") == 0)
410 else if ((ofp = fopen(of, eo->wmode)) == 0) {
411 die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
412 ofp, strerror(errno));
415 e = initenc(eo, ofp, bd);
418 n = fread(buf, 1, sizeof(buf), fp);
419 if (e->ops->write(e, buf, n))
420 die(EXIT_FAILURE, "error writing output: %s", strerror(errno));
421 } while (n == sizeof(buf));
429 int cmd_decode(int argc, char *argv[])
434 const char *ef = "binary";
445 static const struct option opt[] = {
446 { "format", OPTF_ARGREQ, 0, 'f' },
447 { "boundary", OPTF_ARGREQ, 0, 'b' },
448 { "output", OPTF_ARGREQ, 0, 'o' },
451 i = mdwopt(argc, argv, "f:b:o:", opt, 0, 0, 0);
454 case 'f': ef = optarg; break;
455 case 'b': bd = optarg; break;
456 case 'o': of = optarg; break;
457 default: f |= f_bogus; break;
460 if (argc - optind > 1 || (f & f_bogus))
461 die(EXIT_FAILURE, "Usage: decode [-OPTIONS] [FILE]");
463 if ((eo = getenc(ef)) == 0)
464 die(EXIT_FAILURE, "encoding `%s' not found", ef);
468 else if (strcmp(argv[optind], "-") == 0) {
471 } else if ((fp = fopen(argv[optind], eo->rmode)) == 0) {
472 die(EXIT_FAILURE, "couldn't open file `%s': %s",
473 argv[optind], strerror(errno));
477 if (!of || strcmp(of, "-") == 0)
479 else if ((ofp = fopen(of, "wb")) == 0) {
480 die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
481 ofp, strerror(errno));
484 e = initdec(eo, fp, checkbdry, (/*unconst*/ void *)bd);
487 if ((i = e->ops->read(e, buf, sizeof(buf))) < 0)
488 die(EXIT_FAILURE, "error reading input: %s", strerror(errno));
489 if (fwrite(buf, 1, i, ofp) < i)
490 die(EXIT_FAILURE, "error writing output: %s", strerror(errno));
491 } while (i == sizeof(buf));
499 /*----- That's all, folks -------------------------------------------------*/