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 -------------------------------------------------*/