3 * $Id: dsig.c,v 1.11 2004/04/08 16:27:49 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 /*----- Header files ------------------------------------------------------*/
40 #include <mLib/alloc.h>
41 #include <mLib/base64.h>
42 #include <mLib/mdwopt.h>
43 #include <mLib/quis.h>
44 #include <mLib/report.h>
64 /*----- Algorithm choice --------------------------------------------------*/
66 /* --- Relevant type operations --- */
69 const struct sigops
*ops
;
74 typedef struct sigops
{
75 const key_fetchdef
*kf
; /* Key fetching structure */
76 size_t kdsz
; /* Size of the key-data structure */
77 sig
*(*init
)(key */
*k*/
, void */
*kd*/
, const gchash */
*hc*/
);
78 int (*doit
)(sig */
*s*/
, dstr */
*d*/
);
79 void (*destroy
)(sig */
*s*/
);
82 /* --- RSA PKCS1 --- */
84 typedef struct rsap1_sigctx
{
90 static sig
*rsap1_siginit(key
*k
, void *kd
, const gchash
*hc
)
92 rsap1_sigctx
*rs
= CREATE(rsap1_sigctx
);
93 rsa_privcreate(&rs
->rp
, kd
, &rand_global
);
94 rs
->p1
.r
= &rand_global
;
96 rs
->p1
.epsz
= strlen(hc
->name
) + 1;
101 static int rsap1_sigdoit(sig
*s
, dstr
*d
)
103 rsap1_sigctx
*rs
= (rsap1_sigctx
*)s
;
105 mp
*m
= rsa_sign(&rs
->rp
, MP_NEW
,
106 GH_DONE(s
->h
, 0), GH_CLASS(s
->h
)->hashsz
,
107 pkcs1_sigencode
, &rs
->p1
);
109 n
= mp_octets(rs
->rp
.rp
->n
); dstr_ensure(d
, n
); mp_storeb(m
, d
->buf
, n
);
110 d
->len
+= n
; mp_drop(m
);
114 static void rsap1_sigdestroy(sig
*s
)
116 rsap1_sigctx
*rs
= (rsap1_sigctx
*)s
;
120 static const sigops rsap1_sig
= {
121 rsa_privfetch
, sizeof(rsa_priv
),
122 rsap1_siginit
, rsap1_sigdoit
, rsap1_sigdestroy
125 typedef struct rsap1_vrfctx
{
131 static sig
*rsap1_vrfinit(key
*k
, void *kd
, const gchash
*hc
)
133 rsap1_vrfctx
*rv
= CREATE(rsap1_vrfctx
);
134 rsa_pubcreate(&rv
->rp
, kd
);
135 rv
->p1
.r
= &rand_global
;
136 rv
->p1
.ep
= hc
->name
;
137 rv
->p1
.epsz
= strlen(hc
->name
) + 1;
142 static int rsap1_vrfdoit(sig
*s
, dstr
*d
)
144 rsap1_vrfctx
*rv
= (rsap1_vrfctx
*)s
;
145 mp
*m
= mp_loadb(MP_NEW
, d
->buf
, d
->len
);
146 int rc
= rsa_verify(&rv
->rp
, m
,
147 GH_DONE(s
->h
, 0), GH_CLASS(s
->h
)->hashsz
,
148 0, pkcs1_sigdecode
, &rv
->p1
);
153 static void rsap1_vrfdestroy(sig
*s
)
155 rsap1_vrfctx
*rv
= (rsap1_vrfctx
*)s
;
159 static const sigops rsap1_vrf
= {
160 rsa_pubfetch
, sizeof(rsa_pub
),
161 rsap1_vrfinit
, rsap1_vrfdoit
, rsap1_vrfdestroy
164 /* --- RSA PSS --- */
166 static const gccipher
*getmgf(key
*k
, const gchash
*hc
)
172 if ((mm
= key_getattr(0, k
, "mgf-alg")) == 0) {
173 dstr_putf(&d
, "%s-mgf", hc
->name
);
176 if ((gc
= gcipher_byname(mm
)) == 0)
177 die(EXIT_FAILURE
, "unknown encryption scheme `%s'", mm
);
182 typedef struct rsapss_sigctx
{
188 static sig
*rsapss_siginit(key
*k
, void *kd
, const gchash
*hc
)
190 rsapss_sigctx
*rs
= CREATE(rsapss_sigctx
);
191 rsa_privcreate(&rs
->rp
, kd
, &rand_global
);
192 rs
->p
.r
= &rand_global
;
193 rs
->p
.cc
= getmgf(k
, hc
);
195 rs
->p
.ssz
= hc
->hashsz
;
199 static int rsapss_sigdoit(sig
*s
, dstr
*d
)
201 rsapss_sigctx
*rs
= (rsapss_sigctx
*)s
;
203 mp
*m
= rsa_sign(&rs
->rp
, MP_NEW
,
204 GH_DONE(s
->h
, 0), GH_CLASS(s
->h
)->hashsz
,
207 n
= mp_octets(rs
->rp
.rp
->n
); dstr_ensure(d
, n
); mp_storeb(m
, d
->buf
, n
);
208 d
->len
+= n
; mp_drop(m
);
212 static void rsapss_sigdestroy(sig
*s
)
214 rsapss_sigctx
*rs
= (rsapss_sigctx
*)s
;
218 static const sigops rsapss_sig
= {
219 rsa_privfetch
, sizeof(rsa_priv
),
220 rsapss_siginit
, rsapss_sigdoit
, rsapss_sigdestroy
223 typedef struct rsapss_vrfctx
{
229 static sig
*rsapss_vrfinit(key
*k
, void *kd
, const gchash
*hc
)
231 rsapss_vrfctx
*rv
= CREATE(rsapss_vrfctx
);
232 rsa_pubcreate(&rv
->rp
, kd
);
233 rv
->p
.r
= &rand_global
;
234 rv
->p
.cc
= getmgf(k
, hc
);
236 rv
->p
.ssz
= hc
->hashsz
;
240 static int rsapss_vrfdoit(sig
*s
, dstr
*d
)
242 rsapss_vrfctx
*rv
= (rsapss_vrfctx
*)s
;
243 mp
*m
= mp_loadb(MP_NEW
, d
->buf
, d
->len
);
244 int rc
= rsa_verify(&rv
->rp
, m
,
245 GH_DONE(s
->h
, 0), GH_CLASS(s
->h
)->hashsz
,
246 0, pss_decode
, &rv
->p
);
251 static void rsapss_vrfdestroy(sig
*s
)
253 rsapss_vrfctx
*rv
= (rsapss_vrfctx
*)s
;
257 static const sigops rsapss_vrf
= {
258 rsa_pubfetch
, sizeof(rsa_pub
),
259 rsapss_vrfinit
, rsapss_vrfdoit
, rsapss_vrfdestroy
262 /* --- DSA and ECDSA --- */
264 typedef struct dsa_sigctx
{
269 static void dsa_initcommon(dsa_sigctx
*ds
, const gchash
*hc
,
272 ds
->g
.r
= &rand_global
;
278 static dsa_sigctx
*dsa_doinit(key
*k
, const gprime_param
*gp
,
279 mp
*y
, const gchash
*hc
)
281 dsa_sigctx
*ds
= CREATE(dsa_sigctx
);
285 if ((ds
->g
.g
= group_prime(gp
)) == 0)
286 die(EXIT_FAILURE
, "bad prime group in key `%s'", t
.buf
);
287 ds
->g
.p
= G_CREATE(ds
->g
.g
);
288 if (G_FROMINT(ds
->g
.g
, ds
->g
.p
, y
))
289 die(EXIT_FAILURE
, "bad public key in key `%s'", t
.buf
);
290 dsa_initcommon(ds
, hc
, t
.buf
);
295 static dsa_sigctx
*ecdsa_doinit(key
*k
, const char *cstr
,
296 ec
*y
, const gchash
*hc
)
298 dsa_sigctx
*ds
= CREATE(dsa_sigctx
);
304 if ((e
= ec_getinfo(&ei
, cstr
)) != 0)
305 die(EXIT_FAILURE
, "bad curve in key `%s': %s", t
.buf
, e
);
306 ds
->g
.g
= group_ec(&ei
);
307 ds
->g
.p
= G_CREATE(ds
->g
.g
);
308 if (G_FROMEC(ds
->g
.g
, ds
->g
.p
, y
))
309 die(EXIT_FAILURE
, "bad public key in key `%s'", t
.buf
);
310 dsa_initcommon(ds
, hc
, t
.buf
);
315 static sig
*dsa_siginit(key
*k
, void *kd
, const gchash
*hc
)
318 dsa_sigctx
*ds
= dsa_doinit(k
, &dp
->dp
, dp
->y
, hc
);
319 ds
->g
.u
= MP_COPY(dp
->x
);
323 static sig
*ecdsa_siginit(key
*k
, void *kd
, const gchash
*hc
)
326 dsa_sigctx
*ds
= ecdsa_doinit(k
, ep
->cstr
, &ep
->p
, hc
);
327 ds
->g
.u
= MP_COPY(ep
->x
);
331 static int dsa_sigdoit(sig
*s
, dstr
*d
)
333 dsa_sigctx
*ds
= (dsa_sigctx
*)s
;
334 gdsa_sig ss
= GDSA_SIG_INIT
;
335 size_t n
= mp_octets(ds
->g
.g
->r
);
337 gdsa_sign(&ds
->g
, &ss
, GH_DONE(ds
->s
.h
, 0), 0);
338 dstr_ensure(d
, 2 * n
);
339 mp_storeb(ss
.r
, d
->buf
, n
);
340 mp_storeb(ss
.s
, d
->buf
+ n
, n
);
342 mp_drop(ss
.r
); mp_drop(ss
.s
);
346 static void dsa_sigdestroy(sig
*s
)
348 dsa_sigctx
*ds
= (dsa_sigctx
*)s
;
349 G_DESTROY(ds
->g
.g
, ds
->g
.p
);
351 G_DESTROYGROUP(ds
->g
.g
);
354 static const sigops dsa_sig
= {
355 dh_privfetch
, sizeof(dh_priv
),
356 dsa_siginit
, dsa_sigdoit
, dsa_sigdestroy
359 static const sigops ecdsa_sig
= {
360 ec_privfetch
, sizeof(ec_priv
),
361 ecdsa_siginit
, dsa_sigdoit
, dsa_sigdestroy
364 static sig
*dsa_vrfinit(key
*k
, void *kd
, const gchash
*hc
)
367 dsa_sigctx
*ds
= dsa_doinit(k
, &dp
->dp
, dp
->y
, hc
);
371 static sig
*ecdsa_vrfinit(key
*k
, void *kd
, const gchash
*hc
)
374 dsa_sigctx
*ds
= ecdsa_doinit(k
, ep
->cstr
, &ep
->p
, hc
);
378 static int dsa_vrfdoit(sig
*s
, dstr
*d
)
380 dsa_sigctx
*ds
= (dsa_sigctx
*)s
;
385 ss
.r
= mp_loadb(MP_NEW
, d
->buf
, n
);
386 ss
.s
= mp_loadb(MP_NEW
, d
->buf
+ n
, d
->len
- n
);
387 rc
= gdsa_verify(&ds
->g
, &ss
, GH_DONE(ds
->s
.h
, 0));
388 mp_drop(ss
.r
); mp_drop(ss
.s
);
392 static const sigops dsa_vrf
= {
393 dh_pubfetch
, sizeof(dh_pub
),
394 dsa_vrfinit
, dsa_vrfdoit
, dsa_sigdestroy
397 static const sigops ecdsa_vrf
= {
398 ec_pubfetch
, sizeof(ec_pub
),
399 ecdsa_vrfinit
, dsa_vrfdoit
, dsa_sigdestroy
402 /* --- KCDSA and ECKCDSA --- */
404 static void kcdsa_privkey(dsa_sigctx
*ds
, mp
*x
)
405 { ds
->g
.u
= mp_modinv(MP_NEW
, x
, ds
->g
.g
->r
); }
407 static void kcdsa_sethash(dsa_sigctx
*ds
, const gchash
*hc
)
408 { ds
->s
.h
= gkcdsa_beginhash(&ds
->g
); }
410 static sig
*kcdsa_siginit(key
*k
, void *kd
, const gchash
*hc
)
413 dsa_sigctx
*ds
= dsa_doinit(k
, &dp
->dp
, dp
->y
, hc
);
414 kcdsa_privkey(ds
, dp
->x
);
415 kcdsa_sethash(ds
, hc
);
419 static sig
*eckcdsa_siginit(key
*k
, void *kd
, const gchash
*hc
)
422 dsa_sigctx
*ds
= ecdsa_doinit(k
, ep
->cstr
, &ep
->p
, hc
);
423 kcdsa_privkey(ds
, ep
->x
);
424 kcdsa_sethash(ds
, hc
);
428 static int kcdsa_sigdoit(sig
*s
, dstr
*d
)
430 dsa_sigctx
*ds
= (dsa_sigctx
*)s
;
431 gkcdsa_sig ss
= GKCDSA_SIG_INIT
;
432 size_t hsz
= ds
->g
.h
->hashsz
, n
= mp_octets(ds
->g
.g
->r
);
434 gkcdsa_sign(&ds
->g
, &ss
, GH_DONE(ds
->s
.h
, 0), 0);
435 dstr_ensure(d
, hsz
+ n
);
436 memcpy(d
->buf
, ss
.r
, hsz
);
437 mp_storeb(ss
.s
, d
->buf
+ hsz
, n
);
439 xfree(ss
.r
); mp_drop(ss
.s
);
443 static const sigops kcdsa_sig
= {
444 dh_privfetch
, sizeof(dh_priv
),
445 kcdsa_siginit
, kcdsa_sigdoit
, dsa_sigdestroy
448 static const sigops eckcdsa_sig
= {
449 ec_privfetch
, sizeof(ec_priv
),
450 eckcdsa_siginit
, kcdsa_sigdoit
, dsa_sigdestroy
453 static sig
*kcdsa_vrfinit(key
*k
, void *kd
, const gchash
*hc
)
456 dsa_sigctx
*ds
= dsa_doinit(k
, &dp
->dp
, dp
->y
, hc
);
457 kcdsa_sethash(ds
, hc
);
461 static sig
*eckcdsa_vrfinit(key
*k
, void *kd
, const gchash
*hc
)
464 dsa_sigctx
*ds
= ecdsa_doinit(k
, ep
->cstr
, &ep
->p
, hc
);
465 kcdsa_sethash(ds
, hc
);
469 static int kcdsa_vrfdoit(sig
*s
, dstr
*d
)
471 dsa_sigctx
*ds
= (dsa_sigctx
*)s
;
473 size_t hsz
= ds
->g
.h
->hashsz
, n
= d
->len
- hsz
;
478 ss
.r
= (octet
*)d
->buf
;
479 ss
.s
= mp_loadb(MP_NEW
, d
->buf
+ hsz
, n
);
480 rc
= gkcdsa_verify(&ds
->g
, &ss
, GH_DONE(ds
->s
.h
, 0));
485 static const sigops kcdsa_vrf
= {
486 dh_pubfetch
, sizeof(dh_pub
),
487 kcdsa_vrfinit
, kcdsa_vrfdoit
, dsa_sigdestroy
490 static const sigops eckcdsa_vrf
= {
491 ec_pubfetch
, sizeof(ec_pub
),
492 eckcdsa_vrfinit
, kcdsa_vrfdoit
, dsa_sigdestroy
495 /* --- The switch table --- */
497 static const struct sigtab
{
499 const sigops
*signops
;
500 const sigops
*verifyops
;
503 { "rsapkcs1", &rsap1_sig
, &rsap1_vrf
, &sha
},
504 { "rsapss", &rsapss_sig
, &rsapss_vrf
, &sha
},
505 { "dsa", &dsa_sig
, &dsa_vrf
, &sha
},
506 { "ecdsa", &ecdsa_sig
, &ecdsa_vrf
, &sha
},
507 { "kcdsa", &kcdsa_sig
, &kcdsa_vrf
, &has160
},
508 { "eckcdsa", &eckcdsa_sig
, &eckcdsa_vrf
, &has160
},
512 /* --- @getsig@ --- *
514 * Arguments: @key *k@ = the key to load
515 * @int wantpriv@ = nonzero if we want to sign
517 * Returns: A signature-making thing.
519 * Use: Loads a key and starts hashing.
522 static sig
*getsig(key
*k
, int wantpriv
)
524 const char *salg
, *halg
= 0;
530 const struct sigtab
*st
;
537 /* --- Setup stuff --- */
541 /* --- Get the signature algorithm --- *
543 * Take the attribute if it's there; otherwise use the key type.
546 if ((q
= key_getattr(0, k
, "sig-alg")) != 0) {
549 } else if (strncmp(k
->type
, "dsig-", 5) == 0 && k
->type
[5]) {
550 dstr_puts(&d
, k
->type
);
553 die(EXIT_FAILURE
, "no signature algorithm for key `%s'", t
.buf
);
555 /* --- Grab the hash algorithm --- *
557 * Grab it from the signature algorithm if it's there. But override that
558 * from the attribute.
562 if ((p
= strchr(p
, '-')) != 0) {
566 if ((q
= key_getattr(0, k
, "hash-alg")) != 0)
569 /* --- Look up the algorithms in the table --- */
571 for (st
= sigtab
; st
->name
; st
++) {
572 if (strcmp(st
->name
, salg
) == 0)
575 die(EXIT_FAILURE
, "signature algorithm `%s' not found in key `%s'",
581 if ((ch
= ghash_byname(halg
)) == 0) {
582 die(EXIT_FAILURE
, "hash algorithm `%s' not found in key `%s'",
586 so
= wantpriv ? st
->signops
: st
->verifyops
;
588 /* --- Load the key --- */
590 kd
= xmalloc(so
->kdsz
);
591 kp
= key_fetchinit(so
->kf
, 0, kd
);
592 if ((e
= key_fetch(kp
, k
)) != 0)
593 die(EXIT_FAILURE
, "error fetching key `%s': %s", t
.buf
, key_strerror(e
));
594 s
= so
->init(k
, kd
, ch
);
600 /* --- Free stuff up --- */
607 /* --- @freesig@ --- *
609 * Arguments: @sig *s@ = signature-making thing
613 * Use: Frees up a signature-making thing
616 static void freesig(sig
*s
)
619 key_fetchdone(s
->kp
);
623 /*----- Data formatting ---------------------------------------------------*/
625 /* --- Binary data structure --- *
627 * The binary format, which is used for hashing and for the optional binary
628 * output, consists of a sequence of tagged blocks. The tag describes the
629 * format and meaining of the following data.
633 /* --- Block tags --- */
635 T_IDENT
= 0, /* An identifying marker */
636 T_KEYID
, /* Key identifier */
637 T_BEGIN
, /* Begin hashing here */
638 T_COMMENT
= T_BEGIN
, /* A textual comment */
639 T_DATE
, /* Creation date of signature */
640 T_EXPIRE
, /* Expiry date of signature */
641 T_FILE
, /* File and corresponding hash */
642 T_SIGNATURE
, /* Final signature block */
644 /* --- Error messages --- */
652 /* --- Name translation table --- */
654 static const char *tagtab
[] = {
656 "comment:", "date:", "expires:", "file:",
661 static const char *errtab
[] = {
663 "Unexpected end-of-file",
664 "Binary object too large",
669 /* --- Memory representation of block types --- */
671 typedef struct block
{
672 int tag
; /* Type tag */
673 dstr d
; /* String data */
674 dstr b
; /* Binary data */
675 time_t t
; /* Timestamp */
676 uint32 k
; /* Keyid */
679 /* --- @getstring@ --- *
681 * Arguments: @FILE *fp@ = stream from which to read
682 * @dstr *d@ = destination string
683 * @unsigned raw@ = raw or cooked read
685 * Returns: Zero if OK, nonzero on end-of-file.
687 * Use: Reads a filename (or something similar) from a stream.
690 static int getstring(FILE *fp
, dstr
*d
, unsigned raw
)
695 /* --- Raw: just read exactly what's written up to a null byte --- */
698 if ((ch
= getc(fp
)) == EOF
)
704 if ((ch
= getc(fp
)) == EOF
)
711 /* --- Skip as far as whitespace --- *
713 * Also skip past comments.
721 do ch
= getc(fp
); while (ch
!= '\n' && ch
!= EOF
);
727 /* --- If the character is a quote then read a quoted string --- */
739 /* --- Now read all sorts of interesting things --- */
743 /* --- Handle an escaped thing --- */
750 case 'a': ch
= '\a'; break;
751 case 'b': ch
= '\b'; break;
752 case 'f': ch
= '\f'; break;
753 case 'n': ch
= '\n'; break;
754 case 'r': ch
= '\r'; break;
755 case 't': ch
= '\t'; break;
756 case 'v': ch
= '\v'; break;
763 /* --- If it's a quote or some other end marker then stop --- */
765 if (ch
== q
|| (!q
&& isspace((unsigned char)ch
)))
768 /* --- Otherwise contribute and continue --- */
771 if ((ch
= getc(fp
)) == EOF
)
781 /* --- @putstring@ --- *
783 * Arguments: @FILE *fp@ = stream to write on
784 * @const char *p@ = pointer to text
785 * @unsigned raw@ = whether the string is to be written raw
789 * Use: Emits a string to a stream.
792 static void putstring(FILE *fp
, const char *p
, unsigned raw
)
794 size_t sz
= strlen(p
);
798 /* --- Just write the string null terminated if raw --- */
801 fwrite(p
, 1, sz
+ 1, fp
);
805 /* --- Check for any dodgy characters --- */
808 for (q
= p
; *q
; q
++) {
809 if (isspace((unsigned char)*q
)) {
818 /* --- Emit the string --- */
820 for (q
= p
; *q
; q
++) {
822 case '\a': fputc('\\', fp
); fputc('a', fp
); break;
823 case '\b': fputc('\\', fp
); fputc('b', fp
); break;
824 case '\f': fputc('\\', fp
); fputc('f', fp
); break;
825 case '\n': fputc('\\', fp
); fputc('n', fp
); break;
826 case '\r': fputc('\\', fp
); fputc('r', fp
); break;
827 case '\t': fputc('\\', fp
); fputc('t', fp
); break;
828 case '\v': fputc('\\', fp
); fputc('v', fp
); break;
829 case '`': fputc('\\', fp
); fputc('`', fp
); break;
830 case '\'': fputc('\\', fp
); fputc('\'', fp
); break;
831 case '\"': fputc('\\', fp
); fputc('\"', fp
); break;
844 /* --- @timestring@ --- *
846 * Arguments: @time_t t@ = a timestamp
847 * @dstr *d@ = a string to write on
851 * Use: Writes a textual representation of the timestamp to the
855 static void timestring(time_t t
, dstr
*d
)
857 if (t
== KEXP_FOREVER
)
860 struct tm
*tm
= localtime(&t
);
862 d
->len
+= strftime(d
->buf
+ d
->len
, 32, "%Y-%m-%d %H:%M:%S %Z", tm
);
867 /* --- @breset@ --- *
869 * Arguments: @block *b@ = block to reset
873 * Use: Resets a block so that more stuff can be put in it.
876 static void breset(block
*b
)
887 * Arguments: @block *b@ = block to initialize
891 * Use: Initializes a block as something to read into.
894 static void binit(block
*b
)
901 /* --- @bdestroy@ --- *
903 * Arguments: @block *b@ = block to destroy
907 * Use: Destroys a block's contents.
910 static void bdestroy(block
*b
)
918 * Arguments: @block *b@ = pointer to block
919 * @FILE *fp@ = stream to read from
920 * @unsigned bin@ = binary switch
922 * Returns: Tag of block, or an error tag.
924 * Use: Reads a block from a stream.
927 static int bget(block
*b
, FILE *fp
, unsigned bin
)
931 /* --- Read the tag --- */
937 if (getstring(fp
, &d
, 0))
939 for (tag
= 0; tagtab
[tag
]; tag
++) {
940 if (strcmp(tagtab
[tag
], d
.buf
) == 0)
947 /* --- Decide what to do next --- */
953 /* --- Reading of strings --- */
957 if (getstring(fp
, &b
->d
, bin
))
961 /* --- Timestamps --- */
967 if (fread(buf
, sizeof(buf
), 1, fp
) < 1)
969 b
->t
= ((time_t)(((LOAD32(buf
+ 0) << 16) << 16) & ~MASK32
) |
970 (time_t)LOAD32(buf
+ 4));
972 if (getstring(fp
, &b
->d
, 0))
974 if (strcmp(b
->d
.buf
, "forever") == 0)
976 else if ((b
->t
= get_date(b
->d
.buf
, 0)) == -1)
981 /* --- Key ids --- */
986 if (fread(buf
, sizeof(buf
), 1, fp
) < 1)
990 if (getstring(fp
, &b
->d
, 0))
992 b
->k
= strtoul(b
->d
.buf
, 0, 16);
996 /* --- Reading of binary data --- */
1003 if (fread(buf
, sizeof(buf
), 1, fp
) < 1)
1009 if (fread(b
->b
.buf
+ b
->b
.len
, 1, sz
, fp
) < sz
)
1014 if (getstring(fp
, &b
->d
, 0))
1017 base64_decode(&b64
, b
->d
.buf
, b
->d
.len
, &b
->b
);
1018 base64_decode(&b64
, 0, 0, &b
->b
);
1021 if (tag
== T_FILE
&& getstring(fp
, &b
->d
, bin
))
1025 /* --- Anything else --- */
1036 * Arguments: @block *b@ = pointer to block to emit
1037 * @dstr *d@ = output buffer
1041 * Use: Encodes a block in a binary format.
1044 static void blob(block
*b
, dstr
*d
)
1056 STORE32(d
->buf
+ d
->len
, ((b
->t
& ~MASK32
) >> 16) >> 16);
1057 STORE32(d
->buf
+ d
->len
+ 4, b
->t
);
1062 STORE32(d
->buf
+ d
->len
, b
->k
);
1068 STORE16(d
->buf
+ d
->len
, b
->b
.len
);
1071 if (b
->tag
== T_FILE
) {
1079 /* --- @bwrite@ --- *
1081 * Arguments: @block *b@ = pointer to block to write
1082 * @FILE *fp@ = stream to write on
1086 * Use: Writes a block on a stream in a textual format.
1089 static void bwrite(block
*b
, FILE *fp
)
1091 fputs(tagtab
[b
->tag
], fp
);
1096 putstring(fp
, b
->d
.buf
, 0);
1101 timestring(b
->t
, &d
);
1102 putstring(fp
, d
.buf
, 0);
1106 fprintf(fp
, "%08lx", (unsigned long)b
->k
);
1114 base64_encode(&b64
, b
->b
.buf
, b
->b
.len
, &d
);
1115 base64_encode(&b64
, 0, 0, &d
);
1117 if (b
->tag
== T_FILE
) {
1119 putstring(fp
, b
->d
.buf
, 0);
1126 /* --- @bemit@ --- *
1128 * Arguments: @block *b@ = pointer to block to write
1129 * @FILE *fp@ = file to write on
1130 * @ghash *h@ = pointer to hash function
1131 * @unsigned bin@ = binary/text flag
1135 * Use: Spits out a block properly.
1138 static void bemit(block
*b
, FILE *fp
, ghash
*h
, unsigned bin
)
1140 if (h
|| (fp
&& bin
)) {
1144 GH_HASH(h
, d
.buf
, d
.len
);
1146 fwrite(d
.buf
, d
.len
, 1, fp
);
1152 /*----- Static variables --------------------------------------------------*/
1154 static const char *keyring
= "keyring";
1156 /*----- Other shared functions --------------------------------------------*/
1158 /* --- @keyreport@ --- *
1160 * Arguments: @const char *file@ = filename containing the error
1161 * @int line@ = line number in file
1162 * @const char *err@ = error text message
1163 * @void *p@ = unimportant pointer
1167 * Use: Reports errors during the opening of a key file.
1170 static void keyreport(const char *file
, int line
, const char *err
, void *p
)
1172 moan("error in keyring `%s' at line `%s': %s", file
, line
, err
);
1175 /* --- @fhash@ --- *
1177 * Arguments: @const gchash *c@ = pointer to hash class
1178 * @const char *file@ = file to hash
1179 * @void *b@ = pointer to output buffer
1181 * Returns: Zero if it worked, or nonzero for a system error.
1183 * Use: Hashes a file.
1186 static int fhash(const gchash
*c
, const char *file
, void *b
)
1188 FILE *fp
= fopen(file
, "rb");
1189 ghash
*h
= GH_INIT(c
);
1196 while ((sz
= fread(buf
, 1, sizeof(buf
), fp
)) > 0)
1197 GH_HASH(h
, buf
, sz
);
1208 * Arguments: @FILE *fp@ = file to write on
1209 * @const void *p@ = pointer to data to be written
1210 * @size_t sz@ = size of the data to write
1214 * Use: Emits a hex dump to a stream.
1217 static void fhex(FILE *fp
, const void *p
, size_t sz
)
1223 fprintf(fp
, "%02x", *q
++);
1227 /* putc(' ', fp); */
1231 /*----- Signature generation ----------------------------------------------*/
1233 static int sign(int argc
, char *argv
[])
1240 const char *ki
= "dsig";
1244 time_t exp
= KEXP_EXPIRE
;
1246 const char *ifile
= 0;
1247 const char *ofile
= 0;
1255 static struct option opts
[] = {
1256 { "null", 0, 0, '0' },
1257 { "binary", 0, 0, 'b' },
1258 { "verbose", 0, 0, 'v' },
1259 { "quiet", 0, 0, 'q' },
1260 { "comment", OPTF_ARGREQ
, 0, 'c' },
1261 { "file", OPTF_ARGREQ
, 0, 'f' },
1262 { "output", OPTF_ARGREQ
, 0, 'o' },
1263 { "key", OPTF_ARGREQ
, 0, 'k' },
1264 { "expire", OPTF_ARGREQ
, 0, 'e' },
1267 int i
= mdwopt(argc
, argv
, "+0vqb c: f:o: k:e:", opts
, 0, 0, 0);
1297 if (strcmp(optarg
, "forever") == 0)
1299 else if ((exp
= get_date(optarg
, 0)) == -1)
1300 die(EXIT_FAILURE
, "bad expiry time");
1307 if (optind
!= argc
|| (f
& f_bogus
))
1308 die(EXIT_FAILURE
, "Usage: sign [-options]");
1310 /* --- Locate the signing key --- */
1312 if (key_open(&kf
, keyring
, KOPEN_WRITE
, keyreport
, 0))
1313 die(EXIT_FAILURE
, "couldn't open keyring `%s'", keyring
);
1314 if ((k
= key_bytag(&kf
, ki
)) == 0)
1315 die(EXIT_FAILURE
, "couldn't find key `%s'", ki
);
1317 if (exp
== KEXP_FOREVER
&& k
->exp
!= KEXP_FOREVER
) {
1318 die(EXIT_FAILURE
, "key `%s' expires: can't create nonexpiring signature",
1323 /* --- Open files --- */
1327 else if ((ifp
= fopen(ifile
, (f
& f_raw
) ?
"rb" : "r")) == 0) {
1328 die(EXIT_FAILURE
, "couldn't open input file `%s': %s",
1329 ifile
, strerror(errno
));
1334 else if ((ofp
= fopen(ofile
, (f
& f_bin
) ?
"wb" : "w")) == 0) {
1335 die(EXIT_FAILURE
, "couldn't open output file `%s': %s",
1336 ofile
, strerror(errno
));
1339 /* --- Emit the start of the output --- */
1341 binit(&b
); b
.tag
= T_IDENT
;
1342 dstr_putf(&b
.d
, "%s, Catacomb version " VERSION
, QUIS
);
1343 bemit(&b
, ofp
, 0, f
& f_bin
);
1345 breset(&b
); b
.tag
= T_KEYID
; b
.k
= k
->id
;
1346 bemit(&b
, ofp
, 0, f
& f_bin
);
1348 /* --- Start hashing, and emit the datestamps and things --- */
1351 time_t now
= time(0);
1353 breset(&b
); b
.tag
= T_DATE
; b
.t
= now
; bemit(&b
, ofp
, s
->h
, f
& f_bin
);
1354 if (exp
== KEXP_EXPIRE
)
1355 exp
= now
+ 86400 * 28;
1356 breset(&b
); b
.tag
= T_EXPIRE
; b
.t
= exp
; bemit(&b
, ofp
, s
->h
, f
& f_bin
);
1358 breset(&b
); b
.tag
= T_COMMENT
; DPUTS(&b
.d
, c
);
1359 bemit(&b
, ofp
, s
->h
, f
& f_bin
);
1366 /* --- Now hash the various files --- */
1370 /* --- Stop on an output error --- */
1377 /* --- Read the next filename to hash --- */
1380 if (getstring(ifp
, &b
.d
, f
& f_raw
))
1383 DENSURE(&b
.b
, GH_CLASS(s
->h
)->hashsz
);
1384 if (fhash(GH_CLASS(s
->h
), b
.d
.buf
, b
.b
.buf
)) {
1385 moan("Error reading `%s': %s", b
.d
.buf
, strerror(errno
));
1388 b
.b
.len
+= GH_CLASS(s
->h
)->hashsz
;
1390 fhex(stderr
, b
.b
.buf
, b
.b
.len
);
1391 fprintf(stderr
, " %s\n", b
.d
.buf
);
1393 bemit(&b
, ofp
, s
->h
, f
& f_bin
);
1397 /* --- Create the signature --- */
1399 if (!(f
& f_bogus
)) {
1401 b
.tag
= T_SIGNATURE
;
1402 if ((e
= s
->ops
->doit(s
, &b
.b
)) != 0) {
1403 moan("error creating signature: %s", key_strerror(e
));
1406 if (!(f
& f_bogus
)) {
1407 bemit(&b
, ofp
, 0, f
& f_bin
);
1408 key_used(&kf
, k
, exp
);
1412 /* --- Tidy up at the end --- */
1425 if ((e
= key_close(&kf
)) != 0) {
1428 die(EXIT_FAILURE
, "couldn't write file `%s': %s",
1429 keyring
, strerror(errno
));
1431 die(EXIT_FAILURE
, "keyring file `%s' broken: %s (repair manually)",
1432 keyring
, strerror(errno
));
1436 die(EXIT_FAILURE
, "error(s) occurred while creating signature");
1437 return (EXIT_SUCCESS
);
1444 /*----- Signature verification --------------------------------------------*/
1446 static int verify(int argc
, char *argv
[])
1462 /* --- Parse the options --- */
1465 static struct option opts
[] = {
1466 { "verbose", 0, 0, 'v' },
1467 { "quiet", 0, 0, 'q' },
1470 int i
= mdwopt(argc
, argv
, "+vq", opts
, 0, 0, 0);
1488 if ((f
& f_bogus
) || argc
> 1)
1489 die(EXIT_FAILURE
, "Usage: verify [-qv] [file]");
1491 /* --- Open the key file, and start reading the input file --- */
1493 if (key_open(&kf
, keyring
, KOPEN_READ
, keyreport
, 0))
1494 die(EXIT_FAILURE
, "couldn't open keyring `%s'\n", keyring
);
1498 if ((fp
= fopen(argv
[0], "rb")) == 0) {
1499 die(EXIT_FAILURE
, "couldn't open file `%s': %s\n",
1500 argv
[0], strerror(errno
));
1502 if (getc(fp
) == 0) {
1507 if ((fp
= fopen(argv
[0], "r")) == 0) {
1508 die(EXIT_FAILURE
, "couldn't open file `%s': %s\n",
1509 argv
[0], strerror(errno
));
1514 /* --- Read the introductory matter --- */
1519 e
= bget(&b
, fp
, f
& f_bin
);
1521 die(EXIT_FAILURE
, "error reading packet: %s", errtab
[-e
]);
1527 printf("INFO ident: `%s'\n", b
.d
.buf
);
1530 if ((k
= key_byid(&kf
, b
.k
)) == 0) {
1532 printf("FAIL key %08lx not found\n", (unsigned long)b
.k
);
1537 key_fulltag(k
, &b
.d
);
1538 printf("INFO key: %s\n", b
.d
.buf
);
1542 die(EXIT_FAILURE
, "(internal) unknown packet type\n");
1547 /* --- Initialize the hash function and start reading hashed packets --- */
1553 puts("FAIL no keyid packet found");
1561 printf("INFO comment: `%s'\n", b
.d
.buf
);
1562 bemit(&b
, 0, s
->h
, 0);
1567 timestring(b
.t
, &b
.d
);
1568 printf("INFO date: %s\n", b
.d
.buf
);
1570 bemit(&b
, 0, s
->h
, 0);
1573 time_t now
= time(0);
1576 puts("BAD signature has expired");
1581 timestring(b
.t
, &b
.d
);
1582 printf("INFO expires: %s\n", b
.d
.buf
);
1584 bemit(&b
, 0, s
->h
, 0);
1588 DENSURE(&d
, GH_CLASS(s
->h
)->hashsz
);
1589 if (fhash(GH_CLASS(s
->h
), b
.d
.buf
, d
.buf
)) {
1591 printf("BAD error reading file `%s': %s\n",
1592 b
.d
.buf
, strerror(errno
));
1595 } else if (b
.b
.len
!= GH_CLASS(s
->h
)->hashsz
||
1596 memcmp(d
.buf
, b
.b
.buf
, b
.b
.len
) != 0) {
1598 printf("BAD file `%s' has incorrect hash\n", b
.d
.buf
);
1600 } else if (verb
> 3) {
1601 fputs("INFO hash: ", stdout
);
1602 fhex(stdout
, b
.b
.buf
, b
.b
.len
);
1603 printf(" %s\n", b
.d
.buf
);
1605 bemit(&b
, 0, s
->h
, 0);
1608 if (s
->ops
->doit(s
, &b
.b
)) {
1610 puts("BAD bad signature");
1612 } else if (verb
> 2)
1613 puts("INFO good signature");
1617 printf("FAIL invalid packet type %i\n", e
);
1622 e
= bget(&b
, fp
, f
& f_bin
);
1625 printf("FAIL error reading packet: %s\n", errtab
[-e
]);
1638 puts("FAIL signature invalid");
1640 puts("OK signature verified");
1642 return (f
& f_bogus ? EXIT_FAILURE
: EXIT_SUCCESS
);
1649 /*----- Main code ---------------------------------------------------------*/
1651 typedef struct cmd
{
1653 int (*func
)(int /*argc*/, char */
*argv*/
[]);
1658 static cmd cmdtab
[] = {
1659 /* { "manifest", manifest, */
1660 /* "manifest [-0] [-o output]" }, */
1663 [-0bqv] [-c comment] [-k tag] [-i keyid]\n\
1664 [-e expire] [-f file] [-o output]", "\
1667 -0, --null Read null-terminated filenames from stdin.\n\
1668 -b, --binary Produce a binary output file.\n\
1669 -q, --quiet Produce fewer messages while working.\n\
1670 -v, --verbose Produce more messages while working.\n\
1671 -c, --comment=COMMENT Include COMMENT in the output file.\n\
1672 -f, --file=FILE Read filenames to hash from FILE.\n\
1673 -o, --output=FILE Write the signed result to FILE.\n\
1674 -k, --key=TAG Use a key named by TAG.\n\
1675 -e, --expire=TIME The signature should expire after TIME.\n\
1678 "verify [-qv] [file]", "\
1681 -q, --quiet Produce fewer messages while working.\n\
1682 -v, --verbose Produce more messages while working.\n\
1687 /* --- @findcmd@ --- *
1689 * Arguments: @const char *name@ = a command name
1691 * Returns: Pointer to the command structure.
1693 * Use: Looks up a command by name. If the command isn't found, an
1694 * error is reported and the program is terminated.
1697 static cmd
*findcmd(const char *name
)
1699 cmd
*c
, *chosen
= 0;
1700 size_t sz
= strlen(name
);
1702 for (c
= cmdtab
; c
->name
; c
++) {
1703 if (strncmp(name
, c
->name
, sz
) == 0) {
1704 if (c
->name
[sz
] == 0) {
1708 die(EXIT_FAILURE
, "ambiguous command name `%s'", name
);
1714 die(EXIT_FAILURE
, "unknown command name `%s'", name
);
1718 static void version(FILE *fp
)
1720 pquis(fp
, "$, Catacomb version " VERSION
"\n");
1723 static void usage(FILE *fp
)
1725 pquis(fp
, "Usage: $ [-k keyring] command [args]\n");
1728 static void help(FILE *fp
, char **argv
)
1734 fprintf(fp
, "Usage: %s [-k keyring] %s\n", QUIS
, c
->usage
);
1744 Create and verify signatures on lists of files.\n\
1746 for (c
= cmdtab
; c
->name
; c
++)
1747 fprintf(fp
, "%s\n", c
->usage
);
1753 * Arguments: @int argc@ = number of command line arguments
1754 * @char *argv[]@ = vector of command line arguments
1756 * Returns: Zero if successful, nonzero otherwise.
1758 * Use: Signs or verifies signatures on lists of files. Useful for
1759 * ensuring that a distribution is unmolested.
1762 int main(int argc
, char *argv
[])
1768 /* --- Initialize the library --- */
1772 rand_noisesrc(RAND_GLOBAL
, &noise_source
);
1773 rand_seed(RAND_GLOBAL
, 160);
1775 /* --- Parse options --- */
1778 static struct option opts
[] = {
1779 { "help", 0, 0, 'h' },
1780 { "version", 0, 0, 'v' },
1781 { "usage", 0, 0, 'u' },
1782 { "keyring", OPTF_ARGREQ
, 0, 'k' },
1785 int i
= mdwopt(argc
, argv
, "+hvu k:", opts
, 0, 0, 0);
1790 help(stdout
, argv
+ optind
);
1812 if (f
& f_bogus
|| argc
< 1) {
1817 /* --- Dispatch to the correct subcommand handler --- */
1819 return (findcmd(argv
[0])->func(argc
, argv
));
1824 /*----- That's all, folks -------------------------------------------------*/