chiark / gitweb /
Merge branch '2.4.x' into 2.5.x
[catacomb] / progs / cc-enc.c
1 /* -*-c-*-
2  *
3  * Catcrypt data encoding
4  *
5  * (c) 2004 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of Catacomb.
11  *
12  * Catacomb is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU Library General Public License as
14  * published by the Free Software Foundation; either version 2 of the
15  * License, or (at your option) any later version.
16  *
17  * Catacomb is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public
23  * License along with Catacomb; if not, write to the Free
24  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25  * MA 02111-1307, USA.
26  */
27
28 /*----- Header files ------------------------------------------------------*/
29
30 #define _FILE_OFFSET_BITS 64
31
32 #include <errno.h>
33 #include <stdio.h>
34
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>
40 #include <mLib/sub.h>
41
42 #include "cc.h"
43
44 typedef int encbdryp(const char *, void *);
45
46 /*----- Main code ---------------------------------------------------------*/
47
48 /* --- Binary --- */
49
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); }
54
55 static int bin_read(enc *e, void *p, size_t sz)
56 {
57   size_t n;
58
59   if (!sz) return (0);
60   n = fread(p, 1, sz, e->fp);
61   if (!n || ferror(e->fp)) return (-1);
62   return (n);
63 }
64
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); }
67
68 static int bin_done(enc *e) { return (0); }
69
70 static void bin_destroy(enc *e) { DESTROY(e); }
71
72 /* --- PEM --- */
73
74 typedef struct pem_encctx {
75   enc e;
76   char *msg;
77   unsigned f;
78   base64_ctx b;
79   dstr d;
80   size_t n;
81 #define PEMF_NL 1u
82 #define PEMF_EOF 2u
83 } pem_encctx;
84
85 static enc *pem_encinit(FILE *fp, const char *msg)
86 {
87   pem_encctx *pe = CREATE(pem_encctx);
88   base64_init(&pe->b);
89   fprintf(fp, "-----BEGIN %s-----\n", msg);
90   pe->msg = xstrdup(msg);
91   dstr_create(&pe->d);
92   pe->n = 0;
93   pe->f = 0;
94   return (&pe->e);
95 }
96
97 int checkbdry(const char *b, void *p) { return (!p || strcmp(b, p) == 0); }
98
99 static enc *pem_decinit(FILE *fp, encbdryp *func, void *p)
100 {
101   char buf[128];
102   int i, d;
103   pem_encctx *pe;
104   int ch;
105
106   /* --- Go until I find a newline and `-----' --- */
107
108 top:
109   d = 0;
110   for (;;) {
111     if ((ch = getc(fp)) == EOF) goto fail;
112     switch (ch) {
113       case '\n': d = 0; break;
114       case '-': if (d >= 0) { d++; if (d == 5) goto banner; }; break;
115       default: d = -1; break;
116     }
117   }
118
119   /* --- See what the banner looks like --- */
120
121 banner:
122   i = d = 0;
123   for (;;) {
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--; }
129     buf[i++] = ch;
130   }
131   buf[i] = 0;
132
133   /* --- Check we have the right framing --- */
134
135   if (d != 5) goto top;
136   if (strncmp(buf, "BEGIN ", 6) != 0 || (func && !func(buf + 6, p)))
137     goto top;
138
139   /* --- Ready --- */
140
141   pe = CREATE(pem_encctx);
142   base64_init(&pe->b);
143   pe->msg = xstrdup(buf + 6);
144   dstr_create(&pe->d);
145   pe->n = 0;
146   pe->f = PEMF_NL;
147   return (&pe->e);
148
149   /* --- Failed --- */
150
151 fail:
152   die(EXIT_FAILURE, "initial encapsulation boundary not found");
153   return (0);
154 }
155
156 #define PEM_CHUNKSZ 4096
157
158 static int pem_read(enc *e, void *p, size_t sz)
159 {
160   pem_encctx *pe = (pem_encctx *)e;
161   char buf[PEM_CHUNKSZ];
162   char *pp = p;
163   int ch;
164   size_t n;
165   int rc = 0;
166
167   for (;;) {
168     n = pe->d.len - pe->n;
169     if (n > sz) n = sz;
170     memcpy(pp, pe->d.buf + pe->n, n);
171     pe->n += n;
172     pp += n;
173     rc += n;
174     sz -= n;
175     if (!sz) break;
176     if (pe->f & PEMF_EOF) return (rc ? rc : -1);
177     dstr_reset(&pe->d);
178     n = 0;
179     for (;;) {
180       if ((ch = getc(pe->e.fp)) == EOF) return (-1);
181       if ((pe->f & PEMF_NL) && ch == '-') {
182         ungetc(ch, pe->e.fp);
183         pe->f |= PEMF_EOF;
184         break;
185       }
186       if (ch == '\n') { pe->f |= PEMF_NL; continue; }
187       pe->f &= ~PEMF_NL;
188       buf[n++] = ch;
189       if (n >= PEM_CHUNKSZ) break;
190     }
191     if (n)
192       base64_decode(&pe->b, buf, n, &pe->d);
193     if (pe->f & PEMF_EOF)
194       base64_decode(&pe->b, 0, 0, &pe->d);
195     pe->n = 0;
196   }
197   return (rc);
198 }
199
200 static int pem_write(enc *e, const void *p, size_t sz)
201 {
202   pem_encctx *pe = (pem_encctx *)e;
203   const char *pp = p;
204   size_t n;
205
206   while (sz) {
207     n = PEM_CHUNKSZ;
208     if (n > sz) n = sz;
209     dstr_reset(&pe->d);
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)
212       return (-1);
213     pp += n;
214     sz -= n;
215   }
216   return (0);
217 }
218
219 static int pem_encdone(enc *e)
220 {
221   pem_encctx *pe = (pem_encctx *)e;
222   dstr_reset(&pe->d);
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)
225     return (-1);
226   if (pe->b.lnlen) fputc('\n', pe->e.fp);
227   fprintf(pe->e.fp, "-----END %s-----\n", pe->msg);
228   return (0);
229 }
230
231 static int pem_decdone(enc *e)
232 {
233   pem_encctx *pe = (pem_encctx *)e;
234   char buf[128];
235   int i, d;
236   int ch;
237
238   for (d = 0; d < 5; d++)
239     if ((ch = getc(pe->e.fp)) != '-') goto fail;
240   i = d = 0;
241   for (;;) {
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--; }
247     buf[i++] = ch;
248   }
249   if (d != 5) goto fail;
250   buf[i] = 0;
251   if (strncmp(buf, "END ", 4) != 0 || strcmp(buf + 4, pe->msg) != 0)
252     goto fail;
253   return (0);
254
255 fail:
256   die(EXIT_FAILURE, "final encapsulation boundary not found");
257   return (-1);
258 }
259
260 static void pem_destroy(enc *e)
261 {
262   pem_encctx *pe = (pem_encctx *)e;
263   dstr_destroy(&pe->d);
264   xfree(pe->msg);
265   DESTROY(pe);
266 }
267
268 /* --- Encoder table --- */
269
270 const encops enctab[] = {
271   { "binary", "rb", "wb", 1, 1,
272     bin_encinit, bin_decinit,
273     bin_read, bin_write,
274     bin_done, bin_done,
275     bin_destroy },
276   { "pem", "r", "w", 3, 4,
277     pem_encinit, pem_decinit,
278     pem_read, pem_write,
279     pem_encdone, pem_decdone,
280     pem_destroy },
281   { 0 }
282 };
283
284 /* --- @getenc@ --- *
285  *
286  * Arguments:   @const char *enc@ = name of wanted encoding
287  *
288  * Returns:     Pointer to encoder operations.
289  *
290  * Use:         Finds a named encoder or decoder.
291  */
292
293 const encops *getenc(const char *enc)
294 {
295   const encops *eo;
296
297   for (eo = enctab; eo->name; eo++) {
298     if (strcmp(eo->name, enc) == 0)
299       goto e_found;
300   }
301   die(EXIT_FAILURE, "couldn't find encoding `%s'", enc);
302 e_found:
303   return (eo);
304 }
305
306 /* --- @initenc@ --- *
307  *
308  * Arguments:   @const encops *eo@ = operations (from @getenc@)
309  *              @FILE *fp@ = file handle to attach
310  *              @const char *msg@ = banner message
311  *
312  * Returns:     The encoder object.
313  *
314  * Use:         Initializes an encoder.
315  */
316
317 enc *initenc(const encops *eo, FILE *fp, const char *msg)
318 {
319   enc *e = eo->initenc(fp, msg);
320   e->ops = eo;
321   e->fp = fp;
322   return (e);
323 }
324
325 /* --- @initdec@ --- *
326  *
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@
331  *
332  * Returns:     The encoder object.
333  *
334  * Use:         Initializes an encoder.
335  */
336
337 enc *initdec(const encops *eo, FILE *fp,
338              int (*func)(const char *, void *), void *p)
339 {
340   enc *e = eo->initdec(fp, func, p);
341   e->ops = eo;
342   e->fp = fp;
343   return (e);
344 }
345
346 /* --- @freeenc@ --- *
347  *
348  * Arguments:   @enc *e@ = encoder object
349  *
350  * Returns:     ---
351  *
352  * Use:         Frees an encoder object.
353  */
354
355 void freeenc(enc *e) { e->ops->destroy(e); }
356
357 /*----- Encoding and decoding commands ------------------------------------*/
358
359 int cmd_encode(int argc, char *argv[])
360 {
361   const char *fn, *of = 0;
362   FILE *ofp = 0;
363   FILE *fp = 0;
364   const char *ef = "binary";
365   const char *bd = "MESSAGE";
366   fprogress ff;
367   int i;
368   size_t n;
369   char buf[4096];
370   unsigned f = 0;
371   const encops *eo;
372   enc *e;
373
374 #define f_bogus 1u
375 #define f_progress 2u
376
377   for (;;) {
378     static const struct option opt[] = {
379       { "format",       OPTF_ARGREQ,    0,      'f' },
380       { "boundary",     OPTF_ARGREQ,    0,      'b' },
381       { "output",       OPTF_ARGREQ,    0,      'o' },
382       { "progress",     0,              0,      'p' },
383       { 0,              0,              0,      0 }
384     };
385     i = mdwopt(argc, argv, "f:b:o:p", opt, 0, 0, 0);
386     if (i < 0) break;
387     switch (i) {
388       case 'f': ef = optarg; break;
389       case 'b': bd = optarg; break;
390       case 'o': of = optarg; break;
391       case 'p': f |= f_progress; break;
392       default: f |= f_bogus; break;
393     }
394   }
395   if (argc - optind > 1 || (f & f_bogus))
396     die(EXIT_FAILURE, "Usage: encode [-OPTIONS] [FILE]");
397
398   if ((eo = getenc(ef)) == 0)
399     die(EXIT_FAILURE, "encoding `%s' not found", ef);
400
401   fn = optind < argc ? argv[optind++] : "-";
402   if (strcmp(fn, "-") == 0)
403     fp = stdin;
404   else if ((fp = fopen(fn, "rb")) == 0) {
405     die(EXIT_FAILURE, "couldn't open file `%s': %s",
406         fn, strerror(errno));
407   }
408
409   if (!of || strcmp(of, "-") == 0)
410     ofp = stdout;
411   else if ((ofp = fopen(of, eo->wmode)) == 0) {
412     die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
413         of, strerror(errno));
414   }
415
416   e = initenc(eo, ofp, bd);
417
418   if (f & f_progress) {
419     if (fprogress_init(&ff, fn, fp)) {
420       die(EXIT_FAILURE, "failed to initialize progress display: %s",
421           strerror(errno));
422     }
423   }
424
425   do {
426     n = fread(buf, 1, sizeof(buf), fp);
427     if (f & f_progress) fprogress_update(&ff, n);
428     if (e->ops->write(e, buf, n)) {
429       if (f & f_progress) fprogress_done(&ff);
430       die(EXIT_FAILURE, "error writing output: %s", strerror(errno));
431     }
432   } while (n == sizeof(buf));
433   if (f & f_progress) fprogress_done(&ff);
434   e->ops->encdone(e);
435   freeenc(e);
436   return (0);
437
438 #undef f_bogus
439 #undef f_progress
440 }
441
442 int cmd_decode(int argc, char *argv[])
443 {
444   const char *fn, *of = 0;
445   FILE *ofp = 0;
446   FILE *fp = 0;
447   const char *ef = "binary";
448   const char *bd = 0;
449   fprogress ff;
450   int i;
451   char buf[4096];
452   unsigned f = 0;
453   const encops *eo;
454   enc *e;
455
456 #define f_bogus 1u
457 #define f_progress 2u
458
459   for (;;) {
460     static const struct option opt[] = {
461       { "format",       OPTF_ARGREQ,    0,      'f' },
462       { "boundary",     OPTF_ARGREQ,    0,      'b' },
463       { "output",       OPTF_ARGREQ,    0,      'o' },
464       { "progress",     0,              0,      'p' },
465       { 0,              0,              0,      0 }
466     };
467     i = mdwopt(argc, argv, "f:b:o:p", opt, 0, 0, 0);
468     if (i < 0) break;
469     switch (i) {
470       case 'f': ef = optarg; break;
471       case 'b': bd = optarg; break;
472       case 'o': of = optarg; break;
473       case 'p': f |= f_progress; break;
474       default: f |= f_bogus; break;
475     }
476   }
477   if (argc - optind > 1 || (f & f_bogus))
478     die(EXIT_FAILURE, "Usage: decode [-OPTIONS] [FILE]");
479
480   if ((eo = getenc(ef)) == 0)
481     die(EXIT_FAILURE, "encoding `%s' not found", ef);
482
483   fn = optind < argc ? argv[optind++] : "-";
484   if (strcmp(fn, "-") == 0)
485     fp = stdin;
486   else if ((fp = fopen(fn, eo->rmode)) == 0) {
487     die(EXIT_FAILURE, "couldn't open file `%s': %s",
488         fn, strerror(errno));
489   }
490
491   if (!of || strcmp(of, "-") == 0)
492     ofp = stdout;
493   else if ((ofp = fopen(of, "wb")) == 0) {
494     die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
495         of, strerror(errno));
496   }
497
498   e = initdec(eo, fp, checkbdry, (/*unconst*/ void *)bd);
499
500   if (f & f_progress) {
501     if (fprogress_init(&ff, fn, fp)) {
502       die(EXIT_FAILURE, "failed to initialize progress display: %s",
503           strerror(errno));
504     }
505   }
506
507   do {
508     if ((i = e->ops->read(e, buf, sizeof(buf))) < 0) {
509       if (f & f_progress) fprogress_done(&ff);
510       die(EXIT_FAILURE, "error reading input: %s", strerror(errno));
511     }
512     if (f & f_progress)
513       fprogress_update(&ff, i*e->ops->ncook/e->ops->nraw);
514     if (fwrite(buf, 1, i, ofp) < i) {
515       if (f & f_progress) fprogress_done(&ff);
516       die(EXIT_FAILURE, "error writing output: %s", strerror(errno));
517     }
518   } while (i == sizeof(buf));
519   e->ops->decdone(e);
520   if (f & f_progress) fprogress_done(&ff);
521   freeenc(e);
522   return (0);
523
524 #undef f_bogus
525 #undef f_progress
526 }
527
528 /*----- That's all, folks -------------------------------------------------*/