5c3f75ec |
1 | /* -*-c-*- |
2 | * |
c65df279 |
3 | * $Id$ |
5c3f75ec |
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 | |
c65df279 |
263 | const encops enctab[] = { |
5c3f75ec |
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 -------------------------------------------------*/ |