ts.c 32.2 KB
Newer Older
1 2 3
/*
 * Written by Zoltan Glozik (zglozik@stones.com) for the OpenSSL project
 * 2002.
4 5 6 7 8 9 10 11 12
 */
/* ====================================================================
 * 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
13
 *    notice, this list of conditions and the following disclaimer.
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
 *
 * 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).
 *
 */

R
Richard Levitte 已提交
59 60 61 62 63 64 65 66 67 68 69 70 71
#include <openssl/opensslconf.h>
#ifndef OPENSSL_NO_TS

# 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>
# include <openssl/bn.h>
72

73
/* Request nonce length, in bits (must be a multiple of 8). */
R
Richard Levitte 已提交
74
# define NONCE_LENGTH            64
75

76
/* Name of config entry that defines the OID file. */
R
Richard Levitte 已提交
77
# define ENV_OID_FILE            "oid_file"
78

79
/* Is |EXACTLY_ONE| of three pointers set? */
R
Richard Levitte 已提交
80
# define EXACTLY_ONE(a, b, c) \
81 82 83
        (( a && !b && !c) || \
         ( b && !a && !c) || \
         ( c && !a && !b))
84 85 86 87 88 89

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,
90 91
                         const EVP_MD *md, const char *policy, int no_nonce,
                         int cert, const char *in, const char *out, int text);
92
static TS_REQ *create_query(BIO *data_bio, char *digest, const EVP_MD *md,
93
                            const char *policy, int no_nonce, int cert);
94
static int create_digest(BIO *input, char *digest,
95
                         const EVP_MD *md, unsigned char **md_value);
96 97 98
static ASN1_INTEGER *create_nonce(int bits);

/* Reply related functions. */
99 100
static int reply_command(CONF *conf, char *section, char *engine,
                         char *queryfile, char *passin, char *inkey,
101 102 103
                         const EVP_MD *md, char *signer, char *chain,
                         const char *policy, char *in, int token_in,
                         char *out, int token_out, int text);
104 105
static TS_RESP *read_PKCS7(BIO *in_bio);
static TS_RESP *create_response(CONF *conf, const char *section, char *engine,
106
                                char *queryfile, char *passin,
107 108
                                char *inkey, const EVP_MD *md, char *signer,
                                char *chain, const char *policy);
109
static ASN1_INTEGER *serial_cb(TS_RESP_CTX *ctx, void *data);
110 111 112 113 114
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,
115
                          char *in, int token_in,
F
fbroda 已提交
116 117
                          char *CApath, char *CAfile, char *untrusted,
                          X509_VERIFY_PARAM *vpm);
118 119
static TS_VERIFY_CTX *create_verify_ctx(char *data, char *digest,
                                        char *queryfile,
120
                                        char *CApath, char *CAfile,
F
fbroda 已提交
121 122 123 124
                                        char *untrusted,
                                        X509_VERIFY_PARAM *vpm);
static X509_STORE *create_cert_store(char *CApath, char *CAfile,
                                     X509_VERIFY_PARAM *vpm);
R
Rich Salz 已提交
125
static int verify_cb(int ok, X509_STORE_CTX *ctx);
126

127 128 129
typedef enum OPTION_choice {
    OPT_ERR = -1, OPT_EOF = 0, OPT_HELP,
    OPT_ENGINE, OPT_CONFIG, OPT_SECTION, OPT_QUERY, OPT_DATA,
F
fbroda 已提交
130
    OPT_DIGEST, OPT_RAND, OPT_TSPOLICY, OPT_NO_NONCE, OPT_CERT,
131 132 133
    OPT_IN, OPT_TOKEN_IN, OPT_OUT, OPT_TOKEN_OUT, OPT_TEXT,
    OPT_REPLY, OPT_QUERYFILE, OPT_PASSIN, OPT_INKEY, OPT_SIGNER,
    OPT_CHAIN, OPT_VERIFY, OPT_CAPATH, OPT_CAFILE, OPT_UNTRUSTED,
F
fbroda 已提交
134
    OPT_MD, OPT_V_ENUM
135 136 137 138 139 140 141 142 143 144 145
} OPTION_CHOICE;

