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