From 8c6afbc55cc8e2d036c0af5adbaff82d8117c6b2 Mon Sep 17 00:00:00 2001 From: Rob Percival Date: Thu, 25 Feb 2016 13:33:48 +0000 Subject: [PATCH] Verify SCT signatures Tests included in future commit, which adds CT policy validation. Reviewed-by: Ben Laurie Reviewed-by: Rich Salz --- crypto/ct/Makefile.in | 6 +- crypto/ct/build.info | 3 +- crypto/ct/ct_b64.c | 213 +++++++++++++++++++ crypto/ct/ct_err.c | 22 ++ crypto/ct/ct_locl.h | 40 ++++ crypto/ct/ct_log.c | 330 +++++++++++++++++++++++++++++ crypto/ct/ct_prn.c | 5 + crypto/ct/ct_sct.c | 66 ++++++ crypto/ct/ct_sct_ctx.c | 267 +++++++++++++++++++++++ crypto/ct/ct_vfy.c | 255 ++++++++++++++++++++++ crypto/include/internal/cryptlib.h | 3 + include/openssl/ct.h | 169 +++++++++++++++ include/openssl/ossl_typ.h | 3 + test/ct/log_list.conf | 38 ++++ util/libeay.num | 22 ++ 15 files changed, 1439 insertions(+), 3 deletions(-) create mode 100644 crypto/ct/ct_b64.c create mode 100644 crypto/ct/ct_log.c create mode 100644 crypto/ct/ct_sct_ctx.c create mode 100644 crypto/ct/ct_vfy.c create mode 100644 test/ct/log_list.conf diff --git a/crypto/ct/Makefile.in b/crypto/ct/Makefile.in index 21ff2313ce..de122df863 100644 --- a/crypto/ct/Makefile.in +++ b/crypto/ct/Makefile.in @@ -15,8 +15,10 @@ CFLAGS= $(INCLUDES) $(CFLAG) $(SHARED_CFLAG) GENERAL=Makefile LIB=$(TOP)/libcrypto.a -LIBSRC= ct_err.c ct_oct.c ct_prn.c ct_sct.c ct_x509v3.c -LIBOBJ= ct_err.o ct_oct.o ct_prn.o ct_sct.o ct_x509v3.o +LIBSRC= ct_b64.c ct_err.c ct_log.c ct_oct.c ct_prn.c ct_sct.c ct_sct_ctx.c \ + ct_vfy.c ct_x509v3.c +LIBOBJ= ct_b64.o ct_err.o ct_log.o ct_oct.o ct_prn.o ct_sct.o ct_sct_ctx.o \ + ct_vfy.o ct_x509v3.o SRC= $(LIBSRC) diff --git a/crypto/ct/build.info b/crypto/ct/build.info index b7766b6ed9..fbf2495813 100644 --- a/crypto/ct/build.info +++ b/crypto/ct/build.info @@ -1,2 +1,3 @@ LIBS=../../libcrypto -SOURCE[../../libcrypto]= ct_err.c ct_oct.c ct_prn.c ct_sct.c ct_x509v3.c +SOURCE[../../libcrypto]= ct_b64.c ct_err.c ct_log.c ct_oct.c ct_prn.c ct_sct.c \ + ct_sct_ctx.c ct_vfy.c ct_x509v3.c diff --git a/crypto/ct/ct_b64.c b/crypto/ct/ct_b64.c new file mode 100644 index 0000000000..a257b8f0d5 --- /dev/null +++ b/crypto/ct/ct_b64.c @@ -0,0 +1,213 @@ +/* + * Written by Rob Stradling (rob@comodo.com) and Stephen Henson + * (steve@openssl.org) for the OpenSSL project 2014. + */ +/* ==================================================================== + * Copyright (c) 2014 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 +#include + +#include +#include +#include + +/* + * TODO(robpercival): These macros are getting duplicated all over the place. + * Is there a single place they should be defined for re-use? + * Also, is there a good reason they aren't functions? + */ +#define n2s(c,s) ((s=(((unsigned int)((c)[0]))<<8) | \ + (((unsigned int)((c)[1])))), \ + c+=2) + +/* + * Decodes the base64 string |in| into |out|. + * A new string will be malloc'd and assigned to |out|. This will be owned by + * the caller. Do not provide a pre-allocated string in |out|. + */ +static int CT_base64_decode(const char *in, unsigned char **out) +{ + size_t inlen; + int outlen; + unsigned char *outbuf = NULL; + + if (in == NULL || out == NULL) { + CTerr(CT_F_CT_BASE64_DECODE, ERR_R_PASSED_NULL_PARAMETER); + goto err; + } + + inlen = strlen(in); + if (inlen == 0) { + *out = NULL; + return 0; + } + + outlen = (inlen / 4) * 3; + outbuf = OPENSSL_malloc(outlen); + if (outbuf == NULL) { + CTerr(CT_F_CT_BASE64_DECODE, ERR_R_MALLOC_FAILURE); + goto err; + } + + outlen = EVP_DecodeBlock(outbuf, (unsigned char *)in, inlen); + if (outlen < 0) { + OPENSSL_free(outbuf); + CTerr(CT_F_CT_BASE64_DECODE, CT_R_BASE64_DECODE_ERROR); + goto err; + } + + *out = outbuf; + return outlen; +err: + OPENSSL_free(outbuf); + return -1; +} + +SCT *SCT_new_from_base64(unsigned char version, const char *logid_base64, + ct_log_entry_type_t entry_type, uint64_t timestamp, + const char *extensions_base64, + const char *signature_base64) +{ + SCT *sct; + unsigned char *dec = NULL; + int declen; + + if (logid_base64 == NULL || + extensions_base64 == NULL || + signature_base64 == NULL) { + CTerr(CT_F_SCT_NEW_FROM_BASE64, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + + sct = SCT_new(); + if (sct == NULL) { + CTerr(CT_F_SCT_NEW_FROM_BASE64, ERR_R_MALLOC_FAILURE); + return NULL; + } + + /* + * RFC6962 section 4.1 says we "MUST NOT expect this to be 0", but we + * can only construct SCT versions that have been defined. + */ + if (!SCT_set_version(sct, version)) { + CTerr(CT_F_SCT_NEW_FROM_BASE64, CT_R_SCT_UNSUPPORTED_VERSION); + goto err; + } + + declen = CT_base64_decode(logid_base64, &dec); + if (declen < 0) { + CTerr(CT_F_SCT_NEW_FROM_BASE64, X509_R_BASE64_DECODE_ERROR); + goto err; + } + if (!SCT_set0_log_id(sct, dec, declen)) + goto err; + dec = NULL; + + declen = CT_base64_decode(extensions_base64, &dec); + if (declen < 0) { + CTerr(CT_F_SCT_NEW_FROM_BASE64, X509_R_BASE64_DECODE_ERROR); + goto err; + } + SCT_set0_extensions(sct, dec, declen); + dec = NULL; + + declen = CT_base64_decode(signature_base64, &dec); + if (declen < 0) { + CTerr(CT_F_SCT_NEW_FROM_BASE64, X509_R_BASE64_DECODE_ERROR); + goto err; + } + if (o2i_SCT_signature(sct, (const unsigned char **)&dec, declen) <= 0) + goto err; + + SCT_set_timestamp(sct, timestamp); + + if (!SCT_set_log_entry_type(sct, entry_type)) + goto err; + + return sct; + + err: + OPENSSL_free(dec); + SCT_free(sct); + return NULL; +} + +CTLOG *CTLOG_new_from_base64(const char *pkey_base64, const char *name) +{ + unsigned char *pkey_der; + int pkey_der_len; + EVP_PKEY *pkey = NULL; + CTLOG *log = NULL; + + pkey_der_len = CT_base64_decode(pkey_base64, &pkey_der); + if (pkey_der_len <= 0) { + CTerr(CT_F_CTLOG_NEW_FROM_BASE64, CT_R_LOG_CONF_INVALID_KEY); + return NULL; + } + + pkey = d2i_PUBKEY(NULL, (const unsigned char **)&pkey_der, pkey_der_len); + if (pkey == NULL) { + CTerr(CT_F_CTLOG_NEW_FROM_BASE64, CT_R_LOG_CONF_INVALID_KEY); + return NULL; + } + + log = CTLOG_new(pkey, name); + if (log == NULL) { + EVP_PKEY_free(pkey); + return NULL; + } + + return log; +} diff --git a/crypto/ct/ct_err.c b/crypto/ct/ct_err.c index 033872e096..6db237b534 100644 --- a/crypto/ct/ct_err.c +++ b/crypto/ct/ct_err.c @@ -69,6 +69,15 @@ # define ERR_REASON(reason) ERR_PACK(ERR_LIB_CT,0,reason) static ERR_STRING_DATA CT_str_functs[] = { + {ERR_FUNC(CT_F_CTLOG_NEW), "CTLOG_new"}, + {ERR_FUNC(CT_F_CTLOG_NEW_FROM_BASE64), "CTLOG_new_from_base64"}, + {ERR_FUNC(CT_F_CTLOG_NEW_FROM_CONF), "CTLOG_new_from_conf"}, + {ERR_FUNC(CT_F_CTLOG_NEW_NULL), "CTLOG_new_null"}, + {ERR_FUNC(CT_F_CTLOG_STORE_GET0_LOG_BY_ID), "CTLOG_STORE_get0_log_by_id"}, + {ERR_FUNC(CT_F_CTLOG_STORE_LOAD_CTX_NEW), "CTLOG_STORE_LOAD_CTX_new"}, + {ERR_FUNC(CT_F_CTLOG_STORE_LOAD_FILE), "CTLOG_STORE_load_file"}, + {ERR_FUNC(CT_F_CT_BASE64_DECODE), "CT_base64_decode"}, + {ERR_FUNC(CT_F_CT_V1_LOG_ID_FROM_PKEY), "CT_v1_log_id_from_pkey"}, {ERR_FUNC(CT_F_D2I_SCT_LIST), "d2i_SCT_LIST"}, {ERR_FUNC(CT_F_I2D_SCT_LIST), "i2d_SCT_LIST"}, {ERR_FUNC(CT_F_I2O_SCT), "i2o_SCT"}, @@ -77,7 +86,9 @@ static ERR_STRING_DATA CT_str_functs[] = { {ERR_FUNC(CT_F_O2I_SCT), "o2i_SCT"}, {ERR_FUNC(CT_F_O2I_SCT_LIST), "o2i_SCT_LIST"}, {ERR_FUNC(CT_F_O2I_SCT_SIGNATURE), "o2i_SCT_signature"}, + {ERR_FUNC(CT_F_SCT_CTX_NEW), "SCT_CTX_new"}, {ERR_FUNC(CT_F_SCT_NEW), "SCT_new"}, + {ERR_FUNC(CT_F_SCT_NEW_FROM_BASE64), "SCT_new_from_base64"}, {ERR_FUNC(CT_F_SCT_SET0_LOG_ID), "SCT_set0_log_id"}, {ERR_FUNC(CT_F_SCT_SET1_EXTENSIONS), "SCT_set1_extensions"}, {ERR_FUNC(CT_F_SCT_SET1_LOG_ID), "SCT_set1_log_id"}, @@ -86,15 +97,26 @@ static ERR_STRING_DATA CT_str_functs[] = { {ERR_FUNC(CT_F_SCT_SET_SIGNATURE_NID), "SCT_set_signature_nid"}, {ERR_FUNC(CT_F_SCT_SET_VERSION), "SCT_set_version"}, {ERR_FUNC(CT_F_SCT_SIGNATURE_IS_VALID), "SCT_signature_is_valid"}, + {ERR_FUNC(CT_F_SCT_VERIFY), "SCT_verify"}, + {ERR_FUNC(CT_F_SCT_VERIFY_V1), "SCT_verify_v1"}, {0, NULL} }; static ERR_STRING_DATA CT_str_reasons[] = { + {ERR_REASON(CT_R_BASE64_DECODE_ERROR), "base64 decode error"}, {ERR_REASON(CT_R_INVALID_LOG_ID_LENGTH), "invalid log id length"}, + {ERR_REASON(CT_R_LOG_CONF_INVALID), "log conf invalid"}, + {ERR_REASON(CT_R_LOG_CONF_INVALID_KEY), "log conf invalid key"}, + {ERR_REASON(CT_R_LOG_CONF_MISSING_DESCRIPTION), + "log conf missing description"}, + {ERR_REASON(CT_R_LOG_CONF_MISSING_KEY), "log conf missing key"}, + {ERR_REASON(CT_R_LOG_KEY_INVALID), "log key invalid"}, {ERR_REASON(CT_R_SCT_INVALID), "sct invalid"}, {ERR_REASON(CT_R_SCT_INVALID_SIGNATURE), "sct invalid signature"}, {ERR_REASON(CT_R_SCT_LIST_INVALID), "sct list invalid"}, + {ERR_REASON(CT_R_SCT_LOG_ID_MISMATCH), "sct log id mismatch"}, {ERR_REASON(CT_R_SCT_NOT_SET), "sct not set"}, + {ERR_REASON(CT_R_SCT_UNSUPPORTED_VERSION), "sct unsupported version"}, {ERR_REASON(CT_R_UNRECOGNIZED_SIGNATURE_NID), "unrecognized signature nid"}, {ERR_REASON(CT_R_UNSUPPORTED_ENTRY_TYPE), "unsupported entry type"}, diff --git a/crypto/ct/ct_locl.h b/crypto/ct/ct_locl.h index 9b76d16207..9409b01a30 100644 --- a/crypto/ct/ct_locl.h +++ b/crypto/ct/ct_locl.h @@ -91,8 +91,48 @@ struct sct_st { size_t sig_len; /* Log entry type */ ct_log_entry_type_t entry_type; + /* Where this SCT was found, e.g. certificate, OCSP response, etc. */ + sct_source_t source; + /* The CT log that produced this SCT. */ + CTLOG *log; }; +/* Miscellaneous data that is useful when verifying an SCT */ +struct sct_ctx_st { + /* Public key */ + EVP_PKEY *pkey; + /* Hash of public key */ + unsigned char *pkeyhash; + size_t pkeyhashlen; + /* For pre-certificate: issuer public key hash */ + unsigned char *ihash; + size_t ihashlen; + /* certificate encoding */ + unsigned char *certder; + size_t certderlen; + /* pre-certificate encoding */ + unsigned char *preder; + size_t prederlen; +}; + +/* + * Creates a new context for verifying an SCT. + */ +SCT_CTX *SCT_CTX_new(void); +/* + * Deletes an SCT verification context. + */ +void SCT_CTX_free(SCT_CTX *sctx); + +/* Sets the certificate that the SCT is related to */ +int SCT_CTX_set1_cert(SCT_CTX *sctx, X509 *cert, X509 *presigner); +/* Sets the issuer of the certificate that the SCT is related to */ +int SCT_CTX_set1_issuer(SCT_CTX *sctx, const X509 *issuer); +/* Sets the public key of the issuer of the certificate that the SCT relates to */ +int SCT_CTX_set1_issuer_pubkey(SCT_CTX *sctx, X509_PUBKEY *pubkey); +/* Sets the public key of the CT log that the SCT is from */ +int SCT_CTX_set1_pubkey(SCT_CTX *sctx, X509_PUBKEY *pubkey); + /* * Does this SCT have the minimum fields populated to be usuable? * Returns 1 if so, 0 otherwise. diff --git a/crypto/ct/ct_log.c b/crypto/ct/ct_log.c new file mode 100644 index 0000000000..aa3afc4ce3 --- /dev/null +++ b/crypto/ct/ct_log.c @@ -0,0 +1,330 @@ +/* Author: Adam Eijdenberg . */ +/* ==================================================================== + * Copyright (c) 1998-2016 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 + * openssl-core@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 +#include +#include +#include +#include + +#include "internal/cryptlib.h" + +/* + * Information about a CT log server. + */ +struct ctlog_st { + char *name; + uint8_t log_id[CT_V1_HASHLEN]; + EVP_PKEY *public_key; +}; + +/* + * A store for multiple CTLOG instances. + * It takes ownership of any CTLOG instances added to it. + */ +struct ctlog_store_st { + STACK_OF(CTLOG) *logs; +}; + +/* The context when loading a CT log list from a CONF file. */ +typedef struct ctlog_store_load_ctx_st { + CTLOG_STORE *log_store; + CONF *conf; +} CTLOG_STORE_LOAD_CTX; + +/* + * Creates an empty context for loading a CT log store. + * It should be populated before use. + */ +static CTLOG_STORE_LOAD_CTX *CTLOG_STORE_LOAD_CTX_new(); + +/* + * Deletes a CT log store load context. + * Does not delete any of the fields. + */ +static void CTLOG_STORE_LOAD_CTX_free(CTLOG_STORE_LOAD_CTX* ctx); + +static CTLOG_STORE_LOAD_CTX *CTLOG_STORE_LOAD_CTX_new() +{ + CTLOG_STORE_LOAD_CTX *ctx = OPENSSL_zalloc(sizeof(CTLOG_STORE_LOAD_CTX)); + if (ctx == NULL) { + CTerr(CT_F_CTLOG_STORE_LOAD_CTX_NEW, ERR_R_MALLOC_FAILURE); + goto err; + } + + return ctx; + +err: + CTLOG_STORE_LOAD_CTX_free(ctx); + return NULL; +} + +static void CTLOG_STORE_LOAD_CTX_free(CTLOG_STORE_LOAD_CTX* ctx) +{ + if (ctx == NULL) + return; + + OPENSSL_free(ctx); +} + +/* Converts a log's public key into a SHA256 log ID */ +static int CT_v1_log_id_from_pkey(EVP_PKEY *pkey, + unsigned char log_id[CT_V1_HASHLEN]) +{ + int ret = 0; + unsigned char *pkey_der = NULL; + int pkey_der_len = i2d_PUBKEY(pkey, &pkey_der); + if (pkey_der_len <= 0) { + CTerr(CT_F_CT_V1_LOG_ID_FROM_PKEY, CT_R_LOG_KEY_INVALID); + goto err; + } + SHA256(pkey_der, pkey_der_len, log_id); + ret = 1; +err: + OPENSSL_free(pkey_der); + return ret; +} + +CTLOG_STORE *CTLOG_STORE_new(void) +{ + CTLOG_STORE *ret = OPENSSL_malloc(sizeof(CTLOG_STORE)); + if (ret == NULL) + goto err; + ret->logs = sk_CTLOG_new_null(); + if (ret->logs == NULL) + goto err; + return ret; +err: + CTLOG_STORE_free(ret); + return NULL; +} + +void CTLOG_STORE_free(CTLOG_STORE *store) +{ + if (store != NULL) { + sk_CTLOG_pop_free(store->logs, CTLOG_free); + OPENSSL_free(store); + } +} + +static CTLOG *CTLOG_new_from_conf(const CONF *conf, const char *section) +{ + CTLOG *ret = NULL; + char *description; + char *pkey_base64; + if (conf == NULL || section == NULL) { + CTerr(CT_F_CTLOG_NEW_FROM_CONF, ERR_R_PASSED_NULL_PARAMETER); + goto end; + } + + description = NCONF_get_string(conf, section, "description"); + + if (description == NULL) { + CTerr(CT_F_CTLOG_NEW_FROM_CONF, CT_R_LOG_CONF_MISSING_DESCRIPTION); + goto end; + } + + pkey_base64 = NCONF_get_string(conf, section, "key"); + + if (pkey_base64 == NULL) { + CTerr(CT_F_CTLOG_NEW_FROM_CONF, CT_R_LOG_CONF_MISSING_KEY); + goto end; + } + + ret = CTLOG_new_from_base64(pkey_base64, description); + if (ret == NULL) { + CTerr(CT_F_CTLOG_NEW_FROM_CONF, CT_R_LOG_CONF_INVALID); + goto end; + } + +end: + return ret; +} + +int CTLOG_STORE_load_default_file(CTLOG_STORE *store) +{ + char *fpath = (char *)getenv(CTLOG_FILE_EVP); + if (fpath == NULL) + fpath = CTLOG_FILE; + return CTLOG_STORE_load_file(store, fpath); +} + +static int CTLOG_STORE_load_log(const char *log_name, int log_name_len, void *arg) +{ + CTLOG_STORE_LOAD_CTX *load_ctx = arg; + CTLOG *ct_log; + + /* log_name may not be null-terminated, so fix that before using it */ + char *tmp = OPENSSL_strndup(log_name, log_name_len); + ct_log = CTLOG_new_from_conf(load_ctx->conf, tmp); + OPENSSL_free(tmp); + if (ct_log == NULL) + return 0; + + sk_CTLOG_push(load_ctx->log_store->logs, ct_log); + return 1; +} + +int CTLOG_STORE_load_file(CTLOG_STORE *store, const char *file) +{ + int ret = -1; + char *enabled_logs; + CTLOG_STORE_LOAD_CTX* load_ctx = CTLOG_STORE_LOAD_CTX_new(); + load_ctx->log_store = store; + load_ctx->conf = NCONF_new(NULL); + if (load_ctx->conf == NULL) + goto end; + + ret = NCONF_load(load_ctx->conf, file, NULL); + if (ret <= 0) { + CTerr(CT_F_CTLOG_STORE_LOAD_FILE, CT_R_LOG_CONF_INVALID); + goto end; + } + + enabled_logs = NCONF_get_string(load_ctx->conf, NULL, "enabled_logs"); + CONF_parse_list(enabled_logs, ',', 1, CTLOG_STORE_load_log, load_ctx); + +end: + NCONF_free(load_ctx->conf); + CTLOG_STORE_LOAD_CTX_free(load_ctx); + return ret; +} + +/* + * Initialize a new CTLOG object. + * Takes ownership of the public key. + * Copies the name. + */ +CTLOG *CTLOG_new(EVP_PKEY *public_key, const char *name) +{ + CTLOG *ret = NULL; + if (public_key == NULL || name == NULL) { + CTerr(CT_F_CTLOG_NEW, ERR_R_PASSED_NULL_PARAMETER); + goto err; + } + ret = CTLOG_new_null(); + if (ret == NULL) + goto err; + ret->name = OPENSSL_strdup(name); + if (ret->name == NULL) + goto err; + ret->public_key = public_key; + if (CT_v1_log_id_from_pkey(public_key, ret->log_id) != 1) + goto err; + return ret; +err: + CTLOG_free(ret); + return NULL; +} + +CTLOG *CTLOG_new_null(void) +{ + CTLOG *ret = OPENSSL_zalloc(sizeof(CTLOG)); + if (ret == NULL) + CTerr(CT_F_CTLOG_NEW_NULL, ERR_R_MALLOC_FAILURE); + return ret; +} + +/* Frees CT log and associated structures */ +void CTLOG_free(CTLOG *log) +{ + if (log != NULL) { + OPENSSL_free(log->name); + log->name = NULL; + + EVP_PKEY_free(log->public_key); + log->public_key = NULL; + + OPENSSL_free(log); + } +} + +const char *CTLOG_get0_name(CTLOG *log) +{ + return log->name; +} + +void CTLOG_get0_log_id(CTLOG *log, uint8_t **log_id, size_t *log_id_len) +{ + *log_id = log->log_id; + *log_id_len = CT_V1_HASHLEN; +} + +EVP_PKEY *CTLOG_get0_public_key(CTLOG *log) +{ + return log->public_key; +} + +/* + * Given a log ID, finds the matching log. + * Returns NULL if no match found. + */ +CTLOG *CTLOG_STORE_get0_log_by_id(const CTLOG_STORE *store, + const uint8_t *log_id, + size_t log_id_len) +{ + int i; + if (store == NULL) { + CTerr(CT_F_CTLOG_STORE_GET0_LOG_BY_ID, ERR_R_PASSED_NULL_PARAMETER); + goto end; + } + for (i = 0; i < sk_CTLOG_num(store->logs); ++i) { + CTLOG *log = sk_CTLOG_value(store->logs, i); + if (memcmp(log->log_id, log_id, log_id_len) == 0) + return log; + } +end: + return NULL; +} diff --git a/crypto/ct/ct_prn.c b/crypto/ct/ct_prn.c index 73aba7e578..3983c3cb3b 100644 --- a/crypto/ct/ct_prn.c +++ b/crypto/ct/ct_prn.c @@ -107,6 +107,11 @@ void SCT_print(const SCT *sct, BIO *out, int indent) BIO_printf(out, "v1 (0x0)"); + if (sct->log != NULL) { + BIO_printf(out, "\n%*sLog : %s", indent + 4, "", + SCT_get0_log_name(sct)); + } + BIO_printf(out, "\n%*sLog ID : ", indent + 4, ""); BIO_hex_string(out, indent + 16, 16, sct->log_id, sct->log_id_len); diff --git a/crypto/ct/ct_sct.c b/crypto/ct/ct_sct.c index 81e51f0f61..62cadc9743 100644 --- a/crypto/ct/ct_sct.c +++ b/crypto/ct/ct_sct.c @@ -239,6 +239,11 @@ size_t SCT_get0_log_id(const SCT *sct, unsigned char **log_id) return sct->log_id_len; } +const char *SCT_get0_log_name(const SCT *sct) +{ + return CTLOG_get0_name(sct->log); +} + uint64_t SCT_get_timestamp(const SCT *sct) { return sct->timestamp; @@ -291,3 +296,64 @@ int SCT_signature_is_complete(const SCT *sct) sct->sig != NULL && sct->sig_len > 0; } +sct_source_t SCT_get_source(const SCT *sct) +{ + return sct->source; +} + +int SCT_set_source(SCT *sct, sct_source_t source) +{ + sct->source = source; + switch (source) { + case SCT_SOURCE_TLS_EXTENSION: + case SCT_SOURCE_OCSP_STAPLED_RESPONSE: + return SCT_set_log_entry_type(sct, CT_LOG_ENTRY_TYPE_X509); + case SCT_SOURCE_X509V3_EXTENSION: + return SCT_set_log_entry_type(sct, CT_LOG_ENTRY_TYPE_PRECERT); + default: /* if we aren't sure, leave the log entry type alone */ + return 1; + } +} + +int SCT_LIST_set_source(const STACK_OF(SCT) *scts, sct_source_t source) +{ + int i, ret = 1; + + for (i = 0; i < sk_SCT_num(scts); ++i) { + ret = SCT_set_source(sk_SCT_value(scts, i), source); + if (ret != 1) + break; + } + + return ret; +} + +CTLOG *SCT_get0_log(const SCT *sct) +{ + return sct->log; +} + +int SCT_set0_log(SCT *sct, const CTLOG_STORE *ct_logs) +{ + sct->log = + CTLOG_STORE_get0_log_by_id(ct_logs, sct->log_id, sct->log_id_len); + + return sct->log != NULL; +} + +int SCT_LIST_set0_logs(STACK_OF(SCT) *sct_list, const CTLOG_STORE *ct_logs) +{ + int sct_logs_found = 0; + int i; + + for (i = 0; i < sk_SCT_num(sct_list); ++i) { + SCT *sct = sk_SCT_value(sct_list, i); + + if (sct->log == NULL) + SCT_set0_log(sct, ct_logs); + if (sct->log != NULL) + ++sct_logs_found; + } + + return sct_logs_found; +} diff --git a/crypto/ct/ct_sct_ctx.c b/crypto/ct/ct_sct_ctx.c new file mode 100644 index 0000000000..4b0da42cd3 --- /dev/null +++ b/crypto/ct/ct_sct_ctx.c @@ -0,0 +1,267 @@ +/* + * Written by Rob Stradling (rob@comodo.com) and Stephen Henson + * (steve@openssl.org) for the OpenSSL project 2014. + */ +/* ==================================================================== + * Copyright (c) 2014 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). + * + */ + +#ifdef OPENSSL_NO_CT +# error "CT is disabled" +#endif + +#include +#include + +#include +#include +#include + +#include "ct_locl.h" + +SCT_CTX *SCT_CTX_new(void) +{ + SCT_CTX *sctx = OPENSSL_zalloc(sizeof(SCT_CTX)); + if (sctx == NULL) + CTerr(CT_F_SCT_CTX_NEW, ERR_R_MALLOC_FAILURE); + return sctx; +} + +void SCT_CTX_free(SCT_CTX *sctx) +{ + if (sctx == NULL) + return; + EVP_PKEY_free(sctx->pkey); + OPENSSL_free(sctx->pkeyhash); + OPENSSL_free(sctx->ihash); + OPENSSL_free(sctx->certder); + OPENSSL_free(sctx->preder); + OPENSSL_free(sctx); +} + +/* retrieve extension index checking for duplicates */ +static int sct_get_ext(X509 *cert, int nid) +{ + int rv = X509_get_ext_by_NID(cert, nid, -1); + if (rv >= 0 && X509_get_ext_by_NID(cert, nid, rv) >= 0) + return -2; + return rv; +} + +/* + * modify certificate by deleting extensions, copying issuer + * and AKID if necessary. + */ +static int sct_cert_fixup(X509 *cert, X509 *presigner) +{ + int preidx, certidx; + if (presigner == NULL) + return 1; + preidx = sct_get_ext(presigner, NID_authority_key_identifier); + certidx = sct_get_ext(cert, NID_authority_key_identifier); + /* Invalid certificate if duplicate */ + if (preidx == -2 || certidx == -2) + return 0; + /* AKID must be present in both certificate or absent in both */ + if (preidx >= 0 && certidx == -1) + return 0; + if (preidx == -1 && certidx >= 0) + return 0; + /* Copy issuer name */ + if (!X509_set_issuer_name(cert, X509_get_issuer_name(presigner))) + return 0; + if (preidx != -1) { + /* Retrieve and copy AKID encoding */ + X509_EXTENSION *preext = X509_get_ext(presigner, preidx); + X509_EXTENSION *certext = X509_get_ext(cert, certidx); + ASN1_OCTET_STRING *preextdata; + /* Should never happen */ + if (preext == NULL || certext == NULL) + return 0; + preextdata = X509_EXTENSION_get_data(preext); + if (preextdata == NULL || !X509_EXTENSION_set_data(certext, preextdata)) + return 0; + } + return 1; +} + +int SCT_CTX_set1_cert(SCT_CTX *sctx, X509 *cert, X509 *presigner) +{ + unsigned char *certder = NULL, *preder = NULL; + X509 *pretmp = NULL; + int certderlen = 0, prederlen = 0; + int idx = -1, idxp = -1; + idxp = sct_get_ext(cert, NID_ct_precert_poison); + /* Duplicate poison */ + if (idxp == -2) + goto err; + /* If no poison store encoding */ + if (idxp == -1) { + /* If presigner must have poison */ + if (presigner) + goto err; + certderlen = i2d_X509(cert, &certder); + if (certderlen < 0) + goto err; + } + /* See if have precert scts extension */ + idx = X509_get_ext_by_NID(cert, NID_ct_precert_scts, -1); + /* Duplicate scts */ + if (idx == -2) + goto err; + if (idx >= 0) { + /* Can't have both poison and scts */ + if (idxp >= 0) + goto err; + } else + idx = idxp; + if (idx >= 0) { + X509_EXTENSION *ext; + /* + * Take a copy of certificate so we don't modify passed version + */ + pretmp = X509_dup(cert); + if (pretmp == NULL) + goto err; + ext = X509_delete_ext(pretmp, idx); + X509_EXTENSION_free(ext); + if (!sct_cert_fixup(pretmp, presigner)) + goto err; + + prederlen = i2d_re_X509_tbs(pretmp, &preder); + if (prederlen <= 0) + goto err; + } + + X509_free(pretmp); + + OPENSSL_free(sctx->certder); + sctx->certder = certder; + sctx->certderlen = certderlen; + + OPENSSL_free(sctx->preder); + sctx->preder = preder; + sctx->prederlen = prederlen; + + return 1; + + err: + OPENSSL_free(certder); + OPENSSL_free(preder); + X509_free(pretmp); + return 0; +} + +static int CT_public_key_hash(X509_PUBKEY *pkey, unsigned char **hash, + size_t *hash_len) +{ + int ret = -1; + unsigned char *md = NULL, *der = NULL; + int der_len; + unsigned int md_len; + if (pkey == NULL) + goto err; + /* Reuse buffer if possible */ + if (*hash != NULL && *hash_len >= SHA256_DIGEST_LENGTH) { + md = *hash; + } else { + md = OPENSSL_malloc(SHA256_DIGEST_LENGTH); + if (md == NULL) + goto err; + } + + /* Calculate key hash */ + der_len = i2d_X509_PUBKEY(pkey, &der); + if (der_len <= 0) + goto err; + if (!EVP_Digest(der, der_len, md, &md_len, EVP_sha256(), NULL)) + goto err; + if (md != *hash) { + OPENSSL_free(*hash); + *hash = md; + *hash_len = SHA256_DIGEST_LENGTH; + } + md = NULL; + ret = 1; + err: + OPENSSL_free(md); + OPENSSL_free(der); + return ret; +} + +int SCT_CTX_set1_issuer(SCT_CTX *sctx, const X509 *issuer) +{ + return CT_public_key_hash(X509_get_X509_PUBKEY(issuer), &sctx->ihash, + &sctx->ihashlen); +} + +int SCT_CTX_set1_issuer_pubkey(SCT_CTX *sctx, X509_PUBKEY *pubkey) +{ + return CT_public_key_hash(pubkey, &sctx->ihash, &sctx->ihashlen); +} + +int SCT_CTX_set1_pubkey(SCT_CTX *sctx, X509_PUBKEY *pubkey) +{ + EVP_PKEY *pkey = X509_PUBKEY_get(pubkey); + if (pkey == NULL) + return 0; + + if (!CT_public_key_hash(pubkey, &sctx->pkeyhash, &sctx->pkeyhashlen)) { + EVP_PKEY_free(pkey); + return 0; + } + + EVP_PKEY_free(sctx->pkey); + sctx->pkey = pkey; + return 1; +} diff --git a/crypto/ct/ct_vfy.c b/crypto/ct/ct_vfy.c new file mode 100644 index 0000000000..41fdcaee60 --- /dev/null +++ b/crypto/ct/ct_vfy.c @@ -0,0 +1,255 @@ +/* + * Written by Rob Stradling (rob@comodo.com) and Stephen Henson + * (steve@openssl.org) for the OpenSSL project 2014. + */ +/* ==================================================================== + * Copyright (c) 2014 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 + +#include +#include +#include +#include + +#include "ct_locl.h" + +#define n2s(c,s) ((s=(((unsigned int)((c)[0]))<< 8)| \ + (((unsigned int)((c)[1])) )),c+=2) + +#define s2n(s,c) ((c[0]=(unsigned char)(((s)>> 8)&0xff), \ + c[1]=(unsigned char)(((s) )&0xff)),c+=2) + +#define l2n3(l,c) ((c[0]=(unsigned char)(((l)>>16)&0xff), \ + c[1]=(unsigned char)(((l)>> 8)&0xff), \ + c[2]=(unsigned char)(((l) )&0xff)),c+=3) + +#define n2l8(c,l) (l =((uint64_t)(*((c)++)))<<56, \ + l|=((uint64_t)(*((c)++)))<<48, \ + l|=((uint64_t)(*((c)++)))<<40, \ + l|=((uint64_t)(*((c)++)))<<32, \ + l|=((uint64_t)(*((c)++)))<<24, \ + l|=((uint64_t)(*((c)++)))<<16, \ + l|=((uint64_t)(*((c)++)))<< 8, \ + l|=((uint64_t)(*((c)++)))) + +#define l2n8(l,c) (*((c)++)=(unsigned char)(((l)>>56)&0xff), \ + *((c)++)=(unsigned char)(((l)>>48)&0xff), \ + *((c)++)=(unsigned char)(((l)>>40)&0xff), \ + *((c)++)=(unsigned char)(((l)>>32)&0xff), \ + *((c)++)=(unsigned char)(((l)>>24)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l) )&0xff)) + +typedef enum sct_signature_type_t { + SIGNATURE_TYPE_NOT_SET = -1, + SIGNATURE_TYPE_CERT_TIMESTAMP, + SIGNATURE_TYPE_TREE_HASH +} SCT_SIGNATURE_TYPE; + +/* + * Update encoding for SCT signature verification/generation to supplied + * EVP_MD_CTX. + */ +static int sct_ctx_update(EVP_MD_CTX *ctx, const SCT_CTX *sctx, const SCT *sct) +{ + unsigned char tmpbuf[12]; + unsigned char *p, *der; + size_t derlen; + /* + * digitally-signed struct { (1 byte) Version sct_version; (1 byte) + * SignatureType signature_type = certificate_timestamp; (8 bytes) uint64 + * timestamp; (2 bytes) LogEntryType entry_type; (? bytes) + * select(entry_type) { case x509_entry: ASN.1Cert; case precert_entry: + * PreCert; } signed_entry; (2 bytes + sct->ext_len) CtExtensions + * extensions; + */ + + if (sct->entry_type == CT_LOG_ENTRY_TYPE_NOT_SET) + return 0; + + if (sct->entry_type == CT_LOG_ENTRY_TYPE_PRECERT && sctx->ihash == NULL) + return 0; + + p = tmpbuf; + + *p++ = sct->version; + *p++ = SIGNATURE_TYPE_CERT_TIMESTAMP; + l2n8(sct->timestamp, p); + s2n(sct->entry_type, p); + + if (!EVP_DigestUpdate(ctx, tmpbuf, p - tmpbuf)) + return 0; + + if (sct->entry_type == CT_LOG_ENTRY_TYPE_X509) { + der = sctx->certder; + derlen = sctx->certderlen; + } else { + if (!EVP_DigestUpdate(ctx, sctx->ihash, sctx->ihashlen)) + return 0; + der = sctx->preder; + derlen = sctx->prederlen; + } + + /* If no encoding available, fatal error */ + if (der == NULL) + return 0; + + /* Include length first */ + p = tmpbuf; + l2n3(derlen, p); + + if (!EVP_DigestUpdate(ctx, tmpbuf, 3)) + return 0; + if (!EVP_DigestUpdate(ctx, der, derlen)) + return 0; + + /* Add any extensions */ + p = tmpbuf; + s2n(sct->ext_len, p); + if (!EVP_DigestUpdate(ctx, tmpbuf, 2)) + return 0; + + if (sct->ext_len && !EVP_DigestUpdate(ctx, sct->ext, sct->ext_len)) + return 0; + + return 1; +} + +int SCT_verify(const SCT_CTX *sctx, const SCT *sct) +{ + EVP_MD_CTX *ctx = NULL; + int ret = -1; + if (!SCT_is_complete(sct) || sctx->pkey == NULL || + sct->entry_type == CT_LOG_ENTRY_TYPE_NOT_SET || + (sct->entry_type == CT_LOG_ENTRY_TYPE_PRECERT && sctx->ihash == NULL)) { + CTerr(CT_F_SCT_VERIFY, CT_R_SCT_NOT_SET); + return -1; + } + if (sct->version != SCT_VERSION_V1) { + CTerr(CT_F_SCT_VERIFY, CT_R_SCT_UNSUPPORTED_VERSION); + return 0; + } + if (sct->log_id_len != sctx->pkeyhashlen || + memcmp(sct->log_id, sctx->pkeyhash, sctx->pkeyhashlen) != 0) { + CTerr(CT_F_SCT_VERIFY, CT_R_SCT_LOG_ID_MISMATCH); + return 0; + } + ctx = EVP_MD_CTX_new(); + if (ctx == NULL) + goto end; + + if (!EVP_DigestVerifyInit(ctx, NULL, EVP_sha256(), NULL, sctx->pkey)) + goto end; + + if (!sct_ctx_update(ctx, sctx, sct)) + goto end; + + /* Verify signature */ + ret = EVP_DigestVerifyFinal(ctx, sct->sig, sct->sig_len); + /* If ret < 0 some other error: fall through without setting error */ + if (ret == 0) + CTerr(CT_F_SCT_VERIFY, CT_R_SCT_INVALID_SIGNATURE); + + end: + EVP_MD_CTX_free(ctx); + return ret; +} + +int SCT_verify_v1(SCT *sct, X509 *cert, X509 *preissuer, + X509_PUBKEY *log_pubkey, X509 *issuer_cert) +{ + int ret = 0; + SCT_CTX *sctx = NULL; + + if (sct == NULL || cert == NULL || log_pubkey == NULL || + (sct->entry_type == CT_LOG_ENTRY_TYPE_PRECERT && issuer_cert == NULL)) { + CTerr(CT_F_SCT_VERIFY_V1, ERR_R_PASSED_NULL_PARAMETER); + return -1; + } else if (!SCT_is_complete(sct)) { + CTerr(CT_F_SCT_VERIFY_V1, CT_R_SCT_NOT_SET); + return -1; + } else if (sct->version != 0) { + CTerr(CT_F_SCT_VERIFY_V1, CT_R_SCT_UNSUPPORTED_VERSION); + return 0; + } + + sctx = SCT_CTX_new(); + if (sctx == NULL) + goto done; + + ret = SCT_CTX_set1_pubkey(sctx, log_pubkey); + + if (ret <= 0) + goto done; + + ret = SCT_CTX_set1_cert(sctx, cert, preissuer); + + if (ret <= 0) + goto done; + + if (sct->entry_type == CT_LOG_ENTRY_TYPE_PRECERT) { + ret = SCT_CTX_set1_issuer(sctx, issuer_cert); + if (ret <= 0) + goto done; + } + + ret = SCT_verify(sctx, sct); + + done: + if (sctx != NULL) + SCT_CTX_free(sctx); + return ret; +} diff --git a/crypto/include/internal/cryptlib.h b/crypto/include/internal/cryptlib.h index 9e620e6428..22caf93311 100644 --- a/crypto/include/internal/cryptlib.h +++ b/crypto/include/internal/cryptlib.h @@ -94,15 +94,18 @@ DEFINE_LHASH_OF(MEM); # define X509_CERT_DIR OPENSSLDIR "/certs" # define X509_CERT_FILE OPENSSLDIR "/cert.pem" # define X509_PRIVATE_DIR OPENSSLDIR "/private" +# define CTLOG_FILE OPENSSLDIR "/log_list.conf" # else # define X509_CERT_AREA "SSLROOT:[000000]" # define X509_CERT_DIR "SSLCERTS:" # define X509_CERT_FILE "SSLCERTS:cert.pem" # define X509_PRIVATE_DIR "SSLPRIVATE:" +# define CTLOG_FILE "SSLCERTS:log_list.conf" # endif # define X509_CERT_DIR_EVP "SSL_CERT_DIR" # define X509_CERT_FILE_EVP "SSL_CERT_FILE" +# define CTLOG_FILE_EVP "CTLOG_FILE" /* size of string representations */ # define DECIMAL_SIZE(type) ((sizeof(type)*8+2)/3+1) diff --git a/include/openssl/ct.h b/include/openssl/ct.h index 520174ff9a..de130c4678 100644 --- a/include/openssl/ct.h +++ b/include/openssl/ct.h @@ -83,7 +83,15 @@ typedef enum { SCT_VERSION_V1 = 0 } sct_version_t; +typedef enum { + SCT_SOURCE_UNKNOWN, + SCT_SOURCE_TLS_EXTENSION, + SCT_SOURCE_X509V3_EXTENSION, + SCT_SOURCE_OCSP_STAPLED_RESPONSE +} sct_source_t; + DEFINE_STACK_OF(SCT) +DEFINE_STACK_OF(CTLOG) /***************** * SCT functions * @@ -95,6 +103,17 @@ DEFINE_STACK_OF(SCT) */ SCT *SCT_new(void); +/* + * Creates a new SCT from some base64-encoded strings. + * The caller is responsible for calling SCT_free when finished with the SCT. + */ +SCT *SCT_new_from_base64(unsigned char version, + const char *logid_base64, + ct_log_entry_type_t entry_type, + uint64_t timestamp, + const char *extensions_base64, + const char *signature_base64); + /* * Frees the SCT and the underlying data structures. */ @@ -149,6 +168,13 @@ int SCT_set0_log_id(SCT *sct, unsigned char *log_id, size_t log_id_len); */ int SCT_set1_log_id(SCT *sct, const unsigned char *log_id, size_t log_id_len); +/* + * Gets the name of the log that an SCT came from. + * Ownership of the log name remains with the SCT. + * Returns the log name, or NULL if it is not known. + */ +const char *SCT_get0_log_name(const SCT *sct); + /* * Returns the timestamp for the SCT (epoch time in milliseconds). */ @@ -213,6 +239,42 @@ void SCT_set0_signature(SCT *sct, unsigned char *sig, size_t sig_len); */ int SCT_set1_signature(SCT *sct, const unsigned char *sig, size_t sig_len); +/* + * The origin of this SCT, e.g. TLS extension, OCSP response, etc. + */ +sct_source_t SCT_get_source(const SCT *sct); + +/* + * Set the origin of this SCT, e.g. TLS extension, OCSP response, etc. + * Returns 1 on success, 0 otherwise. + */ +int SCT_set_source(SCT *sct, sct_source_t source); + +/* + * Sets the source of all of the SCTs to the same value. + * Returns 1 on success. + */ +int SCT_LIST_set_source(const STACK_OF(SCT) *scts, sct_source_t source); + +/* + * Gets information about the log the SCT came from, if set. + */ +CTLOG *SCT_get0_log(const SCT *sct); + +/* + * Looks up information about the log the SCT came from using a CT log store. + * Returns 1 if information about the log is found, 0 otherwise. + * The information can be accessed via SCT_get0_log. + */ +int SCT_set0_log(SCT *sct, const CTLOG_STORE* ct_logs); + +/* + * Looks up information about the logs the SCTs came from using a CT log store. + * Returns the number of SCTs that now have a log set. + * If any SCTs already have a log set, they will be skipped. + */ +int SCT_LIST_set0_logs(STACK_OF(SCT) *sct_list, const CTLOG_STORE *ct_logs); + /* * Pretty-prints an |sct| to |out|. * It will be indented by the number of spaces specified by |indent|. @@ -227,6 +289,21 @@ void SCT_print(const SCT *sct, BIO *out, int indent); void SCT_LIST_print(const STACK_OF(SCT) *sct_list, BIO *out, int indent, const char *separator); +/* + * Verifies an SCT with the given context. + * Returns 1 if the SCT verifies successfully, 0 if it cannot be verified and a + * negative integer if an error occurs. + */ +int SCT_verify(const SCT_CTX *sctx, const SCT *sct); + +/* + * Verifies an SCT against the provided data. + * Returns 1 if the SCT verifies successfully, 0 if it cannot be verified and a + * negative integer if an error occurs. + */ +int SCT_verify_v1(SCT *sct, X509 *cert, X509 *preissuer, + X509_PUBKEY *log_pubkey, X509 *issuer_cert); + /********************************* * SCT parsing and serialisation * *********************************/ @@ -328,6 +405,77 @@ int i2o_SCT_signature(const SCT *sct, unsigned char **out); */ int o2i_SCT_signature(SCT *sct, const unsigned char **in, size_t len); +/******************** + * CT log functions * + ********************/ + +/* + * Creates a new CT log instance with the given |public_key| and |name|. + * Should be deleted by the caller using CTLOG_free when no longer needed. + */ +CTLOG *CTLOG_new(EVP_PKEY *public_key, const char *name); + +/* + * Creates a new, blank CT log instance. + * Should be deleted by the caller using CTLOG_free when no longer needed. + */ +CTLOG *CTLOG_new_null(void); + +/* + * Creates a new CT log instance with the given base64 public_key and |name|. + * Should be deleted by the caller using CTLOG_free when no longer needed. + */ +CTLOG *CTLOG_new_from_base64(const char *pkey_base64, const char *name); + +/* + * Deletes a CT log instance and its fields. + */ +void CTLOG_free(CTLOG *log); + +/* Gets the name of the CT log */ +const char *CTLOG_get0_name(CTLOG *log); +/* Gets the ID of the CT log */ +void CTLOG_get0_log_id(CTLOG *log, uint8_t **log_id, size_t *log_id_len); +/* Gets the public key of the CT log */ +EVP_PKEY *CTLOG_get0_public_key(CTLOG *log); + +/************************** + * CT log store functions * + **************************/ + +/* + * Creates a new CT log store. + * Should be deleted by the caller using CTLOG_STORE_free when no longer needed. + */ +CTLOG_STORE *CTLOG_STORE_new(void); + +/* + * Deletes a CT log store and all of the CT log instances held within. + */ +void CTLOG_STORE_free(CTLOG_STORE *store); + +/* + * Finds a CT log in the store based on its log ID. + * Returns the CT log, or NULL if no match is found. + */ +CTLOG *CTLOG_STORE_get0_log_by_id(const CTLOG_STORE *store, + const uint8_t *log_id, + size_t log_id_len); + +/* + * Loads a CT log list into a |store| from a |file|. + * Returns 1 if loading is successful, or a non-positive integer otherwise. + */ +int CTLOG_STORE_load_file(CTLOG_STORE *store, const char *file); + +/* + * Loads the default CT log list into a |store|. + * See internal/cryptlib.h for the environment variable and file path that are + * consulted to find the default file. + * Returns 1 if loading is successful, or a non-positive integer otherwise. + */ +int CTLOG_STORE_load_default_file(CTLOG_STORE *store); + /* BEGIN ERROR CODES */ /* * The following lines are auto generated by the script mkerr.pl. Any changes @@ -338,6 +486,15 @@ void ERR_load_CT_strings(void); /* Error codes for the CT functions. */ /* Function codes. */ +# define CT_F_CTLOG_NEW 117 +# define CT_F_CTLOG_NEW_FROM_BASE64 118 +# define CT_F_CTLOG_NEW_FROM_CONF 119 +# define CT_F_CTLOG_NEW_NULL 120 +# define CT_F_CTLOG_STORE_GET0_LOG_BY_ID 121 +# define CT_F_CTLOG_STORE_LOAD_CTX_NEW 122 +# define CT_F_CTLOG_STORE_LOAD_FILE 123 +# define CT_F_CT_BASE64_DECODE 124 +# define CT_F_CT_V1_LOG_ID_FROM_PKEY 125 # define CT_F_D2I_SCT_LIST 105 # define CT_F_I2D_SCT_LIST 106 # define CT_F_I2O_SCT 107 @@ -346,7 +503,9 @@ void ERR_load_CT_strings(void); # define CT_F_O2I_SCT 110 # define CT_F_O2I_SCT_LIST 111 # define CT_F_O2I_SCT_SIGNATURE 112 +# define CT_F_SCT_CTX_NEW 126 # define CT_F_SCT_NEW 100 +# define CT_F_SCT_NEW_FROM_BASE64 127 # define CT_F_SCT_SET0_LOG_ID 101 # define CT_F_SCT_SET1_EXTENSIONS 114 # define CT_F_SCT_SET1_LOG_ID 115 @@ -355,13 +514,23 @@ void ERR_load_CT_strings(void); # define CT_F_SCT_SET_SIGNATURE_NID 103 # define CT_F_SCT_SET_VERSION 104 # define CT_F_SCT_SIGNATURE_IS_VALID 113 +# define CT_F_SCT_VERIFY 128 +# define CT_F_SCT_VERIFY_V1 129 /* Reason codes. */ +# define CT_R_BASE64_DECODE_ERROR 108 # define CT_R_INVALID_LOG_ID_LENGTH 100 +# define CT_R_LOG_CONF_INVALID 109 +# define CT_R_LOG_CONF_INVALID_KEY 110 +# define CT_R_LOG_CONF_MISSING_DESCRIPTION 111 +# define CT_R_LOG_CONF_MISSING_KEY 112 +# define CT_R_LOG_KEY_INVALID 113 # define CT_R_SCT_INVALID 104 # define CT_R_SCT_INVALID_SIGNATURE 107 # define CT_R_SCT_LIST_INVALID 105 +# define CT_R_SCT_LOG_ID_MISMATCH 114 # define CT_R_SCT_NOT_SET 106 +# define CT_R_SCT_UNSUPPORTED_VERSION 115 # define CT_R_UNRECOGNIZED_SIGNATURE_NID 101 # define CT_R_UNSUPPORTED_ENTRY_TYPE 102 # define CT_R_UNSUPPORTED_VERSION 103 diff --git a/include/openssl/ossl_typ.h b/include/openssl/ossl_typ.h index 536ffa2fec..013296618c 100644 --- a/include/openssl/ossl_typ.h +++ b/include/openssl/ossl_typ.h @@ -201,6 +201,9 @@ typedef struct ocsp_response_st OCSP_RESPONSE; typedef struct ocsp_responder_id_st OCSP_RESPID; typedef struct sct_st SCT; +typedef struct sct_ctx_st SCT_CTX; +typedef struct ctlog_st CTLOG; +typedef struct ctlog_store_st CTLOG_STORE; #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L && \ defined(INTMAX_MAX) && defined(UINTMAX_MAX) diff --git a/test/ct/log_list.conf b/test/ct/log_list.conf new file mode 100644 index 0000000000..4b68e53558 --- /dev/null +++ b/test/ct/log_list.conf @@ -0,0 +1,38 @@ +enabled_logs=test,pilot,aviator,rocketeer,digicert,certly,izempe,symantec,venafi + +[test] +description = https://github.com/google/certificate-transparency/tree/99218b6445906a81f219d84e9c6d2683e13e4e58/test/testdata +key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmXg8sUUzwBYaWrRb+V0IopzQ6o3UyEJ04r5ZrRXGdpYM8K+hB0pXrGRLI0eeWz+3skXrS0IO83AhA3GpRL6s6w== + +[pilot] +description = Google Pilot Log +key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfahLEimAoz2t01p3uMziiLOl/fHTDM0YDOhBRuiBARsV4UvxG2LdNgoIGLrtCzWE0J5APC2em4JlvR8EEEFMoA== + +[aviator] +description = Google Aviator log +key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1/TMabLkDpCjiupacAlP7xNi0I1JYP8bQFAHDG1xhtolSY1l4QgNRzRrvSe8liE+NPWHdjGxfx3JhTsN9x8/6Q== + +[rocketeer] +description = Google Rocketeer log +key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIFsYyDzBi7MxCAC/oJBXK7dHjG+1aLCOkHjpoHPqTyghLpzA9BYbqvnV16mAw04vUjyYASVGJCUoI3ctBcJAeg== + +[digicert] +description = DigiCert Log Server +key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAkbFvhu7gkAW6MHSrBlpE1n4+HCFRkC5OLAjgqhkTH+/uzSfSl8ois8ZxAD2NgaTZe1M9akhYlrYkes4JECs6A== + +[certly] +description = Certly.IO log +key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAECyPLhWKYYUgEc+tUXfPQB4wtGS2MNvXrjwFCCnyYJifBtd2Sk7Cu+Js9DNhMTh35FftHaHu6ZrclnNBKwmbbSA== + +[izempe] +description = Izempe log +key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJ2Q5DC3cUBj4IQCiDu0s6j51up+TZAkAEcQRF6tczw90rLWXkJMAW7jr9yc92bIKgV8vDXU4lDeZHvYHduDuvg== + +[symantec] +description = Symantec log +key = MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEluqsHEYMG1XcDfy1lCdGV0JwOmkY4r87xNuroPS2bMBTP01CEDPwWJePa75y9CrsHEKqAy8afig1dpkIPSEUhg== + +[venafi] +description = Venafi log +key = MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAolpIHxdSlTXLo1s6H1OCdpSj/4DyHDc8wLG9wVmLqy1lk9fz4ATVmm+/1iN2Nk8jmctUKK2MFUtlWXZBSpym97M7frGlSaQXUWyA3CqQUEuIJOmlEjKTBEiQAvpfDjCHjlV2Be4qTM6jamkJbiWtgnYPhJL6ONaGTiSPm7Byy57iaz/hbckldSOIoRhYBiMzeNoA0DiRZ9KmfSeXZ1rB8y8X5urSW+iBzf2SaOfzBvDpcoTuAaWx2DPazoOl28fP1hZ+kHUYvxbcMjttjauCFx+JII0dmuZNIwjfeG/GBb9frpSX219k1O4Wi6OEbHEr8at/XQ0y7gTikOxBn/s5wQIDAQAB + diff --git a/util/libeay.num b/util/libeay.num index ea14ebdc63..2484fd2fc1 100755 --- a/util/libeay.num +++ b/util/libeay.num @@ -4766,3 +4766,25 @@ ASYNC_WAIT_CTX_get_changed_fds 5269 1_1_0 EXIST::FUNCTION: ASYNC_WAIT_CTX_set_wait_fd 5270 1_1_0 EXIST::FUNCTION: ASYNC_WAIT_CTX_new 5271 1_1_0 EXIST::FUNCTION: ASYNC_get_wait_ctx 5272 1_1_0 EXIST::FUNCTION: +SCT_verify 5273 1_1_0 EXIST::FUNCTION: +CTLOG_STORE_free 5274 1_1_0 EXIST::FUNCTION: +CTLOG_STORE_new 5275 1_1_0 EXIST::FUNCTION: +SCT_verify_v1 5276 1_1_0 EXIST::FUNCTION: +SCT_get0_log 5277 1_1_0 EXIST::FUNCTION: +CTLOG_new_from_base64 5278 1_1_0 EXIST::FUNCTION: +CTLOG_get0_name 5279 1_1_0 EXIST::FUNCTION: +CTLOG_get0_public_key 5280 1_1_0 EXIST::FUNCTION: +CTLOG_get0_log_id 5281 1_1_0 EXIST::FUNCTION: +SCT_new_from_base64 5282 1_1_0 EXIST::FUNCTION: +SCT_get_source 5283 1_1_0 EXIST::FUNCTION: +CTLOG_STORE_load_file 5284 1_1_0 EXIST::FUNCTION: +CTLOG_new_null 5285 1_1_0 EXIST::FUNCTION: +SCT_LIST_set_source 5286 1_1_0 EXIST::FUNCTION: +CTLOG_free 5287 1_1_0 EXIST::FUNCTION: +SCT_get0_log_name 5288 1_1_0 EXIST::FUNCTION: +SCT_set0_log 5289 1_1_0 EXIST::FUNCTION: +SCT_set_source 5290 1_1_0 EXIST::FUNCTION: +SCT_LIST_set0_logs 5291 1_1_0 EXIST::FUNCTION: +CTLOG_STORE_get0_log_by_id 5292 1_1_0 EXIST::FUNCTION: +CTLOG_STORE_load_default_file 5293 1_1_0 EXIST::FUNCTION: +CTLOG_new 5294 1_1_0 EXIST::FUNCTION: