Expunge revision histories in files.
[u/mdw/catacomb] / dsig.c
1 /* -*-c-*-
2 *
3 * $Id: dsig.c,v 1.10 2004/04/08 01:36:15 mdw Exp $
4 *
5 * Verify signatures on distribuitions of files
6 *
7 * (c) 2000 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 "config.h"
33
34 #include <ctype.h>
35 #include <errno.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #include <mLib/alloc.h>
41 #include <mLib/base64.h>
42 #include <mLib/mdwopt.h>
43 #include <mLib/quis.h>
44 #include <mLib/report.h>
45 #include <mLib/sub.h>
46
47 #include "getdate.h"
48 #include "grand.h"
49 #include "ghash.h"
50 #include "key.h"
51 #include "key-data.h"
52 #include "noise.h"
53
54 #include "ec.h"
55 #include "ec-keys.h"
56 #include "dh.h"
57 #include "gdsa.h"
58 #include "gkcdsa.h"
59 #include "rsa.h"
60
61 #include "sha.h"
62 #include "has160.h"
63
64 /*----- Algorithm choice --------------------------------------------------*/
65
66 /* --- Relevant type operations --- */
67
68 typedef struct sig {
69 const struct sigops *ops;
70 key_packdef *kp;
71 ghash *h;
72 } sig;
73
74 typedef struct sigops {
75 const key_fetchdef *kf; /* Key fetching structure */
76 size_t kdsz; /* Size of the key-data structure */
77 sig *(*init)(key */*k*/, void */*kd*/, const gchash */*hc*/);
78 int (*doit)(sig */*s*/, dstr */*d*/);
79 void (*destroy)(sig */*s*/);
80 } sigops;
81
82 /* --- RSA PKCS1 --- */
83
84 typedef struct rsap1_sigctx {
85 sig s;
86 rsa_privctx rp;
87 pkcs1 p1;
88 } rsap1_sigctx;
89
90 static sig *rsap1_siginit(key *k, void *kd, const gchash *hc)
91 {
92 rsap1_sigctx *rs = CREATE(rsap1_sigctx);
93 rsa_privcreate(&rs->rp, kd, &rand_global);
94 rs->p1.r = &rand_global;
95 rs->p1.ep = hc->name;
96 rs->p1.epsz = strlen(hc->name) + 1;
97 rs->s.h = 0;
98 return (&rs->s);
99 }
100
101 static int rsap1_sigdoit(sig *s, dstr *d)
102 {
103 rsap1_sigctx *rs = (rsap1_sigctx *)s;
104 size_t n;
105 mp *m = rsa_sign(&rs->rp, MP_NEW,
106 GH_DONE(s->h, 0), GH_CLASS(s->h)->hashsz,
107 pkcs1_sigencode, &rs->p1);
108 if (!m) return (-1);
109 n = mp_octets(rs->rp.rp->n); dstr_ensure(d, n); mp_storeb(m, d->buf, n);
110 d->len += n; mp_drop(m);
111 return (0);
112 }
113
114 static void rsap1_sigdestroy(sig *s)
115 {
116 rsap1_sigctx *rs = (rsap1_sigctx *)s;
117 DESTROY(rs);
118 }
119
120 static const sigops rsap1_sig = {
121 rsa_privfetch, sizeof(rsa_priv),
122 rsap1_siginit, rsap1_sigdoit, rsap1_sigdestroy
123 };
124
125 typedef struct rsap1_vrfctx {
126 sig s;
127 rsa_pubctx rp;
128 pkcs1 p1;
129 } rsap1_vrfctx;
130
131 static sig *rsap1_vrfinit(key *k, void *kd, const gchash *hc)
132 {
133 rsap1_vrfctx *rv = CREATE(rsap1_vrfctx);
134 rsa_pubcreate(&rv->rp, kd);
135 rv->p1.r = &rand_global;
136 rv->p1.ep = hc->name;
137 rv->p1.epsz = strlen(hc->name) + 1;
138 rv->s.h = 0;
139 return (&rv->s);
140 }
141
142 static int rsap1_vrfdoit(sig *s, dstr *d)
143 {
144 rsap1_vrfctx *rv = (rsap1_vrfctx *)s;
145 mp *m = mp_loadb(MP_NEW, d->buf, d->len);
146 int rc = rsa_verify(&rv->rp, m,
147 GH_DONE(s->h, 0), GH_CLASS(s->h)->hashsz,
148 0, pkcs1_sigdecode, &rv->p1);
149 mp_drop(m);
150 return (rc);
151 }
152
153 static void rsap1_vrfdestroy(sig *s)
154 {
155 rsap1_vrfctx *rv = (rsap1_vrfctx *)s;
156 DESTROY(rv);
157 }
158
159 static const sigops rsap1_vrf = {
160 rsa_pubfetch, sizeof(rsa_pub),
161 rsap1_vrfinit, rsap1_vrfdoit, rsap1_vrfdestroy
162 };
163
164 /* --- RSA PSS --- */
165
166 static const gccipher *getmgf(key *k, const gchash *hc)
167 {
168 dstr d = DSTR_INIT;
169 const gccipher *gc;
170 const char *mm;
171
172 if ((mm = key_getattr(0, k, "mgf-alg")) == 0) {
173 dstr_putf(&d, "%s-mgf", hc->name);
174 mm = d.buf;
175 }
176 if ((gc = gcipher_byname(mm)) == 0)
177 die(EXIT_FAILURE, "unknown encryption scheme `%s'", mm);
178 dstr_destroy(&d);
179 return (gc);
180 }
181
182 typedef struct rsapss_sigctx {
183 sig s;
184 rsa_privctx rp;
185 pss p;
186 } rsapss_sigctx;
187
188 static sig *rsapss_siginit(key *k, void *kd, const gchash *hc)
189 {
190 rsapss_sigctx *rs = CREATE(rsapss_sigctx);
191 rsa_privcreate(&rs->rp, kd, &rand_global);
192 rs->p.r = &rand_global;
193 rs->p.cc = getmgf(k, hc);
194 rs->p.ch = hc;
195 rs->p.ssz = hc->hashsz;
196 return (&rs->s);
197 }
198
199 static int rsapss_sigdoit(sig *s, dstr *d)
200 {
201 rsapss_sigctx *rs = (rsapss_sigctx *)s;
202 size_t n;
203 mp *m = rsa_sign(&rs->rp, MP_NEW,
204 GH_DONE(s->h, 0), GH_CLASS(s->h)->hashsz,
205 pss_encode, &rs->p);
206 if (!m) return (-1);
207 n = mp_octets(rs->rp.rp->n); dstr_ensure(d, n); mp_storeb(m, d->buf, n);
208 d->len += n; mp_drop(m);
209 return (0);
210 }
211
212 static void rsapss_sigdestroy(sig *s)
213 {
214 rsapss_sigctx *rs = (rsapss_sigctx *)s;
215 DESTROY(rs);
216 }
217
218 static const sigops rsapss_sig = {
219 rsa_privfetch, sizeof(rsa_priv),
220 rsapss_siginit, rsapss_sigdoit, rsapss_sigdestroy
221 };
222
223 typedef struct rsapss_vrfctx {
224 sig s;
225 rsa_pubctx rp;
226 pss p;
227 } rsapss_vrfctx;
228
229 static sig *rsapss_vrfinit(key *k, void *kd, const gchash *hc)
230 {
231 rsapss_vrfctx *rv = CREATE(rsapss_vrfctx);
232 rsa_pubcreate(&rv->rp, kd);
233 rv->p.r = &rand_global;
234 rv->p.cc = getmgf(k, hc);
235 rv->p.ch = hc;
236 rv->p.ssz = hc->hashsz;
237 return (&rv->s);
238 }
239
240 static int rsapss_vrfdoit(sig *s, dstr *d)
241 {
242 rsapss_vrfctx *rv = (rsapss_vrfctx *)s;
243 mp *m = mp_loadb(MP_NEW, d->buf, d->len);
244 int rc = rsa_verify(&rv->rp, m,
245 GH_DONE(s->h, 0), GH_CLASS(s->h)->hashsz,
246 0, pss_decode, &rv->p);
247 mp_drop(m);
248 return (rc);
249 }
250
251 static void rsapss_vrfdestroy(sig *s)
252 {
253 rsapss_vrfctx *rv = (rsapss_vrfctx *)s;
254 DESTROY(rv);
255 }
256
257 static const sigops rsapss_vrf = {
258 rsa_pubfetch, sizeof(rsa_pub),
259 rsapss_vrfinit, rsapss_vrfdoit, rsapss_vrfdestroy
260 };
261
262 /* --- DSA and ECDSA --- */
263
264 typedef struct dsa_sigctx {
265 sig s;
266 gdsa g;
267 } dsa_sigctx;
268
269 static dsa_sigctx *dsa_doinit(key *k, const gprime_param *gp,
270 mp *y, const gchash *hc)
271 {
272 dsa_sigctx *ds = CREATE(dsa_sigctx);
273 dstr t = DSTR_INIT;
274
275 key_fulltag(k, &t);
276 if ((ds->g.g = group_prime(gp)) == 0)
277 die(EXIT_FAILURE, "bad prime group in key `%s'", t.buf);
278 ds->g.u = MP_NEW;
279 ds->g.p = G_CREATE(ds->g.g);
280 if (G_FROMINT(ds->g.g, ds->g.p, y))
281 die(EXIT_FAILURE, "bad public key in key `%s'", t.buf);
282 ds->g.r = &rand_global;
283 ds->g.h = hc;
284 ds->s.h = 0;
285 dstr_destroy(&t);
286 return (ds);
287 }
288
289 static dsa_sigctx *ecdsa_doinit(key *k, const char *cstr,
290 ec *y, const gchash *hc)
291 {
292 dsa_sigctx *ds = CREATE(dsa_sigctx);
293 ec_info ei;
294 const char *e;
295 dstr t = DSTR_INIT;
296
297 key_fulltag(k, &t);
298 if ((e = ec_getinfo(&ei, cstr)) != 0)
299 die(EXIT_FAILURE, "bad curve in key `%s': %s", t.buf, e);
300 ds->g.g = group_ec(&ei);
301 ds->g.u = MP_NEW;
302 ds->g.p = G_CREATE(ds->g.g);
303 if (G_FROMEC(ds->g.g, ds->g.p, y))
304 die(EXIT_FAILURE, "bad public key in key `%s'", t.buf);
305 ds->g.r = &rand_global;
306 ds->g.h = hc;
307 ds->s.h = 0;
308 dstr_destroy(&t);
309 return (ds);
310 }
311
312 static sig *dsa_siginit(key *k, void *kd, const gchash *hc)
313 {
314 dh_priv *dp = kd;
315 dsa_sigctx *ds = dsa_doinit(k, &dp->dp, dp->y, hc);
316 ds->g.u = MP_COPY(dp->x);
317 return (&ds->s);
318 }
319
320 static sig *ecdsa_siginit(key *k, void *kd, const gchash *hc)
321 {
322 ec_priv *ep = kd;
323 dsa_sigctx *ds = ecdsa_doinit(k, ep->cstr, &ep->p, hc);
324 ds->g.u = MP_COPY(ep->x);
325 return (&ds->s);
326 }
327
328 static int dsa_sigdoit(sig *s, dstr *d)
329 {
330 dsa_sigctx *ds = (dsa_sigctx *)s;
331 gdsa_sig ss = GDSA_SIG_INIT;
332 size_t n = mp_octets(ds->g.g->r);
333
334 gdsa_sign(&ds->g, &ss, GH_DONE(ds->s.h, 0), 0);
335 dstr_ensure(d, 2 * n);
336 mp_storeb(ss.r, d->buf, n);
337 mp_storeb(ss.s, d->buf + n, n);
338 d->len += 2 * n;
339 mp_drop(ss.r); mp_drop(ss.s);
340 return (0);
341 }
342
343 static void dsa_sigdestroy(sig *s)
344 {
345 dsa_sigctx *ds = (dsa_sigctx *)s;
346 G_DESTROY(ds->g.g, ds->g.p);
347 mp_drop(ds->g.u);
348 G_DESTROYGROUP(ds->g.g);
349 }
350
351 static const sigops dsa_sig = {
352 dh_privfetch, sizeof(dh_priv),
353 dsa_siginit, dsa_sigdoit, dsa_sigdestroy
354 };
355
356 static const sigops ecdsa_sig = {
357 ec_privfetch, sizeof(ec_priv),
358 ecdsa_siginit, dsa_sigdoit, dsa_sigdestroy
359 };
360
361 static sig *dsa_vrfinit(key *k, void *kd, const gchash *hc)
362 {
363 dh_pub *dp = kd;
364 dsa_sigctx *ds = dsa_doinit(k, &dp->dp, dp->y, hc);
365 return (&ds->s);
366 }
367
368 static sig *ecdsa_vrfinit(key *k, void *kd, const gchash *hc)
369 {
370 ec_pub *ep = kd;
371 dsa_sigctx *ds = ecdsa_doinit(k, ep->cstr, &ep->p, hc);
372 return (&ds->s);
373 }
374
375 static int dsa_vrfdoit(sig *s, dstr *d)
376 {
377 dsa_sigctx *ds = (dsa_sigctx *)s;
378 gdsa_sig ss;
379 size_t n = d->len/2;
380 int rc;
381
382 ss.r = mp_loadb(MP_NEW, d->buf, n);
383 ss.s = mp_loadb(MP_NEW, d->buf + n, d->len - n);
384 rc = gdsa_verify(&ds->g, &ss, GH_DONE(ds->s.h, 0));
385 mp_drop(ss.r); mp_drop(ss.s);
386 return (rc);
387 }
388
389 static const sigops dsa_vrf = {
390 dh_pubfetch, sizeof(dh_pub),
391 dsa_vrfinit, dsa_vrfdoit, dsa_sigdestroy
392 };
393
394 static const sigops ecdsa_vrf = {
395 ec_pubfetch, sizeof(ec_pub),
396 ecdsa_vrfinit, dsa_vrfdoit, dsa_sigdestroy
397 };
398
399 /* --- KCDSA and ECKCDSA --- */
400
401 static void kcdsa_privkey(dsa_sigctx *ds, mp *x)
402 { ds->g.u = mp_modinv(MP_NEW, x, ds->g.g->r); }
403
404 static void kcdsa_sethash(dsa_sigctx *ds, const gchash *hc)
405 { ds->s.h = gkcdsa_beginhash(&ds->g); }
406
407 static sig *kcdsa_siginit(key *k, void *kd, const gchash *hc)
408 {
409 dh_priv *dp = kd;
410 dsa_sigctx *ds = dsa_doinit(k, &dp->dp, dp->y, hc);
411 kcdsa_privkey(ds, dp->x);
412 kcdsa_sethash(ds, hc);
413 return (&ds->s);
414 }
415
416 static sig *eckcdsa_siginit(key *k, void *kd, const gchash *hc)
417 {
418 ec_priv *ep = kd;
419 dsa_sigctx *ds = ecdsa_doinit(k, ep->cstr, &ep->p, hc);
420 kcdsa_privkey(ds, ep->x);
421 kcdsa_sethash(ds, hc);
422 return (&ds->s);
423 }
424
425 static int kcdsa_sigdoit(sig *s, dstr *d)
426 {
427 dsa_sigctx *ds = (dsa_sigctx *)s;
428 gkcdsa_sig ss = GKCDSA_SIG_INIT;
429 size_t hsz = ds->g.h->hashsz, n = mp_octets(ds->g.g->r);
430
431 gkcdsa_sign(&ds->g, &ss, GH_DONE(ds->s.h, 0), 0);
432 dstr_ensure(d, hsz + n);
433 memcpy(d->buf, ss.r, hsz);
434 mp_storeb(ss.s, d->buf + hsz, n);
435 d->len += hsz + n;
436 xfree(ss.r); mp_drop(ss.s);
437 return (0);
438 }
439
440 static const sigops kcdsa_sig = {
441 dh_privfetch, sizeof(dh_priv),
442 kcdsa_siginit, kcdsa_sigdoit, dsa_sigdestroy
443 };
444
445 static const sigops eckcdsa_sig = {
446 ec_privfetch, sizeof(ec_priv),
447 eckcdsa_siginit, kcdsa_sigdoit, dsa_sigdestroy
448 };
449
450 static sig *kcdsa_vrfinit(key *k, void *kd, const gchash *hc)
451 {
452 dh_pub *dp = kd;
453 dsa_sigctx *ds = dsa_doinit(k, &dp->dp, dp->y, hc);
454 kcdsa_sethash(ds, hc);
455 return (&ds->s);
456 }
457
458 static sig *eckcdsa_vrfinit(key *k, void *kd, const gchash *hc)
459 {
460 ec_pub *ep = kd;
461 dsa_sigctx *ds = ecdsa_doinit(k, ep->cstr, &ep->p, hc);
462 kcdsa_sethash(ds, hc);
463 return (&ds->s);
464 }
465
466 static int kcdsa_vrfdoit(sig *s, dstr *d)
467 {
468 dsa_sigctx *ds = (dsa_sigctx *)s;
469 gkcdsa_sig ss;
470 size_t hsz = ds->g.h->hashsz, n = d->len - hsz;
471 int rc;
472
473 if (d->len < hsz)
474 return (-1);
475 ss.r = (octet *)d->buf;
476 ss.s = mp_loadb(MP_NEW, d->buf + hsz, n);
477 rc = gkcdsa_verify(&ds->g, &ss, GH_DONE(ds->s.h, 0));
478 mp_drop(ss.s);
479 return (rc);
480 }
481
482 static const sigops kcdsa_vrf = {
483 dh_pubfetch, sizeof(dh_pub),
484 kcdsa_vrfinit, kcdsa_vrfdoit, dsa_sigdestroy
485 };
486
487 static const sigops eckcdsa_vrf = {
488 ec_pubfetch, sizeof(ec_pub),
489 eckcdsa_vrfinit, kcdsa_vrfdoit, dsa_sigdestroy
490 };
491
492 /* --- The switch table --- */
493
494 static const struct sigtab {
495 const char *name;
496 const sigops *signops;
497 const sigops *verifyops;
498 const gchash *ch;
499 } sigtab[] = {
500 { "rsapkcs1", &rsap1_sig, &rsap1_vrf, &sha },
501 { "rsapss", &rsapss_sig, &rsapss_vrf, &sha },
502 { "dsa", &dsa_sig, &dsa_vrf, &sha },
503 { "ecdsa", &ecdsa_sig, &ecdsa_vrf, &sha },
504 { "kcdsa", &kcdsa_sig, &kcdsa_vrf, &has160 },
505 { "eckcdsa", &eckcdsa_sig, &eckcdsa_vrf, &has160 },
506 { 0, 0, 0 }
507 };
508
509 /* --- @getsig@ --- *
510 *
511 * Arguments: @key *k@ = the key to load
512 * @int wantpriv@ = nonzero if we want to sign
513 *
514 * Returns: A signature-making thing.
515 *
516 * Use: Loads a key and starts hashing.
517 */
518
519 static sig *getsig(key *k, int wantpriv)
520 {
521 const char *salg, *halg = 0;
522 dstr d = DSTR_INIT;
523 dstr t = DSTR_INIT;
524 char *p = 0;
525 const char *q;
526 sig *s;
527 const struct sigtab *st;
528 const sigops *so;
529 const gchash *ch;
530 void *kd;
531 int e;
532 key_packdef *kp;
533
534 /* --- Setup stuff --- */
535
536 key_fulltag(k, &t);
537
538 /* --- Get the signature algorithm --- *
539 *
540 * Take the attribute if it's there; otherwise use the key type.
541 */
542
543 if ((q = key_getattr(0, k, "sig-alg")) != 0) {
544 dstr_puts(&d, q);
545 p = d.buf;
546 } else if (strncmp(k->type, "dsig-", 5) == 0 && k->type[5]) {
547 dstr_puts(&d, k->type);
548 p = d.buf + 5;
549 } else
550 die(EXIT_FAILURE, "no signature algorithm for key `%s'", t.buf);
551
552 /* --- Grab the hash algorithm --- *
553 *
554 * Grab it from the signature algorithm if it's there. But override that
555 * from the attribute.
556 */
557
558 salg = p;
559 if ((p = strchr(p, '-')) != 0) {
560 *p++ = 0;
561 halg = p;
562 }
563 if ((q = key_getattr(0, k, "hash-alg")) != 0)
564 halg = q;
565
566 /* --- Look up the algorithms in the table --- */
567
568 for (st = sigtab; st->name; st++) {
569 if (strcmp(st->name, salg) == 0)
570 goto s_found;
571 }
572 die(EXIT_FAILURE, "signature algorithm `%s' not found in key `%s'",
573 salg, t.buf);
574 s_found:;
575 if (!halg)
576 ch = st->ch;
577 else {
578 if ((ch = ghash_byname(halg)) == 0) {
579 die(EXIT_FAILURE, "hash algorithm `%s' not found in key `%s'",
580 halg, t.buf);
581 }
582 }
583 so = wantpriv ? st->signops : st->verifyops;
584
585 /* --- Load the key --- */
586
587 kd = xmalloc(so->kdsz);
588 kp = key_fetchinit(so->kf, 0, kd);
589 if ((e = key_fetch(kp, k)) != 0)
590 die(EXIT_FAILURE, "error fetching key `%s': %s", t.buf, key_strerror(e));
591 s = so->init(k, kd, ch);
592 if (!s->h)
593 s->h = GH_INIT(ch);
594 s->kp = kp;
595 s->ops = so;
596
597 /* --- Free stuff up --- */
598
599 dstr_destroy(&d);
600 dstr_destroy(&t);
601 return (s);
602 }
603
604 /* --- @freesig@ --- *
605 *
606 * Arguments: @sig *s@ = signature-making thing
607 *
608 * Returns: ---
609 *
610 * Use: Frees up a signature-making thing
611 */
612
613 static void freesig(sig *s)
614 {
615 GH_DESTROY(s->h);
616 key_fetchdone(s->kp);
617 s->ops->destroy(s);
618 }
619
620 /*----- Data formatting ---------------------------------------------------*/
621
622 /* --- Binary data structure --- *
623 *
624 * The binary format, which is used for hashing and for the optional binary
625 * output, consists of a sequence of tagged blocks. The tag describes the
626 * format and meaining of the following data.
627 */
628
629 enum {
630 /* --- Block tags --- */
631
632 T_IDENT = 0, /* An identifying marker */
633 T_KEYID, /* Key identifier */
634 T_BEGIN, /* Begin hashing here */
635 T_COMMENT = T_BEGIN, /* A textual comment */
636 T_DATE, /* Creation date of signature */
637 T_EXPIRE, /* Expiry date of signature */
638 T_FILE, /* File and corresponding hash */
639 T_SIGNATURE, /* Final signature block */
640
641 /* --- Error messages --- */
642
643 E_EOF = -1,
644 E_BIN = -2,
645 E_TAG = -3,
646 E_DATE = -4
647 };
648
649 /* --- Name translation table --- */
650
651 static const char *tagtab[] = {
652 "ident:", "keyid:",
653 "comment:", "date:", "expires:", "file:",
654 "signature:",
655 0
656 };
657
658 static const char *errtab[] = {
659 "Off-by-one bug",
660 "Unexpected end-of-file",
661 "Binary object too large",
662 "Unrecognized tag",
663 "Bad date string"
664 };
665
666 /* --- Memory representation of block types --- */
667
668 typedef struct block {
669 int tag; /* Type tag */
670 dstr d; /* String data */
671 dstr b; /* Binary data */
672 time_t t; /* Timestamp */
673 uint32 k; /* Keyid */
674 } block;
675
676 /* --- @getstring@ --- *
677 *
678 * Arguments: @FILE *fp@ = stream from which to read
679 * @dstr *d@ = destination string
680 * @unsigned raw@ = raw or cooked read
681 *
682 * Returns: Zero if OK, nonzero on end-of-file.
683 *
684 * Use: Reads a filename (or something similar) from a stream.
685 */
686
687 static int getstring(FILE *fp, dstr *d, unsigned raw)
688 {
689 int ch;
690 int q = 0;
691
692 /* --- Raw: just read exactly what's written up to a null byte --- */
693
694 if (raw) {
695 if ((ch = getc(fp)) == EOF)
696 return (EOF);
697 for (;;) {
698 if (!ch)
699 break;
700 DPUTC(d, ch);
701 if ((ch = getc(fp)) == EOF)
702 break;
703 }
704 DPUTZ(d);
705 return (0);
706 }
707
708 /* --- Skip as far as whitespace --- *
709 *
710 * Also skip past comments.
711 */
712
713 again:
714 ch = getc(fp);
715 while (isspace(ch))
716 ch = getc(fp);
717 if (ch == '#') {
718 do ch = getc(fp); while (ch != '\n' && ch != EOF);
719 goto again;
720 }
721 if (ch == EOF)
722 return (EOF);
723
724 /* --- If the character is a quote then read a quoted string --- */
725
726 switch (ch) {
727 case '`':
728 ch = '\'';
729 case '\'':
730 case '\"':
731 q = ch;
732 ch = getc(fp);
733 break;
734 }
735
736 /* --- Now read all sorts of interesting things --- */
737
738 for (;;) {
739
740 /* --- Handle an escaped thing --- */
741
742 if (ch == '\\') {
743 ch = getc(fp);
744 if (ch == EOF)
745 break;
746 switch (ch) {
747 case 'a': ch = '\a'; break;
748 case 'b': ch = '\b'; break;
749 case 'f': ch = '\f'; break;
750 case 'n': ch = '\n'; break;
751 case 'r': ch = '\r'; break;
752 case 't': ch = '\t'; break;
753 case 'v': ch = '\v'; break;
754 }
755 DPUTC(d, ch);
756 ch = getc(fp);
757 continue;
758 }
759
760 /* --- If it's a quote or some other end marker then stop --- */
761
762 if (ch == q || (!q && isspace((unsigned char)ch)))
763 break;
764
765 /* --- Otherwise contribute and continue --- */
766
767 DPUTC(d, ch);
768 if ((ch = getc(fp)) == EOF)
769 break;
770 }
771
772 /* --- Done --- */
773
774 DPUTZ(d);
775 return (0);
776 }
777
778 /* --- @putstring@ --- *
779 *
780 * Arguments: @FILE *fp@ = stream to write on
781 * @const char *p@ = pointer to text
782 * @unsigned raw@ = whether the string is to be written raw
783 *
784 * Returns: ---
785 *
786 * Use: Emits a string to a stream.
787 */
788
789 static void putstring(FILE *fp, const char *p, unsigned raw)
790 {
791 size_t sz = strlen(p);
792 unsigned qq;
793 const char *q;
794
795 /* --- Just write the string null terminated if raw --- */
796
797 if (raw) {
798 fwrite(p, 1, sz + 1, fp);
799 return;
800 }
801
802 /* --- Check for any dodgy characters --- */
803
804 qq = 0;
805 for (q = p; *q; q++) {
806 if (isspace((unsigned char)*q)) {
807 qq = '\"';
808 break;
809 }
810 }
811
812 if (qq)
813 putc(qq, fp);
814
815 /* --- Emit the string --- */
816
817 for (q = p; *q; q++) {
818 switch (*q) {
819 case '\a': fputc('\\', fp); fputc('a', fp); break;
820 case '\b': fputc('\\', fp); fputc('b', fp); break;
821 case '\f': fputc('\\', fp); fputc('f', fp); break;
822 case '\n': fputc('\\', fp); fputc('n', fp); break;
823 case '\r': fputc('\\', fp); fputc('r', fp); break;
824 case '\t': fputc('\\', fp); fputc('t', fp); break;
825 case '\v': fputc('\\', fp); fputc('v', fp); break;
826 case '`': fputc('\\', fp); fputc('`', fp); break;
827 case '\'': fputc('\\', fp); fputc('\'', fp); break;
828 case '\"': fputc('\\', fp); fputc('\"', fp); break;
829 default:
830 putc(*q, fp);
831 break;
832 }
833 }
834
835 /* --- Done --- */
836
837 if (qq)
838 putc(qq, fp);
839 }
840
841 /* --- @timestring@ --- *
842 *
843 * Arguments: @time_t t@ = a timestamp
844 * @dstr *d@ = a string to write on
845 *
846 * Returns: ---
847 *
848 * Use: Writes a textual representation of the timestamp to the
849 * string.
850 */
851
852 static void timestring(time_t t, dstr *d)
853 {
854 if (t == KEXP_FOREVER)
855 DPUTS(d, "forever");
856 else {
857 struct tm *tm = localtime(&t);
858 DENSURE(d, 32);
859 d->len += strftime(d->buf + d->len, 32, "%Y-%m-%d %H:%M:%S %Z", tm);
860 DPUTZ(d);
861 }
862 }
863
864 /* --- @breset@ --- *
865 *
866 * Arguments: @block *b@ = block to reset
867 *
868 * Returns: ---
869 *
870 * Use: Resets a block so that more stuff can be put in it.
871 */
872
873 static void breset(block *b)
874 {
875 b->tag = 0;
876 DRESET(&b->d);
877 DRESET(&b->b);
878 b->k = 0;
879 b->t = KEXP_EXPIRE;
880 }
881
882 /* --- @binit@ --- *
883 *
884 * Arguments: @block *b@ = block to initialize
885 *
886 * Returns: ---
887 *
888 * Use: Initializes a block as something to read into.
889 */
890
891 static void binit(block *b)
892 {
893 dstr_create(&b->d);
894 dstr_create(&b->b);
895 breset(b);
896 }
897
898 /* --- @bdestroy@ --- *
899 *
900 * Arguments: @block *b@ = block to destroy
901 *
902 * Returns: ---
903 *
904 * Use: Destroys a block's contents.
905 */
906
907 static void bdestroy(block *b)
908 {
909 dstr_destroy(&b->d);
910 dstr_destroy(&b->b);
911 }
912
913 /* --- @bget@ --- *
914 *
915 * Arguments: @block *b@ = pointer to block
916 * @FILE *fp@ = stream to read from
917 * @unsigned bin@ = binary switch
918 *
919 * Returns: Tag of block, or an error tag.
920 *
921 * Use: Reads a block from a stream.
922 */
923
924 static int bget(block *b, FILE *fp, unsigned bin)
925 {
926 int tag;
927
928 /* --- Read the tag --- */
929
930 if (bin)
931 tag = getc(fp);
932 else {
933 dstr d = DSTR_INIT;
934 if (getstring(fp, &d, 0))
935 return (E_EOF);
936 for (tag = 0; tagtab[tag]; tag++) {
937 if (strcmp(tagtab[tag], d.buf) == 0)
938 goto done;
939 }
940 return (E_TAG);
941 done:;
942 }
943
944 /* --- Decide what to do next --- */
945
946 breset(b);
947 b->tag = tag;
948 switch (tag) {
949
950 /* --- Reading of strings --- */
951
952 case T_IDENT:
953 case T_COMMENT:
954 if (getstring(fp, &b->d, bin))
955 return (E_EOF);
956 break;
957
958 /* --- Timestamps --- */
959
960 case T_DATE:
961 case T_EXPIRE:
962 if (bin) {
963 octet buf[8];
964 if (fread(buf, sizeof(buf), 1, fp) < 1)
965 return (E_EOF);
966 b->t = ((time_t)(((LOAD32(buf + 0) << 16) << 16) & ~MASK32) |
967 (time_t)LOAD32(buf + 4));
968 } else {
969 if (getstring(fp, &b->d, 0))
970 return (E_EOF);
971 if (strcmp(b->d.buf, "forever") == 0)
972 b->t = KEXP_FOREVER;
973 else if ((b->t = get_date(b->d.buf, 0)) == -1)
974 return (E_DATE);
975 }
976 break;
977
978 /* --- Key ids --- */
979
980 case T_KEYID:
981 if (bin) {
982 octet buf[4];
983 if (fread(buf, sizeof(buf), 1, fp) < 1)
984 return (E_EOF);
985 b->k = LOAD32(buf);
986 } else {
987 if (getstring(fp, &b->d, 0))
988 return (E_EOF);
989 b->k = strtoul(b->d.buf, 0, 16);
990 }
991 break;
992
993 /* --- Reading of binary data --- */
994
995 case T_FILE:
996 case T_SIGNATURE:
997 if (bin) {
998 octet buf[2];
999 uint32 sz;
1000 if (fread(buf, sizeof(buf), 1, fp) < 1)
1001 return (E_EOF);
1002 sz = LOAD16(buf);
1003 if (sz > 4096)
1004 return (E_BIN);
1005 DENSURE(&b->b, sz);
1006 if (fread(b->b.buf + b->b.len, 1, sz, fp) < sz)
1007 return (E_EOF);
1008 b->b.len += sz;
1009 } else {
1010 base64_ctx b64;
1011 if (getstring(fp, &b->d, 0))
1012 return (E_EOF);
1013 base64_init(&b64);
1014 base64_decode(&b64, b->d.buf, b->d.len, &b->b);
1015 base64_decode(&b64, 0, 0, &b->b);
1016 DRESET(&b->d);
1017 }
1018 if (tag == T_FILE && getstring(fp, &b->d, bin))
1019 return (E_EOF);
1020 break;
1021
1022 /* --- Anything else --- */
1023
1024 default:
1025 return (E_TAG);
1026 }
1027
1028 return (tag);
1029 }
1030
1031 /* --- @blob@ --- *
1032 *
1033 * Arguments: @block *b@ = pointer to block to emit
1034 * @dstr *d@ = output buffer
1035 *
1036 * Returns: ---
1037 *
1038 * Use: Encodes a block in a binary format.
1039 */
1040
1041 static void blob(block *b, dstr *d)
1042 {
1043 DPUTC(d, b->tag);
1044 switch (b->tag) {
1045 case T_IDENT:
1046 case T_COMMENT:
1047 DPUTD(d, &b->d);
1048 DPUTC(d, 0);
1049 break;
1050 case T_DATE:
1051 case T_EXPIRE:
1052 DENSURE(d, 8);
1053 STORE32(d->buf + d->len, ((b->t & ~MASK32) >> 16) >> 16);
1054 STORE32(d->buf + d->len + 4, b->t);
1055 d->len += 8;
1056 break;
1057 case T_KEYID:
1058 DENSURE(d, 4);
1059 STORE32(d->buf + d->len, b->k);
1060 d->len += 4;
1061 break;
1062 case T_FILE:
1063 case T_SIGNATURE:
1064 DENSURE(d, 2);
1065 STORE16(d->buf + d->len, b->b.len);
1066 d->len += 2;
1067 DPUTD(d, &b->b);
1068 if (b->tag == T_FILE) {
1069 DPUTD(d, &b->d);
1070 DPUTC(d, 0);
1071 }
1072 break;
1073 }
1074 }
1075
1076 /* --- @bwrite@ --- *
1077 *
1078 * Arguments: @block *b@ = pointer to block to write
1079 * @FILE *fp@ = stream to write on
1080 *
1081 * Returns: ---
1082 *
1083 * Use: Writes a block on a stream in a textual format.
1084 */
1085
1086 static void bwrite(block *b, FILE *fp)
1087 {
1088 fputs(tagtab[b->tag], fp);
1089 putc(' ', fp);
1090 switch (b->tag) {
1091 case T_IDENT:
1092 case T_COMMENT:
1093 putstring(fp, b->d.buf, 0);
1094 break;
1095 case T_DATE:
1096 case T_EXPIRE: {
1097 dstr d = DSTR_INIT;
1098 timestring(b->t, &d);
1099 putstring(fp, d.buf, 0);
1100 dstr_destroy(&d);
1101 } break;
1102 case T_KEYID:
1103 fprintf(fp, "%08lx", (unsigned long)b->k);
1104 break;
1105 case T_FILE:
1106 case T_SIGNATURE: {
1107 dstr d = DSTR_INIT;
1108 base64_ctx b64;
1109 base64_init(&b64);
1110 b64.maxline = 0;
1111 base64_encode(&b64, b->b.buf, b->b.len, &d);
1112 base64_encode(&b64, 0, 0, &d);
1113 dstr_write(&d, fp);
1114 if (b->tag == T_FILE) {
1115 putc(' ', fp);
1116 putstring(fp, b->d.buf, 0);
1117 }
1118 } break;
1119 }
1120 putc('\n', fp);
1121 }
1122
1123 /* --- @bemit@ --- *
1124 *
1125 * Arguments: @block *b@ = pointer to block to write
1126 * @FILE *fp@ = file to write on
1127 * @ghash *h@ = pointer to hash function
1128 * @unsigned bin@ = binary/text flag
1129 *
1130 * Returns: ---
1131 *
1132 * Use: Spits out a block properly.
1133 */
1134
1135 static void bemit(block *b, FILE *fp, ghash *h, unsigned bin)
1136 {
1137 if (h || (fp && bin)) {
1138 dstr d = DSTR_INIT;
1139 blob(b, &d);
1140 if (h)
1141 GH_HASH(h, d.buf, d.len);
1142 if (fp && bin)
1143 fwrite(d.buf, d.len, 1, fp);
1144 }
1145 if (fp && !bin)
1146 bwrite(b, fp);
1147 }
1148
1149 /*----- Static variables --------------------------------------------------*/
1150
1151 static const char *keyring = "keyring";
1152
1153 /*----- Other shared functions --------------------------------------------*/
1154
1155 /* --- @keyreport@ --- *
1156 *
1157 * Arguments: @const char *file@ = filename containing the error
1158 * @int line@ = line number in file
1159 * @const char *err@ = error text message
1160 * @void *p@ = unimportant pointer
1161 *
1162 * Returns: ---
1163 *
1164 * Use: Reports errors during the opening of a key file.
1165 */
1166
1167 static void keyreport(const char *file, int line, const char *err, void *p)
1168 {
1169 moan("error in keyring `%s' at line `%s': %s", file, line, err);
1170 }
1171
1172 /* --- @fhash@ --- *
1173 *
1174 * Arguments: @const gchash *c@ = pointer to hash class
1175 * @const char *file@ = file to hash
1176 * @void *b@ = pointer to output buffer
1177 *
1178 * Returns: Zero if it worked, or nonzero for a system error.
1179 *
1180 * Use: Hashes a file.
1181 */
1182
1183 static int fhash(const gchash *c, const char *file, void *b)
1184 {
1185 FILE *fp = fopen(file, "rb");
1186 ghash *h = GH_INIT(c);
1187 char buf[4096];
1188 size_t sz;
1189 int rc = 0;
1190
1191 if (!fp)
1192 return (-1);
1193 while ((sz = fread(buf, 1, sizeof(buf), fp)) > 0)
1194 GH_HASH(h, buf, sz);
1195 if (ferror(fp))
1196 rc = -1;
1197 GH_DONE(h, b);
1198 GH_DESTROY(h);
1199 fclose(fp);
1200 return (rc);
1201 }
1202
1203 /* --- @fhex@ --- *
1204 *
1205 * Arguments: @FILE *fp@ = file to write on
1206 * @const void *p@ = pointer to data to be written
1207 * @size_t sz@ = size of the data to write
1208 *
1209 * Returns: ---
1210 *
1211 * Use: Emits a hex dump to a stream.
1212 */
1213
1214 static void fhex(FILE *fp, const void *p, size_t sz)
1215 {
1216 const octet *q = p;
1217 if (!sz)
1218 return;
1219 for (;;) {
1220 fprintf(fp, "%02x", *q++);
1221 sz--;
1222 if (!sz)
1223 break;
1224 /* putc(' ', fp); */
1225 }
1226 }
1227
1228 /*----- Signature generation ----------------------------------------------*/
1229
1230 static int sign(int argc, char *argv[])
1231 {
1232 #define f_raw 1u
1233 #define f_bin 2u
1234 #define f_bogus 4u
1235
1236 unsigned f = 0;
1237 const char *ki = "dsig";
1238 key_file kf;
1239 key *k;
1240 sig *s;
1241 time_t exp = KEXP_EXPIRE;
1242 unsigned verb = 0;
1243 const char *ifile = 0;
1244 const char *ofile = 0;
1245 const char *c = 0;
1246 FILE *ifp, *ofp;
1247 dstr d = DSTR_INIT;
1248 block b;
1249 int e;
1250
1251 for (;;) {
1252 static struct option opts[] = {
1253 { "null", 0, 0, '0' },
1254 { "binary", 0, 0, 'b' },
1255 { "verbose", 0, 0, 'v' },
1256 { "quiet", 0, 0, 'q' },
1257 { "comment", OPTF_ARGREQ, 0, 'c' },
1258 { "file", OPTF_ARGREQ, 0, 'f' },
1259 { "output", OPTF_ARGREQ, 0, 'o' },
1260 { "key", OPTF_ARGREQ, 0, 'k' },
1261 { "expire", OPTF_ARGREQ, 0, 'e' },
1262 { 0, 0, 0, 0 }
1263 };
1264 int i = mdwopt(argc, argv, "+0vqb c: f:o: k:e:", opts, 0, 0, 0);
1265 if (i < 0)
1266 break;
1267 switch (i) {
1268 case '0':
1269 f |= f_raw;
1270 break;
1271 case 'b':
1272 f |= f_bin;
1273 break;
1274 case 'v':
1275 verb++;
1276 break;
1277 case 'q':
1278 if (verb > 0)
1279 verb--;
1280 break;
1281 case 'c':
1282 c = optarg;
1283 break;
1284 case 'f':
1285 ifile = optarg;
1286 break;
1287 case 'o':
1288 ofile = optarg;
1289 break;
1290 case 'k':
1291 ki = optarg;
1292 break;
1293 case 'e':
1294 if (strcmp(optarg, "forever") == 0)
1295 exp = KEXP_FOREVER;
1296 else if ((exp = get_date(optarg, 0)) == -1)
1297 die(EXIT_FAILURE, "bad expiry time");
1298 break;
1299 default:
1300 f |= f_bogus;
1301 break;
1302 }
1303 }
1304 if (optind != argc || (f & f_bogus))
1305 die(EXIT_FAILURE, "Usage: sign [-options]");
1306
1307 /* --- Locate the signing key --- */
1308
1309 if (key_open(&kf, keyring, KOPEN_WRITE, keyreport, 0))
1310 die(EXIT_FAILURE, "couldn't open keyring `%s'", keyring);
1311 if ((k = key_bytag(&kf, ki)) == 0)
1312 die(EXIT_FAILURE, "couldn't find key `%s'", ki);
1313 key_fulltag(k, &d);
1314 if (exp == KEXP_FOREVER && k->exp != KEXP_FOREVER) {
1315 die(EXIT_FAILURE, "key `%s' expires: can't create nonexpiring signature",
1316 d.buf);
1317 }
1318 s = getsig(k, 1);
1319
1320 /* --- Open files --- */
1321
1322 if (!ifile)
1323 ifp = stdin;
1324 else if ((ifp = fopen(ifile, (f & f_raw) ? "rb" : "r")) == 0) {
1325 die(EXIT_FAILURE, "couldn't open input file `%s': %s",
1326 ifile, strerror(errno));
1327 }
1328
1329 if (!ofile)
1330 ofp = stdout;
1331 else if ((ofp = fopen(ofile, (f & f_bin) ? "wb" : "w")) == 0) {
1332 die(EXIT_FAILURE, "couldn't open output file `%s': %s",
1333 ofile, strerror(errno));
1334 }
1335
1336 /* --- Emit the start of the output --- */
1337
1338 binit(&b); b.tag = T_IDENT;
1339 dstr_putf(&b.d, "%s, Catacomb version " VERSION, QUIS);
1340 bemit(&b, ofp, 0, f & f_bin);
1341
1342 breset(&b); b.tag = T_KEYID; b.k = k->id;
1343 bemit(&b, ofp, 0, f & f_bin);
1344
1345 /* --- Start hashing, and emit the datestamps and things --- */
1346
1347 {
1348 time_t now = time(0);
1349
1350 breset(&b); b.tag = T_DATE; b.t = now; bemit(&b, ofp, s->h, f & f_bin);
1351 if (exp == KEXP_EXPIRE)
1352 exp = now + 86400 * 28;
1353 breset(&b); b.tag = T_EXPIRE; b.t = exp; bemit(&b, ofp, s->h, f & f_bin);
1354 if (c) {
1355 breset(&b); b.tag = T_COMMENT; DPUTS(&b.d, c);
1356 bemit(&b, ofp, s->h, f & f_bin);
1357 }
1358
1359 if (!(f & f_bin))
1360 putc('\n', ofp);
1361 }
1362
1363 /* --- Now hash the various files --- */
1364
1365 for (;;) {
1366
1367 /* --- Stop on an output error --- */
1368
1369 if (ferror(ofp)) {
1370 f |= f_bogus;
1371 break;
1372 }
1373
1374 /* --- Read the next filename to hash --- */
1375
1376 breset(&b);
1377 if (getstring(ifp, &b.d, f & f_raw))
1378 break;
1379 b.tag = T_FILE;
1380 DENSURE(&b.b, GH_CLASS(s->h)->hashsz);
1381 if (fhash(GH_CLASS(s->h), b.d.buf, b.b.buf)) {
1382 moan("Error reading `%s': %s", b.d.buf, strerror(errno));
1383 f |= f_bogus;
1384 } else {
1385 b.b.len += GH_CLASS(s->h)->hashsz;
1386 if (verb) {
1387 fhex(stderr, b.b.buf, b.b.len);
1388 fprintf(stderr, " %s\n", b.d.buf);
1389 }
1390 bemit(&b, ofp, s->h, f & f_bin);
1391 }
1392 }
1393
1394 /* --- Create the signature --- */
1395
1396 if (!(f & f_bogus)) {
1397 breset(&b);
1398 b.tag = T_SIGNATURE;
1399 if ((e = s->ops->doit(s, &b.b)) != 0) {
1400 moan("error creating signature: %s", key_strerror(e));
1401 f |= f_bogus;
1402 }
1403 if (!(f & f_bogus)) {
1404 bemit(&b, ofp, 0, f & f_bin);
1405 key_used(&kf, k, exp);
1406 }
1407 }
1408
1409 /* --- Tidy up at the end --- */
1410
1411 freesig(s);
1412 bdestroy(&b);
1413 if (ifile)
1414 fclose(ifp);
1415 if (ofile) {
1416 if (fclose(ofp))
1417 f |= f_bogus;
1418 } else {
1419 if (fflush(ofp))
1420 f |= f_bogus;
1421 }
1422 if ((e = key_close(&kf)) != 0) {
1423 switch (e) {
1424 case KWRITE_FAIL:
1425 die(EXIT_FAILURE, "couldn't write file `%s': %s",
1426 keyring, strerror(errno));
1427 case KWRITE_BROKEN:
1428 die(EXIT_FAILURE, "keyring file `%s' broken: %s (repair manually)",
1429 keyring, strerror(errno));
1430 }
1431 }
1432 if (f & f_bogus)
1433 die(EXIT_FAILURE, "error(s) occurred while creating signature");
1434 return (EXIT_SUCCESS);
1435
1436 #undef f_raw
1437 #undef f_bin
1438 #undef f_bogus
1439 }
1440
1441 /*----- Signature verification --------------------------------------------*/
1442
1443 static int verify(int argc, char *argv[])
1444 {
1445 #define f_bogus 1u
1446 #define f_bin 2u
1447 #define f_ok 4u
1448
1449 unsigned f = 0;
1450 unsigned verb = 1;
1451 key_file kf;
1452 key *k = 0;
1453 sig *s;
1454 dstr d = DSTR_INIT;
1455 FILE *fp;
1456 block b;
1457 int e;
1458
1459 /* --- Parse the options --- */
1460
1461 for (;;) {
1462 static struct option opts[] = {
1463 { "verbose", 0, 0, 'v' },
1464 { "quiet", 0, 0, 'q' },
1465 { 0, 0, 0, 0 }
1466 };
1467 int i = mdwopt(argc, argv, "+vq", opts, 0, 0, 0);
1468 if (i < 0)
1469 break;
1470 switch (i) {
1471 case 'v':
1472 verb++;
1473 break;
1474 case 'q':
1475 if (verb)
1476 verb--;
1477 break;
1478 default:
1479 f |= f_bogus;
1480 break;
1481 }
1482 }
1483 argc -= optind;
1484 argv += optind;
1485 if ((f & f_bogus) || argc > 1)
1486 die(EXIT_FAILURE, "Usage: verify [-qv] [file]");
1487
1488 /* --- Open the key file, and start reading the input file --- */
1489
1490 if (key_open(&kf, keyring, KOPEN_READ, keyreport, 0))
1491 die(EXIT_FAILURE, "couldn't open keyring `%s'\n", keyring);
1492 if (argc < 1)
1493 fp = stdin;
1494 else {
1495 if ((fp = fopen(argv[0], "rb")) == 0) {
1496 die(EXIT_FAILURE, "couldn't open file `%s': %s\n",
1497 argv[0], strerror(errno));
1498 }
1499 if (getc(fp) == 0) {
1500 ungetc(0, fp);
1501 f |= f_bin;
1502 } else {
1503 fclose(fp);
1504 if ((fp = fopen(argv[0], "r")) == 0) {
1505 die(EXIT_FAILURE, "couldn't open file `%s': %s\n",
1506 argv[0], strerror(errno));
1507 }
1508 }
1509 }
1510
1511 /* --- Read the introductory matter --- */
1512
1513 binit(&b);
1514 for (;;) {
1515 breset(&b);
1516 e = bget(&b, fp, f & f_bin);
1517 if (e < 0)
1518 die(EXIT_FAILURE, "error reading packet: %s", errtab[-e]);
1519 if (e >= T_BEGIN)
1520 break;
1521 switch (e) {
1522 case T_IDENT:
1523 if (verb > 2)
1524 printf("INFO ident: `%s'\n", b.d.buf);
1525 break;
1526 case T_KEYID:
1527 if ((k = key_byid(&kf, b.k)) == 0) {
1528 if (verb)
1529 printf("FAIL key %08lx not found\n", (unsigned long)b.k);
1530 exit(EXIT_FAILURE);
1531 }
1532 if (verb > 2) {
1533 DRESET(&b.d);
1534 key_fulltag(k, &b.d);
1535 printf("INFO key: %s\n", b.d.buf);
1536 }
1537 break;
1538 default:
1539 die(EXIT_FAILURE, "(internal) unknown packet type\n");
1540 break;
1541 }
1542 }
1543
1544 /* --- Initialize the hash function and start reading hashed packets --- */
1545
1546 s = getsig(k, 0);
1547
1548 if (!k) {
1549 if (verb)
1550 puts("FAIL no keyid packet found");
1551 exit(EXIT_FAILURE);
1552 }
1553
1554 for (;;) {
1555 switch (e) {
1556 case T_COMMENT:
1557 if (verb > 1)
1558 printf("INFO comment: `%s'\n", b.d.buf);
1559 bemit(&b, 0, s->h, 0);
1560 break;
1561 case T_DATE:
1562 if (verb > 2) {
1563 DRESET(&b.d);
1564 timestring(b.t, &b.d);
1565 printf("INFO date: %s\n", b.d.buf);
1566 }
1567 bemit(&b, 0, s->h, 0);
1568 break;
1569 case T_EXPIRE: {
1570 time_t now = time(0);
1571 if (b.t < now) {
1572 if (verb > 1)
1573 puts("BAD signature has expired");
1574 f |= f_bogus;
1575 }
1576 if (verb > 2) {
1577 DRESET(&b.d);
1578 timestring(b.t, &b.d);
1579 printf("INFO expires: %s\n", b.d.buf);
1580 }
1581 bemit(&b, 0, s->h, 0);
1582 } break;
1583 case T_FILE:
1584 DRESET(&d);
1585 DENSURE(&d, GH_CLASS(s->h)->hashsz);
1586 if (fhash(GH_CLASS(s->h), b.d.buf, d.buf)) {
1587 if (verb > 1) {
1588 printf("BAD error reading file `%s': %s\n",
1589 b.d.buf, strerror(errno));
1590 }
1591 f |= f_bogus;
1592 } else if (b.b.len != GH_CLASS(s->h)->hashsz ||
1593 memcmp(d.buf, b.b.buf, b.b.len) != 0) {
1594 if (verb > 1)
1595 printf("BAD file `%s' has incorrect hash\n", b.d.buf);
1596 f |= f_bogus;
1597 } else if (verb > 3) {
1598 fputs("INFO hash: ", stdout);
1599 fhex(stdout, b.b.buf, b.b.len);
1600 printf(" %s\n", b.d.buf);
1601 }
1602 bemit(&b, 0, s->h, 0);
1603 break;
1604 case T_SIGNATURE:
1605 if (s->ops->doit(s, &b.b)) {
1606 if (verb > 1)
1607 puts("BAD bad signature");
1608 f |= f_bogus;
1609 } else if (verb > 2)
1610 puts("INFO good signature");
1611 goto done;
1612 default:
1613 if (verb)
1614 printf("FAIL invalid packet type %i\n", e);
1615 exit(EXIT_FAILURE);
1616 break;
1617 }
1618 breset(&b);
1619 e = bget(&b, fp, f & f_bin);
1620 if (e < 0) {
1621 if (verb)
1622 printf("FAIL error reading packet: %s\n", errtab[-e]);
1623 exit(EXIT_FAILURE);
1624 }
1625 }
1626 done:
1627 bdestroy(&b);
1628 dstr_destroy(&d);
1629 freesig(s);
1630 key_close(&kf);
1631 if (fp != stdin)
1632 fclose(fp);
1633 if (verb) {
1634 if (f & f_bogus)
1635 puts("FAIL signature invalid");
1636 else
1637 puts("OK signature verified");
1638 }
1639 return (f & f_bogus ? EXIT_FAILURE : EXIT_SUCCESS);
1640
1641 #undef f_bogus
1642 #undef f_bin
1643 #undef f_ok
1644 }
1645
1646 /*----- Main code ---------------------------------------------------------*/
1647
1648 typedef struct cmd {
1649 const char *name;
1650 int (*func)(int /*argc*/, char */*argv*/[]);
1651 const char *usage;
1652 const char *help;
1653 } cmd;
1654
1655 static cmd cmdtab[] = {
1656 /* { "manifest", manifest, */
1657 /* "manifest [-0] [-o output]" }, */
1658 { "sign", sign,
1659 "sign [-options]\n\
1660 [-0bqv] [-c comment] [-k tag] [-i keyid]\n\
1661 [-e expire] [-f file] [-o output]", "\
1662 Options:\n\
1663 \n\
1664 -0, --null Read null-terminated filenames from stdin.\n\
1665 -b, --binary Produce a binary output file.\n\
1666 -q, --quiet Produce fewer messages while working.\n\
1667 -v, --verbose Produce more messages while working.\n\
1668 -c, --comment=COMMENT Include COMMENT in the output file.\n\
1669 -f, --file=FILE Read filenames to hash from FILE.\n\
1670 -o, --output=FILE Write the signed result to FILE.\n\
1671 -k, --key=TAG Use a key named by TAG.\n\
1672 -e, --expire=TIME The signature should expire after TIME.\n\
1673 " },
1674 { "verify", verify,
1675 "verify [-qv] [file]", "\
1676 Options:\n\
1677 \n\
1678 -q, --quiet Produce fewer messages while working.\n\
1679 -v, --verbose Produce more messages while working.\n\
1680 " },
1681 { 0, 0, 0 }
1682 };
1683
1684 /* --- @findcmd@ --- *
1685 *
1686 * Arguments: @const char *name@ = a command name
1687 *
1688 * Returns: Pointer to the command structure.
1689 *
1690 * Use: Looks up a command by name. If the command isn't found, an
1691 * error is reported and the program is terminated.
1692 */
1693
1694 static cmd *findcmd(const char *name)
1695 {
1696 cmd *c, *chosen = 0;
1697 size_t sz = strlen(name);
1698
1699 for (c = cmdtab; c->name; c++) {
1700 if (strncmp(name, c->name, sz) == 0) {
1701 if (c->name[sz] == 0) {
1702 chosen = c;
1703 break;
1704 } else if (chosen)
1705 die(EXIT_FAILURE, "ambiguous command name `%s'", name);
1706 else
1707 chosen = c;
1708 }
1709 }
1710 if (!chosen)
1711 die(EXIT_FAILURE, "unknown command name `%s'", name);
1712 return (chosen);
1713 }
1714
1715 static void version(FILE *fp)
1716 {
1717 pquis(fp, "$, Catacomb version " VERSION "\n");
1718 }
1719
1720 static void usage(FILE *fp)
1721 {
1722 pquis(fp, "Usage: $ [-k keyring] command [args]\n");
1723 }
1724
1725 static void help(FILE *fp, char **argv)
1726 {
1727 cmd *c;
1728
1729 if (*argv) {
1730 c = findcmd(*argv);
1731 fprintf(fp, "Usage: %s [-k keyring] %s\n", QUIS, c->usage);
1732 if (c->help) {
1733 fputc('\n', fp);
1734 fputs(c->help, fp);
1735 }
1736 } else {
1737 version(fp);
1738 fputc('\n', fp);
1739 usage(fp);
1740 fputs("\n\
1741 Create and verify signatures on lists of files.\n\
1742 \n", fp);
1743 for (c = cmdtab; c->name; c++)
1744 fprintf(fp, "%s\n", c->usage);
1745 }
1746 }
1747
1748 /* --- @main@ --- *
1749 *
1750 * Arguments: @int argc@ = number of command line arguments
1751 * @char *argv[]@ = vector of command line arguments
1752 *
1753 * Returns: Zero if successful, nonzero otherwise.
1754 *
1755 * Use: Signs or verifies signatures on lists of files. Useful for
1756 * ensuring that a distribution is unmolested.
1757 */
1758
1759 int main(int argc, char *argv[])
1760 {
1761 unsigned f = 0;
1762
1763 #define f_bogus 1u
1764
1765 /* --- Initialize the library --- */
1766
1767 ego(argv[0]);
1768 sub_init();
1769 rand_noisesrc(RAND_GLOBAL, &noise_source);
1770 rand_seed(RAND_GLOBAL, 160);
1771
1772 /* --- Parse options --- */
1773
1774 for (;;) {
1775 static struct option opts[] = {
1776 { "help", 0, 0, 'h' },
1777 { "version", 0, 0, 'v' },
1778 { "usage", 0, 0, 'u' },
1779 { "keyring", OPTF_ARGREQ, 0, 'k' },
1780 { 0, 0, 0, 0 }
1781 };
1782 int i = mdwopt(argc, argv, "+hvu k:", opts, 0, 0, 0);
1783 if (i < 0)
1784 break;
1785 switch (i) {
1786 case 'h':
1787 help(stdout, argv + optind);
1788 exit(0);
1789 break;
1790 case 'v':
1791 version(stdout);
1792 exit(0);
1793 break;
1794 case 'u':
1795 usage(stdout);
1796 exit(0);
1797 case 'k':
1798 keyring = optarg;
1799 break;
1800 default:
1801 f |= f_bogus;
1802 break;
1803 }
1804 }
1805
1806 argc -= optind;
1807 argv += optind;
1808 optind = 0;
1809 if (f & f_bogus || argc < 1) {
1810 usage(stderr);
1811 exit(EXIT_FAILURE);
1812 }
1813
1814 /* --- Dispatch to the correct subcommand handler --- */
1815
1816 return (findcmd(argv[0])->func(argc, argv));
1817
1818 #undef f_bogus
1819 }
1820
1821 /*----- That's all, folks -------------------------------------------------*/