OPTIONS ts_options[] = {
    {"help", OPT_HELP, '-', "Display this summary"},
    {"config", OPT_CONFIG, '<', "Configuration file"},
    {"section", OPT_SECTION, 's', "Section to use within config file"},
    {"query", OPT_QUERY, '-', "Generate a TS query"},
    {"data", OPT_DATA, '<', "File to hash"},
    {"digest", OPT_DIGEST, 's', "Digest (as a hex string)"},
    {"rand", OPT_RAND, 's',
     "Load the file(s) into the random number generator"},
F
fbroda 已提交
146
    {"tspolicy", OPT_TSPOLICY, 's', "Policy OID to use"},
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
    {"no_nonce", OPT_NO_NONCE, '-', "Do not include a nonce"},
    {"cert", OPT_CERT, '-', "Put cert request into query"},
    {"in", OPT_IN, '<', "Input file"},
    {"token_in", OPT_TOKEN_IN, '-', "Input is a PKCS#7 file"},
    {"out", OPT_OUT, '>', "Output file"},
    {"token_out", OPT_TOKEN_OUT, '-', "Output is a PKCS#7 file"},
    {"text", OPT_TEXT, '-', "Output text (not DER)"},
    {"reply", OPT_REPLY, '-', "Generate a TS reply"},
    {"queryfile", OPT_QUERYFILE, '<', "File containing a TS query"},
    {"passin", OPT_PASSIN, 's'},
    {"inkey", OPT_INKEY, '<', "File with private key for reply"},
    {"signer", OPT_SIGNER, 's'},
    {"chain", OPT_CHAIN, '<', "File with signer CA chain"},
    {"verify", OPT_VERIFY, '-', "Verify a TS response"},
    {"CApath", OPT_CAPATH, '/', "Path to trusted CA files"},
    {"CAfile", OPT_CAFILE, '<', "File with trusted CA certs"},
    {"untrusted", OPT_UNTRUSTED, '<', "File with untrusted certs"},
164
    {"", OPT_MD, '-', "Any supported digest"},
R
Richard Levitte 已提交
165
# ifndef OPENSSL_NO_ENGINE
166
    {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"},
R
Richard Levitte 已提交
167
# endif
F
fbroda 已提交
168 169 170
    {OPT_HELP_STR, 1, '-', "\nOptions specific to 'ts -verify': \n"},
    OPT_V_OPTIONS,
    {OPT_HELP_STR, 1, '-', "\n"},
171 172
    {NULL}
};
173

174 175 176 177 178 179
/*
 * This comand is so complex, special help is needed.
 */
static char* opt_helplist[] = {
    "Typical uses:",
    "ts -query [-rand file...] [-config file] [-data file]",
F
fbroda 已提交
180
    "          [-digest hexstring] [-tspolicy oid] [-no_nonce] [-cert]",
181 182 183 184 185
    "          [-in file] [-out file] [-text]",
    "  or",
    "ts -reply [-config file] [-section tsa_section]",
    "          [-queryfile file] [-passin password]",
    "          [-signer tsa_cert.pem] [-inkey private_key.pem]",
F
fbroda 已提交
186
    "          [-chain certs_file.pem] [-tspolicy oid]",
187
    "          [-in file] [-token_in] [-out file] [-token_out]",
R
Richard Levitte 已提交
188
# ifndef OPENSSL_NO_ENGINE
189
    "          [-text]",
R
Richard Levitte 已提交
190
# else
191
    "          [-text] [-engine id]",
R
Richard Levitte 已提交
192
# endif
193 194 195 196
    "  or",
    "ts -verify -CApath dir -CAfile file.pem -untrusted file.pem",
    "           [-data file] [-digest hexstring]",
    "           [-queryfile file] -in file [-token_in]",
F
fbroda 已提交
197
    "           [[options specific to 'ts -verify']]",
198 199 200 201
    NULL,
};

int ts_main(int argc, char **argv)
202 203
{
    CONF *conf = NULL;
204
    char *CAfile = NULL, *untrusted = NULL, *engine = NULL, *prog, **helpp;
R
Rich Salz 已提交
205 206
    char *configfile = default_config_file;
    char *section = NULL, *password = NULL;
207 208 209
    char *data = NULL, *digest = NULL, *rnd = NULL, *policy = NULL;
    char *in = NULL, *out = NULL, *queryfile = NULL, *passin = NULL;
    char *inkey = NULL, *signer = NULL, *chain = NULL, *CApath = NULL;
210
    const EVP_MD *md = NULL;
211 212
    OPTION_CHOICE o, mode = OPT_ERR;
    int ret = 1, no_nonce = 0, cert = 0, text = 0;
F
fbroda 已提交
213 214
    int vpmtouched = 0;
    X509_VERIFY_PARAM *vpm = NULL;
215 216 217 218 219
    /* Input is ContentInfo instead of TimeStampResp. */
    int token_in = 0;
    /* Output is ContentInfo instead of TimeStampResp. */
    int token_out = 0;

F
fbroda 已提交
220 221 222
    if ((vpm = X509_VERIFY_PARAM_new()) == NULL)
        goto end;

223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
    prog = opt_init(argc, argv, ts_options);
    while ((o = opt_next()) != OPT_EOF) {
        switch (o) {
        case OPT_EOF:
        case OPT_ERR:
 opthelp:
            BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
            goto end;
        case OPT_HELP:
            opt_help(ts_options);
            for (helpp = opt_helplist; *helpp; ++helpp)
                BIO_printf(bio_err, "%s\n", *helpp);
            ret = 0;
            goto end;
        case OPT_CONFIG:
            configfile = opt_arg();
            break;
        case OPT_SECTION:
            section = opt_arg();
            break;
        case OPT_QUERY:
        case OPT_REPLY:
        case OPT_VERIFY:
            if (mode != OPT_ERR)
                goto opthelp;
            mode = o;
            break;
        case OPT_DATA:
            data = opt_arg();
            break;
        case OPT_DIGEST:
            digest = opt_arg();
            break;
        case OPT_RAND:
            rnd = opt_arg();
            break;
F
fbroda 已提交
259
        case OPT_TSPOLICY:
260 261 262
            policy = opt_arg();
            break;
        case OPT_NO_NONCE:
263
            no_nonce = 1;
264 265
            break;
        case OPT_CERT:
266
            cert = 1;
267 268 269 270 271
            break;
        case OPT_IN:
            in = opt_arg();
            break;
        case OPT_TOKEN_IN:
272
            token_in = 1;
273 274 275 276 277
            break;
        case OPT_OUT:
            out = opt_arg();
            break;
        case OPT_TOKEN_OUT:
278
            token_out = 1;
279 280
            break;
        case OPT_TEXT:
281
            text = 1;
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
            break;
        case OPT_QUERYFILE:
            queryfile = opt_arg();
            break;
        case OPT_PASSIN:
            passin = opt_arg();
            break;
        case OPT_INKEY:
            inkey = opt_arg();
            break;
        case OPT_SIGNER:
            signer = opt_arg();
            break;
        case OPT_CHAIN:
            chain = opt_arg();
            break;
        case OPT_CAPATH:
            CApath = opt_arg();
            break;
        case OPT_CAFILE:
            CAfile = opt_arg();
            break;
        case OPT_UNTRUSTED:
            untrusted = opt_arg();
            break;
        case OPT_ENGINE:
            engine = opt_arg();
            break;
        case OPT_MD:
            if (!opt_md(opt_unknown(), &md))
                goto opthelp;
            break;
F
fbroda 已提交
314 315 316 317 318
        case OPT_V_CASES:
            if (!opt_verify(o, vpm))
                goto end;
            vpmtouched++;
            break;
319
        }
320
    }
321
    argc = opt_num_rest();
F
fbroda 已提交
322
    argv = opt_rest();
323 324
    if (mode == OPT_ERR || argc != 0)
        goto opthelp;
325 326

    /* Seed the random number generator if it is going to be used. */
327 328
    if (mode == OPT_QUERY && !no_nonce) {
        if (!app_RAND_load_file(NULL, 1) && rnd == NULL)
329 330 331 332 333 334 335
            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));
    }

