[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-commits] [tor/master] Split X509 code out of tortls.c



commit 9a4f05b05c12687e640d2aed9bb21229138bd1a5
Author: Nick Mathewson <nickm@xxxxxxxxxxxxxx>
Date:   Sat Aug 11 18:16:04 2018 -0400

    Split X509 code out of tortls.c
---
 src/core/or/channeltls.c       |   1 +
 src/core/or/connection_or.c    |   1 +
 src/feature/nodelist/torcert.c |   1 +
 src/feature/relay/routerkeys.c |   1 +
 src/lib/tls/include.am         |   6 +-
 src/lib/tls/tortls.c           | 506 +-------------------------------------
 src/lib/tls/tortls.h           |  76 ++----
 src/lib/tls/x509.c             | 539 +++++++++++++++++++++++++++++++++++++++++
 src/lib/tls/x509.h             |  78 ++++++
 src/test/test_link_handshake.c |   1 +
 src/test/test_tortls.c         |   1 +
 11 files changed, 649 insertions(+), 562 deletions(-)

diff --git a/src/core/or/channeltls.c b/src/core/or/channeltls.c
index 87f5a02b7..153813c4d 100644
--- a/src/core/or/channeltls.c
+++ b/src/core/or/channeltls.c
@@ -70,6 +70,7 @@
 #include "core/or/var_cell_st.h"
 
 #include "lib/tls/tortls.h"
+#include "lib/tls/x509.h"
 
 /** How many CELL_PADDING cells have we received, ever? */
 uint64_t stats_n_padding_cells_processed = 0;
diff --git a/src/core/or/connection_or.c b/src/core/or/connection_or.c
index c5ff10f6a..08371d1ad 100644
--- a/src/core/or/connection_or.c
+++ b/src/core/or/connection_or.c
@@ -73,6 +73,7 @@
 #include "lib/crypt_ops/crypto_format.h"
 
 #include "lib/tls/tortls.h"
+#include "lib/tls/x509.h"
 
 static int connection_tls_finish_handshake(or_connection_t *conn);
 static int connection_or_launch_v3_or_handshake(or_connection_t *conn);
diff --git a/src/feature/nodelist/torcert.c b/src/feature/nodelist/torcert.c
index a27608202..fe67e5640 100644
--- a/src/feature/nodelist/torcert.c
+++ b/src/feature/nodelist/torcert.c
@@ -33,6 +33,7 @@
 #include "lib/log/log.h"
 #include "trunnel/link_handshake.h"
 #include "lib/tls/tortls.h"
+#include "lib/tls/x509.h"
 
 #include "core/or/or_handshake_certs_st.h"
 
diff --git a/src/feature/relay/routerkeys.c b/src/feature/relay/routerkeys.c
index f12eb3d33..d018f300f 100644
--- a/src/feature/relay/routerkeys.c
+++ b/src/feature/relay/routerkeys.c
@@ -24,6 +24,7 @@
 #include "lib/crypt_ops/crypto_util.h"
 #include "lib/term/getpass.h"
 #include "lib/tls/tortls.h"
+#include "lib/tls/x509.h"
 #include "lib/crypt_ops/crypto_format.h"
 
 #define ENC_KEY_HEADER "Boxed Ed25519 key"
diff --git a/src/lib/tls/include.am b/src/lib/tls/include.am
index 9cc57ca77..1fd25a0b3 100644
--- a/src/lib/tls/include.am
+++ b/src/lib/tls/include.am
@@ -7,7 +7,8 @@ endif
 
 src_lib_libtor_tls_a_SOURCES =			\
 	src/lib/tls/buffers_tls.c		\
-	src/lib/tls/tortls.c
+	src/lib/tls/tortls.c			\
+	src/lib/tls/x509.c
 
 src_lib_libtor_tls_a_CFLAGS = $(AM_CFLAGS) $(TOR_CFLAGS_CRYPTLIB)
 
@@ -20,4 +21,5 @@ src_lib_libtor_tls_testing_a_CFLAGS = \
 noinst_HEADERS +=				\
 	src/lib/tls/ciphers.inc			\
 	src/lib/tls/buffers_tls.h		\
-	src/lib/tls/tortls.h
+	src/lib/tls/tortls.h			\
+	src/lib/tls/x509.h
diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c
index 466df8966..20e432081 100644
--- a/src/lib/tls/tortls.c
+++ b/src/lib/tls/tortls.c
@@ -28,6 +28,7 @@
 #include "lib/crypt_ops/crypto_rand.h"
 #include "lib/crypt_ops/crypto_dh.h"
 #include "lib/crypt_ops/crypto_util.h"
+#include "lib/tls/x509.h"
 
 /* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in
  * srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */
@@ -67,26 +68,6 @@ ENABLE_GCC_WARNING(redundant-decls)
 
 #include "lib/arch/bytes.h"
 
-#ifdef OPENSSL_1_1_API
-#define X509_get_notBefore_const(cert) \
-    X509_get0_notBefore(cert)
-#define X509_get_notAfter_const(cert) \
-    X509_get0_notAfter(cert)
-#ifndef X509_get_notBefore
-#define X509_get_notBefore(cert) \
-    X509_getm_notBefore(cert)
-#endif
-#ifndef X509_get_notAfter
-#define X509_get_notAfter(cert) \
-    X509_getm_notAfter(cert)
-#endif
-#else /* ! OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) */
-#define X509_get_notBefore_const(cert) \
-  ((const ASN1_TIME*) X509_get_notBefore((X509 *)cert))
-#define X509_get_notAfter_const(cert) \
-  ((const ASN1_TIME*) X509_get_notAfter((X509 *)cert))
-#endif
-
 /* Copied from or.h */
 #define LEGAL_NICKNAME_CHARACTERS \
   "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
@@ -162,10 +143,6 @@ tor_tls_get_by_ssl(const SSL *ssl)
 static void tor_tls_context_decref(tor_tls_context_t *ctx);
 static void tor_tls_context_incref(tor_tls_context_t *ctx);
 
-static int check_cert_lifetime_internal(int severity, const X509 *cert,
-                                   time_t now,
-                                   int past_tolerance, int future_tolerance);
-
 /** Global TLS contexts. We keep them here because nobody else needs
  * to touch them.
  *
@@ -267,7 +244,7 @@ tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
 /** Log all pending tls errors at level <b>severity</b> in log domain
  * <b>domain</b>.  Use <b>doing</b> to describe our current activities.
  */
-STATIC void
+void
 tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing)
 {
   unsigned long err;
@@ -375,7 +352,7 @@ tor_tls_get_error(tor_tls_t *tls, int r, int extra,
 
 /** Initialize OpenSSL, unless it has already been initialized.
  */
-static void
+void
 tor_tls_init(void)
 {
   check_no_tls_errors();
@@ -459,145 +436,6 @@ always_accept_verify_cb(int preverify_ok,
   return 1;
 }
 
-/** Return a newly allocated X509 name with commonName <b>cname</b>. */
-static X509_NAME *
-tor_x509_name_new(const char *cname)
-{
-  int nid;
-  X509_NAME *name;
-  /* LCOV_EXCL_BR_START : these branches will only fail on OOM errors */
-  if (!(name = X509_NAME_new()))
-    return NULL;
-  if ((nid = OBJ_txt2nid("commonName")) == NID_undef) goto error;
-  if (!(X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC,
-                                   (unsigned char*)cname, -1, -1, 0)))
-    goto error;
-  /* LCOV_EXCL_BR_STOP */
-  return name;
-
-  /* LCOV_EXCL_START : these lines will only execute on out of memory errors*/
- error:
-  X509_NAME_free(name);
-  return NULL;
-  /* LCOV_EXCL_STOP */
-}
-
-/** Generate and sign an X509 certificate with the public key <b>rsa</b>,
- * signed by the private key <b>rsa_sign</b>.  The commonName of the
- * certificate will be <b>cname</b>; the commonName of the issuer will be
- * <b>cname_sign</b>. The cert will be valid for <b>cert_lifetime</b>
- * seconds, starting from some time in the past.
- *
- * Return a certificate on success, NULL on failure.
- */
-MOCK_IMPL(STATIC X509 *,
-tor_tls_create_certificate,(crypto_pk_t *rsa,
-                            crypto_pk_t *rsa_sign,
-                            const char *cname,
-                            const char *cname_sign,
-                            unsigned int cert_lifetime))
-{
-  /* OpenSSL generates self-signed certificates with random 64-bit serial
-   * numbers, so let's do that too. */
-#define SERIAL_NUMBER_SIZE 8
-
-  time_t start_time, end_time;
-  BIGNUM *serial_number = NULL;
-  unsigned char serial_tmp[SERIAL_NUMBER_SIZE];
-  EVP_PKEY *sign_pkey = NULL, *pkey=NULL;
-  X509 *x509 = NULL;
-  X509_NAME *name = NULL, *name_issuer=NULL;
-
-  tor_tls_init();
-
-  /* Make sure we're part-way through the certificate lifetime, rather
-   * than having it start right now. Don't choose quite uniformly, since
-   * then we might pick a time where we're about to expire. Lastly, be
-   * sure to start on a day boundary. */
-  time_t now = time(NULL);
-  /* Our certificate lifetime will be cert_lifetime no matter what, but if we
-   * start cert_lifetime in the past, we'll have 0 real lifetime.  instead we
-   * start up to (cert_lifetime - min_real_lifetime - start_granularity) in
-   * the past. */
-  const time_t min_real_lifetime = 24*3600;
-  const time_t start_granularity = 24*3600;
-  time_t earliest_start_time;
-  /* Don't actually start in the future! */
-  if (cert_lifetime <= min_real_lifetime + start_granularity) {
-    earliest_start_time = now - 1;
-  } else {
-    earliest_start_time = now + min_real_lifetime + start_granularity
-      - cert_lifetime;
-  }
-  start_time = crypto_rand_time_range(earliest_start_time, now);
-  /* Round the start time back to the start of a day. */
-  start_time -= start_time % start_granularity;
-
-  end_time = start_time + cert_lifetime;
-
-  tor_assert(rsa);
-  tor_assert(cname);
-  tor_assert(rsa_sign);
-  tor_assert(cname_sign);
-  if (!(sign_pkey = crypto_pk_get_openssl_evp_pkey_(rsa_sign,1)))
-    goto error;
-  if (!(pkey = crypto_pk_get_openssl_evp_pkey_(rsa,0)))
-    goto error;
-  if (!(x509 = X509_new()))
-    goto error;
-  if (!(X509_set_version(x509, 2)))
-    goto error;
-
-  { /* our serial number is 8 random bytes. */
-    crypto_rand((char *)serial_tmp, sizeof(serial_tmp));
-    if (!(serial_number = BN_bin2bn(serial_tmp, sizeof(serial_tmp), NULL)))
-      goto error;
-    if (!(BN_to_ASN1_INTEGER(serial_number, X509_get_serialNumber(x509))))
-      goto error;
-  }
-
-  if (!(name = tor_x509_name_new(cname)))
-    goto error;
-  if (!(X509_set_subject_name(x509, name)))
-    goto error;
-  if (!(name_issuer = tor_x509_name_new(cname_sign)))
-    goto error;
-  if (!(X509_set_issuer_name(x509, name_issuer)))
-    goto error;
-
-  if (!X509_time_adj(X509_get_notBefore(x509),0,&start_time))
-    goto error;
-  if (!X509_time_adj(X509_get_notAfter(x509),0,&end_time))
-    goto error;
-  if (!X509_set_pubkey(x509, pkey))
-    goto error;
-
-  if (!X509_sign(x509, sign_pkey, EVP_sha256()))
-    goto error;
-
-  goto done;
- error:
-  if (x509) {
-    X509_free(x509);
-    x509 = NULL;
-  }
- done:
-  tls_log_errors(NULL, LOG_WARN, LD_NET, "generating certificate");
-  if (sign_pkey)
-    EVP_PKEY_free(sign_pkey);
-  if (pkey)
-    EVP_PKEY_free(pkey);
-  if (serial_number)
-    BN_clear_free(serial_number);
-  if (name)
-    X509_NAME_free(name);
-  if (name_issuer)
-    X509_NAME_free(name_issuer);
-  return x509;
-
-#undef SERIAL_NUMBER_SIZE
-}
-
 /** List of ciphers that servers should select from when the client might be
  * claiming extra unsupported ciphers in order to avoid fingerprinting.  */
 static const char SERVER_CIPHER_LIST[] =
@@ -697,156 +535,6 @@ static const char CLIENT_CIPHER_LIST[] =
 #undef CIPHER
 #undef XCIPHER
 
-/** Free all storage held in <b>cert</b> */
-void
-tor_x509_cert_free_(tor_x509_cert_t *cert)
-{
-  if (! cert)
-    return;
-  if (cert->cert)
-    X509_free(cert->cert);
-  tor_free(cert->encoded);
-  memwipe(cert, 0x03, sizeof(*cert));
-  /* LCOV_EXCL_BR_START since cert will never be NULL here */
-  tor_free(cert);
-  /* LCOV_EXCL_BR_STOP */
-}
-
-/**
- * Allocate a new tor_x509_cert_t to hold the certificate "x509_cert".
- *
- * Steals a reference to x509_cert.
- */
-MOCK_IMPL(STATIC tor_x509_cert_t *,
-tor_x509_cert_new,(X509 *x509_cert))
-{
-  tor_x509_cert_t *cert;
-  EVP_PKEY *pkey;
-  RSA *rsa;
-  int length;
-  unsigned char *buf = NULL;
-
-  if (!x509_cert)
-    return NULL;
-
-  length = i2d_X509(x509_cert, &buf);
-  cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
-  if (length <= 0 || buf == NULL) {
-    goto err;
-  }
-  cert->encoded_len = (size_t) length;
-  cert->encoded = tor_malloc(length);
-  memcpy(cert->encoded, buf, length);
-  OPENSSL_free(buf);
-
-  cert->cert = x509_cert;
-
-  crypto_common_digests(&cert->cert_digests,
-                    (char*)cert->encoded, cert->encoded_len);
-
-  if ((pkey = X509_get_pubkey(x509_cert)) &&
-      (rsa = EVP_PKEY_get1_RSA(pkey))) {
-    crypto_pk_t *pk = crypto_new_pk_from_openssl_rsa_(rsa);
-    if (crypto_pk_get_common_digests(pk, &cert->pkey_digests) < 0) {
-      crypto_pk_free(pk);
-      EVP_PKEY_free(pkey);
-      goto err;
-    }
-
-    cert->pkey_digests_set = 1;
-    crypto_pk_free(pk);
-    EVP_PKEY_free(pkey);
-  }
-
-  return cert;
- err:
-  /* LCOV_EXCL_START for the same reason as the exclusion above */
-  tor_free(cert);
-  log_err(LD_CRYPTO, "Couldn't wrap encoded X509 certificate.");
-  X509_free(x509_cert);
-  return NULL;
-  /* LCOV_EXCL_STOP */
-}
-
-/** Return a new copy of <b>cert</b>. */
-tor_x509_cert_t *
-tor_x509_cert_dup(const tor_x509_cert_t *cert)
-{
-  tor_assert(cert);
-  X509 *x509 = cert->cert;
-  return tor_x509_cert_new(X509_dup(x509));
-}
-
-/** Read a DER-encoded X509 cert, of length exactly <b>certificate_len</b>,
- * from a <b>certificate</b>.  Return a newly allocated tor_x509_cert_t on
- * success and NULL on failure. */
-tor_x509_cert_t *
-tor_x509_cert_decode(const uint8_t *certificate, size_t certificate_len)
-{
-  X509 *x509;
-  const unsigned char *cp = (const unsigned char *)certificate;
-  tor_x509_cert_t *newcert;
-  tor_assert(certificate);
-  check_no_tls_errors();
-
-  if (certificate_len > INT_MAX)
-    goto err;
-
-  x509 = d2i_X509(NULL, &cp, (int)certificate_len);
-
-  if (!x509)
-    goto err; /* Couldn't decode */
-  if (cp - certificate != (int)certificate_len) {
-    X509_free(x509);
-    goto err; /* Didn't use all the bytes */
-  }
-  newcert = tor_x509_cert_new(x509);
-  if (!newcert) {
-    goto err;
-  }
-  if (newcert->encoded_len != certificate_len ||
-      fast_memneq(newcert->encoded, certificate, certificate_len)) {
-    /* Cert wasn't in DER */
-    tor_x509_cert_free(newcert);
-    goto err;
-  }
-  return newcert;
- err:
-  tls_log_errors(NULL, LOG_INFO, LD_CRYPTO, "decoding a certificate");
-  return NULL;
-}
-
-/** Set *<b>encoded_out</b> and *<b>size_out</b> to <b>cert</b>'s encoded DER
- * representation and length, respectively. */
-void
-tor_x509_cert_get_der(const tor_x509_cert_t *cert,
-                 const uint8_t **encoded_out, size_t *size_out)
-{
-  tor_assert(cert);
-  tor_assert(encoded_out);
-  tor_assert(size_out);
-  *encoded_out = cert->encoded;
-  *size_out = cert->encoded_len;
-}
-
-/** Return a set of digests for the public key in <b>cert</b>, or NULL if this
- * cert's public key is not one we know how to take the digest of. */
-const common_digests_t *
-tor_x509_cert_get_id_digests(const tor_x509_cert_t *cert)
-{
-  if (cert->pkey_digests_set)
-    return &cert->pkey_digests;
-  else
-    return NULL;
-}
-
-/** Return a set of digests for the public key in <b>cert</b>. */
-const common_digests_t *
-tor_x509_cert_get_cert_digests(const tor_x509_cert_t *cert)
-{
-  return &cert->cert_digests;
-}
-
 /** Remove a reference to <b>ctx</b>, and free it if it has no more
  * references. */
 static void
@@ -898,28 +586,6 @@ tor_tls_get_my_client_auth_key(void)
   return client_tls_context->auth_key;
 }
 
-/**
- * Return a newly allocated copy of the public key that a certificate
- * certifies. Watch out! This returns NULL if the cert's key is not RSA.
- */
-crypto_pk_t *
-tor_tls_cert_get_key(tor_x509_cert_t *cert)
-{
-  crypto_pk_t *result = NULL;
-  EVP_PKEY *pkey = X509_get_pubkey(cert->cert);
-  RSA *rsa;
-  if (!pkey)
-    return NULL;
-  rsa = EVP_PKEY_get1_RSA(pkey);
-  if (!rsa) {
-    EVP_PKEY_free(pkey);
-    return NULL;
-  }
-  result = crypto_new_pk_from_openssl_rsa_(rsa);
-  EVP_PKEY_free(pkey);
-  return result;
-}
-
 /** Return true iff the other side of <b>tls</b> has authenticated to us, and
  * the key certified in <b>cert</b> is the same as the key they used to do it.
  */
@@ -946,71 +612,6 @@ tor_tls_cert_matches_key,(const tor_tls_t *tls, const tor_x509_cert_t *cert))
   return result;
 }
 
