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