336 337
    if (mode == OPT_REPLY && passin &&
        !app_passwd(passin, NULL, &password, NULL)) {
338
        BIO_printf(bio_err, "Error getting password.\n");
339
        goto end;
340 341
    }

R
Richard Levitte 已提交
342 343 344 345
    conf = load_config_file(configfile);
    if (!app_load_modules(conf))
        goto end;

346
    /* Check parameter consistency and execute the appropriate function. */
347
    switch (mode) {
348 349 350 351
    default:
    case OPT_ERR:
        goto opthelp;
    case OPT_QUERY:
F
fbroda 已提交
352 353
        if (vpmtouched)
            goto opthelp;
354
        if ((data != NULL) && (digest != NULL))
355
            goto opthelp;
356 357 358
        ret = !query_command(data, digest, md, policy, no_nonce, cert,
                             in, out, text);
        break;
359
    case OPT_REPLY:
F
fbroda 已提交
360 361
        if (vpmtouched)
            goto opthelp;
362 363
        if ((in != NULL) && (queryfile != NULL))
            goto opthelp;
364
        if (in == NULL) {
365
            if ((conf == NULL) || (token_in != 0))
366
                goto opthelp;
367 368
        }
        ret = !reply_command(conf, section, engine, queryfile,
369
                             password, inkey, md, signer, chain, policy,
370 371
                             in, token_in, out, token_out, text);
        break;
372
    case OPT_VERIFY:
373
        if ((in == NULL) || !EXACTLY_ONE(queryfile, data, digest))
374
            goto opthelp;
375
        ret = !verify_command(data, digest, queryfile, in, token_in,
F
fbroda 已提交
376 377
                              CApath, CAfile, untrusted, 
                              vpmtouched ? vpm : NULL);
378 379
    }

380
 end:
F
fbroda 已提交
381
    X509_VERIFY_PARAM_free(vpm);
382
    app_RAND_write_file(NULL);
383 384 385
    NCONF_free(conf);
    OPENSSL_free(password);
    OBJ_cleanup();
386
    return (ret);
387
}
388 389 390 391 392 393

/*
 * Configuration file-related function definitions.
 */

static ASN1_OBJECT *txt2obj(const char *oid)
394 395
{
    ASN1_OBJECT *oid_obj = NULL;
396

397
    if ((oid_obj = OBJ_txt2obj(oid, 0)) == NULL)
398
        BIO_printf(bio_err, "cannot convert %s to OID\n", oid);
399

400 401
    return oid_obj;
}
402 403

static CONF *load_config_file(const char *configfile)
404
{
R
Rich Salz 已提交
405
    CONF *conf = app_load_config(configfile);
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421

    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();
422
        if (!add_oid_section(conf))
423 424 425 426
            ERR_print_errors(bio_err);
    }
    return conf;
}
427 428 429 430 431

/*
 * Query-related method definitions.
 */
static int query_command(const char *data, char *digest, const EVP_MD *md,
432 433 434 435 436 437 438 439 440
                         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;

441
    /* Build query object. */
442
    if (in != NULL) {
443
        if ((in_bio = bio_open_default(in, 'r', FORMAT_ASN1)) == NULL)
444 445 446
            goto end;
        query = d2i_TS_REQ_bio(in_bio, NULL);
    } else {
447
        if (digest == NULL
448
            && (data_bio = bio_open_default(data, 'r', FORMAT_ASN1)) == NULL)
449 450 451 452 453 454 455
            goto end;
        query = create_query(data_bio, digest, md, policy, no_nonce, cert);
    }
    if (query == NULL)
        goto end;

    if (text) {
456 457
        if ((out_bio = bio_open_default(out, 'w', FORMAT_TEXT)) == NULL)
            goto end;
458 459 460
        if (!TS_REQ_print_bio(out_bio, query))
            goto end;
    } else {
461 462
        if ((out_bio = bio_open_default(out, 'w', FORMAT_ASN1)) == NULL)
            goto end;
463 464 465 466 467
        if (!i2d_TS_REQ_bio(out_bio, query))
            goto end;
    }

    ret = 1;
468 469

 end:
470 471 472 473 474 475 476
    ERR_print_errors(bio_err);
    BIO_free_all(in_bio);
    BIO_free_all(data_bio);
    BIO_free_all(out_bio);
    TS_REQ_free(query);
    return ret;
}
477 478

static TS_REQ *create_query(BIO *data_bio, char *digest, const EVP_MD *md,
479 480 481 482 483 484 485 486 487 488 489
                            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;

490
    if (md == NULL && (md = EVP_get_digestbyname("sha1")) == NULL)
491
        goto err;
492
    if ((ts_req = TS_REQ_new()) == NULL)
493 494 495
        goto err;
    if (!TS_REQ_set_version(ts_req, 1))
        goto err;
496
    if ((msg_imprint = TS_MSG_IMPRINT_new()) == NULL)
497
        goto err;
498
    if ((algo = X509_ALGOR_new()) == NULL)
499
        goto err;
500
    if ((algo->algorithm = OBJ_nid2obj(EVP_MD_type(md))) == NULL)
501
        goto err;
502
    if ((algo->parameter = ASN1_TYPE_new()) == NULL)
503 504 505 506 507 508 509 510 511 512
        goto err;
    algo->parameter->type = V_ASN1_NULL;
    if (!TS_MSG_IMPRINT_set_algo(msg_imprint, algo))
        goto err;
    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;
513
    if (policy && (policy_obj = txt2obj(policy)) == NULL)
514 515 516 517 518
        goto err;
    if (policy_obj && !TS_REQ_set_policy_id(ts_req, policy_obj))
        goto err;

    /* Setting nonce if requested. */
519
    if (!no_nonce && (nonce_asn1 = create_nonce(NONCE_LENGTH)) == NULL)
520 521 522 523 524 525 526
        goto err;
    if (nonce_asn1 && !TS_REQ_set_nonce(ts_req, nonce_asn1))
        goto err;
    if (!TS_REQ_set_cert_req(ts_req, cert))
        goto err;

    ret = 1;
527
 err:
528 529 530 531
    if (!ret) {
        TS_REQ_free(ts_req);
        ts_req = NULL;
        BIO_printf(bio_err, "could not create query\n");
532
        ERR_print_errors(bio_err);
533 534 535 536 537 538 539 540
    }
    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;
}
541 542

