CT policy validation

Specifies a callback that will, in the future, be used by the SSL code to
decide whether to abort a connection on Certificate Transparency grounds.

Reviewed-by: Ben Laurie <ben@openssl.org>
Reviewed-by: Rich Salz <rsalz@openssl.org>
master
Rob Percival 2016-02-29 17:33:02 +00:00
parent 7852414967
commit 7d054e5ab2
15 changed files with 611 additions and 27 deletions

View File

@ -15,10 +15,10 @@ CFLAGS= $(INCLUDES) $(CFLAG) $(SHARED_CFLAG)
GENERAL=Makefile
LIB=$(TOP)/libcrypto.a
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
LIBSRC= ct_b64.c ct_err.c ct_log.c ct_oct.c ct_policy.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_policy.o ct_prn.o ct_sct.o \
ct_sct_ctx.o ct_vfy.o ct_x509v3.o
SRC= $(LIBSRC)

View File

@ -1,3 +1,3 @@
LIBS=../../libcrypto
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
SOURCE[../../libcrypto]= ct_b64.c ct_err.c ct_log.c ct_oct.c ct_policy.c \
ct_prn.c ct_sct.c ct_sct_ctx.c ct_vfy.c ct_x509v3.c

View File

@ -77,7 +77,23 @@ static ERR_STRING_DATA CT_str_functs[] = {
{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_POLICY_EVAL_CTX_GET0_CERT),
"CT_POLICY_EVAL_CTX_get0_cert"},
{ERR_FUNC(CT_F_CT_POLICY_EVAL_CTX_GET0_ISSUER),
"CT_POLICY_EVAL_CTX_get0_issuer"},
{ERR_FUNC(CT_F_CT_POLICY_EVAL_CTX_GET0_LOG_STORE),
"CT_POLICY_EVAL_CTX_get0_log_store"},
{ERR_FUNC(CT_F_CT_POLICY_EVAL_CTX_NEW), "CT_POLICY_EVAL_CTX_new"},
{ERR_FUNC(CT_F_CT_POLICY_EVAL_CTX_SET0_CERT),
"CT_POLICY_EVAL_CTX_set0_cert"},
{ERR_FUNC(CT_F_CT_POLICY_EVAL_CTX_SET0_ISSUER),
"CT_POLICY_EVAL_CTX_set0_issuer"},
{ERR_FUNC(CT_F_CT_POLICY_EVAL_CTX_SET0_LOG_STORE),
"CT_POLICY_EVAL_CTX_set0_log_store"},
{ERR_FUNC(CT_F_CT_V1_LOG_ID_FROM_PKEY), "CT_v1_log_id_from_pkey"},
{ERR_FUNC(CT_F_CT_VERIFY_AT_LEAST_ONE_GOOD_SCT),
"CT_verify_at_least_one_good_sct"},
{ERR_FUNC(CT_F_CT_VERIFY_NO_BAD_SCTS), "CT_verify_no_bad_scts"},
{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"},
@ -87,6 +103,7 @@ static ERR_STRING_DATA CT_str_functs[] = {
{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_LIST_VALIDATE), "SCT_LIST_validate"},
{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"},
@ -97,6 +114,7 @@ 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_VALIDATE), "SCT_validate"},
{ERR_FUNC(CT_F_SCT_VERIFY), "SCT_verify"},
{ERR_FUNC(CT_F_SCT_VERIFY_V1), "SCT_verify_v1"},
{0, NULL}
@ -111,12 +129,15 @@ static ERR_STRING_DATA CT_str_reasons[] = {
"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_NOT_ENOUGH_SCTS), "not enough scts"},
{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_SCT_VALIDATION_STATUS_NOT_SET),
"sct validation status not set"},
{ERR_REASON(CT_R_UNRECOGNIZED_SIGNATURE_NID),
"unrecognized signature nid"},
{ERR_REASON(CT_R_UNSUPPORTED_ENTRY_TYPE), "unsupported entry type"},

View File

@ -127,6 +127,8 @@ struct sct_st {
sct_source_t source;
/* The CT log that produced this SCT. */
CTLOG *log;
/* The result of the last attempt to validate this SCT. */
sct_validation_status_t validation_status;
};
/* Miscellaneous data that is useful when verifying an SCT */
@ -147,6 +149,15 @@ struct sct_ctx_st {
size_t prederlen;
};
/* Context when evaluating whether a Certificate Transparency policy is met */
struct ct_policy_eval_ctx_st {
X509 *cert;
X509 *issuer;
CTLOG_STORE *log_store;
STACK_OF(SCT) *good_scts;
STACK_OF(SCT) *bad_scts;
};
/*
* Creates a new context for verifying an SCT.
*/

110
crypto/ct/ct_policy.c Normal file
View File

