Shut up some warnings.
[u/mdw/catacomb] / dsig.c
1 /* -*-c-*-
2 *
3 * $Id: dsig.c,v 1.5 2000/10/08 12:12:09 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.5 2000/10/08 12:12:09 mdw
34 * Shut up some warnings.
35 *
36 * Revision 1.4 2000/08/04 23:23:44 mdw
37 * Various <ctype.h> fixes.
38 *
39 * Revision 1.3 2000/07/15 20:53:23 mdw
40 * More hash functions. Bug fix in getstring.
41 *
42 * Revision 1.2 2000/07/01 11:27:22 mdw
43 * Use new PKCS#1 padding functions rather than rolling by hand.
44 *
45 * Revision 1.1 2000/06/17 10:54:29 mdw
46 * Program to generate and verify signatures on multiple files.
47 *
48 */
49
50 /*----- Header files ------------------------------------------------------*/
51
52 #include "config.h"
53
54 #include <ctype.h>
55 #include <errno.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59
60 #include <mLib/alloc.h>
61 #include <mLib/base64.h>
62 #include <mLib/mdwopt.h>
63 #include <mLib/quis.h>
64 #include <mLib/report.h>
65 #include <mLib/sub.h>
66
67 #include "getdate.h"
68 #include "grand.h"
69 #include "ghash.h"
70 #include "key.h"
71 #include "key-data.h"
72 #include "noise.h"
73
74 #include "dsa.h"
75 #include "rsa.h"
76 #include "pkcs1.h"
77
78 #include "md4.h"
79 #include "md5.h"
80 #include "rmd128.h"
81 #include "rmd160.h"
82 #include "rmd256.h"
83 #include "rmd320.h"
84 #include "sha.h"
85 #include "tiger.h"
86
87 /*----- Digital signature algorithm ---------------------------------------*/
88
89 static int dsasign(key *k, const void *m, size_t msz, dstr *d)
90 {
91 dsa_priv dp;
92 key_packstruct ks[DSA_PRIVFETCHSZ];
93 key_packdef *kp;
94 size_t sz;
95 octet *p;
96 int e;
97
98 kp = key_fetchinit(dsa_privfetch, ks, &dp);
99 if ((e = key_fetch(kp, k)) != 0) {
100 key_fetchdone(kp);
101 return (e);
102 }
103 sz = mp_octets(dp.dp.q);
104 if (sz < msz)
105 die(EXIT_FAILURE, "hash function too wide for this signing key");
106 DENSURE(d, sz * 2);
107 p = (octet *)d->buf + d->len;
108 rand_get(RAND_GLOBAL, p, sz);
109 dsa_sign(&dp.dp, dp.x, m, msz, p, sz, p, sz, p + sz, sz);
110 d->len += sz * 2;
111 key_fetchdone(kp);
112 return (0);
113 }
114
115 static int dsaverify(key *k, const void *m, size_t msz,
116 const void *s, size_t ssz)
117 {
118 dsa_pub dp;
119 key_packstruct ks[DSA_PUBFETCHSZ];
120 key_packdef *kp;
121 size_t sz;
122 const octet *p = s;
123 int e;
124
125 kp = key_fetchinit(dsa_pubfetch, ks, &dp);
126 if ((e = key_fetch(kp, k)) != 0) {
127 key_fetchdone(kp);
128 return (e);
129 }
130 sz = ssz / 2;
131 e = dsa_verify(&dp.dp, dp.y, m, msz, p, sz, p + sz, sz);
132 key_fetchdone(kp);
133 return (e);
134 }
135
136 /*----- RSA signing -------------------------------------------------------*/
137
138 static int rsasign(key *k, const void *m, size_t msz, dstr *d)
139 {
140 rsa_priv rp;
141 rsa_privctx rpc;
142 pkcs1 pk = { 0, 0, 0 };
143 key_packstruct ks[RSA_PRIVFETCHSZ];
144 key_packdef *kp;
145 int e;
146
147 kp = key_fetchinit(rsa_privfetch, ks, &rp);
148 if ((e = key_fetch(kp, k)) != 0) {
149 key_fetchdone(kp);
150 return (e);
151 }
152 rsa_privcreate(&rpc, &rp, &rand_global);
153 if (rsa_sign(&rpc, m, msz, d, pkcs1_sigencode, &pk) < 0)
154 die(EXIT_FAILURE, "internal error in rsasign (key too small?)");
155 rsa_privdestroy(&rpc);
156 key_fetchdone(kp);
157 return (0);
158 }
159
160 static int rsaverify(key *k, const void *m, size_t msz,
161 const void *s, size_t ssz)
162 {
163 rsa_pub rp;
164 rsa_pubctx rpc;
165 pkcs1 pk = { 0, 0, 0 };
166 key_packstruct ks[RSA_PUBFETCHSZ];
167 key_packdef *kp;
168 int ok = 0;
169 dstr d = DSTR_INIT;
170 int e;
171
172 kp = key_fetchinit(rsa_pubfetch, ks, &rp);
173 if ((e = key_fetch(kp, k)) != 0) {
174 key_fetchdone(kp);
175 return (e);
176 }
177 rsa_pubcreate(&rpc, &rp);
178 if (rsa_verify(&rpc, s, ssz, &d, pkcs1_sigdecode, &pk) > 0 &&
179 msz == d.len && memcmp(d.buf, m, msz) == 0)
180 ok = 1;
181 dstr_destroy(&d);
182 rsa_pubdestroy(&rpc);
183 key_fetchdone(kp);
184 return (ok);
185 }
186
187 /*----- Algorithm choice --------------------------------------------------*/
188
189 typedef struct sig {
190 const char *name;
191 const char *type;
192 int (*sign)(key */*k*/, const void */*m*/, size_t /*msz*/, dstr */*d*/);
193 int (*verify)(key */*k*/, const void */*m*/, size_t /*msz*/,
194 const void */*s*/, size_t /*ssz*/);
195 } sig;
196
197 static const gchash *hashtab[] = {
198 &rmd160, &tiger, &sha, &rmd128, &rmd256, &rmd320, &md5, &md4, 0 };
199 static sig sigtab[] = {
200 { "dsa", "dsig-dsa", dsasign, dsaverify },
201 { "rsa", "dsig-rsa", rsasign, rsaverify },
202 { 0, 0, 0 }
203 };
204
205 /* --- @gethash@ --- *
206 *
207 * Arguments: @const char *name@ = pointer to name string
208 *
209 * Returns: Pointer to appropriate hash class.
210 *
211 * Use: Chooses a hash function by name.
212 */
213
214 static const gchash *gethash(const char *name)
215 {
216 const gchash **g, *gg = 0;
217 size_t sz = strlen(name);
218 for (g = hashtab; *g; g++) {
219 if (strncmp(name, (*g)->name, sz) == 0) {
220 if ((*g)->name[sz] == 0) {
221 gg = *g;
222 break;
223 } else if (gg)
224 return (0);
225 else
226 gg = *g;
227 }
228 }
229 return (gg);
230 }
231
232 /* --- @getsig@ --- *
233 *
234 * Arguments: @const char *name@ = pointer to name string
235 *
236 * Returns: Pointer to appropriate signature type.
237 *
238 * Use: Chooses a signature algorithm by name.
239 */
240
241 static sig *getsig(const char *name)
242 {
243 sig *s, *ss = 0;
244 size_t sz = strlen(name);
245 for (s = sigtab; s->name; s++) {
246 if (strncmp(name, s->name, sz) == 0) {
247 if (s->name[sz] == 0) {
248 ss = s;
249 break;
250 } else if (ss)
251 return (0);
252 else
253 ss = s;
254 }
255 }
256 return (ss);
257 }
258
259 /*----- Data formatting ---------------------------------------------------*/
260
261 /* --- Binary data structure --- *
262 *
263 * The binary format, which is used for hashing and for the optional binary
264 * output, consists of a sequence of tagged blocks. The tag describes the
265 * format and meaining of the following data.
266 */
267
268 enum {
269 /* --- Block tags --- */
270
271 T_IDENT = 0, /* An identifying marker */
272 T_SIGALG, /* Signature algorithm used */
273 T_HASHALG, /* Hash algorithm used */
274 T_KEYID, /* Key identifier */
275 T_BEGIN, /* Begin hashing here */
276 T_COMMENT = T_BEGIN, /* A textual comment */
277 T_DATE, /* Creation date of signature */
278 T_EXPIRE, /* Expiry date of signature */
279 T_FILE, /* File and corresponding hash */
280 T_SIGNATURE, /* Final signature block */
281
282 /* --- Error messages --- */
283
284 E_EOF = -1,
285 E_BIN = -2,
286 E_TAG = -3,
287 E_DATE = -4
288 };
289
290 /* --- Name translation table --- */
291
292 static const char *tagtab[] = {
293 "ident:", "sigalg:", "hashalg:", "keyid:",
294 "comment:", "date:", "expires:", "file:",
295 "signature:",
296 0
297 };
298
299 static const char *errtab[] = {
300 "Off-by-one bug",
301 "Unexpected end-of-file",
302 "Binary object too large",
303 "Unrecognized tag",
304 "Bad date string"
305 };
306
307 /* --- Memory representation of block types --- */
308
309 typedef struct block {
310 int tag; /* Type tag */
311 dstr d; /* String data */
312 dstr b; /* Binary data */
313 time_t t; /* Timestamp */
314 uint32 k; /* Keyid */
315 } block;
316
317 /* --- @getstring@ --- *
318 *
319 * Arguments: @FILE *fp@ = stream from which to read
320 * @dstr *d@ = destination string
321 * @unsigned raw@ = raw or cooked read
322 *
323 * Returns: Zero if OK, nonzero on end-of-file.
324 *
325 * Use: Reads a filename (or something similar) from a stream.
326 */
327
328 static int getstring(FILE *fp, dstr *d, unsigned raw)
329 {
330 int ch;
331 int q = 0;
332
333 /* --- Raw: just read exactly what's written up to a null byte --- */
334
335 if (raw) {
336 if ((ch = getc(fp)) == EOF)
337 return (EOF);
338 for (;;) {
339 if (!ch)
340 break;
341 DPUTC(d, ch);
342 if ((ch = getc(fp)) == EOF)
343 break;
344 }
345 DPUTZ(d);
346 return (0);
347 }
348
349 /* --- Skip as far as whitespace --- *
350 *
351 * Also skip past comments.
352 */
353
354 again:
355 ch = getc(fp);
356 while (isspace(ch))
357 ch = getc(fp);
358 if (ch == '#') {
359 do ch = getc(fp); while (ch != '\n' && ch != EOF);
360 goto again;
361 }
362 if (ch == EOF)
363 return (EOF);
364
365 /* --- If the character is a quote then read a quoted string --- */
366
367 switch (ch) {
368 case '`':
369 ch = '\'';
370 case '\'':
371 case '\"':
372 q = ch;
373 ch = getc(fp);
374 break;
375 }
376
377 /* --- Now read all sorts of interesting things --- */
378
379 for (;;) {
380
381 /* --- Handle an escaped thing --- */
382
383 if (ch == '\\') {
384 ch = getc(fp);
385 if (ch == EOF)
386 break;
387 switch (ch) {
388 case 'a': ch = '\a'; break;
389 case 'b': ch = '\b'; break;
390 case 'f': ch = '\f'; break;
391 case 'n': ch = '\n'; break;
392 case 'r': ch = '\r'; break;
393 case 't': ch = '\t'; break;
394 case 'v': ch = '\v'; break;
395 }
396 DPUTC(d, ch);
397 ch = getc(fp);
398 continue;
399 }
400
401 /* --- If it's a quote or some other end marker then stop --- */
402
403 if (ch == q || (!q && isspace((unsigned char)ch)))
404 break;
405
406 /* --- Otherwise contribute and continue --- */
407
408 DPUTC(d, ch);
409 if ((ch = getc(fp)) == EOF)
410 break;
411 }
412
413 /* --- Done --- */
414
415 DPUTZ(d);
416 return (0);
417 }
418
419 /* --- @putstring@ --- *
420 *
421 * Arguments: @FILE *fp@ = stream to write on
422 * @const char *p@ = pointer to text
423 * @unsigned raw@ = whether the string is to be written raw
424 *
425 * Returns: ---
426 *
427 * Use: Emits a string to a stream.
428 */
429
430 static void putstring(FILE *fp, const char *p, unsigned raw)
431 {
432 size_t sz = strlen(p);
433 unsigned qq;
434 const char *q;
435
436 /* --- Just write the string null terminated if raw --- */
437
438 if (raw) {
439 fwrite(p, 1, sz + 1, fp);
440 return;
441 }
442
443 /* --- Check for any dodgy characters --- */
444
445 qq = 0;
446 for (q = p; *q; q++) {
447 if (isspace((unsigned char)*q)) {
448 qq = '\"';
449 break;
450 }
451 }
452
453 if (qq)
454 putc(qq, fp);
455
456 /* --- Emit the string --- */
457
458 for (q = p; *q; q++) {
459 switch (*q) {
460 case '\a': fputc('\\', fp); fputc('a', fp); break;
461 case '\b': fputc('\\', fp); fputc('b', fp); break;
462 case '\f': fputc('\\', fp); fputc('f', fp); break;
463 case '\n': fputc('\\', fp); fputc('n', fp); break;
464 case '\r': fputc('\\', fp); fputc('r', fp); break;
465 case '\t': fputc('\\', fp); fputc('t', fp); break;
466 case '\v': fputc('\\', fp); fputc('v', fp); break;
467 case '`': fputc('\\', fp); fputc('`', fp); break;
468 case '\'': fputc('\\', fp); fputc('\'', fp); break;
469 case '\"': fputc('\\', fp); fputc('\"', fp); break;
470 default:
471 putc(*q, fp);
472 break;
473 }
474 }
475
476 /* --- Done --- */
477
478 if (qq)
479 putc(qq, fp);
480 }
481
482 /* --- @timestring@ --- *
483 *
484 * Arguments: @time_t t@ = a timestamp
485 * @dstr *d@ = a string to write on
486 *
487 * Returns: ---
488 *
489 * Use: Writes a textual representation of the timestamp to the
490 * string.
491 */
492
493 static void timestring(time_t t, dstr *d)
494 {
495 if (t == KEXP_FOREVER)
496 DPUTS(d, "forever");
497 else {
498 struct tm *tm = localtime(&t);
499 DENSURE(d, 32);
500 d->len += strftime(d->buf + d->len, 32, "%Y-%m-%d %H:%M:%S %Z", tm);
501 DPUTZ(d);
502 }
503 }
504
505 /* --- @breset@ --- *
506 *
507 * Arguments: @block *b@ = block to reset
508 *
509 * Returns: ---
510 *
511 * Use: Resets a block so that more stuff can be put in it.
512 */
513
514 static void breset(block *b)
515 {
516 b->tag = 0;
517 DRESET(&b->d);
518 DRESET(&b->b);
519 b->k = 0;
520 b->t = KEXP_EXPIRE;
521 }
522
523 /* --- @binit@ --- *
524 *
525 * Arguments: @block *b@ = block to initialize
526 *
527 * Returns: ---
528 *
529 * Use: Initializes a block as something to read into.
530 */
531
532 static void binit(block *b)
533 {
534 dstr_create(&b->d);
535 dstr_create(&b->b);
536 breset(b);
537 }
538
539 /* --- @bdestroy@ --- *
540 *
541 * Arguments: @block *b@ = block to destroy
542 *
543 * Returns: ---
544 *
545 * Use: Destroys a block's contents.
546 */
547
548 static void bdestroy(block *b)
549 {
550 dstr_destroy(&b->d);
551 dstr_destroy(&b->b);
552 }
553
554 /* --- @bget@ --- *
555 *
556 * Arguments: @block *b@ = pointer to block
557 * @FILE *fp@ = stream to read from
558 * @unsigned bin@ = binary switch
559 *
560 * Returns: Tag of block, or an error tag.
561 *
562 * Use: Reads a block from a stream.
563 */
564
565 static int bget(block *b, FILE *fp, unsigned bin)
566 {
567 int tag;
568
569 /* --- Read the tag --- */
570
571 if (bin)
572 tag = getc(fp);
573 else {
574 dstr d = DSTR_INIT;
575 if (getstring(fp, &d, 0))
576 return (E_EOF);
577 for (tag = 0; tagtab[tag]; tag++) {
578 if (strcmp(tagtab[tag], d.buf) == 0)
579 goto done;
580 }
581 return (E_TAG);
582 done:;
583 }
584
585 /* --- Decide what to do next --- */
586
587 breset(b);
588 b->tag = tag;
589 switch (tag) {
590
591 /* --- Reading of strings --- */
592
593 case T_IDENT:
594 case T_COMMENT:
595 case T_SIGALG:
596 case T_HASHALG:
597 if (getstring(fp, &b->d, bin))
598 return (E_EOF);
599 break;
600
601 /* --- Timestamps --- */
602
603 case T_DATE:
604 case T_EXPIRE:
605 if (bin) {
606 octet buf[8];
607 if (fread(buf, sizeof(buf), 1, fp) < 1)
608 return (E_EOF);
609 b->t = ((time_t)(((LOAD32(buf + 0) << 16) << 16) & ~MASK32) |
610 (time_t)LOAD32(buf + 4));
611 } else {
612 if (getstring(fp, &b->d, 0))
613 return (E_EOF);
614 if (strcmp(b->d.buf, "forever") == 0)
615 b->t = KEXP_FOREVER;
616 else if ((b->t = get_date(b->d.buf, 0)) == -1)
617 return (E_DATE);
618 }
619 break;
620
621 /* --- Key ids --- */
622
623 case T_KEYID:
624 if (bin) {
625 octet buf[4];
626 if (fread(buf, sizeof(buf), 1, fp) < 1)
627 return (E_EOF);
628 b->k = LOAD32(buf);
629 } else {
630 if (getstring(fp, &b->d, 0))
631 return (E_EOF);
632 b->k = strtoul(b->d.buf, 0, 16);
633 }
634 break;
635
636 /* --- Reading of binary data --- */
637
638 case T_FILE:
639 case T_SIGNATURE:
640 if (bin) {
641 octet buf[2];
642 uint32 sz;
643 if (fread(buf, sizeof(buf), 1, fp) < 1)
644 return (E_EOF);
645 sz = LOAD16(buf);
646 if (sz > 4096)
647 return (E_BIN);
648 DENSURE(&b->b, sz);
649 if (fread(b->b.buf + b->b.len, 1, sz, fp) < sz)
650 return (E_EOF);
651 b->b.len += sz;
652 } else {
653 base64_ctx b64;
654 if (getstring(fp, &b->d, 0))
655 return (E_EOF);
656 base64_init(&b64);
657 base64_decode(&b64, b->d.buf, b->d.len, &b->b);
658 base64_decode(&b64, 0, 0, &b->b);
659 DRESET(&b->d);
660 }
661 if (tag == T_FILE && getstring(fp, &b->d, bin))
662 return (E_EOF);
663 break;
664
665 /* --- Anything else --- */
666
667 default:
668 return (E_TAG);
669 }
670
671 return (tag);
672 }
673
674 /* --- @blob@ --- *
675 *
676 * Arguments: @block *b@ = pointer to block to emit
677 * @dstr *d@ = output buffer
678 *
679 * Returns: ---
680 *
681 * Use: Encodes a block in a binary format.
682 */
683
684 static void blob(block *b, dstr *d)
685 {
686 DPUTC(d, b->tag);
687 switch (b->tag) {
688 case T_IDENT:
689 case T_SIGALG:
690 case T_HASHALG:
691 case T_COMMENT:
692 DPUTD(d, &b->d);
693 DPUTC(d, 0);
694 break;
695 case T_DATE:
696 case T_EXPIRE:
697 DENSURE(d, 8);
698 STORE32(d->buf + d->len, ((b->t & ~MASK32) >> 16) >> 16);
699 STORE32(d->buf + d->len + 4, b->t);
700 d->len += 8;
701 break;
702 case T_KEYID:
703 DENSURE(d, 4);
704 STORE32(d->buf + d->len, b->k);
705 d->len += 4;
706 break;
707 case T_FILE:
708 case T_SIGNATURE:
709 DENSURE(d, 2);
710 STORE16(d->buf + d->len, b->b.len);
711 d->len += 2;
712 DPUTD(d, &b->b);
713 if (b->tag == T_FILE) {
714 DPUTD(d, &b->d);
715 DPUTC(d, 0);
716 }
717 break;
718 }
719 }
720
721 /* --- @bwrite@ --- *
722 *
723 * Arguments: @block *b@ = pointer to block to write
724 * @FILE *fp@ = stream to write on
725 *
726 * Returns: ---
727 *
728 * Use: Writes a block on a stream in a textual format.
729 */
730
731 static void bwrite(block *b, FILE *fp)
732 {
733 fputs(tagtab[b->tag], fp);
734 putc(' ', fp);
735 switch (b->tag) {
736 case T_IDENT:
737 case T_SIGALG:
738 case T_HASHALG:
739 case T_COMMENT:
740 putstring(fp, b->d.buf, 0);
741 break;
742 case T_DATE:
743 case T_EXPIRE: {
744 dstr d = DSTR_INIT;
745 timestring(b->t, &d);
746 putstring(fp, d.buf, 0);
747 dstr_destroy(&d);
748 } break;
749 case T_KEYID:
750 fprintf(fp, "%08lx", (unsigned long)b->k);
751 break;
752 case T_FILE:
753 case T_SIGNATURE: {
754 dstr d = DSTR_INIT;
755 base64_ctx b64;
756 base64_init(&b64);
757 b64.maxline = 0;
758 base64_encode(&b64, b->b.buf, b->b.len, &d);
759 base64_encode(&b64, 0, 0, &d);
760 dstr_write(&d, fp);
761 if (b->tag == T_FILE) {
762 putc(' ', fp);
763 putstring(fp, b->d.buf, 0);
764 }
765 } break;
766 }
767 putc('\n', fp);
768 }
769
770 /* --- @bemit@ --- *
771 *
772 * Arguments: @block *b@ = pointer to block to write
773 * @FILE *fp@ = file to write on
774 * @ghash *h@ = pointer to hash function
775 * @unsigned bin@ = binary/text flag
776 *
777 * Returns: ---
778 *
779 * Use: Spits out a block properly.
780 */
781
782 static void bemit(block *b, FILE *fp, ghash *h, unsigned bin)
783 {
784 if (h || (fp && bin)) {
785 dstr d = DSTR_INIT;
786 blob(b, &d);
787 if (h)
788 h->ops->hash(h, d.buf, d.len);
789 if (fp && bin)
790 fwrite(d.buf, d.len, 1, fp);
791 }
792 if (fp && !bin)
793 bwrite(b, fp);
794 }
795
796 /*----- Static variables --------------------------------------------------*/
797
798 static const char *keyring = "keyring";
799
800 /*----- Other shared functions --------------------------------------------*/
801
802 /* --- @keyreport@ --- *
803 *
804 * Arguments: @const char *file@ = filename containing the error
805 * @int line@ = line number in file
806 * @const char *err@ = error text message
807 * @void *p@ = unimportant pointer
808 *
809 * Returns: ---
810 *
811 * Use: Reports errors during the opening of a key file.
812 */
813
814 static void keyreport(const char *file, int line, const char *err, void *p)
815 {
816 moan("error in keyring `%s' at line `%s': %s", file, line, err);
817 }
818
819 /* --- @fhash@ --- *
820 *
821 * Arguments: @const gchash *c@ = pointer to hash class
822 * @const char *file@ = file to hash
823 * @void *b@ = pointer to output buffer
824 *
825 * Returns: Zero if it worked, or nonzero for a system error.
826 *
827 * Use: Hashes a file.
828 */
829
830 static int fhash(const gchash *c, const char *file, void *b)
831 {
832 FILE *fp = fopen(file, "rb");
833 ghash *h = c->init();
834 char buf[4096];
835 size_t sz;
836 int rc = 0;
837
838 if (!fp)
839 return (-1);
840 while ((sz = fread(buf, 1, sizeof(buf), fp)) > 0)
841 h->ops->hash(h, buf, sz);
842 if (ferror(fp))
843 rc = -1;
844 h->ops->done(h, b);
845 h->ops->destroy(h);
846 fclose(fp);
847 return (rc);
848 }
849
850 /* --- @fhex@ --- *
851 *
852 * Arguments: @FILE *fp@ = file to write on
853 * @const void *p@ = pointer to data to be written
854 * @size_t sz@ = size of the data to write
855 *
856 * Returns: ---
857 *
858 * Use: Emits a hex dump to a stream.
859 */
860
861 static void fhex(FILE *fp, const void *p, size_t sz)
862 {
863 const octet *q = p;
864 if (!sz)
865 return;
866 for (;;) {
867 fprintf(fp, "%02x", *q++);
868 sz--;
869 if (!sz)
870 break;
871 /* putc(' ', fp); */
872 }
873 }
874
875 /*----- Signature generation ----------------------------------------------*/
876
877 static int sign(int argc, char *argv[])
878 {
879 enum {
880 f_raw = 1,
881 f_bin = 2,
882 f_bogus = 4
883 };
884
885 unsigned f = 0;
886 const char *kt = 0;
887 const char *ki = 0;
888 key_file kf;
889 key *k;
890 const sig *s = sigtab;
891 const gchash *gch = &rmd160;
892 ghash *h;
893 time_t exp = KEXP_EXPIRE;
894 unsigned verb = 0;
895 const char *ifile = 0;
896 const char *ofile = 0;
897 const char *c = 0;
898 FILE *ifp, *ofp;
899 dstr d = DSTR_INIT;
900 block b;
901 int e;
902
903 for (;;) {
904 static struct option opts[] = {
905 { "null", 0, 0, '0' },
906 { "binary", 0, 0, 'b' },
907 { "verbose", 0, 0, 'v' },
908 { "quiet", 0, 0, 'q' },
909 { "algorithm", OPTF_ARGREQ, 0, 'a' },
910 { "hash", OPTF_ARGREQ, 0, 'h' },
911 { "comment", OPTF_ARGREQ, 0, 'c' },
912 { "file", OPTF_ARGREQ, 0, 'f' },
913 { "output", OPTF_ARGREQ, 0, 'o' },
914 { "keytype", OPTF_ARGREQ, 0, 't' },
915 { "keyid", OPTF_ARGREQ, 0, 'i' },
916 { "key", OPTF_ARGREQ, 0, 'i' },
917 { "expire", OPTF_ARGREQ, 0, 'e' },
918 { 0, 0, 0, 0 }
919 };
920 int i = mdwopt(argc, argv, "+0vqb a:h:c: f:o: t:i:k:e:", opts, 0, 0, 0);
921 if (i < 0)
922 break;
923 switch (i) {
924 case '0':
925 f |= f_raw;
926 break;
927 case 'b':
928 f |= f_bin;
929 break;
930 case 'v':
931 verb++;
932 break;
933 case 'q':
934 if (verb > 0)
935 verb--;
936 break;
937 case 'a':
938 if ((s = getsig(optarg)) == 0) {
939 die(EXIT_FAILURE, "unknown or ambiguous signature algorithm `%s'",
940 optarg);
941 }
942 break;
943 case 'h':
944 if ((gch = gethash(optarg)) == 0) {
945 die(EXIT_FAILURE, "unknown or ambiguous hash function `%s'",
946 optarg);
947 }
948 break;
949 case 'c':
950 c = optarg;
951 break;
952 case 'f':
953 ifile = optarg;
954 break;
955 case 'o':
956 ofile = optarg;
957 break;
958 case 't':
959 kt = optarg;
960 break;
961 case 'i':
962 case 'k':
963 ki = optarg;
964 break;
965 case 'e':
966 if (strcmp(optarg, "forever") == 0)
967 exp = KEXP_FOREVER;
968 else if ((exp = get_date(optarg, 0)) == -1)
969 die(EXIT_FAILURE, "bad expiry time");
970 break;
971 default:
972 f |= f_bogus;
973 break;
974 }
975 }
976 if (optind != argc || (f & f_bogus))
977 die(EXIT_FAILURE, "Usage: sign [-options]");
978
979 /* --- Locate the signing key --- */
980
981 if (key_open(&kf, keyring, KOPEN_WRITE, keyreport, 0))
982 die(EXIT_FAILURE, "couldn't open keyring `%s'", keyring);
983 if (ki) {
984 if ((k = key_bytag(&kf, ki)) == 0)
985 die(EXIT_FAILURE, "couldn't find key `%s'", ki);
986 } else {
987 if (!kt)
988 kt = s->type;
989 if ((k = key_bytype(&kf, kt)) == 0)
990 die(EXIT_FAILURE, "no appropriate key of type `%s'", kt);
991 }
992 key_fulltag(k, &d);
993 if (exp == KEXP_FOREVER && k->exp != KEXP_FOREVER) {
994 die(EXIT_FAILURE, "key `%s' expires: can't create nonexpiring signature",
995 d.buf);
996 }
997
998 /* --- Open files --- */
999
1000 if (!ifile)
1001 ifp = stdin;
1002 else if ((ifp = fopen(ifile, (f & f_raw) ? "rb" : "r")) == 0) {
1003 die(EXIT_FAILURE, "couldn't open input file `%s': %s",
1004 ifile, strerror(errno));
1005 }
1006
1007 if (!ofile)
1008 ofp = stdout;
1009 else if ((ofp = fopen(ofile, (f & f_bin) ? "wb" : "w")) == 0) {
1010 die(EXIT_FAILURE, "couldn't open output file `%s': %s",
1011 ofile, strerror(errno));
1012 }
1013
1014 /* --- Emit the start of the output --- */
1015
1016 binit(&b); b.tag = T_IDENT;
1017 dstr_putf(&b.d, "%s, Catacomb version " VERSION, QUIS);
1018 bemit(&b, ofp, 0, f & f_bin);
1019
1020 breset(&b); b.tag = T_SIGALG; DPUTS(&b.d, s->name);
1021 bemit(&b, ofp, 0, f & f_bin);
1022
1023 breset(&b); b.tag = T_HASHALG; DPUTS(&b.d, gch->name);
1024 bemit(&b, ofp, 0, f & f_bin);
1025
1026 breset(&b); b.tag = T_KEYID; b.k = k->id;
1027 bemit(&b, ofp, 0, f & f_bin);
1028
1029 /* --- Start hashing, and emit the datestamps and things --- */
1030
1031 {
1032 time_t now = time(0);
1033
1034 h = gch->init();
1035 breset(&b); b.tag = T_DATE; b.t = now; bemit(&b, ofp, h, f & f_bin);
1036 if (exp == KEXP_EXPIRE)
1037 exp = now + 86400 * 28;
1038 breset(&b); b.tag = T_EXPIRE; b.t = exp; bemit(&b, ofp, h, f & f_bin);
1039 if (c) {
1040 breset(&b); b.tag = T_COMMENT; DPUTS(&b.d, c);
1041 bemit(&b, ofp, h, f & f_bin);
1042 }
1043
1044 if (!(f & f_bin))
1045 putc('\n', ofp);
1046 }
1047
1048 /* --- Now hash the various files --- */
1049
1050 for (;;) {
1051
1052 /* --- Stop on an output error --- */
1053
1054 if (ferror(ofp)) {
1055 f |= f_bogus;
1056 break;
1057 }
1058
1059 /* --- Read the next filename to hash --- */
1060
1061 breset(&b);
1062 if (getstring(ifp, &b.d, f & f_raw))
1063 break;
1064 b.tag = T_FILE;
1065 DENSURE(&b.b, h->ops->c->hashsz);
1066 if (fhash(gch, b.d.buf, b.b.buf)) {
1067 moan("Error reading `%s': %s", b.d.buf, strerror(errno));
1068 f |= f_bogus;
1069 } else {
1070 b.b.len += h->ops->c->hashsz;
1071 if (verb) {
1072 fhex(stderr, b.b.buf, b.b.len);
1073 fprintf(stderr, " %s\n", b.d.buf);
1074 }
1075 bemit(&b, ofp, h, f & f_bin);
1076 }
1077 }
1078
1079 /* --- Create the signature --- */
1080
1081 if (!(f & f_bogus)) {
1082 breset(&b);
1083 b.tag = T_SIGNATURE;
1084 DENSURE(&b.d, h->ops->c->hashsz);
1085 h->ops->done(h, b.d.buf);
1086 if ((e = s->sign(k, b.d.buf, h->ops->c->hashsz, &b.b)) != 0) {
1087 moan("error creating signature: %s", key_strerror(e));
1088 f |= f_bogus;
1089 }
1090 if (!(f & f_bogus)) {
1091 bemit(&b, ofp, 0, f & f_bin);
1092 key_used(&kf, k, exp);
1093 }
1094 }
1095
1096 /* --- Tidy up at the end --- */
1097
1098 bdestroy(&b);
1099 if (ifile)
1100 fclose(ifp);
1101 if (ofile) {
1102 if (fclose(ofp))
1103 f |= f_bogus;
1104 } else {
1105 if (fflush(ofp))
1106 f |= f_bogus;
1107 }
1108 if ((e = key_close(&kf)) != 0) {
1109 switch (e) {
1110 case KWRITE_FAIL:
1111 die(EXIT_FAILURE, "couldn't write file `%s': %s",
1112 keyring, strerror(errno));
1113 case KWRITE_BROKEN:
1114 die(EXIT_FAILURE, "keyring file `%s' broken: %s (repair manually)",
1115 keyring, strerror(errno));
1116 }
1117 }
1118 if (f & f_bogus)
1119 die(EXIT_FAILURE, "error(s) occurred while creating signature");
1120 return (EXIT_SUCCESS);
1121 }
1122
1123 /*----- Signature verification --------------------------------------------*/
1124
1125 static int verify(int argc, char *argv[])
1126 {
1127 enum {
1128 f_bogus = 1,
1129 f_bin = 2,
1130 f_ok = 4
1131 };
1132
1133 unsigned f = 0;
1134 unsigned verb = 1;
1135 key_file kf;
1136 key *k = 0;
1137 sig *s = sigtab;
1138 const gchash *gch = &rmd160;
1139 dstr d = DSTR_INIT;
1140 ghash *h = 0;
1141 FILE *fp;
1142 block b;
1143 int e;
1144
1145 /* --- Parse the options --- */
1146
1147 for (;;) {
1148 static struct option opts[] = {
1149 { "verbose", 0, 0, 'v' },
1150 { "quiet", 0, 0, 'q' },
1151 { 0, 0, 0, 0 }
1152 };
1153 int i = mdwopt(argc, argv, "+vq", opts, 0, 0, 0);
1154 if (i < 0)
1155 break;
1156 switch (i) {
1157 case 'v':
1158 verb++;
1159 break;
1160 case 'q':
1161 if (verb)
1162 verb--;
1163 break;
1164 default:
1165 f |= f_bogus;
1166 break;
1167 }
1168 }
1169 argc -= optind;
1170 argv += optind;
1171 if ((f & f_bogus) || argc > 1)
1172 die(EXIT_FAILURE, "Usage: verify [-qv] [file]");
1173
1174 /* --- Open the key file, and start reading the input file --- */
1175
1176 if (key_open(&kf, keyring, KOPEN_READ, keyreport, 0))
1177 die(EXIT_FAILURE, "couldn't open keyring `%s'\n", keyring);
1178 if (argc < 1)
1179 fp = stdin;
1180 else {
1181 if ((fp = fopen(argv[0], "rb")) == 0) {
1182 die(EXIT_FAILURE, "couldn't open file `%s': %s\n",
1183 argv[0], strerror(errno));
1184 }
1185 if (getc(fp) == 0) {
1186 ungetc(0, fp);
1187 f |= f_bin;
1188 } else {
1189 fclose(fp);
1190 if ((fp = fopen(argv[0], "r")) == 0) {
1191 die(EXIT_FAILURE, "couldn't open file `%s': %s\n",
1192 argv[0], strerror(errno));
1193 }
1194 }
1195 }
1196
1197 /* --- Read the introductory matter --- */
1198
1199 binit(&b);
1200 for (;;) {
1201 breset(&b);
1202 e = bget(&b, fp, f & f_bin);
1203 if (e < 0)
1204 die(EXIT_FAILURE, "error reading packet: %s\n", errtab[-e]);
1205 if (e >= T_BEGIN)
1206 break;
1207 switch (e) {
1208 case T_IDENT:
1209 if (verb > 2)
1210 printf("INFO ident: `%s'\n", b.d.buf);
1211 break;
1212 case T_SIGALG:
1213 if ((s = getsig(b.d.buf)) == 0) {
1214 if (verb)
1215 printf("FAIL unknown signature algorithm `%s'\n", b.d.buf);
1216 exit(EXIT_FAILURE);
1217 }
1218 if (verb > 2)
1219 printf("INFO signature algorithm: %s\n", s->name);
1220 break;
1221 case T_HASHALG:
1222 if ((gch = gethash(b.d.buf)) == 0) {
1223 if (verb)
1224 printf("FAIL unknown hash function `%s'\n", b.d.buf);
1225 exit(EXIT_FAILURE);
1226 }
1227 if (verb > 2)
1228 printf("INFO hash function algorithm: %s\n", gch->name);
1229 break;
1230 case T_KEYID:
1231 if ((k = key_byid(&kf, b.k)) == 0) {
1232 if (verb)
1233 printf("FAIL key %08lx not found\n", (unsigned long)b.k);
1234 exit(EXIT_FAILURE);
1235 }
1236 if (verb > 2) {
1237 DRESET(&b.d);
1238 key_fulltag(k, &b.d);
1239 printf("INFO key: %s\n", b.d.buf);
1240 }
1241 break;
1242 default:
1243 die(EXIT_FAILURE, "(internal) unknown packet type\n");
1244 break;
1245 }
1246 }
1247
1248 /* --- Initialize the hash function and start reading hashed packets --- */
1249
1250 h = gch->init();
1251 if (!k) {
1252 if (verb)
1253 puts("FAIL no keyid packet found");
1254 exit(EXIT_FAILURE);
1255 }
1256 for (;;) {
1257 switch (e) {
1258 case T_COMMENT:
1259 if (verb > 1)
1260 printf("INFO comment: `%s'\n", b.d.buf);
1261 bemit(&b, 0, h, 0);
1262 break;
1263 case T_DATE:
1264 if (verb > 2) {
1265 DRESET(&b.d);
1266 timestring(b.t, &b.d);
1267 printf("INFO date: %s\n", b.d.buf);
1268 }
1269 bemit(&b, 0, h, 0);
1270 break;
1271 case T_EXPIRE: {
1272 time_t now = time(0);
1273 if (b.t < now) {
1274 if (verb > 1)
1275 puts("BAD signature has expired");
1276 f |= f_bogus;
1277 }
1278 if (verb > 2) {
1279 DRESET(&b.d);
1280 timestring(b.t, &b.d);
1281 printf("INFO expires: %s\n", b.d.buf);
1282 }
1283 bemit(&b, 0, h, 0);
1284 } break;
1285 case T_FILE:
1286 DRESET(&d);
1287 DENSURE(&d, gch->hashsz);
1288 if (fhash(gch, b.d.buf, d.buf)) {
1289 if (verb > 1) {
1290 printf("BAD error reading file `%s': %s\n",
1291 b.d.buf, strerror(errno));
1292 }
1293 f |= f_bogus;
1294 } else if (b.b.len != gch->hashsz ||
1295 memcmp(d.buf, b.b.buf, b.b.len) != 0) {
1296 if (verb > 1)
1297 printf("BAD file `%s' has incorrect hash\n", b.d.buf);
1298 f |= f_bogus;
1299 } else if (verb > 3) {
1300 fputs("INFO hash: ", stdout);
1301 fhex(stdout, b.b.buf, b.b.len);
1302 printf(" %s\n", b.d.buf);
1303 }
1304 bemit(&b, 0, h, 0);
1305 break;
1306 case T_SIGNATURE:
1307 DRESET(&b.d);
1308 DENSURE(&b.d, h->ops->c->hashsz);
1309 b.d.len += h->ops->c->hashsz;
1310 h->ops->done(h, b.d.buf);
1311 e = s->verify(k, b.d.buf, b.d.len, b.b.buf, b.b.len);
1312 if (e != 1) {
1313 if (verb > 1) {
1314 if (e < 0) {
1315 printf("BAD error unpacking key: %s\n", key_strerror(e));
1316 } else
1317 puts("BAD bad signature");
1318 }
1319 f |= f_bogus;
1320 } else if (verb > 2)
1321 puts("INFO good signature");
1322 goto done;
1323 default:
1324 if (verb)
1325 printf("FAIL invalid packet type %i\n", e);
1326 exit(EXIT_FAILURE);
1327 break;
1328 }
1329 breset(&b);
1330 e = bget(&b, fp, f & f_bin);
1331 if (e < 0) {
1332 if (verb)
1333 printf("FAIL error reading packet: %s\n", errtab[-e]);
1334 exit(EXIT_FAILURE);
1335 }
1336 }
1337 done:
1338 bdestroy(&b);
1339 dstr_destroy(&d);
1340 key_close(&kf);
1341 if (fp != stdin)
1342 fclose(fp);
1343 if (verb) {
1344 if (f & f_bogus)
1345 puts("FAIL signature invalid");
1346 else
1347 puts("OK signature verified");
1348 }
1349 return (f & f_bogus ? EXIT_FAILURE : EXIT_SUCCESS);
1350 }
1351
1352 /*----- Main code ---------------------------------------------------------*/
1353
1354 typedef struct cmd {
1355 const char *name;
1356 int (*func)(int /*argc*/, char */*argv*/[]);
1357 const char *help;
1358 } cmd;
1359
1360 static cmd cmdtab[] = {
1361 /* { "manifest", manifest, */
1362 /* "manifest [-0] [-o output]" }, */
1363 { "sign", sign,
1364 "sign [-options]\n\
1365 [-0v] [-a alg] [-h hash] [-t keytype] [-i keyid]\n\
1366 [-e expire] [-f file] [-o output]" },
1367 { "verify", verify,
1368 "verify [-qv] [file]" },
1369 { 0, 0, 0 }
1370 };
1371
1372 static void version(FILE *fp)
1373 {
1374 pquis(fp, "$, Catacomb version " VERSION "\n");
1375 }
1376
1377 static void usage(FILE *fp)
1378 {
1379 pquis(fp, "Usage: $ [-k keyring] command [args]\n");
1380 }
1381
1382 static void help(FILE *fp)
1383 {
1384 cmd *c;
1385 version(fp);
1386 fputc('\n', fp);
1387 usage(fp);
1388 fputs("\n\
1389 Create and verify signatures on lists of files.\n\
1390 \n", fp);
1391 for (c = cmdtab; c->name; c++)
1392 fprintf(fp, "%s\n", c->help);
1393 }
1394
1395 /* --- @main@ --- *
1396 *
1397 * Arguments: @int argc@ = number of command line arguments
1398 * @char *argv[]@ = vector of command line arguments
1399 *
1400 * Returns: Zero if successful, nonzero otherwise.
1401 *
1402 * Use: Signs or verifies signatures on lists of files. Useful for
1403 * ensuring that a distribution is unmolested.
1404 */
1405
1406 int main(int argc, char *argv[])
1407 {
1408 unsigned f = 0;
1409 cmd *c = 0, *cc = 0;
1410 size_t n;
1411
1412 enum {
1413 f_bogus = 1
1414 };
1415
1416 /* --- Initialize the library --- */
1417
1418 ego(argv[0]);
1419 sub_init();
1420 rand_noisesrc(RAND_GLOBAL, &noise_source);
1421 rand_seed(RAND_GLOBAL, 160);
1422
1423 /* --- Parse options --- */
1424
1425 for (;;) {
1426 static struct option opts[] = {
1427 { "help", 0, 0, 'h' },
1428 { "version", 0, 0, 'v' },
1429 { "usage", 0, 0, 'u' },
1430 { "keyring", OPTF_ARGREQ, 0, 'k' },
1431 { 0, 0, 0, 0 }
1432 };
1433 int i = mdwopt(argc, argv, "+hvu k:", opts, 0, 0, 0);
1434 if (i < 0)
1435 break;
1436 switch (i) {
1437 case 'h':
1438 help(stdout);
1439 exit(0);
1440 break;
1441 case 'v':
1442 version(stdout);
1443 exit(0);
1444 break;
1445 case 'u':
1446 usage(stdout);
1447 exit(0);
1448 case 'k':
1449 keyring = optarg;
1450 break;
1451 default:
1452 f |= f_bogus;
1453 break;
1454 }
1455 }
1456
1457 argc -= optind;
1458 argv += optind;
1459 optind = 0;
1460 if (f & f_bogus || argc < 1) {
1461 usage(stderr);
1462 exit(EXIT_FAILURE);
1463 }
1464
1465 /* --- Dispatch to the correct subcommand handler --- */
1466
1467 n = strlen(argv[0]);
1468 for (c = cmdtab; c->name; c++) {
1469 if (strncmp(argv[0], c->name, n) == 0) {
1470 if (c->name[n] == 0) {
1471 cc = c;
1472 break;
1473 } else if (cc)
1474 die(EXIT_FAILURE, "ambiguous command name `%s'", argv[0]);
1475 else
1476 cc = c;
1477 }
1478 }
1479 if (!cc)
1480 die(EXIT_FAILURE, "unknown command `%s'", argv[0]);
1481 return (cc->func(argc, argv));
1482 }
1483
1484 /*----- That's all, folks -------------------------------------------------*/