chiark / gitweb /
General utilities cleanup. Add signature support to catcrypt. Throw in
[catacomb] / cc-enc.c
1 /* -*-c-*-
2  *
3  * $Id$
4  *
5  * Catcrypt data encoding
6  *
7  * (c) 2004 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of Catacomb.
13  *
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.
18  * 
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.
23  * 
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,
27  * MA 02111-1307, USA.
28  */
29
30 /*----- Header files ------------------------------------------------------*/
31
32 #include <stdio.h>
33
34 #include <mLib/alloc.h>
35 #include <mLib/base64.h>
36 #include <mLib/dstr.h>
37 #include <mLib/report.h>
38 #include <mLib/sub.h>
39
40 #include "cc.h"
41
42 /*----- Main code ---------------------------------------------------------*/
43
44 /* --- Binary --- */
45
46 static enc *bin_init(FILE *fp, const char *msg)
47   { enc *e = CREATE(enc); return (e); }
48
49 static int bin_read(enc *e, void *p, size_t sz)
50 {
51   size_t n;
52   
53   if (!sz) return (0);
54   n = fread(p, 1, sz, e->fp);
55   if (!n || ferror(e->fp)) return (-1);
56   return (n);
57 }
58
59 static int bin_write(enc *e, const void *p, size_t sz)
60   { if (sz && fwrite(p, 1, sz, e->fp) < sz) return (-1); return (0); }
61
62 static int bin_done(enc *e) { return (0); }
63
64 static void bin_destroy(enc *e) { DESTROY(e); }
65
66 /* --- PEM --- */
67
68 typedef struct pem_encctx {
69   enc e;
70   char *msg;
71   unsigned f;
72   base64_ctx b;
73   dstr d;
74   size_t n;
75 #define PEMF_NL 1u
76 #define PEMF_EOF 2u
77 } pem_encctx;
78
79 static enc *pem_encinit(FILE *fp, const char *msg)
80 {
81   pem_encctx *pe = CREATE(pem_encctx);
82   base64_init(&pe->b);
83   fprintf(fp, "-----BEGIN %s-----\n", msg);
84   pe->msg = xstrdup(msg);
85   dstr_create(&pe->d);
86   pe->n = 0;
87   pe->f = 0;
88   return (&pe->e);
89 }
90
91 static enc *pem_decinit(FILE *fp, const char *msg)
92 {
93   char buf[128];
94   int i, d;
95   pem_encctx *pe;
96   int ch;
97
98   /* --- Go until I find a newline and `-----' --- */
99
100 top:
101   d = 0;
102   for (;;) {
103     if ((ch = getc(fp)) == EOF) goto fail;
104     switch (ch) {
105       case '\n': d = 0; break;
106       case '-': if (d >= 0) { d++; if (d == 5) goto banner; }; break;
107       default: d = -1; break;
108     }
109   }
110
111   /* --- See what the banner looks like --- */
112
113 banner:
114   i = d = 0;
115   for (;;) {
116     if ((ch = getc(fp)) == EOF) goto fail;
117     if (ch == '-') { d++; continue; }
118     if (ch == '\n') break;
119     if (i + d + 1 >= sizeof(buf)) goto top;
120     while (d) { buf[i++] = '-'; d--; }
121     buf[i++] = ch;
122   }
123   buf[i] = 0;
124
125   /* --- Check we have the right framing --- */
126
127   if (d != 5) goto top;
128   if (strncmp(buf, "BEGIN ", 6) != 0 ||
129       (msg && strcmp(buf + 6, msg) != 0))
130     goto top;
131
132   /* --- Ready --- */
133
134   pe = CREATE(pem_encctx);
135   base64_init(&pe->b);
136   pe->msg = xstrdup(buf + 6);
137   dstr_create(&pe->d);
138   pe->n = 0;
139   pe->f = PEMF_NL;
140   return (&pe->e);
141
142   /* --- Failed --- */
143
144 fail:
145   die(EXIT_FAILURE, "initial encapsulation boundary not found");
146   return (0);
147 }
148
149 #define PEM_CHUNKSZ 4096
150
151 static int pem_read(enc *e, void *p, size_t sz)
152 {
153   pem_encctx *pe = (pem_encctx *)e;
154   char buf[PEM_CHUNKSZ];
155   char *pp = p;
156   int ch;
157   size_t n;
158   int rc = 0;
159
160   for (;;) {
161     n = pe->d.len - pe->n;
162     if (n > sz) n = sz;
163     memcpy(pp, pe->d.buf + pe->n, n);
164     pe->n += n;
165     pp += n;
166     rc += n;
167     sz -= n;
168     if (!sz) break;
169     if (pe->f & PEMF_EOF) return (rc ? rc : -1);
170     dstr_reset(&pe->d);
171     n = 0;
172     for (;;) {
173       if ((ch = getc(pe->e.fp)) == EOF) return (-1);
174       if ((pe->f & PEMF_NL) && ch == '-') {
175         ungetc(ch, pe->e.fp);
176         pe->f |= PEMF_EOF;
177         break;
178       }
179       if (ch == '\n') { pe->f |= PEMF_NL; continue; }
180       pe->f &= ~PEMF_NL;
181       buf[n++] = ch;
182       if (n >= PEM_CHUNKSZ) break;
183     }
184     if (n)
185       base64_decode(&pe->b, buf, n, &pe->d);
186     if (pe->f & PEMF_EOF)
187       base64_decode(&pe->b, 0, 0, &pe->d);
188     pe->n = 0;
189   }
190   return (rc);
191 }
192
193 static int pem_write(enc *e, const void *p, size_t sz)
194 {
195   pem_encctx *pe = (pem_encctx *)e;
196   const char *pp = p;
197   size_t n;
198
199   while (sz) {
200     n = PEM_CHUNKSZ;
201     if (n > sz) n = sz;
202     dstr_reset(&pe->d);
203     base64_encode(&pe->b, pp, n, &pe->d);
204     if (fwrite(pe->d.buf, 1, pe->d.len, pe->e.fp) < pe->d.len)
205       return (-1);
206     pp += n;
207     sz -= n;
208   }
209   return (0);
210 }
211
212 static int pem_encdone(enc *e)
213 {
214   pem_encctx *pe = (pem_encctx *)e;
215   dstr_reset(&pe->d);
216   base64_encode(&pe->b, 0, 0, &pe->d);
217   if (fwrite(pe->d.buf, 1, pe->d.len, pe->e.fp) < pe->d.len)
218     return (-1);
219   if (pe->b.lnlen) fputc('\n', pe->e.fp);
220   fprintf(pe->e.fp, "-----END %s-----\n", pe->msg);
221   return (0);
222 }
223
224 static int pem_decdone(enc *e)
225 {
226   pem_encctx *pe = (pem_encctx *)e;
227   char buf[128];
228   int i, d;
229   int ch;
230
231   for (d = 0; d < 5; d++)
232     if ((ch = getc(pe->e.fp)) != '-') goto fail;
233   i = d = 0;
234   for (;;) {
235     if ((ch = getc(pe->e.fp)) == EOF) goto fail;
236     if (ch == '-') { d++; continue; }
237     if (ch == '\n') break;
238     if (i + d + 1 >= sizeof(buf)) goto fail;
239     while (d) { buf[i++] = '-'; d--; }
240     buf[i++] = ch;
241   }
242   if (d != 5) goto fail;
243   buf[i] = 0;
244   if (strncmp(buf, "END ", 4) != 0 || strcmp(buf + 4, pe->msg) != 0)
245     goto fail;
246   return (0);
247
248 fail:
249   die(EXIT_FAILURE, "final encapsulation boundary not found");
250   return (-1);
251 }
252
253 static void pem_destroy(enc *e)
254 {
255   pem_encctx *pe = (pem_encctx *)e;
256   dstr_destroy(&pe->d);
257   xfree(pe->msg);
258   DESTROY(pe);
259 }
260
261 /* --- Encoder table --- */
262
263 const encops enctab[] = {
264   { "binary", "rb", "wb",
265     bin_init, bin_init,
266     bin_read, bin_write,
267     bin_done, bin_done,
268     bin_destroy },
269   { "pem", "r", "w",
270     pem_encinit, pem_decinit,
271     pem_read, pem_write,
272     pem_encdone, pem_decdone,
273     pem_destroy },
274   { 0 }
275 };  
276
277 /* --- @getenc@ --- *
278  *
279  * Arguments:   @const char *enc@ = name of wanted encoding
280  *
281  * Returns:     Pointer to encoder operations.
282  *
283  * Use:         Finds a named encoder or decoder.
284  */
285
286 const encops *getenc(const char *enc)
287 {
288   const encops *eo;
289
290   for (eo = enctab; eo->name; eo++) {
291     if (strcmp(eo->name, enc) == 0)
292       goto e_found;
293   }
294   die(EXIT_FAILURE, "couldn't find encoding `%s'", enc);
295 e_found:
296   return (eo);
297 }
298
299 /* --- @initenc@ --- *
300  *
301  * Arguments:   @const encops *eo@ = operations (from @getenc@)
302  *              @FILE *fp@ = file handle to attach
303  *              @const char *msg@ = banner message
304  *              @int wantenc@ = nonzero if we want to encode
305  *
306  * Returns:     The encoder object.
307  *
308  * Use:         Initializes an encoder.
309  */
310
311 enc *initenc(const encops *eo, FILE *fp, const char *msg, int wantenc)
312 {
313   enc *e = (wantenc ? eo->initenc : eo->initdec)(fp, msg);
314   e->ops = eo;
315   e->fp = fp;
316   return (e);  
317 }
318
319 /* --- @freeenc@ --- *
320  *
321  * Arguments:   @enc *e@ = encoder object
322  *
323  * Returns:     ---
324  *
325  * Use:         Frees an encoder object.
326  */
327
328 void freeenc(enc *e) { e->ops->destroy(e); }
329
330 /*----- That's all, folks -------------------------------------------------*/