030ad0af3c288981445520c0403898f38ea06e4b
3 * $Id: dsig.c,v 1.9 2004/04/08 01:02:15 mdw Exp $
5 * Verify signatures on distribuitions of files
7 * (c) 2000 Straylight/Edgeware
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of Catacomb.
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.
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.
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,
30 /*----- Revision history --------------------------------------------------*
33 * Revision 1.9 2004/04/08 01:02:15 mdw
34 * Incompatible change! Add new signature schemes. Key now implies
35 * algorithms (integrity checked by new fingerprinting rules), so don't put
36 * that stuff in the manifest.
38 * Revision 1.8 2004/04/04 19:42:59 mdw
41 * Revision 1.7 2001/02/23 09:04:17 mdw
42 * Add new hash functions. Provide full help for subcommands. Run the
43 * hash function over parts of the header in a canonical order.
45 * Revision 1.6 2000/12/06 20:33:27 mdw
46 * Make flags be macros rather than enumerations, to ensure that they're
49 * Revision 1.5 2000/10/08 12:12:09 mdw
50 * Shut up some warnings.
52 * Revision 1.4 2000/08/04 23:23:44 mdw
53 * Various <ctype.h> fixes.
55 * Revision 1.3 2000/07/15 20:53:23 mdw
56 * More hash functions. Bug fix in getstring.
58 * Revision 1.2 2000/07/01 11:27:22 mdw
59 * Use new PKCS#1 padding functions rather than rolling by hand.
61 * Revision 1.1 2000/06/17 10:54:29 mdw
62 * Program to generate and verify signatures on multiple files.
66 /*----- Header files ------------------------------------------------------*/
76 #include <mLib/alloc.h>
77 #include <mLib/base64.h>
78 #include <mLib/mdwopt.h>
79 #include <mLib/quis.h>
80 #include <mLib/report.h>
100 /*----- Algorithm choice --------------------------------------------------*/
102 /* --- Relevant type operations --- */
105 const struct sigops
*ops
;
110 typedef struct sigops
{
111 const key_fetchdef
*kf
; /* Key fetching structure */
112 size_t kdsz
; /* Size of the key-data structure */
113 sig
*(*init
)(key */
*k*/
, void */
*kd*/
, const gchash */
*hc*/
);
114 int (*doit
)(sig */
*s*/
, dstr */
*d*/
);
115 void (*destroy
)(sig */
*s*/
);
118 /* --- RSA PKCS1 --- */
120 typedef struct rsap1_sigctx
{
126 static sig
*rsap1_siginit(key
*k
, void *kd
, const gchash
*hc
)
128 rsap1_sigctx
*rs
= CREATE(rsap1_sigctx
);
129 rsa_privcreate(&rs
->rp
, kd
, &rand_global
);
130 rs
->p1
.r
= &rand_global
;
131 rs
->p1
.ep
= hc
->name
;
132 rs
->p1
.epsz
= strlen(hc
->name
) + 1;
137 static int rsap1_sigdoit(sig
*s
, dstr
*d
)
139 rsap1_sigctx
*rs
= (rsap1_sigctx
*)s
;
141 mp
*m
= rsa_sign(&rs
->rp
, MP_NEW
,
142 GH_DONE(s
->h
, 0), GH_CLASS(s
->h
)->hashsz
,
143 pkcs1_sigencode
, &rs
->p1
);
145 n
= mp_octets(rs
->rp
.rp
->n
); dstr_ensure(d
, n
); mp_storeb(m
, d
->buf
, n
);
146 d
->len
+= n
; mp_drop(m
);
150 static void rsap1_sigdestroy(sig
*s
)
152 rsap1_sigctx
*rs
= (rsap1_sigctx
*)s
;
156 static const sigops rsap1_sig
= {
157 rsa_privfetch
, sizeof(rsa_priv
),
158 rsap1_siginit
, rsap1_sigdoit
, rsap1_sigdestroy
161 typedef struct rsap1_vrfctx
{
167 static sig
*rsap1_vrfinit(key
*k
, void *kd
, const gchash
*hc
)
169 rsap1_vrfctx
*rv
= CREATE(rsap1_vrfctx
);
170 rsa_pubcreate(&rv
->rp
, kd
);
171 rv
->p1
.r
= &rand_global
;
172 rv
->p1
.ep
= hc
->name
;
173 rv
->p1
.epsz
= strlen(hc
->name
) + 1;
178 static int rsap1_vrfdoit(sig
*s
, dstr
*d
)
180 rsap1_vrfctx
*rv
= (rsap1_vrfctx
*)s
;
181 mp
*m
= mp_loadb(MP_NEW
, d
->buf
, d
->len
);
182 int rc
= rsa_verify(&rv
->rp
, m
,
183 GH_DONE(s
->h
, 0), GH_CLASS(s
->h
)->hashsz
,
184 0, pkcs1_sigdecode
, &rv
->p1
);
189 static void rsap1_vrfdestroy(sig
*s
)
191 rsap1_vrfctx
*rv
= (rsap1_vrfctx
*)s
;
195 static const sigops rsap1_vrf
= {
196 rsa_pubfetch
, sizeof(rsa_pub
),
197 rsap1_vrfinit
, rsap1_vrfdoit
, rsap1_vrfdestroy
200 /* --- RSA PSS --- */
202 static const gccipher
*getmgf(key
*k
, const gchash
*hc
)
208 if ((mm
= key_getattr(0, k
, "mgf-alg")) == 0) {
209 dstr_putf(&d
, "%s-mgf", hc
->name
);
212 if ((gc
= gcipher_byname(mm
)) == 0)
213 die(EXIT_FAILURE
, "unknown encryption scheme `%s'", mm
);
218 typedef struct rsapss_sigctx
{
224 static sig
*rsapss_siginit(key
*k
, void *kd
, const gchash
*hc
)
226 rsapss_sigctx
*rs
= CREATE(rsapss_sigctx
);
227 rsa_privcreate(&rs
->rp
, kd
, &rand_global
);
228 rs
->p
.r
= &rand_global
;
229 rs
->p
.cc
= getmgf(k
, hc
);
231 rs
->p
.ssz
= hc
->hashsz
;
235 static int rsapss_sigdoit(sig
*s
, dstr
*d
)
237 rsapss_sigctx
*rs
= (rsapss_sigctx
*)s
;
239 mp
*m
= rsa_sign(&rs
->rp
, MP_NEW
,
240 GH_DONE(s
->h
, 0), GH_CLASS(s
->h
)->hashsz
,
243 n
= mp_octets(rs
->rp
.rp
->n
); dstr_ensure(d
, n
); mp_storeb(m
, d
->buf
, n
);
244 d
->len
+= n
; mp_drop(m
);
248 static void rsapss_sigdestroy(sig
*s
)
250 rsapss_sigctx
*rs
= (rsapss_sigctx
*)s
;
254 static const sigops rsapss_sig
= {
255 rsa_privfetch
, sizeof(rsa_priv
),
256 rsapss_siginit
, rsapss_sigdoit
, rsapss_sigdestroy
259 typedef struct rsapss_vrfctx
{
265 static sig
*rsapss_vrfinit(key
*k
, void *kd
, const gchash
*hc
)
267 rsapss_vrfctx
*rv
= CREATE(rsapss_vrfctx
);
268 rsa_pubcreate(&rv
->rp
, kd
);
269 rv
->p
.r
= &rand_global
;
270 rv
->p
.cc
= getmgf(k
, hc
);
272 rv
->p
.ssz
= hc
->hashsz
;
276 static int rsapss_vrfdoit(sig
*s
, dstr
*d
)
278 rsapss_vrfctx
*rv
= (rsapss_vrfctx
*)s
;
279 mp
*m
= mp_loadb(MP_NEW
, d
->buf
, d
->len
);
280 int rc
= rsa_verify(&rv
->rp
, m
,
281 GH_DONE(s
->h
, 0), GH_CLASS(s
->h
)->hashsz
,
282 0, pss_decode
, &rv
->p
);
287 static void rsapss_vrfdestroy(sig
*s
)
289 rsapss_vrfctx
*rv
= (rsapss_vrfctx
*)s
;
293 static const sigops rsapss_vrf
= {
294 rsa_pubfetch
, sizeof(rsa_pub
),
295 rsapss_vrfinit
, rsapss_vrfdoit
, rsapss_vrfdestroy
298 /* --- DSA and ECDSA --- */
300 typedef struct dsa_sigctx
{
305 static dsa_sigctx
*dsa_doinit(key
*k
, const gprime_param
*gp
,
306 mp
*y
, const gchash
*hc
)
308 dsa_sigctx
*ds
= CREATE(dsa_sigctx
);
312 if ((ds
->g
.g
= group_prime(gp
)) == 0)
313 die(EXIT_FAILURE
, "bad prime group in key `%s'", t
.buf
);
315 ds
->g
.p
= G_CREATE(ds
->g
.g
);
316 if (G_FROMINT(ds
->g
.g
, ds
->g
.p
, y
))
317 die(EXIT_FAILURE
, "bad public key in key `%s'", t
.buf
);
318 ds
->g
.r
= &rand_global
;
325 static dsa_sigctx
*ecdsa_doinit(key
*k
, const char *cstr
,
326 ec
*y
, const gchash
*hc
)
328 dsa_sigctx
*ds
= CREATE(dsa_sigctx
);
334 if ((e
= ec_getinfo(&ei
, cstr
)) != 0)
335 die(EXIT_FAILURE
, "bad curve in key `%s': %s", t
.buf
, e
);
336 ds
->g
.g
= group_ec(&ei
);
338 ds
->g
.p
= G_CREATE(ds
->g
.g
);
339 if (G_FROMEC(ds
->g
.g
, ds
->g
.p
, y
))
340 die(EXIT_FAILURE
, "bad public key in key `%s'", t
.buf
);
341 ds
->g
.r
= &rand_global
;
348 static sig
*dsa_siginit(key
*k
, void *kd
, const gchash
*hc
)
351 dsa_sigctx
*ds
= dsa_doinit(k
, &dp
->dp
, dp
->y
, hc
);
352 ds
->g
.u
= MP_COPY(dp
->x
);
356 static sig
*ecdsa_siginit(key
*k
, void *kd
, const gchash
*hc
)
359 dsa_sigctx
*ds
= ecdsa_doinit(k
, ep
->cstr
, &ep
->p
, hc
);
360 ds
->g
.u
= MP_COPY(ep
->x
);
364 static int dsa_sigdoit(sig
*s
, dstr
*d
)
366 dsa_sigctx
*ds
= (dsa_sigctx
*)s
;
367 gdsa_sig ss
= GDSA_SIG_INIT
;
368 size_t n
= mp_octets(ds
->g
.g
->r
);
370 gdsa_sign(&ds
->g
, &ss
, GH_DONE(ds
->s
.h
, 0), 0);
371 dstr_ensure(d
, 2 * n
);
372 mp_storeb(ss
.r
, d
->buf
, n
);
373 mp_storeb(ss
.s
, d
->buf
+ n
, n
);
375 mp_drop(ss
.r
); mp_drop(ss
.s
);
379 static void dsa_sigdestroy(sig
*s
)
381 dsa_sigctx
*ds
= (dsa_sigctx
*)s
;
382 G_DESTROY(ds
->g
.g
, ds
->g
.p
);
384 G_DESTROYGROUP(ds
->g
.g
);
387 static const sigops dsa_sig
= {
388 dh_privfetch
, sizeof(dh_priv
),
389 dsa_siginit
, dsa_sigdoit
, dsa_sigdestroy
392 static const sigops ecdsa_sig
= {
393 ec_privfetch
, sizeof(ec_priv
),
394 ecdsa_siginit
, dsa_sigdoit
, dsa_sigdestroy
397 static sig
*dsa_vrfinit(key
*k
, void *kd
, const gchash
*hc
)
400 dsa_sigctx
*ds
= dsa_doinit(k
, &dp
->dp
, dp
->y
, hc
);
404 static sig
*ecdsa_vrfinit(key
*k
, void *kd
, const gchash
*hc
)
407 dsa_sigctx
*ds
= ecdsa_doinit(k
, ep
->cstr
, &ep
->p
, hc
);
411 static int dsa_vrfdoit(sig
*s
, dstr
*d
)
413 dsa_sigctx
*ds
= (dsa_sigctx
*)s
;
418 ss
.r
= mp_loadb(MP_NEW
, d
->buf
, n
);
419 ss
.s
= mp_loadb(MP_NEW
, d
->buf
+ n
, d
->len
- n
);
420 rc
= gdsa_verify(&ds
->g
, &ss
, GH_DONE(ds
->s
.h
, 0));
421 mp_drop(ss
.r
); mp_drop(ss
.s
);
425 static const sigops dsa_vrf
= {
426 dh_pubfetch
, sizeof(dh_pub
),
427 dsa_vrfinit
, dsa_vrfdoit
, dsa_sigdestroy
430 static const sigops ecdsa_vrf
= {
431 ec_pubfetch
, sizeof(ec_pub
),
432 ecdsa_vrfinit
, dsa_vrfdoit
, dsa_sigdestroy
435 /* --- KCDSA and ECKCDSA --- */
437 static void kcdsa_privkey(dsa_sigctx
*ds
, mp
*x
)
438 { ds
->g
.u
= mp_modinv(MP_NEW
, x
, ds
->g
.g
->r
); }
440 static void kcdsa_sethash(dsa_sigctx
*ds
, const gchash
*hc
)
441 { ds
->s
.h
= gkcdsa_beginhash(&ds
->g
); }
443 static sig
*kcdsa_siginit(key
*k
, void *kd
, const gchash
*hc
)
446 dsa_sigctx
*ds
= dsa_doinit(k
, &dp
->dp
, dp
->y
, hc
);
447 kcdsa_privkey(ds
, dp
->x
);
448 kcdsa_sethash(ds
, hc
);
452 static sig
*eckcdsa_siginit(key
*k
, void *kd
, const gchash
*hc
)
455 dsa_sigctx
*ds
= ecdsa_doinit(k
, ep
->cstr
, &ep
->p
, hc
);
456 kcdsa_privkey(ds
, ep
->x
);
457 kcdsa_sethash(ds
, hc
);
461 static int kcdsa_sigdoit(sig
*s
, dstr
*d
)
463 dsa_sigctx
*ds
= (dsa_sigctx
*)s
;
464 gkcdsa_sig ss
= GKCDSA_SIG_INIT
;
465 size_t hsz
= ds
->g
.h
->hashsz
, n
= mp_octets(ds
->g
.g
->r
);
467 gkcdsa_sign(&ds
->g
, &ss
, GH_DONE(ds
->s
.h
, 0), 0);
468 dstr_ensure(d
, hsz
+ n
);
469 memcpy(d
->buf
, ss
.r
, hsz
);
470 mp_storeb(ss
.s
, d
->buf
+ hsz
, n
);
472 xfree(ss
.r
); mp_drop(ss
.s
);
476 static const sigops kcdsa_sig
= {
477 dh_privfetch
, sizeof(dh_priv
),
478 kcdsa_siginit
, kcdsa_sigdoit
, dsa_sigdestroy
481 static const sigops eckcdsa_sig
= {
482 ec_privfetch
, sizeof(ec_priv
),
483 eckcdsa_siginit
, kcdsa_sigdoit
, dsa_sigdestroy
486 static sig
*kcdsa_vrfinit(key
*k
, void *kd
, const gchash
*hc
)
489 dsa_sigctx
*ds
= dsa_doinit(k
, &dp
->dp
, dp
->y
, hc
);
490 kcdsa_sethash(ds
, hc
);
494 static sig
*eckcdsa_vrfinit(key
*k
, void *kd
, const gchash
*hc
)
497 dsa_sigctx
*ds
= ecdsa_doinit(k
, ep
->cstr
, &ep
->p
, hc
);
498 kcdsa_sethash(ds
, hc
);
502 static int kcdsa_vrfdoit(sig
*s
, dstr
*d
)
504 dsa_sigctx
*ds
= (dsa_sigctx
*)s
;
506 size_t hsz
= ds
->g
.h
->hashsz
, n
= d
->len
- hsz
;
511 ss
.r
= (octet
*)d
->buf
;
512 ss
.s
= mp_loadb(MP_NEW
, d
->buf
+ hsz
, n
);
513 rc
= gkcdsa_verify(&ds
->g
, &ss
, GH_DONE(ds
->s
.h
, 0));
518 static const sigops kcdsa_vrf
= {
519 dh_pubfetch
, sizeof(dh_pub
),
520 kcdsa_vrfinit
, kcdsa_vrfdoit
, dsa_sigdestroy
523 static const sigops eckcdsa_vrf
= {
524 ec_pubfetch
, sizeof(ec_pub
),
525 eckcdsa_vrfinit
, kcdsa_vrfdoit
, dsa_sigdestroy
528 /* --- The switch table --- */
530 static const struct sigtab
{
532 const sigops
*signops
;
533 const sigops
*verifyops
;
536 { "rsapkcs1", &rsap1_sig
, &rsap1_vrf
, &sha
},
537 { "rsapss", &rsapss_sig
, &rsapss_vrf
, &sha
},
538 { "dsa", &dsa_sig
, &dsa_vrf
, &sha
},
539 { "ecdsa", &ecdsa_sig
, &ecdsa_vrf
, &sha
},
540 { "kcdsa", &kcdsa_sig
, &kcdsa_vrf
, &has160
},
541 { "eckcdsa", &eckcdsa_sig
, &eckcdsa_vrf
, &has160
},
545 /* --- @getsig@ --- *
547 * Arguments: @key *k@ = the key to load
548 * @int wantpriv@ = nonzero if we want to sign
550 * Returns: A signature-making thing.
552 * Use: Loads a key and starts hashing.
555 static sig
*getsig(key
*k
, int wantpriv
)
557 const char *salg
, *halg
= 0;
563 const struct sigtab
*st
;
570 /* --- Setup stuff --- */
574 /* --- Get the signature algorithm --- *
576 * Take the attribute if it's there; otherwise use the key type.
579 if ((q
= key_getattr(0, k
, "sig-alg")) != 0) {
582 } else if (strncmp(k
->type
, "dsig-", 5) == 0 && k
->type
[5]) {
583 dstr_puts(&d
, k
->type
);
586 die(EXIT_FAILURE
, "no signature algorithm for key `%s'", t
.buf
);
588 /* --- Grab the hash algorithm --- *
590 * Grab it from the signature algorithm if it's there. But override that
591 * from the attribute.
595 if ((p
= strchr(p
, '-')) != 0) {
599 if ((q
= key_getattr(0, k
, "hash-alg")) != 0)
602 /* --- Look up the algorithms in the table --- */
604 for (st
= sigtab
; st
->name
; st
++) {
605 if (strcmp(st
->name
, salg
) == 0)
608 die(EXIT_FAILURE
, "signature algorithm `%s' not found in key `%s'",
614 if ((ch
= ghash_byname(halg
)) == 0) {
615 die(EXIT_FAILURE
, "hash algorithm `%s' not found in key `%s'",
619 so
= wantpriv ? st
->signops
: st
->verifyops
;
621 /* --- Load the key --- */
623 kd
= xmalloc(so
->kdsz
);
624 kp
= key_fetchinit(so
->kf
, 0, kd
);
625 if ((e
= key_fetch(kp
, k
)) != 0)
626 die(EXIT_FAILURE
, "error fetching key `%s': %s", t
.buf
, key_strerror(e
));
627 s
= so
->init(k
, kd
, ch
);
633 /* --- Free stuff up --- */
640 /* --- @freesig@ --- *
642 * Arguments: @sig *s@ = signature-making thing
646 * Use: Frees up a signature-making thing
649 static void freesig(sig
*s
)
652 key_fetchdone(s
->kp
);
656 /*----- Data formatting ---------------------------------------------------*/
658 /* --- Binary data structure --- *
660 * The binary format, which is used for hashing and for the optional binary
661 * output, consists of a sequence of tagged blocks. The tag describes the
662 * format and meaining of the following data.
666 /* --- Block tags --- */
668 T_IDENT
= 0, /* An identifying marker */
669 T_KEYID
, /* Key identifier */
670 T_BEGIN
, /* Begin hashing here */
671 T_COMMENT
= T_BEGIN
, /* A textual comment */
672 T_DATE
, /* Creation date of signature */
673 T_EXPIRE
, /* Expiry date of signature */
674 T_FILE
, /* File and corresponding hash */
675 T_SIGNATURE
, /* Final signature block */
677 /* --- Error messages --- */
685 /* --- Name translation table --- */
687 static const char *tagtab
[] = {
689 "comment:", "date:", "expires:", "file:",
694 static const char *errtab
[] = {
696 "Unexpected end-of-file",
697 "Binary object too large",
702 /* --- Memory representation of block types --- */
704 typedef struct block
{
705 int tag
; /* Type tag */
706 dstr d
; /* String data */
707 dstr b
; /* Binary data */
708 time_t t
; /* Timestamp */
709 uint32 k
; /* Keyid */
712 /* --- @getstring@ --- *
714 * Arguments: @FILE *fp@ = stream from which to read
715 * @dstr *d@ = destination string
716 * @unsigned raw@ = raw or cooked read
718 * Returns: Zero if OK, nonzero on end-of-file.
720 * Use: Reads a filename (or something similar) from a stream.
723 static int getstring(FILE *fp
, dstr
*d
, unsigned raw
)
728 /* --- Raw: just read exactly what's written up to a null byte --- */
731 if ((ch
= getc(fp
)) == EOF
)
737 if ((ch
= getc(fp
)) == EOF
)
744 /* --- Skip as far as whitespace --- *
746 * Also skip past comments.
754 do ch
= getc(fp
); while (ch
!= '\n' && ch
!= EOF
);
760 /* --- If the character is a quote then read a quoted string --- */
772 /* --- Now read all sorts of interesting things --- */
776 /* --- Handle an escaped thing --- */
783 case 'a': ch
= '\a'; break;
784 case 'b': ch
= '\b'; break;
785 case 'f': ch
= '\f'; break;
786 case 'n': ch
= '\n'; break;
787 case 'r': ch
= '\r'; break;
788 case 't': ch
= '\t'; break;
789 case 'v': ch
= '\v'; break;
796 /* --- If it's a quote or some other end marker then stop --- */
798 if (ch
== q
|| (!q
&& isspace((unsigned char)ch
)))
801 /* --- Otherwise contribute and continue --- */
804 if ((ch
= getc(fp
)) == EOF
)
814 /* --- @putstring@ --- *
816 * Arguments: @FILE *fp@ = stream to write on
817 * @const char *p@ = pointer to text
818 * @unsigned raw@ = whether the string is to be written raw
822 * Use: Emits a string to a stream.
825 static void putstring(FILE *fp
, const char *p
, unsigned raw
)
827 size_t sz
= strlen(p
);
831 /* --- Just write the string null terminated if raw --- */
834 fwrite(p
, 1, sz
+ 1, fp
);
838 /* --- Check for any dodgy characters --- */
841 for (q
= p
; *q
; q
++) {
842 if (isspace((unsigned char)*q
)) {
851 /* --- Emit the string --- */
853 for (q
= p
; *q
; q
++) {
855 case '\a': fputc('\\', fp
); fputc('a', fp
); break;
856 case '\b': fputc('\\', fp
); fputc('b', fp
); break;
857 case '\f': fputc('\\', fp
); fputc('f', fp
); break;
858 case '\n': fputc('\\', fp
); fputc('n', fp
); break;
859 case '\r': fputc('\\', fp
); fputc('r', fp
); break;
860 case '\t': fputc('\\', fp
); fputc('t', fp
); break;
861 case '\v': fputc('\\', fp
); fputc('v', fp
); break;
862 case '`': fputc('\\', fp
); fputc('`', fp
); break;
863 case '\'': fputc('\\', fp
); fputc('\'', fp
); break;
864 case '\"': fputc('\\', fp
); fputc('\"', fp
); break;
877 /* --- @timestring@ --- *
879 * Arguments: @time_t t@ = a timestamp
880 * @dstr *d@ = a string to write on
884 * Use: Writes a textual representation of the timestamp to the
888 static void timestring(time_t t
, dstr
*d
)
890 if (t
== KEXP_FOREVER
)
893 struct tm
*tm
= localtime(&t
);
895 d
->len
+= strftime(d
->buf
+ d
->len
, 32, "%Y-%m-%d %H:%M:%S %Z", tm
);
900 /* --- @breset@ --- *
902 * Arguments: @block *b@ = block to reset
906 * Use: Resets a block so that more stuff can be put in it.
909 static void breset(block
*b
)
920 * Arguments: @block *b@ = block to initialize
924 * Use: Initializes a block as something to read into.
927 static void binit(block
*b
)
934 /* --- @bdestroy@ --- *
936 * Arguments: @block *b@ = block to destroy
940 * Use: Destroys a block's contents.
943 static void bdestroy(block
*b
)
951 * Arguments: @block *b@ = pointer to block
952 * @FILE *fp@ = stream to read from
953 * @unsigned bin@ = binary switch
955 * Returns: Tag of block, or an error tag.
957 * Use: Reads a block from a stream.
960 static int bget(block
*b
, FILE *fp
, unsigned bin
)
964 /* --- Read the tag --- */
970 if (getstring(fp
, &d
, 0))
972 for (tag
= 0; tagtab
[tag
]; tag
++) {
973 if (strcmp(tagtab
[tag
], d
.buf
) == 0)
980 /* --- Decide what to do next --- */
986 /* --- Reading of strings --- */
990 if (getstring(fp
, &b
->d
, bin
))
994 /* --- Timestamps --- */
1000 if (fread(buf
, sizeof(buf
), 1, fp
) < 1)
1002 b
->t
= ((time_t)(((LOAD32(buf
+ 0) << 16) << 16) & ~MASK32
) |
1003 (time_t)LOAD32(buf
+ 4));
1005 if (getstring(fp
, &b
->d
, 0))
1007 if (strcmp(b
->d
.buf
, "forever") == 0)
1008 b
->t
= KEXP_FOREVER
;
1009 else if ((b
->t
= get_date(b
->d
.buf
, 0)) == -1)
1014 /* --- Key ids --- */
1019 if (fread(buf
, sizeof(buf
), 1, fp
) < 1)
1023 if (getstring(fp
, &b
->d
, 0))
1025 b
->k
= strtoul(b
->d
.buf
, 0, 16);
1029 /* --- Reading of binary data --- */
1036 if (fread(buf
, sizeof(buf
), 1, fp
) < 1)
1042 if (fread(b
->b
.buf
+ b
->b
.len
, 1, sz
, fp
) < sz
)
1047 if (getstring(fp
, &b
->d
, 0))
1050 base64_decode(&b64
, b
->d
.buf
, b
->d
.len
, &b
->b
);
1051 base64_decode(&b64
, 0, 0, &b
->b
);
1054 if (tag
== T_FILE
&& getstring(fp
, &b
->d
, bin
))
1058 /* --- Anything else --- */
1069 * Arguments: @block *b@ = pointer to block to emit
1070 * @dstr *d@ = output buffer
1074 * Use: Encodes a block in a binary format.
1077 static void blob(block
*b
, dstr
*d
)
1089 STORE32(d
->buf
+ d
->len
, ((b
->t
& ~MASK32
) >> 16) >> 16);
1090 STORE32(d
->buf
+ d
->len
+ 4, b
->t
);
1095 STORE32(d
->buf
+ d
->len
, b
->k
);
1101 STORE16(d
->buf
+ d
->len
, b
->b
.len
);
1104 if (b
->tag
== T_FILE
) {
1112 /* --- @bwrite@ --- *
1114 * Arguments: @block *b@ = pointer to block to write
1115 * @FILE *fp@ = stream to write on
1119 * Use: Writes a block on a stream in a textual format.
1122 static void bwrite(block
*b
, FILE *fp
)
1124 fputs(tagtab
[b
->tag
], fp
);
1129 putstring(fp
, b
->d
.buf
, 0);
1134 timestring(b
->t
, &d
);
1135 putstring(fp
, d
.buf
, 0);
1139 fprintf(fp
, "%08lx", (unsigned long)b
->k
);
1147 base64_encode(&b64
, b
->b
.buf
, b
->b
.len
, &d
);
1148 base64_encode(&b64
, 0, 0, &d
);
1150 if (b
->tag
== T_FILE
) {
1152 putstring(fp
, b
->d
.buf
, 0);
1159 /* --- @bemit@ --- *
1161 * Arguments: @block *b@ = pointer to block to write
1162 * @FILE *fp@ = file to write on
1163 * @ghash *h@ = pointer to hash function
1164 * @unsigned bin@ = binary/text flag
1168 * Use: Spits out a block properly.
1171 static void bemit(block
*b
, FILE *fp
, ghash
*h
, unsigned bin
)
1173 if (h
|| (fp
&& bin
)) {
1177 GH_HASH(h
, d
.buf
, d
.len
);
1179 fwrite(d
.buf
, d
.len
, 1, fp
);
1185 /*----- Static variables --------------------------------------------------*/
1187 static const char *keyring
= "keyring";
1189 /*----- Other shared functions --------------------------------------------*/
1191 /* --- @keyreport@ --- *
1193 * Arguments: @const char *file@ = filename containing the error
1194 * @int line@ = line number in file
1195 * @const char *err@ = error text message
1196 * @void *p@ = unimportant pointer
1200 * Use: Reports errors during the opening of a key file.
1203 static void keyreport(const char *file
, int line
, const char *err
, void *p
)
1205 moan("error in keyring `%s' at line `%s': %s", file
, line
, err
);
1208 /* --- @fhash@ --- *
1210 * Arguments: @const gchash *c@ = pointer to hash class
1211 * @const char *file@ = file to hash
1212 * @void *b@ = pointer to output buffer
1214 * Returns: Zero if it worked, or nonzero for a system error.
1216 * Use: Hashes a file.
1219 static int fhash(const gchash
*c
, const char *file
, void *b
)
1221 FILE *fp
= fopen(file
, "rb");
1222 ghash
*h
= GH_INIT(c
);
1229 while ((sz
= fread(buf
, 1, sizeof(buf
), fp
)) > 0)
1230 GH_HASH(h
, buf
, sz
);
1241 * Arguments: @FILE *fp@ = file to write on
1242 * @const void *p@ = pointer to data to be written
1243 * @size_t sz@ = size of the data to write
1247 * Use: Emits a hex dump to a stream.
1250 static void fhex(FILE *fp
, const void *p
, size_t sz
)
1256 fprintf(fp
, "%02x", *q
++);
1260 /* putc(' ', fp); */
1264 /*----- Signature generation ----------------------------------------------*/
1266 static int sign(int argc
, char *argv
[])
1273 const char *ki
= "dsig";
1277 time_t exp
= KEXP_EXPIRE
;
1279 const char *ifile
= 0;
1280 const char *ofile
= 0;
1288 static struct option opts
[] = {
1289 { "null", 0, 0, '0' },
1290 { "binary", 0, 0, 'b' },
1291 { "verbose", 0, 0, 'v' },
1292 { "quiet", 0, 0, 'q' },
1293 { "comment", OPTF_ARGREQ
, 0, 'c' },
1294 { "file", OPTF_ARGREQ
, 0, 'f' },
1295 { "output", OPTF_ARGREQ
, 0, 'o' },
1296 { "key", OPTF_ARGREQ
, 0, 'k' },
1297 { "expire", OPTF_ARGREQ
, 0, 'e' },
1300 int i
= mdwopt(argc
, argv
, "+0vqb c: f:o: k:e:", opts
, 0, 0, 0);
1330 if (strcmp(optarg
, "forever") == 0)
1332 else if ((exp
= get_date(optarg
, 0)) == -1)
1333 die(EXIT_FAILURE
, "bad expiry time");
1340 if (optind
!= argc
|| (f
& f_bogus
))
1341 die(EXIT_FAILURE
, "Usage: sign [-options]");
1343 /* --- Locate the signing key --- */
1345 if (key_open(&kf
, keyring
, KOPEN_WRITE
, keyreport
, 0))
1346 die(EXIT_FAILURE
, "couldn't open keyring `%s'", keyring
);
1347 if ((k
= key_bytag(&kf
, ki
)) == 0)
1348 die(EXIT_FAILURE
, "couldn't find key `%s'", ki
);
1350 if (exp
== KEXP_FOREVER
&& k
->exp
!= KEXP_FOREVER
) {
1351 die(EXIT_FAILURE
, "key `%s' expires: can't create nonexpiring signature",
1356 /* --- Open files --- */
1360 else if ((ifp
= fopen(ifile
, (f
& f_raw
) ?
"rb" : "r")) == 0) {
1361 die(EXIT_FAILURE
, "couldn't open input file `%s': %s",
1362 ifile
, strerror(errno
));
1367 else if ((ofp
= fopen(ofile
, (f
& f_bin
) ?
"wb" : "w")) == 0) {
1368 die(EXIT_FAILURE
, "couldn't open output file `%s': %s",
1369 ofile
, strerror(errno
));
1372 /* --- Emit the start of the output --- */
1374 binit(&b
); b
.tag
= T_IDENT
;
1375 dstr_putf(&b
.d
, "%s, Catacomb version " VERSION
, QUIS
);
1376 bemit(&b
, ofp
, 0, f
& f_bin
);
1378 breset(&b
); b
.tag
= T_KEYID
; b
.k
= k
->id
;
1379 bemit(&b
, ofp
, 0, f
& f_bin
);
1381 /* --- Start hashing, and emit the datestamps and things --- */
1384 time_t now
= time(0);
1386 breset(&b
); b
.tag
= T_DATE
; b
.t
= now
; bemit(&b
, ofp
, s
->h
, f
& f_bin
);
1387 if (exp
== KEXP_EXPIRE
)
1388 exp
= now
+ 86400 * 28;
1389 breset(&b
); b
.tag
= T_EXPIRE
; b
.t
= exp
; bemit(&b
, ofp
, s
->h
, f
& f_bin
);
1391 breset(&b
); b
.tag
= T_COMMENT
; DPUTS(&b
.d
, c
);
1392 bemit(&b
, ofp
, s
->h
, f
& f_bin
);
1399 /* --- Now hash the various files --- */
1403 /* --- Stop on an output error --- */
1410 /* --- Read the next filename to hash --- */
1413 if (getstring(ifp
, &b
.d
, f
& f_raw
))
1416 DENSURE(&b
.b
, GH_CLASS(s
->h
)->hashsz
);
1417 if (fhash(GH_CLASS(s
->h
), b
.d
.buf
, b
.b
.buf
)) {
1418 moan("Error reading `%s': %s", b
.d
.buf
, strerror(errno
));
1421 b
.b
.len
+= GH_CLASS(s
->h
)->hashsz
;
1423 fhex(stderr
, b
.b
.buf
, b
.b
.len
);
1424 fprintf(stderr
, " %s\n", b
.d
.buf
);
1426 bemit(&b
, ofp
, s
->h
, f
& f_bin
);
1430 /* --- Create the signature --- */
1432 if (!(f
& f_bogus
)) {
1434 b
.tag
= T_SIGNATURE
;
1435 if ((e
= s
->ops
->doit(s
, &b
.b
)) != 0) {
1436 moan("error creating signature: %s", key_strerror(e
));
1439 if (!(f
& f_bogus
)) {
1440 bemit(&b
, ofp
, 0, f
& f_bin
);
1441 key_used(&kf
, k
, exp
);
1445 /* --- Tidy up at the end --- */
1458 if ((e
= key_close(&kf
)) != 0) {
1461 die(EXIT_FAILURE
, "couldn't write file `%s': %s",
1462 keyring
, strerror(errno
));
1464 die(EXIT_FAILURE
, "keyring file `%s' broken: %s (repair manually)",
1465 keyring
, strerror(errno
));
1469 die(EXIT_FAILURE
, "error(s) occurred while creating signature");
1470 return (EXIT_SUCCESS
);
1477 /*----- Signature verification --------------------------------------------*/
1479 static int verify(int argc
, char *argv
[])
1495 /* --- Parse the options --- */
1498 static struct option opts
[] = {
1499 { "verbose", 0, 0, 'v' },
1500 { "quiet", 0, 0, 'q' },
1503 int i
= mdwopt(argc
, argv
, "+vq", opts
, 0, 0, 0);
1521 if ((f
& f_bogus
) || argc
> 1)
1522 die(EXIT_FAILURE
, "Usage: verify [-qv] [file]");
1524 /* --- Open the key file, and start reading the input file --- */
1526 if (key_open(&kf
, keyring
, KOPEN_READ
, keyreport
, 0))
1527 die(EXIT_FAILURE
, "couldn't open keyring `%s'\n", keyring
);
1531 if ((fp
= fopen(argv
[0], "rb")) == 0) {
1532 die(EXIT_FAILURE
, "couldn't open file `%s': %s\n",
1533 argv
[0], strerror(errno
));
1535 if (getc(fp
) == 0) {
1540 if ((fp
= fopen(argv
[0], "r")) == 0) {
1541 die(EXIT_FAILURE
, "couldn't open file `%s': %s\n",
1542 argv
[0], strerror(errno
));
1547 /* --- Read the introductory matter --- */
1552 e
= bget(&b
, fp
, f
& f_bin
);
1554 die(EXIT_FAILURE
, "error reading packet: %s", errtab
[-e
]);
1560 printf("INFO ident: `%s'\n", b
.d
.buf
);
1563 if ((k
= key_byid(&kf
, b
.k
)) == 0) {
1565 printf("FAIL key %08lx not found\n", (unsigned long)b
.k
);
1570 key_fulltag(k
, &b
.d
);
1571 printf("INFO key: %s\n", b
.d
.buf
);
1575 die(EXIT_FAILURE
, "(internal) unknown packet type\n");
1580 /* --- Initialize the hash function and start reading hashed packets --- */
1586 puts("FAIL no keyid packet found");
1594 printf("INFO comment: `%s'\n", b
.d
.buf
);
1595 bemit(&b
, 0, s
->h
, 0);
1600 timestring(b
.t
, &b
.d
);
1601 printf("INFO date: %s\n", b
.d
.buf
);
1603 bemit(&b
, 0, s
->h
, 0);
1606 time_t now
= time(0);
1609 puts("BAD signature has expired");
1614 timestring(b
.t
, &b
.d
);
1615 printf("INFO expires: %s\n", b
.d
.buf
);
1617 bemit(&b
, 0, s
->h
, 0);
1621 DENSURE(&d
, GH_CLASS(s
->h
)->hashsz
);
1622 if (fhash(GH_CLASS(s
->h
), b
.d
.buf
, d
.buf
)) {
1624 printf("BAD error reading file `%s': %s\n",
1625 b
.d
.buf
, strerror(errno
));
1628 } else if (b
.b
.len
!= GH_CLASS(s
->h
)->hashsz
||
1629 memcmp(d
.buf
, b
.b
.buf
, b
.b
.len
) != 0) {
1631 printf("BAD file `%s' has incorrect hash\n", b
.d
.buf
);
1633 } else if (verb
> 3) {
1634 fputs("INFO hash: ", stdout
);
1635 fhex(stdout
, b
.b
.buf
, b
.b
.len
);
1636 printf(" %s\n", b
.d
.buf
);
1638 bemit(&b
, 0, s
->h
, 0);
1641 if (s
->ops
->doit(s
, &b
.b
)) {
1643 puts("BAD bad signature");
1645 } else if (verb
> 2)
1646 puts("INFO good signature");
1650 printf("FAIL invalid packet type %i\n", e
);
1655 e
= bget(&b
, fp
, f
& f_bin
);
1658 printf("FAIL error reading packet: %s\n", errtab
[-e
]);
1671 puts("FAIL signature invalid");
1673 puts("OK signature verified");
1675 return (f
& f_bogus ? EXIT_FAILURE
: EXIT_SUCCESS
);
1682 /*----- Main code ---------------------------------------------------------*/
1684 typedef struct cmd
{
1686 int (*func
)(int /*argc*/, char */
*argv*/
[]);
1691 static cmd cmdtab
[] = {
1692 /* { "manifest", manifest, */
1693 /* "manifest [-0] [-o output]" }, */
1696 [-0bqv] [-c comment] [-k tag] [-i keyid]\n\
1697 [-e expire] [-f file] [-o output]", "\
1700 -0, --null Read null-terminated filenames from stdin.\n\
1701 -b, --binary Produce a binary output file.\n\
1702 -q, --quiet Produce fewer messages while working.\n\
1703 -v, --verbose Produce more messages while working.\n\
1704 -c, --comment=COMMENT Include COMMENT in the output file.\n\
1705 -f, --file=FILE Read filenames to hash from FILE.\n\
1706 -o, --output=FILE Write the signed result to FILE.\n\
1707 -k, --key=TAG Use a key named by TAG.\n\
1708 -e, --expire=TIME The signature should expire after TIME.\n\
1711 "verify [-qv] [file]", "\
1714 -q, --quiet Produce fewer messages while working.\n\
1715 -v, --verbose Produce more messages while working.\n\
1720 /* --- @findcmd@ --- *
1722 * Arguments: @const char *name@ = a command name
1724 * Returns: Pointer to the command structure.
1726 * Use: Looks up a command by name. If the command isn't found, an
1727 * error is reported and the program is terminated.
1730 static cmd
*findcmd(const char *name
)
1732 cmd
*c
, *chosen
= 0;
1733 size_t sz
= strlen(name
);
1735 for (c
= cmdtab
; c
->name
; c
++) {
1736 if (strncmp(name
, c
->name
, sz
) == 0) {
1737 if (c
->name
[sz
] == 0) {
1741 die(EXIT_FAILURE
, "ambiguous command name `%s'", name
);
1747 die(EXIT_FAILURE
, "unknown command name `%s'", name
);
1751 static void version(FILE *fp
)
1753 pquis(fp
, "$, Catacomb version " VERSION
"\n");
1756 static void usage(FILE *fp
)
1758 pquis(fp
, "Usage: $ [-k keyring] command [args]\n");
1761 static void help(FILE *fp
, char **argv
)
1767 fprintf(fp
, "Usage: %s [-k keyring] %s\n", QUIS
, c
->usage
);
1777 Create and verify signatures on lists of files.\n\
1779 for (c
= cmdtab
; c
->name
; c
++)
1780 fprintf(fp
, "%s\n", c
->usage
);
1786 * Arguments: @int argc@ = number of command line arguments
1787 * @char *argv[]@ = vector of command line arguments
1789 * Returns: Zero if successful, nonzero otherwise.
1791 * Use: Signs or verifies signatures on lists of files. Useful for
1792 * ensuring that a distribution is unmolested.
1795 int main(int argc
, char *argv
[])
1801 /* --- Initialize the library --- */
1805 rand_noisesrc(RAND_GLOBAL
, &noise_source
);
1806 rand_seed(RAND_GLOBAL
, 160);
1808 /* --- Parse options --- */
1811 static struct option opts
[] = {
1812 { "help", 0, 0, 'h' },
1813 { "version", 0, 0, 'v' },
1814 { "usage", 0, 0, 'u' },
1815 { "keyring", OPTF_ARGREQ
, 0, 'k' },
1818 int i
= mdwopt(argc
, argv
, "+hvu k:", opts
, 0, 0, 0);
1823 help(stdout
, argv
+ optind
);
1845 if (f
& f_bogus
|| argc
< 1) {
1850 /* --- Dispatch to the correct subcommand handler --- */
1852 return (findcmd(argv
[0])->func(argc
, argv
));
1857 /*----- That's all, folks -------------------------------------------------*/