@ -0,0 +1,110 @@
/*
* Implementations of Certificate Transparency SCT policies.
* Written by Rob Percival (robpercival@google.com) for the OpenSSL project.
*/
/* ====================================================================
* Copyright (c) 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
* 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.
* ====================================================================
*/
#ifdef OPENSSL_NO_CT
# error "CT is disabled"
#endif
#include <openssl/ct.h>
#include <openssl/err.h>
#include "ct_locl.h"
CT_POLICY_EVAL_CTX *CT_POLICY_EVAL_CTX_new(void)
{
CT_POLICY_EVAL_CTX *ctx = OPENSSL_zalloc(sizeof(CT_POLICY_EVAL_CTX));
if (ctx == NULL) {
CTerr(CT_F_CT_POLICY_EVAL_CTX_NEW, ERR_R_MALLOC_FAILURE);
return NULL;
}
return ctx;
}
void CT_POLICY_EVAL_CTX_free(CT_POLICY_EVAL_CTX *ctx)
{
OPENSSL_free(ctx);
}
void CT_POLICY_EVAL_CTX_set0_cert(CT_POLICY_EVAL_CTX *ctx, X509 *cert)
{
ctx->cert = cert;
}
void CT_POLICY_EVAL_CTX_set0_issuer(CT_POLICY_EVAL_CTX *ctx, X509 *issuer)
{
ctx->issuer = issuer;
}
void CT_POLICY_EVAL_CTX_set0_log_store(CT_POLICY_EVAL_CTX *ctx,
CTLOG_STORE *log_store)
{
ctx->log_store = log_store;
}
X509* CT_POLICY_EVAL_CTX_get0_cert(CT_POLICY_EVAL_CTX *ctx)
{
return ctx->cert;
}
X509* CT_POLICY_EVAL_CTX_get0_issuer(CT_POLICY_EVAL_CTX *ctx)
{
return ctx->issuer;
}
CTLOG_STORE *CT_POLICY_EVAL_CTX_get0_log_store(CT_POLICY_EVAL_CTX *ctx)
{
return ctx->log_store;
}

View File

@ -356,3 +356,94 @@ int SCT_LIST_set0_logs(STACK_OF(SCT) *sct_list, const CTLOG_STORE *ct_logs)
return sct_logs_found;
}
sct_validation_status_t SCT_get_validation_status(const SCT *sct)
{
return sct->validation_status;
}
int SCT_validate(SCT *sct, const CT_POLICY_EVAL_CTX *ctx)
{
int is_sct_valid = -1;
SCT_CTX *sctx = NULL;
X509_PUBKEY *pub = NULL, *log_pkey = NULL;
switch (sct->version) {
case SCT_VERSION_V1:
if (sct->log == NULL)
sct->log = CTLOG_STORE_get0_log_by_id(ctx->log_store,
sct->log_id,
CT_V1_HASHLEN);
break;
default:
sct->validation_status = SCT_VALIDATION_STATUS_UNKNOWN_VERSION;
goto end;
}
if (sct->log == NULL) {
sct->validation_status = SCT_VALIDATION_STATUS_UNKNOWN_LOG;
goto end;
}
sctx = SCT_CTX_new();
if (sctx == NULL)
goto err;
if (X509_PUBKEY_set(&log_pkey, CTLOG_get0_public_key(sct->log)) != 1)
goto err;
if (SCT_CTX_set1_pubkey(sctx, log_pkey) != 1)
goto err;
if (SCT_get_log_entry_type(sct) == CT_LOG_ENTRY_TYPE_PRECERT) {
EVP_PKEY *issuer_pkey;
if (ctx->issuer == NULL) {
sct->validation_status = SCT_VALIDATION_STATUS_UNVERIFIED;
goto end;
}
issuer_pkey = X509_get_pubkey(ctx->issuer);
if (X509_PUBKEY_set(&pub, issuer_pkey) != 1)
goto err;
if (SCT_CTX_set1_issuer_pubkey(sctx, pub) != 1)
goto err;
}
if (SCT_CTX_set1_cert(sctx, ctx->cert, NULL) != 1)
goto err;
sct->validation_status = SCT_verify(sctx, sct) == 1 ?
SCT_VALIDATION_STATUS_VALID : SCT_VALIDATION_STATUS_INVALID;
end:
is_sct_valid = sct->validation_status == SCT_VALIDATION_STATUS_VALID;
err:
X509_PUBKEY_free(pub);
X509_PUBKEY_free(log_pkey);
SCT_CTX_free(sctx);
return is_sct_valid;
}
int SCT_LIST_validate(const STACK_OF(SCT) *scts, CT_POLICY_EVAL_CTX *ctx)
{
int are_scts_valid = 1;
int sct_count = scts != NULL ? sk_SCT_num(scts) : 0;
int i;
for (i = 0; i < sct_count; ++i) {
int is_sct_valid = -1;
SCT *sct = sk_SCT_value(scts, i);
if (sct == NULL)
continue;
is_sct_valid = SCT_validate(sct, ctx);
if (is_sct_valid < 0)
return is_sct_valid;
are_scts_valid &= is_sct_valid;
}
return are_scts_valid;
}

