5 * (c) 2005 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of Catacomb.
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.
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.
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,
28 /*----- Header files ------------------------------------------------------*/
30 #define _FILE_OFFSET_BITS 64
41 #include <mLib/base64.h>
42 #include <mLib/dstr.h>
43 #include <mLib/macros.h>
44 #include <mLib/mdwopt.h>
45 #include <mLib/quis.h>
46 #include <mLib/report.h>
60 /*----- Static variables --------------------------------------------------*/
62 static const char *keyring
= "keyring";
64 /*----- Data formats ------------------------------------------------------*
66 * Our crypto stuff is split into three parts: a header describing the key,
67 * the message itself, and the signature. In a detached signature, the
68 * message is separate, and the header and signature are combined into one
69 * encoded chunk. In an attached signature, the message is included. There
70 * are two forms of message: binary and text. Binary messages are divided
71 * into chunks, each preceded by a 2-octet length, and terminated by a
72 * zero-length chunk. Text messages are byte-stuffed, as for RFC821, and
73 * trailing whitespace before a newline is ignored.
76 typedef struct sigmsg
{
77 unsigned f
; /* Flags */
81 #define F_HASHMASK (F_BINARY)
82 uint32 keyid
; /* Key identifier */
83 time_t t
; /* When the signature was made */
84 dstr kh
; /* Key fingerprint (sanity check) */
85 dstr sig
; /* Signature */
86 sig
*s
; /* Signature algorithm */
95 #define F_NOCHECK 1024u
96 #define F_PROGRESS 2048u
98 /*----- Chunk I/O ---------------------------------------------------------*/
100 static void chunk_write(enc
*e
, const void *p
, size_t n
)
106 if (e
->ops
->write(e
, b
, 2) || e
->ops
->write(e
, p
, n
))
107 die(EXIT_FAILURE
, "error writing output: %s", strerror(errno
));
110 static size_t chunk_read(enc
*e
, void *p
)
115 if (e
->ops
->read(e
, b
, 2) != 2)
118 if (n
&& e
->ops
->read(e
, p
, n
) != n
)
123 if (!errno
) die(EXIT_FAILURE
, "unexpected end-of-file on input");
124 else die(EXIT_FAILURE
, "error reading input: %s", strerror(errno
));
128 /*----- Message canonification --------------------------------------------*/
130 #define MSGBUFSZ 65536
131 #define MSGBUFTHRESH 32768
133 typedef struct msgcanon
{
138 size_t (*read
)(struct msgcanon
*, void *);
139 void (*write
)(struct msgcanon
*, const void *, size_t);
140 void (*close
)(struct msgcanon
*);
143 #define MC_INIT { 0 }
145 static size_t textread(msgcanon
*m
, void *bp
)
147 size_t n
= 0, nsp
= 0;
152 if (f
& F_EOF
) return (0);
154 if ((ch
= getc(m
->fp
)) == EOF
) goto eof
;
155 if (!(f
& (F_DETACH
| F_MIDLINE
)) && ch
== '.') {
157 if (ch
== '\n') goto eof
;
161 if (n
>= MSGBUFTHRESH
) goto full
;
164 } else if (isspace(ch
)) {
166 if (n
>= MSGBUFSZ
) goto full
;
171 if (n
>= MSGBUFTHRESH
) goto full
;
182 if (m
->f
& F_PROGRESS
) fprogress_update(&m
->ff
, n
);
186 static void textwrite(msgcanon
*m
, const void *bp
, size_t n
)
188 const char *p
= bp
, *l
= p
+ n
;
192 if (!(f
& (F_MIDLINE
| F_DETACH
)) &&
193 (*p
== '.' || *p
== '-'))
195 if (*p
== '\n') f
&= ~F_MIDLINE
;
203 static size_t binreadembed(msgcanon
*m
, void *bp
)
205 size_t n
= chunk_read(m
->e
, bp
);
206 if (m
->f
& F_PROGRESS
) fprogress_update(&m
->ff
, n
);
210 static size_t binreaddetach(msgcanon
*m
, void *bp
)
212 size_t n
= fread(bp
, 1, MSGBUFSZ
- 1, m
->fp
);
213 if (m
->f
& F_PROGRESS
) fprogress_update(&m
->ff
, n
);
217 static void binwriteembed(msgcanon
*m
, const void *bp
, size_t n
)
218 { chunk_write(m
->e
, bp
, n
); }
220 static void nullwrite(msgcanon
*m
, const void *bp
, size_t n
) { ; }
222 static void mcsetup_readfile(msgcanon
*m
, unsigned f
, const char *fn
)
224 m
->f
= F_DETACH
| (f
& (F_BINARY
| F_PROGRESS
));
225 if (!fn
|| strcmp(fn
, "-") == 0) {
228 } else if ((m
->fp
= fopen(fn
, (f
& F_BINARY
) ?
"rb" : "r")) == 0)
229 die(EXIT_FAILURE
, "couldn't open file `%s': %s", fn
, strerror(errno
));
230 if (m
->f
& F_PROGRESS
) {
231 if (fprogress_init(&m
->ff
, fn
, m
->fp
)) {
232 die(EXIT_FAILURE
, "failed to initialize progress indicator: %s",
236 m
->read
= (f
& F_BINARY
) ? binreaddetach
: textread
;
239 static void mcsetup_writenull(msgcanon
*m
) { m
->write
= nullwrite
; }
241 static void mcsetup_read(msgcanon
*m
, unsigned f
, enc
**ee
,
242 const char *fn
, const char *dfn
)
246 m
->f
= f
| F_NOCLOSE
;
248 if (dfn
&& !(f
& F_DETACH
)) die(EXIT_FAILURE
, "signature is not detached");
250 if (!(f
& F_DETACH
)) {
254 m
->read
= binreadembed
;
255 if (m
->f
& F_PROGRESS
) {
256 if (fprogress_init(&m
->ff
, fn
, m
->fp
)) {
257 die(EXIT_FAILURE
, "failed to initialize progress indicator: %s",
262 m
->read
= binreaddetach
;
263 if (!dfn
|| strcmp(dfn
, "-") == 0)
265 else if ((m
->fp
= fopen(dfn
, "rb")) == 0)
266 die(EXIT_FAILURE
, "can't open `%s': %s", dfn
, strerror(errno
));
269 if (m
->f
& F_PROGRESS
) {
270 if (fprogress_init(&m
->ff
, dfn
, m
->fp
)) {
271 die(EXIT_FAILURE
, "failed to initialize progress indicator: %s",
278 if (!(f
& F_DETACH
)) {
279 if (e
->ops
->decdone(e
) || ferror(e
->fp
))
280 die(EXIT_FAILURE
, "error at end of signature header");
284 if (m
->f
& F_PROGRESS
) {
285 if (fprogress_init(&m
->ff
, fn
, m
->fp
)) {
286 die(EXIT_FAILURE
, "failed to initialize progress indicator: %s",
291 if (!dfn
|| strcmp(dfn
, "-") == 0)
293 else if ((m
->fp
= fopen(dfn
, "r")) == 0)
294 die(EXIT_FAILURE
, "can't read file `%s': %s", dfn
, strerror(errno
));
297 if (m
->f
& F_PROGRESS
) {
298 if (fprogress_init(&m
->ff
, dfn
, m
->fp
)) {
299 die(EXIT_FAILURE
, "failed to initialize progress indicator: %s",
307 static void mcsetup_write(msgcanon
*m
, unsigned f
, enc
**ee
)
311 m
->f
= f
| F_NOCLOSE
;
314 m
->write
= nullwrite
;
315 else if (f
& F_BINARY
) {
319 m
->write
= binwriteembed
;
321 if (e
->ops
->encdone(e
) || ferror(e
->fp
))
322 die(EXIT_FAILURE
, "error at end of signature header");
326 m
->write
= textwrite
;
330 static void mc_endread(msgcanon
*m
, const encops
*eops
, enc
**ee
)
332 if (!(m
->f
& F_DETACH
)) {
336 *ee
= initdec(eops
, m
->fp
, checkbdry
, "CATSIGN SIGNATURE");
338 if (m
->fp
&& !(m
->f
& F_NOCLOSE
)) {
339 if (ferror(m
->fp
) || fclose(m
->fp
))
340 die(EXIT_FAILURE
, "error closing message file: %s", strerror(errno
));
342 if (m
->f
& F_PROGRESS
) fprogress_done(&m
->ff
);
345 static void mc_endwrite(msgcanon
*m
, const encops
*eops
, enc
**ee
)
347 if (!(m
->f
& F_BINARY
)) {
348 if (m
->f
& F_MIDLINE
) putc('\n', m
->fp
);
349 if (!(m
->f
& F_DETACH
)) {
350 putc('.', m
->fp
); putc('\n', m
->fp
);
351 *ee
= initenc(eops
, m
->fp
, "CATSIGN SIGNATURE");
353 } else if (!(m
->f
& F_DETACH
)) {
354 chunk_write(m
->e
, 0, 0);
357 if (m
->fp
&& !(m
->f
& F_NOCLOSE
)) {
358 if (fflush(m
->fp
) || ferror(m
->fp
) || fclose(m
->fp
))
359 die(EXIT_FAILURE
, "error closing message file: %s", strerror(errno
));
363 /*----- Signature reading and writing -------------------------------------*/
365 static void sig_init(sigmsg
*s
, unsigned f
, uint32 keyid
)
367 s
->f
= f
& F_SIGMASK
;
372 dstr_create(&s
->sig
);
375 static void sig_destroy(sigmsg
*s
)
377 dstr_destroy(&s
->kh
);
378 dstr_destroy(&s
->sig
);
379 if (s
->s
) freesig(s
->s
);
382 static void sigtobuffer(sigmsg
*s
, buf
*b
, int hashp
)
387 if (hashp
) buf_putu16(b
, s
->f
& F_HASHMASK
);
388 else buf_putu16(b
, s
->f
& F_SIGMASK
);
389 buf_putu32(b
, s
->keyid
);
390 buf_putu32(b
, HI64(t
));
391 buf_putu32(b
, LO64(t
));
392 buf_putdstr16(b
, &s
->kh
);
396 static void dohash(ghash
*h
, const void *p
, size_t n
)
401 static void sig_hash(sigmsg
*s
)
406 buf_init(&b
, bb
, sizeof(bb
));
407 sigtobuffer(s
, &b
, 1);
408 dohash(s
->s
->h
, BBASE(&b
), BLEN(&b
));
411 static void keyhash(key
*k
, sig
*s
, dstr
*d
)
419 key_fingerprint(k
, h
, &kf
);
420 dstr_ensure(d
, GH_CLASS(h
)->hashsz
);
421 GH_DONE(h
, d
->buf
+ d
->len
);
422 d
->len
+= GH_CLASS(h
)->hashsz
;
426 static void sig_writeheader(enc
*e
, sigmsg
*s
)
431 buf_init(&b
, bb
, sizeof(bb
));
432 sigtobuffer(s
, &b
, 0);
433 chunk_write(e
, BBASE(&b
), BLEN(&b
));
436 static void sig_writesig(enc
*e
, sigmsg
*s
)
437 { chunk_write(e
, s
->sig
.buf
, s
->sig
.len
); }
439 static void NORETURN
diechoke(const char *m
, void *p
)
440 { die(EXIT_FAILURE
, "%s%s%s", (const char *)p
, p ?
": " : "", m
); }
442 static void sig_readheader(enc
*e
, sigmsg
*s
,
443 void NORETURN (*choke
)(const char *, void *),
453 n
= chunk_read(e
, bb
);
455 if (buf_getu16(&b
, &f
)) choke("missing flags", p
);
456 if (buf_getu32(&b
, &x
)) choke("missing keyid", p
);
458 if (buf_getu32(&b
, &x
) || buf_getu32(&b
, &y
))
459 choke("missing datestamp", p
);
460 SET64(t
, x
, y
); s
->t
= GET64(time_t, t
);
461 if (buf_getdstr16(&b
, &s
->kh
))
462 choke("missing key hash", p
);
464 choke("junk at end", p
);
467 static void sig_readsig(enc
*e
, sigmsg
*s
)
472 n
= chunk_read(e
, bb
);
473 dstr_putm(&s
->sig
, bb
, n
);
476 /*----- Signing -----------------------------------------------------------*/
478 static int sign(int argc
, char *argv
[])
480 const char *ef
= "binary", *fn
= 0, *of
= 0, *kn
= "ccsig", *err
;
491 msgcanon mc_in
= MC_INIT
, mc_out
= MC_INIT
;
495 static const struct option opt
[] = {
496 { "armour", 0, 0, 'a' },
497 { "armor", 0, 0, 'a' },
498 { "binary", 0, 0, 'b' },
499 { "detach", 0, 0, 'd' },
500 { "key", OPTF_ARGREQ
, 0, 'k' },
501 { "format", OPTF_ARGREQ
, 0, 'f' },
502 { "output", OPTF_ARGREQ
, 0, 'o' },
503 { "progress", 0, 0, 'p' },
504 { "text", 0, 0, 't' },
505 { "nocheck", 0, 0, 'C' },
508 i
= mdwopt(argc
, argv
, "k:f:o:abdptC", opt
, 0, 0, 0);
511 case 'k': kn
= optarg
; break;
512 case 'f': ef
= optarg
; break;
513 case 'o': of
= optarg
; break;
514 case 'a': ef
= "pem"; break;
515 case 't': f
&= ~F_BINARY
; break;
516 case 'b': f
|= F_BINARY
; break;
517 case 'd': f
|= F_DETACH
; break;
518 case 'C': f
|= F_NOCHECK
; break;
519 case 'p': f
|= F_PROGRESS
; break;
520 default: f
|= F_BOGUS
; break;
523 if (argc
- optind
> 1 || (f
& F_BOGUS
))
524 die(EXIT_FAILURE
, "Usage: sign [-OPTIONS] [FILE]");
526 if (key_open(&kf
, keyring
, KOPEN_READ
, key_moan
, 0))
527 die(EXIT_FAILURE
, "can't open keyring `%s'", keyring
);
528 if ((k
= key_bytag(&kf
, kn
)) == 0)
529 die(EXIT_FAILURE
, "key `%s' not found", kn
);
531 if ((eo
= getenc(ef
)) == 0)
532 die(EXIT_FAILURE
, "encoding `%s' not found", ef
);
534 fn
= (optind
>= argc
) ?
0 : argv
[optind
++];
536 if (!of
|| strcmp(of
, "-") == 0)
538 else if ((ofp
= fopen(of
, eo
->wmode
)) == 0) {
539 die(EXIT_FAILURE
, "couldn't open file `%s' for output: %s",
540 of
, strerror(errno
));
543 /* --- Start the work --- */
545 sig_init(&s
, f
, k
->id
);
548 s
.s
= getsig(k
, "ccsig", 1);
549 if (!(f
& F_NOCHECK
) && (err
= s
.s
->ops
->check(s
.s
)) != 0)
550 moan("key %s fails check: %s", d
.buf
, err
);
551 keyhash(k
, s
.s
, &s
.kh
);
553 (f
& F_DETACH
) ?
"CATSIGN SIGNATURE" :
554 (f
& F_BINARY
) ?
"CATSIGN MESSAGE" :
555 "CATSIGN MESSAGE HEADER");
556 sig_writeheader(e
, &s
);
558 /* --- Hash the message --- */
560 mcsetup_readfile(&mc_in
, f
, fn
);
561 mcsetup_write(&mc_out
, f
, &e
);
564 n
= mc_in
.read(&mc_in
, bb
);
566 dohash(s
.s
->h
, bb
, n
);
567 mc_out
.write(&mc_out
, bb
, n
);
569 mc_endread(&mc_in
, 0, 0);
570 mc_endwrite(&mc_out
, eo
, &e
);
572 /* --- Write the signature --- */
574 if (s
.s
->ops
->doit(s
.s
, &s
.sig
))
575 die(EXIT_FAILURE
, "signature failed");
578 if (fflush(ofp
) || ferror(ofp
) || fclose(ofp
))
579 die(EXIT_FAILURE
, "error writing signature: %s", strerror(errno
));
581 /* --- All done --- */
590 /*----- Verifying ---------------------------------------------------------*/
592 typedef struct vrfctx
{
598 static int vrfbdry(const char *b
, void *p
)
602 if (strcmp(b
, "CATSIGN MESSAGE") == 0) {
604 v
->m
|= F_BINARY
| F_DETACH
;
606 } else if (strcmp(b
, "CATSIGN MESSAGE HEADER") == 0) {
607 v
->m
|= F_BINARY
| F_DETACH
;
609 } else if (strcmp(b
, "CATSIGN SIGNATURE") == 0) {
617 static void NORETURN
vrfchoke(const char *m
, void *p
)
620 if (v
->verb
) printf("FAIL %s: %s\n", v
->what
, m
);
624 static int verify(int argc
, char *argv
[])
626 const char *ef
= "binary", *of
= 0, *fn
, *dfn
= 0, *kn
= 0, *err
;
627 vrfctx v
= { 0, 0, 1 };
631 FILE *fp
, *ofp
= 0, *rfp
= 0;
638 dstr d
= DSTR_INIT
, dd
= DSTR_INIT
;
640 msgcanon mc_in
= MC_INIT
;
644 static const struct option opt
[] = {
645 { "armour", 0, 0, 'a' },
646 { "armor", 0, 0, 'a' },
647 { "buffer", 0, 0, 'b' },
648 { "key", OPTF_ARGREQ
, 0, 'k' },
649 { "format", OPTF_ARGREQ
, 0, 'f' },
650 { "output", OPTF_ARGREQ
, 0, 'o' },
651 { "progress", 0, 0, 'p' },
652 { "quiet", 0, 0, 'q' },
653 { "utc", 0, 0, 'u' },
654 { "fresh-time", 0, 0, 't' },
655 { "gmt", 0, 0, 'u' },
656 { "verbose", 0, 0, 'v' },
657 { "nocheck", 0, 0, 'C' },
660 i
= mdwopt(argc
, argv
, "k:f:o:abpqt:uvC", opt
, 0, 0, 0);
663 case 'a': ef
= "pem"; break;
664 case 'b': v
.f
|= F_BUFFER
; break;
665 case 'k': kn
= optarg
; break;
666 case 'f': ef
= optarg
; break;
667 case 'o': of
= optarg
; break;
668 case 'u': v
.f
|= F_UTC
; break;
669 case 'C': v
.f
|= F_NOCHECK
; break;
671 if (strcmp(optarg
, "always") == 0) t_fresh
= 0;
672 else if ((t_fresh
= get_date(optarg
, 0)) < 0)
673 die(EXIT_FAILURE
, "bad freshness time");
675 case 'q': if (v
.verb
> 0) v
.verb
--; break;
676 case 'v': if (v
.verb
< 10) v
.verb
++; break;
677 case 'p': v
.f
|= F_PROGRESS
; break;
678 default: v
.f
|= F_BOGUS
; break;
681 if (argc
- optind
> 2 || (v
.f
& F_BOGUS
))
682 die(EXIT_FAILURE
, "Usage: verify [-OPTIONS] [FILE [MESSAGE]]");
684 if ((eo
= getenc(ef
)) == 0)
685 die(EXIT_FAILURE
, "encoding `%s' not found", ef
);
687 fn
= optind
< argc ? argv
[optind
++] : "-";
688 if (strcmp(fn
, "-") == 0)
690 else if ((fp
= fopen(fn
, eo
->rmode
)) == 0) {
691 die(EXIT_FAILURE
, "couldn't open file `%s': %s",
692 fn
, strerror(errno
));
695 if (key_open(&kf
, keyring
, KOPEN_READ
, key_moan
, 0))
696 die(EXIT_FAILURE
, "can't open keyring `%s'", keyring
);
697 if (kn
&& (kk
= key_bytag(&kf
, kn
)) == 0)
698 die(EXIT_FAILURE
, "key `%s' not found", kn
);
700 e
= initdec(eo
, fp
, vrfbdry
, &v
);
702 /* --- Read the header chunk --- */
704 v
.what
= "malformed header";
705 sig_readheader(e
, &s
, vrfchoke
, &v
);
707 if (((s
.f
^ v
.f
) & v
.m
) != 0) {
708 if (v
.verb
) printf("FAIL boundary string inconsistent with contents\n");
713 if ((k
= key_byid(&kf
, s
.keyid
)) == 0) {
714 if (v
.verb
) printf("FAIL key id %08lx not found\n",
715 (unsigned long)s
.keyid
);
720 dstr_reset(&d
); key_fulltag(k
, &d
);
721 dstr_reset(&dd
); key_fulltag(kk
, &dd
);
722 printf("FAIL signing key is %s; expected key %s\n", d
.buf
, dd
.buf
);
727 s
.s
= getsig(k
, "ccsig", 0);
728 dstr_reset(&d
); key_fulltag(k
, &d
);
729 if (!(v
.f
& F_NOCHECK
) && v
.verb
&& (err
= s
.s
->ops
->check(s
.s
)) != 0)
730 printf("WARN verification key %s fails check: %s\n", d
.buf
, err
);
732 dstr_reset(&dd
); keyhash(k
, s
.s
, &dd
);
733 if (dd
.len
!= s
.kh
.len
|| memcmp(dd
.buf
, s
.kh
.buf
, dd
.len
) != 0) {
734 if (v
.verb
) printf("FAIL key hash mismatch\n");
738 /* --- Now a merry dance --- */
743 dfn
= argv
[optind
++];
744 mcsetup_read(&mc_in
, v
.f
, &e
, fn
, dfn
);
746 if (!of
&& (v
.f
& F_DETACH
)) {
749 } else if (!of
|| strcmp(of
, "-") == 0) {
752 } else if (of
&& !(v
.f
& F_BUFFER
)) {
753 if ((ofp
= fopen(of
, (v
.f
& F_BINARY
) ?
"wb" : "w")) == 0) {
754 die(EXIT_FAILURE
, "couldn't open file `%s' for output: %s",
755 of
, strerror(errno
));
759 if ((v
.f
& F_BUFFER
) && (rfp
= tmpfile()) == 0)
760 die(EXIT_FAILURE
, "couldn't create temporary file: %s", strerror(errno
));
762 /* --- Read the message and verify the signature --- */
766 n
= mc_in
.read(&mc_in
, bb
);
768 dohash(s
.s
->h
, bb
, n
);
769 if (rfp
) fwrite(bb
, 1, n
, rfp
);
771 mc_endread(&mc_in
, eo
, &e
);
772 if (!(v
.f
& F_DETACH
))
774 if (rfp
&& (ferror(rfp
) || fflush(rfp
))) {
775 if (v
.verb
) printf("FAIL error writing message: %s\n", strerror(errno
));
779 /* --- Check the signature --- */
781 if (s
.s
->ops
->doit(s
.s
, &s
.sig
)) {
782 if (v
.verb
) printf("FAIL signature verification failed\n");
785 if (t_fresh
&& s
.t
< t_fresh
) {
786 if (v
.verb
) printf("FAIL signature is stale\n");
790 if (v
.verb
) printf("FAIL signature timestamp in the future\n");
794 tm
= (v
.f
& F_UTC
) ?
gmtime(&s
.t
) : localtime(&s
.t
);
795 strftime(bb
, sizeof(bb
), "%Y-%m-%d %H:%M:%S %Z", tm
);
796 printf("INFO good-signature %s\n", d
.buf
);
797 printf("INFO date %s\n", bb
);
800 /* --- Unbuffer buffered input --- */
802 if (v
.f
& F_BUFFER
) {
803 if (!ofp
&& (ofp
= fopen(of
, "wb")) == 0) {
804 die(EXIT_FAILURE
, "couldn't open file `%s' for output: %s",
805 of
, strerror(errno
));
808 if (v
.f
& F_PROGRESS
) fprogress_init(&ff
, "copying buffer", rfp
);
809 if (v
.verb
&& ofp
== stdout
) printf("DATA\n");
811 n
= fread(bb
, 1, sizeof(bb
), rfp
);
813 if (v
.f
& F_PROGRESS
) fprogress_update(&ff
, n
);
814 if (fwrite(bb
, 1, n
, ofp
) < n
) {
815 if (v
.f
& F_PROGRESS
) fprogress_done(&ff
);
816 die(EXIT_FAILURE
, "error writing output: %s", strerror(errno
));
819 if (v
.f
& F_PROGRESS
) fprogress_done(&ff
);
820 if (ferror(rfp
) || fclose(rfp
))
821 die(EXIT_FAILURE
, "error unbuffering output: %s", strerror(errno
));
823 if (ofp
&& (fflush(ofp
) || ferror(ofp
) || fclose(ofp
)))
824 die(EXIT_FAILURE
, "error writing output: %s", strerror(errno
));
826 /* --- Tidy up --- */
829 if (v
.verb
&& ofp
!= stdout
)
830 printf("OK verified successfully\n");
839 /*----- Reformatting ------------------------------------------------------*/
841 static int format(int argc
, char *argv
[])
843 const char *ief
= "binary", *oef
= "binary";
844 const char *fn
, *dfn
= 0, *of
= 0, *mf
= 0;
846 FILE *fp
, *ofp
= 0, *mfp
= 0;
849 msgcanon mc_in
= MC_INIT
, mc_out
= MC_INIT
;
851 vrfctx v
= { 0, 0, 1 };
852 unsigned f
= 0, fm
= ~F_SIGMASK
, sf
;
853 const encops
*ieo
, *oeo
;
857 static const struct option opt
[] = {
858 { "armour-in", 0, 0, 'a' },
859 { "armor-in", 0, 0, 'a' },
860 { "armour-out", 0, 0, 'A' },
861 { "armor-out", 0, 0, 'A' },
862 { "detach", 0, 0, 'D' },
863 { "embed", 0, 0, 'E' },
864 { "format-in", OPTF_ARGREQ
, 0, 'f' },
865 { "format-out", OPTF_ARGREQ
, 0, 'F' },
866 { "message", OPTF_ARGREQ
, 0, 'm' },
867 { "output", OPTF_ARGREQ
, 0, 'o' },
868 { "progress", 0, 0, 'p' },
871 i
= mdwopt(argc
, argv
, "f:F:m:o:apADE", opt
, 0, 0, 0);
874 case 'a': ief
= "pem"; break;
875 case 'A': oef
= "pem"; break;
876 case 'f': ief
= optarg
; break;
877 case 'F': oef
= optarg
; break;
878 case 'D': f
|= F_DETACH
; fm
|= F_DETACH
; break;
879 case 'E': f
&= ~F_DETACH
; fm
|= F_DETACH
; break;
880 case 'm': mf
= optarg
; break;
881 case 'o': of
= optarg
; break;
882 case 'p': f
|= F_PROGRESS
; break;
883 default: f
|= F_BOGUS
; break;
887 if (argc
- optind
> 2 || (f
& F_BOGUS
))
888 die(EXIT_FAILURE
, "Usage: format [-OPTIONS] [FILE [MESSAGE]]");
890 if ((ieo
= getenc(ief
)) == 0)
891 die(EXIT_FAILURE
, "encoding `%s' not found", ief
);
892 if ((oeo
= getenc(oef
)) == 0)
893 die(EXIT_FAILURE
, "encoding `%s' not found", oef
);
895 fn
= optind
< argc ? argv
[optind
++] : "-";
896 if (strcmp(fn
, "-") == 0)
898 else if ((fp
= fopen(fn
, ieo
->rmode
)) == 0) {
899 die(EXIT_FAILURE
, "couldn't open file `%s': %s",
900 fn
, strerror(errno
));
904 dfn
= argv
[optind
++];
906 ie
= initdec(ieo
, fp
, vrfbdry
, &v
);
908 /* --- Read the header chunk --- */
910 sig_readheader(ie
, &s
, diechoke
, "malformed header");
912 if (((s
.f
^ v
.f
) & v
.m
) != 0)
913 moan("boundary string inconsistent with contents (ignoring)");
915 mcsetup_read(&mc_in
, s
.f
, &ie
, fn
, dfn
);
917 /* --- Prepare the output stuff --- */
919 if (!of
&& !mf
) of
= "-";
921 f
= (f
& fm
) | (s
.f
& ~fm
);
928 mcsetup_writenull(&mc_out
);
930 if (strcmp(of
, "-") == 0)
932 else if ((ofp
= fopen(of
, oeo
->wmode
)) == 0) {
933 die(EXIT_FAILURE
, "couldn't open file `%s' for output: %s",
934 of
, strerror(errno
));
936 oe
= initenc(oeo
, ofp
,
937 (f
& F_DETACH
) ?
"CATSIGN SIGNATURE" :
938 (f
& F_BINARY
) ?
"CATSIGN MESSAGE" :
939 "CATSIGN MESSAGE HEADER");
940 sig_writeheader(oe
, &s
);
941 mcsetup_write(&mc_out
, f
, &oe
);
945 if (strcmp(mf
, "-") == 0)
947 else if ((mfp
= fopen(mf
, (f
& F_BINARY
) ?
"wb" : "w")) == 0) {
948 die(EXIT_FAILURE
, "couldn't open file `%s' for output: %s",
949 mf
, strerror(errno
));
953 /* --- Wade through the message body --- */
956 n
= mc_in
.read(&mc_in
, bb
);
958 mc_out
.write(&mc_out
, bb
, n
);
959 if (mfp
) fwrite(bb
, 1, n
, mfp
);
961 mc_endread(&mc_in
, ieo
, &ie
);
962 if (of
) mc_endwrite(&mc_out
, oeo
, &oe
);
964 /* --- Write the signature --- */
966 if (!(sf
& F_DETACH
))
969 sig_writesig(oe
, &s
);
970 oe
->ops
->encdone(oe
);
973 /* --- All done --- */
975 ie
->ops
->decdone(ie
);
976 if (ferror(fp
) || fclose(fp
))
977 die(EXIT_FAILURE
, "error reading input signature: %s", strerror(errno
));
978 if (ofp
&& (fflush(ofp
) || ferror(ofp
) || fclose(ofp
)))
979 die(EXIT_FAILURE
, "error writing output signature: %s", strerror(errno
));
980 if (mfp
&& (fflush(mfp
) || ferror(mfp
) || fclose(mfp
)))
981 die(EXIT_FAILURE
, "error writing output message: %s", strerror(errno
));
988 static void NORETURN
infochoke(const char *m
, void *p
)
991 printf("BAD %s: %s\n", v
->what
, m
);
995 static void infokeyreport(const char *file
, int line
,
996 const char *err
, void *p
)
999 static int info(int argc
, char *argv
[])
1001 const char *ef
= "binary";
1002 vrfctx v
= { 0, 0, 1 };
1015 static const struct option opt
[] = {
1016 { "armour", 0, 0, 'a' },
1017 { "armor", 0, 0, 'a' },
1018 { "format", OPTF_ARGREQ
, 0, 'f' },
1019 { "gmt", 0, 0, 'u' },
1020 { "utc", 0, 0, 'u' },
1023 i
= mdwopt(argc
, argv
, "f:au", opt
, 0, 0, 0);
1026 case 'a': ef
= "pem"; break;
1027 case 'f': ef
= optarg
; break;
1028 case 'u': v
.f
|= F_UTC
; break;
1029 default: v
.f
|= F_BOGUS
; break;
1032 if (argc
- optind
> 1 || (v
.f
& F_BOGUS
))
1033 die(EXIT_FAILURE
, "Usage: info [-OPTIONS] [FILE]");
1035 if ((eo
= getenc(ef
)) == 0)
1036 die(EXIT_FAILURE
, "encoding `%s' not found", ef
);
1040 else if (strcmp(argv
[optind
], "-") == 0) {
1043 } else if ((fp
= fopen(argv
[optind
], eo
->rmode
)) == 0) {
1044 die(EXIT_FAILURE
, "couldn't open file `%s': %s",
1045 argv
[optind
], strerror(errno
));
1049 if (key_open(&kf
, keyring
, KOPEN_READ
, infokeyreport
, 0)) {
1050 printf("NOTE can't open keyring `%s'\n", keyring
);
1053 e
= initdec(eo
, fp
, vrfbdry
, &v
);
1055 v
.what
= "malformed header";
1056 sig_readheader(e
, &s
, infochoke
, &v
);
1058 printf("INFO flags %sdetach %sbinary\n",
1059 (s
.f
& F_DETACH
) ?
"" : "!",
1060 (s
.f
& F_BINARY
) ?
"" : "!");
1062 if (((s
.f
^ v
.f
) & v
.m
) != 0) {
1063 printf("WARN boundary string inconsistent with contents\n");
1064 printf("INFO expected-flags");
1065 if (v
.m
& F_DETACH
) printf(" %sdetach", (v
.f
& F_DETACH
) ?
"" : "!");
1066 if (v
.m
& F_BINARY
) printf(" %sbinary", (v
.f
& F_BINARY
) ?
"" : "!");
1071 tm
= (v
.f
& F_UTC
) ?
gmtime(&s
.t
) : localtime(&s
.t
);
1072 strftime(bb
, sizeof(bb
), "%Y-%m-%d %H:%M:%S %Z", tm
);
1073 printf("INFO date %s\n", bb
);
1075 if (keyring
&& (k
= key_byid(&kf
, s
.keyid
)) != 0) {
1076 dstr_reset(&d
); key_fulltag(k
, &d
);
1077 printf("INFO key %s\n", d
.buf
);
1079 printf("INFO unknown-key %08lx\n", (unsigned long)s
.keyid
);
1081 if (keyring
) key_close(&kf
);
1087 /*----- Main code ---------------------------------------------------------*/
1091 listtab[i].name, listtab[i].name) \
1092 LI("Signature schemes", sig, \
1093 sigtab[i].name, sigtab[i].name) \
1094 LI("Encodings", enc, \
1095 enctab[i].name, enctab[i].name) \
1096 LI("Hash functions", hash, \
1097 ghashtab[i], ghashtab[i]->name)
1099 MAKELISTTAB(listtab
, LISTS
)
1101 int cmd_show(int argc
, char *argv
[])
1103 return (displaylists(listtab
, argv
+ 1));
1106 static int cmd_help(int, char **);
1108 static cmd cmdtab
[] = {
1109 { "help", cmd_help
, "help [COMMAND...]" },
1110 { "show", cmd_show
, "show [ITEM...]" },
1114 "sign [-adptC] [-k TAG] [-f FORMAT] [-o OUTPUT] [FILE]", "\
1117 -a, --armour Same as `-f pem'.\n\
1118 -b, --binary Treat the input message as binary data.\n\
1119 -d, --detach Produce a detached signature.\n\
1120 -f, --format=FORMAT Encode as FORMAT.\n\
1121 -k, --key=TAG Use public encryption key named by TAG.\n\
1122 -o, --output=FILE Write output to FILE.\n\
1123 -p, --progress Show progress on large files.\n\
1124 -t, --text Canonify input message as a text file.\n\
1125 -C, --nocheck Don't check the private key.\n\
1128 "verify [-abpquvC] [-f FORMAT] [-k TAG] [-o OUTPUT]\n\t\
1129 [FILE [MESSAGE]]", "\
1132 -a, --armour Same as `-f pem'.\n\
1133 -b, --buffer Buffer message until signature is verified.\n\
1134 -f, --format=FORMAT Decode as FORMAT.\n\
1135 -k, --key=TAG Require that the message be signed by key TAG.\n\
1136 -o, --output=FILE Write message to FILE.\n\
1137 -p, --progress Show progress on large files.\n\
1138 -q, --quiet Produce fewer messages.\n\
1139 -t, --freshtime=TIME Only accept signatures made after this time.\n\
1140 -u, --utc Show dates in UTC rather than local time.\n\
1141 -v, --verbose Produce more verbose messages.\n\
1142 -C, --nocheck Don't check the public key.\n\
1145 "info [-au] [-f FORMAT] [FILE]", "\
1148 -a, --armour Same as `-f pem'.\n\
1149 -f, --format=FORMAT Decode as FORMAT.\n\
1150 -u, --utc Show dates in UTC rather than local time.\n\
1153 "format [-apuADE] [-f FORMAT] [-F format] [-m FILE] [-o FILE]\n\t\
1154 [FILE [MESSAGE]]", "\
1157 -a, --armour-in Same as `-f pem'.\n\
1158 -A, --armour-out Same as `-F pem'.\n\
1159 -D, --detach Create detached signature.\n\
1160 -E, --embed Create signature with embedded message.\n\
1161 -f, --format-in=FORMAT Decode input as FORMAT.\n\
1162 -F, --format-out=FORMAT Encode output as FORMAT.\n\
1163 -m, --message=FILE Write message to FILE.\n\
1164 -o, --output=FILE Write new signature to FILE.\n\
1165 -p, --progress Show progress on large files.\n\
1168 }; /* " Emacs seems confused. */
1170 static int cmd_help(int argc
, char **argv
)
1172 sc_help(cmdtab
, stdout
, argv
+ 1);
1176 void version(FILE *fp
)
1178 pquis(fp
, "$, Catacomb version " VERSION
"\n");
1181 static void usage(FILE *fp
)
1183 pquis(fp
, "Usage: $ [-k KEYRING] COMMAND [ARGS]\n");
1186 void help_global(FILE *fp
)
1190 Sign and verify data.\n\
1192 Global command-line options:\n\
1194 -h, --help [COMMAND...] Show this help message, or help for COMMANDs.\n\
1195 -v, --version Show program version number.\n\
1196 -u, --usage Show a terse usage message.\n\
1198 -k, --keyring=FILE Read keys from FILE.\n",
1204 * Arguments: @int argc@ = number of command line arguments
1205 * @char *argv[]@ = vector of command line arguments
1207 * Returns: Zero if successful, nonzero otherwise.
1209 * Use: Encrypts or decrypts files.
1212 int main(int argc
, char *argv
[])
1218 /* --- Initialize the library --- */
1222 rand_noisesrc(RAND_GLOBAL
, &noise_source
);
1223 rand_seed(RAND_GLOBAL
, 160);
1225 /* --- Parse options --- */
1228 static struct option opts
[] = {
1229 { "help", 0, 0, 'h' },
1230 { "version", 0, 0, 'v' },
1231 { "usage", 0, 0, 'u' },
1232 { "keyring", OPTF_ARGREQ
, 0, 'k' },
1235 int i
= mdwopt(argc
, argv
, "+hvu k:", opts
, 0, 0, 0);
1240 sc_help(cmdtab
, stdout
, argv
+ optind
);
1262 if (f
& f_bogus
|| argc
< 1) {
1267 /* --- Dispatch to the correct subcommand handler --- */
1269 return (findcmd(cmdtab
, argv
[0])->cmd(argc
, argv
));
1274 /*----- That's all, folks -------------------------------------------------*/