diff options
author | Rob Percival <robpercival@google.com> | 2016-02-25 13:33:48 +0000 |
---|---|---|
committer | Rich Salz <rsalz@openssl.org> | 2016-03-01 11:59:28 -0500 |
commit | 8c6afbc55cc8e2d036c0af5adbaff82d8117c6b2 (patch) | |
tree | 29f5feb6cf77df46204d65a6dcb6e1af65aedb06 /crypto/ct | |
parent | 09375d12fb684c6991c06b473664a0630b8b2edf (diff) |
Verify SCT signatures
Tests included in future commit, which adds CT policy validation.
Reviewed-by: Ben Laurie <ben@openssl.org>
Reviewed-by: Rich Salz <rsalz@openssl.org>
Diffstat (limited to 'crypto/ct')
-rw-r--r-- | crypto/ct/Makefile.in | 6 | ||||
-rw-r--r-- | crypto/ct/build.info | 3 | ||||
-rw-r--r-- | crypto/ct/ct_b64.c | 213 | ||||
-rw-r--r-- | crypto/ct/ct_err.c | 22 | ||||
-rw-r--r-- | crypto/ct/ct_locl.h | 40 | ||||
-rw-r--r-- | crypto/ct/ct_log.c | 330 | ||||
-rw-r--r-- | crypto/ct/ct_prn.c | 5 | ||||
-rw-r--r-- | crypto/ct/ct_sct.c | 66 | ||||
-rw-r--r-- | crypto/ct/ct_sct_ctx.c | 267 | ||||
-rw-r--r-- | crypto/ct/ct_vfy.c | 255 |
10 files changed, 1204 insertions, 3 deletions
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 <limits.h> +#include <string.h> + +#include <openssl/ct.h> +#include <openssl/err.h> +#include <openssl/evp.h> + +/* + * 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 <adam.eijdenberg@gmail.com>. */ +/* ==================================================================== + * 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 <openssl/conf.h> +#include <openssl/ct.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/safestack.h> + +#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 <stddef.h> +#include <string.h> + +#include <openssl/err.h> +#include <openssl/obj_mac.h> +#include <openssl/x509.h> + +#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) { + X5 |