diff options
author | Ulf Möller <ulf@openssl.org> | 2006-02-12 23:11:56 +0000 |
---|---|---|
committer | Ulf Möller <ulf@openssl.org> | 2006-02-12 23:11:56 +0000 |
commit | c7235be6e36c4bef84594aa3b2f0561db84b63d8 (patch) | |
tree | d304c3d8ac064a1345d64f0e25a1eebf52e564a4 /apps | |
parent | 1c17d91c53ba73c907e0559d2bb80122dc7a8284 (diff) |
RFC 3161 compliant time stamp request creation, response generation
and response verification.
Submitted by: Zoltan Glozik <zglozik@opentsa.org>
Reviewed by: Ulf Moeller
Diffstat (limited to 'apps')
-rw-r--r-- | apps/Makefile | 8 | ||||
-rw-r--r-- | apps/openssl-vms.cnf | 42 | ||||
-rw-r--r-- | apps/openssl.cnf | 40 | ||||
-rw-r--r-- | apps/progs.h | 2 | ||||
-rw-r--r-- | apps/ts.c | 1143 | ||||
-rw-r--r-- | apps/tsget | 195 |
6 files changed, 1423 insertions, 7 deletions
diff --git a/apps/Makefile b/apps/Makefile index 20738d1276..c638569ca2 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -31,7 +31,7 @@ LIBSSL=-L.. -lssl PROGRAM= openssl -SCRIPTS=CA.sh CA.pl +SCRIPTS=CA.sh CA.pl tsget EXE= $(PROGRAM)$(EXE_EXT) @@ -39,7 +39,7 @@ E_EXE= verify asn1pars req dgst dh dhparam enc passwd gendh errstr \ ca crl rsa rsautl dsa dsaparam ec ecparam \ x509 genrsa gendsa s_server s_client speed \ s_time version pkcs7 crl2pkcs7 sess_id ciphers nseq pkcs12 \ - pkcs8 spkac smime rand engine ocsp prime + pkcs8 spkac smime rand engine ocsp prime ts PROGS= $(PROGRAM).c @@ -56,7 +56,7 @@ E_OBJ= verify.o asn1pars.o req.o dgst.o dh.o dhparam.o enc.o passwd.o gendh.o er x509.o genrsa.o gendsa.o s_server.o s_client.o speed.o \ s_time.o $(A_OBJ) $(S_OBJ) $(RAND_OBJ) version.o sess_id.o \ ciphers.o nseq.o pkcs12.o pkcs8.o spkac.o smime.o rand.o engine.o \ - ocsp.o prime.o + ocsp.o prime.o ts.o E_SRC= verify.c asn1pars.c req.c dgst.c dh.c enc.c passwd.c gendh.c errstr.c ca.c \ pkcs7.c crl2p7.c crl.c \ @@ -64,7 +64,7 @@ E_SRC= verify.c asn1pars.c req.c dgst.c dh.c enc.c passwd.c gendh.c errstr.c ca. x509.c genrsa.c gendsa.c s_server.c s_client.c speed.c \ s_time.c $(A_SRC) $(S_SRC) $(RAND_SRC) version.c sess_id.c \ ciphers.c nseq.c pkcs12.c pkcs8.c spkac.c smime.c rand.c engine.c \ - ocsp.c prime.c + ocsp.c prime.c ts.c SRC=$(E_SRC) diff --git a/apps/openssl-vms.cnf b/apps/openssl-vms.cnf index c8afa2ccfc..6685cf1df1 100644 --- a/apps/openssl-vms.cnf +++ b/apps/openssl-vms.cnf @@ -21,12 +21,17 @@ oid_section = new_oids [ new_oids ] -# We can add new OIDs in here for use by 'ca' and 'req'. +# We can add new OIDs in here for use by 'ca', 'req' and 'ts'. # Add a simple OID like this: # testoid1=1.2.3.4 # Or use config file substitution like this: # testoid2=${testoid1}.5.6 +# Policies used by the TSA examples. +tsa_policy1 = 1.2.3.4.1 +tsa_policy2 = 1.2.3.4.5.6 +tsa_policy3 = 1.2.3.4.5.7 + #################################################################### [ ca ] default_ca = CA_default # The default ca section @@ -188,7 +193,7 @@ nsComment = "OpenSSL Generated Certificate" # PKIX recommendations harmless if included in all certificates. subjectKeyIdentifier=hash -authorityKeyIdentifier=keyid,issuer:always +authorityKeyIdentifier=keyid,issuer # This stuff is for subjectAltName and issuerAltname. # Import the email address. @@ -207,6 +212,9 @@ authorityKeyIdentifier=keyid,issuer:always #nsCaPolicyUrl #nsSslServerName +# This is required for TSA certificates. +extendedKeyUsage = critical,timeStamping + [ v3_req ] # Extensions to add to a certificate request @@ -311,3 +319,33 @@ authorityKeyIdentifier=keyid,issuer:always # This really needs to be in place for it to be a proxy certificate. proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo + +#################################################################### +[ tsa ] + +default_tsa = tsa_config1 # the default TSA section + +[ tsa_config1 ] + +# These are used by the TSA reply generation only. +dir = sys\$disk:[.demoCA # TSA root directory +serial = $dir]tsaserial. # The current serial number (mandatory) +crypto_device = builtin # OpenSSL engine to use for signing +signer_cert = $dir/tsacert.pem # The TSA signing certificate + # (optional) +certs = $dir.cacert.pem] # Certificate chain to include in reply + # (optional) +signer_key = $dir/private/tsakey.pem # The TSA private key (optional) + +default_policy = tsa_policy1 # Policy if request did not specify it + # (optional) +other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) +digests = md5, sha1 # Acceptable message digests (mandatory) +accuracy = secs:1, millisecs:500, microsecs:100 # (optional) +clock_precision_digits = 0 # number of digits after dot. (optional) +ordering = yes # Is ordering defined for timestamps? + # (optional, default: no) +tsa_name = yes # Must the TSA name be included in the reply? + # (optional, default: no) +ess_cert_id_chain = no # Must the ESS cert id chain be included? + # (optional, default: no) diff --git a/apps/openssl.cnf b/apps/openssl.cnf index f58a30af43..a620b98cf7 100644 --- a/apps/openssl.cnf +++ b/apps/openssl.cnf @@ -21,12 +21,17 @@ oid_section = new_oids [ new_oids ] -# We can add new OIDs in here for use by 'ca' and 'req'. +# We can add new OIDs in here for use by 'ca', 'req' and 'ts'. # Add a simple OID like this: # testoid1=1.2.3.4 # Or use config file substitution like this: # testoid2=${testoid1}.5.6 +# Policies used by the TSA examples. +tsa_policy1 = 1.2.3.4.1 +tsa_policy2 = 1.2.3.4.5.6 +tsa_policy3 = 1.2.3.4.5.7 + #################################################################### [ ca ] default_ca = CA_default # The default ca section @@ -206,6 +211,9 @@ authorityKeyIdentifier=keyid,issuer #nsCaPolicyUrl #nsSslServerName +# This is required for TSA certificates. +extendedKeyUsage = critical,timeStamping + [ v3_req ] # Extensions to add to a certificate request @@ -310,3 +318,33 @@ authorityKeyIdentifier=keyid,issuer:always # This really needs to be in place for it to be a proxy certificate. proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo + +#################################################################### +[ tsa ] + +default_tsa = tsa_config1 # the default TSA section + +[ tsa_config1 ] + +# These are used by the TSA reply generation only. +dir = ./demoCA # TSA root directory +serial = $dir/tsaserial # The current serial number (mandatory) +crypto_device = builtin # OpenSSL engine to use for signing +signer_cert = $dir/tsacert.pem # The TSA signing certificate + # (optional) +certs = $dir/cacert.pem # Certificate chain to include in reply + # (optional) +signer_key = $dir/private/tsakey.pem # The TSA private key (optional) + +default_policy = tsa_policy1 # Policy if request did not specify it + # (optional) +other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) +digests = md5, sha1 # Acceptable message digests (mandatory) +accuracy = secs:1, millisecs:500, microsecs:100 # (optional) +clock_precision_digits = 0 # number of digits after dot. (optional) +ordering = yes # Is ordering defined for timestamps? + # (optional, default: no) +tsa_name = yes # Must the TSA name be included in the reply? + # (optional, default: no) +ess_cert_id_chain = no # Must the ESS cert id chain be included? + # (optional, default: no) diff --git a/apps/progs.h b/apps/progs.h index dc665c53a7..8bfd616d99 100644 --- a/apps/progs.h +++ b/apps/progs.h @@ -40,6 +40,7 @@ extern int rand_main(int argc,char *argv[]); extern int engine_main(int argc,char *argv[]); extern int ocsp_main(int argc,char *argv[]); extern int prime_main(int argc,char *argv[]); +extern int ts_main(int argc,char *argv[]); #define FUNC_TYPE_GENERAL 1 #define FUNC_TYPE_MD 2 @@ -127,6 +128,7 @@ FUNCTION functions[] = { #endif {FUNC_TYPE_GENERAL,"ocsp",ocsp_main}, {FUNC_TYPE_GENERAL,"prime",prime_main}, + {FUNC_TYPE_GENERAL,"ts",ts_main}, #ifndef OPENSSL_NO_MD2 {FUNC_TYPE_MD,"md2",dgst_main}, #endif diff --git a/apps/ts.c b/apps/ts.c new file mode 100644 index 0000000000..7edd5380e3 --- /dev/null +++ b/apps/ts.c @@ -0,0 +1,1143 @@ +/* apps/ts.c */ +/* Written by Zoltan Glozik (zglozik@stones.com) for the OpenSSL + * project 2002. + */ +/* ==================================================================== + * Copyright (c) 2001 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "apps.h" +#include <openssl/bio.h> +#include <openssl/err.h> +#include <openssl/pem.h> +#include <openssl/rand.h> +#include <openssl/ts.h> + +#undef PROG +#define PROG ts_main + +/* Length of the nonce of the request in bits (must be a multiple of 8). */ +#define NONCE_LENGTH 64 + +/* Macro definitions for the configuration file. */ +#define ENV_OID_FILE "oid_file" + +/* Local function declarations. */ + +static ASN1_OBJECT *txt2obj(const char *oid); +static CONF *load_config_file(const char *configfile); + +/* Query related functions. */ +static int query_command(const char *data, char *digest, + const EVP_MD *md, const char *policy, int no_nonce, + int cert, const char *in, const char *out, int text); +static BIO *BIO_open_with_default(const char *file, const char *mode, + FILE *default_fp); +static TS_REQ *create_query(BIO *data_bio, char *digest, const EVP_MD *md, + const char *policy, int no_nonce, int cert); +static int create_digest(BIO *input, char *digest, + const EVP_MD *md, unsigned char **md_value); +static ASN1_INTEGER *create_nonce(int bits); + +/* Reply related functions. */ +static int reply_command(CONF *conf, char *section, char *engine, + char *queryfile, char *passin, char *inkey, + char *signer, char *chain, const char *policy, + char *in, int token_in, char *out, int token_out, + int text); +static TS_RESP *read_PKCS7(BIO *in_bio); +static TS_RESP *create_response(CONF *conf, const char *section, char *engine, + char *queryfile, char *passin, char *inkey, + char *signer, char *chain, const char *policy); +static ASN1_INTEGER * MS_CALLBACK serial_cb(TS_RESP_CTX *ctx, void *data); +static ASN1_INTEGER *next_serial(const char *serialfile); +static int save_ts_serial(const char *serialfile, ASN1_INTEGER *serial); + +/* Verify related functions. */ +static int verify_command(char *data, char *digest, char *queryfile, + char *in, int token_in, + char *ca_path, char *ca_file, char *untrusted); +static TS_VERIFY_CTX *create_verify_ctx(char *data, char *digest, + char *queryfile, + char *ca_path, char *ca_file, + char *untrusted); +static X509_STORE *create_cert_store(char *ca_path, char *ca_file); +static int MS_CALLBACK verify_cb(int ok, X509_STORE_CTX *ctx); + +/* Main function definition. */ + +int MAIN(int argc, char **argv) + { + int ret = 1; + char *configfile = NULL; + char *section = NULL; + CONF *conf = NULL; + enum mode { + CMD_NONE, CMD_QUERY, CMD_REPLY, CMD_VERIFY + } mode = CMD_NONE; + char *data = NULL; + char *digest = NULL; + const EVP_MD *md = NULL; + char *rnd = NULL; + char *policy = NULL; + int no_nonce = 0; + int cert = 0; + char *in = NULL; + char *out = NULL; + int text = 0; + char *queryfile = NULL; + char *passin = NULL; /* Password source. */ + char *password =NULL; /* Password itself. */ + char *inkey = NULL; + char *signer = NULL; + char *chain = NULL; + char *ca_path = NULL; + char *ca_file = NULL; + char *untrusted = NULL; + char *engine = NULL; + /* Input is ContentInfo instead of TimeStampResp. */ + int token_in = 0; + /* Output is ContentInfo instead of TimeStampResp. */ + int token_out = 0; + int free_bio_err = 0; + + ERR_load_crypto_strings(); + apps_startup(); + + if (bio_err == NULL && (bio_err = BIO_new(BIO_s_file())) != NULL) + { + free_bio_err = 1; + BIO_set_fp(bio_err, stderr, BIO_NOCLOSE | BIO_FP_TEXT); + } + + for (argc--, argv++; argc > 0; argc--, argv++) + { + if (strcmp(*argv, "-config") == 0) + { + if (argc-- < 1) goto usage; + configfile = *++argv; + } + else if (strcmp(*argv, "-section") == 0) + { + if (argc-- < 1) goto usage; + section = *++argv; + } + else if (strcmp(*argv, "-query") == 0) + { + if (mode != CMD_NONE) goto usage; + mode = CMD_QUERY; + } + else if (strcmp(*argv, "-data") == 0) + { + if (argc-- < 1) goto usage; + data = *++argv; + } + else if (strcmp(*argv, "-digest") == 0) + { + if (argc-- < 1) goto usage; + digest = *++argv; + } + else if (strcmp(*argv, "-md2") == 0 + || strcmp(*argv, "-md4") == 0 + || strcmp(*argv, "-md5") == 0 + || strcmp(*argv, "-sha") == 0 + || strcmp(*argv, "-sha1") == 0 + || strcmp(*argv, "-mdc2") == 0 + || strcmp(*argv, "-ripemd160") == 0) + { + md = EVP_get_digestbyname(*argv + 1); + } + else if (strcmp(*argv, "-rand") == 0) + { + if (argc-- < 1) goto usage; + rnd = *++argv; + } + else if (strcmp(*argv, "-policy") == 0) + { + if (argc-- < 1) goto usage; + policy = *++argv; + } + else if (strcmp(*argv, "-no_nonce") == 0) + { + no_nonce = 1; + } + else if (strcmp(*argv, "-cert") == 0) + { + cert = 1; + } + else if (strcmp(*argv, "-in") == 0) + { + if (argc-- < 1) goto usage; + in = *++argv; + } + else if (strcmp(*argv, "-token_in") == 0) + { + token_in = 1; + } + else if (strcmp(*argv, "-out") == 0) + { + if (argc-- < 1) goto usage; + out = *++argv; + } + else if (strcmp(*argv, "-token_out") == 0) + { + token_out = 1; + } + else if (strcmp(*argv, "-text") == 0) + { + text = 1; + } + else if (strcmp(*argv, "-reply") == 0) + { + if (mode != CMD_NONE) goto usage; + mode = CMD_REPLY; + } + else if (strcmp(*argv, "-queryfile") == 0) + { + if (argc-- < 1) goto usage; + queryfile = *++argv; + } + else if (strcmp(*argv, "-passin") == 0) + { + if (argc-- < 1) goto usage; + passin = *++argv; + } + else if (strcmp(*argv, "-inkey") == 0) + { + if (argc-- < 1) goto usage; + inkey = *++argv; + } + else if (strcmp(*argv, "-signer") == 0) + { + if (argc-- < 1) goto usage; + signer = *++argv; + } + else if (strcmp(*argv, "-chain") == 0) + { + if (argc-- < 1) goto usage; + chain = *++argv; + } + else if (strcmp(*argv, "-verify") == 0) + { + if (mode != CMD_NONE) goto usage; + mode = CMD_VERIFY; + } + else if (strcmp(*argv, "-CApath") == 0) + { + if (argc-- < 1) goto usage; + ca_path = *++argv; + } + else if (strcmp(*argv, "-CAfile") == 0) + { + if (argc-- < 1) goto usage; + ca_file = *++argv; + } + else if (strcmp(*argv, "-untrusted") == 0) + { + if (argc-- < 1) goto usage; + untrusted = *++argv; + } + else if (strcmp(*argv, "-engine") == 0) + { + if (argc-- < 1) goto usage; + engine = *++argv; + } + else + goto usage; + } + + /* Seed the random number generator if it is going to be used. */ + if (mode == CMD_QUERY && !no_nonce) + { + if (!app_RAND_load_file(NULL, bio_err, 1) && rnd == NULL) + BIO_printf(bio_err, "warning, not much extra random " + "data, consider using the -rand option\n"); + if (rnd != NULL) + BIO_printf(bio_err,"%ld semi-random bytes loaded\n", + app_RAND_load_files(rnd)); + } + + /* Get the password if required. */ + if(mode == CMD_REPLY && passin && + !app_passwd(bio_err, passin, NULL, &password, NULL)) + { + BIO_printf(bio_err,"Error getting password.\n"); + goto cleanup; + } + + /* Check consistency of parameters and execute + the appropriate function. */ + switch (mode) + { + case CMD_NONE: + goto usage; + case CMD_QUERY: + /* Data file and message imprint cannot be specified + at the same time. */ + ret = data != NULL && digest != NULL; + if (ret) goto usage; + /* Load the config file for possible policy OIDs. */ + conf = load_config_file(configfile); + ret = !query_command(data, digest, md, policy, no_nonce, cert, + in, out, text); + break; + case CMD_REPLY: + conf = load_config_file(configfile); + if (in == NULL) + { + ret = !(queryfile != NULL && conf != NULL && !token_in); + if (ret) goto usage; + } + else + { + /* 'in' and 'queryfile' are exclusive. */ + ret = !(queryfile == NULL); + if (ret) goto usage; + } + + ret = !reply_command(conf, section, engine, queryfile, + password, inkey, signer, chain, policy, + in, token_in, out, token_out, text); + break; + case CMD_VERIFY: + ret = !(((queryfile && !data && !digest) + || (!queryfile && data && !digest) + || (!queryfile && !data && digest)) + && in != NULL); + if (ret) goto usage; + + ret = !verify_command(data, digest, queryfile, in, token_in, + ca_path, ca_file, untrusted); + } + + goto cleanup; + + usage: + BIO_printf(bio_err, "usage:\n" + "ts -query [-rand file%cfile%c...] [-config configfile] " + "[-data file_to_hash] [-digest digest_bytes]" + "[-md2|-md4|-md5|-sha|-sha1|-mdc2|-ripemd160] " + "[-policy object_id] [-no_nonce] [-cert] " + "[-in request.tsq] [-out request.tsq] [-text]\n", + LIST_SEPARATOR_CHAR, LIST_SEPARATOR_CHAR); + BIO_printf(bio_err, "or\n" + "ts -reply [-config configfile] [-section tsa_section] " + "[-queryfile request.tsq] [-passin password] " + "[-signer tsa_cert.pem] [-inkey private_key.pem] " + "[-chain certs_file.pem] [-policy object_id] " + "[-in response.tsr] [-token_in] " + "[-out response.tsr] [-token_out] [-text] [-engine id]\n"); + BIO_printf(bio_err, "or\n" + "ts -verify [-data file_to_hash] [-digest digest_bytes] " + "[-queryfile request.tsq] " + "-in response.tsr [-token_in] " + "-CApath ca_path -CAfile ca_file.pem " + "-untrusted cert_file.pem\n"); + cleanup: + /* Clean up. */ + app_RAND_write_file(NULL, bio_err); + NCONF_free(conf); + OPENSSL_free(password); + OBJ_cleanup(); + if (free_bio_err) + { + BIO_free_all(bio_err); + bio_err = NULL; + } + + OPENSSL_EXIT(ret); + } + +/* + * Configuration file-related function definitions. + */ + +static ASN1_OBJECT *txt2obj(const char *oid) + { + ASN1_OBJECT *oid_obj = NULL; + + if (!(oid_obj = OBJ_txt2obj(oid, 0))) + BIO_printf(bio_err, "cannot convert %s to OID\n", oid); + + return oid_obj; + } + +static CONF *load_config_file(const char *configfile) + { + CONF *conf = NULL; + long errorline = -1; + + if (!configfile) configfile = getenv("OPENSSL_CONF"); + if (!configfile) configfile = getenv("SSLEAY_CONF"); + + if (configfile && + (!(conf = NCONF_new(NULL)) || + NCONF_load(conf, configfile, &errorline) <= 0)) + { + if (errorline <= 0) + BIO_printf(bio_err, "error loading the config file " + "'%s'\n", configfile); + else + BIO_printf(bio_err, "error on line %ld of config file " + "'%s'\n", errorline, configfile); + } + + if (conf != NULL) + { + const char *p; + + BIO_printf(bio_err,"Using configuration from %s\n", configfile); + p = NCONF_get_string(conf, NULL, ENV_OID_FILE); + if (p != NULL) + { + BIO *oid_bio = BIO_new_file(p, "r"); + if (!oid_bio) + ERR_print_errors(bio_err); + else + { + OBJ_create_objects(oid_bio); + BIO_free_all(oid_bio); + } + } + else + ERR_clear_error(); + if(!add_oid_section(bio_err, conf)) + ERR_print_errors(bio_err); + } + return conf; + } + +/* + * Query-related method definitions. + */ + +static int query_command(const char *data, char *digest, const EVP_MD *md, + const char *policy, int no_nonce, + int cert, const char *in, const char *out, int text) + { + int ret = 0; + TS_REQ *query = NULL; + BIO *in_bio = NULL; + BIO *data_bio = NULL; + BIO *out_bio = NULL; + + /* Build query object either from file or from scratch. */ + if (in != NULL) + { + if ((in_bio = BIO_new_file(in, "rb")) == NULL) goto end; + query = d2i_TS_REQ_bio(in_bio, NULL); + } + else + { + /* Open the file if no explicit digest bytes were specified. */ + if (!digest + && !(data_bio = BIO_open_with_default(data, "rb", stdin))) + goto end; + /* Creating the query object. */ + query = create_query(data_bio, digest, md, + policy, no_nonce, cert); + /* Saving the random number generator state. */ + } + if (query == NULL) goto end; + + /* Write query either in ASN.1 or in text format. */ + if ((out_bio = BIO_open_with_default(out, "wb", stdout)) == NULL) + goto end; + if (text) + { + /* Text output. */ + if (!TS_REQ_print_bio(out_bio, query)) + goto end; + } + else + { + /* ASN.1 output. */ + if (!i2d_TS_REQ_bio(out_bio, query)) + goto end; + } + + ret = 1; + + end: + ERR_print_errors(bio_err); + + /* Clean up. */ + BIO_free_all(in_bio); + BIO_free_all(data_bio); + BIO_free_all(out_bio); + TS_REQ_free(query); + + return ret; + } + +static BIO *BIO_open_with_default(const char *file, const char *mode, + FILE *default_fp) + { + return file == NULL ? + BIO_new_fp(default_fp, BIO_NOCLOSE) + : BIO_new_file(file, mode); + } + +static TS_REQ *create_query(BIO *data_bio, char *digest, const EVP_MD *md, + const char *policy, int no_nonce, int cert) + { + int ret = 0; + TS_REQ *ts_req = NULL; + int len; + TS_MSG_IMPRINT *msg_imprint = NULL; + X509_ALGOR *algo = NULL; + unsigned char *data = NULL; + ASN1_OBJECT *policy_obj = NULL; + ASN1_INTEGER *nonce_asn1 = NULL; + + /* Setting default message digest. */ + if (!md && !(md = EVP_get_digestbyname("sha1"))) goto err; + + /* Creating request object. */ + if (!(ts_req = TS_REQ_new())) goto err; + + /* Setting version. */ + if (!TS_REQ_set_version(ts_req, 1)) goto err; + + /* Creating and adding MSG_IMPRINT object. */ + if (!(msg_imprint = TS_MSG_IMPRINT_new())) goto err; + + /* Adding algorithm. */ + if (!(algo = X509_ALGOR_new())) goto err; + if (!(algo->algorithm = OBJ_nid2obj(EVP_MD_type(md)))) goto err; + if (!(algo->parameter = ASN1_TYPE_new())) goto err; + algo->parameter->type = V_ASN1_NULL; + if (!TS_MSG_IMPRINT_set_algo(msg_imprint, algo)) goto err; + + /* Adding message digest. */ + if ((len = create_digest(data_bio, digest, md, &data)) == 0) + goto err; + if (!TS_MSG_IMPRINT_set_msg(msg_imprint, data, len)) goto err; + + if (!TS_REQ_set_msg_imprint(ts_req, msg_imprint)) goto err; + + /* Setting policy if requested. */ + if (policy && !(policy_obj = txt2obj(policy))) goto err; + if (policy_obj && !TS_REQ_set_policy_id(ts_req, policy_obj)) goto err; + + /* Setting nonce if requested. */ + if (!no_nonce && !(nonce_asn1 = create_nonce(NONCE_LENGTH))) goto err; + if (nonce_asn1 && !TS_REQ_set_nonce(ts_req, nonce_asn1)) goto err; + + /* Setting certificate request flag if requested. */ + if (!TS_REQ_set_cert_req(ts_req, cert)) goto err; + + ret = 1; + err: + if (!ret) + { + TS_REQ_free(ts_req); + ts_req = NULL; + BIO_printf(bio_err, "could not create query\n"); + } + TS_MSG_IMPRINT_free(msg_imprint); + X509_ALGOR_free(algo); + OPENSSL_free(data); + ASN1_OBJECT_free(policy_obj); + ASN1_INTEGER_free(nonce_asn1); + return ts_req; + } + +static int create_digest(BIO *input, char *digest, const EVP_MD *md, + unsigned char **md_value) + { + int md_value_len; + + md_value_len = EVP_MD_size(md); + if (input) + { + /* Digest must be computed from an input file. */ + EVP_MD_CTX md_ctx; + unsigned char buffer[4096]; + int length; + + *md_value = OPENSSL_malloc(md_value_len); + if (*md_value == 0) goto err; + + EVP_DigestInit(&md_ctx, md); + while ((length = BIO_read(input, buffer, sizeof(buffer))) > 0) + { + EVP_DigestUpdate(&md_ctx, buffer, length); + } + EVP_DigestFinal(&md_ctx, *md_value, NULL); + } + else + { + /* Digest bytes are specified with digest. */ + long digest_len; + *md_value = string_to_hex(digest, &digest_len); + if (!*md_value || md_value_len != digest_len) + { + OPENSSL_free(*md_value); + *md_value = NULL; + BIO_printf(bio_err, "bad digest, %d bytes " + "must be specified\n", md_value_len); + goto err; + } + } + + return md_value_len; + err: + return 0; + } + +static ASN1_INTEGER *create_nonce(int bits) + { + unsigned char buf[20]; + ASN1_INTEGER *nonce = NULL; + int len = (bits - 1) / 8 + 1; + int i; + + /* Generating random byte sequence. */ + if (len > (int)sizeof(buf)) goto err; + if (!RAND_bytes(buf, len)) goto err; + + /* Find the first non-zero byte and creating ASN1_INTEGER object. */ + for (i = 0; i < len && !buf[i]; ++i); + if (!(nonce = ASN1_INTEGER_new())) goto err; + OPENSSL_free(nonce->data); + /* Allocate at least one byte. */ + nonce->length = len - i; + if (!(nonce->data = OPENSSL_malloc(nonce->length + 1))) goto err; + memcpy(nonce->data, buf + i, nonce->length); + + return nonce; + err: + BIO_printf(bio_err, "could not create nonce\n"); + ASN1_INTEGER_free(nonce); + return NULL; + } +/* + * Reply-related method definitions. + */ + +static int reply_command(CONF *conf, char *section, char *engine, + char *queryfile, char *passin, char *inkey, + char *signer, char *chain, const char *policy, + char *in, int token_in, + char *out, int token_out, int text) + { + int ret = 0; + TS_RESP *response = NULL; + BIO *in_bio = NULL; + BIO *query_bio = NULL; + BIO *inkey_bio = NULL; + BIO *signer_bio = NULL; + BIO *out_bio = NULL; + + /* Build response object either from response or query. */ + if (in != NULL) + { + if ((in_bio = BIO_new_file(in, "rb")) == NULL) goto end; + if (token_in) + { + /* We have a ContentInfo (PKCS7) object, add + 'granted' status info around it. */ + response = read_PKCS7(in_bio); + } + else + { + /* We have a ready-made TS_RESP object. */ + response = d2i_TS_RESP_bio(in_bio, NULL); + } + } + else + { + response = create_response(conf, section, engine, queryfile, + passin, inkey, signer, chain, + policy); + if (response) + BIO_printf(bio_err, "Response has been generated.\n"); + else + BIO_printf(bio_err, "Response is not generated.\n"); + } + if (response == NULL) goto end; + + /* Write response either in ASN.1 or text format. */ + if ((out_bio = BIO_open_with_default(out, "wb", stdout)) == NULL) + goto end; + if (text) + { + /* Text output. */ + if (token_out) + { + TS_TST_INFO *tst_info = TS_RESP_get_tst_info(response); + if (!TS_TST_INFO_print_bio(out_bio, tst_info)) goto end; + } + else + { + if (!TS_RESP_print_bio(out_bio, response)) goto end; + } + } + else + { + /* ASN.1 DER output. */ + if (token_out) + { + PKCS7 *token = TS_RESP_get_token(response); + if (!i2d_PKCS7_bio(out_bio, token)) goto end; + } + else + { + if (!i2d_TS_RESP_bio(out_bio, response)) goto end; + } + } + + ret = 1; + + end: + ERR_print_errors(bio_err); + + /* Clean up. */ + BIO_free_all(in_bio); + BIO_free_all(query_bio); + BIO_free_all(inkey_bio); + BIO_free_all(signer_bio); + BIO_free_all(out_bio); + TS_RESP_free(response); + + return ret; + } + +/* Reads a PKCS7 token and adds default 'granted' status info to it. */ +static TS_RESP *read_PKCS7(BIO *in_bio) + { + int ret = 0; + PKCS7 *token = NULL; + TS_TST_INFO *tst_info = NULL; + TS_RESP *resp = NULL; + TS_STATUS_INFO *si = NULL; + + /* Read PKCS7 object and extract the signed time stamp info. */ + if (!(token = d2i_PKCS7_bio(in_bio, NULL))) goto end; + if (!(tst_info = PKCS7_to_TS_TST_INFO(token))) goto end; + + /* Creating response object. */ + if (!(resp = TS_RESP_new())) goto end; + + /* Create granted status info. */ + if (!(si = TS_STATUS_INFO_new())) goto end; + if (!(ASN1_INTEGER_set(si->status, TS_STATUS_GRANTED))) goto end; + if (!TS_RESP_set_status_info(resp, si)) goto end; + + /* Setting encapsulated token. */ + TS_RESP_set_tst_info(resp, token, tst_info); + token = NULL; /* Ownership is lost. */ + tst_info = NULL; /* Ownership is lost. */ + + ret = 1; + end: + PKCS7_free(token); + TS_TST_INFO_free(tst_info); + if (!ret) + { + TS_RESP_free(resp); + resp = NULL; + } + TS_STATUS_INFO_free(si); + return resp; + } + +static TS_RESP *create_response(CONF *conf, const char *section, char *engine, + char *queryfile, char *passin, char *inkey, + char *signer, char *chain, const char *policy) + { + int ret = 0; + TS_RESP *response = NULL; + BIO *query_bio = NULL; + TS_RESP_CTX *resp_ctx = NULL; + + if (!(query_bio = BIO_new_file(queryfile, "rb"))) + goto end; + + /* Getting TSA configuration section. */ + if (!(section = TS_CONF_get_tsa_section(conf, section))) + goto end; + + /* Setting up response generation context. */ + if (!(resp_ctx = TS_RESP_CTX_new())) goto end; + + /* Setting serial number provider callback. */ + if (!TS_CONF_set_serial(conf, section, serial_cb, resp_ctx)) goto end; + + /* Setting default OpenSSL engine. */ + if (!TS_CONF_set_crypto_device(conf, section, engine)) goto end; + + /* Setting TSA signer certificate. */ + if (!TS_CONF_set_signer_cert(conf, section, signer, resp_ctx)) goto end; + + /* Setting TSA signer certificate chain. */ + if (!TS_CONF_set_certs(conf, section, chain, resp_ctx)) goto end; + + /* Setting TSA signer private key. */ + if (!TS_CONF_set_signer_key(conf, section, inkey, passin, resp_ctx)) + goto end; + + /* Setting default policy OID. */ + if (!TS_CONF_set_def_policy(conf, section, policy, resp_ctx)) goto end; + + /* Setting acceptable policy OIDs. */ + if (!TS_CONF_set_policies(conf, section, resp_ctx)) goto end; + + /* Setting the acceptable one-way hash algorithms. */ + if (!TS_CONF_set_digests(conf, section, resp_ctx)) goto end; + + /* Setting guarant |