From c65df27983057ec76ed0e72bb370f9a5ae7dad28 Mon Sep 17 00:00:00 2001 From: mdw Date: Fri, 1 Oct 2004 21:08:29 +0000 Subject: [PATCH] General utilities cleanup. Add signature support to catcrypt. Throw in cookie. Add manual pages. --- Makefile.m4 | 23 +- catcrypt.1 | 676 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ catcrypt.c | 281 +++++++++++++++--------- cc-enc.c | 4 +- cc-kem.c | 10 +- cc-list.c | 73 +++++++ cc-sig.c | 9 +- cc-subcmd.c | 109 ++++++++++ cc.h | 128 ++++++++++- cookie.1 | 269 +++++++++++++++++++++++ cookie.c | 692 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dsig.1 | 512 ++++++++++++++++++++++++++++++++++++++++++++ dsig.c | 116 +++++----- hashsum.1 | 34 ++- hashsum.c | 61 +++--- key.1 | 135 +++++++++++- keyutil.c | 235 ++++++++++----------- perftest.c | 44 +++- 18 files changed, 3050 insertions(+), 361 deletions(-) create mode 100644 catcrypt.1 create mode 100644 cc-list.c create mode 100644 cc-subcmd.c create mode 100644 cookie.1 create mode 100644 cookie.c create mode 100644 dsig.1 diff --git a/Makefile.m4 b/Makefile.m4 index 04d03f6..1cccc9b 100644 --- a/Makefile.m4 +++ b/Makefile.m4 @@ -1,6 +1,6 @@ ## -*-m4-*- ## -## $Id: Makefile.m4,v 1.83 2004/04/21 00:37:32 mdw Exp $ +## $Id$ ## ## Makefile for Catacomb ## @@ -246,17 +246,24 @@ patsubst(MP_SOURCES, `\.c\>', `.lo') dsig.o keyutil.o rspit.o: \ ## --- Utility programs --- bin_PROGRAMS = \ - dsig key pixie rspit factorial hashsum mkphrase catcrypt + dsig key pixie cookie rspit factorial hashsum mkphrase catcrypt +noinst_LIBRARIES = libcatcrypt.a bin_SCRIPTS = catacomb-config xpixie noinst_PROGRAMS = \ genprimes mptypes serpent-check bittest mpdump \ perftest \ addsuffix(`gen_tables', `-mktab') -LDADD = libcatacomb.la - -dsig_SOURCES = dsig.c cc.h cc-sig.c getdate.y getdate.h -catcrypt_SOURCES = catcrypt.c cc.h cc-sig.c cc-kem.c cc-enc.c -key_SOURCES = keyutil.c getdate.y getdate.h +LDADD = libcatacomb.la libcatcrypt.a + +libcatcrypt_a_SOURCES = \ + cc.h getdate.h \ + cc-sig.c cc-subcmd.c cc-enc.c cc-kem.c cc-list.c \ + getdate.y + +dsig_SOURCES = dsig.c +cookie_SOURCES = cookie.c +catcrypt_SOURCES = catcrypt.c +key_SOURCES = keyutil.c hashsum_SOURCES = hashsum.c rspit_SOURCES = rspit.c factorial_SOURCES = factorial.c @@ -309,7 +316,7 @@ changequote(`, ') ## --- Documentation --- -man_MANS = key.1 hashsum.1 keyring.5 pixie.1 +man_MANS = key.1 dsig.1 cookie.1 catcrypt.1 hashsum.1 keyring.5 pixie.1 ## --- Other handy definitions --- diff --git a/catcrypt.1 b/catcrypt.1 new file mode 100644 index 0000000..603d1d1 --- /dev/null +++ b/catcrypt.1 @@ -0,0 +1,676 @@ +.\" -*-nroff-*- +.de VS +.sp 1 +.RS +.nf +.ft B +.. +.de VE +.ft R +.fi +.RE +.sp 1 +.. +.ie t \{\ +. if \n(.g \{\ +. fam P +. \} +.\} +.de hP +.IP +.ft B +\h'-\w'\\$1\ 'u'\\$1\ \c +.ft P +.. +.ie t .ds o \(bu +.el .ds o o +.TH catcrypt 1 "30 September 2004" "Straylight/Edgeware" "Catacomb cryptographic library" +.SH NAME +catcrypt \- encrypt and decrypt messages +.SH SYNOPSIS +.B catcrypt +.RB [ \-k +.IR keyring ] +.I command +.PP +where +.I command +is one of: +.PP +.B help +.RI [ command ...] +.br +.B show +.RI [ item ...] +.br +.B encrypt +.RB [ \-a ] +.RB [ \-k +.IR tag ] +.RB [ \-f +.IR format ] +.RB [ \-o +.IR output ] +.RI [ file ] +.br +.B decrypt +.RB [ \-aqv ] +.RB [ \-f +.IR format ] +.RB [ \-o +.IR output ] +.RI [ file ] +.br +.B encode +.RB [ \-f +.IR format ] +.RB [ \-b +.IR boundary ] +.RB [ \-o +.IR output ] +.RI [ file ] +.br +.B encode +.RB [ \-f +.IR format ] +.RB [ \-b +.IR boundary ] +.RB [ \-o +.IR output ] +.RI [ file ] +.SH "DESCRIPTION" +The +.B catcrypt +command encrypts and decrypts messages. It also works as a simple PEM +encoder and decoder. It provides a number of subcommands, by which the +various operations may be carried out. +.SS "Global options" +Before the command name, +.I "global options" +may be given. The following global options are supported: +.TP +.BR "\-h, \-\-help " [ \fIcommand ...] +Writes a brief summary of +.BR catcrypt 's +various options to standard output, and returns a successful exit +status. With command names, gives help on those commands. +.TP +.B "\-v, \-\-version" +Writes the program's version number to standard output, and returns a +successful exit status. +.TP +.B "\-u, \-\-usage" +Writes a very terse command line summary to standard output, and returns +a successful exit status. +.TP +.BI "\-k, \-\-keyring " file +Names the keyring file which +.B key +is to process. The default keyring, used if this option doesn't specify +one, is the file named +.B keyring +in the current directory. See +.BR key (1) +and +.BR keyring (5) +for more details about keyring files. +.SH "KEY SETUP" +Algorithms to be used with a particular key are described by attributes +on the key, or its type. The +.B catcrypt +command deals with both signing and key-encapsulation keys. +.SS "Key-encapsulation keys" +(Key encapsulation is a means of transmitting a short, known, random +secret to a recipient. It differs from encryption in technical ways +which are largely uninteresting at this point.) +.PP +A +.I kemalgspec +has the syntax +.IR kem \c +.RB [ / \c +.IR cipher \c +.RB [ / \c +.IR hash ]]. +If a +.B kem +attribute is present on the key, then it must have this form; otherwise, +the key's type must have the form +.BR cckem- \c +.IR kemalgspec . +Algorithm selections are taken from appropriately-named attributes, or, +failing that, from the +.IR kemalgspec . +.PP +The key-encapsulation mechanism is chosen according to the setting of +.I kem +as follows. Run +.B catcrypt show kem +for a list of supported KEMs. +.TP +.B rsa +This is Shoup's RSA-KEM (formerly Simple RSA); see +.I +A proposal for an ISO standard for public key encryption (version 2.0) +available at +.BR http://eprint.iacr.org/2000/060/ . +Use the +.B rsa +algorithm of the +.B key add +command (see +.BR key (1)) +to generate the key. +.TP +.B dh +This is standard Diffie-Hellman key exchange, hashing the resulting +shared secret to form the key, as used in, e.g., DLIES (P1363a). +Use the +.B dh +algorithm of the +.B key add +command, preferably with the +.B \-LS +options, to generate the key. +.TP +.B ec +This is the elliptic-curve analogue of +.BR dh . Use the +.B ec +algorithm of the +.BR key (1)) +command to generate the key. +.PP +As well as the KEM itself, a number of supporting algorithms are used. +These are taken from appropriately named attributes on the key or, +failing that, derived from other attributes as described below. +.TP +.B cipher +This is the symmetric encryption algorithm used for bulk data +encryption. If there is no +.B cipher +attribute then the +.I cipher +in the +.I kemalgspec +is used; if that it absent, then the default of +.B blowfish-cbc +is used. Run +.B catcrypt show cipher +for a list of supported symmetric encryption algorithms. +.TP +.B hash +This is the hash function used to distil entropy from the shared secret +constructed by the raw KEM. If there is no +.B hash +attribute then the +.I hash +in the +.I kemalgspec is used; if that is absent then the default of +.B rmd160 +is used. Run +.B catcrypt show hash +for a list of supported symmetric encryption algorithms. +.TP +.B mac +This is the message authentication algorithm used during bulk data +encryption to ensure integrity of the encrypted message and defend +against chosen-ciphertext attacks. If there is no +.B mac +attribute then +.IB hash -hmac +is chosen as a default. Run +.B catcrypt show mac +for a list of supported message authentication algorithms. +.TP +.B kdf +This is the key derivation function used to stretch the hashed shared +secret to a sufficient length to select symmetric encryption and +authentication keys, initialization vectors and other necessary +pseudorandom quantities. If there is no +.B kdf +attribute then +.IB hash -mgf +is chosen as a default. Run +.B catcrypt show kdf +for a list of supported key derivation functions. +.B Caution! +Not all supported functions have the required security features: don't +override the default choice unless you know what you're doing. +.SS "Signing keys" +A +.I sigalgspec +has the form +.IR sig \c +.RB [ / \c +.IR hash ]. +If a +.B sig +attribute is present on the key, then it must have this form; otherwise, +the key's type must have the form +.BI ccsig- \c +.IR sigalgspec . +Algorithm selections are taken from appropriately-named attributes, or, +failing that, from the +.IR sigalgspec . +.PP +The signature algorithm is chosen according to the setting of +.I sig +as follows. Run +.B catcrypt show sig +for a list of supported signature algorithms. +.TP +.B rsapkcs1 +This is almost the same as the RSASSA-PKCS1-v1_5 algorithm described in +RFC3447; the difference is that the hash is left bare rather than being +wrapped in a DER-encoded +.B DigestInfo +structure. This doesn't affect security since the key can only be used +with the one hash function anyway, and dropping the DER wrapping permits +rapid adoption of new hash functions. Regardless, use of this algorithm +is not recommended, since the padding method has been shown vulnerable +to attack. Use the +.B rsa +algorithm of the +.B key add +command (see +.BR key (1)) +to generate the key. +.TP +.B rsapss +This is the RSASSA-PSS algorithm described in RFC3447. It is the +preferred RSA-based signature scheme. Use the +.B rsa +algorithm of the +.B key add +command (see +.BR key (1)) +to generate the key. +.TP +.B dsa +This is the DSA algorithm described in FIPS180-1 and FIPS180-2. Use the +.B dsa +algorithm of the +.B key add +command (see +.BR key (1)) +to generate the key. +.TP +.B ecdsa +This is the ECDSA algorithm described in ANSI X9.62 and FIPS180-2. Use +the +.B ec +algorithm of the +.B key add +command (see +.BR key (1)) +to generate the key. +.TP +.B kcdsa +This is the revised KCDSA (Korean Certificate-based Digital Signature +Algorithm) described in +.I The Revised Version of KCDSA +.RB ( http://dasan.sejong.ac.kr/~chlim/pub/kcdsa1.ps ). +Use the +.B dh +algorithm of the +.B key add +command with the +.B \-LS +options (see +.BR key (1)) +to generate the key. +.TP +.B eckcdsa +This is an unofficial elliptic-curve analogue of the KCDSA algorithm. +Use the +.B ec +algorithm of the +.B key add +command (see +.BR key (1)) +to generate the key. +.PP +As well as the signature algorithm itself, a hash function is used. +This is taken from the +.B hash +attribute on the key, or, failing that, from the +.I hash +specified in the +.IR sigalgspec , +or, if that is absent, determined by the signature algorithm as follows. +.hP \*o +For +.BR rsapkcs1 , +.BR rsapss , +.BR dsa , +and +.BR ecdsa , +the default hash function is +.BR sha . +.hP \*o +For +.BR kcdsa +and +.BR eckcdsa , +the default hash function is +.BR has160 . +.PP +Run +.B catcrypt show hash +for a list of supported hash functions. +.SH "ENCODINGS" +Two encodings for the ciphertext are supported. +.TP +.B binary +The raw format, which has the benefit of being smaller, but needs to be +attached to mail messages and generally handled with care. +.TP +.B pem +PEM-encapsulated Base-64 encoded text. This format can be included +directly in email and picked out again automatically; but there is a +4-to-3 data expansion as a result. +.SH "COMMAND REFERENCE" +.SS help +The +.B help +command behaves exactly as the +.B \-\-help +option. With no arguments, it shows an overview of +.BR catcrypt 's +options; with arguments, it describes the named subcommands. +.SS show +The +.B show +command prints various lists of tokens understood by +.BR catcrypt . +With no arguments, it prints all of the lists; with arguments, it prints +just the named lists, in order. The recognized lists can be enumerated +using the +.VS +catcrypt show list +.VE +command. The lists are as follows. +.TP +.B list +The lists which can be enumerated by the +.B show +command. +.TP +.B kem +The key-encapsulation algorithms which can be used in a +key-encapsulation key's +.B kem +attribute. +.TP +.B cipher +The symmetric encryption algorithms which can be used in a +key-encapsulation key's +.B cipher +attribute. +.TP +.B mac +The message authentication algorithms which can be used in a +key-encapsulation key's +.B mac +attribute. +.TP +.B sig +The signature algorithms which can be used in a signing key's +.B sig +attribute. +.TP +.B hash +The hash functions which can be used in a key's +.B hash +attribute. +.TP +.B enc +The encodings which can be applied to encrypted messages; see +.B ENCODINGS +above. +.SS encrypt +The +.B encrypt +command encrypts a file and writes out the appropriately-encoded +ciphertext. By default, it reads from standard input and writes to +standard output. If a filename argument is given, this file is read +instead (as binary data). +.PP +The following options are recognized. +.TP +.B "\-a, \-\-armour" +Produce ASCII-armoured output. This is equivalent to specifying +.BR "\-f pem" . +The variant spelling +.B "\-\-armor" +is also accepted. +.TP +.BI "\-f, \-\-format " format +Produce output encoded according to +.IR format . +.TP +.BI "\-k, \-\-key " tag +Use the key-encapsulation key named +.I tag +in the current keyring; the default key is +.BR ccrypt . +.TP +.BI "\-s, \-\-sign-key " tag +Use the signature key named +.I tag +in the current keyring; the default is not to sign the ciphertext. +.TP +.BI "\-o, \-\-ouptut " file +Write output to +.I file +rather than to standard output. +.SS decrypt +The +.B decrypt +command decrypts a ciphertext and writes out the plaintext. By default, +it reads from standard input and writes to standard output. If a +filename argument is given, this file is read instead. +.PP +The following options are recognized. +.TP +.B "\-a, \-\-armour" +Read ASCII-armoured output. This is equivalent to specifying +.BR "\-f pem" . +The variant spelling +.B "\-\-armor" +is also accepted. +.TP +.BI "\-f, \-\-format " format +Read input encoded according to +.IR format . +.TP +.B "\-v, \-\-verbose" +Produce more verbose messages. See below for the messages produced +during decryption. The default verbosity level is 1. (Currently this +is the most verbose setting. This might not be the case always.) +.TP +.B "\-q, \-\-quiet" +Produce fewer messages. +.TP +.BI "\-o, \-\-output " file +Write output to +.I file +instead of to standard output. The file is written in binary mode. +Fixing line-end conventions is your problem; there are lots of good +tools for dealing with it. +.PP +Output is written to standard output in a machine-readable format. +Major problems cause the program to write a diagnostic to standard error +and exit nonzero as usual. The quantity of output varies depending on +the verbosity level and whether the plaintext is also being written to +standard output. Output lines begin with a keyword.: +.TP +.BI "FAIL " reason +An error prevented decryption. The program will exit nonzero. +.TP +.BI "WARN " reason +.B catcrypt +encountered a situation which may or may not invalidate the decryption. +.TP +.BI "OK " message +Decryption was successful. This is only produced if main output is +being sent somewhere other than standard output. +.TP +.B "DATA" +The plaintext follows, starting just after the next newline character or +sequence. This is only produced if main output is being sent to +standard output. If anything goes wrong, a +.B FAIL +message is printed, preceded and followed by a newline, and the program +exits nonzero. +.TP +.BI "INFO " note +Any other information. +.PP +The information written at the various verbosity levels is as follows. +.hP 0. +No output. Watch the exit status. +.hP 1. +All messages. +.PP +.B Warning! +All output written has been checked for authenticity. However, since +the input is chunked, a chunk will be checked and written before the +authenticity of following chunks is established. Don't rely on the +output being complete until +.B catcrypt decrypt +prints +.B OK +and/or exits successfully. +.SS "encode" +The +.B encode +command encodes an input file according to one of the encodings +described above in +.BR ENCODINGS . +The input is read from the +.I file +given on the command line, or from standard input if none is specified. +Options provided are: +.TP +.BI "\-f, \-\-format " format +Produce output in +.IR format . +Run +.B catcrypt show enc +for a list of encoding formats. +.TP +.BI "\-b, \-\-boundary " label +Set the PEM boundary string to +.IR label ; +i.e., assuming we're encoding in PEM format, the output will have +.BI "\-\-\-\-\-BEGIN " label "\-\-\-\-\-" +at the top and +.BI "\-\-\-\-\-END " label "\-\-\-\-\-" +at the bottom. The default +.I label +is +.BR MESSAGE . +.TP +.BI "\-o, \-\-output " file +Write output to +.I file +instead of to standard output. +.SS "decode" +The +.B decode +command decodes an input file encoded according to one of the encodings +described above in +.BR ENCODINGS . +The input is read from the +.I file +given on the command line, or from standard input if none is specified. +Options provided are: +.TP +.BI "\-f, \-\-format " format +Decode input in +.IR format . +Run +.B catcrypt show enc +for a list of encoding formats. +.TP +.BI "\-b, \-\-boundary " label +Set the PEM boundary string to +.IR label ; +i.e., assuming we're encoding in PEM format, start processing input +between +.BI "\-\-\-\-\-BEGIN " label "\-\-\-\-\-" +and +.BI "\-\-\-\-\-END " label "\-\-\-\-\-" +lines. Without this option, +.B catcrypt +will start reading at the first plausible boundary string, and continue +processing until it reaches the matching end boundary. +.TP +.BI "\-o, \-\-output " file +Write output to +.I file +instead of to standard output. +.SH "SECURITY PROPERTIES" +Assuming the security of the underlying primitive algorithms, the +following security properties of the ciphertext hold. +.hP \*o +An adversary given the public key-encapsulation key and capable of +requesting encryption of arbitrary plaintexts of his own devising is +unable to decide whether he is given ciphertexts corresponding to his +chosen plaintexts or random plaintexts of the same length. This holds +even if the adversary is permitted to request decryption of any +ciphertext other than one produced as a result of an encryption request. +This property is called +.BR IND-CCA2 . +.hP \*o +An adversary given the public key-encapsulation and verification keys, +and capable of requesting encryption of arbitrary plaintext of his own +devising is unable to produce a new ciphertext which will be accepted as +genuine. This property is called +.BR INT-CTXT . +.hP \*o +An adversary given the public key-encapsulation and verification keys, +and capable of requesting encryption of arbitrary plaintext of his own +devising is unable to decide whether the ciphertexts he is given are +correctly signed. This property doesn't seem to have a name. +.PP +Not all is rosy. If you leak intermediate values during decryption then +an adversary can construct a new correctly-signed message. Don't do +that, then \(en leaking intermediate values often voids security +warranties. But it does avoid the usual problem with separate signing +and encryption that a careful leak by the recipient can produce evidence +that you signed some incriminating message. +.SH "CRYPTOGRAPHIC THEORY" +Encryption of a message proceeds as follows. +.hP 0. +Emit a header packet containing the key-ids for the key-encapsulation +key, and signature key if any. +.hP 1. +Use the KEM to produce a public value and a shared secret the recipient +will be able to extract from the public value using his private key. +Emit a packet containing the public value. +.hP 2. +Hash the shared secret. Use the KDF to produce a pseudorandom keystream +of indefinite length. +.hP 3. +Use the first bits of the keystream to key a symmetric encryption +scheme; use the next bits to key a message authentication code. +.hP 4. +If we're signing the message then extract 1024 bytes from the keystream, +sign them, and emit a packet containing the signature. The signature +packet doesn't contain the signed message, just the signature. +.hP 5. +Split the message into blocks. For each block, pick a random IV from +the keystream, encrypt the block and emit a packet containing the +IV, ciphertext and a MAC tag. +.PP +That's it. Nothing terribly controversial, really. +.SH "SEE ALSO" +.BR key (1), +.BR dsig (1), +.BR hashsum (1), +.BR keyring (5). +.SH AUTHOR +Mark Wooding, diff --git a/catcrypt.c b/catcrypt.c index 546b6c3..cc1ab39 100644 --- a/catcrypt.c +++ b/catcrypt.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: catcrypt.c,v 1.2 2004/05/09 13:03:46 mdw Exp $ + * $Id$ * * Command-line encryption tool * @@ -50,6 +50,9 @@ #include "key.h" #include "cc.h" +#include "ectab.h" +#include "ptab.h" + /*----- Utilities ---------------------------------------------------------*/ /* --- @keyreport@ --- * @@ -142,12 +145,13 @@ err: static int encrypt(int argc, char *argv[]) { - const char *of = 0, *kn = "ccrypt"; + const char *of = 0, *kn = "ccrypt", *skn = 0; FILE *ofp = 0; FILE *fp = 0; const char *ef = "binary"; const char *err; int i; + int en; size_t n; dstr d = DSTR_INIT; octet *tag, *ct; @@ -157,7 +161,9 @@ static int encrypt(int argc, char *argv[]) unsigned f = 0; key_file kf; key *k; + key *sk = 0; kem *km; + sig *s = 0; gcipher *cx, *c; gmac *m; ghash *h; @@ -169,16 +175,18 @@ static int encrypt(int argc, char *argv[]) for (;;) { static const struct option opt[] = { { "key", OPTF_ARGREQ, 0, 'k' }, + { "sign-key", OPTF_ARGREQ, 0, 's' }, { "armour", 0, 0, 'a' }, { "armor", 0, 0, 'a' }, { "format", OPTF_ARGREQ, 0, 'f' }, { "output", OPTF_ARGREQ, 0, 'o' }, { 0, 0, 0, 0 } }; - i = mdwopt(argc, argv, "k:af:o:", opt, 0, 0, 0); + i = mdwopt(argc, argv, "k:s:af:o:", opt, 0, 0, 0); if (i < 0) break; switch (i) { case 'k': kn = optarg; break; + case 's': skn = optarg; break; case 'a': ef = "pem"; break; case 'f': ef = optarg; break; case 'o': of = optarg; break; @@ -186,12 +194,14 @@ static int encrypt(int argc, char *argv[]) } } if (argc - optind > 1 || (f & f_bogus)) - die(EXIT_FAILURE, "Usage: encrypt [-options] [file]"); + die(EXIT_FAILURE, "Usage: encrypt [-OPTIONS] [FILE]"); if (key_open(&kf, keyring, KOPEN_READ, keyreport, 0)) die(EXIT_FAILURE, "can't open keyring `%s'", keyring); if ((k = key_bytag(&kf, kn)) == 0) die(EXIT_FAILURE, "key `%s' not found", kn); + if (skn && (sk = key_bytag(&kf, skn)) == 0) + die(EXIT_FAILURE, "key `%s' not found", skn); if ((eo = getenc(ef)) == 0) die(EXIT_FAILURE, "encoding `%s' not found", ef); @@ -214,11 +224,19 @@ static int encrypt(int argc, char *argv[]) ofp, strerror(errno)); } + dstr_reset(&d); key_fulltag(k, &d); e = initenc(eo, ofp, "CATCRYPT ENCRYPTED MESSAGE", 1); - km = getkem(k, "ccrypt", 0); + km = getkem(k, "cckem", 0); if ((err = km->ops->check(km)) != 0) moan("key `%s' fails check: %s", d.buf, err); + if (sk) { + dstr_reset(&d); + key_fulltag(sk, &d); + s = getsig(sk, "ccsig", 1); + if ((err = s->ops->check(s)) != 0) + moan("key `%s' fails check: %s", d.buf, err); + } /* --- Build the header chunk --- */ @@ -226,6 +244,7 @@ static int encrypt(int argc, char *argv[]) dstr_ensure(&d, 256); buf_init(&b, d.buf, 256); buf_putu32(&b, k->id); + if (sk) buf_putu32(&b, sk->id); assert(BOK(&b)); chunk_write(e, &b); @@ -238,6 +257,19 @@ static int encrypt(int argc, char *argv[]) BSTEP(&b, d.len); chunk_write(e, &b); + /* --- Write the signature chunk --- */ + + if (s) { + GC_ENCRYPT(cx, 0, bb, 1024); + GH_HASH(s->h, bb, 1024); + dstr_reset(&d); + if ((en = s->ops->doit(s, &d)) != 0) + die(EXIT_FAILURE, "error creating signature: %s", key_strerror(en)); + buf_init(&b, d.buf, d.len); + BSTEP(&b, d.len); + chunk_write(e, &b); + } + /* --- Now do the main crypto --- */ assert(GC_CLASS(c)->blksz <= sizeof(bb)); @@ -281,6 +313,7 @@ static int encrypt(int argc, char *argv[]) GC_DESTROY(c); GC_DESTROY(cx); freeenc(e); + if (s) freesig(s); freekem(km); if (of) fclose(ofp); key_close(&kf); @@ -305,7 +338,9 @@ static int decrypt(int argc, char *argv[]) size_t seq; uint32 id; key *k; + key *sk = 0; kem *km; + sig *s = 0; gcipher *cx; gcipher *c; ghash *h; @@ -313,6 +348,8 @@ static int decrypt(int argc, char *argv[]) octet *tag; unsigned f = 0; const encops *eo; + const char *err; + int verb = 1; enc *e; #define f_bogus 1u @@ -321,21 +358,25 @@ static int decrypt(int argc, char *argv[]) static const struct option opt[] = { { "armour", 0, 0, 'a' }, { "armor", 0, 0, 'a' }, + { "verbose", 0, 0, 'v' }, + { "quiet", 0, 0, 'q' }, { "format", OPTF_ARGREQ, 0, 'f' }, { "output", OPTF_ARGREQ, 0, 'o' }, { 0, 0, 0, 0 } }; - i = mdwopt(argc, argv, "af:o:", opt, 0, 0, 0); + i = mdwopt(argc, argv, "af:o:qv", opt, 0, 0, 0); if (i < 0) break; switch (i) { case 'a': ef = "pem"; break; + case 'v': verb++; break; + case 'q': if (verb) verb--; break; case 'f': ef = optarg; break; case 'o': of = optarg; break; default: f |= f_bogus; break; } } if (argc - optind > 1 || (f & f_bogus)) - die(EXIT_FAILURE, "Usage: decrypt [-options] [file]"); + die(EXIT_FAILURE, "Usage: decrypt [-OPTIONS] [FILE]"); if ((eo = getenc(ef)) == 0) die(EXIT_FAILURE, "encoding `%s' not found", ef); @@ -359,28 +400,72 @@ static int decrypt(int argc, char *argv[]) /* --- Read the header chunk --- */ chunk_read(e, &d, &b); - if (buf_getu32(&b, &id)) - die(EXIT_FAILURE, "malformed header: missing keyid"); - if (BLEFT(&b)) - die(EXIT_FAILURE, "malformed header: junk at end"); + if (buf_getu32(&b, &id)) { + if (verb) printf("FAIL malformed header: missing keyid\n"); + exit(EXIT_FAILURE); + } + if ((k = key_byid(&kf, id)) == 0) { + if (verb) printf("FAIL key id %08lx not found\n", (unsigned long)id); + exit(EXIT_FAILURE); + } + if (BLEFT(&b)) { + if (buf_getu32(&b, &id)) { + if (verb) printf("FAIL malformed header: missing signature keyid\n"); + exit(EXIT_FAILURE); + } + if ((sk = key_byid(&kf, id)) == 0) { + if (verb) printf("FAIL key id %08lx not found\n", (unsigned long)id); + exit(EXIT_FAILURE); + } + } + if (BLEFT(&b)) { + if (verb) printf("FAIL malformed header: junk at end\n"); + exit(EXIT_FAILURE); + } /* --- Find the key --- */ - if ((k = key_byid(&kf, id)) == 0) - die(EXIT_FAILURE, "key id %08lx not found", (unsigned long)id); - km = getkem(k, "ccrypt", 1); + km = getkem(k, "cckem", 1); /* --- Read the KEM chunk --- */ chunk_read(e, &d, &b); - if (setupkem(km, &d, &cx, &c, &m)) - die(EXIT_FAILURE, "failed to decapsulate key"); + if (setupkem(km, &d, &cx, &c, &m)) { + if (verb) printf("FAIL failed to decapsulate key\n"); + exit(EXIT_FAILURE); + } + + /* --- Verify the signature, if there is one --- */ + + if (sk) { + s = getsig(sk, "ccsig", 0); + dstr_reset(&d); + key_fulltag(sk, &d); + if (verb && (err = s->ops->check(s)) != 0) + printf("WARN verification key %s fails check: %s\n", d.buf, err); + dstr_reset(&d); + dstr_ensure(&d, 1024); + GC_ENCRYPT(cx, 0, d.buf, 1024); + GH_HASH(s->h, d.buf, 1024); + chunk_read(e, &d, &b); + if (s->ops->doit(s, &d)) { + if (verb) printf("FAIL signature verification failed\n"); + exit(EXIT_FAILURE); + } + if (verb) { + dstr_reset(&d); + key_fulltag(sk, &d); + printf("INFO good signature from %s\n", d.buf); + } + } else if (verb) + printf("INFO no signature packet\n"); /* --- Now decrypt the main body --- */ - if (!of || strcmp(of, "-") == 0) + if (!of || strcmp(of, "-") == 0) { ofp = stdout; - else if ((ofp = fopen(of, "wb")) == 0) { + if (verb) printf("DATA\n"); + } else if ((ofp = fopen(of, "wb")) == 0) { die(EXIT_FAILURE, "couldn't open file `%s' for output: %s", ofp, strerror(errno)); } @@ -398,22 +483,37 @@ static int decrypt(int argc, char *argv[]) GH_HASH(h, d.buf, 4); seq++; chunk_read(e, &d, &b); - if ((tag = buf_get(&b, GM_CLASS(m)->hashsz)) == 0) - die(EXIT_FAILURE, "bad ciphertext chunk: no tag"); + if ((tag = buf_get(&b, GM_CLASS(m)->hashsz)) == 0) { + if (verb) printf("%sFAIL bad ciphertext chunk: no tag\n", + ofp == stdout ? "\n" : ""); + exit(EXIT_FAILURE); + } GH_HASH(h, BCUR(&b), BLEFT(&b)); - if (memcmp(tag, GH_DONE(h, 0), GM_CLASS(m)->hashsz) != 0) - die(EXIT_FAILURE, "bad ciphertext chunk: authentication failure"); + if (memcmp(tag, GH_DONE(h, 0), GM_CLASS(m)->hashsz) != 0) { + if (verb) + printf("%sFAIL bad ciphertext chunk: authentication failure\n", + ofp == stdout ? "\n" : ""); + exit(EXIT_FAILURE); + } if (!BLEFT(&b)) break; GC_DECRYPT(c, BCUR(&b), BCUR(&b), BLEFT(&b)); - if (fwrite(BCUR(&b), 1, BLEFT(&b), ofp) != BLEFT(&b)) - die(EXIT_FAILURE, "error writing output: %s", strerror(errno)); + if (fwrite(BCUR(&b), 1, BLEFT(&b), ofp) != BLEFT(&b)) { + if (verb) printf("%sFAIL error writing output: %s\n", + ofp == stdout ? "\n" : "", strerror(errno)); + exit(EXIT_FAILURE); + } + } + + if (fflush(ofp) || ferror(ofp)) { + if (verb) printf("%sFAIL error writing output: %s\n", + ofp == stdout ? "\n" : "", strerror(errno)); + exit(EXIT_FAILURE); } - if (fflush(ofp) || ferror(ofp)) - die(EXIT_FAILURE, "error writing output: %s", strerror(errno)); - e->ops->decdone(e); + if (verb && ofp != stdout) + printf("OK decrypted successfully\n"); freeenc(e); GC_DESTROY(c); GC_DESTROY(cx); @@ -462,7 +562,7 @@ static int encode(int argc, char *argv[]) } } if (argc - optind > 1 || (f & f_bogus)) - die(EXIT_FAILURE, "Usage: encode [-options] [file]"); + die(EXIT_FAILURE, "Usage: encode [-OPTIONS] [FILE]"); if ((eo = getenc(ef)) == 0) die(EXIT_FAILURE, "encoding `%s' not found", ef); @@ -531,7 +631,7 @@ static int decode(int argc, char *argv[]) } } if (argc - optind > 1 || (f & f_bogus)) - die(EXIT_FAILURE, "Usage: decode [-options] [file]"); + die(EXIT_FAILURE, "Usage: decode [-OPTIONS] [FILE]"); if ((eo = getenc(ef)) == 0) die(EXIT_FAILURE, "encoding `%s' not found", ef); @@ -571,16 +671,36 @@ static int decode(int argc, char *argv[]) /*----- Main code ---------------------------------------------------------*/ -typedef struct cmd { - const char *name; - int (*func)(int /*argc*/, char */*argv*/[]); - const char *usage; - const char *help; -} cmd; +#define LISTS(LI) \ + LI("Lists", list, \ + listtab[i].name, listtab[i].name) \ + LI("Key-encapsulation mechanisms", kem, \ + kemtab[i].name, kemtab[i].name) \ + LI("Signature schemes", sig, \ + sigtab[i].name, sigtab[i].name) \ + LI("Encodings", enc, \ + enctab[i].name, enctab[i].name) \ + LI("Symmetric encryption algorithms", cipher, \ + gciphertab[i], gciphertab[i]->name) \ + LI("Hash functions", hash, \ + ghashtab[i], ghashtab[i]->name) \ + LI("Message authentication codes", mac, \ + gmactab[i], gmactab[i]->name) + +MAKELISTTAB(listtab, LISTS) + +int cmd_show(int argc, char *argv[]) +{ + return (displaylists(listtab, argv + 1)); +} + +static int cmd_help(int, char **); static cmd cmdtab[] = { + { "help", cmd_help, "help [COMMAND...]" }, + { "show", cmd_show, "show [ITEM...]" }, { "encode", encode, - "encode [-f format] [-b label] [-o output] [file]", + "encode [-f FORMAT] [-b LABEL] [-o OUTPUT] [FILE]", "\ Options:\n\ \n\ @@ -589,7 +709,7 @@ Options:\n\ -o, --output=FILE Write output to FILE.\n\ " }, { "decode", decode, - "decode [-f format] [-b label] [-o output] [file]", + "decode [-f FORMAT] [-b LABEL] [-o OUTPUT] [FILE]", "\ Options:\n\ \n\ @@ -598,87 +718,59 @@ Options:\n\ -o, --output=FILE Write output to FILE.\n\ " }, { "encrypt", encrypt, - "encrypt [-a] [-k tag] [f format]] [-o output] [file]", - "\ + "encrypt [-a] [-k TAG] [-s TAG] [-f FORMAT]\n\t\ +[-o OUTPUT] [FILE]", "\ Options:\n\ \n\ -a, --armour Same as `-f pem'.\n\ -f, --format=FORMAT Encode as FORMAT.\n\ --k, --key=TAG Use public key named by TAG.\n\ +-k, --key=TAG Use public encryption key named by TAG.\n\ +-s, --sign-key=TAG Use private signature key named by TAG.\n\ -o, --output=FILE Write output to FILE.\n\ " }, { "decrypt", decrypt, - "decrypt [-t] [-o output] [file]", "\ + "decrypt [-aqv] [-f FORMAT] [-o OUTPUT] [FILE]", "\ Options:\n\ \n\ --t, --text Read PEM-encoded input.\n\ +-a, --armour Same as `-f pem'.\n\ +-v, --verbose Produce more verbose messages.\n\ +-q, --quiet Produce fewer messages.\n\ +-f, --format=FORMAT Decode as FORMAT.\n\ -o, --output=FILE Write output to FILE.\n\ " }, { 0, 0, 0 } }; -/* --- @findcmd@ --- * - * - * Arguments: @const char *name@ = a command name - * - * Returns: Pointer to the command structure. - * - * Use: Looks up a command by name. If the command isn't found, an - * error is reported and the program is terminated. - */ - -static cmd *findcmd(const char *name) +static int cmd_help(int argc, char **argv) { - cmd *c, *chosen = 0; - size_t sz = strlen(name); - - for (c = cmdtab; c->name; c++) { - if (strncmp(name, c->name, sz) == 0) { - if (c->name[sz] == 0) { - chosen = c; - break; - } else if (chosen) - die(EXIT_FAILURE, "ambiguous command name `%s'", name); - else - chosen = c; - } - } - if (!chosen) - die(EXIT_FAILURE, "unknown command name `%s'", name); - return (chosen); + sc_help(cmdtab, stdout, argv + 1); + return (0); } -static void version(FILE *fp) +void version(FILE *fp) { pquis(fp, "$, Catacomb version " VERSION "\n"); } static void usage(FILE *fp) { - pquis(fp, "Usage: $ [-k keyring] command [args]\n"); + pquis(fp, "Usage: $ [-k KEYRING] COMMAND [ARGS]\n"); } -static void help(FILE *fp, char **argv) +void help_global(FILE *fp) { - cmd *c; - - if (*argv) { - c = findcmd(*argv); - fprintf(fp, "Usage: %s [-k keyring] %s\n", QUIS, c->usage); - if (c->help) { - fputc('\n', fp); - fputs(c->help, fp); - } - } else { - version(fp); - fputc('\n', fp); - usage(fp); - fputs("\n\ + usage(fp); + fputs("\n\ Encrypt and decrypt files.\n\ -\n", fp); - for (c = cmdtab; c->name; c++) - fprintf(fp, "%s\n", c->usage); - } +\n\ +Global command-line options:\n\ +\n\ +-h, --help [COMMAND...] Show this help message, or help for COMMANDs.\n\ +-v, --version Show program version number.\n\ +-u, --usage Show a terse usage message.\n\ +\n\ +-k, --keyring=FILE Read keys from FILE.\n", + fp); } /* --- @main@ --- * @@ -688,8 +780,7 @@ Encrypt and decrypt files.\n\ * * Returns: Zero if successful, nonzero otherwise. * - * Use: Signs or verifies signatures on lists of files. Useful for - * ensuring that a distribution is unmolested. + * Use: Encrypts or decrypts files. */ int main(int argc, char *argv[]) @@ -720,7 +811,7 @@ int main(int argc, char *argv[]) break; switch (i) { case 'h': - help(stdout, argv + optind); + sc_help(cmdtab, stdout, argv + optind); exit(0); break; case 'v': @@ -749,7 +840,7 @@ int main(int argc, char *argv[]) /* --- Dispatch to the correct subcommand handler --- */ - return (findcmd(argv[0])->func(argc, argv)); + return (findcmd(cmdtab, argv[0])->cmd(argc, argv)); #undef f_bogus } diff --git a/cc-enc.c b/cc-enc.c index f44bb33..c3da95b 100644 --- a/cc-enc.c +++ b/cc-enc.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: cc-enc.c,v 1.1 2004/04/17 09:58:37 mdw Exp $ + * $Id$ * * Catcrypt data encoding * @@ -260,7 +260,7 @@ static void pem_destroy(enc *e) /* --- Encoder table --- */ -static const encops enctab[] = { +const encops enctab[] = { { "binary", "rb", "wb", bin_init, bin_init, bin_read, bin_write, diff --git a/cc-kem.c b/cc-kem.c index 4f71d0d..8ba29fd 100644 --- a/cc-kem.c +++ b/cc-kem.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: cc-kem.c,v 1.1 2004/04/17 09:58:37 mdw Exp $ + * $Id$ * * Catcrypt key-encapsulation * @@ -324,11 +324,7 @@ static const kemops ec_decops = { /* --- The switch table --- */ -static const struct kemtab { - const char *name; - const kemops *encops; - const kemops *decops; -} kemtab[] = { +const struct kemtab kemtab[] = { { "rsa", &rsa_encops, &rsa_decops }, { "dh", &dh_encops, &dh_decops }, { "ec", &ec_encops, &ec_decops }, @@ -483,7 +479,7 @@ int setupkem(kem *k, dstr *d, gcipher **cx, gcipher **c, gmac **m) octet *kd; size_t n, cn, mn; ghash *h; - int rc = 0; + int rc = -1; h = GH_INIT(k->h); if (k->ops->doit(k, d, h)) diff --git a/cc-list.c b/cc-list.c new file mode 100644 index 0000000..f5aef42 --- /dev/null +++ b/cc-list.c @@ -0,0 +1,73 @@ +/* -*-c-*- + * + * $Id$ + * + * Emit lists of things in tables + * + * (c) 2004 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include "cc.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @displaylists@ --- * + * + * Arguments: @const struct listent *listtab@ = table of lists to show + * @char *const argv[]@ = list of lists to show + * + * Returns: Nonzero if anything failed. + * + * Use: Dumps the named lists, or all of them. + */ + +int displaylists(const struct listent *listtab, char *const argv[]) +{ + const struct listent *li; + int i; + int rc = 0; + + if (!argv || !*argv) { + for (li = listtab; li->name; li++) + li->list(); + } else { + for (i = 0; argv[i]; i++) { + for (li = listtab; li->name; li++) { + if (strcmp(li->name, argv[i]) == 0) { + li->list(); + goto cool; + } + } + moan("unknown list `%s'", argv[i]); + rc = 1; + cool:; + } + } + return (rc); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/cc-sig.c b/cc-sig.c index 2bfe7e9..b7ce0b7 100644 --- a/cc-sig.c +++ b/cc-sig.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: cc-sig.c,v 1.1 2004/04/17 09:58:37 mdw Exp $ + * $Id$ * * Catcrypt signatures * @@ -517,12 +517,7 @@ static const sigops eckcdsa_vrf = { /* --- The switch table --- */ -static const struct sigtab { - const char *name; - const sigops *signops; - const sigops *verifyops; - const gchash *ch; -} sigtab[] = { +const struct sigtab sigtab[] = { { "rsapkcs1", &rsap1_sig, &rsap1_vrf, &sha }, { "rsapss", &rsapss_sig, &rsapss_vrf, &sha }, { "dsa", &dsa_sig, &dsa_vrf, &sha }, diff --git a/cc-subcmd.c b/cc-subcmd.c new file mode 100644 index 0000000..9af70e1 --- /dev/null +++ b/cc-subcmd.c @@ -0,0 +1,109 @@ +/* -*-c-*- + * + * $Id$ + * + * Subcommand infrastructure + * + * (c) 2004 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include + +#include "cc.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @findcmd@ --- * + * + * Arguments: @const cmd *cmds@ = pointer to command table + * @const char *name@ = a command name + * + * Returns: Pointer to the command structure. + * + * Use: Looks up a command by name. If the command isn't found, an + * error is reported and the program is terminated. + */ + +const cmd *findcmd(const cmd *cmds, const char *name) +{ + const cmd *c, *chosen = 0; + size_t sz = strlen(name); + + for (c = cmds; c->name; c++) { + if (strncmp(name, c->name, sz) == 0) { + if (c->name[sz] == 0) { + chosen = c; + break; + } else if (chosen) + die(EXIT_FAILURE, "ambiguous command name `%s'", name); + else + chosen = c; + } + } + if (!chosen) + die(EXIT_FAILURE, "unknown command name `%s'", name); + return (chosen); +} + +/* --- @sc_help@ --- * + * + * Arguments: @const cmd *cmds@ = pointer to command table + * @FILE *fp@ = output file handle + * @char *const *argv@ = remaining arguments + * + * Returns: --- + * + * Use: Prints a help message, maybe with help about subcommands. + */ + +void sc_help(const cmd *cmds, FILE *fp, char *const *argv) +{ + const cmd *c; + + version(fp); + fputc('\n', fp); + if (!*argv) { + help_global(fp); + fputs("\n\ +The following commands are understood:\n\n", + fp); + for (c = cmds; c->name; c++) + fprintf(fp, "%s\n", c->usage); + } else { + while (*argv) { + c = findcmd(cmds, *argv); + fprintf(fp, "Usage: %s [-OPTIONS] %s\n", QUIS, c->usage); + if (c->help) { + fputc('\n', fp); + pquis(fp, c->help); + } + argv++; + if (*argv) fputc('\n', fp); + } + } +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/cc.h b/cc.h index 7c2c092..24fc205 100644 --- a/cc.h +++ b/cc.h @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: cc.h,v 1.1 2004/04/17 09:58:37 mdw Exp $ + * $Id$ * * Catcrypt common stuff * @@ -37,6 +37,7 @@ /*----- Header files ------------------------------------------------------*/ #include +#include #include @@ -67,6 +68,14 @@ typedef struct kemops { void (*destroy)(kem */*k*/); } kemops; +struct kemtab { + const char *name; + const kemops *encops; + const kemops *decops; +}; + +extern const struct kemtab kemtab[]; + /* --- Signing --- */ typedef struct sig { @@ -85,6 +94,15 @@ typedef struct sigops { void (*destroy)(sig */*s*/); } sigops; +struct sigtab { + const char *name; + const sigops *signops; + const sigops *verifyops; + const gchash *ch; +}; + +extern const struct sigtab sigtab[]; + /* --- Data encoding --- */ typedef struct enc { @@ -104,6 +122,8 @@ typedef struct encops { void (*destroy)(enc */*e*/); } encops; +extern const encops enctab[]; + /*----- Functions provided ------------------------------------------------*/ /* --- @getkem@ --- * @@ -207,6 +227,112 @@ extern enc *initenc(const encops */*eo*/, FILE */*fp*/, extern void freeenc(enc */*e*/); +/* --- @LIST(STRING, FP, END-TEST, NAME-EXPR)@ --- * + * + * Produce list of things. Requires @i@ and @w@ variables in scope. + * END-TEST and NAME-EXPR are in terms of @i@. + */ + +#define LIST(what, fp, end, name) do { \ + fputs(what ":\n ", fp); \ + w = 2; \ + for (i = 0; end; i++) { \ + if (w == 2) \ + w += strlen(name); \ + else { \ + if (strlen(name) + w > 76) { \ + fputs("\n ", fp); \ + w = 2 + strlen(name); \ + } else { \ + fputc(' ', fp); \ + w += strlen(name) + 1; \ + } \ + } \ + fputs(name, fp); \ + } \ + fputc('\n', fp); \ +} while (0) + +#define STDLISTS(LI) \ + LI("Hash functions", hash, \ + ghashtab[i], ghashtab[i]->name) \ + LI("Encryption schemes", enc, \ + gciphertab[i], gciphertab[i]->name) \ + LI("Message authentication schemes", mac, \ + gmactab[i], gmactab[i]->name) \ + LI("Elliptic curves", ec, \ + ectab[i].name, ectab[i].name) \ + LI("Diffie-Hellman groups", dh, \ + ptab[i].name, ptab[i].name) + +#define LIDECL(text, tag, test, name) \ + static void show_##tag(void); + +#define LIDEF(text, tag, test, name) \ + static void show_##tag(void) \ + { \ + unsigned i, w; \ + LIST(text, stdout, test, name); \ + } + +#define LIENT(text, tag, test, name) \ + { #tag, show_##tag }, + +struct listent { + const char *name; + void (*list)(void); +}; + +#define MAKELISTTAB(listtab, LISTS) \ + LISTS(LIDECL) \ + static const struct listent listtab[] = { \ + LISTS(LIENT) \ + { 0, 0 } \ + }; \ + LISTS(LIDEF) + +extern int displaylists(const struct listent */*listtab*/, + char *const /*argv*/[]); + +/*----- Subcommand dispatch -----------------------------------------------*/ + +typedef struct cmd { + const char *name; + int (*cmd)(int /*argc*/, char */*argv*/[]); + const char *usage; + const char *help; +} cmd; + +extern void version(FILE */*fp*/); +extern void help_global(FILE */*fp*/); + +/* --- @findcmd@ --- * + * + * Arguments: @const cmd *cmds@ = pointer to command table + * @const char *name@ = a command name + * + * Returns: Pointer to the command structure. + * + * Use: Looks up a command by name. If the command isn't found, an + * error is reported and the program is terminated. + */ + +const cmd *findcmd(const cmd */*cmds*/, const char */*name*/); + +/* --- @sc_help@ --- * + * + * Arguments: @const cmd *cmds@ = pointer to command table + * @FILE *fp@ = output file handle + * @char *const *argv@ = remaining arguments + * + * Returns: --- + * + * Use: Prints a help message, maybe with help about subcommands. + */ + +extern void sc_help(const cmd */*cmds*/, FILE */*fp*/, + char *const */*argv*/); + /*----- That's all, folks -------------------------------------------------*/ #ifdef __cplusplus diff --git a/cookie.1 b/cookie.1 new file mode 100644 index 0000000..61d9726 --- /dev/null +++ b/cookie.1 @@ -0,0 +1,269 @@ +.\" -*-nroff-*- +.de hP +.IP +.ft B +\h'-\w'\\$1\ 'u'\\$1\ \c +.ft P +.. +.ie t \{\ +. if \n(.g \{\ +. fam P +. \} +.\} +.ie t .ds o \(bu +.el .ds o o +. +.TH cookie 1 "5 June 1999" "Straylight/Edgeware" "Catacomb cryptographic library" +.SH NAME +cookie \- generate and validate cryptographic cookies +.SH SYNOPSIS +.B cookie +.RB [ \-k +.IR keyring ] +.I command +.PP +where command is one of: +.PP +.B help +.RI [ command ...] +.br +.B show +.RI [ item ...] +.br +.B generate +.RB [ \-b +.IR bits ] +.RB [ \-e +.IR expire ] +.RB [ \-k +.IR tag ] +.I data +.br +.B verify +.RB [ \-fquv ] +.RB [ \-b +.IR bits ] +.RB [ \-m +.IR bits ] +.I cookie +.RI [ data ] +.SH DESCRIPTION +The +.B cookie +program generates timestamped cryptographic cookies which can be handed +out to clients and later validated. It provides two subcommands: one to +create a new cookie, and one to ensure that a particular cookie is +valid. +.SS "Global options" +Before the command name, +.I "global options" +may be given. The following global options are supported: +.TP +.BR "\-h, \-\-help " [ \fIcommand ...] +Displays help text for +.B cookie +on standard output and exits successfullly. With command names, gives +help on each command. +.TP +.B "\-v, \-\-version" +Displays +.BR cookie 's +version number on standard output and exits successfullly. +.TP +.B "\-u, \-\-usage" +Displays an unhelpfully terse usage summary on standard output and exits +successfully. +.TP +.BI "\-k, \-\-keyring " file +Use +.I file +as the keyring file rather than the default, which is the file named +.B keyring +in the current directory. See +.BR key (1) +and +.BR keyring (5) +for more details about keyring files. +.SH "KEY SETUP" +The message authentication algorithm used to tag and verify cookies is +described by an attribute on the key, or its type. If the key has a +.B mac +attribute, then that is taken to be the name of the MAC algorithm to +use; otherwise the key must have the type +.BR cookie- \c +.IR mac . +.SH "COMMAND REFERENCE" +.SS help +The +.B help +command behaves exactly as the +.B \-\-help +option. With no arguments, it shows an overview of +.BR cookie 's +options; with arguments, it describes the named subcommands. +.SS show +The +.B show +command prints various lists of tokens understood by +.BR cookie . +With no arguments, it prints all of the lists; with arguments, it prints +just the named lists, in order. The recognized lists can be enumerated +using the +.VS +cookie show list +.VE +command. The lists are as follows. +.TP +.B list +The lists which can be enumerated by the +.B show +command. +.TP +.B mac +The message authentication algorithms which can be used in a key's +.B mac +attribute. +.SS generate +The +.B generate +command creates a new cookie. The command understands the following +options: +.TP +.BI "\-b, \-\-bits " bits +Specifies the size of the cryptographic token to generate. The default +token size is 32 bits. +.TP +.BI "\-e, \-\-expire " expire +The expiry date for the cookie. This may be the string +.RB ` forever ' +if the cookie should never expire, or any date acceptable to the +.BR getdate (3) +library function, e.g., +.RB ` "+2 weeks" '. +It is an error to ask for a non-expiring cookie to be created using a +key which will expire. +.TP +.BI "\-k, \-\-key " tag +Use the key named +.I tag +to authenticate the cookie; the default key is named +.BR cookie . +.PP +If a +.I data +argument is supplied, then an identical argument must be supplied to the +.B verify +command if the cookie is to be accepted as valid. The data will usually +be some way of identifying the cookie's recipient, e.g., an email +address. +.PP +The generated cookie is written to standard output, followed by a +newline. Cookies are not architecture-specific. +.SS verify +The +.B verify +command checks a cookie for validity. It accepts the following +options: +.TP +.BI "\-b, \-\-bits " bits +Only accept a cookie with a cryptographic token exactly +.I bits +bits long. +.TP +.BI "\-m, \-\-min\-bits " bits +Only accept a cookie with a cryptographic token at least +.I bits +bits long. The default is to accept any token with size 32 bits or +more. +.TP +.B "\-f, \-\-forever" +Allow non-expiring cookies. Without this option, cookies which never +expire are automatically rejected by +.BR cookie . +.TP +.B "\-u, \-\-utc" +Display expiry time as UTC rather than using the local time zone. +.TP +.B "\-q, \-\-quiet" +Make +.B cookie +more quiet. Repeat for a greater effect. +.TP +.B "\-v, \-\-verbose" +Make +.B cookie +more verbose. Repeat for a greater effect. +.PP +The argument +.I cookie +is the cookie to check. The cookie may have whitespace before, after +or within it. +.PP +If +.I data +is specified, it must exactly match the data passed to +.B generate +if the cookie is to be accepted. +.PP +Each line of text emitted by the +.B verify +command begins with a status indicator, which is one of the following: +.TP +.B INFO +This line provides possibly useful information about the cookie. +.B INFO +lines are displayed while +.B cookie +is working, before it's made its mind up about whether to reject the +cookie. The remainder of the line contains the information gleaned. +.TP +.B FAIL +The cookie is invalid. The remainder of the line is a human-readable +error message giving a reason for rejecting the cookie. +.TP +.B OK +The cookie is valid. +.PP +The +.B \-q +and +.B \-v +flags control the amount of output that +.B cookie +generates. By default, only +.B OK +and +.B FAIL +lines are generated. One +.B \-v +option shows the cookie's keyid and expiry time. Two +.B \-v +options also show the authentication token width. A +.B \-q +option suppresses all output, or cancels out a previous +.BR \-v . +.PP +Regardless of how much output is generated, +.B cookie +exits with return code 0 if the cookie was accepted and return code 1 if +validation failed for some reason. +.SH "COOKIE FORMAT" +Cookies are Base64-encoded binary objects. The binary data consists of: +.hP \*o +A 32-bit keyid referring to the key with which the cookie was created. +.hP \*o +An expiry date, expressed as a 64-bit integer in the format returned by +the +.BR time (2) +system call. +.hP \*o +The `authentication token', which is the leftmost bits of a MAC over the +keyid and date and any extra data supplied to the +.B generate +command +.PP +The keyid and expiry date are stored in network (big-endian) byte order. +.SH "SEE ALSO" +.BR key (1). +.SH "AUTHOR" +Mark Wooding, diff --git a/cookie.c b/cookie.c new file mode 100644 index 0000000..9b32389 --- /dev/null +++ b/cookie.c @@ -0,0 +1,692 @@ +/* -*-c-*- + * + * $Id$ + * + * Generate and validate cryptographic cookies + * + * (c) 1999 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Catacomb; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "cc.h" +#include "key.h" +#include "gmac.h" +#include "getdate.h" + +/*----- Handy global state ------------------------------------------------*/ + +static const char *keyfile = "keyring"; + +/*----- Cookie format -----------------------------------------------------*/ + +/* --- Cookie header structure (unpacked) --- */ + +typedef struct cookie { + uint32 k; + time_t exp; +} cookie; + +/* --- Size of a cookie header (packed) --- */ + +#define COOKIE_SZ (4 + 8) + +/* --- @COOKIE_PACK@ --- * + * + * Arguments: @p@ = pointer to destination buffer + * @c@ = pointer to source cookie header block + * + * Use: Packs a cookie header into an octet buffer in a machine- + * independent way. + */ + +#define COOKIE_PACK(p, c) do { \ + octet *_p = (octet *)(p); \ + const cookie *_c = (c); \ + STORE32(_p + 0, _c->k); \ + STORE32(_p + 4, ((_c->exp & ~MASK32) >> 16) >> 16); \ + STORE32(_p + 8, _c->exp); \ +} while (0) + +/* --- @COOKIE_UNPACK@ --- * + * + * Arguments: @c@ = pointer to destination cookie header + * @p@ = pointer to source buffer + * + * Use: Unpacks a cookie header from an octet buffer into a + * machine-specific but comprehensible structure. + */ + +#define COOKIE_UNPACK(c, p) do { \ + cookie *_c = (c); \ + const octet *_p = (const octet *)(p); \ + _c->k = LOAD32(_p + 0); \ + _c->exp = ((time_t)(((LOAD32(_p + 4) << 16) << 16) & ~MASK32) | \ + (time_t)LOAD32(_p + 8)); \ +} while (0) + +/*----- Useful shared functions -------------------------------------------*/ + +/* --- @doopen@ --- * + * + * Arguments: @key_file *f@ = pointer to key file block + * @unsigned how@ = method to open file with + * + * Returns: --- + * + * Use: Opens a key file and handles errors by panicking + * appropriately. + */ + +static void doopen(key_file *f, unsigned how) +{ + if (key_open(f, keyfile, how, key_moan, 0)) { + die(EXIT_FAILURE, "couldn't open file `%s': %s", + keyfile, strerror(errno)); + } +} + +/* --- @doclose@ --- * + * + * Arguments: @key_file *f@ = pointer to key file block + * + * Returns: --- + * + * Use: Closes a key file and handles errors by panicking + * appropriately. + */ + +static void doclose(key_file *f) +{ + switch (key_close(f)) { + case KWRITE_FAIL: + die(EXIT_FAILURE, "couldn't write file `%s': %s", + keyfile, strerror(errno)); + case KWRITE_BROKEN: + die(EXIT_FAILURE, "keyring file `%s' broken: %s (repair manually)", + keyfile, strerror(errno)); + } +} + +/* --- @getmac@ --- * + * + * Arguments: @key *k@ = key to use + * @const char *app@ = application name + * + * Returns: The MAC to use. + * + * Use: Finds the right MAC for the given key. + */ + +static gmac *getmac(key *k, const char *app) +{ + dstr t = DSTR_INIT; + dstr d = DSTR_INIT; + char *p = 0; + const char *q; + size_t n; + key_bin kb; + key_packdef kp; + const gcmac *cm; + int e; + gmac *m; + + /* --- Set up --- */ + + key_fulltag(k, &t); + + /* --- Pick out the right MAC --- */ + + n = strlen(app); + if ((q = key_getattr(0, k, "mac")) != 0) { + dstr_puts(&d, q); + p = d.buf; + } else if (strncmp(k->type, app, n) == 0 && k->type[n] == '-') { + dstr_puts(&d, k->type); + p = d.buf + n + 1; + } else + die(EXIT_FAILURE, "no MAC algorithm for key `%s'", t.buf); + if ((cm = gmac_byname(p)) == 0) { + die(EXIT_FAILURE, "MAC algorithm `%s' not found in key `%s'", + p, t.buf); + } + + /* --- Unlock the key --- */ + + kp.kd.e = KENC_BINARY; + kp.p = &kb; + if ((e = key_unpack(&kp, &k->k, &t)) != 0) { + die(EXIT_FAILURE, "error unpacking key `%s': %s", + t.buf, key_strerror(e)); + } + + /* --- Make the MAC object --- */ + + if (keysz(kb.sz, cm->keysz) != kb.sz) + die(EXIT_FAILURE, "key %s has bad length (%lu) for MAC %s", + t.buf, (unsigned long)kb.sz, cm->name); + m = cm->key(kb.k, kb.sz); + key_unpackdone(&kp); + return (m); +} + +/*----- Command implementation --------------------------------------------*/ + +/* --- @cmd_gen@ --- */ + +static int cmd_gen(int argc, char *argv[]) +{ + key_file f; + key *k; + gmac *m; + ghash *h; + const char *tag = "cookie"; + int err; + cookie c = { 0, KEXP_EXPIRE }; + unsigned fl = 0; + int bits = 32; + const octet *t; + dstr d = DSTR_INIT; + octet buf[COOKIE_SZ]; + base64_ctx b; + + /* --- Various useful flag bits --- */ + +#define f_bogus 1u + + /* --- Parse options for the subcommand --- */ + + for (;;) { + static struct option opt[] = { + { "bits", OPTF_ARGREQ, 0, 'b' }, + { "expire", OPTF_ARGREQ, 0, 'e' }, + { "key", OPTF_ARGREQ, 0, 'k' }, + { 0, 0, 0, 0 } + }; + int i = mdwopt(argc, argv, "+b:e:i:t:", opt, 0, 0, 0); + if (i < 0) + break; + + /* --- Handle the various options --- */ + + switch (i) { + + /* --- Fetch a size in bits --- */ + + case 'b': + if (!(bits = atoi(optarg)) || bits % 8) + die(EXIT_FAILURE, "bad number of bits: `%s'", optarg); + break; + + /* --- Fetch an expiry time --- */ + + case 'e': + if (strcmp(optarg, "forever") == 0) + c.exp = KEXP_FOREVER; + else if ((c.exp = get_date(optarg, 0)) == -1) + die(EXIT_FAILURE, "bad expiry date: `%s'", optarg); + break; + + /* --- Fetch a key type --- */ + + case 'k': + tag = optarg; + break; + + /* --- Other things are bogus --- */ + + default: + fl |= f_bogus; + break; + } + } + + /* --- Various sorts of bogosity --- */ + + if (fl & f_bogus || optind + 1 < argc) + die(EXIT_FAILURE, + "Usage: generate [-b BITS] [-e TIME] [-k TAG] [DATA]"); + + /* --- Choose a default expiry time --- */ + + if (c.exp == KEXP_EXPIRE) + c.exp = time(0) + 7 * 24 * 60 * 60; + + /* --- Open the key file and get the key --- */ + + doopen(&f, KOPEN_WRITE); + if ((k = key_bytag(&f, tag)) == 0) { + die(EXIT_FAILURE, "no key with tag `%s' in keyring `%s'", + tag, keyfile); + } + + c.k = k->id; + if ((err = key_used(&f, k, c.exp)) != 0) + die(EXIT_FAILURE, "can't generate cookie: %s", key_strerror(err)); + m = getmac(k, "cookie"); + if (bits/8 > GM_CLASS(m)->hashsz) { + die(EXIT_FAILURE, "inapproriate bit length for `%s' MACs", + GM_CLASS(m)->name); + } + + /* --- Store and MAC the cookie --- */ + + COOKIE_PACK(buf, &c); + + h = GM_INIT(m); + GH_HASH(h, buf, sizeof(buf)); + if (argv[optind]) + GH_HASH(h, argv[optind], strlen(argv[optind])); + t = GH_DONE(h, 0); + + /* --- Encode and emit the finished cookie --- */ + + base64_init(&b); + b.indent = ""; + base64_encode(&b, buf, sizeof(buf), &d); + base64_encode(&b, t, bits/8, &d); + base64_encode(&b, 0, 0, &d); + DWRITE(&d, stdout); + fputc('\n', stdout); + DDESTROY(&d); + GH_DESTROY(h); + GM_DESTROY(m); + + doclose(&f); + return (0); + +#undef f_bogus +} + +/* --- @cmd_verify@ --- */ + +static int cmd_verify(int argc, char *argv[]) +{ + key_file f; + dstr d = DSTR_INIT; + unsigned fl = 0; + int bits = -1, minbits = 32; + int v = 1; + base64_ctx b; + gmac *m; + ghash *h; + cookie c; + key *k; + int cbits; + const octet *t; + time_t now = time(0); + + /* --- Various useful flag bits --- */ + +#define f_bogus 1u +#define f_forever 2u +#define f_utc 4u + + /* --- Parse options for the subcommand --- */ + + for (;;) { + static struct option opt[] = { + { "bits", OPTF_ARGREQ, 0, 'b' }, + { "min-bits", OPTF_ARGREQ, 0, 'm' }, + { "forever", 0, 0, 'f' }, + { "quiet", 0, 0, 'q' }, + { "verbose", 0, 0, 'v' }, + { "utc", 0, 0, 'u' }, + { 0, 0, 0, 0 } + }; + int i = mdwopt(argc, argv, "+b:m:fqvu", opt, 0, 0, 0); + if (i < 0) + break; + + /* --- Handle the various options --- */ + + switch (i) { + + /* --- Fetch a size in bits --- */ + + case 'b': + if (!(bits = atoi(optarg)) || bits % 8) + die(EXIT_FAILURE, "bad number of bits: `%s'", optarg); + break; + case 'm': + if (!(minbits = atoi(optarg)) || minbits % 8) + die(EXIT_FAILURE, "bad number of bits: `%s'", optarg); + break; + + /* --- Miscellaneous flags --- */ + + case 'f': + fl |= f_forever; + break; + case 'u': + fl |= f_utc; + break; + case 'q': + if (v > 0) v--; + break; + case 'v': + v++; + break; + + /* --- Other things are bogus --- */ + + default: + fl |= f_bogus; + break; + } + } + + /* --- Various sorts of bogosity --- */ + + if (fl & f_bogus || optind == argc || optind + 2 < argc) { + die(EXIT_FAILURE, + "Usage: verify [-fuqv] [-b BITS] [-m BITS] COOKIE [DATA]"); + } + doopen(&f, KOPEN_READ); + + /* --- Decode the base64 wrapping --- */ + + base64_init(&b); + base64_decode(&b, argv[optind], strlen(argv[optind]), &d); + base64_decode(&b, 0, 0, &d); + + if (d.len < COOKIE_SZ + 1) { + if (v) printf("FAIL cookie too small\n"); + goto fail; + } + + /* --- Extract the relevant details --- */ + + COOKIE_UNPACK(&c, d.buf); + + if (v > 1) { + char buf[64]; + if (c.exp == KEXP_FOREVER) + strcpy(buf, "forever"); + else { + struct tm *tm; + const char *fmt; + + if (fl & f_utc) { + tm = gmtime(&c.exp); + fmt = "%Y-%m-%d %H:%M:%S UTC"; + } else { + tm = localtime(&c.exp); + fmt = "%Y-%m-%d %H:%M:%S %Z"; + } + strftime(buf, sizeof(buf), fmt, tm); + } + printf("INFO keyid = %08lx; expiry = %s\n", (unsigned long)c.k, buf); + } + + /* --- Check the authentication token width --- */ + + cbits = (d.len - COOKIE_SZ) * 8; + if (v > 2) printf("INFO authentication token width = %i bits\n", cbits); + if (bits == -1) { + if (cbits < minbits) { + if (v) printf("FAIL authentication token too narrow\n"); + goto fail; + } + } else { + if (cbits != bits) { + if (v) printf("FAIL authentication token width doesn't match\n"); + goto fail; + } + } + /* --- Get the key --- */ + + if ((k = key_byid(&f, c.k)) == 0) { + if (v) printf("FAIL keyid %08lx unavailable\n", (unsigned long)c.k); + goto fail; + } + + /* --- Check that the cookie authenticates OK --- */ + + m = getmac(k, "cookie"); + h = GM_INIT(m); + GH_HASH(h, d.buf, COOKIE_SZ); + if (argv[optind + 1]) + GH_HASH(h, argv[optind + 1], strlen(argv[optind + 1])); + t = GH_DONE(h, 0); + + if (memcmp(t, d.buf + COOKIE_SZ, cbits / 8) != 0) { + if (v) printf("FAIL bad authentication token\n"); + goto fail; + } + + /* --- See whether the cookie has expired --- */ + + if (c.exp == KEXP_FOREVER) { + if (!(fl & f_forever)) { + if (v) printf("FAIL forever cookies not allowed\n"); + goto fail; + } + if (k->exp != KEXP_FOREVER) { + if (v) printf("FAIL cookie lasts forever but key will expire\n"); + goto fail; + } + } else if (c.exp < now) { + if (v) printf("FAIL cookie has expired\n"); + goto fail; + } + + if (v) printf("OK\n"); + key_close(&f); + GM_DESTROY(m); + GH_DESTROY(h); + dstr_destroy(&d); + return (0); + +fail: + key_close(&f); + dstr_destroy(&d); + return (1); + +#undef f_bogus +#undef f_forever +#undef f_utc +} + +/*----- Main command table ------------------------------------------------*/ + +static int cmd_help(int, char **); + +#define LISTS(LI) \ + LI("Lists", list, \ + listtab[i].name, listtab[i].name) \ + LI("Message authentication algorithms", mac, \ + gmactab[i], gmactab[i]->name) + +MAKELISTTAB(listtab, LISTS) + +static int cmd_show(int argc, char *argv[]) +{ + return (displaylists(listtab, argv + 1)); +} + +static cmd cmds[] = { + { "help", cmd_help, "help [COMMAND...]" }, + { "show", cmd_show, "show [ITEM...]" }, + { "generate", cmd_gen, + "generate [-b BITS] [-e TIME] [-k TAG] [DATA]", "\ +Options:\n\ +\n\ +-b, --bits=N Use an N-bit token in the cookie.\n\ +-e, --expire=TIME Make the cookie expire after TIME.\n\ +-k, --key=TAG Use key TAG to create the token.\n\ +" }, + { "verify", cmd_verify, + "verify [-fuqv] [-b BITS] [-m BITS] COOKIE [DATA]", "\ +Options:\n\ +\n\ +-b, --bits=N Accept tokens exactly N bits long only.\n\ +-m, --min-bits=N Accept tokens N bits long or more.\n\ +-f, --forever Accept cookies which never expire.\n\ +-u, --utc Output cookie expiry dates in UTC.\n\ +-q, --quiet Produce less output while checking cookies.\n\ +-v, --verbose Produce more output while checking cookies.\n\ +" }, + { 0, 0, 0 } +}; + +static int cmd_help(int argc, char *argv[]) +{ + sc_help(cmds, stdout, argv + 1); + return (0); +} + +/*----- Main code ---------------------------------------------------------*/ + +/* --- Helpful GNUy functions --- */ + +static void usage(FILE *fp) +{ + fprintf(fp, "Usage: %s [-k KEYRING] COMMAND [ARGS]\n", QUIS); +} + +void version(FILE *fp) +{ + fprintf(fp, "%s, Catacomb version " VERSION "\n", QUIS); +} + +void help_global(FILE *fp) +{ + usage(fp); + fputs("\n\ +Generates and validates cryptographic cookies. Command line options\n\ +recognized are:\n\ +\n\ +-h, --help [COMMAND] Display this help text (or help for COMMAND).\n\ +-v, --version Display version number.\n\ +-u, --usage Display short usage summary.\n\ +\n\ +-k, --key-file=FILE Read and write keys in FILE.\n", + fp); +} + +/* --- @main@ --- * + * + * Arguments: @int argc@ = number of command line arguments + * @char *argv[]@ = array of arguments + * + * Returns: Zero if OK, nonzero if not. + * + * Use: Generates and validates cryptographic cookies. + */ + +int main(int argc, char *argv[]) +{ + unsigned f = 0; + +#define f_bogus 1u +#define f_forever 2u + + /* --- Initialize the library --- */ + + ego(argv[0]); + sub_init(); + + /* --- Options parsing --- */ + + for (;;) { + static struct option opt[] = { + + /* --- Standard GNUy help options --- */ + + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "usage", 0, 0, 'u' }, + + /* --- Actual relevant options --- */ + + { "keyring", OPTF_ARGREQ, 0, 'k' }, + + /* --- Magic terminator --- */ + + { 0, 0, 0, 0 } + }; + int i = mdwopt(argc, argv, "+hvu k:", opt, 0, 0, 0); + + if (i < 0) + break; + switch (i) { + + /* --- Helpful GNUs --- */ + + case 'u': + usage(stdout); + exit(0); + case 'v': + version(stdout); + exit(0); + case 'h': + sc_help(cmds, stdout, argv + optind); + exit(0); + + /* --- Real genuine useful options --- */ + + case 'k': + keyfile = optarg; + break; + + /* --- Bogus things --- */ + + default: + f |= f_bogus; + break; + } + } + + if ((f & f_bogus) || optind == argc) { + usage(stderr); + exit(EXIT_FAILURE); + } + + /* --- Dispatch to appropriate command handler --- */ + + argc -= optind; + argv += optind; + optind = 0; + return (findcmd(cmds, argv[0])->cmd(argc, argv)); + +#undef f_bogus +#undef f_forever +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/dsig.1 b/dsig.1 new file mode 100644 index 0000000..0d7c83b --- /dev/null +++ b/dsig.1 @@ -0,0 +1,512 @@ +.\" -*-nroff-*- +.de VS +.sp 1 +.RS +.nf +.ft B +.. +.de VE +.ft R +.fi +.RE +.sp 1 +.. +.ie t \{\ +. if \n(.g \{\ +. fam P +. \} +.\} +.de hP +.IP +.ft B +\h'-\w'\\$1\ 'u'\\$1\ \c +.ft P +.. +.ie t .ds o \(bu +.el .ds o o +.TH dsig 1 "30 September 2004" "Straylight/Edgeware" "Catacomb cryptographic library" +.SH NAME +dsig \- compute and verify signatures on collections of files +.SH SYNOPSIS +.B dsig +.RB [ \-k +.IR keyring ] +.I command +.PP +where +.I command +is one of: +.PP +.B help +.RI [ command ...] +.br +.B show +.RI [ item ...] +.br +.B sign +.RB [ \-0bqv ] +.RB [ \-c +.IR comment ] +.RB [ \-k +.IR tag ] +.RB [ \-e +.IR expire ] +.br +\h'8n' +.RB [ \-f +.IR file ] +.RB [ \-o +.IR output ] +.br +.B verify +.RB [ \-qv ] +.RI [ file ] +.SH DESCRIPTION +The +.B dsig +command signs and verifies signatures on a collection of files. It +provides a number of subcommands, by which the various operations may be +carried out. +.SS "Global options" +Before the command name, +.I "global options" +may be given. The following global options are supported: +.TP +.BR "\-h, \-\-help " [ \fIcommand ...] +Writes a brief summary of +.BR dsig 's +various options to standard output, and returns a successful exit +status. With command names, gives help on those commands. +.TP +.B "\-v, \-\-version" +Writes the program's version number to standard output, and returns a +successful exit status. +.TP +.B "\-u, \-\-usage" +Writes a very terse command line summary to standard output, and returns +a successful exit status. +.TP +.BI "\-k, \-\-keyring " file +Names the keyring file which +.B key +is to process. The default keyring, used if this option doesn't specify +one, is the file named +.B keyring +in the current directory. See +.BR key (1) +and +.BR keyring (5) +for more details about keyring files. +.SH "KEY SETUP" +A +.I sigalgspec +has the form +.IR sig \c +.RB [ / \c +.IR hash ]. +If a +.B sig +attribute is present on the key, then it must have this form; otherwise, +the key's type must have the form +.BI dsig- \c +.IR sigalgspec . +Algorithm selections are taken from appropriately-named attributes, or, +failing that, from the +.IR sigalgspec . +.PP +The signature algorithm is chosen according to the setting of +.I sig +as follows. Run +.B dsig show sig +for a list of supported signature algorithms. +.TP +.B rsapkcs1 +This is almost the same as the RSASSA-PKCS1-v1_5 algorithm described in +RFC3447; the difference is that the hash is left bare rather than being +wrapped in a DER-encoded +.B DigestInfo +structure. This doesn't affect security since the key can only be used +with the one hash function anyway, and dropping the DER wrapping permits +rapid adoption of new hash functions. Regardless, use of this algorithm +is not recommended, since the padding method has been shown vulnerable +to attack. Use the +.B rsa +algorithm of the +.B key add +command (see +.BR key (1)) +to generate the key. +.TP +.B rsapss +This is the RSASSA-PSS algorithm described in RFC3447. It is the +preferred RSA-based signature scheme. Use the +.B rsa +algorithm of the +.B key add +command (see +.BR key (1)) +to generate the key. +.TP +.B dsa +This is the DSA algorithm described in FIPS180-1 and FIPS180-2. Use the +.B dsa +algorithm of the +.B key add +command (see +.BR key (1)) +to generate the key. +.TP +.B ecdsa +This is the ECDSA algorithm described in ANSI X9.62 and FIPS180-2. Use +the +.B ec +algorithm of the +.B key add +command (see +.BR key (1)) +to generate the key. +.TP +.B kcdsa +This is the revised KCDSA (Korean Certificate-based Digital Signature +Algorithm) described in +.I The Revised Version of KCDSA +.RB ( http://dasan.sejong.ac.kr/~chlim/pub/kcdsa1.ps ). +Use the +.B dh +algorithm of the +.B key add +command with the +.B \-LS +options (see +.BR key (1)) +to generate the key. +.TP +.B eckcdsa +This is an unofficial elliptic-curve analogue of the KCDSA algorithm. +Use the +.B ec +algorithm of the +.B key add +command (see +.BR key (1)) +to generate the key. +.PP +As well as the signature algorithm itself, a hash function is used. +This is taken from the +.B hash +attribute on the key, or, failing that, from the +.I hash +specified in the +.IR sigalgspec , +or, if that is absent, determined by the signature algorithm as follows. +.hP \*o +For +.BR rsapkcs1 , +.BR rsapss , +.BR dsa , +and +.BR ecdsa , +the default hash function is +.BR sha . +.hP \*o +For +.BR kcdsa +and +.BR eckcdsa , +the default hash function is +.BR has160 . +.PP +Run +.B dsig show hash +for a list of supported hash functions. +.SH "COMMAND REFERENCE" +.SS help +The +.B help +command behaves exactly as the +.B \-\-help +option. With no arguments, it shows an overview of +.BR dsig 's +options; with arguments, it describes the named subcommands. +.SS show +The +.B show +command prints various lists of tokens understood by +.BR dsig . +With no arguments, it prints all of the lists; with arguments, it prints +just the named lists, in order. The recognized lists can be enumerated +using the +.VS +dsig show list +.VE +command. The lists are as follows. +.TP +.B list +The lists which can be enumerated by the +.B show +command. +.TP +.B sig +The signature algorithms which can be used in a key's +.B sig +attribute. +.TP +.B hash +The hash functions which can be used in a key's +.B hash +attribute. +.SS sign +The +.B sign +command creates a signature for a collection of files. The default +behaviour is to read a list of whitespace-separated file names (see +below for the precise format) from standard input and write the +an output file, containing hashes of the files and a digital signature +made by the key +.B dsig +in the current keyring, to standard output, in plain text with binary +values Base64-encoded. It is intended to be used in conjunction with +.BR find (1). +This behaviour can be modified by specifying command-line options. +.TP +.B "\-0, \-\-null" +Read null-terminated filenames, rather than whitespace-separated names. +This is the recommended mode of operation if you have a +.BR find (1) +which understands the +.B \-print0 +option. +.TP +.B "\-b, \-\-binary" +Produce output in raw binary rather than the textual output. This isn't +a useful thing to do unless you're trying to debug +.BR dsig . +.TP +.B "\-v, \-\-verbose" +Makes +.B dsig +more verbose. At present, this just means that it'll print the hashes +of files that it comes across in hex. (Use +.BR hashsum (1) +if this is the output you actually wanted.) +.TP +.B "\-q, \-\-quiet" +Makes +.B dsig +less verbose. +.TP +.BI "\-c, \-\-comment " string +Writes +.I string +as a comment in the output file. The comment's integrity is protected +by the signature. +.TP +.BI "\-f, \-\-file " name +Read filenames from +.I name +instead of from standard input. +.TP +.BI "\-o, \-\-output " name +Write output to +.I name +instead of to standard output. +.TP +.BI "\-k, \-\-key " tag +Use the key named +.I tag +rather than the default +.BR dsig . +.TP +.BI "\-e, \-\-expire " date +Set the signature to expire at +.IR date . +The default is to expire 28 days from creation. Use +.B forever +to make the signature not expire. +.PP +The whitespace-separated format for filenames allows quoting and +escaping of strange characters. The backslash +.RB ` \e ' +can be used to escape whitespace, quotes, or other special characters +(including itself), and to represent special characters using the +standard C escape sequences +.RB ` \ea ', +.RB ` \eb ', +.RB ` \ef ', +.RB ` \en ', +.RB ` \et ', +and +.RB ` \eb '. +A filename can be quoted in +.BR ` ... ', +.BR ' ... ' +or +.BR """" ... """". +Whitespace within quotes is part of the filename. The quotes must be at +the beginning and end of the name. +.SS verify +The +.B verify +command will verify signatures made by the +.B sign +command. With no arguments, it expects to read a text-format signature +file from standard input; with an argument, it examines the file it +names to see whether it's text or binary. +.PP +Command-line options provided are: +.TP +.B "\-v, \-\-verbose" +Produce more informational output. The default verbosity level is 1. +.TP +.B "\-q, \-\-quiet" +Produce less information output. +.PP +Output is written to standard output in a machine-readable format. +Formatting errors cause the program to write a diagnostic to standard +error and exit nonzero as usual. Lines begin with a keyword: +.TP +.BI "FAIL " reason +An error prevented verification. +.TP +.BI "BAD " reason +The signature is bad: some file had the wrong hash or the signature is +invalid. +.TP +.BI "WARN " reason +.B dsig +encountered a situation which may or may not invalidate the signature. +.TP +.BI "OK " message +The signature verified correctly. +.TP +.BI "INFO " note +Any other information. +.PP +The information written at the various verbosity levels is as follows. +.hP 0. +No output. Watch the exit status. +.B dsig +exits zero if the signature was good. +.hP 1. +All +.BR OK , +.B FAIL +and +.B WARN +messages are printed. +.hP 2. +As for level 1; also +.B BAD +messages are printed describing reasons why the signature verification +failed, and an +.B INFO +message is printed showing the signature file's comment if any. +.hP 3. +As for level 2; also +.B INFO +messages are shown listing the signing program's identification string, +the signing key, the signature and expiry dates, and actual signature +verification. +.hP 4. +As for level 3; also +.B INFO +messages are printed for each file covered, showing its name and hash. +.SH "OUTPUT FORMAT" +There are two output formats: textual and binary. The hash used in the +digital signature is always computed on the +.I binary +version of the data, regardless of the external representation. +.SS "Textual format" +Within the file, whitespace and comments between strings are ignored. A +comment begins with a hash +.RB (` # ') +and extends until the next newline. +.PP +Strings are either quoted or whitespace-delimited. A string may be +quoted by +.BR ` ... ', +.BR ' ... ' +or +.BR """" ... """". +The end-quote character can be backslash-escaped within the string. An +occurrence of the unescaped end-quote character terminates the string. +A whitespace-delimited string is terminated by any unescaped whitespace +character. The C-language escape sequences +.RB ` \ea ', +.RB ` \eb ', +.RB ` \ef ', +.RB ` \en ', +.RB ` \et ', +and +.RB ` \eb ' +are recognized within either kind of string. +.PP +Blocks within the file consist of sequences of strings. The first +string is a +.I tag +\(en a simple string ending in a colon +.RB (` : ') +\(en which describes the format of the remaining strings. +.SS "Binary format" +The file consists of a sequence of blocks, each of which begins with a +tag byte. The format of the test of the block depends on the tag. +Strings are null-terminated; all integers are in network byte order. +.PP +A binary file always begins with an ident block, which has a tag of 0. +.SS "Block types" +The following block types are known. They must appear in the order +given, and except where noted must appear exactly once each. +.TP +.BR "ident: " (0) +Identification string of the generating program. +.BR "keyid: " (1) +The signing key's id, as eight hex digits (text) or a 32-bit integer +(binary). +.TP +.BR "comment: " (2) +The comment string set with the +.B \-c +option to the +.B sign +command. This block need not appear. +.TP +.BR "date: " (3) +The date the signature was made. In a text file, this has the form +.IB yyyy-mm-dd +.IB hh:mm:ss +.IR timezone ; +in a binary file, it's a 64-bit integer representing the POSIX time. +.TP +.BR "expires: " (4) +The expiry time of the signature, expressed as for +.BR date: . +A non-expiring signature is represented by the string +.B forever +in text files, or all-bits-set in binary. +.TP +.BR "file: " (5) +A file hash. In text, this is two strings which are the Base-64-encoded +hash and the file name; in binary, this is a 16-bit hash length, the raw +hash, and the null-terminated filename. There can be any number of +.B file: +blocks. +.TP +.BR "signature: " (6) +The signature. In text, this is the Base-64-encoded signature; in +binary, it is a 16-bit length followed by the binary signature. +.PP +The signature covers the +.I binary +representations of the file's +.BR date: , +.B expires: +and +.B file: +blocks. +.SH "SEE ALSO" +.BR key (1), +.BR hashsum (1), +.BR catcrypt (1), +.BR keyring (5). +.SH AUTHOR +Mark Wooding, diff --git a/dsig.c b/dsig.c index 2aeb9ef..c45d204 100644 --- a/dsig.c +++ b/dsig.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: dsig.c,v 1.12 2004/04/17 09:58:37 mdw Exp $ + * $Id$ * * Verify signatures on distribuitions of files * @@ -485,8 +485,13 @@ static void blob(block *b, dstr *d) case T_DATE: case T_EXPIRE: DENSURE(d, 8); - STORE32(d->buf + d->len, ((b->t & ~MASK32) >> 16) >> 16); - STORE32(d->buf + d->len + 4, b->t); + if (b->t == KEXP_FOREVER) { + STORE32(d->buf + d->len, 0xffffffff); + STORE32(d->buf + d->len + 4, 0xffffffff); + } else { + STORE32(d->buf + d->len, ((b->t & ~MASK32) >> 16) >> 16); + STORE32(d->buf + d->len + 4, b->t); + } d->len += 8; break; case T_KEYID: @@ -737,7 +742,7 @@ static int sign(int argc, char *argv[]) } } if (optind != argc || (f & f_bogus)) - die(EXIT_FAILURE, "Usage: sign [-options]"); + die(EXIT_FAILURE, "Usage: sign [-OPTIONS]"); /* --- Locate the signing key --- */ @@ -924,7 +929,7 @@ static int verify(int argc, char *argv[]) argc -= optind; argv += optind; if ((f & f_bogus) || argc > 1) - die(EXIT_FAILURE, "Usage: verify [-qv] [file]"); + die(EXIT_FAILURE, "Usage: verify [-qv] [FILE]"); /* --- Open the key file, and start reading the input file --- */ @@ -1011,7 +1016,7 @@ static int verify(int argc, char *argv[]) break; case T_EXPIRE: { time_t now = time(0); - if (b.t < now) { + if (b.t != KEXP_FOREVER && b.t < now) { if (verb > 1) puts("BAD signature has expired"); f |= f_bogus; @@ -1088,16 +1093,29 @@ done: /*----- Main code ---------------------------------------------------------*/ -typedef struct cmd { - const char *name; - int (*func)(int /*argc*/, char */*argv*/[]); - const char *usage; - const char *help; -} cmd; +#define LISTS(LI) \ + LI("Lists", list, \ + listtab[i].name, listtab[i].name) \ + LI("Signature schemes", sig, \ + sigtab[i].name, sigtab[i].name) \ + LI("Hash functions", hash, \ + ghashtab[i], ghashtab[i]->name) + +MAKELISTTAB(listtab, LISTS) + +int cmd_show(int argc, char *argv[]) +{ + return (displaylists(listtab, argv + 1)); +} + +static int cmd_help(int, char **); static cmd cmdtab[] = { + { "help", cmd_help, "help [COMMAND...]" }, + { "show", cmd_show, "show [ITEM...]" }, { "sign", sign, - "sign [-0bqv] [-c comment] [-k tag] [-e expire] [-f file] [-o output]", + "sign [-0bqv] [-c COMMENT] [-k TAG] [-e EXPIRE]\n\t\ +[-f FILE] [-o OUTPUT]", "\ Options:\n\ \n\ @@ -1112,7 +1130,7 @@ Options:\n\ -e, --expire=TIME The signature should expire after TIME.\n\ " }, { "verify", verify, - "verify [-qv] [file]", "\ + "verify [-qv] [FILE]", "\ Options:\n\ \n\ -q, --quiet Produce fewer messages while working.\n\ @@ -1121,68 +1139,36 @@ Options:\n\ { 0, 0, 0 } }; -/* --- @findcmd@ --- * - * - * Arguments: @const char *name@ = a command name - * - * Returns: Pointer to the command structure. - * - * Use: Looks up a command by name. If the command isn't found, an - * error is reported and the program is terminated. - */ - -static cmd *findcmd(const char *name) +static int cmd_help(int argc, char **argv) { - cmd *c, *chosen = 0; - size_t sz = strlen(name); - - for (c = cmdtab; c->name; c++) { - if (strncmp(name, c->name, sz) == 0) { - if (c->name[sz] == 0) { - chosen = c; - break; - } else if (chosen) - die(EXIT_FAILURE, "ambiguous command name `%s'", name); - else - chosen = c; - } - } - if (!chosen) - die(EXIT_FAILURE, "unknown command name `%s'", name); - return (chosen); + sc_help(cmdtab, stdout, argv + 1); + return (0); } -static void version(FILE *fp) +void version(FILE *fp) { pquis(fp, "$, Catacomb version " VERSION "\n"); } static void usage(FILE *fp) { - pquis(fp, "Usage: $ [-k keyring] command [args]\n"); + pquis(fp, "Usage: $ [-k KEYRING] COMMAND [ARGS]\n"); } -static void help(FILE *fp, char **argv) +void help_global(FILE *fp) { - cmd *c; - - if (*argv) { - c = findcmd(*argv); - fprintf(fp, "Usage: %s [-k keyring] %s\n", QUIS, c->usage); - if (c->help) { - fputc('\n', fp); - fputs(c->help, fp); - } - } else { - version(fp); - fputc('\n', fp); - usage(fp); - fputs("\n\ + usage(fp); + fputs("\n\ Create and verify signatures on lists of files.\n\ -\n", fp); - for (c = cmdtab; c->name; c++) - fprintf(fp, "%s\n", c->usage); - } +\n\ +Global command-line options:\n\ +\n\ +-h, --help [COMMAND...] Show this help message, or help for COMMANDs.\n\ +-v, --version Show program version number.\n\ +-u, --usage Show a terse usage message.\n\ +\n\ +-k, --keyring=FILE Read keys from FILE.\n", + fp); } /* --- @main@ --- * @@ -1224,7 +1210,7 @@ int main(int argc, char *argv[]) break; switch (i) { case 'h': - help(stdout, argv + optind); + sc_help(cmdtab, stdout, argv + optind); exit(0); break; case 'v': @@ -1253,7 +1239,7 @@ int main(int argc, char *argv[]) /* --- Dispatch to the correct subcommand handler --- */ - return (findcmd(argv[0])->func(argc, argv)); + return (findcmd(cmdtab, argv[0])->cmd(argc, argv)); #undef f_bogus } diff --git a/hashsum.1 b/hashsum.1 index f500d25..5eb280b 100644 --- a/hashsum.1 +++ b/hashsum.1 @@ -15,6 +15,8 @@ hashsum \- compute and verify cryptographic checksums of files .RB [ \-f0ecbv ] .RB [ \-a .IR algorithm ] +.RB [ \-E +.IR encoding ] .IR files ... .SH DESCRIPTION The @@ -52,6 +54,9 @@ successfully. .B "\-u, \-\-usage" Prints a brief usage summary to standard output and exits successfully. .TP +.BR "\-l, \-\-list " [ \fIitem ...] +Show lists of hash functions and encodings supported. +.TP .BI "\-a, \-\-algorithm=" alg Use the hash algorithm .IR alg . @@ -60,9 +65,20 @@ see .B "Hashing algorithms" below. .TP -.B "\-l, \-\-list" -Prints a space-separated list of available hashing algorithms to -standard output and exits successfully. +.BI "\-E, \-\-encoding=" encoding +Use the given +.I encoding +to represent hashes in the output. This is not interoperable with other +programs, but it's handy, e.g., for building sha1 URNs. The encodings +recognized are +.B hex +(the default), +.B base64 +and +.BR base32 . +Type +.B hashsum \-\-list enc +for a list of supported encodings. .TP .B "\-f, \-\-files" Each input file is considered to be a list of filenames which should be @@ -132,19 +148,19 @@ character. Two directives are currently understood: Subsequent hashes in this file were generated using the algorithm .IR alg . .TP +.BI "#encoding " encoding +Subsequent hashes in this file are represented using the named +.IR encoding . +.TP .BI "#escape" Filenames in subsequence lines are written using the `escaped' format, described below. .PP A .I "file line" -consists of a hash, in hexadecimal, followed by a space, a +consists of a hash, in the requested encoding, followed by a space, a .IR flag , -and the filename. If the current hash algorithm produces -.IR n -bit -output, there must be -.IR n /4 -hex digits of hash in a file line. The +and the filename. The .I flag is either a star .RB (` * ') diff --git a/hashsum.c b/hashsum.c index 492eb9b..c9bb3ec 100644 --- a/hashsum.c +++ b/hashsum.c @@ -51,6 +51,7 @@ #include #include "ghash.h" +#include "cc.h" /*----- Static variables --------------------------------------------------*/ @@ -164,24 +165,24 @@ static size_t getb32(const char *p, octet *q, size_t sz, char **pp) /* --- Table --- */ -typedef struct encops { +typedef struct encodeops { const char *name; void (*put)(const octet *, size_t, FILE *); size_t (*get)(const char *, octet *, size_t, char **); -} encops; +} encodeops; -static const encops enctab[] = { +static const encodeops encodingtab[] = { { "hex", puthex, gethex }, { "base64", putb64, getb64 }, { "base32", putb32, getb32 }, { 0, 0, 0 } }; -static const encops *getenc(const char *ename) +static const encodeops *getencoding(const char *ename) { - const encops *e; + const encodeops *e; - for (e = enctab; e->name; e++) { + for (e = encodingtab; e->name; e++) { if (strcmp(ename, e->name) == 0) return (e); } @@ -431,7 +432,7 @@ static void putstring(FILE *fp, const char *p, unsigned raw) /*----- Guts --------------------------------------------------------------*/ static int checkhash(const char *file, unsigned f, - const gchash *gch, const encops *e) + const gchash *gch, const encodeops *e) { int rc; FILE *fp; @@ -468,10 +469,10 @@ static int checkhash(const char *file, unsigned f, xfree(buf); buf = xmalloc(2 * gch->hashsz); } else if (strcmp(q, "encoding") == 0) { - const encops *ee; + const encodeops *ee; if ((q = str_getword(&p)) == 0) continue; - if ((ee = getenc(q)) == 0) + if ((ee = getencoding(q)) == 0) continue; e = ee; } else if (strcmp(q, "escape") == 0) @@ -531,7 +532,7 @@ static int checkhash(const char *file, unsigned f, } static int dohash(const char *file, unsigned f, - const gchash *gch, const encops *e) + const gchash *gch, const encodeops *e) { int rc = 0; octet *p = xmalloc(gch->hashsz); @@ -557,13 +558,13 @@ static int dohash(const char *file, unsigned f, } static int dofile(const char *file, unsigned f, - const gchash *gch, const encops *e) + const gchash *gch, const encodeops *e) { return (f & f_check ? checkhash : dohash)(file, f, gch, e); } static int hashfiles(const char *file, unsigned f, - const gchash *gch, const encops *e) + const gchash *gch, const encodeops *e) { FILE *fp; dstr d = DSTR_INIT; @@ -589,21 +590,21 @@ static int hashfiles(const char *file, unsigned f, } static int hashsum(const char *file, unsigned f, - const gchash *gch, const encops *e) + const gchash *gch, const encodeops *e) { return (f & f_files ? hashfiles : dofile)(file, f, gch, e); } /*----- Main driver -------------------------------------------------------*/ -static void version(FILE *fp) +void version(FILE *fp) { pquis(fp, "$, Catacomb version " VERSION "\n"); } static void usage(FILE *fp) { - pquis(fp, "Usage: $ [-f0ebcv] [-a algorithm] [files...]\n"); + pquis(fp, "Usage: $ [-f0ebcv] [-a ALGORITHM] [-E ENC] [FILES...]\n"); } static void help(FILE *fp, const gchash *gch) @@ -617,6 +618,7 @@ Generates or checks message digests on files. Options available:\n\ -h, --help Display this help message.\n\ -V, --version Display program's version number.\n\ -u, --usage Display a terse usage message.\n\ +-l, --list [ITEM...] Show known hash functions and/or encodings.\n\ \n\ -a, --algorithm=ALG Use the message digest algorithm ALG.\n\ -E, --encoding=ENC Represent hashes using encoding ENC.\n\ @@ -635,11 +637,18 @@ For a list of hashing algorithms and encodings, type `$ --list'.\n\ fprintf(fp, "The default message digest algorithm is %s.\n", gch->name); } +#define LISTS(LI) \ + LI("Lists", list, listtab[i].name, listtab[i].name) \ + LI("Hash functions", hash, ghashtab[i], ghashtab[i]->name) \ + LI("Encodings", enc, encodingtab[i].name, encodingtab[i].name) + +MAKELISTTAB(listtab, LISTS) + int main(int argc, char *argv[]) { unsigned f = 0; const gchash *gch = 0; - const encops *e = &enctab[0]; + const encodeops *e = &encodingtab[0]; int rc; /* --- Initialization --- */ @@ -699,29 +708,15 @@ int main(int argc, char *argv[]) case 'u': usage(stdout); exit(0); + case 'l': + exit(displaylists(listtab, argv + optind)); case 'a': if ((gch = gethash(optarg)) == 0) die(EXIT_FAILURE, "unknown hash algorithm `%s'", optarg); f |= f_oddhash; break; - case 'l': { - unsigned j; - printf("Algorithms: "); - for (j = 0; ghashtab[j]; j++) { - if (j) fputc(' ', stdout); - printf("%s", ghashtab[j]->name); - } - fputc('\n', stdout); - printf("Encodings: "); - for (j = 0; enctab[j].name; j++) { - if (j) fputc(' ', stdout); - printf("%s", enctab[j].name); - } - fputc('\n', stdout); - exit(0); - } break; case 'E': - if ((e = getenc(optarg)) == 0) + if ((e = getencoding(optarg)) == 0) die(EXIT_FAILURE, "unknown encoding `%s'", optarg); f |= f_oddenc; break; diff --git a/key.1 b/key.1 index 5c7bee6..07237a7 100644 --- a/key.1 +++ b/key.1 @@ -16,6 +16,18 @@ . ds ue . ds *b \fIbeta\fP .\} +.de VS +.sp 1 +.RS +.nf +.ft B +.. +.de VE +.ft R +.fi +.RE +.sp 1 +.. .TH key 1 "5 June 1999" "Straylight/Edgeware" "Catacomb cryptographic library" .SH NAME key \- simple key management system @@ -29,6 +41,12 @@ where .I command is one of: .PP +.B help +.RI [ command ...] +.br +.B show +.RI [ item ...] +.br .B add .RB [ \-lqrLS ] .RB [ \-a @@ -41,6 +59,14 @@ is one of: .IR tag ] .br \h'8n' +.RB [ \-A +.IR seed-alg ] +.RB [ \-s +.IR seed ] +.RB [ \-n +.IR bits ] +.br +\h'8n' .RB [ \-e .IR expire ] .RB [ \-t @@ -112,12 +138,12 @@ Before the command name, .I "global options" may be given. The following global options are supported: .TP -.BR "\-h, \-\-help " [ \fIcommand ] +.BR "\-h, \-\-help " [ \fIcommand ...] Writes a brief summary of .BR key 's various options to standard output, and -returns a successful exit status. With a command name, gives help on -that command. +returns a successful exit status. With command names, gives help on +those commands. .TP .B "\-v, \-\-version" Writes the program's version number to standard output, and returns a @@ -246,6 +272,60 @@ arbitrary strings, except they may not contain null bytes. Some attributes may have meaning for particular applications or key types; others may be assigned global meanings in future. .SH "COMMAND REFERENCE" +.SS help +The +.B help +command behaves exactly as the +.B \-\-help +option. With no arguments, it shows an overview of +.BR key 's +options; with arguments, it describes the named subcommands. +.SS show +The +.B show +command prints various lists of tokens understood by +.BR key . +With no arguments, it prints all of the lists; with arguments, it prints +just the named lists, in order. The recognized lists can be enumerated +using the +.VS +key show list +.VE +command. The lists are as follows. +.TP +.B list +The lists which can be enumerated by the +.B show +command. +.TP +.B hash +The hash functions which can be used with the +.B fingerprint +command. +.TP +.B ec +The built-in elliptic curves which can be used with the +.B add \-a ec +command. +.TP +.B dh +The built-in Diffie-Hellman groups which can be used with the +.B add \-a dh +command. +.TP +.B keygen +The key-generation algorithms which are acceptable to the +.B \-a +option of the +.B add +command. +.TP +.B seed +The pseudorandom generators which are acceptable to the +.B \-s +option of the +.B add +command. .SS add The .B add @@ -255,7 +335,9 @@ accepts the following options: .BI "\-a, \-\-algorithm " alg Selects a key generation algorithm. The default algorithm is .BR binary ; -the different algorithms are described below. +the different algorithms are described below. The command +.B key show keygen +lists the recognized key-generation algorithms. .TP .BI "\-b, \-\-bits " bits The length of the key to generate, in bits. The default, if this option @@ -270,6 +352,51 @@ Selects a key containing parameter values to copy. Not all key-generation algorithms allow the use of shared parameters. A new key also inherits attributes from its parameter key. .TP +.BI "\-A, \-\-seedalg " seed-alg +Use the deterministic random number generator algorithm +.I seed-alg +to generate the key. Use +.I before +the +.B \-s +or +.B \-n +options; without one of these, +.B \-A +has no effect. The default algorithm is +.BR rmd160-mgf . +The command +.B key show seed +shows a list of recognized seeding algorithms. The seeding algorithm +used to generate a key is recorded as the key's +.B seedalg +attribute. +.TP +.BI "\-s, \-\-seed " seed +Generate the key deterministically using the given +.IR seed , +which should be a Base64-encoded binary string. This is mainly useful +for parameters keys (types +.BR dsa-param +and +.BR dh-param ), +to demonstrate that a set of parameters has been generated in an honest +fashion. The +.B dsarand +generation algorithm can be used to generate +.B dsa-param +keys as required by FIPS186. The requested seed is recorded, +Base64-encoded, as the new key's +.B seed +attribute. +.TP +.BI "\-n, \-\-newseed " bits +Generate a new seed, with the given length in +.IR bits . +The generated seed is recorded, Base64-encoded, as the new key's +.B seed +attribute. +.TP .BI "\-e, \-\-expire " expire The expiry date for the generated key. This may be the string .RB ` forever ' diff --git a/keyutil.c b/keyutil.c index 13b8784..0b54df2 100644 --- a/keyutil.c +++ b/keyutil.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: keyutil.c,v 1.25 2004/04/21 00:38:22 mdw Exp $ + * $Id$ * * Simple key manager program * @@ -64,6 +64,7 @@ #include "ptab.h" #include "rsa.h" +#include "cc.h" #include "sha-mgf.h" #include "sha256-mgf.h" #include "sha224-mgf.h" @@ -571,10 +572,8 @@ static void alg_dhparam(keyopts *k) qd_parse qd; if (strcmp(k->curve, "list") == 0) { - const pentry *pe; - printf("Built-in prime groups:\n"); - for (pe = ptab; pe->name; pe++) - printf(" %s\n", pe->name); + unsigned i, w; + LIST("Built-in prime groups", stdout, ptab[i].name, ptab[i].name); exit(0); } qd.p = k->curve; @@ -711,10 +710,9 @@ static void alg_ecparam(keyopts *k) if (!k->bits) k->bits = 256; if (k->curve && strcmp(k->curve, "list") == 0) { - const ecentry *ee; - printf("Built-in elliptic curves:\n"); - for (ee = ectab; ee->name; ee++) - printf(" %s\n", ee->name); + unsigned i, w; + LIST("Built-in elliptic curves", stdout, + ectab[i].name, ectab[i].name); exit(0); } if (!k->curve) { @@ -1022,7 +1020,7 @@ static int cmd_add(int argc, char *argv[]) if ((k.f & f_bogus) || optind + 1 > argc) { die(EXIT_FAILURE, - "Usage: add [options] type [attr...]"); + "Usage: add [OPTIONS] TYPE [ATTR...]"); } if (key_chkident(argv[optind])) die(EXIT_FAILURE, "bad key type `%s'", argv[optind]); @@ -1389,7 +1387,7 @@ static int cmd_list(int argc, char *argv[]) } if (o.f & f_bogus) - die(EXIT_FAILURE, "Usage: list [-uqv] [-f filter] [tag...]"); + die(EXIT_FAILURE, "Usage: list [-uqv] [-f FILTER] [TAG...]"); /* --- Open the key file --- */ @@ -1447,7 +1445,7 @@ static int cmd_expire(int argc, char *argv[]) int rc = 0; if (argc < 2) - die(EXIT_FAILURE, "Usage: expire tag..."); + die(EXIT_FAILURE, "Usage: expire TAG..."); doopen(&f, KOPEN_WRITE); for (i = 1; i < argc; i++) { if ((k = key_bytag(&f, argv[i])) != 0) @@ -1471,7 +1469,7 @@ static int cmd_delete(int argc, char *argv[]) int rc = 0; if (argc < 2) - die(EXIT_FAILURE, "Usage: delete tag..."); + die(EXIT_FAILURE, "Usage: delete TAG..."); doopen(&f, KOPEN_WRITE); for (i = 1; i < argc; i++) { if ((k = key_bytag(&f, argv[i])) != 0) @@ -1493,7 +1491,7 @@ static int cmd_setattr(int argc, char *argv[]) key *k; if (argc < 3) - die(EXIT_FAILURE, "Usage: setattr tag attr..."); + die(EXIT_FAILURE, "Usage: setattr TAG ATTR..."); doopen(&f, KOPEN_WRITE); if ((k = key_bytag(&f, argv[1])) == 0) die(EXIT_FAILURE, "key `%s' not found", argv[1]); @@ -1561,7 +1559,7 @@ static int cmd_finger(int argc, char *argv[]) argv += optind; argc -= optind; if (rc) - die(EXIT_FAILURE, "Usage: fingerprint [-f filter] [tag...]"); + die(EXIT_FAILURE, "Usage: fingerprint [-f FILTER] [TAG...]"); doopen(&f, KOPEN_READ); @@ -1594,7 +1592,7 @@ static int cmd_comment(int argc, char *argv[]) int err; if (argc < 2 || argc > 3) - die(EXIT_FAILURE, "Usage: comment tag [comment]"); + die(EXIT_FAILURE, "Usage: comment TAG [COMMENT]"); doopen(&f, KOPEN_WRITE); if ((k = key_bytag(&f, argv[1])) == 0) die(EXIT_FAILURE, "key `%s' not found", argv[1]); @@ -1634,7 +1632,7 @@ static int cmd_tag(int argc, char *argv[]) argv += optind; argc -= optind; if (argc < 1 || argc > 2 || rc) - die(EXIT_FAILURE, "Usage: tag [-r] tag [new-tag]"); + die(EXIT_FAILURE, "Usage: tag [-r] TAG [NEW-TAG]"); doopen(&f, KOPEN_WRITE); if (flags & f_retag) { if ((k = key_bytag(&f, argv[1])) != 0 && strcmp(k->tag, argv[1]) == 0) @@ -1658,7 +1656,7 @@ static int cmd_lock(int argc, char *argv[]) dstr d = DSTR_INIT; if (argc != 2) - die(EXIT_FAILURE, "Usage: lock qtag"); + die(EXIT_FAILURE, "Usage: lock QTAG"); doopen(&f, KOPEN_WRITE); if (key_qtag(&f, argv[1], &d, &k, &kd)) die(EXIT_FAILURE, "key `%s' not found", argv[1]); @@ -1681,7 +1679,7 @@ static int cmd_unlock(int argc, char *argv[]) dstr d = DSTR_INIT; if (argc != 2) - die(EXIT_FAILURE, "Usage: unlock qtag"); + die(EXIT_FAILURE, "Usage: unlock QTAG"); doopen(&f, KOPEN_WRITE); if (key_qtag(&f, argv[1], &d, &k, &kd)) die(EXIT_FAILURE, "key `%s' not found", argv[1]); @@ -1728,7 +1726,7 @@ static int cmd_extract(int argc, char *argv[]) argv += optind; argc -= optind; if (rc || argc < 1) - die(EXIT_FAILURE, "Usage: extract [-f filter] file [tag...]"); + die(EXIT_FAILURE, "Usage: extract [-f FILTER] FILE [TAG...]"); if (strcmp(*argv, "-") == 0) fp = stdout; else if (!(fp = fopen(*argv, "w"))) { @@ -1764,7 +1762,7 @@ static int cmd_tidy(int argc, char *argv[]) { key_file f; if (argc != 1) - die(EXIT_FAILURE, "usage: tidy"); + die(EXIT_FAILURE, "Usage: tidy"); doopen(&f, KOPEN_WRITE); f.f |= KF_MODIFIED; /* Nasty hack */ doclose(&f); @@ -1779,7 +1777,7 @@ static int cmd_merge(int argc, char *argv[]) FILE *fp; if (argc != 2) - die(EXIT_FAILURE, "Usage: merge file"); + die(EXIT_FAILURE, "Usage: merge FILE"); if (strcmp(argv[1], "-") == 0) fp = stdin; else if (!(fp = fopen(argv[1], "r"))) { @@ -1793,25 +1791,86 @@ static int cmd_merge(int argc, char *argv[]) return (0); } +/* --- @cmd_show@ --- */ + +#define LISTS(LI) \ + LI("Lists", list, \ + listtab[i].name, listtab[i].name) \ + LI("Hash functions", hash, \ + ghashtab[i], ghashtab[i]->name) \ + LI("Elliptic curves", ec, \ + ectab[i].name, ectab[i].name) \ + LI("Diffie-Hellman groups", dh, \ + ptab[i].name, ptab[i].name) \ + LI("Key-generation algorithms", keygen, \ + algtab[i].name, algtab[i].name) \ + LI("Random seeding algorithms", seed, \ + seedtab[i].p, seedtab[i].p) + +MAKELISTTAB(listtab, LISTS) + +static int cmd_show(int argc, char *argv[]) +{ + return (displaylists(listtab, argv + 1)); +} + /*----- Main command table ------------------------------------------------*/ -static struct cmd { - const char *name; - int (*cmd)(int /*argc*/, char */*argv*/[]); - const char *usage; - const char *help; -} cmds[] = { +static int cmd_help(int argc, char *argv[]); + +static cmd cmds[] = { + { "help", cmd_help, "help [COMMAND...]" }, + { "show", cmd_show, "show [ITEM...]" }, + { "list", cmd_list, "list [-uqv] [-f FILTER] [TAG...]", "\ +Options:\n\ +\n\ +-u, --utc Display expiry times etc. in UTC, not local time.\n\ +-q, --quiet Show less information.\n\ +-v, --verbose Show more information.\n\ +" }, + { "fingerprint", cmd_finger, "fingerprint [-f FILTER] [TAG...]", "\ +Options:\n\ +\n\ +-f, --filter=FILT Only hash key components matching FILT.\n\ +-a, --algorithm=HASH Use the named HASH algorithm.\n\ + ($ show hash for list.)\n\ +" }, + { "extract", cmd_extract, "extract [-f FILTER] FILE [TAG...]", "\ +Options:\n\ +\n\ +-f, --filter=FILT Only extract key components matching FILT.\n\ +" }, + { "merge", cmd_merge, "merge FILE" }, + { "expire", cmd_expire, "expire TAG..." }, + { "delete", cmd_delete, "delete TAG..." }, + { "setattr", cmd_setattr, "setattr TAG ATTR..." }, + { "comment", cmd_comment, "comment TAG [COMMENT]" }, + { "lock", cmd_lock, "lock QTAG" }, + { "unlock", cmd_unlock, "unlock QTAG" }, + { "tag", cmd_tag, "tag [-r] TAG [NEW-TAG]", "\ +Options:\n\ +\n\ +-r, --retag Untag any key currently called new-tag.\n\ +" }, + { "tidy", cmd_tidy, "tidy" }, { "add", cmd_add, - "add [options] type [attr...]\n\ - Options: [-lqrLS] [-a alg] [-bB bits] [-p param] [-R tag]\n\ - [-e expire] [-t tag] [-c comment]", "\ + "add [-OPTIONS] TYPE [ATTR...]\n\ + Options: [-lqrLS] [-a ALG] [-bB BITS] [-p PARAM] [-R TAG]\n\ + [-A SEEDALG] [-s SEED] [-n BITS]\n\ + [-e EXPIRE] [-t TAG] [-c COMMENT]", "\ Options:\n\ \n\ -a, --algorithm=ALG Generate keys suitable for ALG.\n\ + ($ show keygen for list.)\n\ -b, --bits=N Generate an N-bit key.\n\ -B, --qbits=N Use an N-bit subgroup or factors.\n\ -p, --parameters=TAG Get group parameters from TAG.\n\ --C, --curve=CURVE Use elliptic curve CURVE.\n\ +-C, --curve=NAME Use elliptic curve or DH group NAME.\n\ + ($ show ec or $ show dh for list.)\n\ +-A, --seedalg=ALG Use pseudorandom generator ALG to generate key.\n\ + ($ show seed for list.)\n\ +-s, --seed=BASE64 Use Base64-encoded string BASE64 as seed.\n\ +-n, --newseed=COUNT Generate new COUNT-bit seed.\n\ -e, --expire=TIME Make the key expire after TIME.\n\ -c, --comment=STRING Attach the command STRING to the key.\n\ -t, --tag=TAG Tag the key with the name TAG.\n\ @@ -1822,80 +1881,22 @@ Options:\n\ -L, --lim-lee Generate Lim-Lee primes for Diffie-Hellman groups.\n\ -S, --subgroup Use a prime-order subgroup for Diffie-Hellman.\n\ " }, - { "expire", cmd_expire, "expire tag..." }, - { "delete", cmd_delete, "delete tag..." }, - { "tag", cmd_tag, "tag [-r] tag [new-tag]", "\ -Options:\n\ -\n\ --r, --retag Untag any key currently called new-tag.\n\ -" }, - { "setattr", cmd_setattr, "setattr tag attr..." }, - { "comment", cmd_comment, "comment tag [comment]" }, - { "lock", cmd_lock, "lock qtag" }, - { "unlock", cmd_unlock, "unlock qtag" }, - { "list", cmd_list, "list [-uqv] [-f filter] [tag...]", "\ -Options:\n\ -\n\ --u, --utc Display expiry times etc. in UTC, not local time.\n\ --q, --quiet Show less information.\n\ --v, --verbose Show more information.\n\ -" }, - { "fingerprint", cmd_finger, "fingerprint [-f filter] [tag...]", "\ -Options:\n\ -\n\ --f, --filter=FILT Only hash key components matching FILT.\n\ --a, --algorithm=HASH Use the named HASH algorithm.\n\ -" }, - { "tidy", cmd_tidy, "tidy" }, - { "extract", cmd_extract, "extract [-f filter] file [tag...]", "\ -Options:\n\ -\n\ --f, --filter=FILT Only extract key components matching FILT.\n\ -" }, - { "merge", cmd_merge, "merge file" }, { 0, 0, 0 } }; -typedef struct cmd cmd; - -/*----- Main code ---------------------------------------------------------*/ - -/* --- @findcmd@ --- * - * - * Arguments: @const char *name@ = a command name - * - * Returns: Pointer to the command structure. - * - * Use: Looks up a command by name. If the command isn't found, an - * error is reported and the program is terminated. - */ - -static cmd *findcmd(const char *name) +static int cmd_help(int argc, char *argv[]) { - cmd *c, *chosen = 0; - size_t sz = strlen(name); - - for (c = cmds; c->name; c++) { - if (strncmp(name, c->name, sz) == 0) { - if (c->name[sz] == 0) { - chosen = c; - break; - } else if (chosen) - die(EXIT_FAILURE, "ambiguous command name `%s'", name); - else - chosen = c; - } - } - if (!chosen) - die(EXIT_FAILURE, "unknown command name `%s'", name); - return (chosen); + sc_help(cmds, stdout, argv + 1); + return (0); } +/*----- Main code ---------------------------------------------------------*/ + /* --- Helpful GNUy functions --- */ -void usage(FILE *fp) +static void usage(FILE *fp) { - pquis(fp, "Usage: $ [-k keyring] command [args]\n"); + pquis(fp, "Usage: $ [-k KEYRING] COMMAND [ARGS]\n"); } void version(FILE *fp) @@ -1903,38 +1904,20 @@ void version(FILE *fp) pquis(fp, "$, Catacomb version " VERSION "\n"); } -void help(FILE *fp, char **argv) +void help_global(FILE *fp) { - cmd *c; - - version(fp); - fputc('\n', fp); - if (*argv) { - c = findcmd(*argv); - fprintf(fp, "Usage: %s [-k keyring] %s\n", QUIS, c->usage); - if (c->help) { - fputc('\n', fp); - fputs(c->help, fp); - } - } else { - usage(fp); - fputs("\n\ -Performs various simple key management operations. Command line options\n\ -recognized are:\n\ + usage(fp); + fputs("\n\ +Performs various simple key management operations.\n\ +\n\ +Global command line options:\n\ \n\ --h, --help [COMMAND] Display this help text (or help for COMMAND).\n\ +-h, --help [COMMAND...] Display this help text (or help for COMMANDs).\n\ -v, --version Display version number.\n\ -u, --usage Display short usage summary.\n\ \n\ --k, --keyring=FILE Read and write keys in FILE.\n\ --i, --id=TAG Use key TAG for random number generator.\n\ --t, --type=TYPE Use key TYPE for random number generator.\n\ -\n\ -The following commands are understood:\n\n", - fp); - for (c = cmds; c->name; c++) - fprintf(fp, "%s\n", c->usage); - } +-k, --keyring=FILE Read and write keys in FILE.\n", + fp); } /* --- @main@ --- * @@ -1986,7 +1969,7 @@ int main(int argc, char *argv[]) /* --- GNU help options --- */ case 'h': - help(stdout, argv + optind); + sc_help(cmds, stdout, argv + optind); exit(0); case 'v': version(stdout); @@ -2026,7 +2009,7 @@ int main(int argc, char *argv[]) argc -= optind; argv += optind; optind = 0; - return (findcmd(argv[0])->cmd(argc, argv)); + return (findcmd(cmds, argv[0])->cmd(argc, argv)); } /*----- That's all, folks -------------------------------------------------*/ diff --git a/perftest.c b/perftest.c index 4b8d158..ad1f182 100644 --- a/perftest.c +++ b/perftest.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: perftest.c,v 1.1 2004/04/21 00:37:32 mdw Exp $ + * $Id$ * * Measure performance of various operations (Unix-specific) * @@ -63,9 +63,12 @@ #include "ec.h" #include "group.h" +#include "cc.h" #include "gcipher.h" #include "ghash.h" #include "gmac.h" +#include "ectab.h" +#include "ptab.h" /*----- Options -----------------------------------------------------------*/ @@ -426,7 +429,7 @@ static void hash_run(void *cc) /* --- Job table --- */ -typedef struct jobobs { +typedef struct jobops { const char *name; void *(*init)(opts *); void (*run)(void *); @@ -452,7 +455,7 @@ static const jobops jobtab[] = { /*----- Main code ---------------------------------------------------------*/ -static void version(FILE *fp) +void version(FILE *fp) { pquis(fp, "$, Catacomb " VERSION "\n"); } @@ -469,9 +472,40 @@ static void help(FILE *fp) usage(fp); pquis(fp, "\n\ Various performance tests.\n\ +\n\ +Options:\n\ +\n\ +-h, --help Show this help text.\n\ +-v, --version Show program version number.\n\ +-u, --usage Show terse usage message.\n\ +-l, --list [ITEM...] List all the various names of things.\n\ +\n\ +-C, --name=NAME Select curve/DH-group/enc/hash name.\n\ +-b, --field-bits Field size for g-prime and rsa.\n\ +-B, --group-bits Group size for g-prime; key size for ksched;\n\ + data size for enc and hash.\n\ +-n, --factors=COUNT Number of factors for {exp,mul}-sim.\n\ +-i, --intervals=COUNT Number of intervals to run for. [0; forever]\n\ +-t, --time=TIME Length of an interval in seconds. [1]\n\ "); } +#define LISTS(LI) \ + LI("Lists", list, \ + listtab[i].name, listtab[i].name) \ + LI("Jobs", job, \ + jobtab[i].name, jobtab[i].name) \ + LI("Elliptic curves", ec, \ + ectab[i].name, ectab[i].name) \ + LI("Diffie-Hellman groups", dh, \ + ptab[i].name, ptab[i].name) \ + LI("Encryption algorithms", cipher, \ + gciphertab[i], gciphertab[i]->name) \ + LI("Hash functions", hash, \ + ghashtab[i], ghashtab[i]->name) + +MAKELISTTAB(listtab, LISTS) + static unsigned uarg(const char *what, const char *p) { char *q; @@ -514,6 +548,7 @@ int main(int argc, char *argv[]) { "help", 0, 0, 'h' }, { "version", 0, 0, 'v' }, { "usage", 0, 0, 'u' }, + { "list", 0, 0, 'l' }, { "name", OPTF_ARGREQ, 0, 'C' }, { "field-bits", OPTF_ARGREQ, 0, 'b' }, { "group-bits", OPTF_ARGREQ, 0, 'B' }, @@ -523,12 +558,13 @@ int main(int argc, char *argv[]) { 0, 0, 0, 0 } }; - i = mdwopt(argc, argv, "hvuC:b:B:n:i:t:", opts, 0, 0, 0); + i = mdwopt(argc, argv, "hvulC:b:B:n:i:t:", opts, 0, 0, 0); if (i < 0) break; switch (i) { case 'h': help(stdout); exit(0); case 'v': version(stdout); exit(0); case 'u': usage(stdout); exit(0); + case 'l': exit(displaylists(listtab, argv + optind)); case 'C': o.name = optarg; break; case 'b': o.fbits = uarg("field bits", optarg); break; case 'B': o.gbits = uarg("subgroup bits", optarg); break; -- 2.11.0