-/** Check whether <b>cert</b> is well-formed, currently live, and correctly
- * signed by the public key in <b>signing_cert</b>.  If <b>check_rsa_1024</b>,
- * make sure that it has an RSA key with 1024 bits; otherwise, just check that
- * the key is long enough. Return 1 if the cert is good, and 0 if it's bad or
- * we couldn't check it. */
-int
-tor_tls_cert_is_valid(int severity,
-                      const tor_x509_cert_t *cert,
-                      const tor_x509_cert_t *signing_cert,
-                      time_t now,
-                      int check_rsa_1024)
-{
-  check_no_tls_errors();
-  EVP_PKEY *cert_key;
-  int r, key_ok = 0;
-
-  if (!signing_cert || !cert)
-    goto bad;
-
-  EVP_PKEY *signing_key = X509_get_pubkey(signing_cert->cert);
-  if (!signing_key)
-    goto bad;
-  r = X509_verify(cert->cert, signing_key);
-  EVP_PKEY_free(signing_key);
-  if (r <= 0)
-    goto bad;
-
-  /* okay, the signature checked out right.  Now let's check the check the
-   * lifetime. */
-  if (check_cert_lifetime_internal(severity, cert->cert, now,
-                                   48*60*60, 30*24*60*60) < 0)
-    goto bad;
-
-  cert_key = X509_get_pubkey(cert->cert);
-  if (check_rsa_1024 && cert_key) {
-    RSA *rsa = EVP_PKEY_get1_RSA(cert_key);
-#ifdef OPENSSL_1_1_API
-    if (rsa && RSA_bits(rsa) == 1024)
-#else
-    if (rsa && BN_num_bits(rsa->n) == 1024)
-#endif
-      key_ok = 1;
-    if (rsa)
-      RSA_free(rsa);
-  } else if (cert_key) {
-    int min_bits = 1024;
-#ifdef EVP_PKEY_EC
-    if (EVP_PKEY_base_id(cert_key) == EVP_PKEY_EC)
-      min_bits = 128;
-#endif
-    if (EVP_PKEY_bits(cert_key) >= min_bits)
-      key_ok = 1;
-  }
-  EVP_PKEY_free(cert_key);
-  if (!key_ok)
-    goto bad;
-
-  /* XXXX compare DNs or anything? */
-
-  return 1;
- bad:
-  tls_log_errors(NULL, LOG_INFO, LD_CRYPTO, "checking a certificate");
-  return 0;
-}
-
 /** Increase the reference count of <b>ctx</b>. */
 static void
 tor_tls_context_incref(tor_tls_context_t *ctx)
@@ -2146,63 +1747,6 @@ tor_tls_get_own_cert,(tor_tls_t *tls))
   return tor_x509_cert_new(duplicate);
 }
 
