ghash.h: Fix GH_HASHSTR64*.
[u/mdw/catacomb] / catsign.c
CommitLineData
fa54fe1e 1/* -*-c-*-
2 *
3 * $Id$
4 *
5 * Sign files
6 *
7 * (c) 2005 Straylight/Edgeware
8 */
9
45c0fd36 10/*----- Licensing notice --------------------------------------------------*
fa54fe1e 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.
45c0fd36 18 *
fa54fe1e 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.
45c0fd36 23 *
fa54fe1e 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 "config.h"
33
34#include <ctype.h>
35#include <errno.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <time.h>
40
41#include <mLib/base64.h>
42#include <mLib/dstr.h>
43#include <mLib/mdwopt.h>
44#include <mLib/quis.h>
45#include <mLib/report.h>
46#include <mLib/sub.h>
47
48#include "buf.h"
49#include "rand.h"
50#include "noise.h"
51#include "mprand.h"
52#include "key.h"
9cea6911 53#include "getdate.h"
fa54fe1e 54#include "cc.h"
55
56#include "ectab.h"
57#include "ptab.h"
58
fa54fe1e 59/*----- Static variables --------------------------------------------------*/
60
61static const char *keyring = "keyring";
62
63/*----- Data formats ------------------------------------------------------*
64 *
65 * Our crypto stuff is split into three parts: a header describing the key,
66 * the message itself, and the signature. In a detached signature, the
67 * message is separate, and the header and signature are combined into one
68 * encoded chunk. In an attached signature, the message is included. There
69 * are two forms of message: binary and text. Binary messages are divided
70 * into chunks, each preceded by a 2-octet length, and terminated by a
71 * zero-length chunk. Text messages are byte-stuffed, as for RFC821, and
72 * trailing whitespace before a newline is ignored.
73 */
74
75typedef struct sigmsg {
76 unsigned f; /* Flags */
77#define F_DETACH 1u
78#define F_BINARY 2u
79#define F_SIGMASK 3u
80#define F_HASHMASK (F_BINARY)
81 uint32 keyid; /* Key identifier */
82 time_t t; /* When the signature was made */
83 dstr kh; /* Key fingerprint (sanity check) */
84 dstr sig; /* Signature */
85 sig *s; /* Signature algorithm */
86} sigmsg;
87
88#define F_MIDLINE 16u
89#define F_EOF 32u
90#define F_NOCLOSE 64u
91#define F_BOGUS 128u
92#define F_BUFFER 256u
93#define F_UTC 512u
946c3f72 94#define F_NOCHECK 1024u
fa54fe1e 95
96/*----- Chunk I/O ---------------------------------------------------------*/
97
98static void chunk_write(enc *e, const void *p, size_t n)
99{
100 octet b[2];
101
102 assert(n <= 0xffff);
103 STORE16(b, n);
104 if (e->ops->write(e, b, 2) || e->ops->write(e, p, n))
105 die(EXIT_FAILURE, "error writing output: %s", strerror(errno));
106}
107
108static size_t chunk_read(enc *e, void *p)
109{
110 octet b[2];
111 size_t n;
112
113 if (e->ops->read(e, b, 2) != 2)
114 goto err;
115 n = LOAD16(b);
116 if (n && e->ops->read(e, p, n) != n)
117 goto err;
118 return (n);
119
120err:
121 if (!errno) die(EXIT_FAILURE, "unexpected end-of-file on input");
122 else die(EXIT_FAILURE, "error reading input: %s", strerror(errno));
123 return (0);
124}
125
126/*----- Message canonification --------------------------------------------*/
127
128#define MSGBUFSZ 65536
129#define MSGBUFTHRESH 32768
130
131typedef struct msgcanon {
132 unsigned f;
133 FILE *fp;
134 enc *e;
135 size_t (*read)(struct msgcanon *, void *);
136 void (*write)(struct msgcanon *, const void *, size_t);
137 void (*close)(struct msgcanon *);
138} msgcanon;
139
140#define MC_INIT { 0 }
141
142static size_t textread(msgcanon *m, void *bp)
143{
144 size_t n = 0, nsp = 0;
145 int ch;
146 char *b = bp;
147 unsigned f = m->f;
148
149 if (f & F_EOF) return (0);
150 for (;;) {
151 if ((ch = getc(m->fp)) == EOF) goto eof;
152 if (!(f & (F_DETACH | F_MIDLINE)) && ch == '.') {
153 ch = getc(m->fp);
154 if (ch == '\n') goto eof;
155 }
156 if (ch == '\n') {
157 n -= nsp; nsp = 0;
158 if (n >= MSGBUFTHRESH) goto full;
159 b[n++] = ch;
160 f &= ~F_MIDLINE;
161 } else if (isspace(ch)) {
162 f |= F_MIDLINE;
163 if (n >= MSGBUFSZ) goto full;
164 b[n++] = ch; nsp++;
165 } else {
166 f |= F_MIDLINE;
167 nsp = 0;
168 if (n >= MSGBUFTHRESH) goto full;
169 b[n++] = ch;
170 }
171 }
172eof:
173 f |= F_EOF;
174 goto done;
175full:
176 ungetc(ch, m->fp);
177done:
178 m->f = f;
179 return (n);
180}
181
182static void textwrite(msgcanon *m, const void *bp, size_t n)
183{
184 const char *p = bp, *l = p + n;
185 unsigned f = m->f;
186
187 while (p < l) {
188 if (!(f & (F_MIDLINE | F_DETACH)) &&
189 (*p == '.' || *p == '-'))
190 putc('.', m->fp);
191 if (*p == '\n') f &= ~F_MIDLINE;
192 else f |= F_MIDLINE;
193 putc(*p, m->fp);
194 p++;
195 }
196 m->f = f;
197}
198
199static size_t binreadembed(msgcanon *m, void *bp)
200 { return (chunk_read(m->e, bp)); }
201static size_t binreaddetach(msgcanon *m, void *bp)
202 { return (fread(bp, 1, MSGBUFSZ, m->fp)); }
203
204static void binwriteembed(msgcanon *m, const void *bp, size_t n)
205 { chunk_write(m->e, bp, n); }
206
207static void nullwrite(msgcanon *m, const void *bp, size_t n) { ; }
208
209static void mcsetup_readfile(msgcanon *m, unsigned f, const char *fn)
210{
211 m->f = F_DETACH | (f & F_BINARY);
212 if (!fn || strcmp(fn, "-") == 0) {
213 m->fp = stdin;
214 m->f |= F_NOCLOSE;
215 } else if ((m->fp = fopen(fn, (f & F_BINARY) ? "rb" : "r")) == 0)
216 die(EXIT_FAILURE, "couldn't open file `%s': %s", fn, strerror(errno));
217 m->read = (f & F_BINARY) ? binreaddetach : textread;
218}
219
220static void mcsetup_writenull(msgcanon *m) { m->write = nullwrite; }
221
222static void mcsetup_read(msgcanon *m, unsigned f, enc **ee, const char *dfn)
223{
224 enc *e = *ee;
225
226 m->f = f | F_NOCLOSE;
227
228 if (dfn && !(f & F_DETACH)) die(EXIT_FAILURE, "signature is not detached");
229 if (f & F_BINARY) {
230 if (!(f & F_DETACH)) {
231 m->e = e;
232 *ee = 0;
233 m->fp = e->fp;
234 m->read = binreadembed;
235 } else {
236 m->read = binreaddetach;
237 if (!dfn || strcmp(dfn, "-") == 0)
238 m->fp = stdin;
239 else if ((m->fp = fopen(dfn, "rb")) == 0)
240 die(EXIT_FAILURE, "can't open `%s': %s", dfn, strerror(errno));
241 else
242 m->f &= ~F_NOCLOSE;
243 }
244 } else {
245 m->read = textread;
246 if (!(f & F_DETACH)) {
247 if (e->ops->decdone(e) || ferror(e->fp))
248 die(EXIT_FAILURE, "error at end of signature header");
249 m->fp = e->fp;
250 e->ops->destroy(e);
251 *ee = 0;
252 } else if (!dfn || strcmp(dfn, "-") == 0)
253 m->fp = stdin;
254 else if ((m->fp = fopen(dfn, "r")) == 0)
255 die(EXIT_FAILURE, "can't read file `%s': %s", dfn, strerror(errno));
256 else
257 m->f &= ~F_NOCLOSE;
258 }
259}
260
261static void mcsetup_write(msgcanon *m, unsigned f, enc **ee)
262{
263 enc *e = *ee;
264
265 m->f = f | F_NOCLOSE;
266
267 if (f & F_DETACH)
268 m->write = nullwrite;
269 else if (f & F_BINARY) {
270 m->e = e;
271 *ee = 0;
272 m->fp = e->fp;
273 m->write = binwriteembed;
274 } else {
275 if (e->ops->encdone(e) || ferror(e->fp))
276 die(EXIT_FAILURE, "error at end of signature header");
277 m->fp = e->fp;
278 e->ops->destroy(e);
279 *ee = 0;
280 m->write = textwrite;
281 }
282}
283
284static void mc_endread(msgcanon *m, const encops *eops, enc **ee)
285{
286 if (!(m->f & F_DETACH)) {
287 if (m->f & F_BINARY)
288 *ee = m->e;
289 else
290 *ee = initdec(eops, m->fp, checkbdry, "CATSIGN SIGNATURE");
291 }
292 if (m->fp && !(m->f & F_NOCLOSE)) {
293 if (ferror(m->fp) || fclose(m->fp))
294 die(EXIT_FAILURE, "error closing message file: %s", strerror(errno));
45c0fd36 295 }
fa54fe1e 296}
297
298static void mc_endwrite(msgcanon *m, const encops *eops, enc **ee)
299{
300 if (!(m->f & F_BINARY)) {
301 if (m->f & F_MIDLINE) putc('\n', m->fp);
302 if (!(m->f & F_DETACH)) {
303 putc('.', m->fp); putc('\n', m->fp);
304 *ee = initenc(eops, m->fp, "CATSIGN SIGNATURE");
305 }
306 } else if (!(m->f & F_DETACH)) {
307 chunk_write(m->e, 0, 0);
308 *ee = m->e;
309 }
310 if (m->fp && !(m->f & F_NOCLOSE)) {
311 if (fflush(m->fp) || ferror(m->fp) || fclose(m->fp))
312 die(EXIT_FAILURE, "error closing message file: %s", strerror(errno));
45c0fd36 313 }
fa54fe1e 314}
315
316/*----- Signature reading and writing -------------------------------------*/
317
318static void sig_init(sigmsg *s, unsigned f, uint32 keyid)
319{
320 s->f = f & F_SIGMASK;
321 s->keyid = keyid;
322 time(&s->t);
323 s->s = 0;
324 dstr_create(&s->kh);
325 dstr_create(&s->sig);
326}
327
328static void sig_destroy(sigmsg *s)
329{
330 dstr_destroy(&s->kh);
331 dstr_destroy(&s->sig);
332 if (s->s) freesig(s->s);
333}
334
335static void sigtobuffer(sigmsg *s, buf *b, int hashp)
336{
337 kludge64 t;
338
339 ASSIGN64(t, s->t);
340 if (hashp) buf_putu16(b, s->f & F_HASHMASK);
341 else buf_putu16(b, s->f & F_SIGMASK);
342 buf_putu32(b, s->keyid);
343 buf_putu32(b, HI64(t));
344 buf_putu32(b, LO64(t));
f387fcb1 345 buf_putdstr16(b, &s->kh);
fa54fe1e 346 assert(BOK(b));
347}
348
349static void dohash(ghash *h, const void *p, size_t n)
350{
351/* trace_block(1, "hashing", p, n); */
352 GH_HASH(h, p, n);
353}
354
355static void sig_hash(sigmsg *s)
356{
357 octet bb[16384];
358 buf b;
359
360 buf_init(&b, bb, sizeof(bb));
361 sigtobuffer(s, &b, 1);
362 dohash(s->s->h, BBASE(&b), BLEN(&b));
363}
364
365static void keyhash(key *k, sig *s, dstr *d)
366{
367 ghash *h;
368 key_filter kf;
369
02dfbd5b 370 h = GH_INIT(s->ch);
fa54fe1e 371 kf.f = KCAT_PUB;
372 kf.m = KF_CATMASK;
373 key_fingerprint(k, h, &kf);
374 dstr_ensure(d, GH_CLASS(h)->hashsz);
375 GH_DONE(h, d->buf + d->len);
376 d->len += GH_CLASS(h)->hashsz;
377 GH_DESTROY(h);
378}
379
380static void sig_writeheader(enc *e, sigmsg *s)
381{
382 octet bb[16384];
383 buf b;
384
385 buf_init(&b, bb, sizeof(bb));
386 sigtobuffer(s, &b, 0);
387 chunk_write(e, BBASE(&b), BLEN(&b));
388}
389
390static void sig_writesig(enc *e, sigmsg *s)
391 { chunk_write(e, s->sig.buf, s->sig.len); }
392
393static void diechoke(const char *m, void *p)
394 { die(EXIT_FAILURE, "%s%s%s", p, p ? ": " : "", m); }
395
396static void sig_readheader(enc *e, sigmsg *s,
397 void (*choke)(const char *, void *), void *p)
398{
399 uint16 f;
400 octet bb[MSGBUFSZ];
401 uint32 x, y;
402 kludge64 t;
403 buf b;
404 size_t n;
405
406 n = chunk_read(e, bb);
407 buf_init(&b, bb, n);
408 if (buf_getu16(&b, &f)) choke("missing flags", p);
409 if (buf_getu32(&b, &x)) choke("missing keyid", p);
410 sig_init(s, f, x);
411 if (buf_getu32(&b, &x) || buf_getu32(&b, &y))
412 choke("missing datestamp", p);
413 SET64(t, x, y); s->t = GET64(time_t, t);
f387fcb1 414 if (buf_getdstr16(&b, &s->kh))
fa54fe1e 415 choke("missing key hash", p);
416 if (BLEFT(&b))
417 choke("junk at end", p);
418}
419
420static void sig_readsig(enc *e, sigmsg *s)
421{
422 octet bb[MSGBUFSZ];
423 size_t n;
424
425 n = chunk_read(e, bb);
426 dstr_putm(&s->sig, bb, n);
427}
428
429/*----- Signing -----------------------------------------------------------*/
430
431static int sign(int argc, char *argv[])
432{
433 const char *ef = "binary", *fn = 0, *of = 0, *kn = "ccsig", *err;
434 unsigned f = 0;
435 key_file kf;
436 key *k;
437 sigmsg s;
438 FILE *ofp = 0;
439 int i;
440 char bb[MSGBUFSZ];
441 size_t n;
45c0fd36 442 dstr d = DSTR_INIT;
fa54fe1e 443 const encops *eo;
444 msgcanon mc_in = MC_INIT, mc_out = MC_INIT;
445 enc *e;
446
447 for (;;) {
448 static const struct option opt[] = {
449 { "armour", 0, 0, 'a' },
450 { "armor", 0, 0, 'a' },
451 { "binary", 0, 0, 'b' },
452 { "detach", 0, 0, 'd' },
453 { "key", OPTF_ARGREQ, 0, 'k' },
454 { "format", OPTF_ARGREQ, 0, 'f' },
455 { "output", OPTF_ARGREQ, 0, 'o' },
456 { "text", 0, 0, 't' },
946c3f72 457 { "nocheck", 0, 0, 'C' },
fa54fe1e 458 { 0, 0, 0, 0 }
459 };
946c3f72 460 i = mdwopt(argc, argv, "k:f:o:abdtC", opt, 0, 0, 0);
fa54fe1e 461 if (i < 0) break;
462 switch (i) {
463 case 'k': kn = optarg; break;
464 case 'f': ef = optarg; break;
465 case 'o': of = optarg; break;
466 case 'a': ef = "pem"; break;
467 case 't': f &= ~F_BINARY; break;
468 case 'b': f |= F_BINARY; break;
469 case 'd': f |= F_DETACH; break;
946c3f72 470 case 'C': f |= F_NOCHECK; break;
fa54fe1e 471 default: f |= F_BOGUS; break;
472 }
473 }
474 if (argc - optind > 1 || (f & F_BOGUS))
475 die(EXIT_FAILURE, "Usage: sign [-OPTIONS] [FILE]");
476
213e565f 477 if (key_open(&kf, keyring, KOPEN_READ, key_moan, 0))
fa54fe1e 478 die(EXIT_FAILURE, "can't open keyring `%s'", keyring);
479 if ((k = key_bytag(&kf, kn)) == 0)
480 die(EXIT_FAILURE, "key `%s' not found", kn);
481
482 if ((eo = getenc(ef)) == 0)
483 die(EXIT_FAILURE, "encoding `%s' not found", ef);
484
485 fn = (optind >= argc) ? 0 : argv[optind++];
486
487 if (!of || strcmp(of, "-") == 0)
488 ofp = stdout;
489 else if ((ofp = fopen(of, eo->wmode)) == 0) {
490 die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
491 ofp, strerror(errno));
492 }
493
494 /* --- Start the work --- */
495
496 sig_init(&s, f, k->id);
497 dstr_reset(&d);
498 key_fulltag(k, &d);
499 s.s = getsig(k, "ccsig", 1);
946c3f72 500 if (!(f & F_NOCHECK) && (err = s.s->ops->check(s.s)) != 0)
fa54fe1e 501 moan("key %s fails check: %s", d.buf, err);
502 keyhash(k, s.s, &s.kh);
503 e = initenc(eo, ofp,
504 (f & F_DETACH) ? "CATSIGN SIGNATURE" :
505 (f & F_BINARY) ? "CATSIGN MESSAGE" :
506 "CATSIGN MESSAGE HEADER");
507 sig_writeheader(e, &s);
508
509 /* --- Hash the message --- */
510
511 mcsetup_readfile(&mc_in, f, fn);
512 mcsetup_write(&mc_out, f, &e);
513 sig_hash(&s);
514 for (;;) {
515 n = mc_in.read(&mc_in, bb);
516 if (!n) break;
517 dohash(s.s->h, bb, n);
518 mc_out.write(&mc_out, bb, n);
519 }
520 mc_endread(&mc_in, 0, 0);
521 mc_endwrite(&mc_out, eo, &e);
522
523 /* --- Write the signature --- */
524
525 if (s.s->ops->doit(s.s, &s.sig))
526 die(EXIT_FAILURE, "signature failed");
527 sig_writesig(e, &s);
528 e->ops->encdone(e);
529 if (fflush(ofp) || ferror(ofp) || fclose(ofp))
530 die(EXIT_FAILURE, "error writing signature: %s", strerror(errno));
531
532 /* --- All done --- */
533
534 freeenc(e);
535 key_close(&kf);
536 sig_destroy(&s);
537 dstr_destroy(&d);
538 return (0);
539}
540
541/*----- Verifying ---------------------------------------------------------*/
542
543typedef struct vrfctx {
544 unsigned f, m;
545 int verb;
546 const char *what;
547} vrfctx;
548
549static int vrfbdry(const char *b, void *p)
550{
551 vrfctx *v = p;
552
553 if (strcmp(b, "CATSIGN MESSAGE") == 0) {
554 v->f |= F_BINARY;
555 v->m |= F_BINARY | F_DETACH;
556 return (1);
557 } else if (strcmp(b, "CATSIGN MESSAGE HEADER") == 0) {
558 v->m |= F_BINARY | F_DETACH;
559 return (1);
560 } else if (strcmp(b, "CATSIGN SIGNATURE") == 0) {
561 v->f |= F_DETACH;
562 v->m |= F_DETACH;
563 return (1);
564 } else
565 return (0);
566}
567
568static void vrfchoke(const char *m, void *p)
569{
570 vrfctx *v = p;
571 if (v->verb) printf("FAIL %s: %s\n", v->what, m);
572 exit(EXIT_FAILURE);
573}
574
575static int verify(int argc, char *argv[])
576{
577 const char *ef = "binary", *of = 0, *dfn = 0, *kn = 0, *err;
578 vrfctx v = { 0, 0, 1 };
579 key_file kf;
580 key *k, *kk = 0;
581 sigmsg s;
582 FILE *fp, *ofp = 0, *rfp = 0;
583 struct tm *tm;
584 int i;
585 char bb[MSGBUFSZ];
586 size_t n;
9cea6911 587 time_t t_fresh = 0;
fa54fe1e 588 dstr d = DSTR_INIT, dd = DSTR_INIT;
589 const encops *eo;
590 msgcanon mc_in = MC_INIT;
591 enc *e;
592
593 for (;;) {
594 static const struct option opt[] = {
595 { "armour", 0, 0, 'a' },
596 { "armor", 0, 0, 'a' },
597 { "buffer", 0, 0, 'b' },
598 { "key", OPTF_ARGREQ, 0, 'k' },
599 { "format", OPTF_ARGREQ, 0, 'f' },
600 { "output", OPTF_ARGREQ, 0, 'o' },
601 { "quiet", 0, 0, 'q' },
602 { "utc", 0, 0, 'u' },
9cea6911 603 { "fresh-time", 0, 0, 't' },
fa54fe1e 604 { "gmt", 0, 0, 'u' },
605 { "verbose", 0, 0, 'v' },
946c3f72 606 { "nocheck", 0, 0, 'C' },
fa54fe1e 607 { 0, 0, 0, 0 }
608 };
cfe65b26 609 i = mdwopt(argc, argv, "k:f:o:abqt:uvC", opt, 0, 0, 0);
fa54fe1e 610 if (i < 0) break;
611 switch (i) {
612 case 'a': ef = "pem"; break;
613 case 'b': v.f |= F_BUFFER; break;
614 case 'k': kn = optarg; break;
615 case 'f': ef = optarg; break;
616 case 'o': of = optarg; break;
617 case 'u': v.f |= F_UTC; break;
946c3f72 618 case 'C': v.f |= F_NOCHECK; break;
9cea6911 619 case 't':
620 if (strcmp(optarg, "always") == 0) t_fresh = 0;
45c0fd36 621 else if ((t_fresh = get_date(optarg, 0)) < 0)
9cea6911 622 die(EXIT_FAILURE, "bad freshness time");
623 break;
fa54fe1e 624 case 'q': if (v.verb > 0) v.verb--; break;
625 case 'v': if (v.verb < 10) v.verb++; break;
626 default: v.f |= F_BOGUS; break;
627 }
628 }
629 if (argc - optind > 2 || (v.f & F_BOGUS))
630 die(EXIT_FAILURE, "Usage: verify [-OPTIONS] [FILE [MESSAGE]]");
631
632 if ((eo = getenc(ef)) == 0)
633 die(EXIT_FAILURE, "encoding `%s' not found", ef);
634
635 if (optind >= argc)
636 fp = stdin;
637 else if (strcmp(argv[optind], "-") == 0) {
638 fp = stdin;
639 optind++;
640 } else if ((fp = fopen(argv[optind], eo->rmode)) == 0) {
641 die(EXIT_FAILURE, "couldn't open file `%s': %s",
642 argv[optind], strerror(errno));
643 } else
644 optind++;
645
213e565f 646 if (key_open(&kf, keyring, KOPEN_READ, key_moan, 0))
fa54fe1e 647 die(EXIT_FAILURE, "can't open keyring `%s'", keyring);
648 if (kn && (kk = key_bytag(&kf, kn)) == 0)
649 die(EXIT_FAILURE, "key `%s' not found", kn);
650
651 e = initdec(eo, fp, vrfbdry, &v);
652
653 /* --- Read the header chunk --- */
654
655 v.what = "malformed header";
656 sig_readheader(e, &s, vrfchoke, &v);
657
658 if (((s.f ^ v.f) & v.m) != 0) {
659 if (v.verb) printf("FAIL boundary string inconsistent with contents\n");
660 exit(EXIT_FAILURE);
661 }
662 v.f |= s.f;
663
664 if ((k = key_byid(&kf, s.keyid)) == 0) {
665 if (v.verb) printf("FAIL key id %08lx not found\n",
666 (unsigned long)s.keyid);
667 exit(EXIT_FAILURE);
668 }
669 if (kk && k->id != kk->id) {
670 if (v.verb) {
671 dstr_reset(&d); key_fulltag(k, &d);
672 dstr_reset(&dd); key_fulltag(kk, &dd);
673 printf("FAIL signing key is %s; expected key %s\n", d.buf, dd.buf);
674 }
675 exit(EXIT_FAILURE);
676 }
677
678 s.s = getsig(k, "ccsig", 0);
679 dstr_reset(&d); key_fulltag(k, &d);
946c3f72 680 if (!(v.f & F_NOCHECK) && v.verb && (err = s.s->ops->check(s.s)) != 0)
fa54fe1e 681 printf("WARN verification key %s fails check: %s\n", d.buf, err);
682
683 dstr_reset(&dd); keyhash(k, s.s, &dd);
684 if (dd.len != s.kh.len || memcmp(dd.buf, s.kh.buf, dd.len) != 0) {
685 if (v.verb) printf("FAIL key hash mismatch\n");
686 exit(EXIT_FAILURE);
45c0fd36 687 }
fa54fe1e 688
689 /* --- Now a merry dance --- */
690
691 if (v.f & F_DETACH)
692 sig_readsig(e, &s);
693 if (optind < argc)
694 dfn = argv[optind++];
695 mcsetup_read(&mc_in, v.f, &e, dfn);
696
697 if (!of && (v.f & F_DETACH)) {
698 rfp = ofp = 0;
699 v.f &= ~F_BUFFER;
700 } else if (!of || strcmp(of, "-") == 0) {
701 v.f |= F_BUFFER;
702 ofp = stdout;
703 }
704 if (of && !(v.f & F_BUFFER)) {
705 if ((ofp = fopen(of, (v.f & F_BINARY) ? "wb" : "w")) == 0) {
706 die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
707 of, strerror(errno));
708 }
709 rfp = ofp;
710 } else if ((rfp = tmpfile()) == 0)
711 die(EXIT_FAILURE, "couldn't create temporary file: %s", strerror(errno));
712
713 /* --- Read the message and verify the signature --- */
714
715 sig_hash(&s);
716 for (;;) {
717 n = mc_in.read(&mc_in, bb);
718 if (!n) break;
719 dohash(s.s->h, bb, n);
720 if (rfp) fwrite(bb, 1, n, rfp);
721 }
722 mc_endread(&mc_in, eo, &e);
723 if (!(v.f & F_DETACH))
724 sig_readsig(e, &s);
725 if (rfp && (ferror(rfp) || fflush(rfp))) {
726 if (v.verb) printf("FAIL error writing message: %s\n", strerror(errno));
727 exit(EXIT_FAILURE);
728 }
729
730 /* --- Check the signature --- */
731
732 if (s.s->ops->doit(s.s, &s.sig)) {
733 if (v.verb) printf("FAIL signature verification failed\n");
734 exit(EXIT_FAILURE);
735 }
9cea6911 736 if (t_fresh && s.t < t_fresh) {
737 if (v.verb) printf("FAIL signature is stale\n");
738 exit(EXIT_FAILURE);
739 }
740 if (s.t > time(0)) {
741 if (v.verb) printf("FAIL signature timestamp in the future\n");
742 exit(EXIT_FAILURE);
743 }
fa54fe1e 744 if (v.verb) {
745 tm = (v.f & F_UTC) ? gmtime(&s.t) : localtime(&s.t);
746 strftime(bb, sizeof(bb), "%Y-%m-%d %H:%M:%S %Z", tm);
747 printf("INFO good-signature %s\n", d.buf);
748 printf("INFO date %s\n", bb);
749 }
750
751 /* --- Unbuffer buffered input --- */
752
753 if (v.f & F_BUFFER) {
754 if (!ofp && (ofp = fopen(of, "wb")) == 0) {
755 die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
756 of, strerror(errno));
757 }
758 rewind(rfp);
759 if (v.verb && ofp == stdout) printf("DATA\n");
760 for (;;) {
761 n = fread(bb, 1, sizeof(bb), rfp);
762 if (!n) break;
763 if (fwrite(bb, 1, n, ofp) < n)
764 die(EXIT_FAILURE, "error writing output: %s", strerror(errno));
765 }
766 if (ferror(rfp) || fclose(rfp))
767 die(EXIT_FAILURE, "error unbuffering output: %s", strerror(errno));
768 }
769 if (ofp && (fflush(ofp) || ferror(ofp) || fclose(ofp)))
45c0fd36 770 die(EXIT_FAILURE, "error writing output: %s", strerror(errno));
fa54fe1e 771
772 /* --- Tidy up --- */
773
774 e->ops->decdone(e);
775 if (v.verb && ofp != stdout)
776 printf("OK verified successfully\n");
777 freeenc(e);
778 key_close(&kf);
779 sig_destroy(&s);
780 dstr_destroy(&d);
781 dstr_destroy(&dd);
45c0fd36 782 return (0);
fa54fe1e 783}
784
785/*----- Reformatting ------------------------------------------------------*/
786
787static int format(int argc, char *argv[])
788{
789 const char *ief = "binary", *oef = "binary";
790 const char *dfn = 0, *of = 0, *mf = 0;
791 sigmsg s;
792 FILE *fp, *ofp = 0, *mfp = 0;
793 int i;
794 size_t n;
795 msgcanon mc_in = MC_INIT, mc_out = MC_INIT;
796 char bb[MSGBUFSZ];
797 vrfctx v = { 0, 0, 1 };
798 unsigned f = 0, fm = ~F_SIGMASK, sf;
799 const encops *ieo, *oeo;
800 enc *ie, *oe;
801
802 for (;;) {
803 static const struct option opt[] = {
804 { "armour-in", 0, 0, 'a' },
805 { "armor-in", 0, 0, 'a' },
806 { "armour-out", 0, 0, 'A' },
807 { "armor-out", 0, 0, 'A' },
808 { "detach", 0, 0, 'D' },
809 { "embed", 0, 0, 'E' },
810 { "format-in", OPTF_ARGREQ, 0, 'f' },
811 { "format-out", OPTF_ARGREQ, 0, 'F' },
45c0fd36
MW
812 { "message", OPTF_ARGREQ, 0, 'm' },
813 { "output", OPTF_ARGREQ, 0, 'o' },
fa54fe1e 814 { 0, 0, 0, 0 }
815 };
816 i = mdwopt(argc, argv, "f:F:m:o:aADE", opt, 0, 0, 0);
817 if (i < 0) break;
818 switch (i) {
819 case 'a': ief = "pem"; break;
820 case 'A': oef = "pem"; break;
821 case 'f': ief = optarg; break;
822 case 'F': oef = optarg; break;
823 case 'D': f |= F_DETACH; fm |= F_DETACH; break;
824 case 'E': f &= ~F_DETACH; fm |= F_DETACH; break;
825 case 'm': mf = optarg; break;
826 case 'o': of = optarg; break;
827 default: f |= F_BOGUS; break;
828 }
829 }
830
831 if (argc - optind > 2 || (f & F_BOGUS))
832 die(EXIT_FAILURE, "Usage: format [-OPTIONS] [FILE [MESSAGE]]");
833
834 if ((ieo = getenc(ief)) == 0)
835 die(EXIT_FAILURE, "encoding `%s' not found", ief);
836 if ((oeo = getenc(oef)) == 0)
837 die(EXIT_FAILURE, "encoding `%s' not found", oef);
838
839 if (optind >= argc)
840 fp = stdin;
841 else if (strcmp(argv[optind], "-") == 0) {
842 fp = stdin;
843 optind++;
844 } else if ((fp = fopen(argv[optind], ieo->rmode)) == 0) {
845 die(EXIT_FAILURE, "couldn't open file `%s': %s",
846 argv[optind], strerror(errno));
847 } else
848 optind++;
849
850 if (optind < argc)
851 dfn = argv[optind++];
852
853 ie = initdec(ieo, fp, vrfbdry, &v);
854
855 /* --- Read the header chunk --- */
856
857 sig_readheader(ie, &s, diechoke, "malformed header");
858
859 if (((s.f ^ v.f) & v.m) != 0)
860 moan("boundary string inconsistent with contents (ignoring)");
45c0fd36 861
fa54fe1e 862 mcsetup_read(&mc_in, s.f, &ie, dfn);
863
864 /* --- Prepare the output stuff --- */
865
866 if (!of && !mf) of = "-";
867 sf = s.f;
868 f = (f & fm) | (s.f & ~fm);
869 s.f = f & F_SIGMASK;
870
871 if (sf & F_DETACH)
872 sig_readsig(ie, &s);
873
874 if (!of)
875 mcsetup_writenull(&mc_out);
876 else {
877 if (strcmp(of, "-") == 0)
878 ofp = stdout;
879 else if ((ofp = fopen(of, oeo->wmode)) == 0) {
880 die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
881 of, strerror(errno));
882 }
883 oe = initenc(oeo, ofp,
884 (f & F_DETACH) ? "CATSIGN SIGNATURE" :
885 (f & F_BINARY) ? "CATSIGN MESSAGE" :
886 "CATSIGN MESSAGE HEADER");
887 sig_writeheader(oe, &s);
888 mcsetup_write(&mc_out, f, &oe);
889 }
890
891 if (mf) {
892 if (strcmp(mf, "-") == 0)
893 mfp = stdout;
894 else if ((mfp = fopen(mf, (f & F_BINARY) ? "wb" : "w")) == 0) {
895 die(EXIT_FAILURE, "couldn't open file `%s' for output: %s",
896 mf, strerror(errno));
897 }
898 }
899
900 /* --- Wade through the message body --- */
901
902 for (;;) {
903 n = mc_in.read(&mc_in, bb);
904 if (!n) break;
905 mc_out.write(&mc_out, bb, n);
906 if (mfp) fwrite(bb, 1, n, mfp);
907 }
908 mc_endread(&mc_in, ieo, &ie);
909 if (of) mc_endwrite(&mc_out, oeo, &oe);
910
911 /* --- Write the signature --- */
912
913 if (!(sf & F_DETACH))
914 sig_readsig(ie, &s);
915 if (of) {
916 sig_writesig(oe, &s);
917 oe->ops->encdone(oe);
918 }
919
920 /* --- All done --- */
921
922 ie->ops->decdone(ie);
923 if (ferror(fp) || fclose(fp))
924 die(EXIT_FAILURE, "error reading input signature: %s", strerror(errno));
925 if (ofp && (fflush(ofp) || ferror(ofp) || fclose(ofp)))
926 die(EXIT_FAILURE, "error writing output signature: %s", strerror(errno));
927 if (mfp && (fflush(mfp) || ferror(mfp) || fclose(mfp)))
928 die(EXIT_FAILURE, "error writing output message: %s", strerror(errno));
929 freeenc(ie);
930 if (of) freeenc(oe);
931 sig_destroy(&s);
932 return (0);
933}
934
935static void infochoke(const char *m, void *p)
936{
937 vrfctx *v = p;
938 printf("BAD %s: %s\n", v->what, m);
939 exit(EXIT_FAILURE);
940}
941
942static void infokeyreport(const char *file, int line,
943 const char *err, void *p)
944{ /*whatever*/; }
945
946static int info(int argc, char *argv[])
947{
948 const char *ef = "binary";
949 vrfctx v = { 0, 0, 1 };
950 key_file kf;
951 key *k;
952 sigmsg s;
953 FILE *fp;
954 int i;
955 struct tm *tm;
956 char bb[256];
957 dstr d = DSTR_INIT;
958 const encops *eo;
959 enc *e;
960
961 for (;;) {
962 static const struct option opt[] = {
963 { "armour", 0, 0, 'a' },
964 { "armor", 0, 0, 'a' },
965 { "format", OPTF_ARGREQ, 0, 'f' },
966 { "gmt", 0, 0, 'u' },
967 { "utc", 0, 0, 'u' },
968 { 0, 0, 0, 0 }
969 };
970 i = mdwopt(argc, argv, "f:au", opt, 0, 0, 0);
971 if (i < 0) break;
972 switch (i) {
973 case 'a': ef = "pem"; break;
974 case 'f': ef = optarg; break;
975 case 'u': v.f |= F_UTC; break;
976 default: v.f |= F_BOGUS; break;
977 }
978 }
979 if (argc - optind > 1 || (v.f & F_BOGUS))
980 die(EXIT_FAILURE, "Usage: info [-OPTIONS] [FILE]");
981
982 if ((eo = getenc(ef)) == 0)
983 die(EXIT_FAILURE, "encoding `%s' not found", ef);
984
985 if (optind >= argc)
986 fp = stdin;
987 else if (strcmp(argv[optind], "-") == 0) {
988 fp = stdin;
989 optind++;
990 } else if ((fp = fopen(argv[optind], eo->rmode)) == 0) {
991 die(EXIT_FAILURE, "couldn't open file `%s': %s",
992 argv[optind], strerror(errno));
993 } else
994 optind++;
995
996 if (key_open(&kf, keyring, KOPEN_READ, infokeyreport, 0)) {
997 printf("NOTE can't open keyring `%s'\n", keyring);
998 keyring = 0;
999 }
1000 e = initdec(eo, fp, vrfbdry, &v);
1001
1002 v.what = "malformed header";
1003 sig_readheader(e, &s, infochoke, &v);
1004
1005 printf("INFO flags %sdetach %sbinary\n",
1006 (s.f & F_DETACH) ? "" : "!",
1007 (s.f & F_BINARY) ? "" : "!");
1008
1009 if (((s.f ^ v.f) & v.m) != 0) {
1010 printf("WARN boundary string inconsistent with contents\n");
1011 printf("INFO expected-flags");
1012 if (v.m & F_DETACH) printf(" %sdetach", (v.f & F_DETACH) ? "" : "!");
1013 if (v.m & F_BINARY) printf(" %sbinary", (v.f & F_BINARY) ? "" : "!");
1014 putchar('\n');
1015 }
1016 v.f |= s.f;
1017
1018 tm = (v.f & F_UTC) ? gmtime(&s.t) : localtime(&s.t);
1019 strftime(bb, sizeof(bb), "%Y-%m-%d %H:%M:%S %Z", tm);
1020 printf("INFO date %s\n", bb);
1021
1022 if (keyring && (k = key_byid(&kf, s.keyid)) != 0) {
1023 dstr_reset(&d); key_fulltag(k, &d);
1024 printf("INFO key %s\n", d.buf);
1025 } else
1026 printf("INFO unknown-key %08lx\n", (unsigned long)s.keyid);
1027
1028 if (keyring) key_close(&kf);
1029 dstr_destroy(&d);
1030 sig_destroy(&s);
1031 return (0);
1032}
1033
1034/*----- Main code ---------------------------------------------------------*/
1035
1036#define LISTS(LI) \
1037 LI("Lists", list, \
1038 listtab[i].name, listtab[i].name) \
1039 LI("Signature schemes", sig, \
1040 sigtab[i].name, sigtab[i].name) \
1041 LI("Encodings", enc, \
1042 enctab[i].name, enctab[i].name) \
1043 LI("Hash functions", hash, \
1044 ghashtab[i], ghashtab[i]->name)
1045
1046MAKELISTTAB(listtab, LISTS)
1047
1048int cmd_show(int argc, char *argv[])
1049{
1050 return (displaylists(listtab, argv + 1));
1051}
1052
1053static int cmd_help(int, char **);
1054
1055static cmd cmdtab[] = {
1056 { "help", cmd_help, "help [COMMAND...]" },
1057 { "show", cmd_show, "show [ITEM...]" },
1058 CMD_ENCODE,
1059 CMD_DECODE,
1060 { "sign", sign,
946c3f72 1061 "sign [-adtC] [-k TAG] [-f FORMAT] [-o OUTPUT] [FILE]", "\
fa54fe1e 1062Options:\n\
1063\n\
1064-a, --armour Same as `-f pem'.\n\
1065-b, --binary Treat the input message as binary data.\n\
1066-d, --detach Produce a detached signature.\n\
1067-f, --format=FORMAT Encode as FORMAT.\n\
1068-k, --key=TAG Use public encryption key named by TAG.\n\
1069-o, --output=FILE Write output to FILE.\n\
1070-t, --text Canonify input message as a text file.\n\
946c3f72 1071-C, --nocheck Don't check the private key.\n\
fa54fe1e 1072" },
1073 { "verify", verify,
946c3f72 1074 "verify [-abquvC] [-f FORMAT] [-k TAG] [-o OUTPUT]\n\t\
fa54fe1e 1075[FILE [MESSAGE]]", "\
1076Options:\n\
1077\n\
1078-a, --armour Same as `-f pem'.\n\
1079-b, --buffer Buffer message until signature is verified.\n\
1080-f, --format=FORMAT Decode as FORMAT.\n\
1081-k, --key=TAG Require that the message be signed by key TAG.\n\
1082-o, --output=FILE Write message to FILE.\n\
1083-q, --quiet Produce fewer messages.\n\
9cea6911 1084-t, --freshtime=TIME Only accept signatures made after this time.\n\
fa54fe1e 1085-u, --utc Show dates in UTC rather than local time.\n\
1086-v, --verbose Produce more verbose messages.\n\
946c3f72 1087-C, --nocheck Don't check the public key.\n\
fa54fe1e 1088" },
1089 { "info", info,
1090 "info [-au] [-f FORMAT] [FILE]", "\
1091Options:\n\
1092\n\
1093-a, --armour Same as `-f pem'.\n\
1094-f, --format=FORMAT Decode as FORMAT.\n\
1095-u, --utc Show dates in UTC rather than local time.\n\
1096"},
1097 { "format", format,
1098 "format [-auADE] [-f FORMAT] [-F format] [-m FILE] [-o FILE]\n\t\
1099[FILE [MESSAGE]]", "\
1100Options:\n\
1101\n\
1102-a, --armour-in Same as `-f pem'.\n\
1103-A, --armour-out Same as `-F pem'.\n\
1104-D, --detach Create detached signature.\n\
1105-E, --embed Create signature with embedded message.\n\
1106-f, --format-in=FORMAT Decode input as FORMAT.\n\
1107-F, --format-out=FORMAT Encode output as FORMAT.\n\
1108-m, --message=FILE Write message to FILE.\n\
1109-o, --output=FILE Write new signature to FILE.\n\
1110"},
1111 { 0, 0, 0 }
1112}; /* " Emacs seems confused. */
1113
1114static int cmd_help(int argc, char **argv)
1115{
1116 sc_help(cmdtab, stdout, argv + 1);
1117 return (0);
1118}
1119
1120void version(FILE *fp)
1121{
1122 pquis(fp, "$, Catacomb version " VERSION "\n");
1123}
1124
1125static void usage(FILE *fp)
1126{
1127 pquis(fp, "Usage: $ [-k KEYRING] COMMAND [ARGS]\n");
1128}
1129
1130void help_global(FILE *fp)
1131{
1132 usage(fp);
1133 fputs("\n\
1134Sign and verify data.\n\
1135\n\
1136Global command-line options:\n\
1137\n\
1138-h, --help [COMMAND...] Show this help message, or help for COMMANDs.\n\
1139-v, --version Show program version number.\n\
1140-u, --usage Show a terse usage message.\n\
1141\n\
1142-k, --keyring=FILE Read keys from FILE.\n",
1143 fp);
1144}
1145
1146/* --- @main@ --- *
1147 *
1148 * Arguments: @int argc@ = number of command line arguments
1149 * @char *argv[]@ = vector of command line arguments
1150 *
1151 * Returns: Zero if successful, nonzero otherwise.
1152 *
1153 * Use: Encrypts or decrypts files.
1154 */
1155
1156int main(int argc, char *argv[])
1157{
1158 unsigned f = 0;
1159
1160#define f_bogus 1u
1161
1162 /* --- Initialize the library --- */
1163
1164 ego(argv[0]);
1165 sub_init();
1166 rand_noisesrc(RAND_GLOBAL, &noise_source);
1167 rand_seed(RAND_GLOBAL, 160);
1168/* trace_on(stderr, 1); */
1169
1170 /* --- Parse options --- */
1171
1172 for (;;) {
1173 static struct option opts[] = {
1174 { "help", 0, 0, 'h' },
1175 { "version", 0, 0, 'v' },
1176 { "usage", 0, 0, 'u' },
1177 { "keyring", OPTF_ARGREQ, 0, 'k' },
1178 { 0, 0, 0, 0 }
1179 };
1180 int i = mdwopt(argc, argv, "+hvu k:", opts, 0, 0, 0);
1181 if (i < 0)
1182 break;
1183 switch (i) {
1184 case 'h':
1185 sc_help(cmdtab, stdout, argv + optind);
1186 exit(0);
1187 break;
1188 case 'v':
1189 version(stdout);
1190 exit(0);
1191 break;
1192 case 'u':
1193 usage(stdout);
1194 exit(0);
1195 case 'k':
1196 keyring = optarg;
1197 break;
1198 default:
1199 f |= f_bogus;
1200 break;
1201 }
1202 }
1203
1204 argc -= optind;
1205 argv += optind;
1206 optind = 0;
1207 if (f & f_bogus || argc < 1) {
1208 usage(stderr);
1209 exit(EXIT_FAILURE);
1210 }
1211
1212 /* --- Dispatch to the correct subcommand handler --- */
1213
1214 return (findcmd(cmdtab, argv[0])->cmd(argc, argv));
1215
1216#undef f_bogus
1217}
1218
1219/*----- That's all, folks -------------------------------------------------*/