3 * $Id: dsig.c,v 1.6 2000/12/06 20:33:27 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.6 2000/12/06 20:33:27 mdw
34 * Make flags be macros rather than enumerations, to ensure that they're
37 * Revision 1.5 2000/10/08 12:12:09 mdw
38 * Shut up some warnings.
40 * Revision 1.4 2000/08/04 23:23:44 mdw
41 * Various <ctype.h> fixes.
43 * Revision 1.3 2000/07/15 20:53:23 mdw
44 * More hash functions. Bug fix in getstring.
46 * Revision 1.2 2000/07/01 11:27:22 mdw
47 * Use new PKCS#1 padding functions rather than rolling by hand.
49 * Revision 1.1 2000/06/17 10:54:29 mdw
50 * Program to generate and verify signatures on multiple files.
54 /*----- Header files ------------------------------------------------------*/
64 #include <mLib/alloc.h>
65 #include <mLib/base64.h>
66 #include <mLib/mdwopt.h>
67 #include <mLib/quis.h>
68 #include <mLib/report.h>
91 /*----- Digital signature algorithm ---------------------------------------*/
93 static int dsasign(key
*k
, const void *m
, size_t msz
, dstr
*d
)
96 key_packstruct ks
[DSA_PRIVFETCHSZ
];
102 kp
= key_fetchinit(dsa_privfetch
, ks
, &dp
);
103 if ((e
= key_fetch(kp
, k
)) != 0) {
107 sz
= mp_octets(dp
.dp
.q
);
109 die(EXIT_FAILURE
, "hash function too wide for this signing key");
111 p
= (octet
*)d
->buf
+ d
->len
;
112 rand_get(RAND_GLOBAL
, p
, sz
);
113 dsa_sign(&dp
.dp
, dp
.x
, m
, msz
, p
, sz
, p
, sz
, p
+ sz
, sz
);
119 static int dsaverify(key
*k
, const void *m
, size_t msz
,
120 const void *s
, size_t ssz
)
123 key_packstruct ks
[DSA_PUBFETCHSZ
];
129 kp
= key_fetchinit(dsa_pubfetch
, ks
, &dp
);
130 if ((e
= key_fetch(kp
, k
)) != 0) {
135 e
= dsa_verify(&dp
.dp
, dp
.y
, m
, msz
, p
, sz
, p
+ sz
, sz
);
140 /*----- RSA signing -------------------------------------------------------*/
142 static int rsasign(key
*k
, const void *m
, size_t msz
, dstr
*d
)
146 pkcs1 pk
= { 0, 0, 0 };
147 key_packstruct ks
[RSA_PRIVFETCHSZ
];
151 kp
= key_fetchinit(rsa_privfetch
, ks
, &rp
);
152 if ((e
= key_fetch(kp
, k
)) != 0) {
156 rsa_privcreate(&rpc
, &rp
, &rand_global
);
157 if (rsa_sign(&rpc
, m
, msz
, d
, pkcs1_sigencode
, &pk
) < 0)
158 die(EXIT_FAILURE
, "internal error in rsasign (key too small?)");
159 rsa_privdestroy(&rpc
);
164 static int rsaverify(key
*k
, const void *m
, size_t msz
,
165 const void *s
, size_t ssz
)
169 pkcs1 pk
= { 0, 0, 0 };
170 key_packstruct ks
[RSA_PUBFETCHSZ
];
176 kp
= key_fetchinit(rsa_pubfetch
, ks
, &rp
);
177 if ((e
= key_fetch(kp
, k
)) != 0) {
181 rsa_pubcreate(&rpc
, &rp
);
182 if (rsa_verify(&rpc
, s
, ssz
, &d
, pkcs1_sigdecode
, &pk
) > 0 &&
183 msz
== d
.len
&& memcmp(d
.buf
, m
, msz
) == 0)
186 rsa_pubdestroy(&rpc
);
191 /*----- Algorithm choice --------------------------------------------------*/
196 int (*sign
)(key */
*k*/
, const void */
*m*/
, size_t /*msz*/, dstr */
*d*/
);
197 int (*verify
)(key */
*k*/
, const void */
*m*/
, size_t /*msz*/,
198 const void */
*s*/
, size_t /*ssz*/);
201 static const gchash
*hashtab
[] = {
202 &rmd160
, &tiger
, &sha
, &rmd128
, &rmd256
, &rmd320
, &md5
, &md4
, 0 };
203 static sig sigtab
[] = {
204 { "dsa", "dsig-dsa", dsasign
, dsaverify
},
205 { "rsa", "dsig-rsa", rsasign
, rsaverify
},
209 /* --- @gethash@ --- *
211 * Arguments: @const char *name@ = pointer to name string
213 * Returns: Pointer to appropriate hash class.
215 * Use: Chooses a hash function by name.
218 static const gchash
*gethash(const char *name
)
220 const gchash
**g
, *gg
= 0;
221 size_t sz
= strlen(name
);
222 for (g
= hashtab
; *g
; g
++) {
223 if (strncmp(name
, (*g
)->name
, sz
) == 0) {
224 if ((*g
)->name
[sz
] == 0) {
236 /* --- @getsig@ --- *
238 * Arguments: @const char *name@ = pointer to name string
240 * Returns: Pointer to appropriate signature type.
242 * Use: Chooses a signature algorithm by name.
245 static sig
*getsig(const char *name
)
248 size_t sz
= strlen(name
);
249 for (s
= sigtab
; s
->name
; s
++) {
250 if (strncmp(name
, s
->name
, sz
) == 0) {
251 if (s
->name
[sz
] == 0) {
263 /*----- Data formatting ---------------------------------------------------*/
265 /* --- Binary data structure --- *
267 * The binary format, which is used for hashing and for the optional binary
268 * output, consists of a sequence of tagged blocks. The tag describes the
269 * format and meaining of the following data.
273 /* --- Block tags --- */
275 T_IDENT
= 0, /* An identifying marker */
276 T_SIGALG
, /* Signature algorithm used */
277 T_HASHALG
, /* Hash algorithm used */
278 T_KEYID
, /* Key identifier */
279 T_BEGIN
, /* Begin hashing here */
280 T_COMMENT
= T_BEGIN
, /* A textual comment */
281 T_DATE
, /* Creation date of signature */
282 T_EXPIRE
, /* Expiry date of signature */
283 T_FILE
, /* File and corresponding hash */
284 T_SIGNATURE
, /* Final signature block */
286 /* --- Error messages --- */
294 /* --- Name translation table --- */
296 static const char *tagtab
[] = {
297 "ident:", "sigalg:", "hashalg:", "keyid:",
298 "comment:", "date:", "expires:", "file:",
303 static const char *errtab
[] = {
305 "Unexpected end-of-file",
306 "Binary object too large",
311 /* --- Memory representation of block types --- */
313 typedef struct block
{
314 int tag
; /* Type tag */
315 dstr d
; /* String data */
316 dstr b
; /* Binary data */
317 time_t t
; /* Timestamp */
318 uint32 k
; /* Keyid */
321 /* --- @getstring@ --- *
323 * Arguments: @FILE *fp@ = stream from which to read
324 * @dstr *d@ = destination string
325 * @unsigned raw@ = raw or cooked read
327 * Returns: Zero if OK, nonzero on end-of-file.
329 * Use: Reads a filename (or something similar) from a stream.
332 static int getstring(FILE *fp
, dstr
*d
, unsigned raw
)
337 /* --- Raw: just read exactly what's written up to a null byte --- */
340 if ((ch
= getc(fp
)) == EOF
)
346 if ((ch
= getc(fp
)) == EOF
)
353 /* --- Skip as far as whitespace --- *
355 * Also skip past comments.
363 do ch
= getc(fp
); while (ch
!= '\n' && ch
!= EOF
);
369 /* --- If the character is a quote then read a quoted string --- */
381 /* --- Now read all sorts of interesting things --- */
385 /* --- Handle an escaped thing --- */
392 case 'a': ch
= '\a'; break;
393 case 'b': ch
= '\b'; break;
394 case 'f': ch
= '\f'; break;
395 case 'n': ch
= '\n'; break;
396 case 'r': ch
= '\r'; break;
397 case 't': ch
= '\t'; break;
398 case 'v': ch
= '\v'; break;
405 /* --- If it's a quote or some other end marker then stop --- */
407 if (ch
== q
|| (!q
&& isspace((unsigned char)ch
)))
410 /* --- Otherwise contribute and continue --- */
413 if ((ch
= getc(fp
)) == EOF
)
423 /* --- @putstring@ --- *
425 * Arguments: @FILE *fp@ = stream to write on
426 * @const char *p@ = pointer to text
427 * @unsigned raw@ = whether the string is to be written raw
431 * Use: Emits a string to a stream.
434 static void putstring(FILE *fp
, const char *p
, unsigned raw
)
436 size_t sz
= strlen(p
);
440 /* --- Just write the string null terminated if raw --- */
443 fwrite(p
, 1, sz
+ 1, fp
);
447 /* --- Check for any dodgy characters --- */
450 for (q
= p
; *q
; q
++) {
451 if (isspace((unsigned char)*q
)) {
460 /* --- Emit the string --- */
462 for (q
= p
; *q
; q
++) {
464 case '\a': fputc('\\', fp
); fputc('a', fp
); break;
465 case '\b': fputc('\\', fp
); fputc('b', fp
); break;
466 case '\f': fputc('\\', fp
); fputc('f', fp
); break;
467 case '\n': fputc('\\', fp
); fputc('n', fp
); break;
468 case '\r': fputc('\\', fp
); fputc('r', fp
); break;
469 case '\t': fputc('\\', fp
); fputc('t', fp
); break;
470 case '\v': fputc('\\', fp
); fputc('v', fp
); break;
471 case '`': fputc('\\', fp
); fputc('`', fp
); break;
472 case '\'': fputc('\\', fp
); fputc('\'', fp
); break;
473 case '\"': fputc('\\', fp
); fputc('\"', fp
); break;
486 /* --- @timestring@ --- *
488 * Arguments: @time_t t@ = a timestamp
489 * @dstr *d@ = a string to write on
493 * Use: Writes a textual representation of the timestamp to the
497 static void timestring(time_t t
, dstr
*d
)
499 if (t
== KEXP_FOREVER
)
502 struct tm
*tm
= localtime(&t
);
504 d
->len
+= strftime(d
->buf
+ d
->len
, 32, "%Y-%m-%d %H:%M:%S %Z", tm
);
509 /* --- @breset@ --- *
511 * Arguments: @block *b@ = block to reset
515 * Use: Resets a block so that more stuff can be put in it.
518 static void breset(block
*b
)
529 * Arguments: @block *b@ = block to initialize
533 * Use: Initializes a block as something to read into.
536 static void binit(block
*b
)
543 /* --- @bdestroy@ --- *
545 * Arguments: @block *b@ = block to destroy
549 * Use: Destroys a block's contents.
552 static void bdestroy(block
*b
)
560 * Arguments: @block *b@ = pointer to block
561 * @FILE *fp@ = stream to read from
562 * @unsigned bin@ = binary switch
564 * Returns: Tag of block, or an error tag.
566 * Use: Reads a block from a stream.
569 static int bget(block
*b
, FILE *fp
, unsigned bin
)
573 /* --- Read the tag --- */
579 if (getstring(fp
, &d
, 0))
581 for (tag
= 0; tagtab
[tag
]; tag
++) {
582 if (strcmp(tagtab
[tag
], d
.buf
) == 0)
589 /* --- Decide what to do next --- */
595 /* --- Reading of strings --- */
601 if (getstring(fp
, &b
->d
, bin
))
605 /* --- Timestamps --- */
611 if (fread(buf
, sizeof(buf
), 1, fp
) < 1)
613 b
->t
= ((time_t)(((LOAD32(buf
+ 0) << 16) << 16) & ~MASK32
) |
614 (time_t)LOAD32(buf
+ 4));
616 if (getstring(fp
, &b
->d
, 0))
618 if (strcmp(b
->d
.buf
, "forever") == 0)
620 else if ((b
->t
= get_date(b
->d
.buf
, 0)) == -1)
625 /* --- Key ids --- */
630 if (fread(buf
, sizeof(buf
), 1, fp
) < 1)
634 if (getstring(fp
, &b
->d
, 0))
636 b
->k
= strtoul(b
->d
.buf
, 0, 16);
640 /* --- Reading of binary data --- */
647 if (fread(buf
, sizeof(buf
), 1, fp
) < 1)
653 if (fread(b
->b
.buf
+ b
->b
.len
, 1, sz
, fp
) < sz
)
658 if (getstring(fp
, &b
->d
, 0))
661 base64_decode(&b64
, b
->d
.buf
, b
->d
.len
, &b
->b
);
662 base64_decode(&b64
, 0, 0, &b
->b
);
665 if (tag
== T_FILE
&& getstring(fp
, &b
->d
, bin
))
669 /* --- Anything else --- */
680 * Arguments: @block *b@ = pointer to block to emit
681 * @dstr *d@ = output buffer
685 * Use: Encodes a block in a binary format.
688 static void blob(block
*b
, dstr
*d
)
702 STORE32(d
->buf
+ d
->len
, ((b
->t
& ~MASK32
) >> 16) >> 16);
703 STORE32(d
->buf
+ d
->len
+ 4, b
->t
);
708 STORE32(d
->buf
+ d
->len
, b
->k
);
714 STORE16(d
->buf
+ d
->len
, b
->b
.len
);
717 if (b
->tag
== T_FILE
) {
725 /* --- @bwrite@ --- *
727 * Arguments: @block *b@ = pointer to block to write
728 * @FILE *fp@ = stream to write on
732 * Use: Writes a block on a stream in a textual format.
735 static void bwrite(block
*b
, FILE *fp
)
737 fputs(tagtab
[b
->tag
], fp
);
744 putstring(fp
, b
->d
.buf
, 0);
749 timestring(b
->t
, &d
);
750 putstring(fp
, d
.buf
, 0);
754 fprintf(fp
, "%08lx", (unsigned long)b
->k
);
762 base64_encode(&b64
, b
->b
.buf
, b
->b
.len
, &d
);
763 base64_encode(&b64
, 0, 0, &d
);
765 if (b
->tag
== T_FILE
) {
767 putstring(fp
, b
->d
.buf
, 0);
776 * Arguments: @block *b@ = pointer to block to write
777 * @FILE *fp@ = file to write on
778 * @ghash *h@ = pointer to hash function
779 * @unsigned bin@ = binary/text flag
783 * Use: Spits out a block properly.
786 static void bemit(block
*b
, FILE *fp
, ghash
*h
, unsigned bin
)
788 if (h
|| (fp
&& bin
)) {
792 h
->ops
->hash(h
, d
.buf
, d
.len
);
794 fwrite(d
.buf
, d
.len
, 1, fp
);
800 /*----- Static variables --------------------------------------------------*/
802 static const char *keyring
= "keyring";
804 /*----- Other shared functions --------------------------------------------*/
806 /* --- @keyreport@ --- *
808 * Arguments: @const char *file@ = filename containing the error
809 * @int line@ = line number in file
810 * @const char *err@ = error text message
811 * @void *p@ = unimportant pointer
815 * Use: Reports errors during the opening of a key file.
818 static void keyreport(const char *file
, int line
, const char *err
, void *p
)
820 moan("error in keyring `%s' at line `%s': %s", file
, line
, err
);
825 * Arguments: @const gchash *c@ = pointer to hash class
826 * @const char *file@ = file to hash
827 * @void *b@ = pointer to output buffer
829 * Returns: Zero if it worked, or nonzero for a system error.
831 * Use: Hashes a file.
834 static int fhash(const gchash
*c
, const char *file
, void *b
)
836 FILE *fp
= fopen(file
, "rb");
837 ghash
*h
= c
->init();
844 while ((sz
= fread(buf
, 1, sizeof(buf
), fp
)) > 0)
845 h
->ops
->hash(h
, buf
, sz
);
856 * Arguments: @FILE *fp@ = file to write on
857 * @const void *p@ = pointer to data to be written
858 * @size_t sz@ = size of the data to write
862 * Use: Emits a hex dump to a stream.
865 static void fhex(FILE *fp
, const void *p
, size_t sz
)
871 fprintf(fp
, "%02x", *q
++);
879 /*----- Signature generation ----------------------------------------------*/
881 static int sign(int argc
, char *argv
[])
892 const sig
*s
= sigtab
;
893 const gchash
*gch
= &rmd160
;
895 time_t exp
= KEXP_EXPIRE
;
897 const char *ifile
= 0;
898 const char *ofile
= 0;
906 static struct option opts
[] = {
907 { "null", 0, 0, '0' },
908 { "binary", 0, 0, 'b' },
909 { "verbose", 0, 0, 'v' },
910 { "quiet", 0, 0, 'q' },
911 { "algorithm", OPTF_ARGREQ
, 0, 'a' },
912 { "hash", OPTF_ARGREQ
, 0, 'h' },
913 { "comment", OPTF_ARGREQ
, 0, 'c' },
914 { "file", OPTF_ARGREQ
, 0, 'f' },
915 { "output", OPTF_ARGREQ
, 0, 'o' },
916 { "keytype", OPTF_ARGREQ
, 0, 't' },
917 { "keyid", OPTF_ARGREQ
, 0, 'i' },
918 { "key", OPTF_ARGREQ
, 0, 'i' },
919 { "expire", OPTF_ARGREQ
, 0, 'e' },
922 int i
= mdwopt(argc
, argv
, "+0vqb a:h:c: f:o: t:i:k:e:", opts
, 0, 0, 0);
940 if ((s
= getsig(optarg
)) == 0) {
941 die(EXIT_FAILURE
, "unknown or ambiguous signature algorithm `%s'",
946 if ((gch
= gethash(optarg
)) == 0) {
947 die(EXIT_FAILURE
, "unknown or ambiguous hash function `%s'",
968 if (strcmp(optarg
, "forever") == 0)
970 else if ((exp
= get_date(optarg
, 0)) == -1)
971 die(EXIT_FAILURE
, "bad expiry time");
978 if (optind
!= argc
|| (f
& f_bogus
))
979 die(EXIT_FAILURE
, "Usage: sign [-options]");
981 /* --- Locate the signing key --- */
983 if (key_open(&kf
, keyring
, KOPEN_WRITE
, keyreport
, 0))
984 die(EXIT_FAILURE
, "couldn't open keyring `%s'", keyring
);
986 if ((k
= key_bytag(&kf
, ki
)) == 0)
987 die(EXIT_FAILURE
, "couldn't find key `%s'", ki
);
991 if ((k
= key_bytype(&kf
, kt
)) == 0)
992 die(EXIT_FAILURE
, "no appropriate key of type `%s'", kt
);
995 if (exp
== KEXP_FOREVER
&& k
->exp
!= KEXP_FOREVER
) {
996 die(EXIT_FAILURE
, "key `%s' expires: can't create nonexpiring signature",
1000 /* --- Open files --- */
1004 else if ((ifp
= fopen(ifile
, (f
& f_raw
) ?
"rb" : "r")) == 0) {
1005 die(EXIT_FAILURE
, "couldn't open input file `%s': %s",
1006 ifile
, strerror(errno
));
1011 else if ((ofp
= fopen(ofile
, (f
& f_bin
) ?
"wb" : "w")) == 0) {
1012 die(EXIT_FAILURE
, "couldn't open output file `%s': %s",
1013 ofile
, strerror(errno
));
1016 /* --- Emit the start of the output --- */
1018 binit(&b
); b
.tag
= T_IDENT
;
1019 dstr_putf(&b
.d
, "%s, Catacomb version " VERSION
, QUIS
);
1020 bemit(&b
, ofp
, 0, f
& f_bin
);
1022 breset(&b
); b
.tag
= T_SIGALG
; DPUTS(&b
.d
, s
->name
);
1023 bemit(&b
, ofp
, 0, f
& f_bin
);
1025 breset(&b
); b
.tag
= T_HASHALG
; DPUTS(&b
.d
, gch
->name
);
1026 bemit(&b
, ofp
, 0, f
& f_bin
);
1028 breset(&b
); b
.tag
= T_KEYID
; b
.k
= k
->id
;
1029 bemit(&b
, ofp
, 0, f
& f_bin
);
1031 /* --- Start hashing, and emit the datestamps and things --- */
1034 time_t now
= time(0);
1037 breset(&b
); b
.tag
= T_DATE
; b
.t
= now
; bemit(&b
, ofp
, h
, f
& f_bin
);
1038 if (exp
== KEXP_EXPIRE
)
1039 exp
= now
+ 86400 * 28;
1040 breset(&b
); b
.tag
= T_EXPIRE
; b
.t
= exp
; bemit(&b
, ofp
, h
, f
& f_bin
);
1042 breset(&b
); b
.tag
= T_COMMENT
; DPUTS(&b
.d
, c
);
1043 bemit(&b
, ofp
, h
, f
& f_bin
);
1050 /* --- Now hash the various files --- */
1054 /* --- Stop on an output error --- */
1061 /* --- Read the next filename to hash --- */
1064 if (getstring(ifp
, &b
.d
, f
& f_raw
))
1067 DENSURE(&b
.b
, h
->ops
->c
->hashsz
);
1068 if (fhash(gch
, b
.d
.buf
, b
.b
.buf
)) {
1069 moan("Error reading `%s': %s", b
.d
.buf
, strerror(errno
));
1072 b
.b
.len
+= h
->ops
->c
->hashsz
;
1074 fhex(stderr
, b
.b
.buf
, b
.b
.len
);
1075 fprintf(stderr
, " %s\n", b
.d
.buf
);
1077 bemit(&b
, ofp
, h
, f
& f_bin
);
1081 /* --- Create the signature --- */
1083 if (!(f
& f_bogus
)) {
1085 b
.tag
= T_SIGNATURE
;
1086 DENSURE(&b
.d
, h
->ops
->c
->hashsz
);
1087 h
->ops
->done(h
, b
.d
.buf
);
1088 if ((e
= s
->sign(k
, b
.d
.buf
, h
->ops
->c
->hashsz
, &b
.b
)) != 0) {
1089 moan("error creating signature: %s", key_strerror(e
));
1092 if (!(f
& f_bogus
)) {
1093 bemit(&b
, ofp
, 0, f
& f_bin
);
1094 key_used(&kf
, k
, exp
);
1098 /* --- Tidy up at the end --- */
1110 if ((e
= key_close(&kf
)) != 0) {
1113 die(EXIT_FAILURE
, "couldn't write file `%s': %s",
1114 keyring
, strerror(errno
));
1116 die(EXIT_FAILURE
, "keyring file `%s' broken: %s (repair manually)",
1117 keyring
, strerror(errno
));
1121 die(EXIT_FAILURE
, "error(s) occurred while creating signature");
1122 return (EXIT_SUCCESS
);
1129 /*----- Signature verification --------------------------------------------*/
1131 static int verify(int argc
, char *argv
[])
1142 const gchash
*gch
= &rmd160
;
1149 /* --- Parse the options --- */
1152 static struct option opts
[] = {
1153 { "verbose", 0, 0, 'v' },
1154 { "quiet", 0, 0, 'q' },
1157 int i
= mdwopt(argc
, argv
, "+vq", opts
, 0, 0, 0);
1175 if ((f
& f_bogus
) || argc
> 1)
1176 die(EXIT_FAILURE
, "Usage: verify [-qv] [file]");
1178 /* --- Open the key file, and start reading the input file --- */
1180 if (key_open(&kf
, keyring
, KOPEN_READ
, keyreport
, 0))
1181 die(EXIT_FAILURE
, "couldn't open keyring `%s'\n", keyring
);
1185 if ((fp
= fopen(argv
[0], "rb")) == 0) {
1186 die(EXIT_FAILURE
, "couldn't open file `%s': %s\n",
1187 argv
[0], strerror(errno
));
1189 if (getc(fp
) == 0) {
1194 if ((fp
= fopen(argv
[0], "r")) == 0) {
1195 die(EXIT_FAILURE
, "couldn't open file `%s': %s\n",
1196 argv
[0], strerror(errno
));
1201 /* --- Read the introductory matter --- */
1206 e
= bget(&b
, fp
, f
& f_bin
);
1208 die(EXIT_FAILURE
, "error reading packet: %s\n", errtab
[-e
]);
1214 printf("INFO ident: `%s'\n", b
.d
.buf
);
1217 if ((s
= getsig(b
.d
.buf
)) == 0) {
1219 printf("FAIL unknown signature algorithm `%s'\n", b
.d
.buf
);
1223 printf("INFO signature algorithm: %s\n", s
->name
);
1226 if ((gch
= gethash(b
.d
.buf
)) == 0) {
1228 printf("FAIL unknown hash function `%s'\n", b
.d
.buf
);
1232 printf("INFO hash function algorithm: %s\n", gch
->name
);
1235 if ((k
= key_byid(&kf
, b
.k
)) == 0) {
1237 printf("FAIL key %08lx not found\n", (unsigned long)b
.k
);
1242 key_fulltag(k
, &b
.d
);
1243 printf("INFO key: %s\n", b
.d
.buf
);
1247 die(EXIT_FAILURE
, "(internal) unknown packet type\n");
1252 /* --- Initialize the hash function and start reading hashed packets --- */
1257 puts("FAIL no keyid packet found");
1264 printf("INFO comment: `%s'\n", b
.d
.buf
);
1270 timestring(b
.t
, &b
.d
);
1271 printf("INFO date: %s\n", b
.d
.buf
);
1276 time_t now
= time(0);
1279 puts("BAD signature has expired");
1284 timestring(b
.t
, &b
.d
);
1285 printf("INFO expires: %s\n", b
.d
.buf
);
1291 DENSURE(&d
, gch
->hashsz
);
1292 if (fhash(gch
, b
.d
.buf
, d
.buf
)) {
1294 printf("BAD error reading file `%s': %s\n",
1295 b
.d
.buf
, strerror(errno
));
1298 } else if (b
.b
.len
!= gch
->hashsz
||
1299 memcmp(d
.buf
, b
.b
.buf
, b
.b
.len
) != 0) {
1301 printf("BAD file `%s' has incorrect hash\n", b
.d
.buf
);
1303 } else if (verb
> 3) {
1304 fputs("INFO hash: ", stdout
);
1305 fhex(stdout
, b
.b
.buf
, b
.b
.len
);
1306 printf(" %s\n", b
.d
.buf
);
1312 DENSURE(&b
.d
, h
->ops
->c
->hashsz
);
1313 b
.d
.len
+= h
->ops
->c
->hashsz
;
1314 h
->ops
->done(h
, b
.d
.buf
);
1315 e
= s
->verify(k
, b
.d
.buf
, b
.d
.len
, b
.b
.buf
, b
.b
.len
);
1319 printf("BAD error unpacking key: %s\n", key_strerror(e
));
1321 puts("BAD bad signature");
1324 } else if (verb
> 2)
1325 puts("INFO good signature");
1329 printf("FAIL invalid packet type %i\n", e
);
1334 e
= bget(&b
, fp
, f
& f_bin
);
1337 printf("FAIL error reading packet: %s\n", errtab
[-e
]);
1349 puts("FAIL signature invalid");
1351 puts("OK signature verified");
1353 return (f
& f_bogus ? EXIT_FAILURE
: EXIT_SUCCESS
);
1360 /*----- Main code ---------------------------------------------------------*/
1362 typedef struct cmd
{
1364 int (*func
)(int /*argc*/, char */
*argv*/
[]);
1368 static cmd cmdtab
[] = {
1369 /* { "manifest", manifest, */
1370 /* "manifest [-0] [-o output]" }, */
1373 [-0v] [-a alg] [-h hash] [-t keytype] [-i keyid]\n\
1374 [-e expire] [-f file] [-o output]" },
1376 "verify [-qv] [file]" },
1380 static void version(FILE *fp
)
1382 pquis(fp
, "$, Catacomb version " VERSION
"\n");
1385 static void usage(FILE *fp
)
1387 pquis(fp
, "Usage: $ [-k keyring] command [args]\n");
1390 static void help(FILE *fp
)
1397 Create and verify signatures on lists of files.\n\
1399 for (c
= cmdtab
; c
->name
; c
++)
1400 fprintf(fp
, "%s\n", c
->help
);
1405 * Arguments: @int argc@ = number of command line arguments
1406 * @char *argv[]@ = vector of command line arguments
1408 * Returns: Zero if successful, nonzero otherwise.
1410 * Use: Signs or verifies signatures on lists of files. Useful for
1411 * ensuring that a distribution is unmolested.
1414 int main(int argc
, char *argv
[])
1417 cmd
*c
= 0, *cc
= 0;
1422 /* --- Initialize the library --- */
1426 rand_noisesrc(RAND_GLOBAL
, &noise_source
);
1427 rand_seed(RAND_GLOBAL
, 160);
1429 /* --- Parse options --- */
1432 static struct option opts
[] = {
1433 { "help", 0, 0, 'h' },
1434 { "version", 0, 0, 'v' },
1435 { "usage", 0, 0, 'u' },
1436 { "keyring", OPTF_ARGREQ
, 0, 'k' },
1439 int i
= mdwopt(argc
, argv
, "+hvu k:", opts
, 0, 0, 0);
1466 if (f
& f_bogus
|| argc
< 1) {
1471 /* --- Dispatch to the correct subcommand handler --- */
1473 n
= strlen(argv
[0]);
1474 for (c
= cmdtab
; c
->name
; c
++) {
1475 if (strncmp(argv
[0], c
->name
, n
) == 0) {
1476 if (c
->name
[n
] == 0) {
1480 die(EXIT_FAILURE
, "ambiguous command name `%s'", argv
[0]);
1486 die(EXIT_FAILURE
, "unknown command `%s'", argv
[0]);
1487 return (cc
->func(argc
, argv
));
1492 /*----- That's all, folks -------------------------------------------------*/