-/** Warn that a certificate lifetime extends through a certain range. */
-static void
-log_cert_lifetime(int severity, const X509 *cert, const char *problem,
-                  time_t now)
-{
-  BIO *bio = NULL;
-  BUF_MEM *buf;
-  char *s1=NULL, *s2=NULL;
-  char mytime[33];
-  struct tm tm;
-  size_t n;
-
-  if (problem)
-    tor_log(severity, LD_GENERAL,
-        "Certificate %s. Either their clock is set wrong, or your clock "
-        "is wrong.",
-           problem);
-
-  if (!(bio = BIO_new(BIO_s_mem()))) {
-    log_warn(LD_GENERAL, "Couldn't allocate BIO!"); goto end;
-  }
-  if (!(ASN1_TIME_print(bio, X509_get_notBefore_const(cert)))) {
-    tls_log_errors(NULL, LOG_WARN, LD_NET, "printing certificate lifetime");
-    goto end;
-  }
-  BIO_get_mem_ptr(bio, &buf);
-  s1 = tor_strndup(buf->data, buf->length);
-
-  (void)BIO_reset(bio);
-  if (!(ASN1_TIME_print(bio, X509_get_notAfter_const(cert)))) {
-    tls_log_errors(NULL, LOG_WARN, LD_NET, "printing certificate lifetime");
-    goto end;
-  }
-  BIO_get_mem_ptr(bio, &buf);
-  s2 = tor_strndup(buf->data, buf->length);
-
-  n = strftime(mytime, 32, "%b %d %H:%M:%S %Y UTC", tor_gmtime_r(&now, &tm));
-  if (n > 0) {
-    tor_log(severity, LD_GENERAL,
-        "(certificate lifetime runs from %s through %s. Your time is %s.)",
-        s1,s2,mytime);
-  } else {
-    tor_log(severity, LD_GENERAL,
-        "(certificate lifetime runs from %s through %s. "
-        "Couldn't get your time.)",
-        s1, s2);
-  }
-
- end:
-  /* Not expected to get invoked */
-  tls_log_errors(NULL, LOG_WARN, LD_NET, "getting certificate lifetime");
-  if (bio)
-    BIO_free(bio);
-  tor_free(s1);
-  tor_free(s2);
-}
-
 /** Helper function: try to extract a link certificate and an identity
  * certificate from <b>tls</b>, and store them in *<b>cert_out</b> and
  * *<b>id_cert_out</b> respectively.  Log all messages at level
@@ -2325,50 +1869,6 @@ tor_tls_check_lifetime(int severity, tor_tls_t *tls,
   return r;
 }
 
-/** Helper: check whether <b>cert</b> is expired give or take
- * <b>past_tolerance</b> seconds, or not-yet-valid give or take
- * <b>future_tolerance</b> seconds.  (Relative to the current time
- * <b>now</b>.)  If it is live, return 0.  If it is not live, log a message
- * and return -1. */
-static int
-check_cert_lifetime_internal(int severity, const X509 *cert,
-                             time_t now,
-                             int past_tolerance, int future_tolerance)
-{
-  time_t t;
-
-  t = now + future_tolerance;
-  if (X509_cmp_time(X509_get_notBefore_const(cert), &t) > 0) {
-    log_cert_lifetime(severity, cert, "not yet valid", now);
-    return -1;
-  }
-  t = now - past_tolerance;
-  if (X509_cmp_time(X509_get_notAfter_const(cert), &t) < 0) {
-    log_cert_lifetime(severity, cert, "already expired", now);
-    return -1;
-  }
-
-  return 0;
-}
-
-#ifdef TOR_UNIT_TESTS
-/* Testing only: return a new x509 cert with the same contents as <b>inp</b>,
-   but with the expiration time <b>new_expiration_time</b>, signed with
-   <b>signing_key</b>. */
-STATIC tor_x509_cert_t *
-tor_x509_cert_replace_expiration(const tor_x509_cert_t *inp,
-                                 time_t new_expiration_time,
-                                 crypto_pk_t *signing_key)
-{
-  X509 *newc = X509_dup(inp->cert);
-  X509_time_adj(X509_get_notAfter(newc), 0, &new_expiration_time);
-  EVP_PKEY *pk = crypto_pk_get_openssl_evp_pkey_(signing_key, 1);
-  tor_assert(X509_sign(newc, pk, EVP_sha256()));
-  EVP_PKEY_free(pk);
-  return tor_x509_cert_new(newc);
-}
-#endif /* defined(TOR_UNIT_TESTS) */
-
 /** Return the number of bytes available for reading from <b>tls</b>.
  */
 int
diff --git a/src/lib/tls/tortls.h b/src/lib/tls/tortls.h
index fe192b2ab..a1d90c16b 100644
--- a/src/lib/tls/tortls.h
+++ b/src/lib/tls/tortls.h
@@ -18,8 +18,7 @@
 /* Opaque structure to hold a TLS connection. */
 typedef struct tor_tls_t tor_tls_t;
 
-/* Opaque structure to hold an X509 certificate. */
-typedef struct tor_x509_cert_t tor_x509_cert_t;
+struct tor_x509_cert_t;
 
 /* Possible return values for most tor_tls_* functions. */
 #define MIN_TOR_TLS_ERROR_VAL_     -9
@@ -62,10 +61,11 @@ typedef enum {
 } tor_tls_state_t;
 #define tor_tls_state_bitfield_t ENUM_BF(tor_tls_state_t)
 
-struct x509_st;
+#ifdef ENABLE_OPENSSL
 struct ssl_st;
 struct ssl_ctx_st;
 struct ssl_session_st;
+#endif
 
 /** Holds a SSL_CTX object and related state used to configure TLS
  * connections.
@@ -73,23 +73,13 @@ struct ssl_session_st;
 typedef struct tor_tls_context_t {
   int refcnt;
   struct ssl_ctx_st *ctx;
-  tor_x509_cert_t *my_link_cert;
-  tor_x509_cert_t *my_id_cert;
-  tor_x509_cert_t *my_auth_cert;
+  struct tor_x509_cert_t *my_link_cert;
+  struct tor_x509_cert_t *my_id_cert;
+  struct tor_x509_cert_t *my_auth_cert;
   crypto_pk_t *link_key;
   crypto_pk_t *auth_key;
 } tor_tls_context_t;
 
-/** Structure that we use for a single certificate. */
-struct tor_x509_cert_t {
-  struct x509_st *cert;
-  uint8_t *encoded;
-  size_t encoded_len;
-  unsigned pkey_digests_set : 1;
-  common_digests_t cert_digests;
-  common_digests_t pkey_digests;
-};
-
 /** Holds a SSL object and its associated data.  Members are only
  * accessed from within tortls.c.
  */
@@ -134,15 +124,15 @@ STATIC int tor_tls_get_error(tor_tls_t *tls, int r, int extra,
                   const char *doing, int severity, int domain);
 STATIC tor_tls_t *tor_tls_get_by_ssl(const struct ssl_st *ssl);
 STATIC void tor_tls_allocate_tor_tls_object_ex_data_index(void);
+MOCK_DECL(STATIC void, try_to_extract_certs_from_tls,
+          (int severity, tor_tls_t *tls, struct x509_st **cert_out,
+           struct x509_st **id_cert_out));
 #ifdef TORTLS_OPENSSL_PRIVATE
 STATIC int always_accept_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx);
 STATIC int tor_tls_classify_client_ciphers(const struct ssl_st *ssl,
                                            STACK_OF(SSL_CIPHER) *peer_ciphers);
 #endif
 STATIC int tor_tls_client_is_using_v2_ciphers(const struct ssl_st *ssl);