static int create_digest(BIO *input, char *digest, const EVP_MD *md,
543 544 545 546 547 548
                         unsigned char **md_value)
{
    int md_value_len;

    md_value_len = EVP_MD_size(md);
    if (md_value_len < 0)
549 550
        return 0;

551
    if (input) {
552
        EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
553 554 555
        unsigned char buffer[4096];
        int length;

556 557
        if (md_ctx == NULL)
            return 0;
R
Rich Salz 已提交
558
        *md_value = app_malloc(md_value_len, "digest buffer");
559
        EVP_DigestInit(md_ctx, md);
560
        while ((length = BIO_read(input, buffer, sizeof(buffer))) > 0) {
561
            EVP_DigestUpdate(md_ctx, buffer, length);
562
        }
563
        if (!EVP_DigestFinal(md_ctx, *md_value, NULL)) {
564
            EVP_MD_CTX_free(md_ctx);
565
            return 0;
566
        }
567
        EVP_MD_CTX_free(md_ctx);
568 569 570 571 572 573 574 575
    } else {
        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);
576
            return 0;
577 578 579 580
        }
    }
    return md_value_len;
}
581 582

static ASN1_INTEGER *create_nonce(int bits)
583 584 585 586 587 588 589 590 591 592 593 594
{
    unsigned char buf[20];
    ASN1_INTEGER *nonce = NULL;
    int len = (bits - 1) / 8 + 1;
    int i;

    if (len > (int)sizeof(buf))
        goto err;
    if (RAND_bytes(buf, len) <= 0)
        goto err;

    /* Find the first non-zero byte and creating ASN1_INTEGER object. */
595 596 597
    for (i = 0; i < len && !buf[i]; ++i)
        continue;
    if ((nonce = ASN1_INTEGER_new()) == NULL)
598 599 600
        goto err;
    OPENSSL_free(nonce->data);
    nonce->length = len - i;
R
Rich Salz 已提交
601
    nonce->data = app_malloc(nonce->length + 1, "nonce buffer");
602 603
    memcpy(nonce->data, buf + i, nonce->length);
    return nonce;
604

605
 err:
606 607 608 609 610
    BIO_printf(bio_err, "could not create nonce\n");
    ASN1_INTEGER_free(nonce);
    return NULL;
}

611 612 613 614
/*
 * Reply-related method definitions.
 */

