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