View File

@ -71,6 +71,65 @@ typedef enum sct_signature_type_t {
SIGNATURE_TYPE_TREE_HASH
} SCT_SIGNATURE_TYPE;
int CT_verify_no_bad_scts(const CT_POLICY_EVAL_CTX *ctx,
const STACK_OF(SCT) *scts, void *arg)
{
int sct_count = scts != NULL ? sk_SCT_num(scts) : 0;
int i;
for (i = 0; i < sct_count; ++i) {
SCT *sct = sk_SCT_value(scts, i);
switch (SCT_get_validation_status(sct)) {
case SCT_VALIDATION_STATUS_INVALID:
return 0;
case SCT_VALIDATION_STATUS_NOT_SET:
CTerr(CT_F_CT_VERIFY_NO_BAD_SCTS,
CT_R_SCT_VALIDATION_STATUS_NOT_SET);
return -1;
default:
/* Ignore other validation statuses. */
break;
}
}
return 1;
}
int CT_verify_at_least_one_good_sct(const CT_POLICY_EVAL_CTX *ctx,
const STACK_OF(SCT) *scts, void *arg)
{
int sct_count = scts != NULL ? sk_SCT_num(scts) : 0;
int valid_scts = 0;
int i;
for (i = 0; i < sct_count; ++i) {
SCT *sct = sk_SCT_value(scts, i);
switch (SCT_get_validation_status(sct)) {
case SCT_VALIDATION_STATUS_VALID:
++valid_scts;
break;
case SCT_VALIDATION_STATUS_INVALID:
return 0;
case SCT_VALIDATION_STATUS_NOT_SET:
CTerr(CT_F_CT_VERIFY_AT_LEAST_ONE_GOOD_SCT,
CT_R_SCT_VALIDATION_STATUS_NOT_SET);
return -1;
default:
/* Ignore other validation statuses. */
break;
}
}
if (valid_scts == 0) {
CTerr(CT_F_CT_VERIFY_AT_LEAST_ONE_GOOD_SCT, CT_R_NOT_ENOUGH_SCTS);
return 0;
}
return 1;
}
/*
* Update encoding for SCT signature verification/generation to supplied
* EVP_MD_CTX.

View File

@ -90,9 +90,62 @@ typedef enum {
SCT_SOURCE_OCSP_STAPLED_RESPONSE
} sct_source_t;
typedef enum {
SCT_VALIDATION_STATUS_NOT_SET,
SCT_VALIDATION_STATUS_UNKNOWN_LOG,
SCT_VALIDATION_STATUS_VALID,
SCT_VALIDATION_STATUS_INVALID,
SCT_VALIDATION_STATUS_UNVERIFIED,
SCT_VALIDATION_STATUS_UNKNOWN_VERSION
} sct_validation_status_t;
DEFINE_STACK_OF(SCT)
DEFINE_STACK_OF(CTLOG)
/******************************************
* CT policy evaluation context functions *
******************************************/
/* Creates a new, empty policy evaluation context */
CT_POLICY_EVAL_CTX *CT_POLICY_EVAL_CTX_new(void);
/* Deletes a policy evaluation context */
void CT_POLICY_EVAL_CTX_free(CT_POLICY_EVAL_CTX *ctx);
/* Gets the peer certificate that the SCTs are for */
X509* CT_POLICY_EVAL_CTX_get0_cert(CT_POLICY_EVAL_CTX *ctx);
/* Sets the certificate associated with the received SCTs */
void CT_POLICY_EVAL_CTX_set0_cert(CT_POLICY_EVAL_CTX *ctx, X509 *cert);
/* Gets the issuer of the aforementioned certificate */
X509* CT_POLICY_EVAL_CTX_get0_issuer(CT_POLICY_EVAL_CTX *ctx);
/* Sets the issuer of the certificate associated with the received SCTs */
void CT_POLICY_EVAL_CTX_set0_issuer(CT_POLICY_EVAL_CTX *ctx, X509 *issuer);
/* Gets the CT logs that are trusted sources of SCTs */
CTLOG_STORE *CT_POLICY_EVAL_CTX_get0_log_store(CT_POLICY_EVAL_CTX *ctx);
/* Sets the log store that is in use */
void CT_POLICY_EVAL_CTX_set0_log_store(CT_POLICY_EVAL_CTX *ctx,
CTLOG_STORE *log_store);
/*
* A callback for verifying that the received SCTs are sufficient.
* Expected to return 1 if they are sufficient, otherwise 0.
* May return a negative integer if an error occurs.
* A connection should be aborted if the SCTs are deemed insufficient.
*/
typedef int(*ct_validation_cb)(const CT_POLICY_EVAL_CTX *ctx,
const STACK_OF(SCT) *scts, void *arg);
/* Returns 0 if there are invalid SCTs */
int CT_verify_no_bad_scts(const CT_POLICY_EVAL_CTX *ctx,
const STACK_OF(SCT) *scts, void *arg);
/* Returns 0 if there are invalid SCTS or fewer than one valid SCT */
int CT_verify_at_least_one_good_sct(const CT_POLICY_EVAL_CTX *ctx,
const STACK_OF(SCT) *scts, void *arg);
/*****************
* SCT functions *
*****************/
@ -304,6 +357,31 @@ int SCT_verify(const SCT_CTX *sctx, const SCT *sct);
int SCT_verify_v1(SCT *sct, X509 *cert, X509 *preissuer,
X509_PUBKEY *log_pubkey, X509 *issuer_cert);
/*
* Gets the last result of validating this SCT.
* If it has not been validated yet, returns SCT_VALIDATION_STATUS_NOT_SET.
*/
sct_validation_status_t SCT_get_validation_status(const SCT *sct);
/*
* Validates the given SCT with the provided context.
* Sets the "validation_status" field of the SCT.
* Returns 1 if the SCT is valid and the signature verifies.
* Returns 0 if the SCT is invalid or could not be verified.
* Returns -1 if an error occurs.
*/
int SCT_validate(SCT *sct, const CT_POLICY_EVAL_CTX *ctx);
/*
* Validates the given list of SCTs with the provided context.
* Populates the "good_scts" and "bad_scts" of the evaluation context.
* Returns 1 if there are no invalid SCTs and all signatures verify.
* Returns 0 if at least one SCT is invalid or could not be verified.
* Returns a negative integer if an error occurs.
*/
int SCT_LIST_validate(const STACK_OF(SCT) *scts, CT_POLICY_EVAL_CTX *ctx);
/*********************************
* SCT parsing and serialisation *
*********************************/
@ -494,7 +572,16 @@ void ERR_load_CT_strings(void);
# 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_POLICY_EVAL_CTX_GET0_CERT 130
# define CT_F_CT_POLICY_EVAL_CTX_GET0_ISSUER 131
# define CT_F_CT_POLICY_EVAL_CTX_GET0_LOG_STORE 132
# define CT_F_CT_POLICY_EVAL_CTX_NEW 133
# define CT_F_CT_POLICY_EVAL_CTX_SET0_CERT 134
# define CT_F_CT_POLICY_EVAL_CTX_SET0_ISSUER 135
# define CT_F_CT_POLICY_EVAL_CTX_SET0_LOG_STORE 136
# define CT_F_CT_V1_LOG_ID_FROM_PKEY 125
# define CT_F_CT_VERIFY_AT_LEAST_ONE_GOOD_SCT 137
# define CT_F_CT_VERIFY_NO_BAD_SCTS 138
# define CT_F_D2I_SCT_LIST 105
# define CT_F_I2D_SCT_LIST 106
# define CT_F_I2O_SCT 107
@ -504,6 +591,7 @@ void ERR_load_CT_strings(void);
# 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_LIST_VALIDATE 139
# define CT_F_SCT_NEW 100
# define CT_F_SCT_NEW_FROM_BASE64 127
# define CT_F_SCT_SET0_LOG_ID 101
@ -514,6 +602,7 @@ 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_VALIDATE 140
# define CT_F_SCT_VERIFY 128
# define CT_F_SCT_VERIFY_V1 129
@ -525,12 +614,14 @@ void ERR_load_CT_strings(void);
# 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_NOT_ENOUGH_SCTS 116
# 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_SCT_VALIDATION_STATUS_NOT_SET 117
# define CT_R_UNRECOGNIZED_SIGNATURE_NID 101
# define CT_R_UNSUPPORTED_ENTRY_TYPE 102
# define CT_R_UNSUPPORTED_VERSION 103

View File

@ -204,6 +204,7 @@ typedef struct sct_st SCT;
typedef struct sct_ctx_st SCT_CTX;
typedef struct ctlog_st CTLOG;
typedef struct ctlog_store_st CTLOG_STORE;
typedef struct ct_policy_eval_ctx_st CT_POLICY_EVAL_CTX;
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L && \
defined(INTMAX_MAX) && defined(UINTMAX_MAX)

View File

@ -373,7 +373,7 @@ $(ASYNCTEST)$(EXE_EXT): $(ASYNCTEST).o
$(DTLSV1LISTENTEST)$(EXE_EXT): $(DTLSV1LISTENTEST).o
@target=$(DTLSV1LISTENTEST) $(BUILD_CMD)
$(CTTEST)$(EXE_EXT): $(CTTEST).o testutil.o
$(CTTEST)$(EXE_EXT): $(CTTEST).o $(DLIBCRYPTO) testutil.o
@target=$(CTTEST) testutil=testutil.o; $(BUILD_CMD)
$(THREADSTEST)$(EXE_EXT): $(THREADSTEST).o $(DLIBCRYPTO)

View File

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC0DCCAjmgAwIBAgIBADANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJHQjEk
MCIGA1UEChMbQ2VydGlmaWNhdGUgVHJhbnNwYXJlbmN5IENBMQ4wDAYDVQQIEwVX
YWxlczEQMA4GA1UEBxMHRXJ3IFdlbjAeFw0xMjA2MDEwMDAwMDBaFw0yMjA2MDEw
MDAwMDBaMFUxCzAJBgNVBAYTAkdCMSQwIgYDVQQKExtDZXJ0aWZpY2F0ZSBUcmFu
c3BhcmVuY3kgQ0ExDjAMBgNVBAgTBVdhbGVzMRAwDgYDVQQHEwdFcncgV2VuMIGf
MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVimhTYhCicRmTbneDIRgcKkATxtB7
jHbrkVfT0PtLO1FuzsvRyY2RxS90P6tjXVUJnNE6uvMa5UFEJFGnTHgW8iQ8+EjP
KDHM5nugSlojgZ88ujfmJNnDvbKZuDnd/iYx0ss6hPx7srXFL8/BT/9Ab1zURmnL
svfP34b7arnRsQIDAQABo4GvMIGsMB0GA1UdDgQWBBRfnYgNyHPmVNT4DdjmsMEk
tEfDVTB9BgNVHSMEdjB0gBRfnYgNyHPmVNT4DdjmsMEktEfDVaFZpFcwVTELMAkG
A1UEBhMCR0IxJDAiBgNVBAoTG0NlcnRpZmljYXRlIFRyYW5zcGFyZW5jeSBDQTEO
MAwGA1UECBMFV2FsZXMxEDAOBgNVBAcTB0VydyBXZW6CAQAwDAYDVR0TBAUwAwEB
/zANBgkqhkiG9w0BAQUFAAOBgQAGCMxKbWTyIF4UbASydvkrDvqUpdryOvw4BmBt
OZDQoeojPUApV2lGOwRmYef6HReZFSCa6i4Kd1F2QRIn18ADB8dHDmFYT9czQiRy
f1HWkLxHqd81TbD26yWVXeGJPE3VICskovPkQNJ0tU4b03YmnKliibduyqQQkOFP
OwqULg==
-----END CERTIFICATE-----

