Import Certificate Transparency verbatim from OpenSSL 1.1.1
authorbeck <beck@openbsd.org>
Thu, 28 Oct 2021 11:21:03 +0000 (11:21 +0000)
committerbeck <beck@openbsd.org>
Thu, 28 Oct 2021 11:21:03 +0000 (11:21 +0000)
This is not yet hooked up and will not compile. Follow on commits
will KNF and then make it build.

ok jsing@ tb@

13 files changed:
lib/libcrypto/ct/ct.h [new file with mode: 0644]
lib/libcrypto/ct/ct_b64.c [new file with mode: 0644]
lib/libcrypto/ct/ct_err.c [new file with mode: 0644]
lib/libcrypto/ct/ct_local.h [new file with mode: 0644]
lib/libcrypto/ct/ct_log.c [new file with mode: 0644]
lib/libcrypto/ct/ct_oct.c [new file with mode: 0644]
lib/libcrypto/ct/ct_policy.c [new file with mode: 0644]
lib/libcrypto/ct/ct_prn.c [new file with mode: 0644]
lib/libcrypto/ct/ct_sct.c [new file with mode: 0644]
lib/libcrypto/ct/ct_sct_ctx.c [new file with mode: 0644]
lib/libcrypto/ct/ct_vfy.c [new file with mode: 0644]
lib/libcrypto/ct/ct_x509v3.c [new file with mode: 0644]
lib/libcrypto/ct/cterr.h [new file with mode: 0644]

diff --git a/lib/libcrypto/ct/ct.h b/lib/libcrypto/ct/ct.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/lib/libcrypto/ct/ct_b64.c b/lib/libcrypto/ct/ct_b64.c
new file mode 100644 (file)
index 0000000..4abe11c
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <limits.h>
+#include <string.h>
+
+#include <openssl/ct.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+
+#include "ct_local.h"
+
+/*
+ * Decodes the base64 string |in| into |out|.
+ * A new string will be malloc'd and assigned to |out|. This will be owned by
+ * the caller. Do not provide a pre-allocated string in |out|.
+ */
+static int ct_base64_decode(const char *in, unsigned char **out)
+{
+    size_t inlen = strlen(in);
+    int outlen, i;
+    unsigned char *outbuf = NULL;
+
+    if (inlen == 0) {
+        *out = NULL;
+        return 0;
+    }
+
+    outlen = (inlen / 4) * 3;
+    outbuf = OPENSSL_malloc(outlen);
+    if (outbuf == NULL) {
+        CTerr(CT_F_CT_BASE64_DECODE, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    outlen = EVP_DecodeBlock(outbuf, (unsigned char *)in, inlen);
+    if (outlen < 0) {
+        CTerr(CT_F_CT_BASE64_DECODE, CT_R_BASE64_DECODE_ERROR);
+        goto err;
+    }
+
+    /* Subtract padding bytes from |outlen|.  Any more than 2 is malformed. */
+    i = 0;
+    while (in[--inlen] == '=') {
+        --outlen;
+        if (++i > 2)
+            goto err;
+    }
+
+    *out = outbuf;
+    return outlen;
+err:
+    OPENSSL_free(outbuf);
+    return -1;
+}
+
+SCT *SCT_new_from_base64(unsigned char version, const char *logid_base64,
+                         ct_log_entry_type_t entry_type, uint64_t timestamp,
+                         const char *extensions_base64,
+                         const char *signature_base64)
+{
+    SCT *sct = SCT_new();
+    unsigned char *dec = NULL;
+    const unsigned char* p = NULL;
+    int declen;
+
+    if (sct == NULL) {
+        CTerr(CT_F_SCT_NEW_FROM_BASE64, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    /*
+     * RFC6962 section 4.1 says we "MUST NOT expect this to be 0", but we
+     * can only construct SCT versions that have been defined.
+     */
+    if (!SCT_set_version(sct, version)) {
+        CTerr(CT_F_SCT_NEW_FROM_BASE64, CT_R_SCT_UNSUPPORTED_VERSION);
+        goto err;
+    }
+
+    declen = ct_base64_decode(logid_base64, &dec);
+    if (declen < 0) {
+        CTerr(CT_F_SCT_NEW_FROM_BASE64, X509_R_BASE64_DECODE_ERROR);
+        goto err;
+    }
+    if (!SCT_set0_log_id(sct, dec, declen))
+        goto err;
+    dec = NULL;
+
+    declen = ct_base64_decode(extensions_base64, &dec);
+    if (declen < 0) {
+        CTerr(CT_F_SCT_NEW_FROM_BASE64, X509_R_BASE64_DECODE_ERROR);
+        goto err;
+    }
+    SCT_set0_extensions(sct, dec, declen);
+    dec = NULL;
+
+    declen = ct_base64_decode(signature_base64, &dec);
+    if (declen < 0) {
+        CTerr(CT_F_SCT_NEW_FROM_BASE64, X509_R_BASE64_DECODE_ERROR);
+        goto err;
+    }
+
+    p = dec;
+    if (o2i_SCT_signature(sct, &p, declen) <= 0)
+        goto err;
+    OPENSSL_free(dec);
+    dec = NULL;
+
+    SCT_set_timestamp(sct, timestamp);
+
+    if (!SCT_set_log_entry_type(sct, entry_type))
+        goto err;
+
+    return sct;
+
+ err:
+    OPENSSL_free(dec);
+    SCT_free(sct);
+    return NULL;
+}
+
+/*
+ * Allocate, build and returns a new |ct_log| from input |pkey_base64|
+ * It returns 1 on success,
+ * 0 on decoding failure, or invalid parameter if any
+ * -1 on internal (malloc) failure
+ */
+int CTLOG_new_from_base64(CTLOG **ct_log, const char *pkey_base64, const char *name)
+{
+    unsigned char *pkey_der = NULL;
+    int pkey_der_len;
+    const unsigned char *p;
+    EVP_PKEY *pkey = NULL;
+
+    if (ct_log == NULL) {
+        CTerr(CT_F_CTLOG_NEW_FROM_BASE64, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+
+    pkey_der_len = ct_base64_decode(pkey_base64, &pkey_der);
+    if (pkey_der_len < 0) {
+        CTerr(CT_F_CTLOG_NEW_FROM_BASE64, CT_R_LOG_CONF_INVALID_KEY);
+        return 0;
+    }
+
+    p = pkey_der;
+    pkey = d2i_PUBKEY(NULL, &p, pkey_der_len);
+    OPENSSL_free(pkey_der);
+    if (pkey == NULL) {
+        CTerr(CT_F_CTLOG_NEW_FROM_BASE64, CT_R_LOG_CONF_INVALID_KEY);
+        return 0;
+    }
+
+    *ct_log = CTLOG_new(pkey, name);
+    if (*ct_log == NULL) {
+        EVP_PKEY_free(pkey);
+        return 0;
+    }
+
+    return 1;
+}
diff --git a/lib/libcrypto/ct/ct_err.c b/lib/libcrypto/ct/ct_err.c
new file mode 100644 (file)
index 0000000..c0c62fe
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/err.h>
+#include <openssl/cterr.h>
+
+#ifndef OPENSSL_NO_ERR
+
+static const ERR_STRING_DATA CT_str_functs[] = {
+    {ERR_PACK(ERR_LIB_CT, CT_F_CTLOG_NEW, 0), "CTLOG_new"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_CTLOG_NEW_FROM_BASE64, 0),
+     "CTLOG_new_from_base64"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_CTLOG_NEW_FROM_CONF, 0), "ctlog_new_from_conf"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_CTLOG_STORE_LOAD_CTX_NEW, 0),
+     "ctlog_store_load_ctx_new"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_CTLOG_STORE_LOAD_FILE, 0),
+     "CTLOG_STORE_load_file"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_CTLOG_STORE_LOAD_LOG, 0),
+     "ctlog_store_load_log"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_CTLOG_STORE_NEW, 0), "CTLOG_STORE_new"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_CT_BASE64_DECODE, 0), "ct_base64_decode"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_CT_POLICY_EVAL_CTX_NEW, 0),
+     "CT_POLICY_EVAL_CTX_new"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_CT_V1_LOG_ID_FROM_PKEY, 0),
+     "ct_v1_log_id_from_pkey"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_I2O_SCT, 0), "i2o_SCT"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_I2O_SCT_LIST, 0), "i2o_SCT_LIST"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_I2O_SCT_SIGNATURE, 0), "i2o_SCT_signature"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_O2I_SCT, 0), "o2i_SCT"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_O2I_SCT_LIST, 0), "o2i_SCT_LIST"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_O2I_SCT_SIGNATURE, 0), "o2i_SCT_signature"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_SCT_CTX_NEW, 0), "SCT_CTX_new"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_SCT_CTX_VERIFY, 0), "SCT_CTX_verify"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_SCT_NEW, 0), "SCT_new"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_SCT_NEW_FROM_BASE64, 0), "SCT_new_from_base64"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_SCT_SET0_LOG_ID, 0), "SCT_set0_log_id"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_SCT_SET1_EXTENSIONS, 0), "SCT_set1_extensions"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_SCT_SET1_LOG_ID, 0), "SCT_set1_log_id"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_SCT_SET1_SIGNATURE, 0), "SCT_set1_signature"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_SCT_SET_LOG_ENTRY_TYPE, 0),
+     "SCT_set_log_entry_type"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_SCT_SET_SIGNATURE_NID, 0),
+     "SCT_set_signature_nid"},
+    {ERR_PACK(ERR_LIB_CT, CT_F_SCT_SET_VERSION, 0), "SCT_set_version"},
+    {0, NULL}
+};
+
+static const ERR_STRING_DATA CT_str_reasons[] = {
+    {ERR_PACK(ERR_LIB_CT, 0, CT_R_BASE64_DECODE_ERROR), "base64 decode error"},
+    {ERR_PACK(ERR_LIB_CT, 0, CT_R_INVALID_LOG_ID_LENGTH),
+    "invalid log id length"},
+    {ERR_PACK(ERR_LIB_CT, 0, CT_R_LOG_CONF_INVALID), "log conf invalid"},
+    {ERR_PACK(ERR_LIB_CT, 0, CT_R_LOG_CONF_INVALID_KEY),
+    "log conf invalid key"},
+    {ERR_PACK(ERR_LIB_CT, 0, CT_R_LOG_CONF_MISSING_DESCRIPTION),
+    "log conf missing description"},
+    {ERR_PACK(ERR_LIB_CT, 0, CT_R_LOG_CONF_MISSING_KEY),
+    "log conf missing key"},
+    {ERR_PACK(ERR_LIB_CT, 0, CT_R_LOG_KEY_INVALID), "log key invalid"},
+    {ERR_PACK(ERR_LIB_CT, 0, CT_R_SCT_FUTURE_TIMESTAMP),
+    "sct future timestamp"},
+    {ERR_PACK(ERR_LIB_CT, 0, CT_R_SCT_INVALID), "sct invalid"},
+    {ERR_PACK(ERR_LIB_CT, 0, CT_R_SCT_INVALID_SIGNATURE),
+    "sct invalid signature"},
+    {ERR_PACK(ERR_LIB_CT, 0, CT_R_SCT_LIST_INVALID), "sct list invalid"},
+    {ERR_PACK(ERR_LIB_CT, 0, CT_R_SCT_LOG_ID_MISMATCH), "sct log id mismatch"},
+    {ERR_PACK(ERR_LIB_CT, 0, CT_R_SCT_NOT_SET), "sct not set"},
+    {ERR_PACK(ERR_LIB_CT, 0, CT_R_SCT_UNSUPPORTED_VERSION),
+    "sct unsupported version"},
+    {ERR_PACK(ERR_LIB_CT, 0, CT_R_UNRECOGNIZED_SIGNATURE_NID),
+    "unrecognized signature nid"},
+    {ERR_PACK(ERR_LIB_CT, 0, CT_R_UNSUPPORTED_ENTRY_TYPE),
+    "unsupported entry type"},
+    {ERR_PACK(ERR_LIB_CT, 0, CT_R_UNSUPPORTED_VERSION), "unsupported version"},
+    {0, NULL}
+};
+
+#endif
+
+int ERR_load_CT_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+    if (ERR_func_error_string(CT_str_functs[0].error) == NULL) {
+        ERR_load_strings_const(CT_str_functs);
+        ERR_load_strings_const(CT_str_reasons);
+    }
+#endif
+    return 1;
+}
diff --git a/lib/libcrypto/ct/ct_local.h b/lib/libcrypto/ct/ct_local.h
new file mode 100644 (file)
index 0000000..9f983c9
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stddef.h>
+#include <openssl/ct.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/safestack.h>
+
+/*
+ * From RFC6962: opaque SerializedSCT<1..2^16-1>; struct { SerializedSCT
+ * sct_list <1..2^16-1>; } SignedCertificateTimestampList;
+ */
+# define MAX_SCT_SIZE            65535
+# define MAX_SCT_LIST_SIZE       MAX_SCT_SIZE
+
+/*
+ * Macros to read and write integers in network-byte order.
+ */
+
+#define n2s(c,s)        ((s=(((unsigned int)((c)[0]))<< 8)| \
+                            (((unsigned int)((c)[1]))    )),c+=2)
+
+#define s2n(s,c)        ((c[0]=(unsigned char)(((s)>> 8)&0xff), \
+                          c[1]=(unsigned char)(((s)    )&0xff)),c+=2)
+
+#define l2n3(l,c)       ((c[0]=(unsigned char)(((l)>>16)&0xff), \
+                          c[1]=(unsigned char)(((l)>> 8)&0xff), \
+                          c[2]=(unsigned char)(((l)    )&0xff)),c+=3)
+
+#define n2l8(c,l)       (l =((uint64_t)(*((c)++)))<<56, \
+                         l|=((uint64_t)(*((c)++)))<<48, \
+                         l|=((uint64_t)(*((c)++)))<<40, \
+                         l|=((uint64_t)(*((c)++)))<<32, \
+                         l|=((uint64_t)(*((c)++)))<<24, \
+                         l|=((uint64_t)(*((c)++)))<<16, \
+                         l|=((uint64_t)(*((c)++)))<< 8, \
+                         l|=((uint64_t)(*((c)++))))
+
+#define l2n8(l,c)       (*((c)++)=(unsigned char)(((l)>>56)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>48)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>40)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>32)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>24)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>>16)&0xff), \
+                         *((c)++)=(unsigned char)(((l)>> 8)&0xff), \
+                         *((c)++)=(unsigned char)(((l)    )&0xff))
+
+/* Signed Certificate Timestamp */
+struct sct_st {
+    sct_version_t version;
+    /* If version is not SCT_VERSION_V1, this contains the encoded SCT */
+    unsigned char *sct;
+    size_t sct_len;
+    /* If version is SCT_VERSION_V1, fields below contain components of the SCT */
+    unsigned char *log_id;
+    size_t log_id_len;
+    /*
+    * Note, we cannot distinguish between an unset timestamp, and one
+    * that is set to 0.  However since CT didn't exist in 1970, no real
+    * SCT should ever be set as such.
+    */
+    uint64_t timestamp;
+    unsigned char *ext;
+    size_t ext_len;
+    unsigned char hash_alg;
+    unsigned char sig_alg;
+    unsigned char *sig;
+    size_t sig_len;
+    /* Log entry type */
+    ct_log_entry_type_t entry_type;
+    /* Where this SCT was found, e.g. certificate, OCSP response, etc. */
+    sct_source_t source;
+    /* The result of the last attempt to validate this SCT. */
+    sct_validation_status_t validation_status;
+};
+
+/* Miscellaneous data that is useful when verifying an SCT  */
+struct sct_ctx_st {
+    /* Public key */
+    EVP_PKEY *pkey;
+    /* Hash of public key */
+    unsigned char *pkeyhash;
+    size_t pkeyhashlen;
+    /* For pre-certificate: issuer public key hash */
+    unsigned char *ihash;
+    size_t ihashlen;
+    /* certificate encoding */
+    unsigned char *certder;
+    size_t certderlen;
+    /* pre-certificate encoding */
+    unsigned char *preder;
+    size_t prederlen;
+    /* milliseconds since epoch (to check that the SCT isn't from the future) */
+    uint64_t epoch_time_in_ms;
+};
+
+/* Context when evaluating whether a Certificate Transparency policy is met */
+struct ct_policy_eval_ctx_st {
+    X509 *cert;
+    X509 *issuer;
+    CTLOG_STORE *log_store;
+    /* milliseconds since epoch (to check that SCTs aren't from the future) */
+    uint64_t epoch_time_in_ms;
+};
+
+/*
+ * Creates a new context for verifying an SCT.
+ */
+SCT_CTX *SCT_CTX_new(void);
+/*
+ * Deletes an SCT verification context.
+ */
+void SCT_CTX_free(SCT_CTX *sctx);
+
+/*
+ * Sets the certificate that the SCT was created for.
+ * If *cert does not have a poison extension, presigner must be NULL.
+ * If *cert does not have a poison extension, it may have a single SCT
+ * (NID_ct_precert_scts) extension.
+ * If either *cert or *presigner have an AKID (NID_authority_key_identifier)
+ * extension, both must have one.
+ * Returns 1 on success, 0 on failure.
+ */
+__owur int SCT_CTX_set1_cert(SCT_CTX *sctx, X509 *cert, X509 *presigner);
+
+/*
+ * Sets the issuer of the certificate that the SCT was created for.
+ * This is just a convenience method to save extracting the public key and
+ * calling SCT_CTX_set1_issuer_pubkey().
+ * Issuer must not be NULL.
+ * Returns 1 on success, 0 on failure.
+ */
+__owur int SCT_CTX_set1_issuer(SCT_CTX *sctx, const X509 *issuer);
+
+/*
+ * Sets the public key of the issuer of the certificate that the SCT was created
+ * for.
+ * The public key must not be NULL.
+ * Returns 1 on success, 0 on failure.
+ */
+__owur int SCT_CTX_set1_issuer_pubkey(SCT_CTX *sctx, X509_PUBKEY *pubkey);
+
+/*
+ * Sets the public key of the CT log that the SCT is from.
+ * Returns 1 on success, 0 on failure.
+ */
+__owur int SCT_CTX_set1_pubkey(SCT_CTX *sctx, X509_PUBKEY *pubkey);
+
+/*
+ * Sets the time to evaluate the SCT against, in milliseconds since the Unix
+ * epoch. If the SCT's timestamp is after this time, it will be interpreted as
+ * having been issued in the future. RFC6962 states that "TLS clients MUST
+ * reject SCTs whose timestamp is in the future", so an SCT will not validate
+ * in this case.
+ */
+void SCT_CTX_set_time(SCT_CTX *sctx, uint64_t time_in_ms);
+
+/*
+ * Verifies an SCT with the given context.
+ * Returns 1 if the SCT verifies successfully; any other value indicates
+ * failure. See EVP_DigestVerifyFinal() for the meaning of those values.
+ */
+__owur int SCT_CTX_verify(const SCT_CTX *sctx, const SCT *sct);
+
+/*
+ * Does this SCT have the minimum fields populated to be usable?
+ * Returns 1 if so, 0 otherwise.
+ */
+__owur int SCT_is_complete(const SCT *sct);
+
+/*
+ * Does this SCT have the signature-related fields populated?
+ * Returns 1 if so, 0 otherwise.
+ * This checks that the signature and hash algorithms are set to supported
+ * values and that the signature field is set.
+ */
+__owur int SCT_signature_is_complete(const SCT *sct);
+
+/*
+ * TODO(RJPercival): Create an SCT_signature struct and make i2o_SCT_signature
+ * and o2i_SCT_signature conform to the i2d/d2i conventions.
+ */
+
+/*
+* Serialize (to TLS format) an |sct| signature and write it to |out|.
+* If |out| is null, no signature will be output but the length will be returned.
+* If |out| points to a null pointer, a string will be allocated to hold the
+* TLS-format signature. It is the responsibility of the caller to free it.
+* If |out| points to an allocated string, the signature will be written to it.
+* The length of the signature in TLS format will be returned.
+*/
+__owur int i2o_SCT_signature(const SCT *sct, unsigned char **out);
+
+/*
+* Parses an SCT signature in TLS format and populates the |sct| with it.
+* |in| should be a pointer to a string containing the TLS-format signature.
+* |in| will be advanced to the end of the signature if parsing succeeds.
+* |len| should be the length of the signature in |in|.
+* Returns the number of bytes parsed, or a negative integer if an error occurs.
+* If an error occurs, the SCT's signature NID may be updated whilst the
+* signature field itself remains unset.
+*/
+__owur int o2i_SCT_signature(SCT *sct, const unsigned char **in, size_t len);
+
+/*
+ * Handlers for Certificate Transparency X509v3/OCSP extensions
+ */
+extern const X509V3_EXT_METHOD v3_ct_scts[3];
diff --git a/lib/libcrypto/ct/ct_log.c b/lib/libcrypto/ct/ct_log.c
new file mode 100644 (file)
index 0000000..c1bca3e
--- /dev/null
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/conf.h>
+#include <openssl/ct.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/safestack.h>
+
+#include "internal/cryptlib.h"
+
+/*
+ * Information about a CT log server.
+ */
+struct ctlog_st {
+    char *name;
+    uint8_t log_id[CT_V1_HASHLEN];
+    EVP_PKEY *public_key;
+};
+
+/*
+ * A store for multiple CTLOG instances.
+ * It takes ownership of any CTLOG instances added to it.
+ */
+struct ctlog_store_st {
+    STACK_OF(CTLOG) *logs;
+};
+
+/* The context when loading a CT log list from a CONF file. */
+typedef struct ctlog_store_load_ctx_st {
+    CTLOG_STORE *log_store;
+    CONF *conf;
+    size_t invalid_log_entries;
+} CTLOG_STORE_LOAD_CTX;
+
+/*
+ * Creates an empty context for loading a CT log store.
+ * It should be populated before use.
+ */
+static CTLOG_STORE_LOAD_CTX *ctlog_store_load_ctx_new(void);
+
+/*
+ * Deletes a CT log store load context.
+ * Does not delete any of the fields.
+ */
+static void ctlog_store_load_ctx_free(CTLOG_STORE_LOAD_CTX* ctx);
+
+static CTLOG_STORE_LOAD_CTX *ctlog_store_load_ctx_new(void)
+{
+    CTLOG_STORE_LOAD_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+    if (ctx == NULL)
+        CTerr(CT_F_CTLOG_STORE_LOAD_CTX_NEW, ERR_R_MALLOC_FAILURE);
+
+    return ctx;
+}
+
+static void ctlog_store_load_ctx_free(CTLOG_STORE_LOAD_CTX* ctx)
+{
+    OPENSSL_free(ctx);
+}
+
+/* Converts a log's public key into a SHA256 log ID */
+static int ct_v1_log_id_from_pkey(EVP_PKEY *pkey,
+                                  unsigned char log_id[CT_V1_HASHLEN])
+{
+    int ret = 0;
+    unsigned char *pkey_der = NULL;
+    int pkey_der_len = i2d_PUBKEY(pkey, &pkey_der);
+
+    if (pkey_der_len <= 0) {
+        CTerr(CT_F_CT_V1_LOG_ID_FROM_PKEY, CT_R_LOG_KEY_INVALID);
+        goto err;
+    }
+
+    SHA256(pkey_der, pkey_der_len, log_id);
+    ret = 1;
+err:
+    OPENSSL_free(pkey_der);
+    return ret;
+}
+
+CTLOG_STORE *CTLOG_STORE_new(void)
+{
+    CTLOG_STORE *ret = OPENSSL_zalloc(sizeof(*ret));
+
+    if (ret == NULL) {
+        CTerr(CT_F_CTLOG_STORE_NEW, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    ret->logs = sk_CTLOG_new_null();
+    if (ret->logs == NULL)
+        goto err;
+
+    return ret;
+err:
+    OPENSSL_free(ret);
+    return NULL;
+}
+
+void CTLOG_STORE_free(CTLOG_STORE *store)
+{
+    if (store != NULL) {
+        sk_CTLOG_pop_free(store->logs, CTLOG_free);
+        OPENSSL_free(store);
+    }
+}
+
+static int ctlog_new_from_conf(CTLOG **ct_log, const CONF *conf, const char *section)
+{
+    const char *description = NCONF_get_string(conf, section, "description");
+    char *pkey_base64;
+
+    if (description == NULL) {
+        CTerr(CT_F_CTLOG_NEW_FROM_CONF, CT_R_LOG_CONF_MISSING_DESCRIPTION);
+        return 0;
+    }
+
+    pkey_base64 = NCONF_get_string(conf, section, "key");
+    if (pkey_base64 == NULL) {
+        CTerr(CT_F_CTLOG_NEW_FROM_CONF, CT_R_LOG_CONF_MISSING_KEY);
+        return 0;
+    }
+
+    return CTLOG_new_from_base64(ct_log, pkey_base64, description);
+}
+
+int CTLOG_STORE_load_default_file(CTLOG_STORE *store)
+{
+    const char *fpath = ossl_safe_getenv(CTLOG_FILE_EVP);
+
+    if (fpath == NULL)
+      fpath = CTLOG_FILE;
+
+    return CTLOG_STORE_load_file(store, fpath);
+}
+
+/*
+ * Called by CONF_parse_list, which stops if this returns <= 0,
+ * Otherwise, one bad log entry would stop loading of any of
+ * the following log entries.
+ * It may stop parsing and returns -1 on any internal (malloc) error.
+ */
+static int ctlog_store_load_log(const char *log_name, int log_name_len,
+                                void *arg)
+{
+    CTLOG_STORE_LOAD_CTX *load_ctx = arg;
+    CTLOG *ct_log = NULL;
+    /* log_name may not be null-terminated, so fix that before using it */
+    char *tmp;
+    int ret = 0;
+
+    /* log_name will be NULL for empty list entries */
+    if (log_name == NULL)
+        return 1;
+
+    tmp = OPENSSL_strndup(log_name, log_name_len);
+    if (tmp == NULL)
+        goto mem_err;
+
+    ret = ctlog_new_from_conf(&ct_log, load_ctx->conf, tmp);
+    OPENSSL_free(tmp);
+
+    if (ret < 0) {
+        /* Propagate any internal error */
+        return ret;
+    }
+    if (ret == 0) {
+        /* If we can't load this log, record that fact and skip it */
+        ++load_ctx->invalid_log_entries;
+        return 1;
+    }
+
+    if (!sk_CTLOG_push(load_ctx->log_store->logs, ct_log)) {
+        goto mem_err;
+    }
+    return 1;
+
+mem_err:
+    CTLOG_free(ct_log);
+    CTerr(CT_F_CTLOG_STORE_LOAD_LOG, ERR_R_MALLOC_FAILURE);
+    return -1;
+}
+
+int CTLOG_STORE_load_file(CTLOG_STORE *store, const char *file)
+{
+    int ret = 0;
+    char *enabled_logs;
+    CTLOG_STORE_LOAD_CTX* load_ctx = ctlog_store_load_ctx_new();
+
+    if (load_ctx == NULL)
+        return 0;
+    load_ctx->log_store = store;
+    load_ctx->conf = NCONF_new(NULL);
+    if (load_ctx->conf == NULL)
+        goto end;
+
+    if (NCONF_load(load_ctx->conf, file, NULL) <= 0) {
+        CTerr(CT_F_CTLOG_STORE_LOAD_FILE, CT_R_LOG_CONF_INVALID);
+        goto end;
+    }
+
+    enabled_logs = NCONF_get_string(load_ctx->conf, NULL, "enabled_logs");
+    if (enabled_logs == NULL) {
+        CTerr(CT_F_CTLOG_STORE_LOAD_FILE, CT_R_LOG_CONF_INVALID);
+        goto end;
+    }
+
+    if (!CONF_parse_list(enabled_logs, ',', 1, ctlog_store_load_log, load_ctx) ||
+        load_ctx->invalid_log_entries > 0) {
+        CTerr(CT_F_CTLOG_STORE_LOAD_FILE, CT_R_LOG_CONF_INVALID);
+        goto end;
+    }
+
+    ret = 1;
+end:
+    NCONF_free(load_ctx->conf);
+    ctlog_store_load_ctx_free(load_ctx);
+    return ret;
+}
+
+/*
+ * Initialize a new CTLOG object.
+ * Takes ownership of the public key.
+ * Copies the name.
+ */
+CTLOG *CTLOG_new(EVP_PKEY *public_key, const char *name)
+{
+    CTLOG *ret = OPENSSL_zalloc(sizeof(*ret));
+
+    if (ret == NULL) {
+        CTerr(CT_F_CTLOG_NEW, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    ret->name = OPENSSL_strdup(name);
+    if (ret->name == NULL) {
+        CTerr(CT_F_CTLOG_NEW, ERR_R_MALLOC_FAILURE);
+        goto err;
+    }
+
+    if (ct_v1_log_id_from_pkey(public_key, ret->log_id) != 1)
+        goto err;
+
+    ret->public_key = public_key;
+    return ret;
+err:
+    CTLOG_free(ret);
+    return NULL;
+}
+
+/* Frees CT log and associated structures */
+void CTLOG_free(CTLOG *log)
+{
+    if (log != NULL) {
+        OPENSSL_free(log->name);
+        EVP_PKEY_free(log->public_key);
+        OPENSSL_free(log);
+    }
+}
+
+const char *CTLOG_get0_name(const CTLOG *log)
+{
+    return log->name;
+}
+
+void CTLOG_get0_log_id(const CTLOG *log, const uint8_t **log_id,
+                       size_t *log_id_len)
+{
+    *log_id = log->log_id;
+    *log_id_len = CT_V1_HASHLEN;
+}
+
+EVP_PKEY *CTLOG_get0_public_key(const CTLOG *log)
+{
+    return log->public_key;
+}
+
+/*
+ * Given a log ID, finds the matching log.
+ * Returns NULL if no match found.
+ */
+const CTLOG *CTLOG_STORE_get0_log_by_id(const CTLOG_STORE *store,
+                                        const uint8_t *log_id,
+                                        size_t log_id_len)
+{
+    int i;
+
+    for (i = 0; i < sk_CTLOG_num(store->logs); ++i) {
+        const CTLOG *log = sk_CTLOG_value(store->logs, i);
+        if (memcmp(log->log_id, log_id, log_id_len) == 0)
+            return log;
+    }
+
+    return NULL;
+}
diff --git a/lib/libcrypto/ct/ct_oct.c b/lib/libcrypto/ct/ct_oct.c
new file mode 100644 (file)
index 0000000..d4b6645
--- /dev/null
@@ -0,0 +1,407 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifdef OPENSSL_NO_CT
+# error "CT is disabled"
+#endif
+
+#include <limits.h>
+#include <string.h>
+
+#include <openssl/asn1.h>
+#include <openssl/buffer.h>
+#include <openssl/ct.h>
+#include <openssl/err.h>
+
+#include "ct_local.h"
+
+int o2i_SCT_signature(SCT *sct, const unsigned char **in, size_t len)
+{
+    size_t siglen;
+    size_t len_remaining = len;
+    const unsigned char *p;
+
+    if (sct->version != SCT_VERSION_V1) {
+        CTerr(CT_F_O2I_SCT_SIGNATURE, CT_R_UNSUPPORTED_VERSION);
+        return -1;
+    }
+    /*
+     * digitally-signed struct header: (1 byte) Hash algorithm (1 byte)
+     * Signature algorithm (2 bytes + ?) Signature
+     *
+     * This explicitly rejects empty signatures: they're invalid for
+     * all supported algorithms.
+     */
+    if (len <= 4) {
+        CTerr(CT_F_O2I_SCT_SIGNATURE, CT_R_SCT_INVALID_SIGNATURE);
+        return -1;
+    }
+
+    p = *in;
+    /* Get hash and signature algorithm */
+    sct->hash_alg = *p++;
+    sct->sig_alg = *p++;
+    if (SCT_get_signature_nid(sct) == NID_undef) {
+        CTerr(CT_F_O2I_SCT_SIGNATURE, CT_R_SCT_INVALID_SIGNATURE);
+        return -1;
+    }
+    /* Retrieve signature and check it is consistent with the buffer length */
+    n2s(p, siglen);
+    len_remaining -= (p - *in);
+    if (siglen > len_remaining) {
+        CTerr(CT_F_O2I_SCT_SIGNATURE, CT_R_SCT_INVALID_SIGNATURE);
+        return -1;
+    }
+
+    if (SCT_set1_signature(sct, p, siglen) != 1)
+        return -1;
+    len_remaining -= siglen;
+    *in = p + siglen;
+
+    return len - len_remaining;
+}
+
+SCT *o2i_SCT(SCT **psct, const unsigned char **in, size_t len)
+{
+    SCT *sct = NULL;
+    const unsigned char *p;
+
+    if (len == 0 || len > MAX_SCT_SIZE) {
+        CTerr(CT_F_O2I_SCT, CT_R_SCT_INVALID);
+        goto err;
+    }
+
+    if ((sct = SCT_new()) == NULL)
+        goto err;
+
+    p = *in;
+
+    sct->version = *p;
+    if (sct->version == SCT_VERSION_V1) {
+        int sig_len;
+        size_t len2;
+        /*-
+         * Fixed-length header:
+         *   struct {
+         *     Version sct_version;     (1 byte)
+         *     log_id id;               (32 bytes)
+         *     uint64 timestamp;        (8 bytes)
+         *     CtExtensions extensions; (2 bytes + ?)
+         *   }
+         */
+        if (len < 43) {
+            CTerr(CT_F_O2I_SCT, CT_R_SCT_INVALID);
+            goto err;
+        }
+        len -= 43;
+        p++;
+        sct->log_id = BUF_memdup(p, CT_V1_HASHLEN);
+        if (sct->log_id == NULL)
+            goto err;
+        sct->log_id_len = CT_V1_HASHLEN;
+        p += CT_V1_HASHLEN;
+
+        n2l8(p, sct->timestamp);
+
+        n2s(p, len2);
+        if (len < len2) {
+            CTerr(CT_F_O2I_SCT, CT_R_SCT_INVALID);
+            goto err;
+        }
+        if (len2 > 0) {
+            sct->ext = BUF_memdup(p, len2);
+            if (sct->ext == NULL)
+                goto err;
+        }
+        sct->ext_len = len2;
+        p += len2;
+        len -= len2;
+
+        sig_len = o2i_SCT_signature(sct, &p, len);
+        if (sig_len <= 0) {
+            CTerr(CT_F_O2I_SCT, CT_R_SCT_INVALID);
+            goto err;
+        }
+        len -= sig_len;
+        *in = p + len;
+    } else {
+        /* If not V1 just cache encoding */
+        sct->sct = BUF_memdup(p, len);
+        if (sct->sct == NULL)
+            goto err;
+        sct->sct_len = len;
+        *in = p + len;
+    }
+
+    if (psct != NULL) {
+        SCT_free(*psct);
+        *psct = sct;
+    }
+
+    return sct;
+err:
+    SCT_free(sct);
+    return NULL;
+}
+
+int i2o_SCT_signature(const SCT *sct, unsigned char **out)
+{
+    size_t len;
+    unsigned char *p = NULL, *pstart = NULL;
+
+    if (!SCT_signature_is_complete(sct)) {
+        CTerr(CT_F_I2O_SCT_SIGNATURE, CT_R_SCT_INVALID_SIGNATURE);
+        goto err;
+    }
+
+    if (sct->version != SCT_VERSION_V1) {
+        CTerr(CT_F_I2O_SCT_SIGNATURE, CT_R_UNSUPPORTED_VERSION);
+        goto err;
+    }
+
+    /*
+    * (1 byte) Hash algorithm
+    * (1 byte) Signature algorithm
+    * (2 bytes + ?) Signature
+    */
+    len = 4 + sct->sig_len;
+
+    if (out != NULL) {
+        if (*out != NULL) {
+            p = *out;
+            *out += len;
+        } else {
+            pstart = p = OPENSSL_malloc(len);
+            if (p == NULL) {
+                CTerr(CT_F_I2O_SCT_SIGNATURE, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+            *out = p;
+        }
+
+        *p++ = sct->hash_alg;
+        *p++ = sct->sig_alg;
+        s2n(sct->sig_len, p);
+        memcpy(p, sct->sig, sct->sig_len);
+    }
+
+    return len;
+err:
+    OPENSSL_free(pstart);
+    return -1;
+}
+
+int i2o_SCT(const SCT *sct, unsigned char **out)
+{
+    size_t len;
+    unsigned char *p = NULL, *pstart = NULL;
+
+    if (!SCT_is_complete(sct)) {
+        CTerr(CT_F_I2O_SCT, CT_R_SCT_NOT_SET);
+        goto err;
+    }
+    /*
+     * Fixed-length header: struct { (1 byte) Version sct_version; (32 bytes)
+     * log_id id; (8 bytes) uint64 timestamp; (2 bytes + ?) CtExtensions
+     * extensions; (1 byte) Hash algorithm (1 byte) Signature algorithm (2
+     * bytes + ?) Signature
+     */
+    if (sct->version == SCT_VERSION_V1)
+        len = 43 + sct->ext_len + 4 + sct->sig_len;
+    else
+        len = sct->sct_len;
+
+    if (out == NULL)
+        return len;
+
+    if (*out != NULL) {
+        p = *out;
+        *out += len;
+    } else {
+        pstart = p = OPENSSL_malloc(len);
+        if (p == NULL) {
+            CTerr(CT_F_I2O_SCT, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        *out = p;
+    }
+
+    if (sct->version == SCT_VERSION_V1) {
+        *p++ = sct->version;
+        memcpy(p, sct->log_id, CT_V1_HASHLEN);
+        p += CT_V1_HASHLEN;
+        l2n8(sct->timestamp, p);
+        s2n(sct->ext_len, p);
+        if (sct->ext_len > 0) {
+            memcpy(p, sct->ext, sct->ext_len);
+            p += sct->ext_len;
+        }
+        if (i2o_SCT_signature(sct, &p) <= 0)
+            goto err;
+    } else {
+        memcpy(p, sct->sct, len);
+    }
+
+    return len;
+err:
+    OPENSSL_free(pstart);
+    return -1;
+}
+
+STACK_OF(SCT) *o2i_SCT_LIST(STACK_OF(SCT) **a, const unsigned char **pp,
+                            size_t len)
+{
+    STACK_OF(SCT) *sk = NULL;
+    size_t list_len, sct_len;
+
+    if (len < 2 || len > MAX_SCT_LIST_SIZE) {
+        CTerr(CT_F_O2I_SCT_LIST, CT_R_SCT_LIST_INVALID);
+        return NULL;
+    }
+
+    n2s(*pp, list_len);
+    if (list_len != len - 2) {
+        CTerr(CT_F_O2I_SCT_LIST, CT_R_SCT_LIST_INVALID);
+        return NULL;
+    }
+
+    if (a == NULL || *a == NULL) {
+        sk = sk_SCT_new_null();
+        if (sk == NULL)
+            return NULL;
+    } else {
+        SCT *sct;
+
+        /* Use the given stack, but empty it first. */
+        sk = *a;
+        while ((sct = sk_SCT_pop(sk)) != NULL)
+            SCT_free(sct);
+    }
+
+    while (list_len > 0) {
+        SCT *sct;
+
+        if (list_len < 2) {
+            CTerr(CT_F_O2I_SCT_LIST, CT_R_SCT_LIST_INVALID);
+            goto err;
+        }
+        n2s(*pp, sct_len);
+        list_len -= 2;
+
+        if (sct_len == 0 || sct_len > list_len) {
+            CTerr(CT_F_O2I_SCT_LIST, CT_R_SCT_LIST_INVALID);
+            goto err;
+        }
+        list_len -= sct_len;
+
+        if ((sct = o2i_SCT(NULL, pp, sct_len)) == NULL)
+            goto err;
+        if (!sk_SCT_push(sk, sct)) {
+            SCT_free(sct);
+            goto err;
+        }
+    }
+
+    if (a != NULL && *a == NULL)
+        *a = sk;
+    return sk;
+
+ err:
+    if (a == NULL || *a == NULL)
+        SCT_LIST_free(sk);
+    return NULL;
+}
+
+int i2o_SCT_LIST(const STACK_OF(SCT) *a, unsigned char **pp)
+{
+    int len, sct_len, i, is_pp_new = 0;
+    size_t len2;
+    unsigned char *p = NULL, *p2;
+
+    if (pp != NULL) {
+        if (*pp == NULL) {
+            if ((len = i2o_SCT_LIST(a, NULL)) == -1) {
+                CTerr(CT_F_I2O_SCT_LIST, CT_R_SCT_LIST_INVALID);
+                return -1;
+            }
+            if ((*pp = OPENSSL_malloc(len)) == NULL) {
+                CTerr(CT_F_I2O_SCT_LIST, ERR_R_MALLOC_FAILURE);
+                return -1;
+            }
+            is_pp_new = 1;
+        }
+        p = *pp + 2;
+    }
+
+    len2 = 2;
+    for (i = 0; i < sk_SCT_num(a); i++) {
+        if (pp != NULL) {
+            p2 = p;
+            p += 2;
+            if ((sct_len = i2o_SCT(sk_SCT_value(a, i), &p)) == -1)
+                goto err;
+            s2n(sct_len, p2);
+        } else {
+          if ((sct_len = i2o_SCT(sk_SCT_value(a, i), NULL)) == -1)
+              goto err;
+        }
+        len2 += 2 + sct_len;
+    }
+
+    if (len2 > MAX_SCT_LIST_SIZE)
+        goto err;
+
+    if (pp != NULL) {
+        p = *pp;
+        s2n(len2 - 2, p);
+        if (!is_pp_new)
+            *pp += len2;
+    }
+    return len2;
+
+ err:
+    if (is_pp_new) {
+        OPENSSL_free(*pp);
+        *pp = NULL;
+    }
+    return -1;
+}
+
+STACK_OF(SCT) *d2i_SCT_LIST(STACK_OF(SCT) **a, const unsigned char **pp,
+                            long len)
+{
+    ASN1_OCTET_STRING *oct = NULL;
+    STACK_OF(SCT) *sk = NULL;
+    const unsigned char *p;
+
+    p = *pp;
+    if (d2i_ASN1_OCTET_STRING(&oct, &p, len) == NULL)
+        return NULL;
+
+    p = oct->data;
+    if ((sk = o2i_SCT_LIST(a, &p, oct->length)) != NULL)
+        *pp += len;
+
+    ASN1_OCTET_STRING_free(oct);
+    return sk;
+}
+
+int i2d_SCT_LIST(const STACK_OF(SCT) *a, unsigned char **out)
+{
+    ASN1_OCTET_STRING oct;
+    int len;
+
+    oct.data = NULL;
+    if ((oct.length = i2o_SCT_LIST(a, &oct.data)) == -1)
+        return -1;
+
+    len = i2d_ASN1_OCTET_STRING(&oct, out);
+    OPENSSL_free(oct.data);
+    return len;
+}
diff --git a/lib/libcrypto/ct/ct_policy.c b/lib/libcrypto/ct/ct_policy.c
new file mode 100644 (file)
index 0000000..df66e8a
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifdef OPENSSL_NO_CT
+# error "CT is disabled"
+#endif
+
+#include <openssl/ct.h>
+#include <openssl/err.h>
+#include <time.h>
+
+#include "ct_local.h"
+
+/*
+ * Number of seconds in the future that an SCT timestamp can be, by default,
+ * without being considered invalid. This is added to time() when setting a
+ * default value for CT_POLICY_EVAL_CTX.epoch_time_in_ms.
+ * It can be overridden by calling CT_POLICY_EVAL_CTX_set_time().
+ */
+static const time_t SCT_CLOCK_DRIFT_TOLERANCE = 300;
+
+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;
+    }
+
+    /* time(NULL) shouldn't ever fail, so don't bother checking for -1. */
+    ctx->epoch_time_in_ms = (uint64_t)(time(NULL) + SCT_CLOCK_DRIFT_TOLERANCE) *
+            1000;
+
+    return ctx;
+}
+
+void CT_POLICY_EVAL_CTX_free(CT_POLICY_EVAL_CTX *ctx)
+{
+    if (ctx == NULL)
+        return;
+    X509_free(ctx->cert);
+    X509_free(ctx->issuer);
+    OPENSSL_free(ctx);
+}
+
+int CT_POLICY_EVAL_CTX_set1_cert(CT_POLICY_EVAL_CTX *ctx, X509 *cert)
+{
+    if (!X509_up_ref(cert))
+        return 0;
+    ctx->cert = cert;
+    return 1;
+}
+
+int CT_POLICY_EVAL_CTX_set1_issuer(CT_POLICY_EVAL_CTX *ctx, X509 *issuer)
+{
+    if (!X509_up_ref(issuer))
+        return 0;
+    ctx->issuer = issuer;
+    return 1;
+}
+
+void CT_POLICY_EVAL_CTX_set_shared_CTLOG_STORE(CT_POLICY_EVAL_CTX *ctx,
+                                               CTLOG_STORE *log_store)
+{
+    ctx->log_store = log_store;
+}
+
+void CT_POLICY_EVAL_CTX_set_time(CT_POLICY_EVAL_CTX *ctx, uint64_t time_in_ms)
+{
+    ctx->epoch_time_in_ms = time_in_ms;
+}
+
+X509* CT_POLICY_EVAL_CTX_get0_cert(const CT_POLICY_EVAL_CTX *ctx)
+{
+    return ctx->cert;
+}
+
+X509* CT_POLICY_EVAL_CTX_get0_issuer(const CT_POLICY_EVAL_CTX *ctx)
+{
+    return ctx->issuer;
+}
+
+const CTLOG_STORE *CT_POLICY_EVAL_CTX_get0_log_store(const CT_POLICY_EVAL_CTX *ctx)
+{
+    return ctx->log_store;
+}
+
+uint64_t CT_POLICY_EVAL_CTX_get_time(const CT_POLICY_EVAL_CTX *ctx)
+{
+    return ctx->epoch_time_in_ms;
+}
diff --git a/lib/libcrypto/ct/ct_prn.c b/lib/libcrypto/ct/ct_prn.c
new file mode 100644 (file)
index 0000000..e6584b5
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifdef OPENSSL_NO_CT
+# error "CT is disabled"
+#endif
+
+#include <openssl/asn1.h>
+#include <openssl/bio.h>
+
+#include "ct_local.h"
+
+static void SCT_signature_algorithms_print(const SCT *sct, BIO *out)
+{
+    int nid = SCT_get_signature_nid(sct);
+
+    if (nid == NID_undef)
+        BIO_printf(out, "%02X%02X", sct->hash_alg, sct->sig_alg);
+    else
+        BIO_printf(out, "%s", OBJ_nid2ln(nid));
+}
+
+static void timestamp_print(uint64_t timestamp, BIO *out)
+{
+    ASN1_GENERALIZEDTIME *gen = ASN1_GENERALIZEDTIME_new();
+    char genstr[20];
+
+    if (gen == NULL)
+        return;
+    ASN1_GENERALIZEDTIME_adj(gen, (time_t)0,
+                             (int)(timestamp / 86400000),
+                             (timestamp % 86400000) / 1000);
+    /*
+     * Note GeneralizedTime from ASN1_GENERALIZETIME_adj is always 15
+     * characters long with a final Z. Update it with fractional seconds.
+     */
+    BIO_snprintf(genstr, sizeof(genstr), "%.14s.%03dZ",
+                 ASN1_STRING_get0_data(gen), (unsigned int)(timestamp % 1000));
+    if (ASN1_GENERALIZEDTIME_set_string(gen, genstr))
+        ASN1_GENERALIZEDTIME_print(out, gen);
+    ASN1_GENERALIZEDTIME_free(gen);
+}
+
+const char *SCT_validation_status_string(const SCT *sct)
+{
+
+    switch (SCT_get_validation_status(sct)) {
+    case SCT_VALIDATION_STATUS_NOT_SET:
+        return "not set";
+    case SCT_VALIDATION_STATUS_UNKNOWN_VERSION:
+        return "unknown version";
+    case SCT_VALIDATION_STATUS_UNKNOWN_LOG:
+        return "unknown log";
+    case SCT_VALIDATION_STATUS_UNVERIFIED:
+        return "unverified";
+    case SCT_VALIDATION_STATUS_INVALID:
+        return "invalid";
+    case SCT_VALIDATION_STATUS_VALID:
+        return "valid";
+    }
+    return "unknown status";
+}
+
+void SCT_print(const SCT *sct, BIO *out, int indent,
+               const CTLOG_STORE *log_store)
+{
+    const CTLOG *log = NULL;
+
+    if (log_store != NULL) {
+        log = CTLOG_STORE_get0_log_by_id(log_store, sct->log_id,
+                                         sct->log_id_len);
+    }
+
+    BIO_printf(out, "%*sSigned Certificate Timestamp:", indent, "");
+    BIO_printf(out, "\n%*sVersion   : ", indent + 4, "");
+
+    if (sct->version != SCT_VERSION_V1) {
+        BIO_printf(out, "unknown\n%*s", indent + 16, "");
+        BIO_hex_string(out, indent + 16, 16, sct->sct, sct->sct_len);
+        return;
+    }
+
+    BIO_printf(out, "v1 (0x0)");
+
+    if (log != NULL) {
+        BIO_printf(out, "\n%*sLog       : %s", indent + 4, "",
+                   CTLOG_get0_name(log));
+    }
+
+    BIO_printf(out, "\n%*sLog ID    : ", indent + 4, "");
+    BIO_hex_string(out, indent + 16, 16, sct->log_id, sct->log_id_len);
+
+    BIO_printf(out, "\n%*sTimestamp : ", indent + 4, "");
+    timestamp_print(sct->timestamp, out);
+
+    BIO_printf(out, "\n%*sExtensions: ", indent + 4, "");
+    if (sct->ext_len == 0)
+        BIO_printf(out, "none");
+    else
+        BIO_hex_string(out, indent + 16, 16, sct->ext, sct->ext_len);
+
+    BIO_printf(out, "\n%*sSignature : ", indent + 4, "");
+    SCT_signature_algorithms_print(sct, out);
+    BIO_printf(out, "\n%*s            ", indent + 4, "");
+    BIO_hex_string(out, indent + 16, 16, sct->sig, sct->sig_len);
+}
+
+void SCT_LIST_print(const STACK_OF(SCT) *sct_list, BIO *out, int indent,
+                    const char *separator, const CTLOG_STORE *log_store)
+{
+    int sct_count = sk_SCT_num(sct_list);
+    int i;
+
+    for (i = 0; i < sct_count; ++i) {
+        SCT *sct = sk_SCT_value(sct_list, i);
+
+        SCT_print(sct, out, indent, log_store);
+        if (i < sk_SCT_num(sct_list) - 1)
+            BIO_printf(out, "%s", separator);
+    }
+}
diff --git a/lib/libcrypto/ct/ct_sct.c b/lib/libcrypto/ct/ct_sct.c
new file mode 100644 (file)
index 0000000..4ff36e2
--- /dev/null
@@ -0,0 +1,396 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifdef OPENSSL_NO_CT
+# error "CT disabled"
+#endif
+
+#include <openssl/ct.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/tls1.h>
+#include <openssl/x509.h>
+
+#include "ct_local.h"
+
+SCT *SCT_new(void)
+{
+    SCT *sct = OPENSSL_zalloc(sizeof(*sct));
+
+    if (sct == NULL) {
+        CTerr(CT_F_SCT_NEW, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    sct->entry_type = CT_LOG_ENTRY_TYPE_NOT_SET;
+    sct->version = SCT_VERSION_NOT_SET;
+    return sct;
+}
+
+void SCT_free(SCT *sct)
+{
+    if (sct == NULL)
+        return;
+
+    OPENSSL_free(sct->log_id);
+    OPENSSL_free(sct->ext);
+    OPENSSL_free(sct->sig);
+    OPENSSL_free(sct->sct);
+    OPENSSL_free(sct);
+}
+
+void SCT_LIST_free(STACK_OF(SCT) *a)
+{
+    sk_SCT_pop_free(a, SCT_free);
+}
+
+int SCT_set_version(SCT *sct, sct_version_t version)
+{
+    if (version != SCT_VERSION_V1) {
+        CTerr(CT_F_SCT_SET_VERSION, CT_R_UNSUPPORTED_VERSION);
+        return 0;
+    }
+    sct->version = version;
+    sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+    return 1;
+}
+
+int SCT_set_log_entry_type(SCT *sct, ct_log_entry_type_t entry_type)
+{
+    sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+
+    switch (entry_type) {
+    case CT_LOG_ENTRY_TYPE_X509:
+    case CT_LOG_ENTRY_TYPE_PRECERT:
+        sct->entry_type = entry_type;
+        return 1;
+    case CT_LOG_ENTRY_TYPE_NOT_SET:
+        break;
+    }
+    CTerr(CT_F_SCT_SET_LOG_ENTRY_TYPE, CT_R_UNSUPPORTED_ENTRY_TYPE);
+    return 0;
+}
+
+int SCT_set0_log_id(SCT *sct, unsigned char *log_id, size_t log_id_len)
+{
+    if (sct->version == SCT_VERSION_V1 && log_id_len != CT_V1_HASHLEN) {
+        CTerr(CT_F_SCT_SET0_LOG_ID, CT_R_INVALID_LOG_ID_LENGTH);
+        return 0;
+    }
+
+    OPENSSL_free(sct->log_id);
+    sct->log_id = log_id;
+    sct->log_id_len = log_id_len;
+    sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+    return 1;
+}
+
+int SCT_set1_log_id(SCT *sct, const unsigned char *log_id, size_t log_id_len)
+{
+    if (sct->version == SCT_VERSION_V1 && log_id_len != CT_V1_HASHLEN) {
+        CTerr(CT_F_SCT_SET1_LOG_ID, CT_R_INVALID_LOG_ID_LENGTH);
+        return 0;
+    }
+
+    OPENSSL_free(sct->log_id);
+    sct->log_id = NULL;
+    sct->log_id_len = 0;
+    sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+
+    if (log_id != NULL && log_id_len > 0) {
+        sct->log_id = OPENSSL_memdup(log_id, log_id_len);
+        if (sct->log_id == NULL) {
+            CTerr(CT_F_SCT_SET1_LOG_ID, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        sct->log_id_len = log_id_len;
+    }
+    return 1;
+}
+
+
+void SCT_set_timestamp(SCT *sct, uint64_t timestamp)
+{
+    sct->timestamp = timestamp;
+    sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+}
+
+int SCT_set_signature_nid(SCT *sct, int nid)
+{
+    switch (nid) {
+    case NID_sha256WithRSAEncryption:
+        sct->hash_alg = TLSEXT_hash_sha256;
+        sct->sig_alg = TLSEXT_signature_rsa;
+        sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+        return 1;
+    case NID_ecdsa_with_SHA256:
+        sct->hash_alg = TLSEXT_hash_sha256;
+        sct->sig_alg = TLSEXT_signature_ecdsa;
+        sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+        return 1;
+    default:
+        CTerr(CT_F_SCT_SET_SIGNATURE_NID, CT_R_UNRECOGNIZED_SIGNATURE_NID);
+        return 0;
+    }
+}
+
+void SCT_set0_extensions(SCT *sct, unsigned char *ext, size_t ext_len)
+{
+    OPENSSL_free(sct->ext);
+    sct->ext = ext;
+    sct->ext_len = ext_len;
+    sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+}
+
+int SCT_set1_extensions(SCT *sct, const unsigned char *ext, size_t ext_len)
+{
+    OPENSSL_free(sct->ext);
+    sct->ext = NULL;
+    sct->ext_len = 0;
+    sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+
+    if (ext != NULL && ext_len > 0) {
+        sct->ext = OPENSSL_memdup(ext, ext_len);
+        if (sct->ext == NULL) {
+            CTerr(CT_F_SCT_SET1_EXTENSIONS, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        sct->ext_len = ext_len;
+    }
+    return 1;
+}
+
+void SCT_set0_signature(SCT *sct, unsigned char *sig, size_t sig_len)
+{
+    OPENSSL_free(sct->sig);
+    sct->sig = sig;
+    sct->sig_len = sig_len;
+    sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+}
+
+int SCT_set1_signature(SCT *sct, const unsigned char *sig, size_t sig_len)
+{
+    OPENSSL_free(sct->sig);
+    sct->sig = NULL;
+    sct->sig_len = 0;
+    sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+
+    if (sig != NULL && sig_len > 0) {
+        sct->sig = OPENSSL_memdup(sig, sig_len);
+        if (sct->sig == NULL) {
+            CTerr(CT_F_SCT_SET1_SIGNATURE, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        sct->sig_len = sig_len;
+    }
+    return 1;
+}
+
+sct_version_t SCT_get_version(const SCT *sct)
+{
+    return sct->version;
+}
+
+ct_log_entry_type_t SCT_get_log_entry_type(const SCT *sct)
+{
+    return sct->entry_type;
+}
+
+size_t SCT_get0_log_id(const SCT *sct, unsigned char **log_id)
+{
+    *log_id = sct->log_id;
+    return sct->log_id_len;
+}
+
+uint64_t SCT_get_timestamp(const SCT *sct)
+{
+    return sct->timestamp;
+}
+
+int SCT_get_signature_nid(const SCT *sct)
+{
+    if (sct->version == SCT_VERSION_V1) {
+        if (sct->hash_alg == TLSEXT_hash_sha256) {
+            switch (sct->sig_alg) {
+            case TLSEXT_signature_ecdsa:
+                return NID_ecdsa_with_SHA256;
+            case TLSEXT_signature_rsa:
+                return NID_sha256WithRSAEncryption;
+            default:
+                return NID_undef;
+            }
+        }
+    }
+    return NID_undef;
+}
+
+size_t SCT_get0_extensions(const SCT *sct, unsigned char **ext)
+{
+    *ext = sct->ext;
+    return sct->ext_len;
+}
+
+size_t SCT_get0_signature(const SCT *sct, unsigned char **sig)
+{
+    *sig = sct->sig;
+    return sct->sig_len;
+}
+
+int SCT_is_complete(const SCT *sct)
+{
+    switch (sct->version) {
+    case SCT_VERSION_NOT_SET:
+        return 0;
+    case SCT_VERSION_V1:
+        return sct->log_id != NULL && SCT_signature_is_complete(sct);
+    default:
+        return sct->sct != NULL; /* Just need cached encoding */
+    }
+}
+
+int SCT_signature_is_complete(const SCT *sct)
+{
+    return SCT_get_signature_nid(sct) != NID_undef &&
+        sct->sig != NULL && sct->sig_len > 0;
+}
+
+sct_source_t SCT_get_source(const SCT *sct)
+{
+    return sct->source;
+}
+
+int SCT_set_source(SCT *sct, sct_source_t source)
+{
+    sct->source = source;
+    sct->validation_status = SCT_VALIDATION_STATUS_NOT_SET;
+    switch (source) {
+    case SCT_SOURCE_TLS_EXTENSION:
+    case SCT_SOURCE_OCSP_STAPLED_RESPONSE:
+        return SCT_set_log_entry_type(sct, CT_LOG_ENTRY_TYPE_X509);
+    case SCT_SOURCE_X509V3_EXTENSION:
+        return SCT_set_log_entry_type(sct, CT_LOG_ENTRY_TYPE_PRECERT);
+    case SCT_SOURCE_UNKNOWN:
+        break;
+    }
+    /* if we aren't sure, leave the log entry type alone */
+    return 1;
+}
+
+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;
+    const CTLOG *log;
+
+    /*
+     * With an unrecognized SCT version we don't know what such an SCT means,
+     * let alone validate one.  So we return validation failure (0).
+     */
+    if (sct->version != SCT_VERSION_V1) {
+        sct->validation_status = SCT_VALIDATION_STATUS_UNKNOWN_VERSION;
+        return 0;
+    }
+
+    log = CTLOG_STORE_get0_log_by_id(ctx->log_store,
+                                     sct->log_id, sct->log_id_len);
+
+    /* Similarly, an SCT from an unknown log also cannot be validated. */
+    if (log == NULL) {
+        sct->validation_status = SCT_VALIDATION_STATUS_UNKNOWN_LOG;
+        return 0;
+    }
+
+    sctx = SCT_CTX_new();
+    if (sctx == NULL)
+        goto err;
+
+    if (X509_PUBKEY_set(&log_pkey, CTLOG_get0_public_key(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_get0_pubkey(ctx->issuer);
+
+        if (X509_PUBKEY_set(&pub, issuer_pkey) != 1)
+            goto err;
+        if (SCT_CTX_set1_issuer_pubkey(sctx, pub) != 1)
+            goto err;
+    }
+
+    SCT_CTX_set_time(sctx, ctx->epoch_time_in_ms);
+
+    /*
+     * XXX: Potential for optimization.  This repeats some idempotent heavy
+     * lifting on the certificate for each candidate SCT, and appears to not
+     * use any information in the SCT itself, only the certificate is
+     * processed.  So it may make more sense to to do this just once, perhaps
+     * associated with the shared (by all SCTs) policy eval ctx.
+     *
+     * XXX: Failure here is global (SCT independent) and represents either an
+     * issue with the certificate (e.g. duplicate extensions) or an out of
+     * memory condition.  When the certificate is incompatible with CT, we just
+     * mark the SCTs invalid, rather than report a failure to determine the
+     * validation status.  That way, callbacks that want to do "soft" SCT
+     * processing will not abort handshakes with false positive internal
+     * errors.  Since the function does not distinguish between certificate
+     * issues (peer's fault) and internal problems (out fault) the safe thing
+     * to do is to report a validation failure and let the callback or
+     * application decide what to do.
+     */
+    if (SCT_CTX_set1_cert(sctx, ctx->cert, NULL) != 1)
+        sct->validation_status = SCT_VALIDATION_STATUS_UNVERIFIED;
+    else
+        sct->validation_status = SCT_CTX_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/lib/libcrypto/ct/ct_sct_ctx.c b/lib/libcrypto/ct/ct_sct_ctx.c
new file mode 100644 (file)
index 0000000..841e768
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifdef OPENSSL_NO_CT
+# error "CT is disabled"
+#endif
+
+#include <stddef.h>
+#include <string.h>
+
+#include <openssl/err.h>
+#include <openssl/obj_mac.h>
+#include <openssl/x509.h>
+
+#include "ct_local.h"
+
+SCT_CTX *SCT_CTX_new(void)
+{
+    SCT_CTX *sctx = OPENSSL_zalloc(sizeof(*sctx));
+
+    if (sctx == NULL)
+        CTerr(CT_F_SCT_CTX_NEW, ERR_R_MALLOC_FAILURE);
+
+    return sctx;
+}
+
+void SCT_CTX_free(SCT_CTX *sctx)
+{
+    if (sctx == NULL)
+        return;
+    EVP_PKEY_free(sctx->pkey);
+    OPENSSL_free(sctx->pkeyhash);
+    OPENSSL_free(sctx->ihash);
+    OPENSSL_free(sctx->certder);
+    OPENSSL_free(sctx->preder);
+    OPENSSL_free(sctx);
+}
+
+/*
+ * Finds the index of the first extension with the given NID in cert.
+ * If there is more than one extension with that NID, *is_duplicated is set to
+ * 1, otherwise 0 (unless it is NULL).
+ */
+static int ct_x509_get_ext(X509 *cert, int nid, int *is_duplicated)
+{
+    int ret = X509_get_ext_by_NID(cert, nid, -1);
+
+    if (is_duplicated != NULL)
+        *is_duplicated = ret >= 0 && X509_get_ext_by_NID(cert, nid, ret) >= 0;
+
+    return ret;
+}
+
+/*
+ * Modifies a certificate by deleting extensions and copying the issuer and
+ * AKID from the presigner certificate, if necessary.
+ * Returns 1 on success, 0 otherwise.
+ */
+__owur static int ct_x509_cert_fixup(X509 *cert, X509 *presigner)
+{
+    int preidx, certidx;
+    int pre_akid_ext_is_dup, cert_akid_ext_is_dup;
+
+    if (presigner == NULL)
+        return 1;
+
+    preidx = ct_x509_get_ext(presigner, NID_authority_key_identifier,
+                             &pre_akid_ext_is_dup);
+    certidx = ct_x509_get_ext(cert, NID_authority_key_identifier,
+                              &cert_akid_ext_is_dup);
+
+    /* An error occurred whilst searching for the extension */
+    if (preidx < -1 || certidx < -1)
+        return 0;
+    /* Invalid certificate if they contain duplicate extensions */
+    if (pre_akid_ext_is_dup || cert_akid_ext_is_dup)
+        return 0;
+    /* AKID must be present in both certificate or absent in both */
+    if (preidx >= 0 && certidx == -1)
+        return 0;
+    if (preidx == -1 && certidx >= 0)
+        return 0;
+    /* Copy issuer name */
+    if (!X509_set_issuer_name(cert, X509_get_issuer_name(presigner)))
+        return 0;
+    if (preidx != -1) {
+        /* Retrieve and copy AKID encoding */
+        X509_EXTENSION *preext = X509_get_ext(presigner, preidx);
+        X509_EXTENSION *certext = X509_get_ext(cert, certidx);
+        ASN1_OCTET_STRING *preextdata;
+
+        /* Should never happen */
+        if (preext == NULL || certext == NULL)
+            return 0;
+        preextdata = X509_EXTENSION_get_data(preext);
+        if (preextdata == NULL ||
+            !X509_EXTENSION_set_data(certext, preextdata))
+            return 0;
+    }
+    return 1;
+}
+
+int SCT_CTX_set1_cert(SCT_CTX *sctx, X509 *cert, X509 *presigner)
+{
+    unsigned char *certder = NULL, *preder = NULL;
+    X509 *pretmp = NULL;
+    int certderlen = 0, prederlen = 0;
+    int idx = -1;
+    int poison_ext_is_dup, sct_ext_is_dup;
+    int poison_idx = ct_x509_get_ext(cert, NID_ct_precert_poison, &poison_ext_is_dup);
+
+    /* Duplicate poison extensions are present - error */
+    if (poison_ext_is_dup)
+        goto err;
+
+    /* If *cert doesn't have a poison extension, it isn't a precert */
+    if (poison_idx == -1) {
+        /* cert isn't a precert, so we shouldn't have a presigner */
+        if (presigner != NULL)
+            goto err;
+
+        certderlen = i2d_X509(cert, &certder);
+        if (certderlen < 0)
+            goto err;
+    }
+
+    /* See if cert has a precert SCTs extension */
+    idx = ct_x509_get_ext(cert, NID_ct_precert_scts, &sct_ext_is_dup);
+    /* Duplicate SCT extensions are present - error */
+    if (sct_ext_is_dup)
+        goto err;
+
+    if (idx >= 0 && poison_idx >= 0) {
+        /*
+         * cert can't both contain SCTs (i.e. have an SCT extension) and be a
+         * precert (i.e. have a poison extension).
+         */
+        goto err;
+    }
+
+    if (idx == -1) {
+        idx = poison_idx;
+    }
+
+    /*
+     * If either a poison or SCT extension is present, remove it before encoding
+     * cert. This, along with ct_x509_cert_fixup(), gets a TBSCertificate (see
+     * RFC5280) from cert, which is what the CT log signed when it produced the
+     * SCT.
+     */
+    if (idx >= 0) {
+        X509_EXTENSION *ext;
+
+        /* Take a copy of certificate so we don't modify passed version */
+        pretmp = X509_dup(cert);
+        if (pretmp == NULL)
+            goto err;
+
+        ext = X509_delete_ext(pretmp, idx);
+        X509_EXTENSION_free(ext);
+
+        if (!ct_x509_cert_fixup(pretmp, presigner))
+            goto err;
+
+        prederlen = i2d_re_X509_tbs(pretmp, &preder);
+        if (prederlen <= 0)
+            goto err;
+    }
+
+    X509_free(pretmp);
+
+    OPENSSL_free(sctx->certder);
+    sctx->certder = certder;
+    sctx->certderlen = certderlen;
+
+    OPENSSL_free(sctx->preder);
+    sctx->preder = preder;
+    sctx->prederlen = prederlen;
+
+    return 1;
+err:
+    OPENSSL_free(certder);
+    OPENSSL_free(preder);
+    X509_free(pretmp);
+    return 0;
+}
+
+__owur static int ct_public_key_hash(X509_PUBKEY *pkey, unsigned char **hash,
+                                     size_t *hash_len)
+{
+    int ret = 0;
+    unsigned char *md = NULL, *der = NULL;
+    int der_len;
+    unsigned int md_len;
+
+    /* Reuse buffer if possible */
+    if (*hash != NULL && *hash_len >= SHA256_DIGEST_LENGTH) {
+        md = *hash;
+    } else {
+        md = OPENSSL_malloc(SHA256_DIGEST_LENGTH);
+        if (md == NULL)
+            goto err;
+    }
+
+    /* Calculate key hash */
+    der_len = i2d_X509_PUBKEY(pkey, &der);
+    if (der_len <= 0)
+        goto err;
+
+    if (!EVP_Digest(der, der_len, md, &md_len, EVP_sha256(), NULL))
+        goto err;
+
+    if (md != *hash) {
+        OPENSSL_free(*hash);
+        *hash = md;
+        *hash_len = SHA256_DIGEST_LENGTH;
+    }
+
+    md = NULL;
+    ret = 1;
+ err:
+    OPENSSL_free(md);
+    OPENSSL_free(der);
+    return ret;
+}
+
+int SCT_CTX_set1_issuer(SCT_CTX *sctx, const X509 *issuer)
+{
+    return SCT_CTX_set1_issuer_pubkey(sctx, X509_get_X509_PUBKEY(issuer));
+}
+
+int SCT_CTX_set1_issuer_pubkey(SCT_CTX *sctx, X509_PUBKEY *pubkey)
+{
+    return ct_public_key_hash(pubkey, &sctx->ihash, &sctx->ihashlen);
+}
+
+int SCT_CTX_set1_pubkey(SCT_CTX *sctx, X509_PUBKEY *pubkey)
+{
+    EVP_PKEY *pkey = X509_PUBKEY_get(pubkey);
+
+    if (pkey == NULL)
+        return 0;
+
+    if (!ct_public_key_hash(pubkey, &sctx->pkeyhash, &sctx->pkeyhashlen)) {
+        EVP_PKEY_free(pkey);
+        return 0;
+    }
+
+    EVP_PKEY_free(sctx->pkey);
+    sctx->pkey = pkey;
+    return 1;
+}
+
+void SCT_CTX_set_time(SCT_CTX *sctx, uint64_t time_in_ms)
+{
+    sctx->epoch_time_in_ms = time_in_ms;
+}
diff --git a/lib/libcrypto/ct/ct_vfy.c b/lib/libcrypto/ct/ct_vfy.c
new file mode 100644 (file)
index 0000000..74fd34f
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h>
+
+#include <openssl/ct.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+#include "ct_local.h"
+
+typedef enum sct_signature_type_t {
+    SIGNATURE_TYPE_NOT_SET = -1,
+    SIGNATURE_TYPE_CERT_TIMESTAMP,
+    SIGNATURE_TYPE_TREE_HASH
+} SCT_SIGNATURE_TYPE;
+
+/*
+ * Update encoding for SCT signature verification/generation to supplied
+ * EVP_MD_CTX.
+ */
+static int sct_ctx_update(EVP_MD_CTX *ctx, const SCT_CTX *sctx, const SCT *sct)
+{
+    unsigned char tmpbuf[12];
+    unsigned char *p, *der;
+    size_t derlen;
+    /*+
+     * digitally-signed struct {
+     *   (1 byte) Version sct_version;
+     *   (1 byte) SignatureType signature_type = certificate_timestamp;
+     *   (8 bytes) uint64 timestamp;
+     *   (2 bytes) LogEntryType entry_type;
+     *   (? bytes) select(entry_type) {
+     *     case x509_entry: ASN.1Cert;
+     *     case precert_entry: PreCert;
+     *   } signed_entry;
+     *   (2 bytes + sct->ext_len) CtExtensions extensions;
+     * }
+     */
+    if (sct->entry_type == CT_LOG_ENTRY_TYPE_NOT_SET)
+        return 0;
+    if (sct->entry_type == CT_LOG_ENTRY_TYPE_PRECERT && sctx->ihash == NULL)
+        return 0;
+
+    p = tmpbuf;
+    *p++ = sct->version;
+    *p++ = SIGNATURE_TYPE_CERT_TIMESTAMP;
+    l2n8(sct->timestamp, p);
+    s2n(sct->entry_type, p);
+
+    if (!EVP_DigestUpdate(ctx, tmpbuf, p - tmpbuf))
+        return 0;
+
+    if (sct->entry_type == CT_LOG_ENTRY_TYPE_X509) {
+        der = sctx->certder;
+        derlen = sctx->certderlen;
+    } else {
+        if (!EVP_DigestUpdate(ctx, sctx->ihash, sctx->ihashlen))
+            return 0;
+        der = sctx->preder;
+        derlen = sctx->prederlen;
+    }
+
+    /* If no encoding available, fatal error */
+    if (der == NULL)
+        return 0;
+
+    /* Include length first */
+    p = tmpbuf;
+    l2n3(derlen, p);
+
+    if (!EVP_DigestUpdate(ctx, tmpbuf, 3))
+        return 0;
+    if (!EVP_DigestUpdate(ctx, der, derlen))
+        return 0;
+
+    /* Add any extensions */
+    p = tmpbuf;
+    s2n(sct->ext_len, p);
+    if (!EVP_DigestUpdate(ctx, tmpbuf, 2))
+        return 0;
+
+    if (sct->ext_len && !EVP_DigestUpdate(ctx, sct->ext, sct->ext_len))
+        return 0;
+
+    return 1;
+}
+
+int SCT_CTX_verify(const SCT_CTX *sctx, const SCT *sct)
+{
+    EVP_MD_CTX *ctx = NULL;
+    int ret = 0;
+
+    if (!SCT_is_complete(sct) || sctx->pkey == NULL ||
+        sct->entry_type == CT_LOG_ENTRY_TYPE_NOT_SET ||
+        (sct->entry_type == CT_LOG_ENTRY_TYPE_PRECERT && sctx->ihash == NULL)) {
+        CTerr(CT_F_SCT_CTX_VERIFY, CT_R_SCT_NOT_SET);
+        return 0;
+    }
+    if (sct->version != SCT_VERSION_V1) {
+        CTerr(CT_F_SCT_CTX_VERIFY, CT_R_SCT_UNSUPPORTED_VERSION);
+        return 0;
+    }
+    if (sct->log_id_len != sctx->pkeyhashlen ||
+        memcmp(sct->log_id, sctx->pkeyhash, sctx->pkeyhashlen) != 0) {
+        CTerr(CT_F_SCT_CTX_VERIFY, CT_R_SCT_LOG_ID_MISMATCH);
+        return 0;
+    }
+    if (sct->timestamp > sctx->epoch_time_in_ms) {
+        CTerr(CT_F_SCT_CTX_VERIFY, CT_R_SCT_FUTURE_TIMESTAMP);
+        return 0;
+    }
+
+    ctx = EVP_MD_CTX_new();
+    if (ctx == NULL)
+        goto end;
+
+    if (!EVP_DigestVerifyInit(ctx, NULL, EVP_sha256(), NULL, sctx->pkey))
+        goto end;
+
+    if (!sct_ctx_update(ctx, sctx, sct))
+        goto end;
+
+    /* Verify signature */
+    ret = EVP_DigestVerifyFinal(ctx, sct->sig, sct->sig_len);
+    /* If ret < 0 some other error: fall through without setting error */
+    if (ret == 0)
+        CTerr(CT_F_SCT_CTX_VERIFY, CT_R_SCT_INVALID_SIGNATURE);
+
+end:
+    EVP_MD_CTX_free(ctx);
+    return ret;
+}
diff --git a/lib/libcrypto/ct/ct_x509v3.c b/lib/libcrypto/ct/ct_x509v3.c
new file mode 100644 (file)
index 0000000..19c2a85
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifdef OPENSSL_NO_CT
+# error "CT is disabled"
+#endif
+
+#include "ct_local.h"
+
+static char *i2s_poison(const X509V3_EXT_METHOD *method, void *val)
+{
+    return OPENSSL_strdup("NULL");
+}
+
+static void *s2i_poison(const X509V3_EXT_METHOD *method, X509V3_CTX *ctx, const char *str)
+{
+   return ASN1_NULL_new();
+}
+
+static int i2r_SCT_LIST(X509V3_EXT_METHOD *method, STACK_OF(SCT) *sct_list,
+                 BIO *out, int indent)
+{
+    SCT_LIST_print(sct_list, out, indent, "\n", NULL);
+    return 1;
+}
+
+static int set_sct_list_source(STACK_OF(SCT) *s, sct_source_t source)
+{
+    if (s != NULL) {
+        int i;
+
+        for (i = 0; i < sk_SCT_num(s); i++) {
+            int res = SCT_set_source(sk_SCT_value(s, i), source);
+
+            if (res != 1) {
+                return 0;
+            }
+        }
+    }
+    return 1;
+}
+
+static STACK_OF(SCT) *x509_ext_d2i_SCT_LIST(STACK_OF(SCT) **a,
+                                            const unsigned char **pp,
+                                            long len)
+{
+     STACK_OF(SCT) *s = d2i_SCT_LIST(a, pp, len);
+
+     if (set_sct_list_source(s, SCT_SOURCE_X509V3_EXTENSION) != 1) {
+         SCT_LIST_free(s);
+         *a = NULL;
+         return NULL;
+     }
+     return s;
+}
+
+static STACK_OF(SCT) *ocsp_ext_d2i_SCT_LIST(STACK_OF(SCT) **a,
+                                            const unsigned char **pp,
+                                            long len)
+{
+    STACK_OF(SCT) *s = d2i_SCT_LIST(a, pp, len);
+
+    if (set_sct_list_source(s, SCT_SOURCE_OCSP_STAPLED_RESPONSE) != 1) {
+        SCT_LIST_free(s);
+        *a = NULL;
+        return NULL;
+    }
+    return s;
+}
+
+/* Handlers for X509v3/OCSP Certificate Transparency extensions */
+const X509V3_EXT_METHOD v3_ct_scts[3] = {
+    /* X509v3 extension in certificates that contains SCTs */
+    { NID_ct_precert_scts, 0, NULL,
+    NULL, (X509V3_EXT_FREE)SCT_LIST_free,
+    (X509V3_EXT_D2I)x509_ext_d2i_SCT_LIST, (X509V3_EXT_I2D)i2d_SCT_LIST,
+    NULL, NULL,
+    NULL, NULL,
+    (X509V3_EXT_I2R)i2r_SCT_LIST, NULL,
+    NULL },
+
+    /* X509v3 extension to mark a certificate as a pre-certificate */
+    { NID_ct_precert_poison, 0, ASN1_ITEM_ref(ASN1_NULL),
+    NULL, NULL, NULL, NULL,
+    i2s_poison, s2i_poison,
+    NULL, NULL,
+    NULL, NULL,
+    NULL },
+
+    /* OCSP extension that contains SCTs */
+    { NID_ct_cert_scts, 0, NULL,
+    0, (X509V3_EXT_FREE)SCT_LIST_free,
+    (X509V3_EXT_D2I)ocsp_ext_d2i_SCT_LIST, (X509V3_EXT_I2D)i2d_SCT_LIST,
+    NULL, NULL,
+    NULL, NULL,
+    (X509V3_EXT_I2R)i2r_SCT_LIST, NULL,
+    NULL },
+};
diff --git a/lib/libcrypto/ct/cterr.h b/lib/libcrypto/ct/cterr.h
new file mode 100644 (file)
index 0000000..e69de29