-MOCK_DECL(STATIC void, try_to_extract_certs_from_tls,
-          (int severity, tor_tls_t *tls, struct x509_st **cert_out,
-           struct x509_st **id_cert_out));
 #ifndef HAVE_SSL_SESSION_GET_MASTER_KEY
 STATIC size_t SSL_SESSION_get_master_key(struct ssl_session_st *s,
                                          uint8_t *out,
@@ -161,23 +151,13 @@ STATIC int tor_tls_session_secret_cb(struct ssl_st *ssl, void *secret,
 STATIC int find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m,
                              uint16_t cipher);
 #endif /* defined(TORTLS_OPENSSL_PRIVATE) */
-MOCK_DECL(STATIC struct x509_st *, tor_tls_create_certificate,
-                                                   (crypto_pk_t *rsa,
-                                                    crypto_pk_t *rsa_sign,
-                                                    const char *cname,
-                                                    const char *cname_sign,
-                                                  unsigned int cert_lifetime));
 STATIC tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity,
                    unsigned int key_lifetime, unsigned flags, int is_client);
-MOCK_DECL(STATIC tor_x509_cert_t *, tor_x509_cert_new,
-          (struct x509_st *x509_cert));
 STATIC int tor_tls_context_init_one(tor_tls_context_t **ppcontext,
                                     crypto_pk_t *identity,
                                     unsigned int key_lifetime,
                                     unsigned int flags,
                                     int is_client);
-STATIC void tls_log_errors(tor_tls_t *tls, int severity, int domain,
-                           const char *doing);
 
 #ifdef TOR_UNIT_TESTS
 extern int tor_tls_object_ex_data_index;
@@ -187,15 +167,10 @@ extern uint16_t v2_cipher_list[];
 extern uint64_t total_bytes_written_over_tls;
 extern uint64_t total_bytes_written_by_tls;
 
-STATIC tor_x509_cert_t *tor_x509_cert_replace_expiration(
-                                               const tor_x509_cert_t *inp,
-                                               time_t new_expiration_time,
-                                               crypto_pk_t *signing_key);
 #endif /* defined(TOR_UNIT_TESTS) */
 
 #endif /* defined(TORTLS_PRIVATE) */
 
-tor_x509_cert_t *tor_x509_cert_dup(const tor_x509_cert_t *cert);
 const char *tor_tls_err_to_string(int err);
 void tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz);
 
@@ -205,6 +180,9 @@ void tor_tls_free_all(void);
 #define TOR_TLS_CTX_USE_ECDHE_P256   (1u<<1)
 #define TOR_TLS_CTX_USE_ECDHE_P224   (1u<<2)
 
+void tor_tls_init(void);
+void tls_log_errors(tor_tls_t *tls, int severity, int domain,
+                    const char *doing);
 int tor_tls_context_init(unsigned flags,
                          crypto_pk_t *client_identity,
                          crypto_pk_t *server_identity,
@@ -218,8 +196,8 @@ int tor_tls_is_server(tor_tls_t *tls);
 void tor_tls_free_(tor_tls_t *tls);
 #define tor_tls_free(tls) FREE_AND_NULL(tor_tls_t, tor_tls_free_, (tls))
 int tor_tls_peer_has_cert(tor_tls_t *tls);
-MOCK_DECL(tor_x509_cert_t *,tor_tls_get_peer_cert,(tor_tls_t *tls));
-MOCK_DECL(tor_x509_cert_t *,tor_tls_get_own_cert,(tor_tls_t *tls));
+MOCK_DECL(struct tor_x509_cert_t *,tor_tls_get_peer_cert,(tor_tls_t *tls));
+MOCK_DECL(struct tor_x509_cert_t *,tor_tls_get_own_cert,(tor_tls_t *tls));
 int tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity);
 int tor_tls_check_lifetime(int severity,
                            tor_tls_t *tls, time_t now,
@@ -248,6 +226,8 @@ MOCK_DECL(double, tls_get_write_overhead_ratio, (void));
 int tor_tls_used_v1_handshake(tor_tls_t *tls);
 int tor_tls_get_num_server_handshakes(tor_tls_t *tls);
 int tor_tls_server_got_renegotiate(tor_tls_t *tls);
+MOCK_DECL(int,tor_tls_cert_matches_key,(const tor_tls_t *tls,
+                                        const struct tor_x509_cert_t *cert));
 MOCK_DECL(int,tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out));
 MOCK_DECL(int,tor_tls_export_key_material,(
                      tor_tls_t *tls, uint8_t *secrets_out,
@@ -263,29 +243,11 @@ void check_no_tls_errors_(const char *fname, int line);
 void tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
                            int severity, int domain, const char *doing);
 
-void tor_x509_cert_free_(tor_x509_cert_t *cert);
-#define tor_x509_cert_free(c) \
-  FREE_AND_NULL(tor_x509_cert_t, tor_x509_cert_free_, (c))
-tor_x509_cert_t *tor_x509_cert_decode(const uint8_t *certificate,
-                            size_t certificate_len);
-void tor_x509_cert_get_der(const tor_x509_cert_t *cert,
-                      const uint8_t **encoded_out, size_t *size_out);
-const common_digests_t *tor_x509_cert_get_id_digests(
-                      const tor_x509_cert_t *cert);
-const common_digests_t *tor_x509_cert_get_cert_digests(
-                      const tor_x509_cert_t *cert);
 int tor_tls_get_my_certs(int server,
-                         const tor_x509_cert_t **link_cert_out,
-                         const tor_x509_cert_t **id_cert_out);
+                         const struct tor_x509_cert_t **link_cert_out,
+                         const struct tor_x509_cert_t **id_cert_out);
 crypto_pk_t *tor_tls_get_my_client_auth_key(void);
-crypto_pk_t *tor_tls_cert_get_key(tor_x509_cert_t *cert);
-MOCK_DECL(int,tor_tls_cert_matches_key,(const tor_tls_t *tls,
-                                        const tor_x509_cert_t *cert));
-int tor_tls_cert_is_valid(int severity,
-                          const tor_x509_cert_t *cert,
-                          const tor_x509_cert_t *signing_cert,
-                          time_t now,
-                          int check_rsa_1024);
+
 const char *tor_tls_get_ciphersuite_name(tor_tls_t *tls);
 
 int evaluate_ecgroup_for_tls(const char *ecgroup);
