aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Percival <robpercival@google.com>2016-02-29 17:33:02 +0000
committerRob Percival <robpercival@google.com>2016-03-01 20:03:25 +0000
commit7d054e5ab2aeaead14c0c19b808d62221020b0e1 (patch)
tree655006eea9d0fe60879b36d09e9046be5a3749de
parent7852414967b87400b08bfdf321732cfbd07286e2 (diff)
downloadopenssl-7d054e5ab2aeaead14c0c19b808d62221020b0e1.zip
openssl-7d054e5ab2aeaead14c0c19b808d62221020b0e1.tar.gz
openssl-7d054e5ab2aeaead14c0c19b808d62221020b0e1.tar.bz2
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>
-rw-r--r--crypto/ct/Makefile.in8
-rw-r--r--crypto/ct/build.info4
-rw-r--r--crypto/ct/ct_err.c21
-rw-r--r--crypto/ct/ct_locl.h11
-rw-r--r--crypto/ct/ct_policy.c110
-rw-r--r--crypto/ct/ct_sct.c91
-rw-r--r--crypto/ct/ct_vfy.c59
-rw-r--r--include/openssl/ct.h91
-rw-r--r--include/openssl/ossl_typ.h1
-rw-r--r--test/Makefile.in2
-rw-r--r--test/certs/embeddedSCTs1_issuer.pem18
-rw-r--r--test/certs/embeddedSCTs3_issuer.pem35
-rw-r--r--test/ct_test.c169
-rw-r--r--test/recipes/80-test_ct.t5
-rwxr-xr-xutil/libeay.num13
15 files changed, 611 insertions, 27 deletions
diff --git a/crypto/ct/Makefile.in b/crypto/ct/Makefile.in
index de122df..0daec36 100644
--- a/crypto/ct/Makefile.in
+++ b/crypto/ct/Makefile.in
@@ -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)
diff --git a/crypto/ct/build.info b/crypto/ct/build.info
index fbf2495..3ca0e31 100644
--- a/crypto/ct/build.info
+++ b/crypto/ct/build.info
@@ -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
diff --git a/crypto/ct/ct_err.c b/crypto/ct/ct_err.c
index 6db237b..c55677c 100644
--- a/crypto/ct/ct_err.c
+++ b/crypto/ct/ct_err.c
@@ -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"},
diff --git a/crypto/ct/ct_locl.h b/crypto/ct/ct_locl.h
index fce234d..b82fabc 100644
--- a/crypto/ct/ct_locl.h
+++ b/crypto/ct/ct_locl.h
@@ -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.
*/
diff --git a/crypto/ct/ct_policy.c b/crypto/ct/ct_policy.c
new file mode 100644
index 0000000..1236411
--- /dev/null
+++ b/crypto/ct/ct_policy.c
@@ -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;
+}
+
diff --git a/crypto/ct/ct_sct.c b/crypto/ct/ct_sct.c
index e75061d..35f8152 100644
--- a/crypto/ct/ct_sct.c
+++ b/crypto/ct/ct_sct.c
@@ -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;
+}
diff --git a/crypto/ct/ct_vfy.c b/crypto/ct/ct_vfy.c
index 5705312..2366783 100644
--- a/crypto/ct/ct_vfy.c
+++ b/crypto/ct/ct_vfy.c
@@ -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.
diff --git a/include/openssl/ct.h b/include/openssl/ct.h
index de130c4..7ea7ff2 100644
--- a/include/openssl/ct.h
+++ b/include/openssl/ct.h
@@ -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
diff --git a/include/openssl/ossl_typ.h b/include/openssl/ossl_typ.h
index 0132966..7a8d319 100644
--- a/include/openssl/ossl_typ.h
+++ b/include/openssl/ossl_typ.h
@@ -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)
diff --git a/test/Makefile.in b/test/Makefile.in
index 2fb1f49..7a5dcc1 100644
--- a/test/Makefile.in
+++ b/test/Makefile.in
@@ -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)
diff --git a/test/certs/embeddedSCTs1_issuer.pem b/test/certs/embeddedSCTs1_issuer.pem
new file mode 100644
index 0000000..1fa449d
--- /dev/null
+++ b/test/certs/embeddedSCTs1_issuer.pem
@@ -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-----
diff --git a/test/certs/embeddedSCTs3_issuer.pem b/test/certs/embeddedSCTs3_issuer.pem
new file mode 100644
index 0000000..f4bc312
--- /dev/null
+++ b/test/certs/embeddedSCTs3_issuer.pem
@@ -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-----
diff --git a/test/ct_test.c b/test/ct_test.c
index ac739ff..99517a6 100644
--- a/test/ct_test.c
+++ b/test/ct_test.c
@@ -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);
diff --git a/test/recipes/80-test_ct.t b/test/recipes/80-test_ct.t
index 616bb17..54c3e22 100644
--- a/test/recipes/80-test_ct.t
+++ b/test/recipes/80-test_ct.t
@@ -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");
diff --git a/util/libeay.num b/util/libeay.num
index 2484fd2..d49fd74 100755
--- a/util/libeay.num
+++ b/util/libeay.num
@@ -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: