[openssl-commits] [openssl] master update

Rich Salz rsalz at openssl.org
Tue Mar 1 21:20:58 UTC 2016


The branch master has been updated
       via  7d054e5ab2aeaead14c0c19b808d62221020b0e1 (commit)
      from  7852414967b87400b08bfdf321732cfbd07286e2 (commit)


- Log -----------------------------------------------------------------
commit 7d054e5ab2aeaead14c0c19b808d62221020b0e1
Author: Rob Percival <robpercival at google.com>
Date:   Mon Feb 29 17:33:02 2016 +0000

    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 at openssl.org>
    Reviewed-by: Rich Salz <rsalz at openssl.org>

-----------------------------------------------------------------------

Summary of changes:
 crypto/ct/Makefile.in               |   8 +-
 crypto/ct/build.info                |   4 +-
 crypto/ct/ct_err.c                  |  21 +++++
 crypto/ct/ct_locl.h                 |  11 +++
 crypto/ct/ct_policy.c               | 110 +++++++++++++++++++++++
 crypto/ct/ct_sct.c                  |  91 +++++++++++++++++++
 crypto/ct/ct_vfy.c                  |  59 +++++++++++++
 include/openssl/ct.h                |  91 +++++++++++++++++++
 include/openssl/ossl_typ.h          |   1 +
 test/Makefile.in                    |   2 +-
 test/certs/embeddedSCTs1_issuer.pem |  18 ++++
 test/certs/embeddedSCTs3_issuer.pem |  35 ++++++++
 test/ct_test.c                      | 169 ++++++++++++++++++++++++++++++++----
 test/recipes/80-test_ct.t           |   5 +-
 util/libeay.num                     |  13 +++
 15 files changed, 611 insertions(+), 27 deletions(-)
 create mode 100644 crypto/ct/ct_policy.c
 create mode 100644 test/certs/embeddedSCTs1_issuer.pem
 create mode 100644 test/certs/embeddedSCTs3_issuer.pem

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 at 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 at 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 at google.com)
- * Date:        2016-01-26
+ * Author:      Rob Percival (robpercival at 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:


More information about the openssl-commits mailing list