diff --git a/src/lib/tls/x509.c b/src/lib/tls/x509.c
new file mode 100644
index 000000000..feded3473
--- /dev/null
+++ b/src/lib/tls/x509.c
@@ -0,0 +1,539 @@
+/* Copyright (c) 2003, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file x509.c
+ * \brief Wrapper functions to present a consistent interface to
+ * X.509 functions from OpenSSL.
+ **/
+
+#include "lib/tls/x509.h"
+#include "lib/tls/tortls.h"
+//#include "lib/crypt_ops/crypto_cipher.h"
+#include "lib/crypt_ops/crypto_rand.h"
+#include "lib/crypt_ops/crypto_util.h"
+
+/* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in
+ * srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */
+DISABLE_GCC_WARNING(redundant-decls)
+
+#include <openssl/opensslv.h>
+
+#ifdef OPENSSL_NO_EC
+#error "We require OpenSSL with ECC support"
+#endif
+
+#include <openssl/err.h>
+#include <openssl/asn1.h>
+#include <openssl/bio.h>
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+
+ENABLE_GCC_WARNING(redundant-decls)
+
+#include "lib/log/log.h"
+#include "lib/log/util_bug.h"
+#include "lib/ctime/di_ops.h"
+#include "lib/encoding/time_fmt.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef OPENSSL_1_1_API
+#define X509_get_notBefore_const(cert) \
+    X509_get0_notBefore(cert)
+#define X509_get_notAfter_const(cert) \
+    X509_get0_notAfter(cert)
+#ifndef X509_get_notBefore
+#define X509_get_notBefore(cert) \
+    X509_getm_notBefore(cert)
+#endif
+#ifndef X509_get_notAfter
+#define X509_get_notAfter(cert) \
+    X509_getm_notAfter(cert)
+#endif
+#else /* ! OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) */
+#define X509_get_notBefore_const(cert) \
+  ((const ASN1_TIME*) X509_get_notBefore((X509 *)cert))
+#define X509_get_notAfter_const(cert) \
+  ((const ASN1_TIME*) X509_get_notAfter((X509 *)cert))
+#endif
+
+/** Return a newly allocated X509 name with commonName <b>cname</b>. */
+static X509_NAME *
+tor_x509_name_new(const char *cname)
+{
+  int nid;
+  X509_NAME *name;
+  /* LCOV_EXCL_BR_START : these branches will only fail on OOM errors */
+  if (!(name = X509_NAME_new()))
+    return NULL;
+  if ((nid = OBJ_txt2nid("commonName")) == NID_undef) goto error;
+  if (!(X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC,
+                                   (unsigned char*)cname, -1, -1, 0)))
+    goto error;
+  /* LCOV_EXCL_BR_STOP */
+  return name;
+
+  /* LCOV_EXCL_START : these lines will only execute on out of memory errors*/
+ error:
+  X509_NAME_free(name);
+  return NULL;
+  /* LCOV_EXCL_STOP */
+}
+
+/** Generate and sign an X509 certificate with the public key <b>rsa</b>,
+ * signed by the private key <b>rsa_sign</b>.  The commonName of the
+ * certificate will be <b>cname</b>; the commonName of the issuer will be
+ * <b>cname_sign</b>. The cert will be valid for <b>cert_lifetime</b>
+ * seconds, starting from some time in the past.
+ *
+ * Return a certificate on success, NULL on failure.
+ */
+MOCK_IMPL(X509 *,
+tor_tls_create_certificate,(crypto_pk_t *rsa,
+                            crypto_pk_t *rsa_sign,
+                            const char *cname,
+                            const char *cname_sign,
+                            unsigned int cert_lifetime))
+{
+  /* OpenSSL generates self-signed certificates with random 64-bit serial
+   * numbers, so let's do that too. */
+#define SERIAL_NUMBER_SIZE 8
+
+  time_t start_time, end_time;
+  BIGNUM *serial_number = NULL;
+  unsigned char serial_tmp[SERIAL_NUMBER_SIZE];
+  EVP_PKEY *sign_pkey = NULL, *pkey=NULL;
+  X509 *x509 = NULL;
+  X509_NAME *name = NULL, *name_issuer=NULL;
+
+  tor_tls_init();
+
+  /* Make sure we're part-way through the certificate lifetime, rather
+   * than having it start right now. Don't choose quite uniformly, since
+   * then we might pick a time where we're about to expire. Lastly, be
+   * sure to start on a day boundary. */
+  time_t now = time(NULL);
+  /* Our certificate lifetime will be cert_lifetime no matter what, but if we
+   * start cert_lifetime in the past, we'll have 0 real lifetime.  instead we
+   * start up to (cert_lifetime - min_real_lifetime - start_granularity) in
+   * the past. */
+  const time_t min_real_lifetime = 24*3600;
+  const time_t start_granularity = 24*3600;
+  time_t earliest_start_time;
+  /* Don't actually start in the future! */
+  if (cert_lifetime <= min_real_lifetime + start_granularity) {
+    earliest_start_time = now - 1;
+  } else {
+    earliest_start_time = now + min_real_lifetime + start_granularity
+      - cert_lifetime;
+  }
+  start_time = crypto_rand_time_range(earliest_start_time, now);
+  /* Round the start time back to the start of a day. */
+  start_time -= start_time % start_granularity;
+
+  end_time = start_time + cert_lifetime;
+
+  tor_assert(rsa);
+  tor_assert(cname);
+  tor_assert(rsa_sign);
+  tor_assert(cname_sign);
+  if (!(sign_pkey = crypto_pk_get_openssl_evp_pkey_(rsa_sign,1)))
+    goto error;
+  if (!(pkey = crypto_pk_get_openssl_evp_pkey_(rsa,0)))
+    goto error;
+  if (!(x509 = X509_new()))
+    goto error;
+  if (!(X509_set_version(x509, 2)))
+    goto error;
+
+  { /* our serial number is 8 random bytes. */
+    crypto_rand((char *)serial_tmp, sizeof(serial_tmp));
+    if (!(serial_number = BN_bin2bn(serial_tmp, sizeof(serial_tmp), NULL)))
+      goto error;
+    if (!(BN_to_ASN1_INTEGER(serial_number, X509_get_serialNumber(x509))))
+      goto error;
+  }
+
+  if (!(name = tor_x509_name_new(cname)))
+    goto error;
+  if (!(X509_set_subject_name(x509, name)))
+    goto error;
+  if (!(name_issuer = tor_x509_name_new(cname_sign)))
+    goto error;
+  if (!(X509_set_issuer_name(x509, name_issuer)))
+    goto error;
+
+  if (!X509_time_adj(X509_get_notBefore(x509),0,&start_time))
+    goto error;
+  if (!X509_time_adj(X509_get_notAfter(x509),0,&end_time))
+    goto error;
+  if (!X509_set_pubkey(x509, pkey))
+    goto error;
+
+  if (!X509_sign(x509, sign_pkey, EVP_sha256()))
+    goto error;
+
+  goto done;
+ error:
+  if (x509) {
+    X509_free(x509);
+    x509 = NULL;
+  }
+ done:
+  tls_log_errors(NULL, LOG_WARN, LD_NET, "generating certificate");
+  if (sign_pkey)
+    EVP_PKEY_free(sign_pkey);
+  if (pkey)
+    EVP_PKEY_free(pkey);
+  if (serial_number)
+    BN_clear_free(serial_number);
+  if (name)
+    X509_NAME_free(name);
+  if (name_issuer)
+    X509_NAME_free(name_issuer);
+  return x509;
+
+#undef SERIAL_NUMBER_SIZE
+}
+
+/** Free all storage held in <b>cert</b> */
+void
+tor_x509_cert_free_(tor_x509_cert_t *cert)
+{
+  if (! cert)
+    return;
+  if (cert->cert)
+    X509_free(cert->cert);
+  tor_free(cert->encoded);
+  memwipe(cert, 0x03, sizeof(*cert));
+  /* LCOV_EXCL_BR_START since cert will never be NULL here */
+  tor_free(cert);
+  /* LCOV_EXCL_BR_STOP */
+}
+
+/**
+ * Allocate a new tor_x509_cert_t to hold the certificate "x509_cert".
+ *
+ * Steals a reference to x509_cert.
+ */
+MOCK_IMPL(tor_x509_cert_t *,
+tor_x509_cert_new,(X509 *x509_cert))
+{
+  tor_x509_cert_t *cert;
+  EVP_PKEY *pkey;
+  RSA *rsa;
+  int length;
+  unsigned char *buf = NULL;
+
+  if (!x509_cert)
+    return NULL;
+
+  length = i2d_X509(x509_cert, &buf);
+  cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+  if (length <= 0 || buf == NULL) {
+    goto err;
+  }
+  cert->encoded_len = (size_t) length;
+  cert->encoded = tor_malloc(length);
+  memcpy(cert->encoded, buf, length);
+  OPENSSL_free(buf);
+
+  cert->cert = x509_cert;
+
+  crypto_common_digests(&cert->cert_digests,
+                    (char*)cert->encoded, cert->encoded_len);
+
+  if ((pkey = X509_get_pubkey(x509_cert)) &&
+      (rsa = EVP_PKEY_get1_RSA(pkey))) {
+    crypto_pk_t *pk = crypto_new_pk_from_openssl_rsa_(rsa);
+    if (crypto_pk_get_common_digests(pk, &cert->pkey_digests) < 0) {
+      crypto_pk_free(pk);
+      EVP_PKEY_free(pkey);
+      goto err;
+    }
+
+    cert->pkey_digests_set = 1;
+    crypto_pk_free(pk);
+    EVP_PKEY_free(pkey);
+  }
+
+  return cert;
+ err:
+  /* LCOV_EXCL_START for the same reason as the exclusion above */
+  tor_free(cert);
+  log_err(LD_CRYPTO, "Couldn't wrap encoded X509 certificate.");
+  X509_free(x509_cert);
+  return NULL;
+  /* LCOV_EXCL_STOP */
+}
+
+/** Return a new copy of <b>cert</b>. */
+tor_x509_cert_t *
+tor_x509_cert_dup(const tor_x509_cert_t *cert)
+{
+  tor_assert(cert);
+  X509 *x509 = cert->cert;
+  return tor_x509_cert_new(X509_dup(x509));
+}
+
+/** Read a DER-encoded X509 cert, of length exactly <b>certificate_len</b>,
+ * from a <b>certificate</b>.  Return a newly allocated tor_x509_cert_t on
+ * success and NULL on failure. */
+tor_x509_cert_t *
+tor_x509_cert_decode(const uint8_t *certificate, size_t certificate_len)
+{
+  X509 *x509;
+  const unsigned char *cp = (const unsigned char *)certificate;
+  tor_x509_cert_t *newcert;
+  tor_assert(certificate);
+  check_no_tls_errors();
+
+  if (certificate_len > INT_MAX)
+    goto err;
+
+  x509 = d2i_X509(NULL, &cp, (int)certificate_len);
+
+  if (!x509)
+    goto err; /* Couldn't decode */
+  if (cp - certificate != (int)certificate_len) {
+    X509_free(x509);
+    goto err; /* Didn't use all the bytes */
+  }
+  newcert = tor_x509_cert_new(x509);
+  if (!newcert) {
+    goto err;
+  }
+  if (newcert->encoded_len != certificate_len ||
+      fast_memneq(newcert->encoded, certificate, certificate_len)) {
+    /* Cert wasn't in DER */
+    tor_x509_cert_free(newcert);
+    goto err;
+  }
+  return newcert;
+ err:
+  tls_log_errors(NULL, LOG_INFO, LD_CRYPTO, "decoding a certificate");
+  return NULL;
+}
+
+/** Set *<b>encoded_out</b> and *<b>size_out</b> to <b>cert</b>'s encoded DER
+ * representation and length, respectively. */
+void
+tor_x509_cert_get_der(const tor_x509_cert_t *cert,
+                 const uint8_t **encoded_out, size_t *size_out)
+{
+  tor_assert(cert);
+  tor_assert(encoded_out);
+  tor_assert(size_out);
+  *encoded_out = cert->encoded;
+  *size_out = cert->encoded_len;
+}
+
+/** Return a set of digests for the public key in <b>cert</b>, or NULL if this
+ * cert's public key is not one we know how to take the digest of. */
+const common_digests_t *
+tor_x509_cert_get_id_digests(const tor_x509_cert_t *cert)
+{
+  if (cert->pkey_digests_set)
+    return &cert->pkey_digests;
+  else
+    return NULL;
+}
+
+/** Return a set of digests for the public key in <b>cert</b>. */
+const common_digests_t *
+tor_x509_cert_get_cert_digests(const tor_x509_cert_t *cert)
+{
+  return &cert->cert_digests;
+}
+
+/**
+ * Return a newly allocated copy of the public key that a certificate
+ * certifies. Watch out! This returns NULL if the cert's key is not RSA.
+ */
+crypto_pk_t *
+tor_tls_cert_get_key(tor_x509_cert_t *cert)
+{
+  crypto_pk_t *result = NULL;
+  EVP_PKEY *pkey = X509_get_pubkey(cert->cert);
+  RSA *rsa;
+  if (!pkey)
+    return NULL;
+  rsa = EVP_PKEY_get1_RSA(pkey);
+  if (!rsa) {
+    EVP_PKEY_free(pkey);
+    return NULL;
+  }
+  result = crypto_new_pk_from_openssl_rsa_(rsa);
+  EVP_PKEY_free(pkey);
+  return result;
+}
+
+/** Check whether <b>cert</b> is well-formed, currently live, and correctly
+ * signed by the public key in <b>signing_cert</b>.  If <b>check_rsa_1024</b>,
+ * make sure that it has an RSA key with 1024 bits; otherwise, just check that
+ * the key is long enough. Return 1 if the cert is good, and 0 if it's bad or
+ * we couldn't check it. */
+int
+tor_tls_cert_is_valid(int severity,
+                      const tor_x509_cert_t *cert,
+                      const tor_x509_cert_t *signing_cert,
+                      time_t now,
+                      int check_rsa_1024)
+{
+  check_no_tls_errors();
+  EVP_PKEY *cert_key;
+  int r, key_ok = 0;
+
+  if (!signing_cert || !cert)
+    goto bad;
+
+  EVP_PKEY *signing_key = X509_get_pubkey(signing_cert->cert);
+  if (!signing_key)
+    goto bad;
+  r = X509_verify(cert->cert, signing_key);
+  EVP_PKEY_free(signing_key);
+  if (r <= 0)
+    goto bad;
+
+  /* okay, the signature checked out right.  Now let's check the check the
+   * lifetime. */
+  if (check_cert_lifetime_internal(severity, cert->cert, now,
+                                   48*60*60, 30*24*60*60) < 0)
+    goto bad;
+
+  cert_key = X509_get_pubkey(cert->cert);
+  if (check_rsa_1024 && cert_key) {
+    RSA *rsa = EVP_PKEY_get1_RSA(cert_key);
+#ifdef OPENSSL_1_1_API
+    if (rsa && RSA_bits(rsa) == 1024)
+#else
+    if (rsa && BN_num_bits(rsa->n) == 1024)
+#endif
+      key_ok = 1;
+    if (rsa)
+      RSA_free(rsa);
+  } else if (cert_key) {
+    int min_bits = 1024;
+#ifdef EVP_PKEY_EC
+    if (EVP_PKEY_base_id(cert_key) == EVP_PKEY_EC)
+      min_bits = 128;
+#endif
+    if (EVP_PKEY_bits(cert_key) >= min_bits)
+      key_ok = 1;
+  }
+  EVP_PKEY_free(cert_key);
+  if (!key_ok)
+    goto bad;
+
+  /* XXXX compare DNs or anything? */
+
+  return 1;
+ bad:
+  tls_log_errors(NULL, LOG_INFO, LD_CRYPTO, "checking a certificate");
+  return 0;
+}
+
+/** Warn that a certificate lifetime extends through a certain range. */
+static void
+log_cert_lifetime(int severity, const X509 *cert, const char *problem,
+                  time_t now)
+{
+  BIO *bio = NULL;
+  BUF_MEM *buf;
+  char *s1=NULL, *s2=NULL;
+  char mytime[33];
+  struct tm tm;
+  size_t n;
+
+  if (problem)
+    tor_log(severity, LD_GENERAL,
+        "Certificate %s. Either their clock is set wrong, or your clock "
+        "is wrong.",
+           problem);
+
+  if (!(bio = BIO_new(BIO_s_mem()))) {
+    log_warn(LD_GENERAL, "Couldn't allocate BIO!"); goto end;
+  }
+  if (!(ASN1_TIME_print(bio, X509_get_notBefore_const(cert)))) {
+    tls_log_errors(NULL, LOG_WARN, LD_NET, "printing certificate lifetime");
+    goto end;
+  }
+  BIO_get_mem_ptr(bio, &buf);
+  s1 = tor_strndup(buf->data, buf->length);
+
+  (void)BIO_reset(bio);
+  if (!(ASN1_TIME_print(bio, X509_get_notAfter_const(cert)))) {
+    tls_log_errors(NULL, LOG_WARN, LD_NET, "printing certificate lifetime");
+    goto end;
+  }
+  BIO_get_mem_ptr(bio, &buf);
+  s2 = tor_strndup(buf->data, buf->length);
+
+  n = strftime(mytime, 32, "%b %d %H:%M:%S %Y UTC", tor_gmtime_r(&now, &tm));
+  if (n > 0) {
+    tor_log(severity, LD_GENERAL,
+        "(certificate lifetime runs from %s through %s. Your time is %s.)",
+        s1,s2,mytime);
+  } else {
+    tor_log(severity, LD_GENERAL,
+        "(certificate lifetime runs from %s through %s. "
+        "Couldn't get your time.)",
+        s1, s2);
+  }
+
+ end:
+  /* Not expected to get invoked */
+  tls_log_errors(NULL, LOG_WARN, LD_NET, "getting certificate lifetime");
+  if (bio)
+    BIO_free(bio);
+  tor_free(s1);
+  tor_free(s2);
+}
+
+/** Helper: check whether <b>cert</b> is expired give or take
+ * <b>past_tolerance</b> seconds, or not-yet-valid give or take
+ * <b>future_tolerance</b> seconds.  (Relative to the current time
+ * <b>now</b>.)  If it is live, return 0.  If it is not live, log a message
+ * and return -1. */
+int
+check_cert_lifetime_internal(int severity, const X509 *cert,
+                             time_t now,
+                             int past_tolerance, int future_tolerance)
+{
+  time_t t;
+
+  t = now + future_tolerance;
+  if (X509_cmp_time(X509_get_notBefore_const(cert), &t) > 0) {
+    log_cert_lifetime(severity, cert, "not yet valid", now);
+    return -1;
+  }
+  t = now - past_tolerance;
+  if (X509_cmp_time(X509_get_notAfter_const(cert), &t) < 0) {
+    log_cert_lifetime(severity, cert, "already expired", now);
+    return -1;
+  }
+
+  return 0;
+}
+
+#ifdef TOR_UNIT_TESTS
+/* Testing only: return a new x509 cert with the same contents as <b>inp</b>,
+   but with the expiration time <b>new_expiration_time</b>, signed with
+   <b>signing_key</b>. */
+STATIC tor_x509_cert_t *
+tor_x509_cert_replace_expiration(const tor_x509_cert_t *inp,
+                                 time_t new_expiration_time,
+                                 crypto_pk_t *signing_key)
+{
+  X509 *newc = X509_dup(inp->cert);
+  X509_time_adj(X509_get_notAfter(newc), 0, &new_expiration_time);
+  EVP_PKEY *pk = crypto_pk_get_openssl_evp_pkey_(signing_key, 1);
+  tor_assert(X509_sign(newc, pk, EVP_sha256()));
+  EVP_PKEY_free(pk);
+  return tor_x509_cert_new(newc);
+}
+#endif /* defined(TOR_UNIT_TESTS) */
diff --git a/src/lib/tls/x509.h b/src/lib/tls/x509.h
new file mode 100644
index 000000000..2f3f3a410
--- /dev/null
+++ b/src/lib/tls/x509.h
@@ -0,0 +1,78 @@
+/* Copyright (c) 2003, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_X509_H
+#define TOR_X509_H
+
+/**
+ * \file x509.h
+ * \brief Headers for tortls.c
+ **/
+
+#include "lib/crypt_ops/crypto_rsa.h"
+#include "lib/crypt_ops/compat_openssl.h"
+#include "lib/testsupport/testsupport.h"
+
+/* Opaque structure to hold an X509 certificate. */
+typedef struct tor_x509_cert_t tor_x509_cert_t;
+
+#ifdef ENABLE_OPENSSL
+struct x509_st;
+#endif
+
+/** Structure that we use for a single certificate. */
+struct tor_x509_cert_t {
+#ifdef ENABLE_OPENSSL
+  struct x509_st *cert;
+#endif
+  uint8_t *encoded;
+  size_t encoded_len;
+  unsigned pkey_digests_set : 1;
+  common_digests_t cert_digests;
+  common_digests_t pkey_digests;
+};
+
+MOCK_DECL(struct x509_st *, tor_tls_create_certificate,
+                                                   (crypto_pk_t *rsa,
+                                                    crypto_pk_t *rsa_sign,
+                                                    const char *cname,
+                                                    const char *cname_sign,
+                                                  unsigned int cert_lifetime));
+MOCK_DECL(tor_x509_cert_t *, tor_x509_cert_new,
+          (struct x509_st *x509_cert));
+
+#ifdef TOR_UNIT_TESTS
+tor_x509_cert_t *tor_x509_cert_replace_expiration(
+                                               const tor_x509_cert_t *inp,
+                                               time_t new_expiration_time,
+                                               crypto_pk_t *signing_key);
+#endif
+
+tor_x509_cert_t *tor_x509_cert_dup(const tor_x509_cert_t *cert);
+
+void tor_x509_cert_free_(tor_x509_cert_t *cert);
+#define tor_x509_cert_free(c) \
+  FREE_AND_NULL(tor_x509_cert_t, tor_x509_cert_free_, (c))
+tor_x509_cert_t *tor_x509_cert_decode(const uint8_t *certificate,
+                            size_t certificate_len);
+void tor_x509_cert_get_der(const tor_x509_cert_t *cert,
+                      const uint8_t **encoded_out, size_t *size_out);
+const common_digests_t *tor_x509_cert_get_id_digests(
+                      const tor_x509_cert_t *cert);
+const common_digests_t *tor_x509_cert_get_cert_digests(
+                      const tor_x509_cert_t *cert);
+
+crypto_pk_t *tor_tls_cert_get_key(tor_x509_cert_t *cert);
+int tor_tls_cert_is_valid(int severity,
+                          const tor_x509_cert_t *cert,
+                          const tor_x509_cert_t *signing_cert,
+                          time_t now,
+                          int check_rsa_1024);
+
+int check_cert_lifetime_internal(int severity, const X509 *cert,
+                                   time_t now,
+                                   int past_tolerance, int future_tolerance);
+
+#endif
diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c
index e0d12fb47..c1ede5420 100644
--- a/src/test/test_link_handshake.c
+++ b/src/test/test_link_handshake.c
@@ -25,6 +25,7 @@
 #include "core/or/var_cell_st.h"
 
 #include "lib/tls/tortls.h"
+#include "lib/tls/x509.h"
 
 #include "test/test.h"
 #include "test/log_test_helpers.h"
diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c
index cd3435556..f5b11d4f2 100644
--- a/src/test/test_tortls.c
+++ b/src/test/test_tortls.c
@@ -34,6 +34,7 @@ ENABLE_GCC_WARNING(redundant-decls)
 #include "lib/log/log.h"
 #include "app/config/config.h"
 #include "lib/tls/tortls.h"
+#include "lib/tls/x509.h"
 #include "app/config/or_state_st.h"
 
 #include "test/test.h"



_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits