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