615 616
static int reply_command(CONF *conf, char *section, char *engine,
                         char *queryfile, char *passin, char *inkey,
617 618
                         const EVP_MD *md, char *signer, char *chain,
                         const char *policy, char *in, int token_in,
619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
                         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;

    if (in != NULL) {
        if ((in_bio = BIO_new_file(in, "rb")) == NULL)
            goto end;
        if (token_in) {
            response = read_PKCS7(in_bio);
        } else {
            response = d2i_TS_RESP_bio(in_bio, NULL);
        }
    } else {
        response = create_response(conf, section, engine, queryfile,
639
                                   passin, inkey, md, signer, chain, policy);
640 641 642 643 644 645 646 647
        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;

648
    /* Write response. */
649
    if (text) {
650 651
        if ((out_bio = bio_open_default(out, 'w', FORMAT_TEXT)) == NULL)
        goto end;
652 653 654 655 656 657 658 659 660
        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 {
661 662
        if ((out_bio = bio_open_default(out, 'w', FORMAT_ASN1)) == NULL)
            goto end;
663 664 665 666 667 668 669 670 671 672 673
        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;
674 675

 end:
676 677 678 679 680 681 682 683 684
    ERR_print_errors(bio_err);
    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;
}
685 686 687

/* Reads a PKCS7 token and adds default 'granted' status info to it. */
static TS_RESP *read_PKCS7(BIO *in_bio)
688 689 690 691 692 693 694
{
    int ret = 0;
    PKCS7 *token = NULL;
    TS_TST_INFO *tst_info = NULL;
    TS_RESP *resp = NULL;
    TS_STATUS_INFO *si = NULL;

695
    if ((token = d2i_PKCS7_bio(in_bio, NULL)) == NULL)
696
        goto end;
697
    if ((tst_info = PKCS7_to_TS_TST_INFO(token)) == NULL)
698
        goto end;
699
    if ((resp = TS_RESP_new()) == NULL)
700
        goto end;
701
    if ((si = TS_STATUS_INFO_new()) == NULL)
702
        goto end;
R
Rich Salz 已提交
703
    if (!TS_STATUS_INFO_set_status(si, TS_STATUS_GRANTED))
704 705 706 707 708 709 710
        goto end;
    if (!TS_RESP_set_status_info(resp, si))
        goto end;
    TS_RESP_set_tst_info(resp, token, tst_info);
    token = NULL;               /* Ownership is lost. */
    tst_info = NULL;            /* Ownership is lost. */
    ret = 1;
711

712
 end:
713 714 715 716 717 718 719 720 721 722 723
    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,
724
                                char *queryfile, char *passin,
725 726
                                char *inkey, const EVP_MD *md, char *signer,
                                char *chain, const char *policy)
727 728 729 730 731 732
{
    int ret = 0;
    TS_RESP *response = NULL;
    BIO *query_bio = NULL;
    TS_RESP_CTX *resp_ctx = NULL;

733
    if ((query_bio = BIO_new_file(queryfile, "rb")) == NULL)
734
        goto end;
735
    if ((section = TS_CONF_get_tsa_section(conf, section)) == NULL)
736
        goto end;
737
    if ((resp_ctx = TS_RESP_CTX_new()) == NULL)
738 739 740
        goto end;
    if (!TS_CONF_set_serial(conf, section, serial_cb, resp_ctx))
        goto end;
R
Richard Levitte 已提交
741
# ifndef OPENSSL_NO_ENGINE
742 743
    if (!TS_CONF_set_crypto_device(conf, section, engine))
        goto end;
R
Richard Levitte 已提交
744
# endif
745 746 747 748 749 750
    if (!TS_CONF_set_signer_cert(conf, section, signer, resp_ctx))
        goto end;
    if (!TS_CONF_set_certs(conf, section, chain, resp_ctx))
        goto end;
    if (!TS_CONF_set_signer_key(conf, section, inkey, passin, resp_ctx))
        goto end;
751 752 753 754 755 756 757 758

    if (md) {
        if (!TS_RESP_CTX_set_signer_digest(resp_ctx, md))
            goto end;
    } else if (!TS_CONF_set_signer_digest(conf, section, NULL, resp_ctx)) {
            goto end;
    }

759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774
    if (!TS_CONF_set_def_policy(conf, section, policy, resp_ctx))
        goto end;
    if (!TS_CONF_set_policies(conf, section, resp_ctx))
        goto end;
    if (!TS_CONF_set_digests(conf, section, resp_ctx))
        goto end;
    if (!TS_CONF_set_accuracy(conf, section, resp_ctx))
        goto end;
    if (!TS_CONF_set_clock_precision_digits(conf, section, resp_ctx))
        goto end;
    if (!TS_CONF_set_ordering(conf, section, resp_ctx))
        goto end;
    if (!TS_CONF_set_tsa_name(conf, section, resp_ctx))
        goto end;
    if (!TS_CONF_set_ess_cert_id_chain(conf, section, resp_ctx))
        goto end;
775
    if ((response = TS_RESP_create_response(resp_ctx, query_bio)) == NULL)
776 777
        goto end;
    ret = 1;
778

779
 end:
780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803
    if (!ret) {
        TS_RESP_free(response);
        response = NULL;
    }
    TS_RESP_CTX_free(resp_ctx);
    BIO_free_all(query_bio);
    return response;
}

static ASN1_INTEGER *serial_cb(TS_RESP_CTX *ctx, void *data)
{
    const char *serial_file = (const char *)data;
    ASN1_INTEGER *serial = next_serial(serial_file);

    if (!serial) {
        TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION,
                                    "Error during serial number "
                                    "generation.");
        TS_RESP_CTX_add_failure_info(ctx, TS_INFO_ADD_INFO_NOT_AVAILABLE);
    } else
        save_ts_serial(serial_file, serial);

    return serial;
}
804 805

static ASN1_INTEGER *next_serial(const char *serialfile)
806 807 808 809 810 811
{
    int ret = 0;
    BIO *in = NULL;
    ASN1_INTEGER *serial = NULL;
    BIGNUM *bn = NULL;

812
    if ((serial = ASN1_INTEGER_new()) == NULL)
813 814
        goto err;

815
    if ((in = BIO_new_file(serialfile, "r")) == NULL) {
816 817 818 819 820 821 822 823 824 825 826 827
        ERR_clear_error();
        BIO_printf(bio_err, "Warning: could not open file %s for "
                   "reading, using serial number: 1\n", serialfile);
        if (!ASN1_INTEGER_set(serial, 1))
            goto err;
    } else {
        char buf[1024];
        if (!a2i_ASN1_INTEGER(in, serial, buf, sizeof(buf))) {
            BIO_printf(bio_err, "unable to load number from %s\n",
                       serialfile);
            goto err;
        }
828
        if ((bn = ASN1_INTEGER_to_BN(serial, NULL)) == NULL)
829 830 831 832 833
            goto err;
        ASN1_INTEGER_free(serial);
        serial = NULL;
        if (!BN_add_word(bn, 1))
            goto err;
834
        if ((serial = BN_to_ASN1_INTEGER(bn, NULL)) == NULL)
835 836 837
            goto err;
    }
    ret = 1;
838

839
 err:
840 841 842 843 844 845 846 847
    if (!ret) {
        ASN1_INTEGER_free(serial);
        serial = NULL;
    }
    BIO_free_all(in);
    BN_free(bn);
    return serial;
}
848 849

static int save_ts_serial(const char *serialfile, ASN1_INTEGER *serial)
850 851 852 853
{
    int ret = 0;
    BIO *out = NULL;

854
    if ((out = BIO_new_file(serialfile, "w")) == NULL)
855 856 857 858 859 860
        goto err;
    if (i2a_ASN1_INTEGER(out, serial) <= 0)
        goto err;
    if (BIO_puts(out, "\n") <= 0)
        goto err;
    ret = 1;
861
 err:
862 863 864 865 866 867
    if (!ret)
        BIO_printf(bio_err, "could not save serial number to %s\n",
                   serialfile);
    BIO_free_all(out);
    return ret;
}
868

869

870 871 872 873 874
/*
 * Verify-related method definitions.
 */

static int verify_command(char *data, char *digest, char *queryfile,
875
                          char *in, int token_in,
F
fbroda 已提交
876 877
                          char *CApath, char *CAfile, char *untrusted,
                          X509_VERIFY_PARAM *vpm)
878 879 880 881 882 883 884
{
    BIO *in_bio = NULL;
    PKCS7 *token = NULL;
    TS_RESP *response = NULL;
    TS_VERIFY_CTX *verify_ctx = NULL;
    int ret = 0;

885
    if ((in_bio = BIO_new_file(in, "rb")) == NULL)
886 887
        goto end;
    if (token_in) {
888
        if ((token = d2i_PKCS7_bio(in_bio, NULL)) == NULL)
889 890
            goto end;
    } else {
891
        if ((response = d2i_TS_RESP_bio(in_bio, NULL)) == NULL)
892 893 894
            goto end;
    }

895
    if ((verify_ctx = create_verify_ctx(data, digest, queryfile,
F
fbroda 已提交
896 897
                                        CApath, CAfile, untrusted,
                                        vpm)) == NULL)
898 899
        goto end;

900 901 902
    ret = token_in
        ? TS_RESP_verify_token(verify_ctx, token)
        : TS_RESP_verify_response(verify_ctx, response);
903 904

 end:
905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921
    printf("Verification: ");
    if (ret)
        printf("OK\n");
    else {
        printf("FAILED\n");
        ERR_print_errors(bio_err);
    }

    BIO_free_all(in_bio);
    PKCS7_free(token);
    TS_RESP_free(response);
    TS_VERIFY_CTX_free(verify_ctx);
    return ret;
}

static TS_VERIFY_CTX *create_verify_ctx(char *data, char *digest,
                                        char *queryfile,
922
                                        char *CApath, char *CAfile,
F
fbroda 已提交
923 924
                                        char *untrusted,
                                        X509_VERIFY_PARAM *vpm)
925 926 927 928 929
{
    TS_VERIFY_CTX *ctx = NULL;
    BIO *input = NULL;
    TS_REQ *request = NULL;
    int ret = 0;
R
Rich Salz 已提交
930
    int f = 0;
931 932

    if (data != NULL || digest != NULL) {
933
        if ((ctx = TS_VERIFY_CTX_new()) == NULL)
934
            goto err;
R
Rich Salz 已提交
935
        f = TS_VFY_VERSION | TS_VFY_SIGNER;
936
        if (data != NULL) {
R
Rich Salz 已提交
937 938
            f |= TS_VFY_DATA;
            if (TS_VERIFY_CTX_set_data(ctx, BIO_new_file(data, "rb")) == NULL)
939 940 941
                goto err;
        } else if (digest != NULL) {
            long imprint_len;
R
Rich Salz 已提交
942 943 944
            unsigned char *hexstr = string_to_hex(digest, &imprint_len);
            f |= TS_VFY_IMPRINT;
            if (TS_VERIFY_CTX_set_imprint(ctx, hexstr, imprint_len) == NULL) {
945 946 947 948 949 950
                BIO_printf(bio_err, "invalid digest string\n");
                goto err;
            }
        }

    } else if (queryfile != NULL) {
951
        if ((input = BIO_new_file(queryfile, "rb")) == NULL)
952
            goto err;
953
        if ((request = d2i_TS_REQ_bio(input, NULL)) == NULL)
954
            goto err;
955
        if ((ctx = TS_REQ_to_TS_VERIFY_CTX(request, NULL)) == NULL)
956 957 958 959 960
            goto err;
    } else
        return NULL;

    /* Add the signature verification flag and arguments. */
R
Rich Salz 已提交
961
    TS_VERIFY_CTX_add_flags(ctx, f | TS_VFY_SIGNATURE);
962 963

    /* Initialising the X509_STORE object. */
F
fbroda 已提交
964
    if (TS_VERIFY_CTX_set_store(ctx, create_cert_store(CApath, CAfile, vpm))
R
Rich Salz 已提交
965
            == NULL)
966 967 968
        goto err;

    /* Loading untrusted certificates. */
R
Rich Salz 已提交
969 970
    if (untrusted
        && TS_VERIFY_CTS_set_certs(ctx, TS_CONF_load_certs(untrusted)) == NULL)
971 972
        goto err;
    ret = 1;
973

974
 err:
975 976 977 978 979 980 981 982
    if (!ret) {
        TS_VERIFY_CTX_free(ctx);
        ctx = NULL;
    }
    BIO_free_all(input);
    TS_REQ_free(request);
    return ctx;
}
983

F
fbroda 已提交
984
static X509_STORE *create_cert_store(char *CApath, char *CAfile, X509_VERIFY_PARAM *vpm)
985 986 987 988 989 990 991
{
    X509_STORE *cert_ctx = NULL;
    X509_LOOKUP *lookup = NULL;
    int i;

    cert_ctx = X509_STORE_new();
    X509_STORE_set_verify_cb(cert_ctx, verify_cb);
992
    if (CApath != NULL) {
993 994 995 996 997
        lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_hash_dir());
        if (lookup == NULL) {
            BIO_printf(bio_err, "memory allocation failure\n");
            goto err;
        }
998
        i = X509_LOOKUP_add_dir(lookup, CApath, X509_FILETYPE_PEM);
999
        if (!i) {
1000
            BIO_printf(bio_err, "Error loading directory %s\n", CApath);
1001 1002 1003 1004
            goto err;
        }
    }

1005
    if (CAfile != NULL) {
1006 1007 1008 1009 1010
        lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_file());
        if (lookup == NULL) {
            BIO_printf(bio_err, "memory allocation failure\n");
            goto err;
        }
1011
        i = X509_LOOKUP_load_file(lookup, CAfile, X509_FILETYPE_PEM);
1012
        if (!i) {
1013
            BIO_printf(bio_err, "Error loading file %s\n", CAfile);
1014 1015 1016
            goto err;
        }
    }
F
fbroda 已提交
1017 1018 1019 1020

    if (vpm != NULL) 
        X509_STORE_set1_param(cert_ctx, vpm);

1021
    return cert_ctx;
1022

1023
 err:
1024 1025 1026
    X509_STORE_free(cert_ctx);
    return NULL;
}
1027

R
Rich Salz 已提交
1028
static int verify_cb(int ok, X509_STORE_CTX *ctx)
1029 1030 1031
{
    return ok;
}
R
Richard Levitte 已提交
1032
#endif