3 * $Id: dsig.c,v 1.10 2004/04/08 01:36: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 /*----- 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 dsa_sigctx
*dsa_doinit(key
*k
, const gprime_param
*gp
,
270 mp
*y
, const gchash
*hc
)
272 dsa_sigctx
*ds
= CREATE(dsa_sigctx
);
276 if ((ds
->g
.g
= group_prime(gp
)) == 0)
277 die(EXIT_FAILURE
, "bad prime group in key `%s'", t
.buf
);
279 ds
->g
.p
= G_CREATE(ds
->g
.g
);
280 if (G_FROMINT(ds
->g
.g
, ds
->g
.p
, y
))
281 die(EXIT_FAILURE
, "bad public key in key `%s'", t
.buf
);
282 ds
->g
.r
= &rand_global
;
289 static dsa_sigctx
*ecdsa_doinit(key
*k
, const char *cstr
,
290 ec
*y
, const gchash
*hc
)
292 dsa_sigctx
*ds
= CREATE(dsa_sigctx
);
298 if ((e
= ec_getinfo(&ei
, cstr
)) != 0)
299 die(EXIT_FAILURE
, "bad curve in key `%s': %s", t
.buf
, e
);
300 ds
->g
.g
= group_ec(&ei
);
302 ds
->g
.p
= G_CREATE(ds
->g
.g
);
303 if (G_FROMEC(ds
->g
.g
, ds
->g
.p
, y
))
304 die(EXIT_FAILURE
, "bad public key in key `%s'", t
.buf
);
305 ds
->g
.r
= &rand_global
;
312 static sig
*dsa_siginit(key
*k
, void *kd
, const gchash
*hc
)
315 dsa_sigctx
*ds
= dsa_doinit(k
, &dp
->dp
, dp
->y
, hc
);
316 ds
->g
.u
= MP_COPY(dp
->x
);
320 static sig
*ecdsa_siginit(key
*k
, void *kd
, const gchash
*hc
)
323 dsa_sigctx
*ds
= ecdsa_doinit(k
, ep
->cstr
, &ep
->p
, hc
);
324 ds
->g
.u
= MP_COPY(ep
->x
);
328 static int dsa_sigdoit(sig
*s
, dstr
*d
)
330 dsa_sigctx
*ds
= (dsa_sigctx
*)s
;
331 gdsa_sig ss
= GDSA_SIG_INIT
;
332 size_t n
= mp_octets(ds
->g
.g
->r
);
334 gdsa_sign(&ds
->g
, &ss
, GH_DONE(ds
->s
.h
, 0), 0);
335 dstr_ensure(d
, 2 * n
);
336 mp_storeb(ss
.r
, d
->buf
, n
);
337 mp_storeb(ss
.s
, d
->buf
+ n
, n
);
339 mp_drop(ss
.r
); mp_drop(ss
.s
);
343 static void dsa_sigdestroy(sig
*s
)
345 dsa_sigctx
*ds
= (dsa_sigctx
*)s
;
346 G_DESTROY(ds
->g
.g
, ds
->g
.p
);
348 G_DESTROYGROUP(ds
->g
.g
);
351 static const sigops dsa_sig
= {
352 dh_privfetch
, sizeof(dh_priv
),
353 dsa_siginit
, dsa_sigdoit
, dsa_sigdestroy
356 static const sigops ecdsa_sig
= {
357 ec_privfetch
, sizeof(ec_priv
),
358 ecdsa_siginit
, dsa_sigdoit
, dsa_sigdestroy
361 static sig
*dsa_vrfinit(key
*k
, void *kd
, const gchash
*hc
)
364 dsa_sigctx
*ds
= dsa_doinit(k
, &dp
->dp
, dp
->y
, hc
);
368 static sig
*ecdsa_vrfinit(key
*k
, void *kd
, const gchash
*hc
)
371 dsa_sigctx
*ds
= ecdsa_doinit(k
, ep
->cstr
, &ep
->p
, hc
);
375 static int dsa_vrfdoit(sig
*s
, dstr
*d
)
377 dsa_sigctx
*ds
= (dsa_sigctx
*)s
;
382 ss
.r
= mp_loadb(MP_NEW
, d
->buf
, n
);
383 ss
.s
= mp_loadb(MP_NEW
, d
->buf
+ n
, d
->len
- n
);
384 rc
= gdsa_verify(&ds
->g
, &ss
, GH_DONE(ds
->s
.h
, 0));
385 mp_drop(ss
.r
); mp_drop(ss
.s
);
389 static const sigops dsa_vrf
= {
390 dh_pubfetch
, sizeof(dh_pub
),
391 dsa_vrfinit
, dsa_vrfdoit
, dsa_sigdestroy
394 static const sigops ecdsa_vrf
= {
395 ec_pubfetch
, sizeof(ec_pub
),
396 ecdsa_vrfinit
, dsa_vrfdoit
, dsa_sigdestroy
399 /* --- KCDSA and ECKCDSA --- */
401 static void kcdsa_privkey(dsa_sigctx
*ds
, mp
*x
)
402 { ds
->g
.u
= mp_modinv(MP_NEW
, x
, ds
->g
.g
->r
); }
404 static void kcdsa_sethash(dsa_sigctx
*ds
, const gchash
*hc
)
405 { ds
->s
.h
= gkcdsa_beginhash(&ds
->g
); }
407 static sig
*kcdsa_siginit(key
*k
, void *kd
, const gchash
*hc
)
410 dsa_sigctx
*ds
= dsa_doinit(k
, &dp
->dp
, dp
->y
, hc
);
411 kcdsa_privkey(ds
, dp
->x
);
412 kcdsa_sethash(ds
, hc
);
416 static sig
*eckcdsa_siginit(key
*k
, void *kd
, const gchash
*hc
)
419 dsa_sigctx
*ds
= ecdsa_doinit(k
, ep
->cstr
, &ep
->p
, hc
);
420 kcdsa_privkey(ds
, ep
->x
);
421 kcdsa_sethash(ds
, hc
);
425 static int kcdsa_sigdoit(sig
*s
, dstr
*d
)
427 dsa_sigctx
*ds
= (dsa_sigctx
*)s
;
428 gkcdsa_sig ss
= GKCDSA_SIG_INIT
;
429 size_t hsz
= ds
->g
.h
->hashsz
, n
= mp_octets(ds
->g
.g
->r
);
431 gkcdsa_sign(&ds
->g
, &ss
, GH_DONE(ds
->s
.h
, 0), 0);
432 dstr_ensure(d
, hsz
+ n
);
433 memcpy(d
->buf
, ss
.r
, hsz
);
434 mp_storeb(ss
.s
, d
->buf
+ hsz
, n
);
436 xfree(ss
.r
); mp_drop(ss
.s
);
440 static const sigops kcdsa_sig
= {
441 dh_privfetch
, sizeof(dh_priv
),
442 kcdsa_siginit
, kcdsa_sigdoit
, dsa_sigdestroy
445 static const sigops eckcdsa_sig
= {
446 ec_privfetch
, sizeof(ec_priv
),
447 eckcdsa_siginit
, kcdsa_sigdoit
, dsa_sigdestroy
450 static sig
*kcdsa_vrfinit(key
*k
, void *kd
, const gchash
*hc
)
453 dsa_sigctx
*ds
= dsa_doinit(k
, &dp
->dp
, dp
->y
, hc
);
454 kcdsa_sethash(ds
, hc
);
458 static sig
*eckcdsa_vrfinit(key
*k
, void *kd
, const gchash
*hc
)
461 dsa_sigctx
*ds
= ecdsa_doinit(k
, ep
->cstr
, &ep
->p
, hc
);
462 kcdsa_sethash(ds
, hc
);
466 static int kcdsa_vrfdoit(sig
*s
, dstr
*d
)
468 dsa_sigctx
*ds
= (dsa_sigctx
*)s
;
470 size_t hsz
= ds
->g
.h
->hashsz
, n
= d
->len
- hsz
;
475 ss
.r
= (octet
*)d
->buf
;
476 ss
.s
= mp_loadb(MP_NEW
, d
->buf
+ hsz
, n
);
477 rc
= gkcdsa_verify(&ds
->g
, &ss
, GH_DONE(ds
->s
.h
, 0));
482 static const sigops kcdsa_vrf
= {
483 dh_pubfetch
, sizeof(dh_pub
),
484 kcdsa_vrfinit
, kcdsa_vrfdoit
, dsa_sigdestroy
487 static const sigops eckcdsa_vrf
= {
488 ec_pubfetch
, sizeof(ec_pub
),
489 eckcdsa_vrfinit
, kcdsa_vrfdoit
, dsa_sigdestroy
492 /* --- The switch table --- */
494 static const struct sigtab
{
496 const sigops
*signops
;
497 const sigops
*verifyops
;
500 { "rsapkcs1", &rsap1_sig
, &rsap1_vrf
, &sha
},
501 { "rsapss", &rsapss_sig
, &rsapss_vrf
, &sha
},
502 { "dsa", &dsa_sig
, &dsa_vrf
, &sha
},
503 { "ecdsa", &ecdsa_sig
, &ecdsa_vrf
, &sha
},
504 { "kcdsa", &kcdsa_sig
, &kcdsa_vrf
, &has160
},
505 { "eckcdsa", &eckcdsa_sig
, &eckcdsa_vrf
, &has160
},
509 /* --- @getsig@ --- *
511 * Arguments: @key *k@ = the key to load
512 * @int wantpriv@ = nonzero if we want to sign
514 * Returns: A signature-making thing.
516 * Use: Loads a key and starts hashing.
519 static sig
*getsig(key
*k
, int wantpriv
)
521 const char *salg
, *halg
= 0;
527 const struct sigtab
*st
;
534 /* --- Setup stuff --- */
538 /* --- Get the signature algorithm --- *
540 * Take the attribute if it's there; otherwise use the key type.
543 if ((q
= key_getattr(0, k
, "sig-alg")) != 0) {
546 } else if (strncmp(k
->type
, "dsig-", 5) == 0 && k
->type
[5]) {
547 dstr_puts(&d
, k
->type
);
550 die(EXIT_FAILURE
, "no signature algorithm for key `%s'", t
.buf
);
552 /* --- Grab the hash algorithm --- *
554 * Grab it from the signature algorithm if it's there. But override that
555 * from the attribute.
559 if ((p
= strchr(p
, '-')) != 0) {
563 if ((q
= key_getattr(0, k
, "hash-alg")) != 0)
566 /* --- Look up the algorithms in the table --- */
568 for (st
= sigtab
; st
->name
; st
++) {
569 if (strcmp(st
->name
, salg
) == 0)
572 die(EXIT_FAILURE
, "signature algorithm `%s' not found in key `%s'",
578 if ((ch
= ghash_byname(halg
)) == 0) {
579 die(EXIT_FAILURE
, "hash algorithm `%s' not found in key `%s'",
583 so
= wantpriv ? st
->signops
: st
->verifyops
;
585 /* --- Load the key --- */
587 kd
= xmalloc(so
->kdsz
);
588 kp
= key_fetchinit(so
->kf
, 0, kd
);
589 if ((e
= key_fetch(kp
, k
)) != 0)
590 die(EXIT_FAILURE
, "error fetching key `%s': %s", t
.buf
, key_strerror(e
));
591 s
= so
->init(k
, kd
, ch
);
597 /* --- Free stuff up --- */
604 /* --- @freesig@ --- *
606 * Arguments: @sig *s@ = signature-making thing
610 * Use: Frees up a signature-making thing
613 static void freesig(sig
*s
)
616 key_fetchdone(s
->kp
);
620 /*----- Data formatting ---------------------------------------------------*/
622 /* --- Binary data structure --- *
624 * The binary format, which is used for hashing and for the optional binary
625 * output, consists of a sequence of tagged blocks. The tag describes the
626 * format and meaining of the following data.
630 /* --- Block tags --- */
632 T_IDENT
= 0, /* An identifying marker */
633 T_KEYID
, /* Key identifier */
634 T_BEGIN
, /* Begin hashing here */
635 T_COMMENT
= T_BEGIN
, /* A textual comment */
636 T_DATE
, /* Creation date of signature */
637 T_EXPIRE
, /* Expiry date of signature */
638 T_FILE
, /* File and corresponding hash */
639 T_SIGNATURE
, /* Final signature block */
641 /* --- Error messages --- */
649 /* --- Name translation table --- */
651 static const char *tagtab
[] = {
653 "comment:", "date:", "expires:", "file:",
658 static const char *errtab
[] = {
660 "Unexpected end-of-file",
661 "Binary object too large",
666 /* --- Memory representation of block types --- */
668 typedef struct block
{
669 int tag
; /* Type tag */
670 dstr d
; /* String data */
671 dstr b
; /* Binary data */
672 time_t t
; /* Timestamp */
673 uint32 k
; /* Keyid */
676 /* --- @getstring@ --- *
678 * Arguments: @FILE *fp@ = stream from which to read
679 * @dstr *d@ = destination string
680 * @unsigned raw@ = raw or cooked read
682 * Returns: Zero if OK, nonzero on end-of-file.
684 * Use: Reads a filename (or something similar) from a stream.
687 static int getstring(FILE *fp
, dstr
*d
, unsigned raw
)
692 /* --- Raw: just read exactly what's written up to a null byte --- */
695 if ((ch
= getc(fp
)) == EOF
)
701 if ((ch
= getc(fp
)) == EOF
)
708 /* --- Skip as far as whitespace --- *
710 * Also skip past comments.
718 do ch
= getc(fp
); while (ch
!= '\n' && ch
!= EOF
);
724 /* --- If the character is a quote then read a quoted string --- */
736 /* --- Now read all sorts of interesting things --- */
740 /* --- Handle an escaped thing --- */
747 case 'a': ch
= '\a'; break;
748 case 'b': ch
= '\b'; break;
749 case 'f': ch
= '\f'; break;
750 case 'n': ch
= '\n'; break;
751 case 'r': ch
= '\r'; break;
752 case 't': ch
= '\t'; break;
753 case 'v': ch
= '\v'; break;
760 /* --- If it's a quote or some other end marker then stop --- */
762 if (ch
== q
|| (!q
&& isspace((unsigned char)ch
)))
765 /* --- Otherwise contribute and continue --- */
768 if ((ch
= getc(fp
)) == EOF
)
778 /* --- @putstring@ --- *
780 * Arguments: @FILE *fp@ = stream to write on
781 * @const char *p@ = pointer to text
782 * @unsigned raw@ = whether the string is to be written raw
786 * Use: Emits a string to a stream.
789 static void putstring(FILE *fp
, const char *p
, unsigned raw
)
791 size_t sz
= strlen(p
);
795 /* --- Just write the string null terminated if raw --- */
798 fwrite(p
, 1, sz
+ 1, fp
);
802 /* --- Check for any dodgy characters --- */
805 for (q
= p
; *q
; q
++) {
806 if (isspace((unsigned char)*q
)) {
815 /* --- Emit the string --- */
817 for (q
= p
; *q
; q
++) {
819 case '\a': fputc('\\', fp
); fputc('a', fp
); break;
820 case '\b': fputc('\\', fp
); fputc('b', fp
); break;
821 case '\f': fputc('\\', fp
); fputc('f', fp
); break;
822 case '\n': fputc('\\', fp
); fputc('n', fp
); break;
823 case '\r': fputc('\\', fp
); fputc('r', fp
); break;
824 case '\t': fputc('\\', fp
); fputc('t', fp
); break;
825 case '\v': fputc('\\', fp
); fputc('v', fp
); break;
826 case '`': fputc('\\', fp
); fputc('`', fp
); break;
827 case '\'': fputc('\\', fp
); fputc('\'', fp
); break;
828 case '\"': fputc('\\', fp
); fputc('\"', fp
); break;
841 /* --- @timestring@ --- *
843 * Arguments: @time_t t@ = a timestamp
844 * @dstr *d@ = a string to write on
848 * Use: Writes a textual representation of the timestamp to the
852 static void timestring(time_t t
, dstr
*d
)
854 if (t
== KEXP_FOREVER
)
857 struct tm
*tm
= localtime(&t
);
859 d
->len
+= strftime(d
->buf
+ d
->len
, 32, "%Y-%m-%d %H:%M:%S %Z", tm
);
864 /* --- @breset@ --- *
866 * Arguments: @block *b@ = block to reset
870 * Use: Resets a block so that more stuff can be put in it.
873 static void breset(block
*b
)
884 * Arguments: @block *b@ = block to initialize
888 * Use: Initializes a block as something to read into.
891 static void binit(block
*b
)
898 /* --- @bdestroy@ --- *
900 * Arguments: @block *b@ = block to destroy
904 * Use: Destroys a block's contents.
907 static void bdestroy(block
*b
)
915 * Arguments: @block *b@ = pointer to block
916 * @FILE *fp@ = stream to read from
917 * @unsigned bin@ = binary switch
919 * Returns: Tag of block, or an error tag.
921 * Use: Reads a block from a stream.
924 static int bget(block
*b
, FILE *fp
, unsigned bin
)
928 /* --- Read the tag --- */
934 if (getstring(fp
, &d
, 0))
936 for (tag
= 0; tagtab
[tag
]; tag
++) {
937 if (strcmp(tagtab
[tag
], d
.buf
) == 0)
944 /* --- Decide what to do next --- */
950 /* --- Reading of strings --- */
954 if (getstring(fp
, &b
->d
, bin
))
958 /* --- Timestamps --- */
964 if (fread(buf
, sizeof(buf
), 1, fp
) < 1)
966 b
->t
= ((time_t)(((LOAD32(buf
+ 0) << 16) << 16) & ~MASK32
) |
967 (time_t)LOAD32(buf
+ 4));
969 if (getstring(fp
, &b
->d
, 0))
971 if (strcmp(b
->d
.buf
, "forever") == 0)
973 else if ((b
->t
= get_date(b
->d
.buf
, 0)) == -1)
978 /* --- Key ids --- */
983 if (fread(buf
, sizeof(buf
), 1, fp
) < 1)
987 if (getstring(fp
, &b
->d
, 0))
989 b
->k
= strtoul(b
->d
.buf
, 0, 16);
993 /* --- Reading of binary data --- */
1000 if (fread(buf
, sizeof(buf
), 1, fp
) < 1)
1006 if (fread(b
->b
.buf
+ b
->b
.len
, 1, sz
, fp
) < sz
)
1011 if (getstring(fp
, &b
->d
, 0))
1014 base64_decode(&b64
, b
->d
.buf
, b
->d
.len
, &b
->b
);
1015 base64_decode(&b64
, 0, 0, &b
->b
);
1018 if (tag
== T_FILE
&& getstring(fp
, &b
->d
, bin
))
1022 /* --- Anything else --- */
1033 * Arguments: @block *b@ = pointer to block to emit
1034 * @dstr *d@ = output buffer
1038 * Use: Encodes a block in a binary format.
1041 static void blob(block
*b
, dstr
*d
)
1053 STORE32(d
->buf
+ d
->len
, ((b
->t
& ~MASK32
) >> 16) >> 16);
1054 STORE32(d
->buf
+ d
->len
+ 4, b
->t
);
1059 STORE32(d
->buf
+ d
->len
, b
->k
);
1065 STORE16(d
->buf
+ d
->len
, b
->b
.len
);
1068 if (b
->tag
== T_FILE
) {
1076 /* --- @bwrite@ --- *
1078 * Arguments: @block *b@ = pointer to block to write
1079 * @FILE *fp@ = stream to write on
1083 * Use: Writes a block on a stream in a textual format.
1086 static void bwrite(block
*b
, FILE *fp
)
1088 fputs(tagtab
[b
->tag
], fp
);
1093 putstring(fp
, b
->d
.buf
, 0);
1098 timestring(b
->t
, &d
);
1099 putstring(fp
, d
.buf
, 0);
1103 fprintf(fp
, "%08lx", (unsigned long)b
->k
);
1111 base64_encode(&b64
, b
->b
.buf
, b
->b
.len
, &d
);
1112 base64_encode(&b64
, 0, 0, &d
);
1114 if (b
->tag
== T_FILE
) {
1116 putstring(fp
, b
->d
.buf
, 0);
1123 /* --- @bemit@ --- *
1125 * Arguments: @block *b@ = pointer to block to write
1126 * @FILE *fp@ = file to write on
1127 * @ghash *h@ = pointer to hash function
1128 * @unsigned bin@ = binary/text flag
1132 * Use: Spits out a block properly.
1135 static void bemit(block
*b
, FILE *fp
, ghash
*h
, unsigned bin
)
1137 if (h
|| (fp
&& bin
)) {
1141 GH_HASH(h
, d
.buf
, d
.len
);
1143 fwrite(d
.buf
, d
.len
, 1, fp
);
1149 /*----- Static variables --------------------------------------------------*/
1151 static const char *keyring
= "keyring";
1153 /*----- Other shared functions --------------------------------------------*/
1155 /* --- @keyreport@ --- *
1157 * Arguments: @const char *file@ = filename containing the error
1158 * @int line@ = line number in file
1159 * @const char *err@ = error text message
1160 * @void *p@ = unimportant pointer
1164 * Use: Reports errors during the opening of a key file.
1167 static void keyreport(const char *file
, int line
, const char *err
, void *p
)
1169 moan("error in keyring `%s' at line `%s': %s", file
, line
, err
);
1172 /* --- @fhash@ --- *
1174 * Arguments: @const gchash *c@ = pointer to hash class
1175 * @const char *file@ = file to hash
1176 * @void *b@ = pointer to output buffer
1178 * Returns: Zero if it worked, or nonzero for a system error.
1180 * Use: Hashes a file.
1183 static int fhash(const gchash
*c
, const char *file
, void *b
)
1185 FILE *fp
= fopen(file
, "rb");
1186 ghash
*h
= GH_INIT(c
);
1193 while ((sz
= fread(buf
, 1, sizeof(buf
), fp
)) > 0)
1194 GH_HASH(h
, buf
, sz
);
1205 * Arguments: @FILE *fp@ = file to write on
1206 * @const void *p@ = pointer to data to be written
1207 * @size_t sz@ = size of the data to write
1211 * Use: Emits a hex dump to a stream.
1214 static void fhex(FILE *fp
, const void *p
, size_t sz
)
1220 fprintf(fp
, "%02x", *q
++);
1224 /* putc(' ', fp); */
1228 /*----- Signature generation ----------------------------------------------*/
1230 static int sign(int argc
, char *argv
[])
1237 const char *ki
= "dsig";
1241 time_t exp
= KEXP_EXPIRE
;
1243 const char *ifile
= 0;
1244 const char *ofile
= 0;
1252 static struct option opts
[] = {
1253 { "null", 0, 0, '0' },
1254 { "binary", 0, 0, 'b' },
1255 { "verbose", 0, 0, 'v' },
1256 { "quiet", 0, 0, 'q' },
1257 { "comment", OPTF_ARGREQ
, 0, 'c' },
1258 { "file", OPTF_ARGREQ
, 0, 'f' },
1259 { "output", OPTF_ARGREQ
, 0, 'o' },
1260 { "key", OPTF_ARGREQ
, 0, 'k' },
1261 { "expire", OPTF_ARGREQ
, 0, 'e' },
1264 int i
= mdwopt(argc
, argv
, "+0vqb c: f:o: k:e:", opts
, 0, 0, 0);
1294 if (strcmp(optarg
, "forever") == 0)
1296 else if ((exp
= get_date(optarg
, 0)) == -1)
1297 die(EXIT_FAILURE
, "bad expiry time");
1304 if (optind
!= argc
|| (f
& f_bogus
))
1305 die(EXIT_FAILURE
, "Usage: sign [-options]");
1307 /* --- Locate the signing key --- */
1309 if (key_open(&kf
, keyring
, KOPEN_WRITE
, keyreport
, 0))
1310 die(EXIT_FAILURE
, "couldn't open keyring `%s'", keyring
);
1311 if ((k
= key_bytag(&kf
, ki
)) == 0)
1312 die(EXIT_FAILURE
, "couldn't find key `%s'", ki
);
1314 if (exp
== KEXP_FOREVER
&& k
->exp
!= KEXP_FOREVER
) {
1315 die(EXIT_FAILURE
, "key `%s' expires: can't create nonexpiring signature",
1320 /* --- Open files --- */
1324 else if ((ifp
= fopen(ifile
, (f
& f_raw
) ?
"rb" : "r")) == 0) {
1325 die(EXIT_FAILURE
, "couldn't open input file `%s': %s",
1326 ifile
, strerror(errno
));
1331 else if ((ofp
= fopen(ofile
, (f
& f_bin
) ?
"wb" : "w")) == 0) {
1332 die(EXIT_FAILURE
, "couldn't open output file `%s': %s",
1333 ofile
, strerror(errno
));
1336 /* --- Emit the start of the output --- */
1338 binit(&b
); b
.tag
= T_IDENT
;
1339 dstr_putf(&b
.d
, "%s, Catacomb version " VERSION
, QUIS
);
1340 bemit(&b
, ofp
, 0, f
& f_bin
);
1342 breset(&b
); b
.tag
= T_KEYID
; b
.k
= k
->id
;
1343 bemit(&b
, ofp
, 0, f
& f_bin
);
1345 /* --- Start hashing, and emit the datestamps and things --- */
1348 time_t now
= time(0);
1350 breset(&b
); b
.tag
= T_DATE
; b
.t
= now
; bemit(&b
, ofp
, s
->h
, f
& f_bin
);
1351 if (exp
== KEXP_EXPIRE
)
1352 exp
= now
+ 86400 * 28;
1353 breset(&b
); b
.tag
= T_EXPIRE
; b
.t
= exp
; bemit(&b
, ofp
, s
->h
, f
& f_bin
);
1355 breset(&b
); b
.tag
= T_COMMENT
; DPUTS(&b
.d
, c
);
1356 bemit(&b
, ofp
, s
->h
, f
& f_bin
);
1363 /* --- Now hash the various files --- */
1367 /* --- Stop on an output error --- */
1374 /* --- Read the next filename to hash --- */
1377 if (getstring(ifp
, &b
.d
, f
& f_raw
))
1380 DENSURE(&b
.b
, GH_CLASS(s
->h
)->hashsz
);
1381 if (fhash(GH_CLASS(s
->h
), b
.d
.buf
, b
.b
.buf
)) {
1382 moan("Error reading `%s': %s", b
.d
.buf
, strerror(errno
));
1385 b
.b
.len
+= GH_CLASS(s
->h
)->hashsz
;
1387 fhex(stderr
, b
.b
.buf
, b
.b
.len
);
1388 fprintf(stderr
, " %s\n", b
.d
.buf
);
1390 bemit(&b
, ofp
, s
->h
, f
& f_bin
);
1394 /* --- Create the signature --- */
1396 if (!(f
& f_bogus
)) {
1398 b
.tag
= T_SIGNATURE
;
1399 if ((e
= s
->ops
->doit(s
, &b
.b
)) != 0) {
1400 moan("error creating signature: %s", key_strerror(e
));
1403 if (!(f
& f_bogus
)) {
1404 bemit(&b
, ofp
, 0, f
& f_bin
);
1405 key_used(&kf
, k
, exp
);
1409 /* --- Tidy up at the end --- */
1422 if ((e
= key_close(&kf
)) != 0) {
1425 die(EXIT_FAILURE
, "couldn't write file `%s': %s",
1426 keyring
, strerror(errno
));
1428 die(EXIT_FAILURE
, "keyring file `%s' broken: %s (repair manually)",
1429 keyring
, strerror(errno
));
1433 die(EXIT_FAILURE
, "error(s) occurred while creating signature");
1434 return (EXIT_SUCCESS
);
1441 /*----- Signature verification --------------------------------------------*/
1443 static int verify(int argc
, char *argv
[])
1459 /* --- Parse the options --- */
1462 static struct option opts
[] = {
1463 { "verbose", 0, 0, 'v' },
1464 { "quiet", 0, 0, 'q' },
1467 int i
= mdwopt(argc
, argv
, "+vq", opts
, 0, 0, 0);
1485 if ((f
& f_bogus
) || argc
> 1)
1486 die(EXIT_FAILURE
, "Usage: verify [-qv] [file]");
1488 /* --- Open the key file, and start reading the input file --- */
1490 if (key_open(&kf
, keyring
, KOPEN_READ
, keyreport
, 0))
1491 die(EXIT_FAILURE
, "couldn't open keyring `%s'\n", keyring
);
1495 if ((fp
= fopen(argv
[0], "rb")) == 0) {
1496 die(EXIT_FAILURE
, "couldn't open file `%s': %s\n",
1497 argv
[0], strerror(errno
));
1499 if (getc(fp
) == 0) {
1504 if ((fp
= fopen(argv
[0], "r")) == 0) {
1505 die(EXIT_FAILURE
, "couldn't open file `%s': %s\n",
1506 argv
[0], strerror(errno
));
1511 /* --- Read the introductory matter --- */
1516 e
= bget(&b
, fp
, f
& f_bin
);
1518 die(EXIT_FAILURE
, "error reading packet: %s", errtab
[-e
]);
1524 printf("INFO ident: `%s'\n", b
.d
.buf
);
1527 if ((k
= key_byid(&kf
, b
.k
)) == 0) {
1529 printf("FAIL key %08lx not found\n", (unsigned long)b
.k
);
1534 key_fulltag(k
, &b
.d
);
1535 printf("INFO key: %s\n", b
.d
.buf
);
1539 die(EXIT_FAILURE
, "(internal) unknown packet type\n");
1544 /* --- Initialize the hash function and start reading hashed packets --- */
1550 puts("FAIL no keyid packet found");
1558 printf("INFO comment: `%s'\n", b
.d
.buf
);
1559 bemit(&b
, 0, s
->h
, 0);
1564 timestring(b
.t
, &b
.d
);
1565 printf("INFO date: %s\n", b
.d
.buf
);
1567 bemit(&b
, 0, s
->h
, 0);
1570 time_t now
= time(0);
1573 puts("BAD signature has expired");
1578 timestring(b
.t
, &b
.d
);
1579 printf("INFO expires: %s\n", b
.d
.buf
);
1581 bemit(&b
, 0, s
->h
, 0);
1585 DENSURE(&d
, GH_CLASS(s
->h
)->hashsz
);
1586 if (fhash(GH_CLASS(s
->h
), b
.d
.buf
, d
.buf
)) {
1588 printf("BAD error reading file `%s': %s\n",
1589 b
.d
.buf
, strerror(errno
));
1592 } else if (b
.b
.len
!= GH_CLASS(s
->h
)->hashsz
||
1593 memcmp(d
.buf
, b
.b
.buf
, b
.b
.len
) != 0) {
1595 printf("BAD file `%s' has incorrect hash\n", b
.d
.buf
);
1597 } else if (verb
> 3) {
1598 fputs("INFO hash: ", stdout
);
1599 fhex(stdout
, b
.b
.buf
, b
.b
.len
);
1600 printf(" %s\n", b
.d
.buf
);
1602 bemit(&b
, 0, s
->h
, 0);
1605 if (s
->ops
->doit(s
, &b
.b
)) {
1607 puts("BAD bad signature");
1609 } else if (verb
> 2)
1610 puts("INFO good signature");
1614 printf("FAIL invalid packet type %i\n", e
);
1619 e
= bget(&b
, fp
, f
& f_bin
);
1622 printf("FAIL error reading packet: %s\n", errtab
[-e
]);
1635 puts("FAIL signature invalid");
1637 puts("OK signature verified");
1639 return (f
& f_bogus ? EXIT_FAILURE
: EXIT_SUCCESS
);
1646 /*----- Main code ---------------------------------------------------------*/
1648 typedef struct cmd
{
1650 int (*func
)(int /*argc*/, char */
*argv*/
[]);
1655 static cmd cmdtab
[] = {
1656 /* { "manifest", manifest, */
1657 /* "manifest [-0] [-o output]" }, */
1660 [-0bqv] [-c comment] [-k tag] [-i keyid]\n\
1661 [-e expire] [-f file] [-o output]", "\
1664 -0, --null Read null-terminated filenames from stdin.\n\
1665 -b, --binary Produce a binary output file.\n\
1666 -q, --quiet Produce fewer messages while working.\n\
1667 -v, --verbose Produce more messages while working.\n\
1668 -c, --comment=COMMENT Include COMMENT in the output file.\n\
1669 -f, --file=FILE Read filenames to hash from FILE.\n\
1670 -o, --output=FILE Write the signed result to FILE.\n\
1671 -k, --key=TAG Use a key named by TAG.\n\
1672 -e, --expire=TIME The signature should expire after TIME.\n\
1675 "verify [-qv] [file]", "\
1678 -q, --quiet Produce fewer messages while working.\n\
1679 -v, --verbose Produce more messages while working.\n\
1684 /* --- @findcmd@ --- *
1686 * Arguments: @const char *name@ = a command name
1688 * Returns: Pointer to the command structure.
1690 * Use: Looks up a command by name. If the command isn't found, an
1691 * error is reported and the program is terminated.
1694 static cmd
*findcmd(const char *name
)
1696 cmd
*c
, *chosen
= 0;
1697 size_t sz
= strlen(name
);
1699 for (c
= cmdtab
; c
->name
; c
++) {
1700 if (strncmp(name
, c
->name
, sz
) == 0) {
1701 if (c
->name
[sz
] == 0) {
1705 die(EXIT_FAILURE
, "ambiguous command name `%s'", name
);
1711 die(EXIT_FAILURE
, "unknown command name `%s'", name
);
1715 static void version(FILE *fp
)
1717 pquis(fp
, "$, Catacomb version " VERSION
"\n");
1720 static void usage(FILE *fp
)
1722 pquis(fp
, "Usage: $ [-k keyring] command [args]\n");
1725 static void help(FILE *fp
, char **argv
)
1731 fprintf(fp
, "Usage: %s [-k keyring] %s\n", QUIS
, c
->usage
);
1741 Create and verify signatures on lists of files.\n\
1743 for (c
= cmdtab
; c
->name
; c
++)
1744 fprintf(fp
, "%s\n", c
->usage
);
1750 * Arguments: @int argc@ = number of command line arguments
1751 * @char *argv[]@ = vector of command line arguments
1753 * Returns: Zero if successful, nonzero otherwise.
1755 * Use: Signs or verifies signatures on lists of files. Useful for
1756 * ensuring that a distribution is unmolested.
1759 int main(int argc
, char *argv
[])
1765 /* --- Initialize the library --- */
1769 rand_noisesrc(RAND_GLOBAL
, &noise_source
);
1770 rand_seed(RAND_GLOBAL
, 160);
1772 /* --- Parse options --- */
1775 static struct option opts
[] = {
1776 { "help", 0, 0, 'h' },
1777 { "version", 0, 0, 'v' },
1778 { "usage", 0, 0, 'u' },
1779 { "keyring", OPTF_ARGREQ
, 0, 'k' },
1782 int i
= mdwopt(argc
, argv
, "+hvu k:", opts
, 0, 0, 0);
1787 help(stdout
, argv
+ optind
);
1809 if (f
& f_bogus
|| argc
< 1) {
1814 /* --- Dispatch to the correct subcommand handler --- */
1816 return (findcmd(argv
[0])->func(argc
, argv
));
1821 /*----- That's all, folks -------------------------------------------------*/