View File

@ -0,0 +1,35 @@
-----BEGIN CERTIFICATE-----
MIIGDjCCA/agAwIBAgIQBqdDgNTr/tQ1taP34Wq92DANBgkqhkiG9w0BAQwFADCB
hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTIwMjEy
MDAwMDAwWhcNMjcwMjExMjM1OTU5WjCBkjELMAkGA1UEBhMCR0IxGzAZBgNVBAgT
EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
Q09NT0RPIENBIExpbWl0ZWQxODA2BgNVBAMTL0NPTU9ETyBSU0EgRXh0ZW5kZWQg
VmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAlVbeVLTf1QJJe9FbXKKyHo+cK2JMK40SKPMalaPGEP0p3uGf
CzhAk9HvbpUQ/OGQF3cs7nU+e2PsYZJuTzurgElr3wDqAwB/L3XVKC/sVmePgIOj
vdwDmZOLlJFWW6G4ajo/Br0OksxgnP214J9mMF/b5pTwlWqvyIqvgNnmiDkBfBzA
xSr3e5Wg8narbZtyOTDr0VdVAZ1YEZ18bYSPSeidCfw8/QpKdhQhXBZzQCMZdMO6
WAqmli7eNuWf0MLw4eDBYuPCGEUZUaoXHugjddTI0JYT/8ck0YwLJ66eetw6YWNg
iJctXQUL5Tvrrs46R3N2qPos3cCHF+msMJn4HwIDAQABo4IBaTCCAWUwHwYDVR0j
BBgwFoAUu69+Aj36pvE8hI6t7jiY7NkyMtQwHQYDVR0OBBYEFDna/8ooFIqodBMI
ueQOqdL6fp1pMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMD4G
A1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwczovL3NlY3VyZS5j
b21vZG8uY29tL0NQUzBMBgNVHR8ERTBDMEGgP6A9hjtodHRwOi8vY3JsLmNvbW9k
b2NhLmNvbS9DT01PRE9SU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDBxBggr
BgEFBQcBAQRlMGMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9jcnQuY29tb2RvY2EuY29t
L0NPTU9ET1JTQUFkZFRydXN0Q0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2Nz
cC5jb21vZG9jYS5jb20wDQYJKoZIhvcNAQEMBQADggIBAERCnUFRK0iIXZebeV4R
AUpSGXtBLMeJPNBy3IX6WK/VJeQT+FhlZ58N/1eLqYVeyqZLsKeyLeCMIs37/3mk
jCuN/gI9JN6pXV/kD0fQ22YlPodHDK4ixVAihNftSlka9pOlk7DgG4HyVsTIEFPk
1Hax0VtpS3ey4E/EhOfUoFDuPPpE/NBXueEoU/1Tzdy5H3pAvTA/2GzS8+cHnx8i
teoiccsq8FZ8/qyo0QYPFBRSTP5kKwxpKrgNUG4+BAe/eiCL+O5lCeHHSQgyPQ0o
fkkdt0rvAucNgBfIXOBhYsvss2B5JdoaZXOcOBCgJjqwyBZ9kzEi7nQLiMBciUEA
KKlHMd99SUWa9eanRRrSjhMQ34Ovmw2tfn6dNVA0BM7pINae253UqNpktNEvWS5e
ojZh1CSggjMziqHRbO9haKPl0latxf1eYusVqHQSTC8xjOnB3xBLAer2VBvNfzu9
XJ/B288ByvK6YBIhMe2pZLiySVgXbVrXzYxtvp5/4gJYp9vDLVj2dAZqmvZh+fYA
tmnYOosxWd2R5nwnI4fdAw+PKowegwFOAWEMUnNt/AiiuSpm5HZNMaBWm9lTjaK2
jwLI5jqmBNFI+8NKAnb9L9K8E7bobTQk+p0pisehKxTxlgBzuRPpwLk6R1YCcYAn
pLwltum95OmYdBbxN4SBB7SC
-----END CERTIFICATE-----

View File

@ -1,8 +1,7 @@
/*
* Tests the Certificate Transparency public and internal APIs.
*
* Author: Rob Percival (robpercival@google.com)
* Date: 2016-01-26
* Author: Rob Percival (robpercival@google.com)
*
* ====================================================================
* Copyright (c) 2016 The OpenSSL Project. All rights reserved.
@ -72,9 +71,12 @@
typedef struct ct_test_fixture {
const char *test_case_name;
/* The CT log store to use during tests */
CTLOG_STORE* ctlog_store;
/* Set the following to test handling of SCTs in X509 certificates */
const char *certificate_file_path;
size_t expected_sct_count;
const char *issuer_file_path;
int expected_sct_count;
/* Set the following to test handling of SCTs in TLS format */
const uint8_t *tls_sct;
size_t tls_sct_len;
@ -85,6 +87,8 @@ typedef struct ct_test_fixture {
* A maximum of |CT_TEST_MAX_FILE_SIZE| bytes will be read of this file.
*/
const char *sct_text_file_path;
/* Whether to test the validity of the SCT(s) */
int test_validity;
} CT_TEST_FIXTURE;
@ -92,10 +96,25 @@ static CT_TEST_FIXTURE set_up(const char *const test_case_name)
{
CT_TEST_FIXTURE fixture;
int setup_ok = 1;
CTLOG_STORE *ctlog_store = CTLOG_STORE_new();
if (ctlog_store == NULL) {
setup_ok = 0;
fprintf(stderr, "Failed to create a new CT log store\n");
goto end;
}
if (CTLOG_STORE_load_default_file(ctlog_store) != 1) {
setup_ok = 0;
fprintf(stderr, "Failed to load CT log list\n");
goto end;
}
memset(&fixture, 0, sizeof(fixture));
fixture.test_case_name = test_case_name;
fixture.ctlog_store = ctlog_store;
end:
if (!setup_ok) {
exit(EXIT_FAILURE);
}
@ -104,6 +123,7 @@ static CT_TEST_FIXTURE set_up(const char *const test_case_name)
static void tear_down(CT_TEST_FIXTURE fixture)
{
CTLOG_STORE_free(fixture.ctlog_store);
ERR_print_errors_fp(stderr);
}
@ -211,11 +231,13 @@ end:
static int execute_cert_test(CT_TEST_FIXTURE fixture)
{
int result = 0;
X509 *cert = NULL;
int test_failed = 0;
X509 *cert = NULL, *issuer = NULL;
STACK_OF(SCT) *scts = NULL;
SCT *sct = NULL;
char expected_sct_text[CT_TEST_MAX_FILE_SIZE];
int sct_text_len = 0;
CT_POLICY_EVAL_CTX *ct_policy_ctx = CT_POLICY_EVAL_CTX_new();
if (fixture.sct_text_file_path != NULL) {
sct_text_len = read_text_file(
@ -224,7 +246,7 @@ static int execute_cert_test(CT_TEST_FIXTURE fixture)
CT_TEST_MAX_FILE_SIZE - 1);
if (sct_text_len < 0) {
result = 1;
test_failed = 1;
fprintf(stderr, "Test data file not found: %s\n",
fixture.sct_text_file_path);
goto end;
@ -233,38 +255,106 @@ static int execute_cert_test(CT_TEST_FIXTURE fixture)
expected_sct_text[sct_text_len] = '\0';
}
CT_POLICY_EVAL_CTX_set0_log_store(ct_policy_ctx, fixture.ctlog_store);
if (fixture.certificate_file_path != NULL) {
int sct_extension_index;
X509_EXTENSION *sct_extension = NULL;
cert = load_pem_cert(fixture.certificate_file_path);
if (cert == NULL) {
result = 1;
test_failed = 1;
fprintf(stderr, "Unable to load certificate: %s\n",
fixture.certificate_file_path);
goto end;
}
sct_extension_index = X509_get_ext_by_NID(cert, NID_ct_precert_scts, -1);
CT_POLICY_EVAL_CTX_set0_cert(ct_policy_ctx, cert);
if (fixture.issuer_file_path != NULL) {
issuer = load_pem_cert(fixture.issuer_file_path);
if (issuer == NULL) {
test_failed = 1;
fprintf(stderr, "Unable to load issuer certificate: %s\n",
fixture.issuer_file_path);
goto end;
}
CT_POLICY_EVAL_CTX_set0_issuer(ct_policy_ctx, issuer);
}
sct_extension_index =
X509_get_ext_by_NID(cert, NID_ct_precert_scts, -1);
sct_extension = X509_get_ext(cert, sct_extension_index);
if (fixture.expected_sct_count > 0) {
if (sct_extension == NULL) {
result = 1;
test_failed = 1;
fprintf(stderr, "SCT extension not found in: %s\n",
fixture.certificate_file_path);
goto end;
}
if (fixture.sct_text_file_path) {
result = compare_extension_printout(sct_extension,
test_failed = compare_extension_printout(sct_extension,
expected_sct_text);
if (result != 0)
if (test_failed != 0)
goto end;
}
if (fixture.test_validity) {
int are_scts_validated = 0;
scts = X509V3_EXT_d2i(sct_extension);
SCT_LIST_set_source(scts, SCT_SOURCE_X509V3_EXTENSION);
are_scts_validated = SCT_LIST_validate(scts, ct_policy_ctx);
if (are_scts_validated < 0) {
fprintf(stderr, "Error verifying SCTs\n");
test_failed = 1;
} else if (!are_scts_validated) {
int invalid_sct_count = 0;
int valid_sct_count = 0;
int i;
for (i = 0; i < sk_SCT_num(scts); ++i) {
SCT *sct_i = sk_SCT_value(scts, i);
switch (SCT_get_validation_status(sct_i)) {
case SCT_VALIDATION_STATUS_VALID:
++valid_sct_count;
break;
case SCT_VALIDATION_STATUS_INVALID:
++invalid_sct_count;
break;
default:
/* Ignore other validation statuses. */
break;
}
}
if (valid_sct_count != fixture.expected_sct_count) {
int unverified_sct_count = sk_SCT_num(scts) -
invalid_sct_count - valid_sct_count;
fprintf(stderr,
"%d SCTs failed verification\n"
"%d SCTs passed verification (%d expected)\n"
"%d SCTs were unverified\n",
invalid_sct_count,
valid_sct_count,
fixture.expected_sct_count,
unverified_sct_count);
}
test_failed = 1;
}
if (test_failed != 0)
goto end;
}
} else if (sct_extension != NULL) {
result = 1;
fprintf(stderr, "Expected no SCTs, but found SCT extension in: %s\n",
fixture.certificate_file_path);
test_failed = 1;
fprintf(stderr,
"Expected no SCTs, but found SCT extension in: %s\n",
fixture.certificate_file_path);
goto end;
}
}
@ -274,30 +364,46 @@ static int execute_cert_test(CT_TEST_FIXTURE fixture)
unsigned char *tls_sct;
size_t tls_sct_len;
if (o2i_SCT(&sct, &p, fixture.tls_sct_len) == NULL) {
result = 1;
test_failed = 1;
fprintf(stderr, "Failed to decode SCT from TLS format\n");
goto end;
}
if (fixture.sct_text_file_path) {
result = compare_sct_printout(sct, expected_sct_text);
if (result != 0)
test_failed = compare_sct_printout(sct, expected_sct_text);
if (test_failed != 0)
goto end;
}
tls_sct_len = i2o_SCT(sct, &tls_sct);
if (tls_sct_len != fixture.tls_sct_len ||
memcmp(fixture.tls_sct, tls_sct, tls_sct_len) != 0) {
result = 1;
test_failed = 1;
fprintf(stderr, "Failed to encode SCT into TLS format correctly\n");
goto end;
}
if (fixture.test_validity && cert != NULL) {
int is_sct_validated = SCT_validate(sct, ct_policy_ctx);
if (is_sct_validated < 0) {
test_failed = 1;
fprintf(stderr, "Error validating SCT\n");
goto end;
} else if (!is_sct_validated) {
test_failed = 1;
fprintf(stderr, "SCT failed verification\n");
goto end;
}
}
}
end:
X509_free(cert);
X509_free(issuer);
SCT_LIST_free(scts);
SCT_free(sct);
return result;
CT_POLICY_EVAL_CTX_free(ct_policy_ctx);
return test_failed;
}
#define SETUP_CT_TEST_FIXTURE() SETUP_TEST_FIXTURE(CT_TEST_FIXTURE, set_up)
@ -307,6 +413,7 @@ static int test_no_scts_in_certificate()
{
SETUP_CT_TEST_FIXTURE();
fixture.certificate_file_path = "certs/leaf.pem";
fixture.issuer_file_path = "certs/subinterCA.pem";
fixture.expected_sct_count = 0;
EXECUTE_CT_TEST();
}
@ -315,6 +422,7 @@ static int test_one_sct_in_certificate()
{
SETUP_CT_TEST_FIXTURE();
fixture.certificate_file_path = "certs/embeddedSCTs1.pem";
fixture.issuer_file_path = "certs/embeddedSCTs1_issuer.pem";
fixture.expected_sct_count = 1;
fixture.sct_text_file_path = "certs/embeddedSCTs1.sct";
EXECUTE_CT_TEST();
@ -324,11 +432,32 @@ static int test_multiple_scts_in_certificate()
{
SETUP_CT_TEST_FIXTURE();
fixture.certificate_file_path = "certs/embeddedSCTs3.pem";
fixture.issuer_file_path = "certs/embeddedSCTs3_issuer.pem";
fixture.expected_sct_count = 3;
fixture.sct_text_file_path = "certs/embeddedSCTs3.sct";
EXECUTE_CT_TEST();
}
static int test_verify_one_sct()
{
SETUP_CT_TEST_FIXTURE();
fixture.certificate_file_path = "certs/embeddedSCTs1.pem";
fixture.issuer_file_path = "certs/embeddedSCTs1_issuer.pem";
fixture.expected_sct_count = 1;
fixture.test_validity = 1;
EXECUTE_CT_TEST();
}
static int test_verify_multiple_scts()
{
SETUP_CT_TEST_FIXTURE();
fixture.certificate_file_path = "certs/embeddedSCTs3.pem";
fixture.issuer_file_path = "certs/embeddedSCTs3_issuer.pem";
fixture.expected_sct_count = 3;
fixture.test_validity = 1;
EXECUTE_CT_TEST();
}
static int test_decode_tls_sct()
{
SETUP_CT_TEST_FIXTURE();
@ -384,6 +513,8 @@ int main(int argc, char *argv[])
ADD_TEST(test_no_scts_in_certificate);
ADD_TEST(test_one_sct_in_certificate);
ADD_TEST(test_multiple_scts_in_certificate);
ADD_TEST(test_verify_one_sct);
ADD_TEST(test_verify_multiple_scts);
ADD_TEST(test_decode_tls_sct);
ADD_TEST(test_encode_tls_sct);

View File

@ -1,6 +1,9 @@
#! /usr/bin/perl
use OpenSSL::Test qw/:DEFAULT srctop_file/;
use OpenSSL::Test::Simple;
simple_test("test_ct", "ct_test");
setup("test_ct");
$ENV{CTLOG_FILE} = srctop_file("test", "ct", "log_list.conf");
simple_test("test_ct", "ct_test", "ct");

View File

@ -4788,3 +4788,16 @@ 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:
SCT_LIST_validate 5295 1_1_0 EXIST::FUNCTION:
CT_verify_at_least_one_good_sct 5296 1_1_0 EXIST::FUNCTION:
CT_POLICY_EVAL_CTX_set0_issuer 5297 1_1_0 EXIST::FUNCTION:
SCT_get_validation_status 5298 1_1_0 EXIST::FUNCTION:
CT_POLICY_EVAL_CTX_set0_log_store 5299 1_1_0 EXIST::FUNCTION:
SCT_validate 5300 1_1_0 EXIST::FUNCTION:
CT_POLICY_EVAL_CTX_new 5301 1_1_0 EXIST::FUNCTION:
CT_POLICY_EVAL_CTX_get0_cert 5302 1_1_0 EXIST::FUNCTION:
CT_POLICY_EVAL_CTX_get0_issuer 5303 1_1_0 EXIST::FUNCTION:
CT_POLICY_EVAL_CTX_set0_cert 5304 1_1_0 EXIST::FUNCTION:
CT_POLICY_EVAL_CTX_get0_log_store 5305 1_1_0 EXIST::FUNCTION:
CT_POLICY_EVAL_CTX_free 5306 1_1_0 EXIST::FUNCTION:
CT_verify_no_bad_scts 5307 1_1_0 EXIST::FUNCTION: