tor-commits
Threads by month
- ----- 2025 -----
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
September 2018
- 17 participants
- 3231 discussions
[tor/master] Split tls modules and their tests into openssl and generic.
by nickm@torproject.org 05 Sep '18
by nickm@torproject.org 05 Sep '18
05 Sep '18
commit 1992c761308538cffea64abecc9e45cbd47b1bda
Author: Nick Mathewson <nickm(a)torproject.org>
Date: Sun Aug 12 16:04:12 2018 -0400
Split tls modules and their tests into openssl and generic.
Also, add a stubbed-out nss version of the modules. The tests won't
pass with NSS yet since the NSS modules don't do anything.
This is a good patch to read with --color-moved.
---
src/lib/tls/include.am | 12 +
src/lib/tls/tortls.c | 2055 +-----------------------------
src/lib/tls/tortls.h | 64 +-
src/lib/tls/tortls_internal.h | 65 +
src/lib/tls/tortls_nss.c | 432 +++++++
src/lib/tls/tortls_openssl.c | 2032 ++++++++++++++++++++++++++++++
src/lib/tls/x509.c | 479 +------
src/lib/tls/x509.h | 4 +-
src/lib/tls/x509_nss.c | 122 ++
src/lib/tls/x509_openssl.c | 489 +++++++
src/test/include.am | 8 +
src/test/test.c | 3 +
src/test/test.h | 1 +
src/test/test_tortls.c | 2732 ++--------------------------------------
src/test/test_tortls_openssl.c | 2597 ++++++++++++++++++++++++++++++++++++++
15 files changed, 5873 insertions(+), 5222 deletions(-)
diff --git a/src/lib/tls/include.am b/src/lib/tls/include.am
index 1fd25a0b3..173104903 100644
--- a/src/lib/tls/include.am
+++ b/src/lib/tls/include.am
@@ -10,6 +10,16 @@ src_lib_libtor_tls_a_SOURCES = \
src/lib/tls/tortls.c \
src/lib/tls/x509.c
+if USE_NSS
+src_lib_libtor_tls_a_SOURCES += \
+ src/lib/tls/tortls_nss.c \
+ src/lib/tls/x509_nss.c
+else
+src_lib_libtor_tls_a_SOURCES += \
+ src/lib/tls/tortls_openssl.c \
+ src/lib/tls/x509_openssl.c
+endif
+
src_lib_libtor_tls_a_CFLAGS = $(AM_CFLAGS) $(TOR_CFLAGS_CRYPTLIB)
src_lib_libtor_tls_testing_a_SOURCES = \
@@ -22,4 +32,6 @@ noinst_HEADERS += \
src/lib/tls/ciphers.inc \
src/lib/tls/buffers_tls.h \
src/lib/tls/tortls.h \
+ src/lib/tls/tortls_internal.h \
+ src/lib/tls/tortls_st.h \
src/lib/tls/x509.h
diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c
index 4c5dedb57..4b35177df 100644
--- a/src/lib/tls/tortls.c
+++ b/src/lib/tls/tortls.c
@@ -3,146 +3,13 @@
* Copyright (c) 2007-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
-/**
- * \file tortls.c
- * \brief Wrapper functions to present a consistent interface to
- * TLS, SSL, and X.509 functions from OpenSSL.
- **/
-
-/* (Unlike other tor functions, these
- * are prefixed with tor_ in order to avoid conflicting with OpenSSL
- * functions and variables.)
- */
-
-#include "orconfig.h"
-
#define TORTLS_PRIVATE
-#define TORTLS_OPENSSL_PRIVATE
-
-#ifdef _WIN32 /*wrkard for dtls1.h >= 0.9.8m of "#include <winsock.h>"*/
- #include <winsock2.h>
- #include <ws2tcpip.h>
-#endif
-
-#include "lib/crypt_ops/crypto_cipher.h"
-#include "lib/crypt_ops/crypto_rand.h"
-#include "lib/crypt_ops/crypto_dh.h"
-#include "lib/crypt_ops/crypto_util.h"
-#include "lib/crypt_ops/compat_openssl.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. */
-DISABLE_GCC_WARNING(redundant-decls)
-
-#include <openssl/opensslv.h>
-
-#ifdef OPENSSL_NO_EC
-#error "We require OpenSSL with ECC support"
-#endif
-
-#include <openssl/ssl.h>
-#include <openssl/ssl3.h>
-#include <openssl/err.h>
-#include <openssl/tls1.h>
-#include <openssl/asn1.h>
-#include <openssl/bio.h>
-#include <openssl/bn.h>
-#include <openssl/rsa.h>
-
-ENABLE_GCC_WARNING(redundant-decls)
-
#include "lib/tls/tortls.h"
#include "lib/tls/tortls_st.h"
-#include "lib/log/log.h"
+#include "lib/tls/tortls_internal.h"
#include "lib/log/util_bug.h"
-#include "lib/container/smartlist.h"
-#include "lib/string/compat_string.h"
-#include "lib/string/printf.h"
-#include "lib/net/socket.h"
#include "lib/intmath/cmp.h"
-#include "lib/ctime/di_ops.h"
-#include "lib/encoding/time_fmt.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "lib/arch/bytes.h"
-
-/* Copied from or.h */
-#define LEGAL_NICKNAME_CHARACTERS \
- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
-
-/** How long do identity certificates live? (sec) */
-#define IDENTITY_CERT_LIFETIME (365*24*60*60)
-
-#define ADDR(tls) (((tls) && (tls)->address) ? tls->address : "peer")
-
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,0,'f')
-/* This is a version of OpenSSL before 1.0.0f. It does not have
- * the CVE-2011-4576 fix, and as such it can't use RELEASE_BUFFERS and
- * SSL3 safely at the same time.
- */
-#define DISABLE_SSL3_HANDSHAKE
-#endif /* OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,0,'f') */
-
-/* We redefine these so that we can run correctly even if the vendor gives us
- * a version of OpenSSL that does not match its header files. (Apple: I am
- * looking at you.)
- */
-#ifndef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
-#define SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0x00040000L
-#endif
-#ifndef SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
-#define SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0x0010
-#endif
-
-/** Return values for tor_tls_classify_client_ciphers.
- *
- * @{
- */
-/** An error occurred when examining the client ciphers */
-#define CIPHERS_ERR -1
-/** The client cipher list indicates that a v1 handshake was in use. */
-#define CIPHERS_V1 1
-/** The client cipher list indicates that the client is using the v2 or the
- * v3 handshake, but that it is (probably!) lying about what ciphers it
- * supports */
-#define CIPHERS_V2 2
-/** The client cipher list indicates that the client is using the v2 or the
- * v3 handshake, and that it is telling the truth about what ciphers it
- * supports */
-#define CIPHERS_UNRESTRICTED 3
-/** @} */
-
-/** The ex_data index in which we store a pointer to an SSL object's
- * corresponding tor_tls_t object. */
-STATIC int tor_tls_object_ex_data_index = -1;
-
-/** Helper: Allocate tor_tls_object_ex_data_index. */
-STATIC void
-tor_tls_allocate_tor_tls_object_ex_data_index(void)
-{
- if (tor_tls_object_ex_data_index == -1) {
- tor_tls_object_ex_data_index =
- SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
- tor_assert(tor_tls_object_ex_data_index != -1);
- }
-}
-
-/** Helper: given a SSL* pointer, return the tor_tls_t object using that
- * pointer. */
-STATIC tor_tls_t *
-tor_tls_get_by_ssl(const SSL *ssl)
-{
- tor_tls_t *result = SSL_get_ex_data(ssl, tor_tls_object_ex_data_index);
- if (result)
- tor_assert(result->magic == TOR_TLS_MAGIC);
- return result;
-}
-
-static void tor_tls_context_decref(tor_tls_context_t *ctx);
-static void tor_tls_context_incref(tor_tls_context_t *ctx);
/** Global TLS contexts. We keep them here because nobody else needs
* to touch them.
@@ -152,126 +19,37 @@ STATIC tor_tls_context_t *server_tls_context = NULL;
STATIC tor_tls_context_t *client_tls_context = NULL;
/**@}*/
-/** True iff tor_tls_init() has been called. */
-static int tls_library_is_initialized = 0;
-
-/* Module-internal error codes. */
-#define TOR_TLS_SYSCALL_ (MIN_TOR_TLS_ERROR_VAL_ - 2)
-#define TOR_TLS_ZERORETURN_ (MIN_TOR_TLS_ERROR_VAL_ - 1)
-
-/** Write a description of the current state of <b>tls</b> into the
- * <b>sz</b>-byte buffer at <b>buf</b>. */
-void
-tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz)
+/**
+ * Return the appropriate TLS context.
+ */
+tor_tls_context_t *
+tor_tls_context_get(int is_server)
{
- const char *ssl_state;
- const char *tortls_state;
-
- if (PREDICT_UNLIKELY(!tls || !tls->ssl)) {
- strlcpy(buf, "(No SSL object)", sz);
- return;
- }
-
- ssl_state = SSL_state_string_long(tls->ssl);
- switch (tls->state) {
-#define CASE(st) case TOR_TLS_ST_##st: tortls_state = " in "#st ; break
- CASE(HANDSHAKE);
- CASE(OPEN);
- CASE(GOTCLOSE);
- CASE(SENTCLOSE);
- CASE(CLOSED);
- CASE(RENEGOTIATE);
-#undef CASE
- case TOR_TLS_ST_BUFFEREVENT:
- tortls_state = "";
- break;
- default:
- tortls_state = " in unknown TLS state";
- break;
- }
-
- tor_snprintf(buf, sz, "%s%s", ssl_state, tortls_state);
+ return is_server ? server_tls_context : client_tls_context;
}
-/** Log a single error <b>err</b> as returned by ERR_get_error(), which was
- * received while performing an operation <b>doing</b> on <b>tls</b>. Log
- * the message at <b>severity</b>, in log domain <b>domain</b>. */
+/** Increase the reference count of <b>ctx</b>. */
void
-tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
- int severity, int domain, const char *doing)
+tor_tls_context_incref(tor_tls_context_t *ctx)
{
- const char *state = NULL, *addr;
- const char *msg, *lib, *func;
-
- state = (tls && tls->ssl)?SSL_state_string_long(tls->ssl):"---";
-
- addr = tls ? tls->address : NULL;
-
- /* Some errors are known-benign, meaning they are the fault of the other
- * side of the connection. The caller doesn't know this, so override the
- * priority for those cases. */
- switch (ERR_GET_REASON(err)) {
- case SSL_R_HTTP_REQUEST:
- case SSL_R_HTTPS_PROXY_REQUEST:
- case SSL_R_RECORD_LENGTH_MISMATCH:
-#ifndef OPENSSL_1_1_API
- case SSL_R_RECORD_TOO_LARGE:
-#endif
- case SSL_R_UNKNOWN_PROTOCOL:
- case SSL_R_UNSUPPORTED_PROTOCOL:
- severity = LOG_INFO;
- break;
- default:
- break;
- }
-
- msg = (const char*)ERR_reason_error_string(err);
- lib = (const char*)ERR_lib_error_string(err);
- func = (const char*)ERR_func_error_string(err);
- if (!msg) msg = "(null)";
- if (!lib) lib = "(null)";
- if (!func) func = "(null)";
- if (doing) {
- tor_log(severity, domain, "TLS error while %s%s%s: %s (in %s:%s:%s)",
- doing, addr?" with ":"", addr?addr:"",
- msg, lib, func, state);
- } else {
- tor_log(severity, domain, "TLS error%s%s: %s (in %s:%s:%s)",
- addr?" with ":"", addr?addr:"",
- msg, lib, func, state);
- }
+ ++ctx->refcnt;
}
-/** 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.
- */
+/** Free all global TLS structures. */
void
-tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing)
+tor_tls_free_all(void)
{
- unsigned long err;
+ check_no_tls_errors();
- while ((err = ERR_get_error()) != 0) {
- tor_tls_log_one_error(tls, err, severity, domain, doing);
+ if (server_tls_context) {
+ tor_tls_context_t *ctx = server_tls_context;
+ server_tls_context = NULL;
+ tor_tls_context_decref(ctx);
}
-}
-
-/** Convert an errno (or a WSAerrno on windows) into a TOR_TLS_* error
- * code. */
-STATIC int
-tor_errno_to_tls_error(int e)
-{
- switch (e) {
- case SOCK_ERRNO(ECONNRESET): // most common
- return TOR_TLS_ERROR_CONNRESET;
- case SOCK_ERRNO(ETIMEDOUT):
- return TOR_TLS_ERROR_TIMEOUT;
- case SOCK_ERRNO(EHOSTUNREACH):
- case SOCK_ERRNO(ENETUNREACH):
- return TOR_TLS_ERROR_NO_ROUTE;
- case SOCK_ERRNO(ECONNREFUSED):
- return TOR_TLS_ERROR_CONNREFUSED; // least common
- default:
- return TOR_TLS_ERROR_MISC;
+ if (client_tls_context) {
+ tor_tls_context_t *ctx = client_tls_context;
+ client_tls_context = NULL;
+ tor_tls_context_decref(ctx);
}
}
@@ -295,331 +73,6 @@ tor_tls_err_to_string(int err)
}
}
-#define CATCH_SYSCALL 1
-#define CATCH_ZERO 2
-
-/** Given a TLS object and the result of an SSL_* call, use
- * SSL_get_error to determine whether an error has occurred, and if so
- * which one. Return one of TOR_TLS_{DONE|WANTREAD|WANTWRITE|ERROR}.
- * If extra&CATCH_SYSCALL is true, return TOR_TLS_SYSCALL_ instead of
- * reporting syscall errors. If extra&CATCH_ZERO is true, return
- * TOR_TLS_ZERORETURN_ instead of reporting zero-return errors.
- *
- * If an error has occurred, log it at level <b>severity</b> and describe the
- * current action as <b>doing</b>.
- */
-STATIC int
-tor_tls_get_error(tor_tls_t *tls, int r, int extra,
- const char *doing, int severity, int domain)
-{
- int err = SSL_get_error(tls->ssl, r);
- int tor_error = TOR_TLS_ERROR_MISC;
- switch (err) {
- case SSL_ERROR_NONE:
- return TOR_TLS_DONE;
- case SSL_ERROR_WANT_READ:
- return TOR_TLS_WANTREAD;
- case SSL_ERROR_WANT_WRITE:
- return TOR_TLS_WANTWRITE;
- case SSL_ERROR_SYSCALL:
- if (extra&CATCH_SYSCALL)
- return TOR_TLS_SYSCALL_;
- if (r == 0) {
- tor_log(severity, LD_NET, "TLS error: unexpected close while %s (%s)",
- doing, SSL_state_string_long(tls->ssl));
- tor_error = TOR_TLS_ERROR_IO;
- } else {
- int e = tor_socket_errno(tls->socket);
- tor_log(severity, LD_NET,
- "TLS error: <syscall error while %s> (errno=%d: %s; state=%s)",
- doing, e, tor_socket_strerror(e),
- SSL_state_string_long(tls->ssl));
- tor_error = tor_errno_to_tls_error(e);
- }
- tls_log_errors(tls, severity, domain, doing);
- return tor_error;
- case SSL_ERROR_ZERO_RETURN:
- if (extra&CATCH_ZERO)
- return TOR_TLS_ZERORETURN_;
- tor_log(severity, LD_NET, "TLS connection closed while %s in state %s",
- doing, SSL_state_string_long(tls->ssl));
- tls_log_errors(tls, severity, domain, doing);
- return TOR_TLS_CLOSE;
- default:
- tls_log_errors(tls, severity, domain, doing);
- return TOR_TLS_ERROR_MISC;
- }
-}
-
-/** Initialize OpenSSL, unless it has already been initialized.
- */
-void
-tor_tls_init(void)
-{
- check_no_tls_errors();
-
- if (!tls_library_is_initialized) {
-#ifdef OPENSSL_1_1_API
- OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL);
-#else
- SSL_library_init();
- SSL_load_error_strings();
-#endif
-
-#if (SIZEOF_VOID_P >= 8 && \
- OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,1))
- long version = OpenSSL_version_num();
-
- /* LCOV_EXCL_START : we can't test these lines on the same machine */
- if (version >= OPENSSL_V_SERIES(1,0,1)) {
- /* Warn if we could *almost* be running with much faster ECDH.
- If we're built for a 64-bit target, using OpenSSL 1.0.1, but we
- don't have one of the built-in __uint128-based speedups, we are
- just one build operation away from an accelerated handshake.
-
- (We could be looking at OPENSSL_NO_EC_NISTP_64_GCC_128 instead of
- doing this test, but that gives compile-time options, not runtime
- behavior.)
- */
- EC_KEY *key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
- const EC_GROUP *g = key ? EC_KEY_get0_group(key) : NULL;
- const EC_METHOD *m = g ? EC_GROUP_method_of(g) : NULL;
- const int warn = (m == EC_GFp_simple_method() ||
- m == EC_GFp_mont_method() ||
- m == EC_GFp_nist_method());
- EC_KEY_free(key);
-
- if (warn)
- log_notice(LD_GENERAL, "We were built to run on a 64-bit CPU, with "
- "OpenSSL 1.0.1 or later, but with a version of OpenSSL "
- "that apparently lacks accelerated support for the NIST "
- "P-224 and P-256 groups. Building openssl with such "
- "support (using the enable-ec_nistp_64_gcc_128 option "
- "when configuring it) would make ECDH much faster.");
- }
- /* LCOV_EXCL_STOP */
-#endif /* (SIZEOF_VOID_P >= 8 && ... */
-
- tor_tls_allocate_tor_tls_object_ex_data_index();
-
- tls_library_is_initialized = 1;
- }
-}
-
-/** Free all global TLS structures. */
-void
-tor_tls_free_all(void)
-{
- check_no_tls_errors();
-
- if (server_tls_context) {
- tor_tls_context_t *ctx = server_tls_context;
- server_tls_context = NULL;
- tor_tls_context_decref(ctx);
- }
- if (client_tls_context) {
- tor_tls_context_t *ctx = client_tls_context;
- client_tls_context = NULL;
- tor_tls_context_decref(ctx);
- }
-}
-
-/** We need to give OpenSSL a callback to verify certificates. This is
- * it: We always accept peer certs and complete the handshake. We
- * don't validate them until later.
- */
-STATIC int
-always_accept_verify_cb(int preverify_ok,
- X509_STORE_CTX *x509_ctx)
-{
- (void) preverify_ok;
- (void) x509_ctx;
- return 1;
-}
-
-/** 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[] =
-#ifdef TLS1_3_TXT_AES_128_GCM_SHA256
- /* This one can never actually get selected, since if the client lists it,
- * we will assume that the client is honest, and not use this list.
- * Nonetheless we list it if it's available, so that the server doesn't
- * conclude that it has no valid ciphers if it's running with TLS1.3.
- */
- TLS1_3_TXT_AES_128_GCM_SHA256 ":"
-#endif
- TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":"
- TLS1_TXT_DHE_RSA_WITH_AES_128_SHA;
-
-/** List of ciphers that servers should select from when we actually have
- * our choice of what cipher to use. */
-static const char UNRESTRICTED_SERVER_CIPHER_LIST[] =
- /* Here are the TLS 1.3 ciphers we like, in the order we prefer. */
-#ifdef TLS1_3_TXT_AES_256_GCM_SHA384
- TLS1_3_TXT_AES_256_GCM_SHA384 ":"
-#endif
-#ifdef TLS1_3_TXT_CHACHA20_POLY1305_SHA256
- TLS1_3_TXT_CHACHA20_POLY1305_SHA256 ":"
-#endif
-#ifdef TLS1_3_TXT_AES_128_GCM_SHA256
- TLS1_3_TXT_AES_128_GCM_SHA256 ":"
-#endif
-#ifdef TLS1_3_TXT_AES_128_CCM_SHA256
- TLS1_3_TXT_AES_128_CCM_SHA256 ":"
-#endif
-
- /* This list is autogenerated with the gen_server_ciphers.py script;
- * don't hand-edit it. */
-#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ":"
-#endif
-#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ":"
-#endif
-#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384
- TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384 ":"
-#endif
-#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256
- TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256 ":"
-#endif
-#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA
- TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA ":"
-#endif
-#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA
- TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA ":"
-#endif
-#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_GCM_SHA384
- TLS1_TXT_DHE_RSA_WITH_AES_256_GCM_SHA384 ":"
-#endif
-#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256
- TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256 ":"
-#endif
-#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_CCM
- TLS1_TXT_DHE_RSA_WITH_AES_256_CCM ":"
-#endif
-#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_CCM
- TLS1_TXT_DHE_RSA_WITH_AES_128_CCM ":"
-#endif
-#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_SHA256
- TLS1_TXT_DHE_RSA_WITH_AES_256_SHA256 ":"
-#endif
-#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_SHA256
- TLS1_TXT_DHE_RSA_WITH_AES_128_SHA256 ":"
-#endif
- /* Required */
- TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":"
- /* Required */
- TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":"
-#ifdef TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305
- TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305 ":"
-#endif
-#ifdef TLS1_TXT_DHE_RSA_WITH_CHACHA20_POLY1305
- TLS1_TXT_DHE_RSA_WITH_CHACHA20_POLY1305
-#endif
- ;
-
-/* Note: to set up your own private testing network with link crypto
- * disabled, set your Tors' cipher list to
- * (SSL3_TXT_RSA_NULL_SHA). If you do this, you won't be able to communicate
- * with any of the "real" Tors, though. */
-
-#define CIPHER(id, name) name ":"
-#define XCIPHER(id, name)
-/** List of ciphers that clients should advertise, omitting items that
- * our OpenSSL doesn't know about. */
-static const char CLIENT_CIPHER_LIST[] =
-#include "ciphers.inc"
- /* Tell it not to use SSLv2 ciphers, so that it can select an SSLv3 version
- * of any cipher we say. */
- "!SSLv2"
- ;
-#undef CIPHER
-#undef XCIPHER
-
-/** Remove a reference to <b>ctx</b>, and free it if it has no more
- * references. */
-static void
-tor_tls_context_decref(tor_tls_context_t *ctx)
-{
- tor_assert(ctx);
- if (--ctx->refcnt == 0) {
- SSL_CTX_free(ctx->ctx);
- tor_x509_cert_free(ctx->my_link_cert);
- tor_x509_cert_free(ctx->my_id_cert);
- tor_x509_cert_free(ctx->my_auth_cert);
- crypto_pk_free(ctx->link_key);
- crypto_pk_free(ctx->auth_key);
- /* LCOV_EXCL_BR_START since ctx will never be NULL here */
- tor_free(ctx);
- /* LCOV_EXCL_BR_STOP */
- }
-}
-
-/** Set *<b>link_cert_out</b> and *<b>id_cert_out</b> to the link certificate
- * and ID certificate that we're currently using for our V3 in-protocol
- * handshake's certificate chain. If <b>server</b> is true, provide the certs
- * that we use in server mode (auth, ID); otherwise, provide the certs that we
- * use in client mode. (link, ID) */
-int
-tor_tls_get_my_certs(int server,
- const tor_x509_cert_t **link_cert_out,
- const tor_x509_cert_t **id_cert_out)
-{
- tor_tls_context_t *ctx = server ? server_tls_context : client_tls_context;
- if (! ctx)
- return -1;
- if (link_cert_out)
- *link_cert_out = server ? ctx->my_link_cert : ctx->my_auth_cert;
- if (id_cert_out)
- *id_cert_out = ctx->my_id_cert;
- return 0;
-}
-
-/**
- * Return the authentication key that we use to authenticate ourselves as a
- * client in the V3 in-protocol handshake.
- */
-crypto_pk_t *
-tor_tls_get_my_client_auth_key(void)
-{
- if (! client_tls_context)
- return NULL;
- return client_tls_context->auth_key;
-}
-
-/** 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.
- */
-MOCK_IMPL(int,
-tor_tls_cert_matches_key,(const tor_tls_t *tls, const tor_x509_cert_t *cert))
-{
- X509 *peercert = SSL_get_peer_certificate(tls->ssl);
- EVP_PKEY *link_key = NULL, *cert_key = NULL;
- int result;
-
- if (!peercert)
- return 0;
- link_key = X509_get_pubkey(peercert);
- cert_key = X509_get_pubkey((X509 *)tor_x509_cert_get_impl(cert));
-
- result = link_key && cert_key && EVP_PKEY_cmp(cert_key, link_key) == 1;
-
- X509_free(peercert);
- if (link_key)
- EVP_PKEY_free(link_key);
- if (cert_key)
- EVP_PKEY_free(cert_key);
-
- return result;
-}
-
-/** Increase the reference count of <b>ctx</b>. */
-static void
-tor_tls_context_incref(tor_tls_context_t *ctx)
-{
- ++ctx->refcnt;
-}
-
/** Create new global client and server TLS contexts.
*
* If <b>server_identity</b> is NULL, this will not generate a server
@@ -686,687 +139,6 @@ tor_tls_context_init(unsigned flags,
return MIN(rv1, rv2);
}
-/** Create a new global TLS context.
- *
- * You can call this function multiple times. Each time you call it,
- * it generates new certificates; all new connections will use
- * the new SSL context.
- */
-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)
-{
- tor_tls_context_t *new_ctx = tor_tls_context_new(identity,
- key_lifetime,
- flags,
- is_client);
- tor_tls_context_t *old_ctx = *ppcontext;
-
- if (new_ctx != NULL) {
- *ppcontext = new_ctx;
-
- /* Free the old context if one existed. */
- if (old_ctx != NULL) {
- /* This is safe even if there are open connections: we reference-
- * count tor_tls_context_t objects. */
- tor_tls_context_decref(old_ctx);
- }
- }
-
- return ((new_ctx != NULL) ? 0 : -1);
-}
-
-/** The group we should use for ecdhe when none was selected. */
-#define NID_tor_default_ecdhe_group NID_X9_62_prime256v1
-
-#define RSA_LINK_KEY_BITS 2048
-
-/** Create a new TLS context for use with Tor TLS handshakes.
- * <b>identity</b> should be set to the identity key used to sign the
- * certificate.
- */
-STATIC tor_tls_context_t *
-tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
- unsigned flags, int is_client)
-{
- crypto_pk_t *rsa = NULL, *rsa_auth = NULL;
- EVP_PKEY *pkey = NULL;
- tor_tls_context_t *result = NULL;
- X509 *cert = NULL, *idcert = NULL, *authcert = NULL;
- char *nickname = NULL, *nn2 = NULL;
-
- tor_tls_init();
- nickname = crypto_random_hostname(8, 20, "www.", ".net");
-#ifdef DISABLE_V3_LINKPROTO_SERVERSIDE
- nn2 = crypto_random_hostname(8, 20, "www.", ".net");
-#else
- nn2 = crypto_random_hostname(8, 20, "www.", ".com");
-#endif
-
- /* Generate short-term RSA key for use with TLS. */
- if (!(rsa = crypto_pk_new()))
- goto error;
- if (crypto_pk_generate_key_with_bits(rsa, RSA_LINK_KEY_BITS)<0)
- goto error;
- if (!is_client) {
- /* Generate short-term RSA key for use in the in-protocol ("v3")
- * authentication handshake. */
- if (!(rsa_auth = crypto_pk_new()))
- goto error;
- if (crypto_pk_generate_key(rsa_auth)<0)
- goto error;
- /* Create a link certificate signed by identity key. */
- cert = tor_tls_create_certificate(rsa, identity, nickname, nn2,
- key_lifetime);
- /* Create self-signed certificate for identity key. */
- idcert = tor_tls_create_certificate(identity, identity, nn2, nn2,
- IDENTITY_CERT_LIFETIME);
- /* Create an authentication certificate signed by identity key. */
- authcert = tor_tls_create_certificate(rsa_auth, identity, nickname, nn2,
- key_lifetime);
- if (!cert || !idcert || !authcert) {
- log_warn(LD_CRYPTO, "Error creating certificate");
- goto error;
- }
- }
-
- result = tor_malloc_zero(sizeof(tor_tls_context_t));
- result->refcnt = 1;
- if (!is_client) {
- result->my_link_cert = tor_x509_cert_new(X509_dup(cert));
- result->my_id_cert = tor_x509_cert_new(X509_dup(idcert));
- result->my_auth_cert = tor_x509_cert_new(X509_dup(authcert));
- if (!result->my_link_cert || !result->my_id_cert || !result->my_auth_cert)
- goto error;
- result->link_key = crypto_pk_dup_key(rsa);
- result->auth_key = crypto_pk_dup_key(rsa_auth);
- }
-
-#if 0
- /* Tell OpenSSL to only use TLS1. This may have subtly different results
- * from SSLv23_method() with SSLv2 and SSLv3 disabled, so we need to do some
- * investigation before we consider adjusting it. It should be compatible
- * with existing Tors. */
- if (!(result->ctx = SSL_CTX_new(TLSv1_method())))
- goto error;
-#endif /* 0 */
-
- /* Tell OpenSSL to use TLS 1.0 or later but not SSL2 or SSL3. */
-#ifdef HAVE_TLS_METHOD
- if (!(result->ctx = SSL_CTX_new(TLS_method())))
- goto error;
-#else
- if (!(result->ctx = SSL_CTX_new(SSLv23_method())))
- goto error;
-#endif /* defined(HAVE_TLS_METHOD) */
- SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv2);
- SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv3);
-
- /* Prefer the server's ordering of ciphers: the client's ordering has
- * historically been chosen for fingerprinting resistance. */
- SSL_CTX_set_options(result->ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
-
- /* Disable TLS tickets if they're supported. We never want to use them;
- * using them can make our perfect forward secrecy a little worse, *and*
- * create an opportunity to fingerprint us (since it's unusual to use them
- * with TLS sessions turned off).
- *
- * In 0.2.4, clients advertise support for them though, to avoid a TLS
- * distinguishability vector. This can give us worse PFS, though, if we
- * get a server that doesn't set SSL_OP_NO_TICKET. With luck, there will
- * be few such servers by the time 0.2.4 is more stable.
- */
-#ifdef SSL_OP_NO_TICKET
- if (! is_client) {
- SSL_CTX_set_options(result->ctx, SSL_OP_NO_TICKET);
- }
-#endif
-
- SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_DH_USE);
- SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_ECDH_USE);
-
-#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
- SSL_CTX_set_options(result->ctx,
- SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
-#endif
- /* Yes, we know what we are doing here. No, we do not treat a renegotiation
- * as authenticating any earlier-received data.
- */
- {
- SSL_CTX_set_options(result->ctx,
- SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
- }
-
- /* Don't actually allow compression; it uses RAM and time, it makes TLS
- * vulnerable to CRIME-style attacks, and most of the data we transmit over
- * TLS is encrypted (and therefore uncompressible) anyway. */
-#ifdef SSL_OP_NO_COMPRESSION
- SSL_CTX_set_options(result->ctx, SSL_OP_NO_COMPRESSION);
-#endif
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0)
-#ifndef OPENSSL_NO_COMP
- if (result->ctx->comp_methods)
- result->ctx->comp_methods = NULL;
-#endif
-#endif /* OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0) */
-
-#ifdef SSL_MODE_RELEASE_BUFFERS
- SSL_CTX_set_mode(result->ctx, SSL_MODE_RELEASE_BUFFERS);
-#endif
- if (! is_client) {
- if (cert && !SSL_CTX_use_certificate(result->ctx,cert))
- goto error;
- X509_free(cert); /* We just added a reference to cert. */
- cert=NULL;
- if (idcert) {
- X509_STORE *s = SSL_CTX_get_cert_store(result->ctx);
- tor_assert(s);
- X509_STORE_add_cert(s, idcert);
- X509_free(idcert); /* The context now owns the reference to idcert */
- idcert = NULL;
- }
- }
- SSL_CTX_set_session_cache_mode(result->ctx, SSL_SESS_CACHE_OFF);
- if (!is_client) {
- tor_assert(rsa);
- if (!(pkey = crypto_pk_get_openssl_evp_pkey_(rsa,1)))
- goto error;
- if (!SSL_CTX_use_PrivateKey(result->ctx, pkey))
- goto error;
- EVP_PKEY_free(pkey);
- pkey = NULL;
- if (!SSL_CTX_check_private_key(result->ctx))
- goto error;
- }
- {
- DH *dh = crypto_dh_new_openssl_tls();
- tor_assert(dh);
- SSL_CTX_set_tmp_dh(result->ctx, dh);
- DH_free(dh);
- }
- if (! is_client) {
- int nid;
- EC_KEY *ec_key;
- if (flags & TOR_TLS_CTX_USE_ECDHE_P224)
- nid = NID_secp224r1;
- else if (flags & TOR_TLS_CTX_USE_ECDHE_P256)
- nid = NID_X9_62_prime256v1;
- else
- nid = NID_tor_default_ecdhe_group;
- /* Use P-256 for ECDHE. */
- ec_key = EC_KEY_new_by_curve_name(nid);
- if (ec_key != NULL) /*XXXX Handle errors? */
- SSL_CTX_set_tmp_ecdh(result->ctx, ec_key);
- EC_KEY_free(ec_key);
- }
- SSL_CTX_set_verify(result->ctx, SSL_VERIFY_PEER,
- always_accept_verify_cb);
- /* let us realloc bufs that we're writing from */
- SSL_CTX_set_mode(result->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
-
- if (rsa)
- crypto_pk_free(rsa);
- if (rsa_auth)
- crypto_pk_free(rsa_auth);
- X509_free(authcert);
- tor_free(nickname);
- tor_free(nn2);
- return result;
-
- error:
- tls_log_errors(NULL, LOG_WARN, LD_NET, "creating TLS context");
- tor_free(nickname);
- tor_free(nn2);
- if (pkey)
- EVP_PKEY_free(pkey);
- if (rsa)
- crypto_pk_free(rsa);
- if (rsa_auth)
- crypto_pk_free(rsa_auth);
- if (result)
- tor_tls_context_decref(result);
- if (cert)
- X509_free(cert);
- if (idcert)
- X509_free(idcert);
- if (authcert)
- X509_free(authcert);
- return NULL;
-}
-
-/** Invoked when a TLS state changes: log the change at severity 'debug' */
-STATIC void
-tor_tls_debug_state_callback(const SSL *ssl, int type, int val)
-{
- /* LCOV_EXCL_START since this depends on whether debug is captured or not */
- log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].",
- ssl, SSL_state_string_long(ssl), type, val);
- /* LCOV_EXCL_STOP */
-}
-
-/* Return the name of the negotiated ciphersuite in use on <b>tls</b> */
-const char *
-tor_tls_get_ciphersuite_name(tor_tls_t *tls)
-{
- return SSL_get_cipher(tls->ssl);
-}
-
-/* Here's the old V2 cipher list we sent from 0.2.1.1-alpha up to
- * 0.2.3.17-beta. If a client is using this list, we can't believe the ciphers
- * that it claims to support. We'll prune this list to remove the ciphers
- * *we* don't recognize. */
-STATIC uint16_t v2_cipher_list[] = {
- 0xc00a, /* TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA */
- 0xc014, /* TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA */
- 0x0039, /* TLS1_TXT_DHE_RSA_WITH_AES_256_SHA */
- 0x0038, /* TLS1_TXT_DHE_DSS_WITH_AES_256_SHA */
- 0xc00f, /* TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA */
- 0xc005, /* TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA */
- 0x0035, /* TLS1_TXT_RSA_WITH_AES_256_SHA */
- 0xc007, /* TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA */
- 0xc009, /* TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA */
- 0xc011, /* TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA */
- 0xc013, /* TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA */
- 0x0033, /* TLS1_TXT_DHE_RSA_WITH_AES_128_SHA */
- 0x0032, /* TLS1_TXT_DHE_DSS_WITH_AES_128_SHA */
- 0xc00c, /* TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA */
- 0xc00e, /* TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA */
- 0xc002, /* TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA */
- 0xc004, /* TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA */
- 0x0004, /* SSL3_TXT_RSA_RC4_128_MD5 */
- 0x0005, /* SSL3_TXT_RSA_RC4_128_SHA */
- 0x002f, /* TLS1_TXT_RSA_WITH_AES_128_SHA */
- 0xc008, /* TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA */
- 0xc012, /* TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA */
- 0x0016, /* SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA */
- 0x0013, /* SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA */
- 0xc00d, /* TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA */
- 0xc003, /* TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA */
- 0xfeff, /* SSL3_TXT_RSA_FIPS_WITH_3DES_EDE_CBC_SHA */
- 0x000a, /* SSL3_TXT_RSA_DES_192_CBC3_SHA */
- 0
-};
-/** Have we removed the unrecognized ciphers from v2_cipher_list yet? */
-static int v2_cipher_list_pruned = 0;
-
-/** Return 0 if <b>m</b> does not support the cipher with ID <b>cipher</b>;
- * return 1 if it does support it, or if we have no way to tell. */
-STATIC int
-find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher)
-{
- const SSL_CIPHER *c;
-#ifdef HAVE_SSL_CIPHER_FIND
- (void) m;
- {
- unsigned char cipherid[3];
- tor_assert(ssl);
- set_uint16(cipherid, tor_htons(cipher));
- cipherid[2] = 0; /* If ssl23_get_cipher_by_char finds no cipher starting
- * with a two-byte 'cipherid', it may look for a v2
- * cipher with the appropriate 3 bytes. */
- c = SSL_CIPHER_find((SSL*)ssl, cipherid);
- if (c)
- tor_assert((SSL_CIPHER_get_id(c) & 0xffff) == cipher);
- return c != NULL;
- }
-#else /* !(defined(HAVE_SSL_CIPHER_FIND)) */
-
-# if defined(HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR)
- if (m && m->get_cipher_by_char) {
- unsigned char cipherid[3];
- set_uint16(cipherid, tor_htons(cipher));
- cipherid[2] = 0; /* If ssl23_get_cipher_by_char finds no cipher starting
- * with a two-byte 'cipherid', it may look for a v2
- * cipher with the appropriate 3 bytes. */
- c = m->get_cipher_by_char(cipherid);
- if (c)
- tor_assert((c->id & 0xffff) == cipher);
- return c != NULL;
- }
-#endif /* defined(HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR) */
-# ifndef OPENSSL_1_1_API
- if (m && m->get_cipher && m->num_ciphers) {
- /* It would seem that some of the "let's-clean-up-openssl" forks have
- * removed the get_cipher_by_char function. Okay, so now you get a
- * quadratic search.
- */
- int i;
- for (i = 0; i < m->num_ciphers(); ++i) {
- c = m->get_cipher(i);
- if (c && (c->id & 0xffff) == cipher) {
- return 1;
- }
- }
- return 0;
- }
-#endif /* !defined(OPENSSL_1_1_API) */
- (void) ssl;
- (void) m;
- (void) cipher;
- return 1; /* No way to search */
-#endif /* defined(HAVE_SSL_CIPHER_FIND) */
-}
-
-/** Remove from v2_cipher_list every cipher that we don't support, so that
- * comparing v2_cipher_list to a client's cipher list will give a sensible
- * result. */
-static void
-prune_v2_cipher_list(const SSL *ssl)
-{
- uint16_t *inp, *outp;
-#ifdef HAVE_TLS_METHOD
- const SSL_METHOD *m = TLS_method();
-#else
- const SSL_METHOD *m = SSLv23_method();
-#endif
-
- inp = outp = v2_cipher_list;
- while (*inp) {
- if (find_cipher_by_id(ssl, m, *inp)) {
- *outp++ = *inp++;
- } else {
- inp++;
- }
- }
- *outp = 0;
-
- v2_cipher_list_pruned = 1;
-}
-
-/** Examine the client cipher list in <b>ssl</b>, and determine what kind of
- * client it is. Return one of CIPHERS_ERR, CIPHERS_V1, CIPHERS_V2,
- * CIPHERS_UNRESTRICTED.
- **/
-STATIC int
-tor_tls_classify_client_ciphers(const SSL *ssl,
- STACK_OF(SSL_CIPHER) *peer_ciphers)
-{
- int i, res;
- tor_tls_t *tor_tls;
- if (PREDICT_UNLIKELY(!v2_cipher_list_pruned))
- prune_v2_cipher_list(ssl);
-
- tor_tls = tor_tls_get_by_ssl(ssl);
- if (tor_tls && tor_tls->client_cipher_list_type)
- return tor_tls->client_cipher_list_type;
-
- /* If we reached this point, we just got a client hello. See if there is
- * a cipher list. */
- if (!peer_ciphers) {
- log_info(LD_NET, "No ciphers on session");
- res = CIPHERS_ERR;
- goto done;
- }
- /* Now we need to see if there are any ciphers whose presence means we're
- * dealing with an updated Tor. */
- for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) {
- const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i);
- const char *ciphername = SSL_CIPHER_get_name(cipher);
- if (strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA) &&
- strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA) &&
- strcmp(ciphername, SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA) &&
- strcmp(ciphername, "(NONE)")) {
- log_debug(LD_NET, "Got a non-version-1 cipher called '%s'", ciphername);
- // return 1;
- goto v2_or_higher;
- }
- }
- res = CIPHERS_V1;
- goto done;
- v2_or_higher:
- {
- const uint16_t *v2_cipher = v2_cipher_list;
- for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) {
- const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i);
- uint16_t id = SSL_CIPHER_get_id(cipher) & 0xffff;
- if (id == 0x00ff) /* extended renegotiation indicator. */
- continue;
- if (!id || id != *v2_cipher) {
- res = CIPHERS_UNRESTRICTED;
- goto dump_ciphers;
- }
- ++v2_cipher;
- }
- if (*v2_cipher != 0) {
- res = CIPHERS_UNRESTRICTED;
- goto dump_ciphers;
- }
- res = CIPHERS_V2;
- }
-
- dump_ciphers:
- {
- smartlist_t *elts = smartlist_new();
- char *s;
- for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) {
- const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i);
- const char *ciphername = SSL_CIPHER_get_name(cipher);
- smartlist_add(elts, (char*)ciphername);
- }
- s = smartlist_join_strings(elts, ":", 0, NULL);
- log_debug(LD_NET, "Got a %s V2/V3 cipher list from %s. It is: '%s'",
- (res == CIPHERS_V2) ? "fictitious" : "real", ADDR(tor_tls), s);
- tor_free(s);
- smartlist_free(elts);
- }
- done:
- if (tor_tls)
- return tor_tls->client_cipher_list_type = res;
-
- return res;
-}
-
-/** Return true iff the cipher list suggested by the client for <b>ssl</b> is
- * a list that indicates that the client knows how to do the v2 TLS connection
- * handshake. */
-STATIC int
-tor_tls_client_is_using_v2_ciphers(const SSL *ssl)
-{
- STACK_OF(SSL_CIPHER) *ciphers;
-#ifdef HAVE_SSL_GET_CLIENT_CIPHERS
- ciphers = SSL_get_client_ciphers(ssl);
-#else
- SSL_SESSION *session;
- if (!(session = SSL_get_session((SSL *)ssl))) {
- log_info(LD_NET, "No session on TLS?");
- return CIPHERS_ERR;
- }
- ciphers = session->ciphers;
-#endif /* defined(HAVE_SSL_GET_CLIENT_CIPHERS) */
-
- return tor_tls_classify_client_ciphers(ssl, ciphers) >= CIPHERS_V2;
-}
-
-/** Invoked when we're accepting a connection on <b>ssl</b>, and the connection
- * changes state. We use this:
- * <ul><li>To alter the state of the handshake partway through, so we
- * do not send or request extra certificates in v2 handshakes.</li>
- * <li>To detect renegotiation</li></ul>
- */
-STATIC void
-tor_tls_server_info_callback(const SSL *ssl, int type, int val)
-{
- tor_tls_t *tls;
- (void) val;
-
- IF_BUG_ONCE(ssl == NULL) {
- return; // LCOV_EXCL_LINE
- }
-
- tor_tls_debug_state_callback(ssl, type, val);
-
- if (type != SSL_CB_ACCEPT_LOOP)
- return;
-
- OSSL_HANDSHAKE_STATE ssl_state = SSL_get_state(ssl);
- if (! STATE_IS_SW_SERVER_HELLO(ssl_state))
- return;
- tls = tor_tls_get_by_ssl(ssl);
- if (tls) {
- /* Check whether we're watching for renegotiates. If so, this is one! */
- if (tls->negotiated_callback)
- tls->got_renegotiate = 1;
- if (tls->server_handshake_count < 127) /*avoid any overflow possibility*/
- ++tls->server_handshake_count;
- } else {
- log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!");
- return;
- }
-
- /* Now check the cipher list. */
- if (tor_tls_client_is_using_v2_ciphers(ssl)) {
- if (tls->wasV2Handshake)
- return; /* We already turned this stuff off for the first handshake;
- * This is a renegotiation. */
-
- /* Yes, we're casting away the const from ssl. This is very naughty of us.
- * Let's hope openssl doesn't notice! */
-
- /* Set SSL_MODE_NO_AUTO_CHAIN to keep from sending back any extra certs. */
- SSL_set_mode((SSL*) ssl, SSL_MODE_NO_AUTO_CHAIN);
- /* Don't send a hello request. */
- SSL_set_verify((SSL*) ssl, SSL_VERIFY_NONE, NULL);
-
- if (tls) {
- tls->wasV2Handshake = 1;
- } else {
- /* LCOV_EXCL_START this line is not reachable */
- log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!");
- /* LCOV_EXCL_STOP */
- }
- }
-}
-
-/** Callback to get invoked on a server after we've read the list of ciphers
- * the client supports, but before we pick our own ciphersuite.
- *
- * We can't abuse an info_cb for this, since by the time one of the
- * client_hello info_cbs is called, we've already picked which ciphersuite to
- * use.
- *
- * Technically, this function is an abuse of this callback, since the point of
- * a session_secret_cb is to try to set up and/or verify a shared-secret for
- * authentication on the fly. But as long as we return 0, we won't actually be
- * setting up a shared secret, and all will be fine.
- */
-STATIC int
-tor_tls_session_secret_cb(SSL *ssl, void *secret, int *secret_len,
- STACK_OF(SSL_CIPHER) *peer_ciphers,
- CONST_IF_OPENSSL_1_1_API SSL_CIPHER **cipher,
- void *arg)
-{
- (void) secret;
- (void) secret_len;
- (void) peer_ciphers;
- (void) cipher;
- (void) arg;
-
- if (tor_tls_classify_client_ciphers(ssl, peer_ciphers) ==
- CIPHERS_UNRESTRICTED) {
- SSL_set_cipher_list(ssl, UNRESTRICTED_SERVER_CIPHER_LIST);
- }
-
- SSL_set_session_secret_cb(ssl, NULL, NULL);
-
- return 0;
-}
-static void
-tor_tls_setup_session_secret_cb(tor_tls_t *tls)
-{
- SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL);
-}
-
-/** Create a new TLS object from a file descriptor, and a flag to
- * determine whether it is functioning as a server.
- */
-tor_tls_t *
-tor_tls_new(int sock, int isServer)
-{
- BIO *bio = NULL;
- tor_tls_t *result = tor_malloc_zero(sizeof(tor_tls_t));
- tor_tls_context_t *context = isServer ? server_tls_context :
- client_tls_context;
- result->magic = TOR_TLS_MAGIC;
-
- check_no_tls_errors();
- tor_assert(context); /* make sure somebody made it first */
- if (!(result->ssl = SSL_new(context->ctx))) {
- tls_log_errors(NULL, LOG_WARN, LD_NET, "creating SSL object");
- tor_free(result);
- goto err;
- }
-
-#ifdef SSL_set_tlsext_host_name
- /* Browsers use the TLS hostname extension, so we should too. */
- if (!isServer) {
- char *fake_hostname = crypto_random_hostname(4,25, "www.",".com");
- SSL_set_tlsext_host_name(result->ssl, fake_hostname);
- tor_free(fake_hostname);
- }
-#endif /* defined(SSL_set_tlsext_host_name) */
-
- if (!SSL_set_cipher_list(result->ssl,
- isServer ? SERVER_CIPHER_LIST : CLIENT_CIPHER_LIST)) {
- tls_log_errors(NULL, LOG_WARN, LD_NET, "setting ciphers");
-#ifdef SSL_set_tlsext_host_name
- SSL_set_tlsext_host_name(result->ssl, NULL);
-#endif
- SSL_free(result->ssl);
- tor_free(result);
- goto err;
- }
- result->socket = sock;
- bio = BIO_new_socket(sock, BIO_NOCLOSE);
- if (! bio) {
- tls_log_errors(NULL, LOG_WARN, LD_NET, "opening BIO");
-#ifdef SSL_set_tlsext_host_name
- SSL_set_tlsext_host_name(result->ssl, NULL);
-#endif
- SSL_free(result->ssl);
- tor_free(result);
- goto err;
- }
- {
- int set_worked =
- SSL_set_ex_data(result->ssl, tor_tls_object_ex_data_index, result);
- if (!set_worked) {
- log_warn(LD_BUG,
- "Couldn't set the tls for an SSL*; connection will fail");
- }
- }
- SSL_set_bio(result->ssl, bio, bio);
- tor_tls_context_incref(context);
- result->context = context;
- result->state = TOR_TLS_ST_HANDSHAKE;
- result->isServer = isServer;
- result->wantwrite_n = 0;
- result->last_write_count = (unsigned long) BIO_number_written(bio);
- result->last_read_count = (unsigned long) BIO_number_read(bio);
- if (result->last_write_count || result->last_read_count) {
- log_warn(LD_NET, "Newly created BIO has read count %lu, write count %lu",
- result->last_read_count, result->last_write_count);
- }
- if (isServer) {
- SSL_set_info_callback(result->ssl, tor_tls_server_info_callback);
- } else {
- SSL_set_info_callback(result->ssl, tor_tls_debug_state_callback);
- }
-
- if (isServer)
- tor_tls_setup_session_secret_cb(result);
-
- goto done;
- err:
- result = NULL;
- done:
- /* Not expected to get called. */
- tls_log_errors(NULL, LOG_WARN, LD_NET, "creating tor_tls_t object");
- return result;
-}
-
/** Make future log messages about <b>tls</b> display the address
* <b>address</b>.
*/
@@ -1378,64 +150,6 @@ tor_tls_set_logged_address(tor_tls_t *tls, const char *address)
tls->address = tor_strdup(address);
}
-/** Set <b>cb</b> to be called with argument <b>arg</b> whenever <b>tls</b>
- * next gets a client-side renegotiate in the middle of a read. Do not
- * invoke this function until <em>after</em> initial handshaking is done!
- */
-void
-tor_tls_set_renegotiate_callback(tor_tls_t *tls,
- void (*cb)(tor_tls_t *, void *arg),
- void *arg)
-{
- tls->negotiated_callback = cb;
- tls->callback_arg = arg;
- tls->got_renegotiate = 0;
- if (cb) {
- SSL_set_info_callback(tls->ssl, tor_tls_server_info_callback);
- } else {
- SSL_set_info_callback(tls->ssl, tor_tls_debug_state_callback);
- }
-}
-
-/** If this version of openssl requires it, turn on renegotiation on
- * <b>tls</b>.
- */
-void
-tor_tls_unblock_renegotiation(tor_tls_t *tls)
-{
- /* Yes, we know what we are doing here. No, we do not treat a renegotiation
- * as authenticating any earlier-received data. */
- SSL_set_options(tls->ssl,
- SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
-}
-
-/** If this version of openssl supports it, turn off renegotiation on
- * <b>tls</b>. (Our protocol never requires this for security, but it's nice
- * to use belt-and-suspenders here.)
- */
-void
-tor_tls_block_renegotiation(tor_tls_t *tls)
-{
-#ifdef SUPPORT_UNSAFE_RENEGOTIATION_FLAG
- tls->ssl->s3->flags &= ~SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
-#else
- (void) tls;
-#endif
-}
-
-/** Assert that the flags that allow legacy renegotiation are still set */
-void
-tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls)
-{
-#if defined(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) && \
- SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION != 0
- long options = SSL_get_options(tls->ssl);
- tor_assert(0 != (options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION));
-#else
- (void) tls;
-#endif /* defined(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) && ... */
-}
-
/** Return whether this tls initiated the connect (client) or
* received it (server). */
int
@@ -1444,730 +158,3 @@ tor_tls_is_server(tor_tls_t *tls)
tor_assert(tls);
return tls->isServer;
}
-
-/** Release resources associated with a TLS object. Does not close the
- * underlying file descriptor.
- */
-void
-tor_tls_free_(tor_tls_t *tls)
-{
- if (!tls)
- return;
- tor_assert(tls->ssl);
- {
- size_t r,w;
- tor_tls_get_n_raw_bytes(tls,&r,&w); /* ensure written_by_tls is updated */
- }
-#ifdef SSL_set_tlsext_host_name
- SSL_set_tlsext_host_name(tls->ssl, NULL);
-#endif
- SSL_free(tls->ssl);
- tls->ssl = NULL;
- tls->negotiated_callback = NULL;
- if (tls->context)
- tor_tls_context_decref(tls->context);
- tor_free(tls->address);
- tls->magic = 0x99999999;
- tor_free(tls);
-}
-
-/** Underlying function for TLS reading. Reads up to <b>len</b>
- * characters from <b>tls</b> into <b>cp</b>. On success, returns the
- * number of characters read. On failure, returns TOR_TLS_ERROR,
- * TOR_TLS_CLOSE, TOR_TLS_WANTREAD, or TOR_TLS_WANTWRITE.
- */
-MOCK_IMPL(int,
-tor_tls_read,(tor_tls_t *tls, char *cp, size_t len))
-{
- int r, err;
- tor_assert(tls);
- tor_assert(tls->ssl);
- tor_assert(tls->state == TOR_TLS_ST_OPEN);
- tor_assert(len<INT_MAX);
- r = SSL_read(tls->ssl, cp, (int)len);
- if (r > 0) {
- if (tls->got_renegotiate) {
- /* Renegotiation happened! */
- log_info(LD_NET, "Got a TLS renegotiation from %s", ADDR(tls));
- if (tls->negotiated_callback)
- tls->negotiated_callback(tls, tls->callback_arg);
- tls->got_renegotiate = 0;
- }
- return r;
- }
- err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading", LOG_DEBUG, LD_NET);
- if (err == TOR_TLS_ZERORETURN_ || err == TOR_TLS_CLOSE) {
- log_debug(LD_NET,"read returned r=%d; TLS is closed",r);
- tls->state = TOR_TLS_ST_CLOSED;
- return TOR_TLS_CLOSE;
- } else {
- tor_assert(err != TOR_TLS_DONE);
- log_debug(LD_NET,"read returned r=%d, err=%d",r,err);
- return err;
- }
-}
-
-/** Total number of bytes that we've used TLS to send. Used to track TLS
- * overhead. */
-STATIC uint64_t total_bytes_written_over_tls = 0;
-/** Total number of bytes that TLS has put on the network for us. Used to
- * track TLS overhead. */
-STATIC uint64_t total_bytes_written_by_tls = 0;
-
-/** Underlying function for TLS writing. Write up to <b>n</b>
- * characters from <b>cp</b> onto <b>tls</b>. On success, returns the
- * number of characters written. On failure, returns TOR_TLS_ERROR,
- * TOR_TLS_WANTREAD, or TOR_TLS_WANTWRITE.
- */
-int
-tor_tls_write(tor_tls_t *tls, const char *cp, size_t n)
-{
- int r, err;
- tor_assert(tls);
- tor_assert(tls->ssl);
- tor_assert(tls->state == TOR_TLS_ST_OPEN);
- tor_assert(n < INT_MAX);
- if (n == 0)
- return 0;
- if (tls->wantwrite_n) {
- /* if WANTWRITE last time, we must use the _same_ n as before */
- tor_assert(n >= tls->wantwrite_n);
- log_debug(LD_NET,"resuming pending-write, (%d to flush, reusing %d)",
- (int)n, (int)tls->wantwrite_n);
- n = tls->wantwrite_n;
- tls->wantwrite_n = 0;
- }
- r = SSL_write(tls->ssl, cp, (int)n);
- err = tor_tls_get_error(tls, r, 0, "writing", LOG_INFO, LD_NET);
- if (err == TOR_TLS_DONE) {
- total_bytes_written_over_tls += r;
- return r;
- }
- if (err == TOR_TLS_WANTWRITE || err == TOR_TLS_WANTREAD) {
- tls->wantwrite_n = n;
- }
- return err;
-}
-
-/** Perform initial handshake on <b>tls</b>. When finished, returns
- * TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD,
- * or TOR_TLS_WANTWRITE.
- */
-int
-tor_tls_handshake(tor_tls_t *tls)
-{
- int r;
- tor_assert(tls);
- tor_assert(tls->ssl);
- tor_assert(tls->state == TOR_TLS_ST_HANDSHAKE);
-
- check_no_tls_errors();
-
- OSSL_HANDSHAKE_STATE oldstate = SSL_get_state(tls->ssl);
-
- if (tls->isServer) {
- log_debug(LD_HANDSHAKE, "About to call SSL_accept on %p (%s)", tls,
- SSL_state_string_long(tls->ssl));
- r = SSL_accept(tls->ssl);
- } else {
- log_debug(LD_HANDSHAKE, "About to call SSL_connect on %p (%s)", tls,
- SSL_state_string_long(tls->ssl));
- r = SSL_connect(tls->ssl);
- }
-
- OSSL_HANDSHAKE_STATE newstate = SSL_get_state(tls->ssl);
-
- if (oldstate != newstate)
- log_debug(LD_HANDSHAKE, "After call, %p was in state %s",
- tls, SSL_state_string_long(tls->ssl));
- /* We need to call this here and not earlier, since OpenSSL has a penchant
- * for clearing its flags when you say accept or connect. */
- tor_tls_unblock_renegotiation(tls);
- r = tor_tls_get_error(tls,r,0, "handshaking", LOG_INFO, LD_HANDSHAKE);
- if (ERR_peek_error() != 0) {
- tls_log_errors(tls, tls->isServer ? LOG_INFO : LOG_WARN, LD_HANDSHAKE,
- "handshaking");
- return TOR_TLS_ERROR_MISC;
- }
- if (r == TOR_TLS_DONE) {
- tls->state = TOR_TLS_ST_OPEN;
- return tor_tls_finish_handshake(tls);
- }
- return r;
-}
-
-/** Perform the final part of the initial TLS handshake on <b>tls</b>. This
- * should be called for the first handshake only: it determines whether the v1
- * or the v2 handshake was used, and adjusts things for the renegotiation
- * handshake as appropriate.
- *
- * tor_tls_handshake() calls this on its own; you only need to call this if
- * bufferevent is doing the handshake for you.
- */
-int
-tor_tls_finish_handshake(tor_tls_t *tls)
-{
- int r = TOR_TLS_DONE;
- check_no_tls_errors();
- if (tls->isServer) {
- SSL_set_info_callback(tls->ssl, NULL);
- SSL_set_verify(tls->ssl, SSL_VERIFY_PEER, always_accept_verify_cb);
- SSL_clear_mode(tls->ssl, SSL_MODE_NO_AUTO_CHAIN);
- if (tor_tls_client_is_using_v2_ciphers(tls->ssl)) {
- /* This check is redundant, but back when we did it in the callback,
- * we might have not been able to look up the tor_tls_t if the code
- * was buggy. Fixing that. */
- if (!tls->wasV2Handshake) {
- log_warn(LD_BUG, "For some reason, wasV2Handshake didn't"
- " get set. Fixing that.");
- }
- tls->wasV2Handshake = 1;
- log_debug(LD_HANDSHAKE, "Completed V2 TLS handshake with client; waiting"
- " for renegotiation.");
- } else {
- tls->wasV2Handshake = 0;
- }
- } else {
- /* Client-side */
- tls->wasV2Handshake = 1;
- /* XXXX this can move, probably? -NM */
- if (SSL_set_cipher_list(tls->ssl, SERVER_CIPHER_LIST) == 0) {
- tls_log_errors(NULL, LOG_WARN, LD_HANDSHAKE, "re-setting ciphers");
- r = TOR_TLS_ERROR_MISC;
- }
- }
- tls_log_errors(NULL, LOG_WARN, LD_NET, "finishing the handshake");
- return r;
-}
-
-/** Shut down an open tls connection <b>tls</b>. When finished, returns
- * TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD,
- * or TOR_TLS_WANTWRITE.
- */
-int
-tor_tls_shutdown(tor_tls_t *tls)
-{
- int r, err;
- char buf[128];
- tor_assert(tls);
- tor_assert(tls->ssl);
- check_no_tls_errors();
-
- while (1) {
- if (tls->state == TOR_TLS_ST_SENTCLOSE) {
- /* If we've already called shutdown once to send a close message,
- * we read until the other side has closed too.
- */
- do {
- r = SSL_read(tls->ssl, buf, 128);
- } while (r>0);
- err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading to shut down",
- LOG_INFO, LD_NET);
- if (err == TOR_TLS_ZERORETURN_) {
- tls->state = TOR_TLS_ST_GOTCLOSE;
- /* fall through... */
- } else {
- return err;
- }
- }
-
- r = SSL_shutdown(tls->ssl);
- if (r == 1) {
- /* If shutdown returns 1, the connection is entirely closed. */
- tls->state = TOR_TLS_ST_CLOSED;
- return TOR_TLS_DONE;
- }
- err = tor_tls_get_error(tls, r, CATCH_SYSCALL|CATCH_ZERO, "shutting down",
- LOG_INFO, LD_NET);
- if (err == TOR_TLS_SYSCALL_) {
- /* The underlying TCP connection closed while we were shutting down. */
- tls->state = TOR_TLS_ST_CLOSED;
- return TOR_TLS_DONE;
- } else if (err == TOR_TLS_ZERORETURN_) {
- /* The TLS connection says that it sent a shutdown record, but
- * isn't done shutting down yet. Make sure that this hasn't
- * happened before, then go back to the start of the function
- * and try to read.
- */
- if (tls->state == TOR_TLS_ST_GOTCLOSE ||
- tls->state == TOR_TLS_ST_SENTCLOSE) {
- log_warn(LD_NET,
- "TLS returned \"half-closed\" value while already half-closed");
- return TOR_TLS_ERROR_MISC;
- }
- tls->state = TOR_TLS_ST_SENTCLOSE;
- /* fall through ... */
- } else {
- return err;
- }
- } /* end loop */
-}
-
-/** Return true iff this TLS connection is authenticated.
- */
-int
-tor_tls_peer_has_cert(tor_tls_t *tls)
-{
- X509 *cert;
- cert = SSL_get_peer_certificate(tls->ssl);
- tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "getting peer certificate");
- if (!cert)
- return 0;
- X509_free(cert);
- return 1;
-}
-
-/** Return a newly allocated copy of the peer certificate, or NULL if there
- * isn't one. */
-MOCK_IMPL(tor_x509_cert_t *,
-tor_tls_get_peer_cert,(tor_tls_t *tls))
-{
- X509 *cert;
- cert = SSL_get_peer_certificate(tls->ssl);
- tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "getting peer certificate");
- if (!cert)
- return NULL;
- return tor_x509_cert_new(cert);
-}
-
-/** Return a newly allocated copy of the cerficate we used on the connection,
- * or NULL if somehow we didn't use one. */
-MOCK_IMPL(tor_x509_cert_t *,
-tor_tls_get_own_cert,(tor_tls_t *tls))
-{
- X509 *cert = SSL_get_certificate(tls->ssl);
- tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE,
- "getting own-connection certificate");
- if (!cert)
- return NULL;
- /* Fun inconsistency: SSL_get_peer_certificate increments the reference
- * count, but SSL_get_certificate does not. */
- X509 *duplicate = X509_dup(cert);
- if (BUG(duplicate == NULL))
- return NULL;
- return tor_x509_cert_new(duplicate);
-}
-
-/** 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
- * <b>severity</b>.
- *
- * Note that a reference is added to cert_out, so it needs to be
- * freed. id_cert_out doesn't. */
-MOCK_IMPL(STATIC void,
-try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls,
- X509 **cert_out, X509 **id_cert_out))
-{
- X509 *cert = NULL, *id_cert = NULL;
- STACK_OF(X509) *chain = NULL;
- int num_in_chain, i;
- *cert_out = *id_cert_out = NULL;
- if (!(cert = SSL_get_peer_certificate(tls->ssl)))
- return;
- *cert_out = cert;
- if (!(chain = SSL_get_peer_cert_chain(tls->ssl)))
- return;
- num_in_chain = sk_X509_num(chain);
- /* 1 means we're receiving (server-side), and it's just the id_cert.
- * 2 means we're connecting (client-side), and it's both the link
- * cert and the id_cert.
- */
- if (num_in_chain < 1) {
- log_fn(severity,LD_PROTOCOL,
- "Unexpected number of certificates in chain (%d)",
- num_in_chain);
- return;
- }
- for (i=0; i<num_in_chain; ++i) {
- id_cert = sk_X509_value(chain, i);
- if (X509_cmp(id_cert, cert) != 0)
- break;
- }
- *id_cert_out = id_cert;
-}
-
-/** If the provided tls connection is authenticated and has a
- * certificate chain that is currently valid and signed, then set
- * *<b>identity_key</b> to the identity certificate's key and return
- * 0. Else, return -1 and log complaints with log-level <b>severity</b>.
- */
-int
-tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity_key)
-{
- X509 *cert = NULL, *id_cert = NULL;
- EVP_PKEY *id_pkey = NULL;
- RSA *rsa;
- int r = -1;
-
- check_no_tls_errors();
- *identity_key = NULL;
-
- try_to_extract_certs_from_tls(severity, tls, &cert, &id_cert);
- if (!cert)
- goto done;
- if (!id_cert) {
- log_fn(severity,LD_PROTOCOL,"No distinct identity certificate found");
- goto done;
- }
- tls_log_errors(tls, severity, LD_HANDSHAKE, "before verifying certificate");
-
- if (!(id_pkey = X509_get_pubkey(id_cert)) ||
- X509_verify(cert, id_pkey) <= 0) {
- log_fn(severity,LD_PROTOCOL,"X509_verify on cert and pkey returned <= 0");
- tls_log_errors(tls, severity, LD_HANDSHAKE, "verifying certificate");
- goto done;
- }
-
- rsa = EVP_PKEY_get1_RSA(id_pkey);
- if (!rsa)
- goto done;
- *identity_key = crypto_new_pk_from_openssl_rsa_(rsa);
-
- r = 0;
-
- done:
- if (cert)
- X509_free(cert);
- if (id_pkey)
- EVP_PKEY_free(id_pkey);
-
- /* This should never get invoked, but let's make sure in case OpenSSL
- * acts unexpectedly. */
- tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "finishing tor_tls_verify");
-
- return r;
-}
-
-/** Check whether the certificate set on the connection <b>tls</b> is expired
- * give or take <b>past_tolerance</b> seconds, or not-yet-valid give or take
- * <b>future_tolerance</b> seconds. Return 0 for valid, -1 for failure.
- *
- * NOTE: you should call tor_tls_verify before tor_tls_check_lifetime.
- */
-int
-tor_tls_check_lifetime(int severity, tor_tls_t *tls,
- time_t now,
- int past_tolerance, int future_tolerance)
-{
- X509 *cert;
- int r = -1;
-
- if (!(cert = SSL_get_peer_certificate(tls->ssl)))
- goto done;
-
- if (tor_x509_check_cert_lifetime_internal(severity, cert, now,
- past_tolerance,
- future_tolerance) < 0)
- goto done;
-
- r = 0;
- done:
- if (cert)
- X509_free(cert);
- /* Not expected to get invoked */
- tls_log_errors(tls, LOG_WARN, LD_NET, "checking certificate lifetime");
-
- return r;
-}
-
-/** Return the number of bytes available for reading from <b>tls</b>.
- */
-int
-tor_tls_get_pending_bytes(tor_tls_t *tls)
-{
- tor_assert(tls);
- return SSL_pending(tls->ssl);
-}
-
-/** If <b>tls</b> requires that the next write be of a particular size,
- * return that size. Otherwise, return 0. */
-size_t
-tor_tls_get_forced_write_size(tor_tls_t *tls)
-{
- return tls->wantwrite_n;
-}
-
-/** Sets n_read and n_written to the number of bytes read and written,
- * respectively, on the raw socket used by <b>tls</b> since the last time this
- * function was called on <b>tls</b>. */
-void
-tor_tls_get_n_raw_bytes(tor_tls_t *tls, size_t *n_read, size_t *n_written)
-{
- BIO *wbio, *tmpbio;
- unsigned long r, w;
- r = (unsigned long) BIO_number_read(SSL_get_rbio(tls->ssl));
- /* We want the number of bytes actually for real written. Unfortunately,
- * sometimes OpenSSL replaces the wbio on tls->ssl with a buffering bio,
- * which makes the answer turn out wrong. Let's cope with that. Note
- * that this approach will fail if we ever replace tls->ssl's BIOs with
- * buffering bios for reasons of our own. As an alternative, we could
- * save the original BIO for tls->ssl in the tor_tls_t structure, but
- * that would be tempting fate. */
- wbio = SSL_get_wbio(tls->ssl);
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5)
- /* BIO structure is opaque as of OpenSSL 1.1.0-pre5-dev. Again, not
- * supposed to use this form of the version macro, but the OpenSSL developers
- * introduced major API changes in the pre-release stage.
- */
- if (BIO_method_type(wbio) == BIO_TYPE_BUFFER &&
- (tmpbio = BIO_next(wbio)) != NULL)
- wbio = tmpbio;
-#else /* !(OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5)) */
- if (wbio->method == BIO_f_buffer() && (tmpbio = BIO_next(wbio)) != NULL)
- wbio = tmpbio;
-#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) */
- w = (unsigned long) BIO_number_written(wbio);
-
- /* We are ok with letting these unsigned ints go "negative" here:
- * If we wrapped around, this should still give us the right answer, unless
- * we wrapped around by more than ULONG_MAX since the last time we called
- * this function.
- */
- *n_read = (size_t)(r - tls->last_read_count);
- *n_written = (size_t)(w - tls->last_write_count);
- if (*n_read > INT_MAX || *n_written > INT_MAX) {
- log_warn(LD_BUG, "Preposterously large value in tor_tls_get_n_raw_bytes. "
- "r=%lu, last_read=%lu, w=%lu, last_written=%lu",
- r, tls->last_read_count, w, tls->last_write_count);
- }
- total_bytes_written_by_tls += *n_written;
- tls->last_read_count = r;
- tls->last_write_count = w;
-}
-
-/** Return a ratio of the bytes that TLS has sent to the bytes that we've told
- * it to send. Used to track whether our TLS records are getting too tiny. */
-MOCK_IMPL(double,
-tls_get_write_overhead_ratio,(void))
-{
- if (total_bytes_written_over_tls == 0)
- return 1.0;
-
- return ((double)total_bytes_written_by_tls) /
- ((double)total_bytes_written_over_tls);
-}
-
-/** Implement check_no_tls_errors: If there are any pending OpenSSL
- * errors, log an error message. */
-void
-check_no_tls_errors_(const char *fname, int line)
-{
- if (ERR_peek_error() == 0)
- return;
- log_warn(LD_CRYPTO, "Unhandled OpenSSL errors found at %s:%d: ",
- tor_fix_source_file(fname), line);
- tls_log_errors(NULL, LOG_WARN, LD_NET, NULL);
-}
-
-/** Return true iff the initial TLS connection at <b>tls</b> did not use a v2
- * TLS handshake. Output is undefined if the handshake isn't finished. */
-int
-tor_tls_used_v1_handshake(tor_tls_t *tls)
-{
- return ! tls->wasV2Handshake;
-}
-
-/** Return the number of server handshakes that we've noticed doing on
- * <b>tls</b>. */
-int
-tor_tls_get_num_server_handshakes(tor_tls_t *tls)
-{
- return tls->server_handshake_count;
-}
-
-/** Return true iff the server TLS connection <b>tls</b> got the renegotiation
- * request it was waiting for. */
-int
-tor_tls_server_got_renegotiate(tor_tls_t *tls)
-{
- return tls->got_renegotiate;
-}
-
-#ifndef HAVE_SSL_GET_CLIENT_RANDOM
-static size_t
-SSL_get_client_random(SSL *s, uint8_t *out, size_t len)
-{
- if (len == 0)
- return SSL3_RANDOM_SIZE;
- tor_assert(len == SSL3_RANDOM_SIZE);
- tor_assert(s->s3);
- memcpy(out, s->s3->client_random, len);
- return len;
-}
-#endif /* !defined(HAVE_SSL_GET_CLIENT_RANDOM) */
-
-#ifndef HAVE_SSL_GET_SERVER_RANDOM
-static size_t
-SSL_get_server_random(SSL *s, uint8_t *out, size_t len)
-{
- if (len == 0)
- return SSL3_RANDOM_SIZE;
- tor_assert(len == SSL3_RANDOM_SIZE);
- tor_assert(s->s3);
- memcpy(out, s->s3->server_random, len);
- return len;
-}
-#endif /* !defined(HAVE_SSL_GET_SERVER_RANDOM) */
-
-#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY
-STATIC size_t
-SSL_SESSION_get_master_key(SSL_SESSION *s, uint8_t *out, size_t len)
-{
- tor_assert(s);
- if (len == 0)
- return s->master_key_length;
- tor_assert(len == (size_t)s->master_key_length);
- tor_assert(out);
- memcpy(out, s->master_key, len);
- return len;
-}
-#endif /* !defined(HAVE_SSL_SESSION_GET_MASTER_KEY) */
-
-/** Set the DIGEST256_LEN buffer at <b>secrets_out</b> to the value used in
- * the v3 handshake to prove that the client knows the TLS secrets for the
- * connection <b>tls</b>. Return 0 on success, -1 on failure.
- */
-MOCK_IMPL(int,
-tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out))
-{
-#define TLSSECRET_MAGIC "Tor V3 handshake TLS cross-certification"
- uint8_t buf[128];
- size_t len;
- tor_assert(tls);
-
- SSL *const ssl = tls->ssl;
- SSL_SESSION *const session = SSL_get_session(ssl);
-
- tor_assert(ssl);
- tor_assert(session);
-
- const size_t server_random_len = SSL_get_server_random(ssl, NULL, 0);
- const size_t client_random_len = SSL_get_client_random(ssl, NULL, 0);
- const size_t master_key_len = SSL_SESSION_get_master_key(session, NULL, 0);
-
- tor_assert(server_random_len);
- tor_assert(client_random_len);
- tor_assert(master_key_len);
-
- len = client_random_len + server_random_len + strlen(TLSSECRET_MAGIC) + 1;
- tor_assert(len <= sizeof(buf));
-
- {
- size_t r = SSL_get_client_random(ssl, buf, client_random_len);
- tor_assert(r == client_random_len);
- }
-
- {
- size_t r = SSL_get_server_random(ssl,
- buf+client_random_len,
- server_random_len);
- tor_assert(r == server_random_len);
- }
-
- uint8_t *master_key = tor_malloc_zero(master_key_len);
- {
- size_t r = SSL_SESSION_get_master_key(session, master_key, master_key_len);
- tor_assert(r == master_key_len);
- }
-
- uint8_t *nextbuf = buf + client_random_len + server_random_len;
- memcpy(nextbuf, TLSSECRET_MAGIC, strlen(TLSSECRET_MAGIC) + 1);
-
- /*
- The value is an HMAC, using the TLS master key as the HMAC key, of
- client_random | server_random | TLSSECRET_MAGIC
- */
- crypto_hmac_sha256((char*)secrets_out,
- (char*)master_key,
- master_key_len,
- (char*)buf, len);
- memwipe(buf, 0, sizeof(buf));
- memwipe(master_key, 0, master_key_len);
- tor_free(master_key);
-
- return 0;
-}
-
-/** Using the RFC5705 key material exporting construction, and the
- * provided <b>context</b> (<b>context_len</b> bytes long) and
- * <b>label</b> (a NUL-terminated string), compute a 32-byte secret in
- * <b>secrets_out</b> that only the parties to this TLS session can
- * compute. Return 0 on success and -1 on failure.
- */
-MOCK_IMPL(int,
-tor_tls_export_key_material,(tor_tls_t *tls, uint8_t *secrets_out,
- const uint8_t *context,
- size_t context_len,
- const char *label))
-{
- tor_assert(tls);
- tor_assert(tls->ssl);
-
- int r = SSL_export_keying_material(tls->ssl,
- secrets_out, DIGEST256_LEN,
- label, strlen(label),
- context, context_len, 1);
- return (r == 1) ? 0 : -1;
-}
-
-/** Examine the amount of memory used and available for buffers in <b>tls</b>.
- * Set *<b>rbuf_capacity</b> to the amount of storage allocated for the read
- * buffer and *<b>rbuf_bytes</b> to the amount actually used.
- * Set *<b>wbuf_capacity</b> to the amount of storage allocated for the write
- * buffer and *<b>wbuf_bytes</b> to the amount actually used.
- *
- * Return 0 on success, -1 on failure.*/
-int
-tor_tls_get_buffer_sizes(tor_tls_t *tls,
- size_t *rbuf_capacity, size_t *rbuf_bytes,
- size_t *wbuf_capacity, size_t *wbuf_bytes)
-{
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)
- (void)tls;
- (void)rbuf_capacity;
- (void)rbuf_bytes;
- (void)wbuf_capacity;
- (void)wbuf_bytes;
-
- return -1;
-#else /* !(OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)) */
- if (tls->ssl->s3->rbuf.buf)
- *rbuf_capacity = tls->ssl->s3->rbuf.len;
- else
- *rbuf_capacity = 0;
- if (tls->ssl->s3->wbuf.buf)
- *wbuf_capacity = tls->ssl->s3->wbuf.len;
- else
- *wbuf_capacity = 0;
- *rbuf_bytes = tls->ssl->s3->rbuf.left;
- *wbuf_bytes = tls->ssl->s3->wbuf.left;
- return 0;
-#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) */
-}
-
-/** Check whether the ECC group requested is supported by the current OpenSSL
- * library instance. Return 1 if the group is supported, and 0 if not.
- */
-int
-evaluate_ecgroup_for_tls(const char *ecgroup)
-{
- EC_KEY *ec_key;
- int nid;
- int ret;
-
- if (!ecgroup)
- nid = NID_tor_default_ecdhe_group;
- else if (!strcasecmp(ecgroup, "P256"))
- nid = NID_X9_62_prime256v1;
- else if (!strcasecmp(ecgroup, "P224"))
- nid = NID_secp224r1;
- else
- return 0;
-
- ec_key = EC_KEY_new_by_curve_name(nid);
- ret = (ec_key != NULL);
- EC_KEY_free(ec_key);
-
- return ret;
-}
diff --git a/src/lib/tls/tortls.h b/src/lib/tls/tortls.h
index f46e73267..306d321cd 100644
--- a/src/lib/tls/tortls.h
+++ b/src/lib/tls/tortls.h
@@ -50,74 +50,13 @@ struct tor_x509_cert_t;
#define TOR_TLS_IS_ERROR(rv) ((rv) < TOR_TLS_CLOSE)
-#ifdef TORTLS_PRIVATE
-
-#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.
*/
typedef struct tor_tls_context_t tor_tls_context_t;
-STATIC int tor_errno_to_tls_error(int e);
-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);
-#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY
-STATIC size_t SSL_SESSION_get_master_key(struct ssl_session_st *s,
- uint8_t *out,
- size_t len);
-#endif
-STATIC void tor_tls_debug_state_callback(const struct ssl_st *ssl,
- int type, int val);
-STATIC void tor_tls_server_info_callback(const struct ssl_st *ssl,
- int type, int val);
-#ifdef TORTLS_OPENSSL_PRIVATE
-STATIC int tor_tls_session_secret_cb(struct ssl_st *ssl, void *secret,
- int *secret_len,
- STACK_OF(SSL_CIPHER) *peer_ciphers,
- CONST_IF_OPENSSL_1_1_API SSL_CIPHER **cipher,
- void *arg);
-STATIC int find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m,
- uint16_t cipher);
-#endif /* defined(TORTLS_OPENSSL_PRIVATE) */
-STATIC tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity,
- unsigned int key_lifetime, unsigned flags, int is_client);
-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);
-
-#ifdef TOR_UNIT_TESTS
-extern int tor_tls_object_ex_data_index;
-extern tor_tls_context_t *server_tls_context;
-extern tor_tls_context_t *client_tls_context;
-extern uint16_t v2_cipher_list[];
-extern uint64_t total_bytes_written_over_tls;
-extern uint64_t total_bytes_written_by_tls;
-
-#endif /* defined(TOR_UNIT_TESTS) */
-
-#endif /* defined(TORTLS_PRIVATE) */
-
const char *tor_tls_err_to_string(int err);
void tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz);
-
void tor_tls_free_all(void);
#define TOR_TLS_CTX_IS_PUBLIC_SERVER (1u<<0)
@@ -131,6 +70,9 @@ int tor_tls_context_init(unsigned flags,
crypto_pk_t *client_identity,
crypto_pk_t *server_identity,
unsigned int key_lifetime);
+void tor_tls_context_incref(tor_tls_context_t *ctx);
+void tor_tls_context_decref(tor_tls_context_t *ctx);
+tor_tls_context_t *tor_tls_context_get(int is_server);
tor_tls_t *tor_tls_new(int sock, int is_server);
void tor_tls_set_logged_address(tor_tls_t *tls, const char *address);
void tor_tls_set_renegotiate_callback(tor_tls_t *tls,
diff --git a/src/lib/tls/tortls_internal.h b/src/lib/tls/tortls_internal.h
new file mode 100644
index 000000000..f6afb348c
--- /dev/null
+++ b/src/lib/tls/tortls_internal.h
@@ -0,0 +1,65 @@
+/* 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 TORTLS_INTERNAL_H
+#define TORTLS_INTERNAL_H
+
+#ifdef ENABLE_OPENSSL
+struct ssl_st;
+struct ssl_ctx_st;
+struct ssl_session_st;
+#endif
+
+int tor_errno_to_tls_error(int e);
+int tor_tls_get_error(tor_tls_t *tls, int r, int extra,
+ const char *doing, int severity, int domain);
+tor_tls_t *tor_tls_get_by_ssl(const struct ssl_st *ssl);
+void tor_tls_allocate_tor_tls_object_ex_data_index(void);
+MOCK_DECL(void, try_to_extract_certs_from_tls,
+ (int severity, tor_tls_t *tls,
+ tor_x509_cert_impl_t **cert_out,
+ tor_x509_cert_impl_t **id_cert_out));
+#ifdef TORTLS_OPENSSL_PRIVATE
+int always_accept_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx);
+int tor_tls_classify_client_ciphers(const struct ssl_st *ssl,
+ STACK_OF(SSL_CIPHER) *peer_ciphers);
+#endif
+int tor_tls_client_is_using_v2_ciphers(const struct ssl_st *ssl);
+#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY
+size_t SSL_SESSION_get_master_key(struct ssl_session_st *s,
+ uint8_t *out,
+ size_t len);
+#endif
+void tor_tls_debug_state_callback(const struct ssl_st *ssl,
+ int type, int val);
+void tor_tls_server_info_callback(const struct ssl_st *ssl,
+ int type, int val);
+#ifdef TORTLS_OPENSSL_PRIVATE
+STATIC int tor_tls_session_secret_cb(struct ssl_st *ssl, void *secret,
+ int *secret_len,
+ STACK_OF(SSL_CIPHER) *peer_ciphers,
+ CONST_IF_OPENSSL_1_1_API SSL_CIPHER **cipher,
+ void *arg);
+STATIC int find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m,
+ uint16_t cipher);
+#endif /* defined(TORTLS_OPENSSL_PRIVATE) */
+tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity,
+ unsigned int key_lifetime, unsigned flags, int is_client);
+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);
+
+#ifdef TOR_UNIT_TESTS
+extern int tor_tls_object_ex_data_index;
+extern tor_tls_context_t *server_tls_context;
+extern tor_tls_context_t *client_tls_context;
+extern uint16_t v2_cipher_list[];
+extern uint64_t total_bytes_written_over_tls;
+extern uint64_t total_bytes_written_by_tls;
+#endif /* defined(TOR_UNIT_TESTS) */
+
+#endif /* defined(TORTLS_INTERNAL_H) */
diff --git a/src/lib/tls/tortls_nss.c b/src/lib/tls/tortls_nss.c
new file mode 100644
index 000000000..078196ac5
--- /dev/null
+++ b/src/lib/tls/tortls_nss.c
@@ -0,0 +1,432 @@
+/* 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 tortls_nss.c
+ * \brief Wrapper functions to present a consistent interface to
+ * TLS and SSL X.509 functions from NSS.
+ **/
+
+#include "orconfig.h"
+
+#define TORTLS_PRIVATE
+
+#ifdef _WIN32 /*wrkard for dtls1.h >= 0.9.8m of "#include <winsock.h>"*/
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
+#endif
+
+#include "lib/crypt_ops/crypto_cipher.h"
+#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"
+#include "lib/tls/tortls.h"
+#include "lib/tls/tortls_internal.h"
+#include "lib/log/util_bug.h"
+
+int
+tor_errno_to_tls_error(int e)
+{
+ (void)e;
+ // XXXX
+ return -1;
+}
+int
+tor_tls_get_error(tor_tls_t *tls, int r, int extra,
+ const char *doing, int severity, int domain)
+{
+ (void)tls;
+ (void)r;
+ (void)extra;
+ (void)doing;
+ (void)severity;
+ (void)domain;
+ // XXXX
+ return -1;
+}
+tor_tls_t *
+tor_tls_get_by_ssl(const struct ssl_st *ssl)
+{
+ (void) ssl;
+ // XXXX
+ // XXXX refers to ssl_st.
+ return NULL;
+}
+void
+tor_tls_allocate_tor_tls_object_ex_data_index(void)
+{
+ // XXXX openssl only.
+}
+MOCK_IMPL(void,
+try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls,
+ tor_x509_cert_impl_t **cert_out,
+ tor_x509_cert_impl_t **id_cert_out))
+{
+ tor_assert(tls);
+ tor_assert(cert_out);
+ tor_assert(id_cert_out);
+ (void)severity;
+ // XXXX
+}
+int
+tor_tls_client_is_using_v2_ciphers(const struct ssl_st *ssl)
+{
+ (void) ssl;
+ // XXXX
+ // XXXX refers to ssl_st.
+ return 0;
+}
+
+void
+tor_tls_debug_state_callback(const struct ssl_st *ssl,
+ int type, int val)
+{
+ (void) ssl;
+ (void)type;
+ (void)val;
+ // XXXX
+ // XXXX refers to ssl_st.
+}
+
+void
+tor_tls_server_info_callback(const struct ssl_st *ssl,
+ int type, int val)
+{
+ (void)ssl;
+ (void)type;
+ (void)val;
+ // XXXX
+ // XXXX refers to ssl_st.
+}
+tor_tls_context_t *
+tor_tls_context_new(crypto_pk_t *identity,
+ unsigned int key_lifetime, unsigned flags, int is_client)
+{
+ tor_assert(identity);
+ tor_assert(key_lifetime);
+ (void)flags;
+ (void)is_client;
+ // XXXX
+ return NULL;
+}
+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)
+{
+ tor_assert(ppcontext);
+ tor_assert(identity);
+ tor_assert(key_lifetime);
+ (void)flags;
+ (void)is_client;
+ // XXXX
+ return -1;
+}
+
+void
+tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz)
+{
+ (void)tls;
+ (void)buf;
+ (void)sz;
+ // XXXX
+}
+
+void
+tor_tls_init(void)
+{
+ // XXXX
+}
+void
+tls_log_errors(tor_tls_t *tls, int severity, int domain,
+ const char *doing)
+{
+ (void)tls;
+ (void)severity;
+ (void)domain;
+ (void)doing;
+ // XXXX
+}
+
+tor_tls_t *
+tor_tls_new(int sock, int is_server)
+{
+ (void)sock;
+ (void)is_server;
+ // XXXX
+ return NULL;
+}
+void
+tor_tls_set_renegotiate_callback(tor_tls_t *tls,
+ void (*cb)(tor_tls_t *, void *arg),
+ void *arg)
+{
+ tor_assert(tls);
+ (void)cb;
+ (void)arg;
+ // XXXX;
+}
+
+void
+tor_tls_free_(tor_tls_t *tls)
+{
+ (void)tls;
+ // XXXX
+}
+
+int
+tor_tls_peer_has_cert(tor_tls_t *tls)
+{
+ (void)tls;
+ // XXXX
+ return -1;
+}
+MOCK_IMPL(tor_x509_cert_t *,
+tor_tls_get_peer_cert,(tor_tls_t *tls))
+{
+ tor_assert(tls);
+ // XXXX
+ return NULL;
+}
+MOCK_IMPL(tor_x509_cert_t *,
+tor_tls_get_own_cert,(tor_tls_t *tls))
+{
+ tor_assert(tls);
+ // XXXX
+ return NULL;
+}
+int
+tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity)
+{
+ tor_assert(tls);
+ tor_assert(identity);
+ (void)severity;
+ // XXXX
+ return -1;
+}
+int
+tor_tls_check_lifetime(int severity,
+ tor_tls_t *tls, time_t now,
+ int past_tolerance,
+ int future_tolerance)
+{
+ tor_assert(tls);
+ (void)severity;
+ (void)now;
+ (void)past_tolerance;
+ (void)future_tolerance;
+ // XXXX
+ return -1;
+}
+MOCK_IMPL(int,
+tor_tls_read, (tor_tls_t *tls, char *cp, size_t len))
+{
+ tor_assert(tls);
+ tor_assert(cp);
+ (void)len;
+ // XXXX
+ return -1;
+}
+int
+tor_tls_write(tor_tls_t *tls, const char *cp, size_t n)
+{
+ tor_assert(tls);
+ tor_assert(cp);
+ (void)n;
+ // XXXX
+ return -1;
+}
+int
+tor_tls_handshake(tor_tls_t *tls)
+{
+ tor_assert(tls);
+ // XXXX
+ return -1;
+}
+int
+tor_tls_finish_handshake(tor_tls_t *tls)
+{
+ tor_assert(tls);
+ // XXXX
+ return -1;
+}
+void
+tor_tls_unblock_renegotiation(tor_tls_t *tls)
+{
+ tor_assert(tls);
+ // XXXX
+}
+void
+tor_tls_block_renegotiation(tor_tls_t *tls)
+{
+ tor_assert(tls);
+ // XXXX
+}
+void
+tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls)
+{
+ tor_assert(tls);
+ // XXXX
+}
+int
+tor_tls_shutdown(tor_tls_t *tls)
+{
+ tor_assert(tls);
+ // XXXX
+ return -1;
+}
+int
+tor_tls_get_pending_bytes(tor_tls_t *tls)
+{
+ tor_assert(tls);
+ // XXXX
+ return -1;
+}
+size_t
+tor_tls_get_forced_write_size(tor_tls_t *tls)
+{
+ tor_assert(tls);
+ // XXXX
+ return 0;
+}
+void
+tor_tls_get_n_raw_bytes(tor_tls_t *tls,
+ size_t *n_read, size_t *n_written)
+{
+ tor_assert(tls);
+ tor_assert(n_read);
+ tor_assert(n_written);
+ // XXXX
+}
+
+int
+tor_tls_get_buffer_sizes(tor_tls_t *tls,
+ size_t *rbuf_capacity, size_t *rbuf_bytes,
+ size_t *wbuf_capacity, size_t *wbuf_bytes)
+{
+ tor_assert(tls);
+ tor_assert(rbuf_capacity);
+ tor_assert(rbuf_bytes);
+ tor_assert(wbuf_capacity);
+ tor_assert(wbuf_bytes);
+ // XXXX
+ return -1;
+}
+MOCK_IMPL(double,
+tls_get_write_overhead_ratio, (void))
+{
+ // XXXX
+ return 0.0;
+}
+
+int
+tor_tls_used_v1_handshake(tor_tls_t *tls)
+{
+ tor_assert(tls);
+ // XXXX
+ return -1;
+}
+int
+tor_tls_get_num_server_handshakes(tor_tls_t *tls)
+{
+ tor_assert(tls);
+ // XXXX
+ return -1;
+}
+int
+tor_tls_server_got_renegotiate(tor_tls_t *tls)
+{
+ tor_assert(tls);
+ // XXXX
+ return -1;
+}
+MOCK_IMPL(int,
+tor_tls_cert_matches_key,(const tor_tls_t *tls,
+ const struct tor_x509_cert_t *cert))
+{
+ tor_assert(tls);
+ tor_assert(cert);
+ // XXXX
+ return 0;
+}
+MOCK_IMPL(int,
+tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out))
+{
+ tor_assert(tls);
+ tor_assert(secrets_out);
+ // XXXX
+ return -1;
+}
+MOCK_IMPL(int,
+tor_tls_export_key_material,(tor_tls_t *tls, uint8_t *secrets_out,
+ const uint8_t *context,
+ size_t context_len,
+ const char *label))
+{
+ tor_assert(tls);
+ tor_assert(secrets_out);
+ tor_assert(context);
+ tor_assert(label);
+ (void)context_len;
+ // XXXX
+ return -1;
+}
+
+void
+check_no_tls_errors_(const char *fname, int line)
+{
+ (void)fname;
+ (void)line;
+ // XXXX
+}
+void
+tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
+ int severity, int domain, const char *doing)
+{
+ tor_assert(tls);
+ (void)err;
+ (void)severity;
+ (void)domain;
+ (void)doing;
+ // XXXX
+}
+
+int
+tor_tls_get_my_certs(int server,
+ const struct tor_x509_cert_t **link_cert_out,
+ const struct tor_x509_cert_t **id_cert_out)
+{
+ tor_assert(link_cert_out);
+ tor_assert(id_cert_out);
+ (void)server;
+ // XXXX
+ return -1;
+}
+
+crypto_pk_t *
+tor_tls_get_my_client_auth_key(void)
+{
+ // XXXX
+ return NULL;
+}
+
+const char *
+tor_tls_get_ciphersuite_name(tor_tls_t *tls)
+{
+ tor_assert(tls);
+ // XXXX
+ return NULL;
+}
+
+int
+evaluate_ecgroup_for_tls(const char *ecgroup)
+{
+ (void)ecgroup;
+ // XXXX
+ return -1;
+}
diff --git a/src/lib/tls/tortls_openssl.c b/src/lib/tls/tortls_openssl.c
new file mode 100644
index 000000000..333b86481
--- /dev/null
+++ b/src/lib/tls/tortls_openssl.c
@@ -0,0 +1,2032 @@
+/* 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 tortls.c
+ * \brief Wrapper functions to present a consistent interface to
+ * TLS, SSL, and X.509 functions from OpenSSL.
+ **/
+
+/* (Unlike other tor functions, these
+ * are prefixed with tor_ in order to avoid conflicting with OpenSSL
+ * functions and variables.)
+ */
+
+#include "orconfig.h"
+
+#define TORTLS_PRIVATE
+#define TORTLS_OPENSSL_PRIVATE
+
+#ifdef _WIN32 /*wrkard for dtls1.h >= 0.9.8m of "#include <winsock.h>"*/
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
+#endif
+
+#include "lib/crypt_ops/crypto_cipher.h"
+#include "lib/crypt_ops/crypto_rand.h"
+#include "lib/crypt_ops/crypto_dh.h"
+#include "lib/crypt_ops/crypto_util.h"
+#include "lib/crypt_ops/compat_openssl.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. */
+DISABLE_GCC_WARNING(redundant-decls)
+
+#include <openssl/opensslv.h>
+
+#ifdef OPENSSL_NO_EC
+#error "We require OpenSSL with ECC support"
+#endif
+
+#include <openssl/ssl.h>
+#include <openssl/ssl3.h>
+#include <openssl/err.h>
+#include <openssl/tls1.h>
+#include <openssl/asn1.h>
+#include <openssl/bio.h>
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+
+ENABLE_GCC_WARNING(redundant-decls)
+
+#include "lib/tls/tortls.h"
+#include "lib/tls/tortls_st.h"
+#include "lib/tls/tortls_internal.h"
+#include "lib/log/log.h"
+#include "lib/log/util_bug.h"
+#include "lib/container/smartlist.h"
+#include "lib/string/compat_string.h"
+#include "lib/string/printf.h"
+#include "lib/net/socket.h"
+#include "lib/intmath/cmp.h"
+#include "lib/ctime/di_ops.h"
+#include "lib/encoding/time_fmt.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "lib/arch/bytes.h"
+
+/* Copied from or.h */
+#define LEGAL_NICKNAME_CHARACTERS \
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+
+/** How long do identity certificates live? (sec) */
+#define IDENTITY_CERT_LIFETIME (365*24*60*60)
+
+#define ADDR(tls) (((tls) && (tls)->address) ? tls->address : "peer")
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,0,'f')
+/* This is a version of OpenSSL before 1.0.0f. It does not have
+ * the CVE-2011-4576 fix, and as such it can't use RELEASE_BUFFERS and
+ * SSL3 safely at the same time.
+ */
+#define DISABLE_SSL3_HANDSHAKE
+#endif /* OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,0,'f') */
+
+/* We redefine these so that we can run correctly even if the vendor gives us
+ * a version of OpenSSL that does not match its header files. (Apple: I am
+ * looking at you.)
+ */
+#ifndef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
+#define SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0x00040000L
+#endif
+#ifndef SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
+#define SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0x0010
+#endif
+
+/** Return values for tor_tls_classify_client_ciphers.
+ *
+ * @{
+ */
+/** An error occurred when examining the client ciphers */
+#define CIPHERS_ERR -1
+/** The client cipher list indicates that a v1 handshake was in use. */
+#define CIPHERS_V1 1
+/** The client cipher list indicates that the client is using the v2 or the
+ * v3 handshake, but that it is (probably!) lying about what ciphers it
+ * supports */
+#define CIPHERS_V2 2
+/** The client cipher list indicates that the client is using the v2 or the
+ * v3 handshake, and that it is telling the truth about what ciphers it
+ * supports */
+#define CIPHERS_UNRESTRICTED 3
+/** @} */
+
+/** The ex_data index in which we store a pointer to an SSL object's
+ * corresponding tor_tls_t object. */
+STATIC int tor_tls_object_ex_data_index = -1;
+
+/** Helper: Allocate tor_tls_object_ex_data_index. */
+void
+tor_tls_allocate_tor_tls_object_ex_data_index(void)
+{
+ if (tor_tls_object_ex_data_index == -1) {
+ tor_tls_object_ex_data_index =
+ SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+ tor_assert(tor_tls_object_ex_data_index != -1);
+ }
+}
+
+/** Helper: given a SSL* pointer, return the tor_tls_t object using that
+ * pointer. */
+tor_tls_t *
+tor_tls_get_by_ssl(const SSL *ssl)
+{
+ tor_tls_t *result = SSL_get_ex_data(ssl, tor_tls_object_ex_data_index);
+ if (result)
+ tor_assert(result->magic == TOR_TLS_MAGIC);
+ return result;
+}
+
+/** True iff tor_tls_init() has been called. */
+static int tls_library_is_initialized = 0;
+
+/* Module-internal error codes. */
+#define TOR_TLS_SYSCALL_ (MIN_TOR_TLS_ERROR_VAL_ - 2)
+#define TOR_TLS_ZERORETURN_ (MIN_TOR_TLS_ERROR_VAL_ - 1)
+
+/** Write a description of the current state of <b>tls</b> into the
+ * <b>sz</b>-byte buffer at <b>buf</b>. */
+void
+tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz)
+{
+ const char *ssl_state;
+ const char *tortls_state;
+
+ if (PREDICT_UNLIKELY(!tls || !tls->ssl)) {
+ strlcpy(buf, "(No SSL object)", sz);
+ return;
+ }
+
+ ssl_state = SSL_state_string_long(tls->ssl);
+ switch (tls->state) {
+#define CASE(st) case TOR_TLS_ST_##st: tortls_state = " in "#st ; break
+ CASE(HANDSHAKE);
+ CASE(OPEN);
+ CASE(GOTCLOSE);
+ CASE(SENTCLOSE);
+ CASE(CLOSED);
+ CASE(RENEGOTIATE);
+#undef CASE
+ case TOR_TLS_ST_BUFFEREVENT:
+ tortls_state = "";
+ break;
+ default:
+ tortls_state = " in unknown TLS state";
+ break;
+ }
+
+ tor_snprintf(buf, sz, "%s%s", ssl_state, tortls_state);
+}
+
+/** Log a single error <b>err</b> as returned by ERR_get_error(), which was
+ * received while performing an operation <b>doing</b> on <b>tls</b>. Log
+ * the message at <b>severity</b>, in log domain <b>domain</b>. */
+void
+tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
+ int severity, int domain, const char *doing)
+{
+ const char *state = NULL, *addr;
+ const char *msg, *lib, *func;
+
+ state = (tls && tls->ssl)?SSL_state_string_long(tls->ssl):"---";
+
+ addr = tls ? tls->address : NULL;
+
+ /* Some errors are known-benign, meaning they are the fault of the other
+ * side of the connection. The caller doesn't know this, so override the
+ * priority for those cases. */
+ switch (ERR_GET_REASON(err)) {
+ case SSL_R_HTTP_REQUEST:
+ case SSL_R_HTTPS_PROXY_REQUEST:
+ case SSL_R_RECORD_LENGTH_MISMATCH:
+#ifndef OPENSSL_1_1_API
+ case SSL_R_RECORD_TOO_LARGE:
+#endif
+ case SSL_R_UNKNOWN_PROTOCOL:
+ case SSL_R_UNSUPPORTED_PROTOCOL:
+ severity = LOG_INFO;
+ break;
+ default:
+ break;
+ }
+
+ msg = (const char*)ERR_reason_error_string(err);
+ lib = (const char*)ERR_lib_error_string(err);
+ func = (const char*)ERR_func_error_string(err);
+ if (!msg) msg = "(null)";
+ if (!lib) lib = "(null)";
+ if (!func) func = "(null)";
+ if (doing) {
+ tor_log(severity, domain, "TLS error while %s%s%s: %s (in %s:%s:%s)",
+ doing, addr?" with ":"", addr?addr:"",
+ msg, lib, func, state);
+ } else {
+ tor_log(severity, domain, "TLS error%s%s: %s (in %s:%s:%s)",
+ addr?" with ":"", addr?addr:"",
+ msg, lib, func, state);
+ }
+}
+
+/** 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.
+ */
+void
+tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing)
+{
+ unsigned long err;
+
+ while ((err = ERR_get_error()) != 0) {
+ tor_tls_log_one_error(tls, err, severity, domain, doing);
+ }
+}
+
+/** Convert an errno (or a WSAerrno on windows) into a TOR_TLS_* error
+ * code. */
+int
+tor_errno_to_tls_error(int e)
+{
+ switch (e) {
+ case SOCK_ERRNO(ECONNRESET): // most common
+ return TOR_TLS_ERROR_CONNRESET;
+ case SOCK_ERRNO(ETIMEDOUT):
+ return TOR_TLS_ERROR_TIMEOUT;
+ case SOCK_ERRNO(EHOSTUNREACH):
+ case SOCK_ERRNO(ENETUNREACH):
+ return TOR_TLS_ERROR_NO_ROUTE;
+ case SOCK_ERRNO(ECONNREFUSED):
+ return TOR_TLS_ERROR_CONNREFUSED; // least common
+ default:
+ return TOR_TLS_ERROR_MISC;
+ }
+}
+
+#define CATCH_SYSCALL 1
+#define CATCH_ZERO 2
+
+/** Given a TLS object and the result of an SSL_* call, use
+ * SSL_get_error to determine whether an error has occurred, and if so
+ * which one. Return one of TOR_TLS_{DONE|WANTREAD|WANTWRITE|ERROR}.
+ * If extra&CATCH_SYSCALL is true, return TOR_TLS_SYSCALL_ instead of
+ * reporting syscall errors. If extra&CATCH_ZERO is true, return
+ * TOR_TLS_ZERORETURN_ instead of reporting zero-return errors.
+ *
+ * If an error has occurred, log it at level <b>severity</b> and describe the
+ * current action as <b>doing</b>.
+ */
+int
+tor_tls_get_error(tor_tls_t *tls, int r, int extra,
+ const char *doing, int severity, int domain)
+{
+ int err = SSL_get_error(tls->ssl, r);
+ int tor_error = TOR_TLS_ERROR_MISC;
+ switch (err) {
+ case SSL_ERROR_NONE:
+ return TOR_TLS_DONE;
+ case SSL_ERROR_WANT_READ:
+ return TOR_TLS_WANTREAD;
+ case SSL_ERROR_WANT_WRITE:
+ return TOR_TLS_WANTWRITE;
+ case SSL_ERROR_SYSCALL:
+ if (extra&CATCH_SYSCALL)
+ return TOR_TLS_SYSCALL_;
+ if (r == 0) {
+ tor_log(severity, LD_NET, "TLS error: unexpected close while %s (%s)",
+ doing, SSL_state_string_long(tls->ssl));
+ tor_error = TOR_TLS_ERROR_IO;
+ } else {
+ int e = tor_socket_errno(tls->socket);
+ tor_log(severity, LD_NET,
+ "TLS error: <syscall error while %s> (errno=%d: %s; state=%s)",
+ doing, e, tor_socket_strerror(e),
+ SSL_state_string_long(tls->ssl));
+ tor_error = tor_errno_to_tls_error(e);
+ }
+ tls_log_errors(tls, severity, domain, doing);
+ return tor_error;
+ case SSL_ERROR_ZERO_RETURN:
+ if (extra&CATCH_ZERO)
+ return TOR_TLS_ZERORETURN_;
+ tor_log(severity, LD_NET, "TLS connection closed while %s in state %s",
+ doing, SSL_state_string_long(tls->ssl));
+ tls_log_errors(tls, severity, domain, doing);
+ return TOR_TLS_CLOSE;
+ default:
+ tls_log_errors(tls, severity, domain, doing);
+ return TOR_TLS_ERROR_MISC;
+ }
+}
+
+/** Initialize OpenSSL, unless it has already been initialized.
+ */
+void
+tor_tls_init(void)
+{
+ check_no_tls_errors();
+
+ if (!tls_library_is_initialized) {
+#ifdef OPENSSL_1_1_API
+ OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL);
+#else
+ SSL_library_init();
+ SSL_load_error_strings();
+#endif
+
+#if (SIZEOF_VOID_P >= 8 && \
+ OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,1))
+ long version = OpenSSL_version_num();
+
+ /* LCOV_EXCL_START : we can't test these lines on the same machine */
+ if (version >= OPENSSL_V_SERIES(1,0,1)) {
+ /* Warn if we could *almost* be running with much faster ECDH.
+ If we're built for a 64-bit target, using OpenSSL 1.0.1, but we
+ don't have one of the built-in __uint128-based speedups, we are
+ just one build operation away from an accelerated handshake.
+
+ (We could be looking at OPENSSL_NO_EC_NISTP_64_GCC_128 instead of
+ doing this test, but that gives compile-time options, not runtime
+ behavior.)
+ */
+ EC_KEY *key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+ const EC_GROUP *g = key ? EC_KEY_get0_group(key) : NULL;
+ const EC_METHOD *m = g ? EC_GROUP_method_of(g) : NULL;
+ const int warn = (m == EC_GFp_simple_method() ||
+ m == EC_GFp_mont_method() ||
+ m == EC_GFp_nist_method());
+ EC_KEY_free(key);
+
+ if (warn)
+ log_notice(LD_GENERAL, "We were built to run on a 64-bit CPU, with "
+ "OpenSSL 1.0.1 or later, but with a version of OpenSSL "
+ "that apparently lacks accelerated support for the NIST "
+ "P-224 and P-256 groups. Building openssl with such "
+ "support (using the enable-ec_nistp_64_gcc_128 option "
+ "when configuring it) would make ECDH much faster.");
+ }
+ /* LCOV_EXCL_STOP */
+#endif /* (SIZEOF_VOID_P >= 8 && ... */
+
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
+ tls_library_is_initialized = 1;
+ }
+}
+
+/** We need to give OpenSSL a callback to verify certificates. This is
+ * it: We always accept peer certs and complete the handshake. We
+ * don't validate them until later.
+ */
+int
+always_accept_verify_cb(int preverify_ok,
+ X509_STORE_CTX *x509_ctx)
+{
+ (void) preverify_ok;
+ (void) x509_ctx;
+ return 1;
+}
+
+/** 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[] =
+#ifdef TLS1_3_TXT_AES_128_GCM_SHA256
+ /* This one can never actually get selected, since if the client lists it,
+ * we will assume that the client is honest, and not use this list.
+ * Nonetheless we list it if it's available, so that the server doesn't
+ * conclude that it has no valid ciphers if it's running with TLS1.3.
+ */
+ TLS1_3_TXT_AES_128_GCM_SHA256 ":"
+#endif
+ TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":"
+ TLS1_TXT_DHE_RSA_WITH_AES_128_SHA;
+
+/** List of ciphers that servers should select from when we actually have
+ * our choice of what cipher to use. */
+static const char UNRESTRICTED_SERVER_CIPHER_LIST[] =
+ /* Here are the TLS 1.3 ciphers we like, in the order we prefer. */
+#ifdef TLS1_3_TXT_AES_256_GCM_SHA384
+ TLS1_3_TXT_AES_256_GCM_SHA384 ":"
+#endif
+#ifdef TLS1_3_TXT_CHACHA20_POLY1305_SHA256
+ TLS1_3_TXT_CHACHA20_POLY1305_SHA256 ":"
+#endif
+#ifdef TLS1_3_TXT_AES_128_GCM_SHA256
+ TLS1_3_TXT_AES_128_GCM_SHA256 ":"
+#endif
+#ifdef TLS1_3_TXT_AES_128_CCM_SHA256
+ TLS1_3_TXT_AES_128_CCM_SHA256 ":"
+#endif
+
+ /* This list is autogenerated with the gen_server_ciphers.py script;
+ * don't hand-edit it. */
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384
+ TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ":"
+#endif
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+ TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ":"
+#endif
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384
+ TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384 ":"
+#endif
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256
+ TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256 ":"
+#endif
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA
+ TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA ":"
+#endif
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA
+ TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA ":"
+#endif
+#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_GCM_SHA384
+ TLS1_TXT_DHE_RSA_WITH_AES_256_GCM_SHA384 ":"
+#endif
+#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256
+ TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256 ":"
+#endif
+#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_CCM
+ TLS1_TXT_DHE_RSA_WITH_AES_256_CCM ":"
+#endif
+#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_CCM
+ TLS1_TXT_DHE_RSA_WITH_AES_128_CCM ":"
+#endif
+#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_SHA256
+ TLS1_TXT_DHE_RSA_WITH_AES_256_SHA256 ":"
+#endif
+#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_SHA256
+ TLS1_TXT_DHE_RSA_WITH_AES_128_SHA256 ":"
+#endif
+ /* Required */
+ TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":"
+ /* Required */
+ TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":"
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305
+ TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305 ":"
+#endif
+#ifdef TLS1_TXT_DHE_RSA_WITH_CHACHA20_POLY1305
+ TLS1_TXT_DHE_RSA_WITH_CHACHA20_POLY1305
+#endif
+ ;
+
+/* Note: to set up your own private testing network with link crypto
+ * disabled, set your Tors' cipher list to
+ * (SSL3_TXT_RSA_NULL_SHA). If you do this, you won't be able to communicate
+ * with any of the "real" Tors, though. */
+
+#define CIPHER(id, name) name ":"
+#define XCIPHER(id, name)
+/** List of ciphers that clients should advertise, omitting items that
+ * our OpenSSL doesn't know about. */
+static const char CLIENT_CIPHER_LIST[] =
+#include "ciphers.inc"
+ /* Tell it not to use SSLv2 ciphers, so that it can select an SSLv3 version
+ * of any cipher we say. */
+ "!SSLv2"
+ ;
+#undef CIPHER
+#undef XCIPHER
+
+/** Remove a reference to <b>ctx</b>, and free it if it has no more
+ * references. */
+void
+tor_tls_context_decref(tor_tls_context_t *ctx)
+{
+ tor_assert(ctx);
+ if (--ctx->refcnt == 0) {
+ SSL_CTX_free(ctx->ctx);
+ tor_x509_cert_free(ctx->my_link_cert);
+ tor_x509_cert_free(ctx->my_id_cert);
+ tor_x509_cert_free(ctx->my_auth_cert);
+ crypto_pk_free(ctx->link_key);
+ crypto_pk_free(ctx->auth_key);
+ /* LCOV_EXCL_BR_START since ctx will never be NULL here */
+ tor_free(ctx);
+ /* LCOV_EXCL_BR_STOP */
+ }
+}
+
+/** Set *<b>link_cert_out</b> and *<b>id_cert_out</b> to the link certificate
+ * and ID certificate that we're currently using for our V3 in-protocol
+ * handshake's certificate chain. If <b>server</b> is true, provide the certs
+ * that we use in server mode (auth, ID); otherwise, provide the certs that we
+ * use in client mode. (link, ID) */
+int
+tor_tls_get_my_certs(int server,
+ const tor_x509_cert_t **link_cert_out,
+ const tor_x509_cert_t **id_cert_out)
+{
+ tor_tls_context_t *ctx = tor_tls_context_get(server);
+ if (! ctx)
+ return -1;
+ if (link_cert_out)
+ *link_cert_out = server ? ctx->my_link_cert : ctx->my_auth_cert;
+ if (id_cert_out)
+ *id_cert_out = ctx->my_id_cert;
+ return 0;
+}
+
+/**
+ * Return the authentication key that we use to authenticate ourselves as a
+ * client in the V3 in-protocol handshake.
+ */
+crypto_pk_t *
+tor_tls_get_my_client_auth_key(void)
+{
+ tor_tls_context_t *context = tor_tls_context_get(0);
+ if (! context)
+ return NULL;
+ return context->auth_key;
+}
+
+/** 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.
+ */
+MOCK_IMPL(int,
+tor_tls_cert_matches_key,(const tor_tls_t *tls, const tor_x509_cert_t *cert))
+{
+ X509 *peercert = SSL_get_peer_certificate(tls->ssl);
+ EVP_PKEY *link_key = NULL, *cert_key = NULL;
+ int result;
+
+ if (!peercert)
+ return 0;
+ link_key = X509_get_pubkey(peercert);
+ cert_key = X509_get_pubkey((X509 *)tor_x509_cert_get_impl(cert));
+
+ result = link_key && cert_key && EVP_PKEY_cmp(cert_key, link_key) == 1;
+
+ X509_free(peercert);
+ if (link_key)
+ EVP_PKEY_free(link_key);
+ if (cert_key)
+ EVP_PKEY_free(cert_key);
+
+ return result;
+}
+
+/** Create a new global TLS context.
+ *
+ * You can call this function multiple times. Each time you call it,
+ * it generates new certificates; all new connections will use
+ * the new SSL context.
+ */
+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)
+{
+ tor_tls_context_t *new_ctx = tor_tls_context_new(identity,
+ key_lifetime,
+ flags,
+ is_client);
+ tor_tls_context_t *old_ctx = *ppcontext;
+
+ if (new_ctx != NULL) {
+ *ppcontext = new_ctx;
+
+ /* Free the old context if one existed. */
+ if (old_ctx != NULL) {
+ /* This is safe even if there are open connections: we reference-
+ * count tor_tls_context_t objects. */
+ tor_tls_context_decref(old_ctx);
+ }
+ }
+
+ return ((new_ctx != NULL) ? 0 : -1);
+}
+
+/** The group we should use for ecdhe when none was selected. */
+#define NID_tor_default_ecdhe_group NID_X9_62_prime256v1
+
+#define RSA_LINK_KEY_BITS 2048
+
+/** Create a new TLS context for use with Tor TLS handshakes.
+ * <b>identity</b> should be set to the identity key used to sign the
+ * certificate.
+ */
+tor_tls_context_t *
+tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
+ unsigned flags, int is_client)
+{
+ crypto_pk_t *rsa = NULL, *rsa_auth = NULL;
+ EVP_PKEY *pkey = NULL;
+ tor_tls_context_t *result = NULL;
+ X509 *cert = NULL, *idcert = NULL, *authcert = NULL;
+ char *nickname = NULL, *nn2 = NULL;
+
+ tor_tls_init();
+ nickname = crypto_random_hostname(8, 20, "www.", ".net");
+#ifdef DISABLE_V3_LINKPROTO_SERVERSIDE
+ nn2 = crypto_random_hostname(8, 20, "www.", ".net");
+#else
+ nn2 = crypto_random_hostname(8, 20, "www.", ".com");
+#endif
+
+ /* Generate short-term RSA key for use with TLS. */
+ if (!(rsa = crypto_pk_new()))
+ goto error;
+ if (crypto_pk_generate_key_with_bits(rsa, RSA_LINK_KEY_BITS)<0)
+ goto error;
+ if (!is_client) {
+ /* Generate short-term RSA key for use in the in-protocol ("v3")
+ * authentication handshake. */
+ if (!(rsa_auth = crypto_pk_new()))
+ goto error;
+ if (crypto_pk_generate_key(rsa_auth)<0)
+ goto error;
+ /* Create a link certificate signed by identity key. */
+ cert = tor_tls_create_certificate(rsa, identity, nickname, nn2,
+ key_lifetime);
+ /* Create self-signed certificate for identity key. */
+ idcert = tor_tls_create_certificate(identity, identity, nn2, nn2,
+ IDENTITY_CERT_LIFETIME);
+ /* Create an authentication certificate signed by identity key. */
+ authcert = tor_tls_create_certificate(rsa_auth, identity, nickname, nn2,
+ key_lifetime);
+ if (!cert || !idcert || !authcert) {
+ log_warn(LD_CRYPTO, "Error creating certificate");
+ goto error;
+ }
+ }
+
+ result = tor_malloc_zero(sizeof(tor_tls_context_t));
+ result->refcnt = 1;
+ if (!is_client) {
+ result->my_link_cert = tor_x509_cert_new(X509_dup(cert));
+ result->my_id_cert = tor_x509_cert_new(X509_dup(idcert));
+ result->my_auth_cert = tor_x509_cert_new(X509_dup(authcert));
+ if (!result->my_link_cert || !result->my_id_cert || !result->my_auth_cert)
+ goto error;
+ result->link_key = crypto_pk_dup_key(rsa);
+ result->auth_key = crypto_pk_dup_key(rsa_auth);
+ }
+
+#if 0
+ /* Tell OpenSSL to only use TLS1. This may have subtly different results
+ * from SSLv23_method() with SSLv2 and SSLv3 disabled, so we need to do some
+ * investigation before we consider adjusting it. It should be compatible
+ * with existing Tors. */
+ if (!(result->ctx = SSL_CTX_new(TLSv1_method())))
+ goto error;
+#endif /* 0 */
+
+ /* Tell OpenSSL to use TLS 1.0 or later but not SSL2 or SSL3. */
+#ifdef HAVE_TLS_METHOD
+ if (!(result->ctx = SSL_CTX_new(TLS_method())))
+ goto error;
+#else
+ if (!(result->ctx = SSL_CTX_new(SSLv23_method())))
+ goto error;
+#endif /* defined(HAVE_TLS_METHOD) */
+ SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv2);
+ SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv3);
+
+ /* Prefer the server's ordering of ciphers: the client's ordering has
+ * historically been chosen for fingerprinting resistance. */
+ SSL_CTX_set_options(result->ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
+
+ /* Disable TLS tickets if they're supported. We never want to use them;
+ * using them can make our perfect forward secrecy a little worse, *and*
+ * create an opportunity to fingerprint us (since it's unusual to use them
+ * with TLS sessions turned off).
+ *
+ * In 0.2.4, clients advertise support for them though, to avoid a TLS
+ * distinguishability vector. This can give us worse PFS, though, if we
+ * get a server that doesn't set SSL_OP_NO_TICKET. With luck, there will
+ * be few such servers by the time 0.2.4 is more stable.
+ */
+#ifdef SSL_OP_NO_TICKET
+ if (! is_client) {
+ SSL_CTX_set_options(result->ctx, SSL_OP_NO_TICKET);
+ }
+#endif
+
+ SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_DH_USE);
+ SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_ECDH_USE);
+
+#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
+ SSL_CTX_set_options(result->ctx,
+ SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
+#endif
+ /* Yes, we know what we are doing here. No, we do not treat a renegotiation
+ * as authenticating any earlier-received data.
+ */
+ {
+ SSL_CTX_set_options(result->ctx,
+ SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
+ }
+
+ /* Don't actually allow compression; it uses RAM and time, it makes TLS
+ * vulnerable to CRIME-style attacks, and most of the data we transmit over
+ * TLS is encrypted (and therefore uncompressible) anyway. */
+#ifdef SSL_OP_NO_COMPRESSION
+ SSL_CTX_set_options(result->ctx, SSL_OP_NO_COMPRESSION);
+#endif
+#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0)
+#ifndef OPENSSL_NO_COMP
+ if (result->ctx->comp_methods)
+ result->ctx->comp_methods = NULL;
+#endif
+#endif /* OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0) */
+
+#ifdef SSL_MODE_RELEASE_BUFFERS
+ SSL_CTX_set_mode(result->ctx, SSL_MODE_RELEASE_BUFFERS);
+#endif
+ if (! is_client) {
+ if (cert && !SSL_CTX_use_certificate(result->ctx,cert))
+ goto error;
+ X509_free(cert); /* We just added a reference to cert. */
+ cert=NULL;
+ if (idcert) {
+ X509_STORE *s = SSL_CTX_get_cert_store(result->ctx);
+ tor_assert(s);
+ X509_STORE_add_cert(s, idcert);
+ X509_free(idcert); /* The context now owns the reference to idcert */
+ idcert = NULL;
+ }
+ }
+ SSL_CTX_set_session_cache_mode(result->ctx, SSL_SESS_CACHE_OFF);
+ if (!is_client) {
+ tor_assert(rsa);
+ if (!(pkey = crypto_pk_get_openssl_evp_pkey_(rsa,1)))
+ goto error;
+ if (!SSL_CTX_use_PrivateKey(result->ctx, pkey))
+ goto error;
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ if (!SSL_CTX_check_private_key(result->ctx))
+ goto error;
+ }
+ {
+ DH *dh = crypto_dh_new_openssl_tls();
+ tor_assert(dh);
+ SSL_CTX_set_tmp_dh(result->ctx, dh);
+ DH_free(dh);
+ }
+ if (! is_client) {
+ int nid;
+ EC_KEY *ec_key;
+ if (flags & TOR_TLS_CTX_USE_ECDHE_P224)
+ nid = NID_secp224r1;
+ else if (flags & TOR_TLS_CTX_USE_ECDHE_P256)
+ nid = NID_X9_62_prime256v1;
+ else
+ nid = NID_tor_default_ecdhe_group;
+ /* Use P-256 for ECDHE. */
+ ec_key = EC_KEY_new_by_curve_name(nid);
+ if (ec_key != NULL) /*XXXX Handle errors? */
+ SSL_CTX_set_tmp_ecdh(result->ctx, ec_key);
+ EC_KEY_free(ec_key);
+ }
+ SSL_CTX_set_verify(result->ctx, SSL_VERIFY_PEER,
+ always_accept_verify_cb);
+ /* let us realloc bufs that we're writing from */
+ SSL_CTX_set_mode(result->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+ if (rsa)
+ crypto_pk_free(rsa);
+ if (rsa_auth)
+ crypto_pk_free(rsa_auth);
+ X509_free(authcert);
+ tor_free(nickname);
+ tor_free(nn2);
+ return result;
+
+ error:
+ tls_log_errors(NULL, LOG_WARN, LD_NET, "creating TLS context");
+ tor_free(nickname);
+ tor_free(nn2);
+ if (pkey)
+ EVP_PKEY_free(pkey);
+ if (rsa)
+ crypto_pk_free(rsa);
+ if (rsa_auth)
+ crypto_pk_free(rsa_auth);
+ if (result)
+ tor_tls_context_decref(result);
+ if (cert)
+ X509_free(cert);
+ if (idcert)
+ X509_free(idcert);
+ if (authcert)
+ X509_free(authcert);
+ return NULL;
+}
+
+/** Invoked when a TLS state changes: log the change at severity 'debug' */
+void
+tor_tls_debug_state_callback(const SSL *ssl, int type, int val)
+{
+ /* LCOV_EXCL_START since this depends on whether debug is captured or not */
+ log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].",
+ ssl, SSL_state_string_long(ssl), type, val);
+ /* LCOV_EXCL_STOP */
+}
+
+/* Return the name of the negotiated ciphersuite in use on <b>tls</b> */
+const char *
+tor_tls_get_ciphersuite_name(tor_tls_t *tls)
+{
+ return SSL_get_cipher(tls->ssl);
+}
+
+/* Here's the old V2 cipher list we sent from 0.2.1.1-alpha up to
+ * 0.2.3.17-beta. If a client is using this list, we can't believe the ciphers
+ * that it claims to support. We'll prune this list to remove the ciphers
+ * *we* don't recognize. */
+STATIC uint16_t v2_cipher_list[] = {
+ 0xc00a, /* TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA */
+ 0xc014, /* TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA */
+ 0x0039, /* TLS1_TXT_DHE_RSA_WITH_AES_256_SHA */
+ 0x0038, /* TLS1_TXT_DHE_DSS_WITH_AES_256_SHA */
+ 0xc00f, /* TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA */
+ 0xc005, /* TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA */
+ 0x0035, /* TLS1_TXT_RSA_WITH_AES_256_SHA */
+ 0xc007, /* TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA */
+ 0xc009, /* TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA */
+ 0xc011, /* TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA */
+ 0xc013, /* TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA */
+ 0x0033, /* TLS1_TXT_DHE_RSA_WITH_AES_128_SHA */
+ 0x0032, /* TLS1_TXT_DHE_DSS_WITH_AES_128_SHA */
+ 0xc00c, /* TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA */
+ 0xc00e, /* TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA */
+ 0xc002, /* TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA */
+ 0xc004, /* TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA */
+ 0x0004, /* SSL3_TXT_RSA_RC4_128_MD5 */
+ 0x0005, /* SSL3_TXT_RSA_RC4_128_SHA */
+ 0x002f, /* TLS1_TXT_RSA_WITH_AES_128_SHA */
+ 0xc008, /* TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA */
+ 0xc012, /* TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA */
+ 0x0016, /* SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA */
+ 0x0013, /* SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA */
+ 0xc00d, /* TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA */
+ 0xc003, /* TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA */
+ 0xfeff, /* SSL3_TXT_RSA_FIPS_WITH_3DES_EDE_CBC_SHA */
+ 0x000a, /* SSL3_TXT_RSA_DES_192_CBC3_SHA */
+ 0
+};
+/** Have we removed the unrecognized ciphers from v2_cipher_list yet? */
+static int v2_cipher_list_pruned = 0;
+
+/** Return 0 if <b>m</b> does not support the cipher with ID <b>cipher</b>;
+ * return 1 if it does support it, or if we have no way to tell. */
+int
+find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher)
+{
+ const SSL_CIPHER *c;
+#ifdef HAVE_SSL_CIPHER_FIND
+ (void) m;
+ {
+ unsigned char cipherid[3];
+ tor_assert(ssl);
+ set_uint16(cipherid, tor_htons(cipher));
+ cipherid[2] = 0; /* If ssl23_get_cipher_by_char finds no cipher starting
+ * with a two-byte 'cipherid', it may look for a v2
+ * cipher with the appropriate 3 bytes. */
+ c = SSL_CIPHER_find((SSL*)ssl, cipherid);
+ if (c)
+ tor_assert((SSL_CIPHER_get_id(c) & 0xffff) == cipher);
+ return c != NULL;
+ }
+#else /* !(defined(HAVE_SSL_CIPHER_FIND)) */
+
+# if defined(HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR)
+ if (m && m->get_cipher_by_char) {
+ unsigned char cipherid[3];
+ set_uint16(cipherid, tor_htons(cipher));
+ cipherid[2] = 0; /* If ssl23_get_cipher_by_char finds no cipher starting
+ * with a two-byte 'cipherid', it may look for a v2
+ * cipher with the appropriate 3 bytes. */
+ c = m->get_cipher_by_char(cipherid);
+ if (c)
+ tor_assert((c->id & 0xffff) == cipher);
+ return c != NULL;
+ }
+#endif /* defined(HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR) */
+# ifndef OPENSSL_1_1_API
+ if (m && m->get_cipher && m->num_ciphers) {
+ /* It would seem that some of the "let's-clean-up-openssl" forks have
+ * removed the get_cipher_by_char function. Okay, so now you get a
+ * quadratic search.
+ */
+ int i;
+ for (i = 0; i < m->num_ciphers(); ++i) {
+ c = m->get_cipher(i);
+ if (c && (c->id & 0xffff) == cipher) {
+ return 1;
+ }
+ }
+ return 0;
+ }
+#endif /* !defined(OPENSSL_1_1_API) */
+ (void) ssl;
+ (void) m;
+ (void) cipher;
+ return 1; /* No way to search */
+#endif /* defined(HAVE_SSL_CIPHER_FIND) */
+}
+
+/** Remove from v2_cipher_list every cipher that we don't support, so that
+ * comparing v2_cipher_list to a client's cipher list will give a sensible
+ * result. */
+static void
+prune_v2_cipher_list(const SSL *ssl)
+{
+ uint16_t *inp, *outp;
+#ifdef HAVE_TLS_METHOD
+ const SSL_METHOD *m = TLS_method();
+#else
+ const SSL_METHOD *m = SSLv23_method();
+#endif
+
+ inp = outp = v2_cipher_list;
+ while (*inp) {
+ if (find_cipher_by_id(ssl, m, *inp)) {
+ *outp++ = *inp++;
+ } else {
+ inp++;
+ }
+ }
+ *outp = 0;
+
+ v2_cipher_list_pruned = 1;
+}
+
+/** Examine the client cipher list in <b>ssl</b>, and determine what kind of
+ * client it is. Return one of CIPHERS_ERR, CIPHERS_V1, CIPHERS_V2,
+ * CIPHERS_UNRESTRICTED.
+ **/
+int
+tor_tls_classify_client_ciphers(const SSL *ssl,
+ STACK_OF(SSL_CIPHER) *peer_ciphers)
+{
+ int i, res;
+ tor_tls_t *tor_tls;
+ if (PREDICT_UNLIKELY(!v2_cipher_list_pruned))
+ prune_v2_cipher_list(ssl);
+
+ tor_tls = tor_tls_get_by_ssl(ssl);
+ if (tor_tls && tor_tls->client_cipher_list_type)
+ return tor_tls->client_cipher_list_type;
+
+ /* If we reached this point, we just got a client hello. See if there is
+ * a cipher list. */
+ if (!peer_ciphers) {
+ log_info(LD_NET, "No ciphers on session");
+ res = CIPHERS_ERR;
+ goto done;
+ }
+ /* Now we need to see if there are any ciphers whose presence means we're
+ * dealing with an updated Tor. */
+ for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) {
+ const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i);
+ const char *ciphername = SSL_CIPHER_get_name(cipher);
+ if (strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA) &&
+ strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA) &&
+ strcmp(ciphername, SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA) &&
+ strcmp(ciphername, "(NONE)")) {
+ log_debug(LD_NET, "Got a non-version-1 cipher called '%s'", ciphername);
+ // return 1;
+ goto v2_or_higher;
+ }
+ }
+ res = CIPHERS_V1;
+ goto done;
+ v2_or_higher:
+ {
+ const uint16_t *v2_cipher = v2_cipher_list;
+ for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) {
+ const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i);
+ uint16_t id = SSL_CIPHER_get_id(cipher) & 0xffff;
+ if (id == 0x00ff) /* extended renegotiation indicator. */
+ continue;
+ if (!id || id != *v2_cipher) {
+ res = CIPHERS_UNRESTRICTED;
+ goto dump_ciphers;
+ }
+ ++v2_cipher;
+ }
+ if (*v2_cipher != 0) {
+ res = CIPHERS_UNRESTRICTED;
+ goto dump_ciphers;
+ }
+ res = CIPHERS_V2;
+ }
+
+ dump_ciphers:
+ {
+ smartlist_t *elts = smartlist_new();
+ char *s;
+ for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) {
+ const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i);
+ const char *ciphername = SSL_CIPHER_get_name(cipher);
+ smartlist_add(elts, (char*)ciphername);
+ }
+ s = smartlist_join_strings(elts, ":", 0, NULL);
+ log_debug(LD_NET, "Got a %s V2/V3 cipher list from %s. It is: '%s'",
+ (res == CIPHERS_V2) ? "fictitious" : "real", ADDR(tor_tls), s);
+ tor_free(s);
+ smartlist_free(elts);
+ }
+ done:
+ if (tor_tls)
+ return tor_tls->client_cipher_list_type = res;
+
+ return res;
+}
+
+/** Return true iff the cipher list suggested by the client for <b>ssl</b> is
+ * a list that indicates that the client knows how to do the v2 TLS connection
+ * handshake. */
+int
+tor_tls_client_is_using_v2_ciphers(const SSL *ssl)
+{
+ STACK_OF(SSL_CIPHER) *ciphers;
+#ifdef HAVE_SSL_GET_CLIENT_CIPHERS
+ ciphers = SSL_get_client_ciphers(ssl);
+#else
+ SSL_SESSION *session;
+ if (!(session = SSL_get_session((SSL *)ssl))) {
+ log_info(LD_NET, "No session on TLS?");
+ return CIPHERS_ERR;
+ }
+ ciphers = session->ciphers;
+#endif /* defined(HAVE_SSL_GET_CLIENT_CIPHERS) */
+
+ return tor_tls_classify_client_ciphers(ssl, ciphers) >= CIPHERS_V2;
+}
+
+/** Invoked when we're accepting a connection on <b>ssl</b>, and the connection
+ * changes state. We use this:
+ * <ul><li>To alter the state of the handshake partway through, so we
+ * do not send or request extra certificates in v2 handshakes.</li>
+ * <li>To detect renegotiation</li></ul>
+ */
+void
+tor_tls_server_info_callback(const SSL *ssl, int type, int val)
+{
+ tor_tls_t *tls;
+ (void) val;
+
+ IF_BUG_ONCE(ssl == NULL) {
+ return; // LCOV_EXCL_LINE
+ }
+
+ tor_tls_debug_state_callback(ssl, type, val);
+
+ if (type != SSL_CB_ACCEPT_LOOP)
+ return;
+
+ OSSL_HANDSHAKE_STATE ssl_state = SSL_get_state(ssl);
+ if (! STATE_IS_SW_SERVER_HELLO(ssl_state))
+ return;
+ tls = tor_tls_get_by_ssl(ssl);
+ if (tls) {
+ /* Check whether we're watching for renegotiates. If so, this is one! */
+ if (tls->negotiated_callback)
+ tls->got_renegotiate = 1;
+ if (tls->server_handshake_count < 127) /*avoid any overflow possibility*/
+ ++tls->server_handshake_count;
+ } else {
+ log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!");
+ return;
+ }
+
+ /* Now check the cipher list. */
+ if (tor_tls_client_is_using_v2_ciphers(ssl)) {
+ if (tls->wasV2Handshake)
+ return; /* We already turned this stuff off for the first handshake;
+ * This is a renegotiation. */
+
+ /* Yes, we're casting away the const from ssl. This is very naughty of us.
+ * Let's hope openssl doesn't notice! */
+
+ /* Set SSL_MODE_NO_AUTO_CHAIN to keep from sending back any extra certs. */
+ SSL_set_mode((SSL*) ssl, SSL_MODE_NO_AUTO_CHAIN);
+ /* Don't send a hello request. */
+ SSL_set_verify((SSL*) ssl, SSL_VERIFY_NONE, NULL);
+
+ if (tls) {
+ tls->wasV2Handshake = 1;
+ } else {
+ /* LCOV_EXCL_START this line is not reachable */
+ log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!");
+ /* LCOV_EXCL_STOP */
+ }
+ }
+}
+
+/** Callback to get invoked on a server after we've read the list of ciphers
+ * the client supports, but before we pick our own ciphersuite.
+ *
+ * We can't abuse an info_cb for this, since by the time one of the
+ * client_hello info_cbs is called, we've already picked which ciphersuite to
+ * use.
+ *
+ * Technically, this function is an abuse of this callback, since the point of
+ * a session_secret_cb is to try to set up and/or verify a shared-secret for
+ * authentication on the fly. But as long as we return 0, we won't actually be
+ * setting up a shared secret, and all will be fine.
+ */
+int
+tor_tls_session_secret_cb(SSL *ssl, void *secret, int *secret_len,
+ STACK_OF(SSL_CIPHER) *peer_ciphers,
+ CONST_IF_OPENSSL_1_1_API SSL_CIPHER **cipher,
+ void *arg)
+{
+ (void) secret;
+ (void) secret_len;
+ (void) peer_ciphers;
+ (void) cipher;
+ (void) arg;
+
+ if (tor_tls_classify_client_ciphers(ssl, peer_ciphers) ==
+ CIPHERS_UNRESTRICTED) {
+ SSL_set_cipher_list(ssl, UNRESTRICTED_SERVER_CIPHER_LIST);
+ }
+
+ SSL_set_session_secret_cb(ssl, NULL, NULL);
+
+ return 0;
+}
+static void
+tor_tls_setup_session_secret_cb(tor_tls_t *tls)
+{
+ SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL);
+}
+
+/** Create a new TLS object from a file descriptor, and a flag to
+ * determine whether it is functioning as a server.
+ */
+tor_tls_t *
+tor_tls_new(int sock, int isServer)
+{
+ BIO *bio = NULL;
+ tor_tls_t *result = tor_malloc_zero(sizeof(tor_tls_t));
+ tor_tls_context_t *context = tor_tls_context_get(isServer);
+ result->magic = TOR_TLS_MAGIC;
+
+ check_no_tls_errors();
+ tor_assert(context); /* make sure somebody made it first */
+ if (!(result->ssl = SSL_new(context->ctx))) {
+ tls_log_errors(NULL, LOG_WARN, LD_NET, "creating SSL object");
+ tor_free(result);
+ goto err;
+ }
+
+#ifdef SSL_set_tlsext_host_name
+ /* Browsers use the TLS hostname extension, so we should too. */
+ if (!isServer) {
+ char *fake_hostname = crypto_random_hostname(4,25, "www.",".com");
+ SSL_set_tlsext_host_name(result->ssl, fake_hostname);
+ tor_free(fake_hostname);
+ }
+#endif /* defined(SSL_set_tlsext_host_name) */
+
+ if (!SSL_set_cipher_list(result->ssl,
+ isServer ? SERVER_CIPHER_LIST : CLIENT_CIPHER_LIST)) {
+ tls_log_errors(NULL, LOG_WARN, LD_NET, "setting ciphers");
+#ifdef SSL_set_tlsext_host_name
+ SSL_set_tlsext_host_name(result->ssl, NULL);
+#endif
+ SSL_free(result->ssl);
+ tor_free(result);
+ goto err;
+ }
+ result->socket = sock;
+ bio = BIO_new_socket(sock, BIO_NOCLOSE);
+ if (! bio) {
+ tls_log_errors(NULL, LOG_WARN, LD_NET, "opening BIO");
+#ifdef SSL_set_tlsext_host_name
+ SSL_set_tlsext_host_name(result->ssl, NULL);
+#endif
+ SSL_free(result->ssl);
+ tor_free(result);
+ goto err;
+ }
+ {
+ int set_worked =
+ SSL_set_ex_data(result->ssl, tor_tls_object_ex_data_index, result);
+ if (!set_worked) {
+ log_warn(LD_BUG,
+ "Couldn't set the tls for an SSL*; connection will fail");
+ }
+ }
+ SSL_set_bio(result->ssl, bio, bio);
+ tor_tls_context_incref(context);
+ result->context = context;
+ result->state = TOR_TLS_ST_HANDSHAKE;
+ result->isServer = isServer;
+ result->wantwrite_n = 0;
+ result->last_write_count = (unsigned long) BIO_number_written(bio);
+ result->last_read_count = (unsigned long) BIO_number_read(bio);
+ if (result->last_write_count || result->last_read_count) {
+ log_warn(LD_NET, "Newly created BIO has read count %lu, write count %lu",
+ result->last_read_count, result->last_write_count);
+ }
+ if (isServer) {
+ SSL_set_info_callback(result->ssl, tor_tls_server_info_callback);
+ } else {
+ SSL_set_info_callback(result->ssl, tor_tls_debug_state_callback);
+ }
+
+ if (isServer)
+ tor_tls_setup_session_secret_cb(result);
+
+ goto done;
+ err:
+ result = NULL;
+ done:
+ /* Not expected to get called. */
+ tls_log_errors(NULL, LOG_WARN, LD_NET, "creating tor_tls_t object");
+ return result;
+}
+
+/** Set <b>cb</b> to be called with argument <b>arg</b> whenever <b>tls</b>
+ * next gets a client-side renegotiate in the middle of a read. Do not
+ * invoke this function until <em>after</em> initial handshaking is done!
+ */
+void
+tor_tls_set_renegotiate_callback(tor_tls_t *tls,
+ void (*cb)(tor_tls_t *, void *arg),
+ void *arg)
+{
+ tls->negotiated_callback = cb;
+ tls->callback_arg = arg;
+ tls->got_renegotiate = 0;
+ if (cb) {
+ SSL_set_info_callback(tls->ssl, tor_tls_server_info_callback);
+ } else {
+ SSL_set_info_callback(tls->ssl, tor_tls_debug_state_callback);
+ }
+}
+
+/** If this version of openssl requires it, turn on renegotiation on
+ * <b>tls</b>.
+ */
+void
+tor_tls_unblock_renegotiation(tor_tls_t *tls)
+{
+ /* Yes, we know what we are doing here. No, we do not treat a renegotiation
+ * as authenticating any earlier-received data. */
+ SSL_set_options(tls->ssl,
+ SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
+}
+
+/** If this version of openssl supports it, turn off renegotiation on
+ * <b>tls</b>. (Our protocol never requires this for security, but it's nice
+ * to use belt-and-suspenders here.)
+ */
+void
+tor_tls_block_renegotiation(tor_tls_t *tls)
+{
+#ifdef SUPPORT_UNSAFE_RENEGOTIATION_FLAG
+ tls->ssl->s3->flags &= ~SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
+#else
+ (void) tls;
+#endif
+}
+
+/** Assert that the flags that allow legacy renegotiation are still set */
+void
+tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls)
+{
+#if defined(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) && \
+ SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION != 0
+ long options = SSL_get_options(tls->ssl);
+ tor_assert(0 != (options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION));
+#else
+ (void) tls;
+#endif /* defined(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) && ... */
+}
+
+/** Release resources associated with a TLS object. Does not close the
+ * underlying file descriptor.
+ */
+void
+tor_tls_free_(tor_tls_t *tls)
+{
+ if (!tls)
+ return;
+ tor_assert(tls->ssl);
+ {
+ size_t r,w;
+ tor_tls_get_n_raw_bytes(tls,&r,&w); /* ensure written_by_tls is updated */
+ }
+#ifdef SSL_set_tlsext_host_name
+ SSL_set_tlsext_host_name(tls->ssl, NULL);
+#endif
+ SSL_free(tls->ssl);
+ tls->ssl = NULL;
+ tls->negotiated_callback = NULL;
+ if (tls->context)
+ tor_tls_context_decref(tls->context);
+ tor_free(tls->address);
+ tls->magic = 0x99999999;
+ tor_free(tls);
+}
+
+/** Underlying function for TLS reading. Reads up to <b>len</b>
+ * characters from <b>tls</b> into <b>cp</b>. On success, returns the
+ * number of characters read. On failure, returns TOR_TLS_ERROR,
+ * TOR_TLS_CLOSE, TOR_TLS_WANTREAD, or TOR_TLS_WANTWRITE.
+ */
+MOCK_IMPL(int,
+tor_tls_read,(tor_tls_t *tls, char *cp, size_t len))
+{
+ int r, err;
+ tor_assert(tls);
+ tor_assert(tls->ssl);
+ tor_assert(tls->state == TOR_TLS_ST_OPEN);
+ tor_assert(len<INT_MAX);
+ r = SSL_read(tls->ssl, cp, (int)len);
+ if (r > 0) {
+ if (tls->got_renegotiate) {
+ /* Renegotiation happened! */
+ log_info(LD_NET, "Got a TLS renegotiation from %s", ADDR(tls));
+ if (tls->negotiated_callback)
+ tls->negotiated_callback(tls, tls->callback_arg);
+ tls->got_renegotiate = 0;
+ }
+ return r;
+ }
+ err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading", LOG_DEBUG, LD_NET);
+ if (err == TOR_TLS_ZERORETURN_ || err == TOR_TLS_CLOSE) {
+ log_debug(LD_NET,"read returned r=%d; TLS is closed",r);
+ tls->state = TOR_TLS_ST_CLOSED;
+ return TOR_TLS_CLOSE;
+ } else {
+ tor_assert(err != TOR_TLS_DONE);
+ log_debug(LD_NET,"read returned r=%d, err=%d",r,err);
+ return err;
+ }
+}
+
+/** Total number of bytes that we've used TLS to send. Used to track TLS
+ * overhead. */
+STATIC uint64_t total_bytes_written_over_tls = 0;
+/** Total number of bytes that TLS has put on the network for us. Used to
+ * track TLS overhead. */
+STATIC uint64_t total_bytes_written_by_tls = 0;
+
+/** Underlying function for TLS writing. Write up to <b>n</b>
+ * characters from <b>cp</b> onto <b>tls</b>. On success, returns the
+ * number of characters written. On failure, returns TOR_TLS_ERROR,
+ * TOR_TLS_WANTREAD, or TOR_TLS_WANTWRITE.
+ */
+int
+tor_tls_write(tor_tls_t *tls, const char *cp, size_t n)
+{
+ int r, err;
+ tor_assert(tls);
+ tor_assert(tls->ssl);
+ tor_assert(tls->state == TOR_TLS_ST_OPEN);
+ tor_assert(n < INT_MAX);
+ if (n == 0)
+ return 0;
+ if (tls->wantwrite_n) {
+ /* if WANTWRITE last time, we must use the _same_ n as before */
+ tor_assert(n >= tls->wantwrite_n);
+ log_debug(LD_NET,"resuming pending-write, (%d to flush, reusing %d)",
+ (int)n, (int)tls->wantwrite_n);
+ n = tls->wantwrite_n;
+ tls->wantwrite_n = 0;
+ }
+ r = SSL_write(tls->ssl, cp, (int)n);
+ err = tor_tls_get_error(tls, r, 0, "writing", LOG_INFO, LD_NET);
+ if (err == TOR_TLS_DONE) {
+ total_bytes_written_over_tls += r;
+ return r;
+ }
+ if (err == TOR_TLS_WANTWRITE || err == TOR_TLS_WANTREAD) {
+ tls->wantwrite_n = n;
+ }
+ return err;
+}
+
+/** Perform initial handshake on <b>tls</b>. When finished, returns
+ * TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD,
+ * or TOR_TLS_WANTWRITE.
+ */
+int
+tor_tls_handshake(tor_tls_t *tls)
+{
+ int r;
+ tor_assert(tls);
+ tor_assert(tls->ssl);
+ tor_assert(tls->state == TOR_TLS_ST_HANDSHAKE);
+
+ check_no_tls_errors();
+
+ OSSL_HANDSHAKE_STATE oldstate = SSL_get_state(tls->ssl);
+
+ if (tls->isServer) {
+ log_debug(LD_HANDSHAKE, "About to call SSL_accept on %p (%s)", tls,
+ SSL_state_string_long(tls->ssl));
+ r = SSL_accept(tls->ssl);
+ } else {
+ log_debug(LD_HANDSHAKE, "About to call SSL_connect on %p (%s)", tls,
+ SSL_state_string_long(tls->ssl));
+ r = SSL_connect(tls->ssl);
+ }
+
+ OSSL_HANDSHAKE_STATE newstate = SSL_get_state(tls->ssl);
+
+ if (oldstate != newstate)
+ log_debug(LD_HANDSHAKE, "After call, %p was in state %s",
+ tls, SSL_state_string_long(tls->ssl));
+ /* We need to call this here and not earlier, since OpenSSL has a penchant
+ * for clearing its flags when you say accept or connect. */
+ tor_tls_unblock_renegotiation(tls);
+ r = tor_tls_get_error(tls,r,0, "handshaking", LOG_INFO, LD_HANDSHAKE);
+ if (ERR_peek_error() != 0) {
+ tls_log_errors(tls, tls->isServer ? LOG_INFO : LOG_WARN, LD_HANDSHAKE,
+ "handshaking");
+ return TOR_TLS_ERROR_MISC;
+ }
+ if (r == TOR_TLS_DONE) {
+ tls->state = TOR_TLS_ST_OPEN;
+ return tor_tls_finish_handshake(tls);
+ }
+ return r;
+}
+
+/** Perform the final part of the initial TLS handshake on <b>tls</b>. This
+ * should be called for the first handshake only: it determines whether the v1
+ * or the v2 handshake was used, and adjusts things for the renegotiation
+ * handshake as appropriate.
+ *
+ * tor_tls_handshake() calls this on its own; you only need to call this if
+ * bufferevent is doing the handshake for you.
+ */
+int
+tor_tls_finish_handshake(tor_tls_t *tls)
+{
+ int r = TOR_TLS_DONE;
+ check_no_tls_errors();
+ if (tls->isServer) {
+ SSL_set_info_callback(tls->ssl, NULL);
+ SSL_set_verify(tls->ssl, SSL_VERIFY_PEER, always_accept_verify_cb);
+ SSL_clear_mode(tls->ssl, SSL_MODE_NO_AUTO_CHAIN);
+ if (tor_tls_client_is_using_v2_ciphers(tls->ssl)) {
+ /* This check is redundant, but back when we did it in the callback,
+ * we might have not been able to look up the tor_tls_t if the code
+ * was buggy. Fixing that. */
+ if (!tls->wasV2Handshake) {
+ log_warn(LD_BUG, "For some reason, wasV2Handshake didn't"
+ " get set. Fixing that.");
+ }
+ tls->wasV2Handshake = 1;
+ log_debug(LD_HANDSHAKE, "Completed V2 TLS handshake with client; waiting"
+ " for renegotiation.");
+ } else {
+ tls->wasV2Handshake = 0;
+ }
+ } else {
+ /* Client-side */
+ tls->wasV2Handshake = 1;
+ /* XXXX this can move, probably? -NM */
+ if (SSL_set_cipher_list(tls->ssl, SERVER_CIPHER_LIST) == 0) {
+ tls_log_errors(NULL, LOG_WARN, LD_HANDSHAKE, "re-setting ciphers");
+ r = TOR_TLS_ERROR_MISC;
+ }
+ }
+ tls_log_errors(NULL, LOG_WARN, LD_NET, "finishing the handshake");
+ return r;
+}
+
+/** Shut down an open tls connection <b>tls</b>. When finished, returns
+ * TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD,
+ * or TOR_TLS_WANTWRITE.
+ */
+int
+tor_tls_shutdown(tor_tls_t *tls)
+{
+ int r, err;
+ char buf[128];
+ tor_assert(tls);
+ tor_assert(tls->ssl);
+ check_no_tls_errors();
+
+ while (1) {
+ if (tls->state == TOR_TLS_ST_SENTCLOSE) {
+ /* If we've already called shutdown once to send a close message,
+ * we read until the other side has closed too.
+ */
+ do {
+ r = SSL_read(tls->ssl, buf, 128);
+ } while (r>0);
+ err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading to shut down",
+ LOG_INFO, LD_NET);
+ if (err == TOR_TLS_ZERORETURN_) {
+ tls->state = TOR_TLS_ST_GOTCLOSE;
+ /* fall through... */
+ } else {
+ return err;
+ }
+ }
+
+ r = SSL_shutdown(tls->ssl);
+ if (r == 1) {
+ /* If shutdown returns 1, the connection is entirely closed. */
+ tls->state = TOR_TLS_ST_CLOSED;
+ return TOR_TLS_DONE;
+ }
+ err = tor_tls_get_error(tls, r, CATCH_SYSCALL|CATCH_ZERO, "shutting down",
+ LOG_INFO, LD_NET);
+ if (err == TOR_TLS_SYSCALL_) {
+ /* The underlying TCP connection closed while we were shutting down. */
+ tls->state = TOR_TLS_ST_CLOSED;
+ return TOR_TLS_DONE;
+ } else if (err == TOR_TLS_ZERORETURN_) {
+ /* The TLS connection says that it sent a shutdown record, but
+ * isn't done shutting down yet. Make sure that this hasn't
+ * happened before, then go back to the start of the function
+ * and try to read.
+ */
+ if (tls->state == TOR_TLS_ST_GOTCLOSE ||
+ tls->state == TOR_TLS_ST_SENTCLOSE) {
+ log_warn(LD_NET,
+ "TLS returned \"half-closed\" value while already half-closed");
+ return TOR_TLS_ERROR_MISC;
+ }
+ tls->state = TOR_TLS_ST_SENTCLOSE;
+ /* fall through ... */
+ } else {
+ return err;
+ }
+ } /* end loop */
+}
+
+/** Return true iff this TLS connection is authenticated.
+ */
+int
+tor_tls_peer_has_cert(tor_tls_t *tls)
+{
+ X509 *cert;
+ cert = SSL_get_peer_certificate(tls->ssl);
+ tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "getting peer certificate");
+ if (!cert)
+ return 0;
+ X509_free(cert);
+ return 1;
+}
+
+/** Return a newly allocated copy of the peer certificate, or NULL if there
+ * isn't one. */
+MOCK_IMPL(tor_x509_cert_t *,
+tor_tls_get_peer_cert,(tor_tls_t *tls))
+{
+ X509 *cert;
+ cert = SSL_get_peer_certificate(tls->ssl);
+ tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "getting peer certificate");
+ if (!cert)
+ return NULL;
+ return tor_x509_cert_new(cert);
+}
+
+/** Return a newly allocated copy of the cerficate we used on the connection,
+ * or NULL if somehow we didn't use one. */
+MOCK_IMPL(tor_x509_cert_t *,
+tor_tls_get_own_cert,(tor_tls_t *tls))
+{
+ X509 *cert = SSL_get_certificate(tls->ssl);
+ tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE,
+ "getting own-connection certificate");
+ if (!cert)
+ return NULL;
+ /* Fun inconsistency: SSL_get_peer_certificate increments the reference
+ * count, but SSL_get_certificate does not. */
+ X509 *duplicate = X509_dup(cert);
+ if (BUG(duplicate == NULL))
+ return NULL;
+ return tor_x509_cert_new(duplicate);
+}
+
+/** 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
+ * <b>severity</b>.
+ *
+ * Note that a reference is added to cert_out, so it needs to be
+ * freed. id_cert_out doesn't. */
+MOCK_IMPL(void,
+try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls,
+ X509 **cert_out, X509 **id_cert_out))
+{
+ X509 *cert = NULL, *id_cert = NULL;
+ STACK_OF(X509) *chain = NULL;
+ int num_in_chain, i;
+ *cert_out = *id_cert_out = NULL;
+ if (!(cert = SSL_get_peer_certificate(tls->ssl)))
+ return;
+ *cert_out = cert;
+ if (!(chain = SSL_get_peer_cert_chain(tls->ssl)))
+ return;
+ num_in_chain = sk_X509_num(chain);
+ /* 1 means we're receiving (server-side), and it's just the id_cert.
+ * 2 means we're connecting (client-side), and it's both the link
+ * cert and the id_cert.
+ */
+ if (num_in_chain < 1) {
+ log_fn(severity,LD_PROTOCOL,
+ "Unexpected number of certificates in chain (%d)",
+ num_in_chain);
+ return;
+ }
+ for (i=0; i<num_in_chain; ++i) {
+ id_cert = sk_X509_value(chain, i);
+ if (X509_cmp(id_cert, cert) != 0)
+ break;
+ }
+ *id_cert_out = id_cert;
+}
+
+/** If the provided tls connection is authenticated and has a
+ * certificate chain that is currently valid and signed, then set
+ * *<b>identity_key</b> to the identity certificate's key and return
+ * 0. Else, return -1 and log complaints with log-level <b>severity</b>.
+ */
+int
+tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity_key)
+{
+ X509 *cert = NULL, *id_cert = NULL;
+ EVP_PKEY *id_pkey = NULL;
+ RSA *rsa;
+ int r = -1;
+
+ check_no_tls_errors();
+ *identity_key = NULL;
+
+ try_to_extract_certs_from_tls(severity, tls, &cert, &id_cert);
+ if (!cert)
+ goto done;
+ if (!id_cert) {
+ log_fn(severity,LD_PROTOCOL,"No distinct identity certificate found");
+ goto done;
+ }
+ tls_log_errors(tls, severity, LD_HANDSHAKE, "before verifying certificate");
+
+ if (!(id_pkey = X509_get_pubkey(id_cert)) ||
+ X509_verify(cert, id_pkey) <= 0) {
+ log_fn(severity,LD_PROTOCOL,"X509_verify on cert and pkey returned <= 0");
+ tls_log_errors(tls, severity, LD_HANDSHAKE, "verifying certificate");
+ goto done;
+ }
+
+ rsa = EVP_PKEY_get1_RSA(id_pkey);
+ if (!rsa)
+ goto done;
+ *identity_key = crypto_new_pk_from_openssl_rsa_(rsa);
+
+ r = 0;
+
+ done:
+ if (cert)
+ X509_free(cert);
+ if (id_pkey)
+ EVP_PKEY_free(id_pkey);
+
+ /* This should never get invoked, but let's make sure in case OpenSSL
+ * acts unexpectedly. */
+ tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "finishing tor_tls_verify");
+
+ return r;
+}
+
+/** Check whether the certificate set on the connection <b>tls</b> is expired
+ * give or take <b>past_tolerance</b> seconds, or not-yet-valid give or take
+ * <b>future_tolerance</b> seconds. Return 0 for valid, -1 for failure.
+ *
+ * NOTE: you should call tor_tls_verify before tor_tls_check_lifetime.
+ */
+int
+tor_tls_check_lifetime(int severity, tor_tls_t *tls,
+ time_t now,
+ int past_tolerance, int future_tolerance)
+{
+ X509 *cert;
+ int r = -1;
+
+ if (!(cert = SSL_get_peer_certificate(tls->ssl)))
+ goto done;
+
+ if (tor_x509_check_cert_lifetime_internal(severity, cert, now,
+ past_tolerance,
+ future_tolerance) < 0)
+ goto done;
+
+ r = 0;
+ done:
+ if (cert)
+ X509_free(cert);
+ /* Not expected to get invoked */
+ tls_log_errors(tls, LOG_WARN, LD_NET, "checking certificate lifetime");
+
+ return r;
+}
+
+/** Return the number of bytes available for reading from <b>tls</b>.
+ */
+int
+tor_tls_get_pending_bytes(tor_tls_t *tls)
+{
+ tor_assert(tls);
+ return SSL_pending(tls->ssl);
+}
+
+/** If <b>tls</b> requires that the next write be of a particular size,
+ * return that size. Otherwise, return 0. */
+size_t
+tor_tls_get_forced_write_size(tor_tls_t *tls)
+{
+ return tls->wantwrite_n;
+}
+
+/** Sets n_read and n_written to the number of bytes read and written,
+ * respectively, on the raw socket used by <b>tls</b> since the last time this
+ * function was called on <b>tls</b>. */
+void
+tor_tls_get_n_raw_bytes(tor_tls_t *tls, size_t *n_read, size_t *n_written)
+{
+ BIO *wbio, *tmpbio;
+ unsigned long r, w;
+ r = (unsigned long) BIO_number_read(SSL_get_rbio(tls->ssl));
+ /* We want the number of bytes actually for real written. Unfortunately,
+ * sometimes OpenSSL replaces the wbio on tls->ssl with a buffering bio,
+ * which makes the answer turn out wrong. Let's cope with that. Note
+ * that this approach will fail if we ever replace tls->ssl's BIOs with
+ * buffering bios for reasons of our own. As an alternative, we could
+ * save the original BIO for tls->ssl in the tor_tls_t structure, but
+ * that would be tempting fate. */
+ wbio = SSL_get_wbio(tls->ssl);
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5)
+ /* BIO structure is opaque as of OpenSSL 1.1.0-pre5-dev. Again, not
+ * supposed to use this form of the version macro, but the OpenSSL developers
+ * introduced major API changes in the pre-release stage.
+ */
+ if (BIO_method_type(wbio) == BIO_TYPE_BUFFER &&
+ (tmpbio = BIO_next(wbio)) != NULL)
+ wbio = tmpbio;
+#else /* !(OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5)) */
+ if (wbio->method == BIO_f_buffer() && (tmpbio = BIO_next(wbio)) != NULL)
+ wbio = tmpbio;
+#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) */
+ w = (unsigned long) BIO_number_written(wbio);
+
+ /* We are ok with letting these unsigned ints go "negative" here:
+ * If we wrapped around, this should still give us the right answer, unless
+ * we wrapped around by more than ULONG_MAX since the last time we called
+ * this function.
+ */
+ *n_read = (size_t)(r - tls->last_read_count);
+ *n_written = (size_t)(w - tls->last_write_count);
+ if (*n_read > INT_MAX || *n_written > INT_MAX) {
+ log_warn(LD_BUG, "Preposterously large value in tor_tls_get_n_raw_bytes. "
+ "r=%lu, last_read=%lu, w=%lu, last_written=%lu",
+ r, tls->last_read_count, w, tls->last_write_count);
+ }
+ total_bytes_written_by_tls += *n_written;
+ tls->last_read_count = r;
+ tls->last_write_count = w;
+}
+
+/** Return a ratio of the bytes that TLS has sent to the bytes that we've told
+ * it to send. Used to track whether our TLS records are getting too tiny. */
+MOCK_IMPL(double,
+tls_get_write_overhead_ratio,(void))
+{
+ if (total_bytes_written_over_tls == 0)
+ return 1.0;
+
+ return ((double)total_bytes_written_by_tls) /
+ ((double)total_bytes_written_over_tls);
+}
+
+/** Implement check_no_tls_errors: If there are any pending OpenSSL
+ * errors, log an error message. */
+void
+check_no_tls_errors_(const char *fname, int line)
+{
+ if (ERR_peek_error() == 0)
+ return;
+ log_warn(LD_CRYPTO, "Unhandled OpenSSL errors found at %s:%d: ",
+ tor_fix_source_file(fname), line);
+ tls_log_errors(NULL, LOG_WARN, LD_NET, NULL);
+}
+
+/** Return true iff the initial TLS connection at <b>tls</b> did not use a v2
+ * TLS handshake. Output is undefined if the handshake isn't finished. */
+int
+tor_tls_used_v1_handshake(tor_tls_t *tls)
+{
+ return ! tls->wasV2Handshake;
+}
+
+/** Return the number of server handshakes that we've noticed doing on
+ * <b>tls</b>. */
+int
+tor_tls_get_num_server_handshakes(tor_tls_t *tls)
+{
+ return tls->server_handshake_count;
+}
+
+/** Return true iff the server TLS connection <b>tls</b> got the renegotiation
+ * request it was waiting for. */
+int
+tor_tls_server_got_renegotiate(tor_tls_t *tls)
+{
+ return tls->got_renegotiate;
+}
+
+#ifndef HAVE_SSL_GET_CLIENT_RANDOM
+static size_t
+SSL_get_client_random(SSL *s, uint8_t *out, size_t len)
+{
+ if (len == 0)
+ return SSL3_RANDOM_SIZE;
+ tor_assert(len == SSL3_RANDOM_SIZE);
+ tor_assert(s->s3);
+ memcpy(out, s->s3->client_random, len);
+ return len;
+}
+#endif /* !defined(HAVE_SSL_GET_CLIENT_RANDOM) */
+
+#ifndef HAVE_SSL_GET_SERVER_RANDOM
+static size_t
+SSL_get_server_random(SSL *s, uint8_t *out, size_t len)
+{
+ if (len == 0)
+ return SSL3_RANDOM_SIZE;
+ tor_assert(len == SSL3_RANDOM_SIZE);
+ tor_assert(s->s3);
+ memcpy(out, s->s3->server_random, len);
+ return len;
+}
+#endif /* !defined(HAVE_SSL_GET_SERVER_RANDOM) */
+
+#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY
+size_t
+SSL_SESSION_get_master_key(SSL_SESSION *s, uint8_t *out, size_t len)
+{
+ tor_assert(s);
+ if (len == 0)
+ return s->master_key_length;
+ tor_assert(len == (size_t)s->master_key_length);
+ tor_assert(out);
+ memcpy(out, s->master_key, len);
+ return len;
+}
+#endif /* !defined(HAVE_SSL_SESSION_GET_MASTER_KEY) */
+
+/** Set the DIGEST256_LEN buffer at <b>secrets_out</b> to the value used in
+ * the v3 handshake to prove that the client knows the TLS secrets for the
+ * connection <b>tls</b>. Return 0 on success, -1 on failure.
+ */
+MOCK_IMPL(int,
+tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out))
+{
+#define TLSSECRET_MAGIC "Tor V3 handshake TLS cross-certification"
+ uint8_t buf[128];
+ size_t len;
+ tor_assert(tls);
+
+ SSL *const ssl = tls->ssl;
+ SSL_SESSION *const session = SSL_get_session(ssl);
+
+ tor_assert(ssl);
+ tor_assert(session);
+
+ const size_t server_random_len = SSL_get_server_random(ssl, NULL, 0);
+ const size_t client_random_len = SSL_get_client_random(ssl, NULL, 0);
+ const size_t master_key_len = SSL_SESSION_get_master_key(session, NULL, 0);
+
+ tor_assert(server_random_len);
+ tor_assert(client_random_len);
+ tor_assert(master_key_len);
+
+ len = client_random_len + server_random_len + strlen(TLSSECRET_MAGIC) + 1;
+ tor_assert(len <= sizeof(buf));
+
+ {
+ size_t r = SSL_get_client_random(ssl, buf, client_random_len);
+ tor_assert(r == client_random_len);
+ }
+
+ {
+ size_t r = SSL_get_server_random(ssl,
+ buf+client_random_len,
+ server_random_len);
+ tor_assert(r == server_random_len);
+ }
+
+ uint8_t *master_key = tor_malloc_zero(master_key_len);
+ {
+ size_t r = SSL_SESSION_get_master_key(session, master_key, master_key_len);
+ tor_assert(r == master_key_len);
+ }
+
+ uint8_t *nextbuf = buf + client_random_len + server_random_len;
+ memcpy(nextbuf, TLSSECRET_MAGIC, strlen(TLSSECRET_MAGIC) + 1);
+
+ /*
+ The value is an HMAC, using the TLS master key as the HMAC key, of
+ client_random | server_random | TLSSECRET_MAGIC
+ */
+ crypto_hmac_sha256((char*)secrets_out,
+ (char*)master_key,
+ master_key_len,
+ (char*)buf, len);
+ memwipe(buf, 0, sizeof(buf));
+ memwipe(master_key, 0, master_key_len);
+ tor_free(master_key);
+
+ return 0;
+}
+
+/** Using the RFC5705 key material exporting construction, and the
+ * provided <b>context</b> (<b>context_len</b> bytes long) and
+ * <b>label</b> (a NUL-terminated string), compute a 32-byte secret in
+ * <b>secrets_out</b> that only the parties to this TLS session can
+ * compute. Return 0 on success and -1 on failure.
+ */
+MOCK_IMPL(int,
+tor_tls_export_key_material,(tor_tls_t *tls, uint8_t *secrets_out,
+ const uint8_t *context,
+ size_t context_len,
+ const char *label))
+{
+ tor_assert(tls);
+ tor_assert(tls->ssl);
+
+ int r = SSL_export_keying_material(tls->ssl,
+ secrets_out, DIGEST256_LEN,
+ label, strlen(label),
+ context, context_len, 1);
+ return (r == 1) ? 0 : -1;
+}
+
+/** Examine the amount of memory used and available for buffers in <b>tls</b>.
+ * Set *<b>rbuf_capacity</b> to the amount of storage allocated for the read
+ * buffer and *<b>rbuf_bytes</b> to the amount actually used.
+ * Set *<b>wbuf_capacity</b> to the amount of storage allocated for the write
+ * buffer and *<b>wbuf_bytes</b> to the amount actually used.
+ *
+ * Return 0 on success, -1 on failure.*/
+int
+tor_tls_get_buffer_sizes(tor_tls_t *tls,
+ size_t *rbuf_capacity, size_t *rbuf_bytes,
+ size_t *wbuf_capacity, size_t *wbuf_bytes)
+{
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)
+ (void)tls;
+ (void)rbuf_capacity;
+ (void)rbuf_bytes;
+ (void)wbuf_capacity;
+ (void)wbuf_bytes;
+
+ return -1;
+#else /* !(OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)) */
+ if (tls->ssl->s3->rbuf.buf)
+ *rbuf_capacity = tls->ssl->s3->rbuf.len;
+ else
+ *rbuf_capacity = 0;
+ if (tls->ssl->s3->wbuf.buf)
+ *wbuf_capacity = tls->ssl->s3->wbuf.len;
+ else
+ *wbuf_capacity = 0;
+ *rbuf_bytes = tls->ssl->s3->rbuf.left;
+ *wbuf_bytes = tls->ssl->s3->wbuf.left;
+ return 0;
+#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) */
+}
+
+/** Check whether the ECC group requested is supported by the current OpenSSL
+ * library instance. Return 1 if the group is supported, and 0 if not.
+ */
+int
+evaluate_ecgroup_for_tls(const char *ecgroup)
+{
+ EC_KEY *ec_key;
+ int nid;
+ int ret;
+
+ if (!ecgroup)
+ nid = NID_tor_default_ecdhe_group;
+ else if (!strcasecmp(ecgroup, "P256"))
+ nid = NID_X9_62_prime256v1;
+ else if (!strcasecmp(ecgroup, "P224"))
+ nid = NID_secp224r1;
+ else
+ return 0;
+
+ ec_key = EC_KEY_new_by_curve_name(nid);
+ ret = (ec_key != NULL);
+ EC_KEY_free(ec_key);
+
+ return ret;
+}
diff --git a/src/lib/tls/x509.c b/src/lib/tls/x509.c
index 8bed6f5a1..dbf1dd927 100644
--- a/src/lib/tls/x509.c
+++ b/src/lib/tls/x509.c
@@ -4,86 +4,15 @@
/* See LICENSE for licensing information */
/**
- * \file x509.c
+ * \file x509_openssl.c
* \brief Wrapper functions to present a consistent interface to
- * X.509 functions from OpenSSL.
+ * X.509 functions.
**/
#define TOR_X509_PRIVATE
#include "lib/tls/x509.h"
-#include "lib/tls/tortls.h"
-#include "lib/crypt_ops/crypto_rand.h"
-#include "lib/crypt_ops/crypto_util.h"
-#include "lib/crypt_ops/compat_openssl.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 */
-}
+#include "lib/crypt_ops/crypto_rand.h"
/** Choose the start and end times for a certificate */
void
@@ -121,221 +50,6 @@ tor_tls_pick_certificate_lifetime(time_t now,
*end_time_out = end_time;
}
-/** 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();
-
- time_t now = time(NULL);
-
- tor_tls_pick_certificate_lifetime(now, cert_lifetime,
- &start_time, &end_time);
-
- 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
@@ -375,190 +89,3 @@ 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 (tor_x509_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
-tor_x509_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
index e3dfcf393..f75d15d7e 100644
--- a/src/lib/tls/x509.h
+++ b/src/lib/tls/x509.h
@@ -17,7 +17,9 @@
/* Opaque structure to hold an X509 certificate. */
typedef struct tor_x509_cert_t tor_x509_cert_t;
-#ifdef ENABLE_OPENSSL
+#ifdef ENABLE_NSS
+typedef struct CERTCertificateStr tor_x509_cert_impl_t;
+#elif defined(ENABLE_OPENSSL)
typedef struct x509_st tor_x509_cert_impl_t;
#endif
diff --git a/src/lib/tls/x509_nss.c b/src/lib/tls/x509_nss.c
new file mode 100644
index 000000000..e856c9518
--- /dev/null
+++ b/src/lib/tls/x509_nss.c
@@ -0,0 +1,122 @@
+/* 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_nss.c
+ * \brief Wrapper functions to present a consistent interface to
+ * X.509 functions from NSS.
+ **/
+
+#define TOR_X509_PRIVATE
+#include "lib/tls/x509.h"
+#include "lib/tls/tortls.h"
+#include "lib/crypt_ops/crypto_rand.h"
+#include "lib/crypt_ops/crypto_util.h"
+#include "lib/log/util_bug.h"
+
+MOCK_IMPL(tor_x509_cert_impl_t *,
+tor_tls_create_certificate,(crypto_pk_t *rsa,
+ crypto_pk_t *rsa_sign,
+ const char *cname,
+ const char *cname_sign,
+ unsigned int cert_lifetime))
+{
+ tor_assert(rsa);
+ tor_assert(rsa_sign);
+ tor_assert(cname);
+ tor_assert(cname_sign);
+ (void) cert_lifetime;
+ // XXXX
+ return NULL;
+}
+
+MOCK_IMPL(tor_x509_cert_t *,
+tor_x509_cert_new,(tor_x509_cert_impl_t *x509_cert))
+{
+ tor_assert(x509_cert);
+ // XXXX
+ return NULL;
+}
+
+tor_x509_cert_t *
+tor_x509_cert_dup(const tor_x509_cert_t *cert)
+{
+ tor_assert(cert);
+ // XXXX
+ return NULL;
+}
+
+void
+tor_x509_cert_free_(tor_x509_cert_t *cert)
+{
+ (void)cert;
+ // XXXX
+}
+
+tor_x509_cert_t *
+tor_x509_cert_decode(const uint8_t *certificate,
+ size_t certificate_len)
+{
+ tor_assert(certificate);
+ (void) certificate_len;
+ // XXXX
+ return NULL;
+}
+
+crypto_pk_t *
+tor_tls_cert_get_key(tor_x509_cert_t *cert)
+{
+ tor_assert(cert);
+ // XXXXX
+ return NULL;
+}
+
+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)
+{
+ tor_assert(cert);
+ tor_assert(signing_cert);
+ (void)severity;
+ (void)now;
+ (void)check_rsa_1024;
+ // XXXXX
+
+ return 0;
+}
+
+int
+tor_x509_check_cert_lifetime_internal(int severity,
+ const tor_x509_cert_impl_t *cert,
+ time_t now,
+ int past_tolerance,
+ int future_tolerance)
+{
+ tor_assert(cert);
+ (void)severity;
+ (void)now;
+ (void)past_tolerance;
+ (void)future_tolerance;
+ // XXXX
+ return -1;
+}
+
+#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)
+{
+ tor_assert(inp);
+ tor_assert(signing_key);
+ (void)new_expiration_time;
+
+ // XXXX
+ return NULL;
+}
+#endif
diff --git a/src/lib/tls/x509_openssl.c b/src/lib/tls/x509_openssl.c
new file mode 100644
index 000000000..c003d4704
--- /dev/null
+++ b/src/lib/tls/x509_openssl.c
@@ -0,0 +1,489 @@
+/* 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_openssl.c
+ * \brief Wrapper functions to present a consistent interface to
+ * X.509 functions from OpenSSL.
+ **/
+
+#define TOR_X509_PRIVATE
+#include "lib/tls/x509.h"
+#include "lib/tls/tortls.h"
+#include "lib/crypt_ops/crypto_rand.h"
+#include "lib/crypt_ops/crypto_util.h"
+#include "lib/crypt_ops/compat_openssl.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();
+
+ time_t now = time(NULL);
+
+ tor_tls_pick_certificate_lifetime(now, cert_lifetime,
+ &start_time, &end_time);
+
+ 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;
+}
+
+/**
+ * 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 (tor_x509_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
+tor_x509_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/test/include.am b/src/test/include.am
index 68372adc7..c2e08aa3d 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -184,6 +184,14 @@ src_test_test_SOURCES += \
src/test/testing_common.c \
src/test/testing_rsakeys.c \
src/ext/tinytest.c
+
+if USE_NSS
+# ...
+else
+src_test_test_SOURCES += \
+ src/test/test_tortls_openssl.c
+endif
+
endif
src_test_test_slow_SOURCES =
diff --git a/src/test/test.c b/src/test/test.c
index 94eae5a4f..3b63f1c07 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -919,6 +919,9 @@ struct testgroup_t testgroups[] = {
{ "status/" , status_tests },
{ "storagedir/", storagedir_tests },
{ "tortls/", tortls_tests },
+#ifndef ENABLE_NSS
+ { "tortls/openssl/", tortls_openssl_tests },
+#endif
{ "util/", util_tests },
{ "util/format/", util_format_tests },
{ "util/logging/", logging_tests },
diff --git a/src/test/test.h b/src/test/test.h
index b3a73271e..94aa9443d 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -257,6 +257,7 @@ extern struct testcase_t socks_tests[];
extern struct testcase_t status_tests[];
extern struct testcase_t thread_tests[];
extern struct testcase_t tortls_tests[];
+extern struct testcase_t tortls_openssl_tests[];
extern struct testcase_t util_tests[];
extern struct testcase_t util_format_tests[];
extern struct testcase_t util_process_tests[];
diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c
index ec197ba99..d20bc5fa6 100644
--- a/src/test/test_tortls.c
+++ b/src/test/test_tortls.c
@@ -2,7 +2,6 @@
/* See LICENSE for licensing information */
#define TORTLS_PRIVATE
-#define TORTLS_OPENSSL_PRIVATE
#define TOR_X509_PRIVATE
#define LOG_PRIVATE
#include "orconfig.h"
@@ -11,64 +10,24 @@
#include <winsock2.h>
#endif
#include <math.h>
+#include <stddef.h>
#include "lib/cc/compat_compiler.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>
-
-#include <openssl/ssl.h>
-#include <openssl/ssl3.h>
-#include <openssl/err.h>
-#include <openssl/asn1t.h>
-#include <openssl/x509.h>
-#include <openssl/rsa.h>
-#include <openssl/evp.h>
-#include <openssl/bn.h>
-
-ENABLE_GCC_WARNING(redundant-decls)
-
#include "core/or/or.h"
#include "lib/log/log.h"
#include "app/config/config.h"
#include "lib/crypt_ops/compat_openssl.h"
+#include "lib/tls/x509.h"
#include "lib/tls/tortls.h"
#include "lib/tls/tortls_st.h"
-#include "lib/tls/x509.h"
+#include "lib/tls/tortls_internal.h"
#include "app/config/or_state_st.h"
#include "test/test.h"
#include "test/log_test_helpers.h"
-#define NS_MODULE tortls
-
-#ifndef HAVE_SSL_STATE
-#define OPENSSL_OPAQUE
-#endif
-#if defined(OPENSSL_OPAQUE) && !defined(LIBRESSL_VERSION_NUMBER)
-#define SSL_STATE_STR "before SSL initialization"
-#else
-#define SSL_STATE_STR "before/accept initialization"
-#endif
-
-#ifndef OPENSSL_OPAQUE
-static SSL_METHOD *
-give_me_a_test_method(void)
-{
- SSL_METHOD *method = tor_malloc_zero(sizeof(SSL_METHOD));
- memcpy(method, TLSv1_method(), sizeof(SSL_METHOD));
- return method;
-}
-
-static int
-fake_num_ciphers(void)
-{
- return 0;
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
+#include "tinytest.h"
static void
test_tortls_errno_to_tls_error(void *data)
@@ -121,66 +80,6 @@ mock_tls_cert_matches_key(const tor_tls_t *tls, const tor_x509_cert_t *cert)
}
static void
-test_tortls_tor_tls_new(void *data)
-{
- (void) data;
- MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key);
- crypto_pk_t *key1 = NULL, *key2 = NULL;
- SSL_METHOD *method = NULL;
-
- key1 = pk_generate(2);
- key2 = pk_generate(3);
-
- tor_tls_t *tls = NULL;
- tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER,
- key1, key2, 86400), OP_EQ, 0);
- tls = tor_tls_new(-1, 0);
- tt_want(tls);
- tor_tls_free(tls); tls = NULL;
-
- SSL_CTX_free(client_tls_context->ctx);
- client_tls_context->ctx = NULL;
- tls = tor_tls_new(-1, 0);
- tt_ptr_op(tls, OP_EQ, NULL);
-
-#ifndef OPENSSL_OPAQUE
- method = give_me_a_test_method();
- SSL_CTX *ctx = SSL_CTX_new(method);
- method->num_ciphers = fake_num_ciphers;
- client_tls_context->ctx = ctx;
- tls = tor_tls_new(-1, 0);
- tt_ptr_op(tls, OP_EQ, NULL);
-#endif /* !defined(OPENSSL_OPAQUE) */
-
- done:
- UNMOCK(tor_tls_cert_matches_key);
- crypto_pk_free(key1);
- crypto_pk_free(key2);
- tor_tls_free(tls);
- tor_free(method);
- tor_tls_free_all();
-}
-
-#define NS_MODULE tortls
-NS_DECL(void, logv, (int severity, log_domain_mask_t domain,
- const char *funcname, const char *suffix,
- const char *format, va_list ap));
-
-static void
-NS(logv)(int severity, log_domain_mask_t domain,
- const char *funcname, const char *suffix, const char *format,
- va_list ap)
-{
- (void) severity;
- (void) domain;
- (void) funcname;
- (void) suffix;
- (void) format;
- (void) ap; // XXXX look at this.
- CALLED(logv)++;
-}
-
-static void
test_tortls_tor_tls_get_error(void *data)
{
(void) data;
@@ -193,11 +92,10 @@ test_tortls_tor_tls_get_error(void *data)
tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER,
key1, key2, 86400), OP_EQ, 0);
tls = tor_tls_new(-1, 0);
- NS_MOCK(logv);
- tt_int_op(CALLED(logv), OP_EQ, 0);
+ setup_capture_of_logs(LOG_WARN);
tor_tls_get_error(tls, 0, 0,
- (const char *)"test", 0, 0);
- tt_int_op(CALLED(logv), OP_EQ, 1);
+ (const char *)"in unit test", LOG_WARN, LD_GENERAL);
+ expect_single_log_msg_containing("unexpected close while in unit test");
done:
UNMOCK(tor_tls_cert_matches_key);
@@ -208,2638 +106,172 @@ test_tortls_tor_tls_get_error(void *data)
}
static void
-library_init(void)
-{
-#ifdef OPENSSL_1_1_API
- OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL);
-#else
- SSL_library_init();
- SSL_load_error_strings();
-#endif
-}
-
-static void
-test_tortls_get_state_description(void *ignored)
+test_tortls_x509_cert_get_id_digests(void *ignored)
{
(void)ignored;
- tor_tls_t *tls;
- char *buf;
- SSL_CTX *ctx;
-
- library_init();
- ctx = SSL_CTX_new(SSLv23_method());
-
- buf = tor_malloc_zero(1000);
- tls = tor_malloc_zero(sizeof(tor_tls_t));
-
- tor_tls_get_state_description(NULL, buf, 20);
- tt_str_op(buf, OP_EQ, "(No SSL object)");
-
- SSL_free(tls->ssl);
- tls->ssl = NULL;
- tor_tls_get_state_description(tls, buf, 20);
- tt_str_op(buf, OP_EQ, "(No SSL object)");
-
- tls->ssl = SSL_new(ctx);
- tor_tls_get_state_description(tls, buf, 200);
- tt_str_op(buf, OP_EQ, SSL_STATE_STR " in HANDSHAKE");
-
- tls->state = TOR_TLS_ST_OPEN;
- tor_tls_get_state_description(tls, buf, 200);
- tt_str_op(buf, OP_EQ, SSL_STATE_STR " in OPEN");
-
- tls->state = TOR_TLS_ST_GOTCLOSE;
- tor_tls_get_state_description(tls, buf, 200);
- tt_str_op(buf, OP_EQ, SSL_STATE_STR " in GOTCLOSE");
-
- tls->state = TOR_TLS_ST_SENTCLOSE;
- tor_tls_get_state_description(tls, buf, 200);
- tt_str_op(buf, OP_EQ, SSL_STATE_STR " in SENTCLOSE");
-
- tls->state = TOR_TLS_ST_CLOSED;
- tor_tls_get_state_description(tls, buf, 200);
- tt_str_op(buf, OP_EQ, SSL_STATE_STR " in CLOSED");
-
- tls->state = TOR_TLS_ST_RENEGOTIATE;
- tor_tls_get_state_description(tls, buf, 200);
- tt_str_op(buf, OP_EQ, SSL_STATE_STR " in RENEGOTIATE");
+ tor_x509_cert_t *cert;
+ common_digests_t *d;
+ const common_digests_t *res;
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ d = tor_malloc_zero(sizeof(common_digests_t));
+ d->d[0][0] = 42;
- tls->state = TOR_TLS_ST_BUFFEREVENT;
- tor_tls_get_state_description(tls, buf, 200);
- tt_str_op(buf, OP_EQ, SSL_STATE_STR);
+ res = tor_x509_cert_get_id_digests(cert);
+ tt_assert(!res);
- tls->state = 7;
- tor_tls_get_state_description(tls, buf, 200);
- tt_str_op(buf, OP_EQ, SSL_STATE_STR " in unknown TLS state");
+ cert->pkey_digests_set = 1;
+ cert->pkey_digests = *d;
+ res = tor_x509_cert_get_id_digests(cert);
+ tt_int_op(res->d[0][0], OP_EQ, 42);
done:
- SSL_CTX_free(ctx);
- SSL_free(tls->ssl);
- tor_free(buf);
- tor_free(tls);
+ tor_free(cert);
+ tor_free(d);
}
static void
-test_tortls_get_by_ssl(void *ignored)
+test_tortls_get_my_certs(void *ignored)
{
(void)ignored;
- tor_tls_t *tls;
- tor_tls_t *res;
- SSL_CTX *ctx;
- SSL *ssl;
-
- library_init();
- tor_tls_allocate_tor_tls_object_ex_data_index();
-
- ctx = SSL_CTX_new(SSLv23_method());
- tls = tor_malloc_zero(sizeof(tor_tls_t));
- tls->magic = TOR_TLS_MAGIC;
-
- ssl = SSL_new(ctx);
-
- res = tor_tls_get_by_ssl(ssl);
- tt_assert(!res);
+ int ret;
+ tor_tls_context_t *ctx;
+ const tor_x509_cert_t *link_cert_out = NULL;
+ const tor_x509_cert_t *id_cert_out = NULL;
- SSL_set_ex_data(ssl, tor_tls_object_ex_data_index, tls);
+ ctx = tor_malloc_zero(sizeof(tor_tls_context_t));
- res = tor_tls_get_by_ssl(ssl);
- tt_assert(res == tls);
+ client_tls_context = NULL;
+ ret = tor_tls_get_my_certs(0, NULL, NULL);
+ tt_int_op(ret, OP_EQ, -1);
- done:
- SSL_free(ssl);
- SSL_CTX_free(ctx);
- tor_free(tls);
-}
+ server_tls_context = NULL;
+ ret = tor_tls_get_my_certs(1, NULL, NULL);
+ tt_int_op(ret, OP_EQ, -1);
-static void
-test_tortls_allocate_tor_tls_object_ex_data_index(void *ignored)
-{
- (void)ignored;
- int first;
+ client_tls_context = ctx;
+ ret = tor_tls_get_my_certs(0, NULL, NULL);
+ tt_int_op(ret, OP_EQ, 0);
- tor_tls_allocate_tor_tls_object_ex_data_index();
+ client_tls_context = ctx;
+ ret = tor_tls_get_my_certs(0, &link_cert_out, &id_cert_out);
+ tt_int_op(ret, OP_EQ, 0);
- first = tor_tls_object_ex_data_index;
- tor_tls_allocate_tor_tls_object_ex_data_index();
- tt_int_op(first, OP_EQ, tor_tls_object_ex_data_index);
+ server_tls_context = ctx;
+ ret = tor_tls_get_my_certs(1, &link_cert_out, &id_cert_out);
+ tt_int_op(ret, OP_EQ, 0);
done:
- (void)0;
+ (void)1;
}
static void
-test_tortls_log_one_error(void *ignored)
+test_tortls_get_forced_write_size(void *ignored)
{
(void)ignored;
+ long ret;
tor_tls_t *tls;
- SSL_CTX *ctx;
- SSL *ssl = NULL;
- library_init();
-
- ctx = SSL_CTX_new(SSLv23_method());
tls = tor_malloc_zero(sizeof(tor_tls_t));
- setup_capture_of_logs(LOG_INFO);
-
- tor_tls_log_one_error(NULL, 0, LOG_WARN, 0, "something");
- expect_log_msg("TLS error while something: "
- "(null) (in (null):(null):---)\n");
-
- mock_clean_saved_logs();
- tor_tls_log_one_error(tls, 0, LOG_WARN, 0, NULL);
- expect_log_msg("TLS error: (null) "
- "(in (null):(null):---)\n");
-
- mock_clean_saved_logs();
- tls->address = tor_strdup("127.hello");
- tor_tls_log_one_error(tls, 0, LOG_WARN, 0, NULL);
- expect_log_msg("TLS error with 127.hello: "
- "(null) (in (null):(null):---)\n");
- tor_free(tls->address);
-
- mock_clean_saved_logs();
- tls->address = tor_strdup("127.hello");
- tor_tls_log_one_error(tls, 0, LOG_WARN, 0, "blarg");
- expect_log_msg("TLS error while blarg with "
- "127.hello: (null) (in (null):(null):---)\n");
-
- mock_clean_saved_logs();
- tor_tls_log_one_error(tls, ERR_PACK(1, 2, 3), LOG_WARN, 0, NULL);
- expect_log_msg("TLS error with 127.hello: "
- "BN lib (in unknown library:(null):---)\n");
-
- mock_clean_saved_logs();
- tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_HTTP_REQUEST),
- LOG_WARN, 0, NULL);
- expect_log_severity(LOG_INFO);
-
- mock_clean_saved_logs();
- tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_HTTPS_PROXY_REQUEST),
- LOG_WARN, 0, NULL);
- expect_log_severity(LOG_INFO);
-
- mock_clean_saved_logs();
- tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_RECORD_LENGTH_MISMATCH),
- LOG_WARN, 0, NULL);
- expect_log_severity(LOG_INFO);
-
-#ifndef OPENSSL_1_1_API
- mock_clean_saved_logs();
- tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_RECORD_TOO_LARGE),
- LOG_WARN, 0, NULL);
- expect_log_severity(LOG_INFO);
-#endif /* !defined(OPENSSL_1_1_API) */
- mock_clean_saved_logs();
- tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_UNKNOWN_PROTOCOL),
- LOG_WARN, 0, NULL);
- expect_log_severity(LOG_INFO);
-
- mock_clean_saved_logs();
- tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_UNSUPPORTED_PROTOCOL),
- LOG_WARN, 0, NULL);
- expect_log_severity(LOG_INFO);
-
- tls->ssl = SSL_new(ctx);
-
- mock_clean_saved_logs();
- tor_tls_log_one_error(tls, 0, LOG_WARN, 0, NULL);
- expect_log_msg("TLS error with 127.hello: (null)"
- " (in (null):(null):" SSL_STATE_STR ")\n");
+ tls->wantwrite_n = 43;
+ ret = tor_tls_get_forced_write_size(tls);
+ tt_int_op(ret, OP_EQ, 43);
done:
- teardown_capture_of_logs();
- SSL_free(ssl);
- SSL_CTX_free(ctx);
- if (tls && tls->ssl)
- SSL_free(tls->ssl);
- if (tls)
- tor_free(tls->address);
tor_free(tls);
}
-#ifndef OPENSSL_OPAQUE
static void
-test_tortls_get_error(void *ignored)
+test_tortls_used_v1_handshake(void *ignored)
{
(void)ignored;
- tor_tls_t *tls;
int ret;
- SSL_CTX *ctx;
-
- library_init();
-
- ctx = SSL_CTX_new(SSLv23_method());
- setup_capture_of_logs(LOG_INFO);
+ tor_tls_t *tls;
tls = tor_malloc_zero(sizeof(tor_tls_t));
- tls->ssl = SSL_new(ctx);
- SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL);
- ret = tor_tls_get_error(tls, 0, 0, "something", LOG_WARN, 0);
- tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_IO);
- expect_log_msg("TLS error: unexpected close while"
- " something (before/accept initialization)\n");
+ // These tests assume both V2 handshake server and client are enabled
+ tls->wasV2Handshake = 0;
+ ret = tor_tls_used_v1_handshake(tls);
+ tt_int_op(ret, OP_EQ, 1);
- mock_clean_saved_logs();
- ret = tor_tls_get_error(tls, 2, 0, "something", LOG_WARN, 0);
+ tls->wasV2Handshake = 1;
+ ret = tor_tls_used_v1_handshake(tls);
tt_int_op(ret, OP_EQ, 0);
- expect_no_log_entry();
-
- mock_clean_saved_logs();
- ret = tor_tls_get_error(tls, 0, 1, "something", LOG_WARN, 0);
- tt_int_op(ret, OP_EQ, -11);
- expect_no_log_entry();
-
- mock_clean_saved_logs();
- ERR_clear_error();
- ERR_put_error(ERR_LIB_BN, 2, -1, "somewhere.c", 99);
- ret = tor_tls_get_error(tls, 0, 0, "something", LOG_WARN, 0);
- tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
- expect_log_msg("TLS error while something: (null)"
- " (in bignum routines:(null):before/accept initialization)\n");
-
- mock_clean_saved_logs();
- ERR_clear_error();
- tls->ssl->rwstate = SSL_READING;
- SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_READ;
- ret = tor_tls_get_error(tls, -1, 0, "something", LOG_WARN, 0);
- tt_int_op(ret, OP_EQ, TOR_TLS_WANTREAD);
- expect_no_log_entry();
-
- mock_clean_saved_logs();
- ERR_clear_error();
- tls->ssl->rwstate = SSL_READING;
- SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_WRITE;
- ret = tor_tls_get_error(tls, -1, 0, "something", LOG_WARN, 0);
- tt_int_op(ret, OP_EQ, TOR_TLS_WANTWRITE);
- expect_no_log_entry();
-
- mock_clean_saved_logs();
- ERR_clear_error();
- tls->ssl->rwstate = 0;
- tls->ssl->shutdown = SSL_RECEIVED_SHUTDOWN;
- tls->ssl->s3->warn_alert =SSL_AD_CLOSE_NOTIFY;
- ret = tor_tls_get_error(tls, 0, 0, "something", LOG_WARN, 0);
- tt_int_op(ret, OP_EQ, TOR_TLS_CLOSE);
- expect_log_entry();
-
- mock_clean_saved_logs();
- ret = tor_tls_get_error(tls, 0, 2, "something", LOG_WARN, 0);
- tt_int_op(ret, OP_EQ, -10);
- expect_no_log_entry();
-
- mock_clean_saved_logs();
- ERR_put_error(ERR_LIB_SYS, 2, -1, "somewhere.c", 99);
- ret = tor_tls_get_error(tls, -1, 0, "something", LOG_WARN, 0);
- tt_int_op(ret, OP_EQ, -9);
- expect_log_msg("TLS error while something: (null) (in system library:"
- "connect:before/accept initialization)\n");
done:
- teardown_capture_of_logs();
- SSL_free(tls->ssl);
tor_free(tls);
- SSL_CTX_free(ctx);
}
-#endif /* !defined(OPENSSL_OPAQUE) */
static void
-test_tortls_always_accept_verify_cb(void *ignored)
+test_tortls_get_num_server_handshakes(void *ignored)
{
(void)ignored;
int ret;
+ tor_tls_t *tls;
- ret = always_accept_verify_cb(0, NULL);
- tt_int_op(ret, OP_EQ, 1);
-
- done:
- (void)0;
-}
-
-#ifndef OPENSSL_OPAQUE
-static void
-test_tortls_x509_cert_free(void *ignored)
-{
- (void)ignored;
- tor_x509_cert_t *cert;
-
- cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
- tor_x509_cert_free(cert);
-
- cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
- cert->cert = X509_new();
- cert->encoded = tor_malloc_zero(1);
- tor_x509_cert_free(cert);
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-static void
-test_tortls_x509_cert_get_id_digests(void *ignored)
-{
- (void)ignored;
- tor_x509_cert_t *cert;
- common_digests_t *d;
- const common_digests_t *res;
- cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
- d = tor_malloc_zero(sizeof(common_digests_t));
- d->d[0][0] = 42;
-
- res = tor_x509_cert_get_id_digests(cert);
- tt_assert(!res);
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
- cert->pkey_digests_set = 1;
- cert->pkey_digests = *d;
- res = tor_x509_cert_get_id_digests(cert);
- tt_int_op(res->d[0][0], OP_EQ, 42);
+ tls->server_handshake_count = 3;
+ ret = tor_tls_get_num_server_handshakes(tls);
+ tt_int_op(ret, OP_EQ, 3);
done:
- tor_free(cert);
- tor_free(d);
-}
-
-#ifndef OPENSSL_OPAQUE
-static int
-fixed_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
-{
- (void) a; (void) b;
- return 1;
-}
-
-/*
- * Use only for the matching fake_x509_free() call
- */
-static X509 *
-fake_x509_malloc(void)
-{
- return tor_malloc_zero(sizeof(X509));
-}
-
-static void
-fake_x509_free(X509 *cert)
-{
- if (cert) {
- if (cert->cert_info) {
- if (cert->cert_info->key) {
- if (cert->cert_info->key->pkey) {
- tor_free(cert->cert_info->key->pkey);
- }
- tor_free(cert->cert_info->key);
- }
- tor_free(cert->cert_info);
- }
- tor_free(cert);
- }
+ tor_free(tls);
}
static void
-test_tortls_cert_matches_key(void *ignored)
+test_tortls_server_got_renegotiate(void *ignored)
{
(void)ignored;
- int res;
+ int ret;
tor_tls_t *tls;
- tor_x509_cert_t *cert;
- X509 *one = NULL, *two = NULL;
- EVP_PKEY_ASN1_METHOD *meth = EVP_PKEY_asn1_new(999, 0, NULL, NULL);
- EVP_PKEY_asn1_set_public(meth, NULL, NULL, fixed_pub_cmp, NULL, NULL, NULL);
tls = tor_malloc_zero(sizeof(tor_tls_t));
- cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
- one = fake_x509_malloc();
- one->references = 1;
- two = fake_x509_malloc();
- two->references = 1;
-
- res = tor_tls_cert_matches_key(tls, cert);
- tt_int_op(res, OP_EQ, 0);
-
- tls->ssl = tor_malloc_zero(sizeof(SSL));
- tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
- tls->ssl->session->peer = one;
- res = tor_tls_cert_matches_key(tls, cert);
- tt_int_op(res, OP_EQ, 0);
-
- cert->cert = two;
- res = tor_tls_cert_matches_key(tls, cert);
- tt_int_op(res, OP_EQ, 0);
-
- one->cert_info = tor_malloc_zero(sizeof(X509_CINF));
- one->cert_info->key = tor_malloc_zero(sizeof(X509_PUBKEY));
- one->cert_info->key->pkey = tor_malloc_zero(sizeof(EVP_PKEY));
- one->cert_info->key->pkey->references = 1;
- one->cert_info->key->pkey->ameth = meth;
- one->cert_info->key->pkey->type = 1;
-
- two->cert_info = tor_malloc_zero(sizeof(X509_CINF));
- two->cert_info->key = tor_malloc_zero(sizeof(X509_PUBKEY));
- two->cert_info->key->pkey = tor_malloc_zero(sizeof(EVP_PKEY));
- two->cert_info->key->pkey->references = 1;
- two->cert_info->key->pkey->ameth = meth;
- two->cert_info->key->pkey->type = 2;
-
- res = tor_tls_cert_matches_key(tls, cert);
- tt_int_op(res, OP_EQ, 0);
- one->cert_info->key->pkey->type = 1;
- two->cert_info->key->pkey->type = 1;
- res = tor_tls_cert_matches_key(tls, cert);
- tt_int_op(res, OP_EQ, 1);
+ tls->got_renegotiate = 1;
+ ret = tor_tls_server_got_renegotiate(tls);
+ tt_int_op(ret, OP_EQ, 1);
done:
- EVP_PKEY_asn1_free(meth);
- tor_free(tls->ssl->session);
- tor_free(tls->ssl);
tor_free(tls);
- tor_free(cert);
- fake_x509_free(one);
- fake_x509_free(two);
}
static void
-test_tortls_cert_get_key(void *ignored)
+test_tortls_evaluate_ecgroup_for_tls(void *ignored)
{
(void)ignored;
- tor_x509_cert_t *cert = NULL;
- crypto_pk_t *res = NULL;
- cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
- X509 *key = NULL;
- key = fake_x509_malloc();
- key->references = 1;
-
- res = tor_tls_cert_get_key(cert);
- tt_assert(!res);
-
- cert->cert = key;
- key->cert_info = tor_malloc_zero(sizeof(X509_CINF));
- key->cert_info->key = tor_malloc_zero(sizeof(X509_PUBKEY));
- key->cert_info->key->pkey = tor_malloc_zero(sizeof(EVP_PKEY));
- key->cert_info->key->pkey->references = 1;
- key->cert_info->key->pkey->type = 2;
- res = tor_tls_cert_get_key(cert);
- tt_assert(!res);
-
- done:
- fake_x509_free(key);
- tor_free(cert);
- crypto_pk_free(res);
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
+ int ret;
-static void
-test_tortls_get_my_client_auth_key(void *ignored)
-{
- (void)ignored;
- crypto_pk_t *ret;
- crypto_pk_t *expected;
- tor_tls_context_t *ctx;
- RSA *k = RSA_new();
+ ret = evaluate_ecgroup_for_tls(NULL);
+ tt_int_op(ret, OP_EQ, 1);
- ctx = tor_malloc_zero(sizeof(tor_tls_context_t));
- expected = crypto_new_pk_from_openssl_rsa_(k);
- ctx->auth_key = expected;
+ ret = evaluate_ecgroup_for_tls("foobar");
+ tt_int_op(ret, OP_EQ, 0);
- client_tls_context = NULL;
- ret = tor_tls_get_my_client_auth_key();
- tt_assert(!ret);
+ ret = evaluate_ecgroup_for_tls("P256");
+ tt_int_op(ret, OP_EQ, 1);
- client_tls_context = ctx;
- ret = tor_tls_get_my_client_auth_key();
- tt_assert(ret == expected);
+ ret = evaluate_ecgroup_for_tls("P224");
+ // tt_int_op(ret, OP_EQ, 1); This varies between machines
+ tt_assert(ret == 0 || ret == 1);
done:
- tor_free(expected);
- tor_free(ctx);
+ (void)0;
}
-static void
-test_tortls_get_my_certs(void *ignored)
-{
- (void)ignored;
- int ret;
- tor_tls_context_t *ctx;
- const tor_x509_cert_t *link_cert_out = NULL;
- const tor_x509_cert_t *id_cert_out = NULL;
-
- ctx = tor_malloc_zero(sizeof(tor_tls_context_t));
-
- client_tls_context = NULL;
- ret = tor_tls_get_my_certs(0, NULL, NULL);
- tt_int_op(ret, OP_EQ, -1);
-
- server_tls_context = NULL;
- ret = tor_tls_get_my_certs(1, NULL, NULL);
- tt_int_op(ret, OP_EQ, -1);
-
- client_tls_context = ctx;
- ret = tor_tls_get_my_certs(0, NULL, NULL);
- tt_int_op(ret, OP_EQ, 0);
-
- client_tls_context = ctx;
- ret = tor_tls_get_my_certs(0, &link_cert_out, &id_cert_out);
- tt_int_op(ret, OP_EQ, 0);
-
- server_tls_context = ctx;
- ret = tor_tls_get_my_certs(1, &link_cert_out, &id_cert_out);
- tt_int_op(ret, OP_EQ, 0);
-
- done:
- (void)1;
-}
-
-#ifndef HAVE_SSL_GET_CLIENT_CIPHERS
-static SSL_CIPHER *
-get_cipher_by_name(const char *name)
-{
- int i;
- const SSL_METHOD *method = SSLv23_method();
- int num = method->num_ciphers();
-
- for (i = 0; i < num; ++i) {
- const SSL_CIPHER *cipher = method->get_cipher(i);
- const char *ciphername = SSL_CIPHER_get_name(cipher);
- if (!strcmp(ciphername, name)) {
- return (SSL_CIPHER *)cipher;
- }
- }
-
- return NULL;
-}
-#endif /* !defined(HAVE_SSL_GET_CLIENT_CIPHERS) */
-
-#ifndef OPENSSL_OPAQUE
-static void
-test_tortls_get_ciphersuite_name(void *ignored)
-{
- (void)ignored;
- const char *ret;
- tor_tls_t *ctx;
- ctx = tor_malloc_zero(sizeof(tor_tls_t));
- ctx->ssl = tor_malloc_zero(sizeof(SSL));
-
- ret = tor_tls_get_ciphersuite_name(ctx);
- tt_str_op(ret, OP_EQ, "(NONE)");
-
- done:
- tor_free(ctx->ssl);
- tor_free(ctx);
-}
-
-static SSL_CIPHER *
-get_cipher_by_id(uint16_t id)
-{
- int i;
- const SSL_METHOD *method = SSLv23_method();
- int num = method->num_ciphers();
- for (i = 0; i < num; ++i) {
- const SSL_CIPHER *cipher = method->get_cipher(i);
- if (id == (SSL_CIPHER_get_id(cipher) & 0xffff)) {
- return (SSL_CIPHER *)cipher;
- }
- }
-
- return NULL;
-}
-
-static void
-test_tortls_classify_client_ciphers(void *ignored)
-{
- (void)ignored;
- int i;
- int ret;
- SSL_CTX *ctx;
- SSL *ssl;
- tor_tls_t *tls;
- STACK_OF(SSL_CIPHER) *ciphers;
- SSL_CIPHER *tmp_cipher;
-
- library_init();
-
- tor_tls_allocate_tor_tls_object_ex_data_index();
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
- tls->magic = TOR_TLS_MAGIC;
-
- ctx = SSL_CTX_new(TLSv1_method());
- ssl = SSL_new(ctx);
- tls->ssl = ssl;
-
- ciphers = sk_SSL_CIPHER_new_null();
-
- ret = tor_tls_classify_client_ciphers(ssl, NULL);
- tt_int_op(ret, OP_EQ, -1);
-
- SSL_set_ex_data(ssl, tor_tls_object_ex_data_index, tls);
- tls->client_cipher_list_type = 42;
-
- ret = tor_tls_classify_client_ciphers(ssl, NULL);
- tt_int_op(ret, OP_EQ, 42);
-
- tls->client_cipher_list_type = 0;
- ret = tor_tls_classify_client_ciphers(ssl, ciphers);
- tt_int_op(ret, OP_EQ, 1);
- tt_int_op(tls->client_cipher_list_type, OP_EQ, 1);
-
- tls->client_cipher_list_type = 0;
- ret = tor_tls_classify_client_ciphers(ssl, SSL_get_ciphers(ssl));
- tt_int_op(ret, OP_EQ, 3);
- tt_int_op(tls->client_cipher_list_type, OP_EQ, 3);
-
- SSL_CIPHER *one = get_cipher_by_name(TLS1_TXT_DHE_RSA_WITH_AES_128_SHA),
- *two = get_cipher_by_name(TLS1_TXT_DHE_RSA_WITH_AES_256_SHA),
- *three = get_cipher_by_name(SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA),
- *four = NULL;
- sk_SSL_CIPHER_push(ciphers, one);
- sk_SSL_CIPHER_push(ciphers, two);
- sk_SSL_CIPHER_push(ciphers, three);
- sk_SSL_CIPHER_push(ciphers, four);
-
- tls->client_cipher_list_type = 0;
- ret = tor_tls_classify_client_ciphers(ssl, ciphers);
- tt_int_op(ret, OP_EQ, 1);
- tt_int_op(tls->client_cipher_list_type, OP_EQ, 1);
-
- sk_SSL_CIPHER_zero(ciphers);
-
- one = get_cipher_by_name("ECDHE-RSA-AES256-GCM-SHA384");
- tt_assert(one);
- one->id = 0x00ff;
- two = get_cipher_by_name("ECDHE-RSA-AES128-GCM-SHA256");
- tt_assert(two);
- two->id = 0x0000;
- sk_SSL_CIPHER_push(ciphers, one);
- tls->client_cipher_list_type = 0;
- ret = tor_tls_classify_client_ciphers(ssl, ciphers);
- tt_int_op(ret, OP_EQ, 3);
- tt_int_op(tls->client_cipher_list_type, OP_EQ, 3);
-
- sk_SSL_CIPHER_push(ciphers, two);
- tls->client_cipher_list_type = 0;
- ret = tor_tls_classify_client_ciphers(ssl, ciphers);
- tt_int_op(ret, OP_EQ, 3);
- tt_int_op(tls->client_cipher_list_type, OP_EQ, 3);
-
- one->id = 0xC00A;
- tls->client_cipher_list_type = 0;
- ret = tor_tls_classify_client_ciphers(ssl, ciphers);
- tt_int_op(ret, OP_EQ, 3);
- tt_int_op(tls->client_cipher_list_type, OP_EQ, 3);
-
- sk_SSL_CIPHER_zero(ciphers);
- for (i=0; v2_cipher_list[i]; i++) {
- tmp_cipher = get_cipher_by_id(v2_cipher_list[i]);
- tt_assert(tmp_cipher);
- sk_SSL_CIPHER_push(ciphers, tmp_cipher);
- }
- tls->client_cipher_list_type = 0;
- ret = tor_tls_classify_client_ciphers(ssl, ciphers);
- tt_int_op(ret, OP_EQ, 2);
- tt_int_op(tls->client_cipher_list_type, OP_EQ, 2);
-
- done:
- sk_SSL_CIPHER_free(ciphers);
- SSL_free(tls->ssl);
- tor_free(tls);
- SSL_CTX_free(ctx);
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-static void
-test_tortls_client_is_using_v2_ciphers(void *ignored)
-{
- (void)ignored;
-
-#ifdef HAVE_SSL_GET_CLIENT_CIPHERS
- tt_skip();
- done:
- (void)1;
-#else
- int ret;
- SSL_CTX *ctx;
- SSL *ssl;
- SSL_SESSION *sess;
- STACK_OF(SSL_CIPHER) *ciphers;
-
- library_init();
-
- ctx = SSL_CTX_new(TLSv1_method());
- ssl = SSL_new(ctx);
- sess = SSL_SESSION_new();
-
- ret = tor_tls_client_is_using_v2_ciphers(ssl);
- tt_int_op(ret, OP_EQ, -1);
-
- ssl->session = sess;
- ret = tor_tls_client_is_using_v2_ciphers(ssl);
- tt_int_op(ret, OP_EQ, 0);
-
- ciphers = sk_SSL_CIPHER_new_null();
- SSL_CIPHER *one = get_cipher_by_name("ECDHE-RSA-AES256-GCM-SHA384");
- tt_assert(one);
- one->id = 0x00ff;
- sk_SSL_CIPHER_push(ciphers, one);
- sess->ciphers = ciphers;
- ret = tor_tls_client_is_using_v2_ciphers(ssl);
- tt_int_op(ret, OP_EQ, 1);
- done:
- SSL_free(ssl);
- SSL_CTX_free(ctx);
-#endif /* defined(HAVE_SSL_GET_CLIENT_CIPHERS) */
-}
-
-#ifndef OPENSSL_OPAQUE
-static X509 *fixed_try_to_extract_certs_from_tls_cert_out_result = NULL;
-static X509 *fixed_try_to_extract_certs_from_tls_id_cert_out_result = NULL;
-
-static void
-fixed_try_to_extract_certs_from_tls(int severity, tor_tls_t *tls,
- X509 **cert_out, X509 **id_cert_out)
-{
- (void) severity;
- (void) tls;
- *cert_out = fixed_try_to_extract_certs_from_tls_cert_out_result;
- *id_cert_out = fixed_try_to_extract_certs_from_tls_id_cert_out_result;
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-#ifndef OPENSSL_OPAQUE
-static const char* notCompletelyValidCertString =
- "-----BEGIN CERTIFICATE-----\n"
- "MIICVjCCAb8CAg37MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG\n"
- "A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE\n"
- "MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl\n"
- "YiBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBmcmFuazRkZC5jb20wHhcNMTIw\n"
- "ODIyMDUyNzIzWhcNMTcwODIxMDUyNzIzWjBKMQswCQYDVQQGEwJKUDEOMAwGA1UE\n"
- "CAwFVG9reW8xETAPBgNVBAoMCEZyYW5rNEREMRgwFgYDVQQDDA93d3cuZXhhbXBs\n"
- "ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMYBBrx5PlP0WNI/ZdzD\n"
- "+6Pktmurn+F2kQYbtc7XQh8/LTBvCo+P6iZoLEmUA9e7EXLRxgU1CVqeAi7QcAn9\n"
- "MwBlc8ksFJHB0rtf9pmf8Oza9E0Bynlq/4/Kb1x+d+AyhL7oK9tQwB24uHOueHi1\n"
- "C/iVv8CSWKiYe6hzN1txYe8rAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAASPdjigJ\n"
- "kXCqKWpnZ/Oc75EUcMi6HztaW8abUMlYXPIgkV2F7YanHOB7K4f7OOLjiz8DTPFf\n"
- "jC9UeuErhaA/zzWi8ewMTFZW/WshOrm3fNvcMrMLKtH534JKvcdMg6qIdjTFINIr\n"
- "evnAhf0cwULaebn+lMs8Pdl7y37+sfluVok=\n"
- "-----END CERTIFICATE-----\n";
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-static const char* validCertString = "-----BEGIN CERTIFICATE-----\n"
- "MIIDpTCCAY0CAg3+MA0GCSqGSIb3DQEBBQUAMF4xCzAJBgNVBAYTAlVTMREwDwYD\n"
- "VQQIDAhJbGxpbm9pczEQMA4GA1UEBwwHQ2hpY2FnbzEUMBIGA1UECgwLVG9yIFRl\n"
- "c3RpbmcxFDASBgNVBAMMC1RvciBUZXN0aW5nMB4XDTE1MDkwNjEzMzk1OVoXDTQz\n"
- "MDEyMjEzMzk1OVowVjELMAkGA1UEBhMCVVMxEDAOBgNVBAcMB0NoaWNhZ28xFDAS\n"
- "BgNVBAoMC1RvciBUZXN0aW5nMR8wHQYDVQQDDBZ0ZXN0aW5nLnRvcnByb2plY3Qu\n"
- "b3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDoT6uyVVhWyOF3wkHjjYbd\n"
- "nKaykyRv4JVtKQdZ4OpEErmX1zw4MmyzpQNV6iR4bQnWiyLfzyVJMZDIC/WILBfX\n"
- "w2Pza/yuLgUvDc3twMuhOACzOQVO8PrEF/aVv2+hbCCy2udXvKhnYn+CCXl3ozc8\n"
- "XcKYvujTXDyvGWY3xwAjlQIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQCUvnhzQWuQ\n"
- "MrN+pERkE+zcTI/9dGS90rUMMLgu8VDNqTa0TUQh8uO0EQ6uDvI8Js6e8tgwS0BR\n"
- "UBahqb7ZHv+rejGCBr5OudqD+x4STiiuPNJVs86JTLN8SpM9CHjIBH5WCCN2KOy3\n"
- "mevNoRcRRyYJzSFULCunIK6FGulszigMYGscrO4oiTkZiHPh9KvWT40IMiHfL+Lw\n"
- "EtEWiLex6064LcA2YQ1AMuSZyCexks63lcfaFmQbkYOKqXa1oLkIRuDsOaSVjTfe\n"
- "vec+X6jvf12cFTKS5WIeqkKF2Irt+dJoiHEGTe5RscUMN/f+gqHPzfFz5dR23sxo\n"
- "g+HC6MZHlFkLAOx3wW6epPS8A/m1mw3zMPoTnb2U2YYt8T0dJMMlUn/7Y1sEAa+a\n"
- "dSTMaeUf6VnJ//11m454EZl1to9Z7oJOgqmFffSrdD4BGIWe8f7hhW6L1Enmqe/J\n"
- "BKL3wbzZh80O1W0bndAwhnEEhlzneFY84cbBo9pmVxpODHkUcStpr5Z7pBDrcL21\n"
- "Ss/aB/1YrsVXhdvJdOGxl3Mnl9dUY57CympLGlT8f0pPS6GAKOelECOhFMHmJd8L\n"
- "dj3XQSmKtYHevZ6IvuMXSlB/fJvSjSlkCuLo5+kJoaqPuRu+i/S1qxeRy3CBwmnE\n"
- "LdSNdcX4N79GQJ996PA8+mUCQG7YRtK+WA==\n"
- "-----END CERTIFICATE-----\n";
-
-static const char* caCertString = "-----BEGIN CERTIFICATE-----\n"
- "MIIFjzCCA3egAwIBAgIJAKd5WgyfPMYRMA0GCSqGSIb3DQEBCwUAMF4xCzAJBgNV\n"
- "BAYTAlVTMREwDwYDVQQIDAhJbGxpbm9pczEQMA4GA1UEBwwHQ2hpY2FnbzEUMBIG\n"
- "A1UECgwLVG9yIFRlc3RpbmcxFDASBgNVBAMMC1RvciBUZXN0aW5nMB4XDTE1MDkw\n"
- "NjEzMzc0MVoXDTQzMDEyMjEzMzc0MVowXjELMAkGA1UEBhMCVVMxETAPBgNVBAgM\n"
- "CElsbGlub2lzMRAwDgYDVQQHDAdDaGljYWdvMRQwEgYDVQQKDAtUb3IgVGVzdGlu\n"
- "ZzEUMBIGA1UEAwwLVG9yIFRlc3RpbmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw\n"
- "ggIKAoICAQCpLMUEiLW5leUgBZoEJms2V7lZRhIAjnJBhVMHD0e3UubNknmaQoxf\n"
- "ARz3rvqOaRd0JlV+qM9qE0DjiYcCVP1cAfqAo9d83uS1vwY3YMVJzADlaIiHfyVW\n"
- "uEgBy0vvkeUBqaua24dYlcwsemOiXYLu41yM1wkcGHW1AhBNHppY6cznb8TyLgNM\n"
- "2x3SGUdzc5XMyAFx51faKGBA3wjs+Hg1PLY7d30nmCgEOBavpm5I1disM/0k+Mcy\n"
- "YmAKEo/iHJX/rQzO4b9znP69juLlR8PDBUJEVIG/CYb6+uw8MjjUyiWXYoqfVmN2\n"
- "hm/lH8b6rXw1a2Aa3VTeD0DxaWeacMYHY/i01fd5n7hCoDTRNdSw5KJ0L3Z0SKTu\n"
- "0lzffKzDaIfyZGlpW5qdouACkWYzsaitQOePVE01PIdO30vUfzNTFDfy42ccx3Di\n"
- "59UCu+IXB+eMtrBfsok0Qc63vtF1linJgjHW1z/8ujk8F7/qkOfODhk4l7wngc2A\n"
- "EmwWFIFoGaiTEZHB9qteXr4unbXZ0AHpM02uGGwZEGohjFyebEb73M+J57WKKAFb\n"
- "PqbLcGUksL1SHNBNAJcVLttX55sO4nbidOS/kA3m+F1R04MBTyQF9qA6YDDHqdI3\n"
- "h/3pw0Z4fxVouTYT4/NfRnX4JTP4u+7Mpcoof28VME0qWqD1LnRhFQIDAQABo1Aw\n"
- "TjAdBgNVHQ4EFgQUMoAgIXH7pZ3QMRwTjT+DM9Yo/v0wHwYDVR0jBBgwFoAUMoAg\n"
- "IXH7pZ3QMRwTjT+DM9Yo/v0wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC\n"
- "AgEAUJxacjXR9sT+Xs6ISFiUsyd0T6WVKMnV46xrYJHirGfx+krWHrjxMY+ZtxYD\n"
- "DBDGlo11Qc4v6QrclNf5QUBfIiGQsP9Cm6hHcQ+Tpg9HHCgSqG1YNPwCPReCR4br\n"
- "BLvLfrfkcBL2IWM0PdQdCze+59DBfipsULD2mEn9fjYRXQEwb2QWtQ9qRc20Yb/x\n"
- "Q4b/+CvUodLkaq7B8MHz0BV8HHcBoph6DYaRmO/N+hPauIuSp6XyaGYcEefGKVKj\n"
- "G2+fcsdyXsoijNdL8vNKwm4j2gVwCBnw16J00yfFoV46YcbfqEdJB2je0XSvwXqt\n"
- "14AOTngxso2h9k9HLtrfpO1ZG/B5AcCMs1lzbZ2fp5DPHtjvvmvA2RJqgo3yjw4W\n"
- "4DHAuTglYFlC3mDHNfNtcGP20JvepcQNzNP2UzwcpOc94hfKikOFw+gf9Vf1qd0y\n"
- "h/Sk6OZHn2+JVUPiWHIQV98Vtoh4RmUZDJD+b55ia3fQGTGzt4z1XFzQYSva5sfs\n"
- "wocS/papthqWldQU7x+3wofNd5CNU1x6WKXG/yw30IT/4F8ADJD6GeygNT8QJYvt\n"
- "u/8lAkbOy6B9xGmSvr0Kk1oq9P2NshA6kalxp1Oz/DTNDdL4AeBXV3JmM6WWCjGn\n"
- "Yy1RT69d0rwYc5u/vnqODz1IjvT90smsrkBumGt791FAFeg=\n"
- "-----END CERTIFICATE-----\n";
-
-static X509 *
-read_cert_from(const char *str)
-{
- BIO *bio = BIO_new(BIO_s_mem());
- BIO_write(bio, str, (int) strlen(str));
- X509 *res = PEM_read_bio_X509(bio, NULL, NULL, NULL);
- BIO_free(bio);
- return res;
-}
-
-#ifndef OPENSSL_OPAQUE
-static void
-test_tortls_verify(void *ignored)
-{
- (void)ignored;
- int ret;
- tor_tls_t *tls;
- crypto_pk_t *k = NULL;
- X509 *cert1 = NULL, *cert2 = NULL, *invalidCert = NULL,
- *validCert = NULL, *caCert = NULL;
-
- cert1 = tor_malloc_zero(sizeof(X509));
- cert1->references = 10;
-
- cert2 = tor_malloc_zero(sizeof(X509));
- cert2->references = 10;
-
- validCert = read_cert_from(validCertString);
- caCert = read_cert_from(caCertString);
- invalidCert = read_cert_from(notCompletelyValidCertString);
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
- ret = tor_tls_verify(LOG_WARN, tls, &k);
- tt_int_op(ret, OP_EQ, -1);
-
- MOCK(try_to_extract_certs_from_tls, fixed_try_to_extract_certs_from_tls);
-
- fixed_try_to_extract_certs_from_tls_cert_out_result = cert1;
- ret = tor_tls_verify(LOG_WARN, tls, &k);
- tt_int_op(ret, OP_EQ, -1);
-
- fixed_try_to_extract_certs_from_tls_id_cert_out_result = cert2;
- ret = tor_tls_verify(LOG_WARN, tls, &k);
- tt_int_op(ret, OP_EQ, -1);
-
- fixed_try_to_extract_certs_from_tls_cert_out_result = invalidCert;
- fixed_try_to_extract_certs_from_tls_id_cert_out_result = invalidCert;
-
- ret = tor_tls_verify(LOG_WARN, tls, &k);
- tt_int_op(ret, OP_EQ, -1);
-
- fixed_try_to_extract_certs_from_tls_cert_out_result = validCert;
- fixed_try_to_extract_certs_from_tls_id_cert_out_result = caCert;
-
- ret = tor_tls_verify(LOG_WARN, tls, &k);
- tt_int_op(ret, OP_EQ, 0);
- tt_assert(k);
-
- done:
- UNMOCK(try_to_extract_certs_from_tls);
- tor_free(cert1);
- tor_free(cert2);
- tor_free(tls);
- tor_free(k);
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-#ifndef OPENSSL_OPAQUE
-static void
-test_tortls_check_lifetime(void *ignored)
-{
- (void)ignored;
- int ret;
- tor_tls_t *tls;
- X509 *validCert = read_cert_from(validCertString);
- time_t now = time(NULL);
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
- ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), 0, 0);
- tt_int_op(ret, OP_EQ, -1);
-
- tls->ssl = tor_malloc_zero(sizeof(SSL));
- tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
- tls->ssl->session->peer = validCert;
- ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), 0, 0);
- tt_int_op(ret, OP_EQ, 0);
-
- ASN1_STRING_free(validCert->cert_info->validity->notBefore);
- validCert->cert_info->validity->notBefore = ASN1_TIME_set(NULL, now-10);
- ASN1_STRING_free(validCert->cert_info->validity->notAfter);
- validCert->cert_info->validity->notAfter = ASN1_TIME_set(NULL, now+60);
-
- ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), 0, -1000);
- tt_int_op(ret, OP_EQ, -1);
-
- ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), -1000, 0);
- tt_int_op(ret, OP_EQ, -1);
-
- done:
- tor_free(tls->ssl->session);
- tor_free(tls->ssl);
- tor_free(tls);
- X509_free(validCert);
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-#ifndef OPENSSL_OPAQUE
-static int fixed_ssl_pending_result = 0;
-
-static int
-fixed_ssl_pending(const SSL *ignored)
-{
- (void)ignored;
- return fixed_ssl_pending_result;
-}
-
-static void
-test_tortls_get_pending_bytes(void *ignored)
-{
- (void)ignored;
- int ret;
- tor_tls_t *tls;
- SSL_METHOD *method;
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
- tls->ssl = tor_malloc_zero(sizeof(SSL));
- method = tor_malloc_zero(sizeof(SSL_METHOD));
- method->ssl_pending = fixed_ssl_pending;
- tls->ssl->method = method;
-
- fixed_ssl_pending_result = 42;
- ret = tor_tls_get_pending_bytes(tls);
- tt_int_op(ret, OP_EQ, 42);
-
- done:
- tor_free(method);
- tor_free(tls->ssl);
- tor_free(tls);
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-static void
-test_tortls_get_forced_write_size(void *ignored)
-{
- (void)ignored;
- long ret;
- tor_tls_t *tls;
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
-
- tls->wantwrite_n = 43;
- ret = tor_tls_get_forced_write_size(tls);
- tt_int_op(ret, OP_EQ, 43);
-
- done:
- tor_free(tls);
-}
-
-static void
-test_tortls_get_write_overhead_ratio(void *ignored)
-{
- (void)ignored;
- double ret;
-
- total_bytes_written_over_tls = 0;
- ret = tls_get_write_overhead_ratio();
- tt_double_op(fabs(ret - 1.0), OP_LT, 1E-12);
-
- total_bytes_written_by_tls = 10;
- total_bytes_written_over_tls = 1;
- ret = tls_get_write_overhead_ratio();
- tt_double_op(fabs(ret - 10.0), OP_LT, 1E-12);
-
- total_bytes_written_by_tls = 10;
- total_bytes_written_over_tls = 2;
- ret = tls_get_write_overhead_ratio();
- tt_double_op(fabs(ret - 5.0), OP_LT, 1E-12);
-
- done:
- (void)0;
-}
-
-static void
-test_tortls_used_v1_handshake(void *ignored)
-{
- (void)ignored;
- int ret;
- tor_tls_t *tls;
- tls = tor_malloc_zero(sizeof(tor_tls_t));
-
- // These tests assume both V2 handshake server and client are enabled
- tls->wasV2Handshake = 0;
- ret = tor_tls_used_v1_handshake(tls);
- tt_int_op(ret, OP_EQ, 1);
-
- tls->wasV2Handshake = 1;
- ret = tor_tls_used_v1_handshake(tls);
- tt_int_op(ret, OP_EQ, 0);
-
- done:
- tor_free(tls);
-}
-
-static void
-test_tortls_get_num_server_handshakes(void *ignored)
-{
- (void)ignored;
- int ret;
- tor_tls_t *tls;
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
-
- tls->server_handshake_count = 3;
- ret = tor_tls_get_num_server_handshakes(tls);
- tt_int_op(ret, OP_EQ, 3);
-
- done:
- tor_free(tls);
-}
-
-static void
-test_tortls_server_got_renegotiate(void *ignored)
-{
- (void)ignored;
- int ret;
- tor_tls_t *tls;
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
-
- tls->got_renegotiate = 1;
- ret = tor_tls_server_got_renegotiate(tls);
- tt_int_op(ret, OP_EQ, 1);
-
- done:
- tor_free(tls);
-}
-
-#ifndef OPENSSL_OPAQUE
-static void
-test_tortls_SSL_SESSION_get_master_key(void *ignored)
-{
- (void)ignored;
- size_t ret;
- tor_tls_t *tls;
- uint8_t *out;
- out = tor_malloc_zero(1);
- tls = tor_malloc_zero(sizeof(tor_tls_t));
- tls->ssl = tor_malloc_zero(sizeof(SSL));
- tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
- tls->ssl->session->master_key_length = 1;
-
-#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY
- tls->ssl->session->master_key[0] = 43;
- ret = SSL_SESSION_get_master_key(tls->ssl->session, out, 0);
- tt_int_op(ret, OP_EQ, 1);
- tt_int_op(out[0], OP_EQ, 0);
-
- ret = SSL_SESSION_get_master_key(tls->ssl->session, out, 1);
- tt_int_op(ret, OP_EQ, 1);
- tt_int_op(out[0], OP_EQ, 43);
-
- done:
-#endif /* !defined(HAVE_SSL_SESSION_GET_MASTER_KEY) */
- tor_free(tls->ssl->session);
- tor_free(tls->ssl);
- tor_free(tls);
- tor_free(out);
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-#ifndef OPENSSL_OPAQUE
-static void
-test_tortls_get_tlssecrets(void *ignored)
-{
- (void)ignored;
- int ret;
- uint8_t *secret_out = tor_malloc_zero(DIGEST256_LEN);
- tor_tls_t *tls;
- tls = tor_malloc_zero(sizeof(tor_tls_t));
- tls->ssl = tor_malloc_zero(sizeof(SSL));
- tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
- tls->ssl->session->master_key_length = 1;
- tls->ssl->s3 = tor_malloc_zero(sizeof(SSL3_STATE));
-
- ret = tor_tls_get_tlssecrets(tls, secret_out);
- tt_int_op(ret, OP_EQ, 0);
-
- done:
- tor_free(secret_out);
- tor_free(tls->ssl->s3);
- tor_free(tls->ssl->session);
- tor_free(tls->ssl);
- tor_free(tls);
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-#ifndef OPENSSL_OPAQUE
-static void
-test_tortls_get_buffer_sizes(void *ignored)
-{
- (void)ignored;
- int ret;
- tor_tls_t *tls;
- size_t rbuf_c=-1, rbuf_b=-1, wbuf_c=-1, wbuf_b=-1;
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
- tls->ssl = tor_malloc_zero(sizeof(SSL));
- tls->ssl->s3 = tor_malloc_zero(sizeof(SSL3_STATE));
-
- tls->ssl->s3->rbuf.buf = NULL;
- tls->ssl->s3->rbuf.len = 1;
- tls->ssl->s3->rbuf.offset = 0;
- tls->ssl->s3->rbuf.left = 42;
-
- tls->ssl->s3->wbuf.buf = NULL;
- tls->ssl->s3->wbuf.len = 2;
- tls->ssl->s3->wbuf.offset = 0;
- tls->ssl->s3->wbuf.left = 43;
-
- ret = tor_tls_get_buffer_sizes(tls, &rbuf_c, &rbuf_b, &wbuf_c, &wbuf_b);
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)
- tt_int_op(ret, OP_EQ, -1);
-#else
- tt_int_op(ret, OP_EQ, 0);
- tt_int_op(rbuf_c, OP_EQ, 0);
- tt_int_op(wbuf_c, OP_EQ, 0);
- tt_int_op(rbuf_b, OP_EQ, 42);
- tt_int_op(wbuf_b, OP_EQ, 43);
-
- tls->ssl->s3->rbuf.buf = tor_malloc_zero(1);
- tls->ssl->s3->wbuf.buf = tor_malloc_zero(1);
- ret = tor_tls_get_buffer_sizes(tls, &rbuf_c, &rbuf_b, &wbuf_c, &wbuf_b);
- tt_int_op(ret, OP_EQ, 0);
- tt_int_op(rbuf_c, OP_EQ, 1);
- tt_int_op(wbuf_c, OP_EQ, 2);
-
-#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) */
-
- done:
- tor_free(tls->ssl->s3->rbuf.buf);
- tor_free(tls->ssl->s3->wbuf.buf);
- tor_free(tls->ssl->s3);
- tor_free(tls->ssl);
- tor_free(tls);
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-static void
-test_tortls_evaluate_ecgroup_for_tls(void *ignored)
-{
- (void)ignored;
- int ret;
-
- ret = evaluate_ecgroup_for_tls(NULL);
- tt_int_op(ret, OP_EQ, 1);
-
- ret = evaluate_ecgroup_for_tls("foobar");
- tt_int_op(ret, OP_EQ, 0);
-
- ret = evaluate_ecgroup_for_tls("P256");
- tt_int_op(ret, OP_EQ, 1);
-
- ret = evaluate_ecgroup_for_tls("P224");
- // tt_int_op(ret, OP_EQ, 1); This varies between machines
- tt_assert(ret == 0 || ret == 1);
-
- done:
- (void)0;
-}
-
-#ifndef OPENSSL_OPAQUE
-typedef struct cert_pkey_st_local
-{
- X509 *x509;
- EVP_PKEY *privatekey;
- const EVP_MD *digest;
-} CERT_PKEY_local;
-
-typedef struct sess_cert_st_local
-{
- STACK_OF(X509) *cert_chain;
- int peer_cert_type;
- CERT_PKEY_local *peer_key;
- CERT_PKEY_local peer_pkeys[8];
- int references;
-} SESS_CERT_local;
-
-static void
-test_tortls_try_to_extract_certs_from_tls(void *ignored)
-{
- (void)ignored;
- tor_tls_t *tls;
- X509 *cert = NULL, *id_cert = NULL, *c1 = NULL, *c2 = NULL;
- SESS_CERT_local *sess = NULL;
-
- c1 = read_cert_from(validCertString);
- c2 = read_cert_from(caCertString);
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
- tls->ssl = tor_malloc_zero(sizeof(SSL));
- tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
- sess = tor_malloc_zero(sizeof(SESS_CERT_local));
- tls->ssl->session->sess_cert = (void *)sess;
-
- try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert);
- tt_assert(!cert);
- tt_assert(!id_cert);
-
- tls->ssl->session->peer = c1;
- try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert);
- tt_assert(cert == c1);
- tt_assert(!id_cert);
- X509_free(cert); /* decrease refcnt */
-
- sess->cert_chain = sk_X509_new_null();
- try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert);
- tt_assert(cert == c1);
- tt_assert(!id_cert);
- X509_free(cert); /* decrease refcnt */
-
- sk_X509_push(sess->cert_chain, c1);
- sk_X509_push(sess->cert_chain, c2);
-
- try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert);
- tt_assert(cert == c1);
- tt_assert(id_cert);
- X509_free(cert); /* decrease refcnt */
-
- done:
- sk_X509_free(sess->cert_chain);
- tor_free(sess);
- tor_free(tls->ssl->session);
- tor_free(tls->ssl);
- tor_free(tls);
- X509_free(c1);
- X509_free(c2);
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-#ifndef OPENSSL_OPAQUE
-static void
-test_tortls_get_peer_cert(void *ignored)
-{
- (void)ignored;
- tor_x509_cert_t *ret;
- tor_tls_t *tls;
- X509 *cert = NULL;
-
- cert = read_cert_from(validCertString);
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
- tls->ssl = tor_malloc_zero(sizeof(SSL));
- tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
-
- ret = tor_tls_get_peer_cert(tls);
- tt_assert(!ret);
-
- tls->ssl->session->peer = cert;
- ret = tor_tls_get_peer_cert(tls);
- tt_assert(ret);
- tt_assert(ret->cert == cert);
-
- done:
- tor_x509_cert_free(ret);
- tor_free(tls->ssl->session);
- tor_free(tls->ssl);
- tor_free(tls);
- X509_free(cert);
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-#ifndef OPENSSL_OPAQUE
-static void
-test_tortls_peer_has_cert(void *ignored)
-{
- (void)ignored;
- int ret;
- tor_tls_t *tls;
- X509 *cert = NULL;
-
- cert = read_cert_from(validCertString);
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
- tls->ssl = tor_malloc_zero(sizeof(SSL));
- tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
-
- ret = tor_tls_peer_has_cert(tls);
- tt_assert(!ret);
-
- tls->ssl->session->peer = cert;
- ret = tor_tls_peer_has_cert(tls);
- tt_assert(ret);
-
- done:
- tor_free(tls->ssl->session);
- tor_free(tls->ssl);
- tor_free(tls);
- X509_free(cert);
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-static void
-test_tortls_is_server(void *ignored)
-{
- (void)ignored;
- tor_tls_t *tls;
- int ret;
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
- tls->isServer = 1;
- ret = tor_tls_is_server(tls);
- tt_int_op(ret, OP_EQ, 1);
-
- done:
- tor_free(tls);
-}
-
-#ifndef OPENSSL_OPAQUE
-static void
-test_tortls_session_secret_cb(void *ignored)
-{
- (void)ignored;
- tor_tls_t *tls;
- SSL_CTX *ctx;
- STACK_OF(SSL_CIPHER) *ciphers = NULL;
- SSL_CIPHER *one;
-
- library_init();
-
- tor_tls_allocate_tor_tls_object_ex_data_index();
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
-
- tls->magic = TOR_TLS_MAGIC;
-
- ctx = SSL_CTX_new(TLSv1_method());
- tls->ssl = SSL_new(ctx);
- SSL_set_ex_data(tls->ssl, tor_tls_object_ex_data_index, tls);
-
- SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL);
-
- tor_tls_session_secret_cb(tls->ssl, NULL, NULL, NULL, NULL, NULL);
- tt_assert(!tls->ssl->tls_session_secret_cb);
-
- one = get_cipher_by_name("ECDHE-RSA-AES256-GCM-SHA384");
- one->id = 0x00ff;
- ciphers = sk_SSL_CIPHER_new_null();
- sk_SSL_CIPHER_push(ciphers, one);
-
- tls->client_cipher_list_type = 0;
- tor_tls_session_secret_cb(tls->ssl, NULL, NULL, ciphers, NULL, NULL);
- tt_assert(!tls->ssl->tls_session_secret_cb);
-
- done:
- sk_SSL_CIPHER_free(ciphers);
- SSL_free(tls->ssl);
- SSL_CTX_free(ctx);
- tor_free(tls);
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-#ifndef OPENSSL_OPAQUE
-/* TODO: It seems block_renegotiation and unblock_renegotiation and
- * using different blags. This might not be correct */
-static void
-test_tortls_block_renegotiation(void *ignored)
-{
- (void)ignored;
- tor_tls_t *tls;
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
- tls->ssl = tor_malloc_zero(sizeof(SSL));
- tls->ssl->s3 = tor_malloc_zero(sizeof(SSL3_STATE));
-#ifndef SUPPORT_UNSAFE_RENEGOTIATION_FLAG
-#define SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0
-#endif
-
- tls->ssl->s3->flags = SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
-
- tor_tls_block_renegotiation(tls);
-
-#ifndef OPENSSL_1_1_API
- tt_assert(!(tls->ssl->s3->flags &
- SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION));
-#endif
-
- done:
- tor_free(tls->ssl->s3);
- tor_free(tls->ssl);
- tor_free(tls);
-}
-
-static void
-test_tortls_unblock_renegotiation(void *ignored)
-{
- (void)ignored;
- tor_tls_t *tls;
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
- tls->ssl = tor_malloc_zero(sizeof(SSL));
- tor_tls_unblock_renegotiation(tls);
-
- tt_uint_op(SSL_get_options(tls->ssl) &
- SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, OP_EQ,
- SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
-
- done:
- tor_free(tls->ssl);
- tor_free(tls);
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-#ifndef OPENSSL_OPAQUE
-static void
-test_tortls_assert_renegotiation_unblocked(void *ignored)
-{
- (void)ignored;
- tor_tls_t *tls;
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
- tls->ssl = tor_malloc_zero(sizeof(SSL));
- tor_tls_unblock_renegotiation(tls);
- tor_tls_assert_renegotiation_unblocked(tls);
- /* No assertion here - this test will fail if tor_assert is turned on
- * and things are bad. */
-
- tor_free(tls->ssl);
- tor_free(tls);
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-static void
-test_tortls_set_logged_address(void *ignored)
-{
- (void)ignored;
- tor_tls_t *tls;
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
-
- tor_tls_set_logged_address(tls, "foo bar");
-
- tt_str_op(tls->address, OP_EQ, "foo bar");
-
- tor_tls_set_logged_address(tls, "foo bar 2");
- tt_str_op(tls->address, OP_EQ, "foo bar 2");
-
- done:
- tor_free(tls->address);
- tor_free(tls);
-}
-
-#ifndef OPENSSL_OPAQUE
-static void
-example_cb(tor_tls_t *t, void *arg)
-{
- (void)t;
- (void)arg;
-}
-
-static void
-test_tortls_set_renegotiate_callback(void *ignored)
-{
- (void)ignored;
- tor_tls_t *tls;
- const char *arg = "hello";
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
- tls->ssl = tor_malloc_zero(sizeof(SSL));
-
- tor_tls_set_renegotiate_callback(tls, example_cb, (void*)arg);
- tt_assert(tls->negotiated_callback == example_cb);
- tt_assert(tls->callback_arg == arg);
- tt_assert(!tls->got_renegotiate);
-
- /* Assumes V2_HANDSHAKE_SERVER */
- tt_assert(tls->ssl->info_callback == tor_tls_server_info_callback);
-
- tor_tls_set_renegotiate_callback(tls, NULL, (void*)arg);
- tt_assert(tls->ssl->info_callback == tor_tls_debug_state_callback);
-
- done:
- tor_free(tls->ssl);
- tor_free(tls);
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-#ifndef OPENSSL_OPAQUE
-static SSL_CIPHER *fixed_cipher1 = NULL;
-static SSL_CIPHER *fixed_cipher2 = NULL;
-static const SSL_CIPHER *
-fake_get_cipher(unsigned ncipher)
-{
-
- switch (ncipher) {
- case 1:
- return fixed_cipher1;
- case 2:
- return fixed_cipher2;
- default:
- return NULL;
- }
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-#ifndef OPENSSL_OPAQUE
-static void
-test_tortls_find_cipher_by_id(void *ignored)
-{
- (void)ignored;
- int ret;
- SSL *ssl;
- SSL_CTX *ctx;
- const SSL_METHOD *m = TLSv1_method();
- SSL_METHOD *empty_method = tor_malloc_zero(sizeof(SSL_METHOD));
-
- fixed_cipher1 = tor_malloc_zero(sizeof(SSL_CIPHER));
- fixed_cipher2 = tor_malloc_zero(sizeof(SSL_CIPHER));
- fixed_cipher2->id = 0xC00A;
-
- library_init();
-
- ctx = SSL_CTX_new(m);
- ssl = SSL_new(ctx);
-
- ret = find_cipher_by_id(ssl, NULL, 0xC00A);
- tt_int_op(ret, OP_EQ, 1);
-
- ret = find_cipher_by_id(ssl, m, 0xC00A);
- tt_int_op(ret, OP_EQ, 1);
-
- ret = find_cipher_by_id(ssl, m, 0xFFFF);
- tt_int_op(ret, OP_EQ, 0);
-
- ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
- tt_int_op(ret, OP_EQ, 1);
-
- ret = find_cipher_by_id(ssl, empty_method, 0xFFFF);
-#ifdef HAVE_SSL_CIPHER_FIND
- tt_int_op(ret, OP_EQ, 0);
-#else
- tt_int_op(ret, OP_EQ, 1);
-#endif
-
- empty_method->get_cipher = fake_get_cipher;
- ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
- tt_int_op(ret, OP_EQ, 1);
-
- empty_method->get_cipher = m->get_cipher;
- empty_method->num_ciphers = m->num_ciphers;
- ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
- tt_int_op(ret, OP_EQ, 1);
-
- empty_method->get_cipher = fake_get_cipher;
- empty_method->num_ciphers = m->num_ciphers;
- ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
- tt_int_op(ret, OP_EQ, 1);
-
- empty_method->num_ciphers = fake_num_ciphers;
- ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
-#ifdef HAVE_SSL_CIPHER_FIND
- tt_int_op(ret, OP_EQ, 1);
-#else
- tt_int_op(ret, OP_EQ, 0);
-#endif
-
- done:
- tor_free(empty_method);
- SSL_free(ssl);
- SSL_CTX_free(ctx);
- tor_free(fixed_cipher1);
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-#ifndef OPENSSL_OPAQUE
-static void
-test_tortls_debug_state_callback(void *ignored)
-{
- (void)ignored;
- SSL *ssl;
- char *buf = tor_malloc_zero(1000);
- int n;
-
- setup_capture_of_logs(LOG_DEBUG);
-
- ssl = tor_malloc_zero(sizeof(SSL));
-
- tor_tls_debug_state_callback(ssl, 32, 45);
-
- n = tor_snprintf(buf, 1000, "SSL %p is now in state unknown"
- " state [type=32,val=45].\n", ssl);
- /* tor's snprintf returns -1 on error */
- tt_int_op(n, OP_NE, -1);
- expect_log_msg(buf);
-
- done:
- teardown_capture_of_logs();
- tor_free(buf);
- tor_free(ssl);
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-#ifndef OPENSSL_OPAQUE
-static void
-test_tortls_server_info_callback(void *ignored)
-{
- (void)ignored;
- tor_tls_t *tls;
- SSL_CTX *ctx;
- SSL *ssl;
-
- library_init();
-
- ctx = SSL_CTX_new(TLSv1_method());
- ssl = SSL_new(ctx);
-
- tor_tls_allocate_tor_tls_object_ex_data_index();
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
- tls->magic = TOR_TLS_MAGIC;
- tls->ssl = ssl;
-
- setup_full_capture_of_logs(LOG_WARN);
- SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_A);
- mock_clean_saved_logs();
- tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
- expect_single_log_msg("Couldn't look up the tls for an SSL*. How odd!\n");
-
- SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_B);
- mock_clean_saved_logs();
- tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
- expect_single_log_msg("Couldn't look up the tls for an SSL*. How odd!\n");
-
- SSL_set_state(ssl, 99);
- mock_clean_saved_logs();
- tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
- expect_no_log_entry();
- teardown_capture_of_logs();
-
- SSL_set_ex_data(tls->ssl, tor_tls_object_ex_data_index, tls);
- SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_B);
- tls->negotiated_callback = 0;
- tls->server_handshake_count = 120;
- tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
- tt_int_op(tls->server_handshake_count, OP_EQ, 121);
-
- tls->server_handshake_count = 127;
- tls->negotiated_callback = (void *)1;
- tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
- tt_int_op(tls->server_handshake_count, OP_EQ, 127);
- tt_int_op(tls->got_renegotiate, OP_EQ, 1);
-
- tls->ssl->session = SSL_SESSION_new();
- tls->wasV2Handshake = 0;
- tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
- tt_int_op(tls->wasV2Handshake, OP_EQ, 0);
-
- done:
- teardown_capture_of_logs();
- SSL_free(ssl);
- SSL_CTX_free(ctx);
- tor_free(tls);
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-#ifndef OPENSSL_OPAQUE
-static int fixed_ssl_read_result_index;
-static int fixed_ssl_read_result[5];
-static int fixed_ssl_shutdown_result;
-
-static int
-fixed_ssl_read(SSL *s, void *buf, int len)
-{
- (void)s;
- (void)buf;
- (void)len;
- return fixed_ssl_read_result[fixed_ssl_read_result_index++];
-}
-
-static int
-fixed_ssl_shutdown(SSL *s)
-{
- (void)s;
- return fixed_ssl_shutdown_result;
-}
-
-#ifndef LIBRESSL_VERSION_NUMBER
-static int fixed_ssl_state_to_set;
-static tor_tls_t *fixed_tls;
-
-static int
-setting_version_ssl_shutdown(SSL *s)
-{
- s->version = SSL2_VERSION;
- return fixed_ssl_shutdown_result;
-}
-
-static int
-setting_version_and_state_ssl_shutdown(SSL *s)
-{
- fixed_tls->state = fixed_ssl_state_to_set;
- s->version = SSL2_VERSION;
- return fixed_ssl_shutdown_result;
-}
-#endif /* !defined(LIBRESSL_VERSION_NUMBER) */
-
-static int
-dummy_handshake_func(SSL *s)
-{
- (void)s;
- return 1;
-}
-
-static void
-test_tortls_shutdown(void *ignored)
-{
- (void)ignored;
- int ret;
- tor_tls_t *tls;
- SSL_METHOD *method = give_me_a_test_method();
- setup_capture_of_logs(LOG_WARN);
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
- tls->ssl = tor_malloc_zero(sizeof(SSL));
- tls->ssl->method = method;
- method->ssl_read = fixed_ssl_read;
- method->ssl_shutdown = fixed_ssl_shutdown;
-
- ret = tor_tls_shutdown(tls);
- tt_int_op(ret, OP_EQ, -9);
-
- tls->state = TOR_TLS_ST_SENTCLOSE;
- fixed_ssl_read_result_index = 0;
- fixed_ssl_read_result[0] = 10;
- fixed_ssl_read_result[1] = -1;
- ret = tor_tls_shutdown(tls);
- tt_int_op(ret, OP_EQ, -9);
-
-#ifndef LIBRESSL_VERSION_NUMBER
- tls->ssl->handshake_func = dummy_handshake_func;
-
- fixed_ssl_read_result_index = 0;
- fixed_ssl_read_result[0] = 10;
- fixed_ssl_read_result[1] = 42;
- fixed_ssl_read_result[2] = 0;
- fixed_ssl_shutdown_result = 1;
- ERR_clear_error();
- tls->ssl->version = SSL2_VERSION;
- ret = tor_tls_shutdown(tls);
- tt_int_op(ret, OP_EQ, TOR_TLS_DONE);
- tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED);
-
- fixed_ssl_read_result_index = 0;
- fixed_ssl_read_result[0] = 10;
- fixed_ssl_read_result[1] = 42;
- fixed_ssl_read_result[2] = 0;
- fixed_ssl_shutdown_result = 0;
- ERR_clear_error();
- tls->ssl->version = 0;
- ret = tor_tls_shutdown(tls);
- tt_int_op(ret, OP_EQ, TOR_TLS_DONE);
- tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED);
-
- fixed_ssl_read_result_index = 0;
- fixed_ssl_read_result[0] = 10;
- fixed_ssl_read_result[1] = 42;
- fixed_ssl_read_result[2] = 0;
- fixed_ssl_shutdown_result = 0;
- ERR_clear_error();
- tls->ssl->version = 0;
- method->ssl_shutdown = setting_version_ssl_shutdown;
- ret = tor_tls_shutdown(tls);
- tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
-
- fixed_ssl_read_result_index = 0;
- fixed_ssl_read_result[0] = 10;
- fixed_ssl_read_result[1] = 42;
- fixed_ssl_read_result[2] = 0;
- fixed_ssl_shutdown_result = 0;
- fixed_tls = tls;
- fixed_ssl_state_to_set = TOR_TLS_ST_GOTCLOSE;
- ERR_clear_error();
- tls->ssl->version = 0;
- method->ssl_shutdown = setting_version_and_state_ssl_shutdown;
- ret = tor_tls_shutdown(tls);
- tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
-
- fixed_ssl_read_result_index = 0;
- fixed_ssl_read_result[0] = 10;
- fixed_ssl_read_result[1] = 42;
- fixed_ssl_read_result[2] = 0;
- fixed_ssl_read_result[3] = -1;
- fixed_ssl_shutdown_result = 0;
- fixed_tls = tls;
- fixed_ssl_state_to_set = 0;
- ERR_clear_error();
- tls->ssl->version = 0;
- method->ssl_shutdown = setting_version_and_state_ssl_shutdown;
- ret = tor_tls_shutdown(tls);
- tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
-#endif /* !defined(LIBRESSL_VERSION_NUMBER) */
-
- done:
- teardown_capture_of_logs();
- tor_free(method);
- tor_free(tls->ssl);
- tor_free(tls);
-}
-
-static int negotiated_callback_called;
-
-static void
-negotiated_callback_setter(tor_tls_t *t, void *arg)
-{
- (void)t;
- (void)arg;
- negotiated_callback_called++;
-}
-
-static void
-test_tortls_read(void *ignored)
-{
- (void)ignored;
- int ret;
- tor_tls_t *tls;
- char buf[100];
- SSL_METHOD *method = give_me_a_test_method();
- setup_capture_of_logs(LOG_WARN);
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
- tls->ssl = tor_malloc_zero(sizeof(SSL));
- tls->state = TOR_TLS_ST_OPEN;
-
- ret = tor_tls_read(tls, buf, 10);
- tt_int_op(ret, OP_EQ, -9);
-
- /* These tests assume that V2_HANDSHAKE_SERVER is set */
- tls->ssl->handshake_func = dummy_handshake_func;
- tls->ssl->method = method;
- method->ssl_read = fixed_ssl_read;
- fixed_ssl_read_result_index = 0;
- fixed_ssl_read_result[0] = 42;
- tls->state = TOR_TLS_ST_OPEN;
- ERR_clear_error();
- ret = tor_tls_read(tls, buf, 10);
- tt_int_op(ret, OP_EQ, 42);
-
- tls->state = TOR_TLS_ST_OPEN;
- tls->got_renegotiate = 1;
- fixed_ssl_read_result_index = 0;
- ERR_clear_error();
- ret = tor_tls_read(tls, buf, 10);
- tt_int_op(tls->got_renegotiate, OP_EQ, 0);
-
- tls->state = TOR_TLS_ST_OPEN;
- tls->got_renegotiate = 1;
- negotiated_callback_called = 0;
- tls->negotiated_callback = negotiated_callback_setter;
- fixed_ssl_read_result_index = 0;
- ERR_clear_error();
- ret = tor_tls_read(tls, buf, 10);
- tt_int_op(negotiated_callback_called, OP_EQ, 1);
-
-#ifndef LIBRESSL_VERSION_NUMBER
- fixed_ssl_read_result_index = 0;
- fixed_ssl_read_result[0] = 0;
- tls->ssl->version = SSL2_VERSION;
- ERR_clear_error();
- ret = tor_tls_read(tls, buf, 10);
- tt_int_op(ret, OP_EQ, TOR_TLS_CLOSE);
- tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED);
-#endif /* !defined(LIBRESSL_VERSION_NUMBER) */
- // TODO: fill up
-
- done:
- teardown_capture_of_logs();
- tor_free(tls->ssl);
- tor_free(tls);
- tor_free(method);
-}
-
-static int fixed_ssl_write_result;
-
-static int
-fixed_ssl_write(SSL *s, const void *buf, int len)
-{
- (void)s;
- (void)buf;
- (void)len;
- return fixed_ssl_write_result;
-}
-
-static void
-test_tortls_write(void *ignored)
-{
- (void)ignored;
- int ret;
- tor_tls_t *tls;
- SSL_METHOD *method = give_me_a_test_method();
- char buf[100];
- setup_capture_of_logs(LOG_WARN);
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
- tls->ssl = tor_malloc_zero(sizeof(SSL));
- tls->state = TOR_TLS_ST_OPEN;
-
- ret = tor_tls_write(tls, buf, 0);
- tt_int_op(ret, OP_EQ, 0);
-
- ret = tor_tls_write(tls, buf, 10);
- tt_int_op(ret, OP_EQ, -9);
-
- tls->ssl->method = method;
- tls->wantwrite_n = 1;
- ret = tor_tls_write(tls, buf, 10);
- tt_int_op(tls->wantwrite_n, OP_EQ, 0);
-
- method->ssl_write = fixed_ssl_write;
- tls->ssl->handshake_func = dummy_handshake_func;
- fixed_ssl_write_result = 1;
- ERR_clear_error();
- ret = tor_tls_write(tls, buf, 10);
- tt_int_op(ret, OP_EQ, 1);
-
- fixed_ssl_write_result = -1;
- ERR_clear_error();
- tls->ssl->rwstate = SSL_READING;
- SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL);
- SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_READ;
- ret = tor_tls_write(tls, buf, 10);
- tt_int_op(ret, OP_EQ, TOR_TLS_WANTREAD);
-
- ERR_clear_error();
- tls->ssl->rwstate = SSL_READING;
- SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL);
- SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_WRITE;
- ret = tor_tls_write(tls, buf, 10);
- tt_int_op(ret, OP_EQ, TOR_TLS_WANTWRITE);
-
- done:
- teardown_capture_of_logs();
- BIO_free(tls->ssl->rbio);
- tor_free(tls->ssl);
- tor_free(tls);
- tor_free(method);
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-#ifndef OPENSSL_OPAQUE
-static int fixed_ssl_accept_result;
-static int fixed_ssl_connect_result;
-
-static int
-setting_error_ssl_accept(SSL *ssl)
-{
- (void)ssl;
- ERR_put_error(ERR_LIB_BN, 2, -1, "somewhere.c", 99);
- ERR_put_error(ERR_LIB_SYS, 2, -1, "somewhere.c", 99);
- return fixed_ssl_accept_result;
-}
-
-static int
-setting_error_ssl_connect(SSL *ssl)
-{
- (void)ssl;
- ERR_put_error(ERR_LIB_BN, 2, -1, "somewhere.c", 99);
- ERR_put_error(ERR_LIB_SYS, 2, -1, "somewhere.c", 99);
- return fixed_ssl_connect_result;
-}
-
-static int
-fixed_ssl_accept(SSL *ssl)
-{
- (void) ssl;
- return fixed_ssl_accept_result;
-}
-
-static void
-test_tortls_handshake(void *ignored)
-{
- (void)ignored;
- int ret;
- tor_tls_t *tls;
- SSL_CTX *ctx;
- SSL_METHOD *method = give_me_a_test_method();
- setup_capture_of_logs(LOG_INFO);
-
- SSL_library_init();
- SSL_load_error_strings();
-
- ctx = SSL_CTX_new(TLSv1_method());
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
- tls->ssl = SSL_new(ctx);
- tls->state = TOR_TLS_ST_HANDSHAKE;
-
- ret = tor_tls_handshake(tls);
- tt_int_op(ret, OP_EQ, -9);
-
- tls->isServer = 1;
- tls->state = TOR_TLS_ST_HANDSHAKE;
- ret = tor_tls_handshake(tls);
- tt_int_op(ret, OP_EQ, -9);
-
- tls->ssl->method = method;
- method->ssl_accept = fixed_ssl_accept;
- fixed_ssl_accept_result = 2;
- ERR_clear_error();
- tls->state = TOR_TLS_ST_HANDSHAKE;
- ret = tor_tls_handshake(tls);
- tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_OPEN);
-
- method->ssl_accept = setting_error_ssl_accept;
- fixed_ssl_accept_result = 1;
- ERR_clear_error();
- mock_clean_saved_logs();
- tls->state = TOR_TLS_ST_HANDSHAKE;
- ret = tor_tls_handshake(tls);
- tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
- expect_log_entry();
- /* This fails on jessie. Investigate why! */
-#if 0
- expect_log_msg("TLS error while handshaking: (null) (in bignum routines:"
- "(null):SSLv3 write client hello B)\n");
- expect_log_msg("TLS error while handshaking: (null) (in system library:"
- "connect:SSLv3 write client hello B)\n");
-#endif /* 0 */
- expect_log_severity(LOG_INFO);
-
- tls->isServer = 0;
- method->ssl_connect = setting_error_ssl_connect;
- fixed_ssl_connect_result = 1;
- ERR_clear_error();
- mock_clean_saved_logs();
- tls->state = TOR_TLS_ST_HANDSHAKE;
- ret = tor_tls_handshake(tls);
- tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
- expect_log_entry();
-#if 0
- /* See above */
- expect_log_msg("TLS error while handshaking: "
- "(null) (in bignum routines:(null):SSLv3 write client hello B)\n");
- expect_log_msg("TLS error while handshaking: "
- "(null) (in system library:connect:SSLv3 write client hello B)\n");
-#endif /* 0 */
- expect_log_severity(LOG_WARN);
-
- done:
- teardown_capture_of_logs();
- SSL_free(tls->ssl);
- SSL_CTX_free(ctx);
- tor_free(tls);
- tor_free(method);
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-#ifndef OPENSSL_OPAQUE
-static void
-test_tortls_finish_handshake(void *ignored)
-{
- (void)ignored;
- int ret;
- tor_tls_t *tls;
- SSL_CTX *ctx;
- SSL_METHOD *method = give_me_a_test_method();
- SSL_library_init();
- SSL_load_error_strings();
-
- X509 *c1 = read_cert_from(validCertString);
- SESS_CERT_local *sess = NULL;
-
- ctx = SSL_CTX_new(method);
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
- tls->ssl = SSL_new(ctx);
- tls->state = TOR_TLS_ST_OPEN;
-
- ret = tor_tls_finish_handshake(tls);
- tt_int_op(ret, OP_EQ, 0);
-
- tls->isServer = 1;
- tls->wasV2Handshake = 0;
- setup_full_capture_of_logs(LOG_WARN);
- ret = tor_tls_finish_handshake(tls);
- tt_int_op(ret, OP_EQ, 0);
- tt_int_op(tls->wasV2Handshake, OP_EQ, 1);
- expect_single_log_msg_containing("For some reason, wasV2Handshake didn't "
- "get set.");
- teardown_capture_of_logs();
-
- tls->wasV2Handshake = 1;
- ret = tor_tls_finish_handshake(tls);
- tt_int_op(ret, OP_EQ, 0);
- tt_int_op(tls->wasV2Handshake, OP_EQ, 1);
-
- tls->wasV2Handshake = 1;
- tls->ssl->session = SSL_SESSION_new();
- ret = tor_tls_finish_handshake(tls);
- tt_int_op(ret, OP_EQ, 0);
- tt_int_op(tls->wasV2Handshake, OP_EQ, 0);
-
- tls->isServer = 0;
-
- sess = tor_malloc_zero(sizeof(SESS_CERT_local));
- tls->ssl->session->sess_cert = (void *)sess;
- sess->cert_chain = sk_X509_new_null();
- sk_X509_push(sess->cert_chain, c1);
- tls->ssl->session->peer = c1;
- tls->wasV2Handshake = 0;
- ret = tor_tls_finish_handshake(tls);
- tt_int_op(ret, OP_EQ, 0);
- tt_int_op(tls->wasV2Handshake, OP_EQ, 1);
-
- method->num_ciphers = fake_num_ciphers;
- ret = tor_tls_finish_handshake(tls);
- tt_int_op(ret, OP_EQ, -9);
-
- done:
- if (sess)
- sk_X509_free(sess->cert_chain);
- if (tls->ssl && tls->ssl->session) {
- tor_free(tls->ssl->session->sess_cert);
- }
- SSL_free(tls->ssl);
- tor_free(tls);
- SSL_CTX_free(ctx);
- tor_free(method);
- teardown_capture_of_logs();
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-static int fixed_crypto_pk_new_result_index;
-static crypto_pk_t *fixed_crypto_pk_new_result[5];
-
-static crypto_pk_t *
-fixed_crypto_pk_new(void)
-{
- return fixed_crypto_pk_new_result[fixed_crypto_pk_new_result_index++];
-}
-
-#ifndef OPENSSL_OPAQUE
-static int fixed_crypto_pk_generate_key_with_bits_result_index;
-static int fixed_crypto_pk_generate_key_with_bits_result[5];
-static int fixed_tor_tls_create_certificate_result_index;
-static X509 *fixed_tor_tls_create_certificate_result[5];
-static int fixed_tor_x509_cert_new_result_index;
-static tor_x509_cert_t *fixed_tor_x509_cert_new_result[5];
-
-static int
-fixed_crypto_pk_generate_key_with_bits(crypto_pk_t *env, int bits)
-{
- (void)env;
- (void)bits;
- return fixed_crypto_pk_generate_key_with_bits_result[
- fixed_crypto_pk_generate_key_with_bits_result_index++];
-}
-
-static X509 *
-fixed_tor_tls_create_certificate(crypto_pk_t *rsa,
- crypto_pk_t *rsa_sign,
- const char *cname,
- const char *cname_sign,
- unsigned int cert_lifetime)
-{
- (void)rsa;
- (void)rsa_sign;
- (void)cname;
- (void)cname_sign;
- (void)cert_lifetime;
- return fixed_tor_tls_create_certificate_result[
- fixed_tor_tls_create_certificate_result_index++];
-}
-
-static tor_x509_cert_t *
-fixed_tor_x509_cert_new(X509 *x509_cert)
-{
- (void) x509_cert;
- return fixed_tor_x509_cert_new_result[
- fixed_tor_x509_cert_new_result_index++];
-}
-
-static void
-test_tortls_context_new(void *ignored)
-{
- (void)ignored;
- tor_tls_context_t *ret;
- crypto_pk_t *pk1, *pk2, *pk3, *pk4, *pk5, *pk6, *pk7, *pk8, *pk9, *pk10,
- *pk11, *pk12, *pk13, *pk14, *pk15, *pk16, *pk17, *pk18;
-
- pk1 = crypto_pk_new();
- pk2 = crypto_pk_new();
- pk3 = crypto_pk_new();
- pk4 = crypto_pk_new();
- pk5 = crypto_pk_new();
- pk6 = crypto_pk_new();
- pk7 = crypto_pk_new();
- pk8 = crypto_pk_new();
- pk9 = crypto_pk_new();
- pk10 = crypto_pk_new();
- pk11 = crypto_pk_new();
- pk12 = crypto_pk_new();
- pk13 = crypto_pk_new();
- pk14 = crypto_pk_new();
- pk15 = crypto_pk_new();
- pk16 = crypto_pk_new();
- pk17 = crypto_pk_new();
- pk18 = crypto_pk_new();
-
- fixed_crypto_pk_new_result_index = 0;
- fixed_crypto_pk_new_result[0] = NULL;
- MOCK(crypto_pk_new, fixed_crypto_pk_new);
- ret = tor_tls_context_new(NULL, 0, 0, 0);
- tt_assert(!ret);
-
- /* note: we already override this in testing_common.c, so we
- * run this unit test in a subprocess. */
- MOCK(crypto_pk_generate_key_with_bits,
- fixed_crypto_pk_generate_key_with_bits);
- fixed_crypto_pk_new_result_index = 0;
- fixed_crypto_pk_new_result[0] = pk1;
- fixed_crypto_pk_new_result[1] = NULL;
- fixed_crypto_pk_generate_key_with_bits_result[0] = -1;
- fixed_crypto_pk_generate_key_with_bits_result_index = 0;
- ret = tor_tls_context_new(NULL, 0, 0, 0);
- tt_assert(!ret);
-
- fixed_crypto_pk_new_result_index = 0;
- fixed_crypto_pk_new_result[0] = pk2;
- fixed_crypto_pk_new_result[1] = NULL;
- fixed_crypto_pk_generate_key_with_bits_result[0] = 0;
- fixed_crypto_pk_generate_key_with_bits_result_index = 0;
- ret = tor_tls_context_new(NULL, 0, 0, 0);
- tt_assert(!ret);
-
- fixed_crypto_pk_new_result_index = 0;
- fixed_crypto_pk_new_result[0] = pk3;
- fixed_crypto_pk_new_result[1] = pk4;
- fixed_crypto_pk_new_result[2] = NULL;
- fixed_crypto_pk_generate_key_with_bits_result[0] = 0;
- fixed_crypto_pk_generate_key_with_bits_result[1] = -1;
- fixed_crypto_pk_generate_key_with_bits_result_index = 0;
- ret = tor_tls_context_new(NULL, 0, 0, 0);
- tt_assert(!ret);
-
- MOCK(tor_tls_create_certificate, fixed_tor_tls_create_certificate);
-
- fixed_crypto_pk_new_result_index = 0;
- fixed_crypto_pk_new_result[0] = pk5;
- fixed_crypto_pk_new_result[1] = pk6;
- fixed_crypto_pk_new_result[2] = NULL;
- fixed_crypto_pk_generate_key_with_bits_result_index = 0;
- fixed_crypto_pk_generate_key_with_bits_result[1] = 0;
- fixed_tor_tls_create_certificate_result_index = 0;
- fixed_tor_tls_create_certificate_result[0] = NULL;
- fixed_tor_tls_create_certificate_result[1] = X509_new();
- fixed_tor_tls_create_certificate_result[2] = X509_new();
- ret = tor_tls_context_new(NULL, 0, 0, 0);
- tt_assert(!ret);
-
- fixed_crypto_pk_new_result_index = 0;
- fixed_crypto_pk_new_result[0] = pk7;
- fixed_crypto_pk_new_result[1] = pk8;
- fixed_crypto_pk_new_result[2] = NULL;
- fixed_crypto_pk_generate_key_with_bits_result_index = 0;
- fixed_tor_tls_create_certificate_result_index = 0;
- fixed_tor_tls_create_certificate_result[0] = X509_new();
- fixed_tor_tls_create_certificate_result[1] = NULL;
- fixed_tor_tls_create_certificate_result[2] = X509_new();
- ret = tor_tls_context_new(NULL, 0, 0, 0);
- tt_assert(!ret);
-
- fixed_crypto_pk_new_result_index = 0;
- fixed_crypto_pk_new_result[0] = pk9;
- fixed_crypto_pk_new_result[1] = pk10;
- fixed_crypto_pk_new_result[2] = NULL;
- fixed_crypto_pk_generate_key_with_bits_result_index = 0;
- fixed_tor_tls_create_certificate_result_index = 0;
- fixed_tor_tls_create_certificate_result[0] = X509_new();
- fixed_tor_tls_create_certificate_result[1] = X509_new();
- fixed_tor_tls_create_certificate_result[2] = NULL;
- ret = tor_tls_context_new(NULL, 0, 0, 0);
- tt_assert(!ret);
-
- MOCK(tor_x509_cert_new, fixed_tor_x509_cert_new);
- fixed_crypto_pk_new_result_index = 0;
- fixed_crypto_pk_new_result[0] = pk11;
- fixed_crypto_pk_new_result[1] = pk12;
- fixed_crypto_pk_new_result[2] = NULL;
- fixed_crypto_pk_generate_key_with_bits_result_index = 0;
- fixed_tor_tls_create_certificate_result_index = 0;
- fixed_tor_tls_create_certificate_result[0] = X509_new();
- fixed_tor_tls_create_certificate_result[1] = X509_new();
- fixed_tor_tls_create_certificate_result[2] = X509_new();
- fixed_tor_x509_cert_new_result_index = 0;
- fixed_tor_x509_cert_new_result[0] = NULL;
- fixed_tor_x509_cert_new_result[1] = NULL;
- fixed_tor_x509_cert_new_result[2] = NULL;
- ret = tor_tls_context_new(NULL, 0, 0, 0);
- tt_assert(!ret);
-
- fixed_crypto_pk_new_result_index = 0;
- fixed_crypto_pk_new_result[0] = pk13;
- fixed_crypto_pk_new_result[1] = pk14;
- fixed_crypto_pk_new_result[2] = NULL;
- fixed_crypto_pk_generate_key_with_bits_result_index = 0;
- fixed_tor_tls_create_certificate_result_index = 0;
- fixed_tor_tls_create_certificate_result[0] = X509_new();
- fixed_tor_tls_create_certificate_result[1] = X509_new();
- fixed_tor_tls_create_certificate_result[2] = X509_new();
- fixed_tor_x509_cert_new_result_index = 0;
- fixed_tor_x509_cert_new_result[0] = tor_malloc_zero(sizeof(tor_x509_cert_t));
- fixed_tor_x509_cert_new_result[1] = NULL;
- fixed_tor_x509_cert_new_result[2] = NULL;
- ret = tor_tls_context_new(NULL, 0, 0, 0);
- tt_assert(!ret);
-
- fixed_crypto_pk_new_result_index = 0;
- fixed_crypto_pk_new_result[0] = pk15;
- fixed_crypto_pk_new_result[1] = pk16;
- fixed_crypto_pk_new_result[2] = NULL;
- fixed_crypto_pk_generate_key_with_bits_result_index = 0;
- fixed_tor_tls_create_certificate_result_index = 0;
- fixed_tor_tls_create_certificate_result[0] = X509_new();
- fixed_tor_tls_create_certificate_result[1] = X509_new();
- fixed_tor_tls_create_certificate_result[2] = X509_new();
- fixed_tor_x509_cert_new_result_index = 0;
- fixed_tor_x509_cert_new_result[0] = tor_malloc_zero(sizeof(tor_x509_cert_t));
- fixed_tor_x509_cert_new_result[1] = tor_malloc_zero(sizeof(tor_x509_cert_t));
- fixed_tor_x509_cert_new_result[2] = NULL;
- ret = tor_tls_context_new(NULL, 0, 0, 0);
- tt_assert(!ret);
-
- fixed_crypto_pk_new_result_index = 0;
- fixed_crypto_pk_new_result[0] = pk17;
- fixed_crypto_pk_new_result[1] = pk18;
- fixed_crypto_pk_new_result[2] = NULL;
- fixed_crypto_pk_generate_key_with_bits_result_index = 0;
- fixed_tor_tls_create_certificate_result_index = 0;
- fixed_tor_tls_create_certificate_result[0] = X509_new();
- fixed_tor_tls_create_certificate_result[1] = X509_new();
- fixed_tor_tls_create_certificate_result[2] = X509_new();
- fixed_tor_x509_cert_new_result_index = 0;
- fixed_tor_x509_cert_new_result[0] = tor_malloc_zero(sizeof(tor_x509_cert_t));
- fixed_tor_x509_cert_new_result[1] = tor_malloc_zero(sizeof(tor_x509_cert_t));
- fixed_tor_x509_cert_new_result[2] = tor_malloc_zero(sizeof(tor_x509_cert_t));
- ret = tor_tls_context_new(NULL, 0, 0, 0);
- tt_assert(!ret);
-
- done:
- UNMOCK(tor_x509_cert_new);
- UNMOCK(tor_tls_create_certificate);
- UNMOCK(crypto_pk_generate_key_with_bits);
- UNMOCK(crypto_pk_new);
-}
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-static int fixed_crypto_pk_get_evp_pkey_result_index = 0;
-static EVP_PKEY *fixed_crypto_pk_get_evp_pkey_result[5];
-
-static EVP_PKEY *
-fixed_crypto_pk_get_evp_pkey_(crypto_pk_t *env, int private)
-{
- (void) env;
- (void) private;
- return fixed_crypto_pk_get_evp_pkey_result[
- fixed_crypto_pk_get_evp_pkey_result_index++];
-}
-
-static void
-test_tortls_create_certificate(void *ignored)
-{
- (void)ignored;
- X509 *ret;
- crypto_pk_t *pk1, *pk2;
-
- pk1 = crypto_pk_new();
- pk2 = crypto_pk_new();
-
- MOCK(crypto_pk_get_openssl_evp_pkey_, fixed_crypto_pk_get_evp_pkey_);
- fixed_crypto_pk_get_evp_pkey_result_index = 0;
- fixed_crypto_pk_get_evp_pkey_result[0] = NULL;
- ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1);
- tt_assert(!ret);
-
- fixed_crypto_pk_get_evp_pkey_result_index = 0;
- fixed_crypto_pk_get_evp_pkey_result[0] = EVP_PKEY_new();
- fixed_crypto_pk_get_evp_pkey_result[1] = NULL;
- ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1);
- tt_assert(!ret);
-
- fixed_crypto_pk_get_evp_pkey_result_index = 0;
- fixed_crypto_pk_get_evp_pkey_result[0] = EVP_PKEY_new();
- fixed_crypto_pk_get_evp_pkey_result[1] = EVP_PKEY_new();
- ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1);
- tt_assert(!ret);
-
- done:
- UNMOCK(crypto_pk_get_openssl_evp_pkey_);
- crypto_pk_free(pk1);
- crypto_pk_free(pk2);
-}
-
-static void
-test_tortls_cert_new(void *ignored)
-{
- (void)ignored;
- tor_x509_cert_t *ret;
- X509 *cert = read_cert_from(validCertString);
-
- ret = tor_x509_cert_new(NULL);
- tt_assert(!ret);
-
- ret = tor_x509_cert_new(cert);
- tt_assert(ret);
- tor_x509_cert_free(ret);
- ret = NULL;
-
-#if 0
- cert = read_cert_from(validCertString);
- /* XXX this doesn't do what you think: it alters a copy of the pubkey. */
- X509_get_pubkey(cert)->type = EVP_PKEY_DSA;
- ret = tor_x509_cert_new(cert);
- tt_assert(ret);
-#endif /* 0 */
-
-#ifndef OPENSSL_OPAQUE
- cert = read_cert_from(validCertString);
- X509_CINF_free(cert->cert_info);
- cert->cert_info = NULL;
- ret = tor_x509_cert_new(cert);
- tt_assert(ret);
-#endif /* !defined(OPENSSL_OPAQUE) */
-
- done:
- tor_x509_cert_free(ret);
-}
-
-static void
-test_tortls_cert_is_valid(void *ignored)
-{
- (void)ignored;
- int ret;
- tor_x509_cert_t *cert = NULL, *scert = NULL;
-
- scert = tor_malloc_zero(sizeof(tor_x509_cert_t));
- ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0);
- tt_int_op(ret, OP_EQ, 0);
-
- cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
- ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0);
- tt_int_op(ret, OP_EQ, 0);
- tor_free(scert);
- tor_free(cert);
-
- cert = tor_x509_cert_new(read_cert_from(validCertString));
- scert = tor_x509_cert_new(read_cert_from(caCertString));
- ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0);
- tt_int_op(ret, OP_EQ, 1);
-
-#ifndef OPENSSL_OPAQUE
- tor_x509_cert_free(cert);
- tor_x509_cert_free(scert);
- cert = tor_x509_cert_new(read_cert_from(validCertString));
- scert = tor_x509_cert_new(read_cert_from(caCertString));
- ASN1_TIME_free(cert->cert->cert_info->validity->notAfter);
- cert->cert->cert_info->validity->notAfter =
- ASN1_TIME_set(NULL, time(NULL)-1000000);
- ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0);
- tt_int_op(ret, OP_EQ, 0);
-
- tor_x509_cert_free(cert);
- tor_x509_cert_free(scert);
- cert = tor_x509_cert_new(read_cert_from(validCertString));
- scert = tor_x509_cert_new(read_cert_from(caCertString));
- X509_PUBKEY_free(cert->cert->cert_info->key);
- cert->cert->cert_info->key = NULL;
- ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 1);
- tt_int_op(ret, OP_EQ, 0);
-#endif /* !defined(OPENSSL_OPAQUE) */
-
-#if 0
- tor_x509_cert_free(cert);
- tor_x509_cert_free(scert);
- cert = tor_x509_cert_new(read_cert_from(validCertString));
- scert = tor_x509_cert_new(read_cert_from(caCertString));
- /* This doesn't actually change the key in the cert. XXXXXX */
- BN_one(EVP_PKEY_get1_RSA(X509_get_pubkey(cert->cert))->n);
- ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 1);
- tt_int_op(ret, OP_EQ, 0);
-
- tor_x509_cert_free(cert);
- tor_x509_cert_free(scert);
- cert = tor_x509_cert_new(read_cert_from(validCertString));
- scert = tor_x509_cert_new(read_cert_from(caCertString));
- /* This doesn't actually change the key in the cert. XXXXXX */
- X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC;
- ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 1);
- tt_int_op(ret, OP_EQ, 0);
-
- tor_x509_cert_free(cert);
- tor_x509_cert_free(scert);
- cert = tor_x509_cert_new(read_cert_from(validCertString));
- scert = tor_x509_cert_new(read_cert_from(caCertString));
- /* This doesn't actually change the key in the cert. XXXXXX */
- X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC;
- ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0);
- tt_int_op(ret, OP_EQ, 1);
-
- tor_x509_cert_free(cert);
- tor_x509_cert_free(scert);
- cert = tor_x509_cert_new(read_cert_from(validCertString));
- scert = tor_x509_cert_new(read_cert_from(caCertString));
- /* This doesn't actually change the key in the cert. XXXXXX */
- X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC;
- X509_get_pubkey(cert->cert)->ameth = NULL;
- ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0);
- tt_int_op(ret, OP_EQ, 0);
-#endif /* 0 */
-
- done:
- tor_x509_cert_free(cert);
- tor_x509_cert_free(scert);
-}
-
-static void
-test_tortls_context_init_one(void *ignored)
-{
- (void)ignored;
- int ret;
- tor_tls_context_t *old = NULL;
-
- MOCK(crypto_pk_new, fixed_crypto_pk_new);
-
- fixed_crypto_pk_new_result_index = 0;
- fixed_crypto_pk_new_result[0] = NULL;
- ret = tor_tls_context_init_one(&old, NULL, 0, 0, 0);
- tt_int_op(ret, OP_EQ, -1);
-
- done:
- UNMOCK(crypto_pk_new);
-}
-
-#define LOCAL_TEST_CASE(name, flags) \
+#define LOCAL_TEST_CASE(name, flags) \
{ #name, test_tortls_##name, (flags|TT_FORK), NULL, NULL }
-#ifdef OPENSSL_OPAQUE
-#define INTRUSIVE_TEST_CASE(name, flags) \
- { #name, NULL, TT_SKIP, NULL, NULL }
-#else
-#define INTRUSIVE_TEST_CASE(name, flags) LOCAL_TEST_CASE(name, flags)
-#endif /* defined(OPENSSL_OPAQUE) */
-
struct testcase_t tortls_tests[] = {
LOCAL_TEST_CASE(errno_to_tls_error, 0),
LOCAL_TEST_CASE(err_to_string, 0),
- LOCAL_TEST_CASE(tor_tls_new, TT_FORK),
LOCAL_TEST_CASE(tor_tls_get_error, 0),
- LOCAL_TEST_CASE(get_state_description, TT_FORK),
- LOCAL_TEST_CASE(get_by_ssl, TT_FORK),
- LOCAL_TEST_CASE(allocate_tor_tls_object_ex_data_index, TT_FORK),
- LOCAL_TEST_CASE(log_one_error, TT_FORK),
- INTRUSIVE_TEST_CASE(get_error, TT_FORK),
- LOCAL_TEST_CASE(always_accept_verify_cb, 0),
- INTRUSIVE_TEST_CASE(x509_cert_free, 0),
LOCAL_TEST_CASE(x509_cert_get_id_digests, 0),
- INTRUSIVE_TEST_CASE(cert_matches_key, 0),
- INTRUSIVE_TEST_CASE(cert_get_key, 0),
- LOCAL_TEST_CASE(get_my_client_auth_key, TT_FORK),
LOCAL_TEST_CASE(get_my_certs, TT_FORK),
- INTRUSIVE_TEST_CASE(get_ciphersuite_name, 0),
- INTRUSIVE_TEST_CASE(classify_client_ciphers, 0),
- LOCAL_TEST_CASE(client_is_using_v2_ciphers, 0),
- INTRUSIVE_TEST_CASE(verify, 0),
- INTRUSIVE_TEST_CASE(check_lifetime, 0),
- INTRUSIVE_TEST_CASE(get_pending_bytes, 0),
LOCAL_TEST_CASE(get_forced_write_size, 0),
- LOCAL_TEST_CASE(get_write_overhead_ratio, TT_FORK),
LOCAL_TEST_CASE(used_v1_handshake, TT_FORK),
LOCAL_TEST_CASE(get_num_server_handshakes, 0),
LOCAL_TEST_CASE(server_got_renegotiate, 0),
- INTRUSIVE_TEST_CASE(SSL_SESSION_get_master_key, 0),
- INTRUSIVE_TEST_CASE(get_tlssecrets, 0),
- INTRUSIVE_TEST_CASE(get_buffer_sizes, 0),
LOCAL_TEST_CASE(evaluate_ecgroup_for_tls, 0),
- INTRUSIVE_TEST_CASE(try_to_extract_certs_from_tls, 0),
- INTRUSIVE_TEST_CASE(get_peer_cert, 0),
- INTRUSIVE_TEST_CASE(peer_has_cert, 0),
- INTRUSIVE_TEST_CASE(shutdown, 0),
- INTRUSIVE_TEST_CASE(finish_handshake, 0),
- INTRUSIVE_TEST_CASE(handshake, 0),
- INTRUSIVE_TEST_CASE(write, 0),
- INTRUSIVE_TEST_CASE(read, 0),
- INTRUSIVE_TEST_CASE(server_info_callback, 0),
- LOCAL_TEST_CASE(is_server, 0),
- INTRUSIVE_TEST_CASE(assert_renegotiation_unblocked, 0),
- INTRUSIVE_TEST_CASE(block_renegotiation, 0),
- INTRUSIVE_TEST_CASE(unblock_renegotiation, 0),
- INTRUSIVE_TEST_CASE(set_renegotiate_callback, 0),
- LOCAL_TEST_CASE(set_logged_address, 0),
- INTRUSIVE_TEST_CASE(find_cipher_by_id, 0),
- INTRUSIVE_TEST_CASE(session_secret_cb, 0),
- INTRUSIVE_TEST_CASE(debug_state_callback, 0),
- INTRUSIVE_TEST_CASE(context_new, TT_FORK /* redundant */),
- LOCAL_TEST_CASE(create_certificate, 0),
- LOCAL_TEST_CASE(cert_new, 0),
- LOCAL_TEST_CASE(cert_is_valid, 0),
- LOCAL_TEST_CASE(context_init_one, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_tortls_openssl.c b/src/test/test_tortls_openssl.c
new file mode 100644
index 000000000..a9336a6f1
--- /dev/null
+++ b/src/test/test_tortls_openssl.c
@@ -0,0 +1,2597 @@
+/* Copyright (c) 2010-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define TORTLS_PRIVATE
+#define TORTLS_OPENSSL_PRIVATE
+#define TOR_X509_PRIVATE
+#define LOG_PRIVATE
+#include "orconfig.h"
+
+#ifdef _WIN32
+#include <winsock2.h>
+#endif
+#include <math.h>
+
+#include "lib/cc/compat_compiler.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>
+
+#include <openssl/ssl.h>
+#include <openssl/ssl3.h>
+#include <openssl/err.h>
+#include <openssl/asn1t.h>
+#include <openssl/x509.h>
+#include <openssl/rsa.h>
+#include <openssl/evp.h>
+#include <openssl/bn.h>
+
+ENABLE_GCC_WARNING(redundant-decls)
+
+#include "core/or/or.h"
+#include "lib/log/log.h"
+#include "app/config/config.h"
+#include "lib/crypt_ops/compat_openssl.h"
+#include "lib/tls/x509.h"
+#include "lib/tls/tortls.h"
+#include "lib/tls/tortls_st.h"
+#include "lib/tls/tortls_internal.h"
+#include "app/config/or_state_st.h"
+
+#include "test/test.h"
+#include "test/log_test_helpers.h"
+#define NS_MODULE tortls
+
+#ifndef HAVE_SSL_STATE
+#define OPENSSL_OPAQUE
+#endif
+
+#if defined(OPENSSL_OPAQUE) && !defined(LIBRESSL_VERSION_NUMBER)
+#define SSL_STATE_STR "before SSL initialization"
+#else
+#define SSL_STATE_STR "before/accept initialization"
+#endif
+
+#ifndef OPENSSL_OPAQUE
+static SSL_METHOD *
+give_me_a_test_method(void)
+{
+ SSL_METHOD *method = tor_malloc_zero(sizeof(SSL_METHOD));
+ memcpy(method, TLSv1_method(), sizeof(SSL_METHOD));
+ return method;
+}
+
+static int
+fake_num_ciphers(void)
+{
+ return 0;
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+static int
+mock_tls_cert_matches_key(const tor_tls_t *tls, const tor_x509_cert_t *cert)
+{
+ (void) tls;
+ (void) cert; // XXXX look at this.
+ return 1;
+}
+
+static void
+test_tortls_tor_tls_new(void *data)
+{
+ (void) data;
+ MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key);
+ crypto_pk_t *key1 = NULL, *key2 = NULL;
+ SSL_METHOD *method = NULL;
+
+ key1 = pk_generate(2);
+ key2 = pk_generate(3);
+
+ tor_tls_t *tls = NULL;
+ tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER,
+ key1, key2, 86400), OP_EQ, 0);
+ tls = tor_tls_new(-1, 0);
+ tt_want(tls);
+ tor_tls_free(tls); tls = NULL;
+
+ SSL_CTX_free(client_tls_context->ctx);
+ client_tls_context->ctx = NULL;
+ tls = tor_tls_new(-1, 0);
+ tt_ptr_op(tls, OP_EQ, NULL);
+
+#ifndef OPENSSL_OPAQUE
+ method = give_me_a_test_method();
+ SSL_CTX *ctx = SSL_CTX_new(method);
+ method->num_ciphers = fake_num_ciphers;
+ client_tls_context->ctx = ctx;
+ tls = tor_tls_new(-1, 0);
+ tt_ptr_op(tls, OP_EQ, NULL);
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+ done:
+ UNMOCK(tor_tls_cert_matches_key);
+ crypto_pk_free(key1);
+ crypto_pk_free(key2);
+ tor_tls_free(tls);
+ tor_free(method);
+ tor_tls_free_all();
+}
+
+#define NS_MODULE tortls
+
+static void
+library_init(void)
+{
+#ifdef OPENSSL_1_1_API
+ OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL);
+#else
+ SSL_library_init();
+ SSL_load_error_strings();
+#endif
+}
+
+static void
+test_tortls_get_state_description(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ char *buf;
+ SSL_CTX *ctx;
+
+ library_init();
+ ctx = SSL_CTX_new(SSLv23_method());
+
+ buf = tor_malloc_zero(1000);
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ tor_tls_get_state_description(NULL, buf, 20);
+ tt_str_op(buf, OP_EQ, "(No SSL object)");
+
+ SSL_free(tls->ssl);
+ tls->ssl = NULL;
+ tor_tls_get_state_description(tls, buf, 20);
+ tt_str_op(buf, OP_EQ, "(No SSL object)");
+
+ tls->ssl = SSL_new(ctx);
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in HANDSHAKE");
+
+ tls->state = TOR_TLS_ST_OPEN;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in OPEN");
+
+ tls->state = TOR_TLS_ST_GOTCLOSE;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in GOTCLOSE");
+
+ tls->state = TOR_TLS_ST_SENTCLOSE;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in SENTCLOSE");
+
+ tls->state = TOR_TLS_ST_CLOSED;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in CLOSED");
+
+ tls->state = TOR_TLS_ST_RENEGOTIATE;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in RENEGOTIATE");
+
+ tls->state = TOR_TLS_ST_BUFFEREVENT;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR);
+
+ tls->state = 7;
+ tor_tls_get_state_description(tls, buf, 200);
+ tt_str_op(buf, OP_EQ, SSL_STATE_STR " in unknown TLS state");
+
+ done:
+ SSL_CTX_free(ctx);
+ SSL_free(tls->ssl);
+ tor_free(buf);
+ tor_free(tls);
+}
+
+static void
+test_tortls_get_by_ssl(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ tor_tls_t *res;
+ SSL_CTX *ctx;
+ SSL *ssl;
+
+ library_init();
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
+ ctx = SSL_CTX_new(SSLv23_method());
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->magic = TOR_TLS_MAGIC;
+
+ ssl = SSL_new(ctx);
+
+ res = tor_tls_get_by_ssl(ssl);
+ tt_assert(!res);
+
+ SSL_set_ex_data(ssl, tor_tls_object_ex_data_index, tls);
+
+ res = tor_tls_get_by_ssl(ssl);
+ tt_assert(res == tls);
+
+ done:
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+ tor_free(tls);
+}
+
+static void
+test_tortls_allocate_tor_tls_object_ex_data_index(void *ignored)
+{
+ (void)ignored;
+ int first;
+
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
+ first = tor_tls_object_ex_data_index;
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+ tt_int_op(first, OP_EQ, tor_tls_object_ex_data_index);
+
+ done:
+ (void)0;
+}
+
+static void
+test_tortls_log_one_error(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ SSL_CTX *ctx;
+ SSL *ssl = NULL;
+
+ library_init();
+
+ ctx = SSL_CTX_new(SSLv23_method());
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ setup_capture_of_logs(LOG_INFO);
+
+ tor_tls_log_one_error(NULL, 0, LOG_WARN, 0, "something");
+ expect_log_msg("TLS error while something: "
+ "(null) (in (null):(null):---)\n");
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, 0, LOG_WARN, 0, NULL);
+ expect_log_msg("TLS error: (null) "
+ "(in (null):(null):---)\n");
+
+ mock_clean_saved_logs();
+ tls->address = tor_strdup("127.hello");
+ tor_tls_log_one_error(tls, 0, LOG_WARN, 0, NULL);
+ expect_log_msg("TLS error with 127.hello: "
+ "(null) (in (null):(null):---)\n");
+ tor_free(tls->address);
+
+ mock_clean_saved_logs();
+ tls->address = tor_strdup("127.hello");
+ tor_tls_log_one_error(tls, 0, LOG_WARN, 0, "blarg");
+ expect_log_msg("TLS error while blarg with "
+ "127.hello: (null) (in (null):(null):---)\n");
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, 3), LOG_WARN, 0, NULL);
+ expect_log_msg("TLS error with 127.hello: "
+ "BN lib (in unknown library:(null):---)\n");
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_HTTP_REQUEST),
+ LOG_WARN, 0, NULL);
+ expect_log_severity(LOG_INFO);
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_HTTPS_PROXY_REQUEST),
+ LOG_WARN, 0, NULL);
+ expect_log_severity(LOG_INFO);
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_RECORD_LENGTH_MISMATCH),
+ LOG_WARN, 0, NULL);
+ expect_log_severity(LOG_INFO);
+
+#ifndef OPENSSL_1_1_API
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_RECORD_TOO_LARGE),
+ LOG_WARN, 0, NULL);
+ expect_log_severity(LOG_INFO);
+#endif /* !defined(OPENSSL_1_1_API) */
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_UNKNOWN_PROTOCOL),
+ LOG_WARN, 0, NULL);
+ expect_log_severity(LOG_INFO);
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_UNSUPPORTED_PROTOCOL),
+ LOG_WARN, 0, NULL);
+ expect_log_severity(LOG_INFO);
+
+ tls->ssl = SSL_new(ctx);
+
+ mock_clean_saved_logs();
+ tor_tls_log_one_error(tls, 0, LOG_WARN, 0, NULL);
+ expect_log_msg("TLS error with 127.hello: (null)"
+ " (in (null):(null):" SSL_STATE_STR ")\n");
+
+ done:
+ teardown_capture_of_logs();
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+ if (tls && tls->ssl)
+ SSL_free(tls->ssl);
+ if (tls)
+ tor_free(tls->address);
+ tor_free(tls);
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_get_error(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ int ret;
+ SSL_CTX *ctx;
+
+ library_init();
+
+ ctx = SSL_CTX_new(SSLv23_method());
+ setup_capture_of_logs(LOG_INFO);
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = SSL_new(ctx);
+ SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL);
+
+ ret = tor_tls_get_error(tls, 0, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_IO);
+ expect_log_msg("TLS error: unexpected close while"
+ " something (before/accept initialization)\n");
+
+ mock_clean_saved_logs();
+ ret = tor_tls_get_error(tls, 2, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ expect_no_log_entry();
+
+ mock_clean_saved_logs();
+ ret = tor_tls_get_error(tls, 0, 1, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, -11);
+ expect_no_log_entry();
+
+ mock_clean_saved_logs();
+ ERR_clear_error();
+ ERR_put_error(ERR_LIB_BN, 2, -1, "somewhere.c", 99);
+ ret = tor_tls_get_error(tls, 0, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+ expect_log_msg("TLS error while something: (null)"
+ " (in bignum routines:(null):before/accept initialization)\n");
+
+ mock_clean_saved_logs();
+ ERR_clear_error();
+ tls->ssl->rwstate = SSL_READING;
+ SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_READ;
+ ret = tor_tls_get_error(tls, -1, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, TOR_TLS_WANTREAD);
+ expect_no_log_entry();
+
+ mock_clean_saved_logs();
+ ERR_clear_error();
+ tls->ssl->rwstate = SSL_READING;
+ SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_WRITE;
+ ret = tor_tls_get_error(tls, -1, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, TOR_TLS_WANTWRITE);
+ expect_no_log_entry();
+
+ mock_clean_saved_logs();
+ ERR_clear_error();
+ tls->ssl->rwstate = 0;
+ tls->ssl->shutdown = SSL_RECEIVED_SHUTDOWN;
+ tls->ssl->s3->warn_alert =SSL_AD_CLOSE_NOTIFY;
+ ret = tor_tls_get_error(tls, 0, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, TOR_TLS_CLOSE);
+ expect_log_entry();
+
+ mock_clean_saved_logs();
+ ret = tor_tls_get_error(tls, 0, 2, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, -10);
+ expect_no_log_entry();
+
+ mock_clean_saved_logs();
+ ERR_put_error(ERR_LIB_SYS, 2, -1, "somewhere.c", 99);
+ ret = tor_tls_get_error(tls, -1, 0, "something", LOG_WARN, 0);
+ tt_int_op(ret, OP_EQ, -9);
+ expect_log_msg("TLS error while something: (null) (in system library:"
+ "connect:before/accept initialization)\n");
+
+ done:
+ teardown_capture_of_logs();
+ SSL_free(tls->ssl);
+ tor_free(tls);
+ SSL_CTX_free(ctx);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+static void
+test_tortls_always_accept_verify_cb(void *ignored)
+{
+ (void)ignored;
+ int ret;
+
+ ret = always_accept_verify_cb(0, NULL);
+ tt_int_op(ret, OP_EQ, 1);
+
+ done:
+ (void)0;
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_x509_cert_free(void *ignored)
+{
+ (void)ignored;
+ tor_x509_cert_t *cert;
+
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ tor_x509_cert_free(cert);
+
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ cert->cert = X509_new();
+ cert->encoded = tor_malloc_zero(1);
+ tor_x509_cert_free(cert);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+#ifndef OPENSSL_OPAQUE
+static int
+fixed_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
+{
+ (void) a; (void) b;
+ return 1;
+}
+
+/*
+ * Use only for the matching fake_x509_free() call
+ */
+static X509 *
+fake_x509_malloc(void)
+{
+ return tor_malloc_zero(sizeof(X509));
+}
+
+static void
+fake_x509_free(X509 *cert)
+{
+ if (cert) {
+ if (cert->cert_info) {
+ if (cert->cert_info->key) {
+ if (cert->cert_info->key->pkey) {
+ tor_free(cert->cert_info->key->pkey);
+ }
+ tor_free(cert->cert_info->key);
+ }
+ tor_free(cert->cert_info);
+ }
+ tor_free(cert);
+ }
+}
+
+static void
+test_tortls_cert_matches_key(void *ignored)
+{
+ (void)ignored;
+ int res;
+ tor_tls_t *tls;
+ tor_x509_cert_t *cert;
+ X509 *one = NULL, *two = NULL;
+ EVP_PKEY_ASN1_METHOD *meth = EVP_PKEY_asn1_new(999, 0, NULL, NULL);
+ EVP_PKEY_asn1_set_public(meth, NULL, NULL, fixed_pub_cmp, NULL, NULL, NULL);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ one = fake_x509_malloc();
+ one->references = 1;
+ two = fake_x509_malloc();
+ two->references = 1;
+
+ res = tor_tls_cert_matches_key(tls, cert);
+ tt_int_op(res, OP_EQ, 0);
+
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+ tls->ssl->session->peer = one;
+ res = tor_tls_cert_matches_key(tls, cert);
+ tt_int_op(res, OP_EQ, 0);
+
+ cert->cert = two;
+ res = tor_tls_cert_matches_key(tls, cert);
+ tt_int_op(res, OP_EQ, 0);
+
+ one->cert_info = tor_malloc_zero(sizeof(X509_CINF));
+ one->cert_info->key = tor_malloc_zero(sizeof(X509_PUBKEY));
+ one->cert_info->key->pkey = tor_malloc_zero(sizeof(EVP_PKEY));
+ one->cert_info->key->pkey->references = 1;
+ one->cert_info->key->pkey->ameth = meth;
+ one->cert_info->key->pkey->type = 1;
+
+ two->cert_info = tor_malloc_zero(sizeof(X509_CINF));
+ two->cert_info->key = tor_malloc_zero(sizeof(X509_PUBKEY));
+ two->cert_info->key->pkey = tor_malloc_zero(sizeof(EVP_PKEY));
+ two->cert_info->key->pkey->references = 1;
+ two->cert_info->key->pkey->ameth = meth;
+ two->cert_info->key->pkey->type = 2;
+
+ res = tor_tls_cert_matches_key(tls, cert);
+ tt_int_op(res, OP_EQ, 0);
+
+ one->cert_info->key->pkey->type = 1;
+ two->cert_info->key->pkey->type = 1;
+ res = tor_tls_cert_matches_key(tls, cert);
+ tt_int_op(res, OP_EQ, 1);
+
+ done:
+ EVP_PKEY_asn1_free(meth);
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ tor_free(cert);
+ fake_x509_free(one);
+ fake_x509_free(two);
+}
+
+static void
+test_tortls_cert_get_key(void *ignored)
+{
+ (void)ignored;
+ tor_x509_cert_t *cert = NULL;
+ crypto_pk_t *res = NULL;
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ X509 *key = NULL;
+ key = fake_x509_malloc();
+ key->references = 1;
+
+ res = tor_tls_cert_get_key(cert);
+ tt_assert(!res);
+
+ cert->cert = key;
+ key->cert_info = tor_malloc_zero(sizeof(X509_CINF));
+ key->cert_info->key = tor_malloc_zero(sizeof(X509_PUBKEY));
+ key->cert_info->key->pkey = tor_malloc_zero(sizeof(EVP_PKEY));
+ key->cert_info->key->pkey->references = 1;
+ key->cert_info->key->pkey->type = 2;
+ res = tor_tls_cert_get_key(cert);
+ tt_assert(!res);
+
+ done:
+ fake_x509_free(key);
+ tor_free(cert);
+ crypto_pk_free(res);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+static void
+test_tortls_get_my_client_auth_key(void *ignored)
+{
+ (void)ignored;
+ crypto_pk_t *ret;
+ crypto_pk_t *expected;
+ tor_tls_context_t *ctx;
+ RSA *k = RSA_new();
+
+ ctx = tor_malloc_zero(sizeof(tor_tls_context_t));
+ expected = crypto_new_pk_from_openssl_rsa_(k);
+ ctx->auth_key = expected;
+
+ client_tls_context = NULL;
+ ret = tor_tls_get_my_client_auth_key();
+ tt_assert(!ret);
+
+ client_tls_context = ctx;
+ ret = tor_tls_get_my_client_auth_key();
+ tt_assert(ret == expected);
+
+ done:
+ tor_free(expected);
+ tor_free(ctx);
+}
+
+#ifndef HAVE_SSL_GET_CLIENT_CIPHERS
+static SSL_CIPHER *
+get_cipher_by_name(const char *name)
+{
+ int i;
+ const SSL_METHOD *method = SSLv23_method();
+ int num = method->num_ciphers();
+
+ for (i = 0; i < num; ++i) {
+ const SSL_CIPHER *cipher = method->get_cipher(i);
+ const char *ciphername = SSL_CIPHER_get_name(cipher);
+ if (!strcmp(ciphername, name)) {
+ return (SSL_CIPHER *)cipher;
+ }
+ }
+
+ return NULL;
+}
+#endif /* !defined(HAVE_SSL_GET_CLIENT_CIPHERS) */
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_get_ciphersuite_name(void *ignored)
+{
+ (void)ignored;
+ const char *ret;
+ tor_tls_t *ctx;
+ ctx = tor_malloc_zero(sizeof(tor_tls_t));
+ ctx->ssl = tor_malloc_zero(sizeof(SSL));
+
+ ret = tor_tls_get_ciphersuite_name(ctx);
+ tt_str_op(ret, OP_EQ, "(NONE)");
+
+ done:
+ tor_free(ctx->ssl);
+ tor_free(ctx);
+}
+
+static SSL_CIPHER *
+get_cipher_by_id(uint16_t id)
+{
+ int i;
+ const SSL_METHOD *method = SSLv23_method();
+ int num = method->num_ciphers();
+ for (i = 0; i < num; ++i) {
+ const SSL_CIPHER *cipher = method->get_cipher(i);
+ if (id == (SSL_CIPHER_get_id(cipher) & 0xffff)) {
+ return (SSL_CIPHER *)cipher;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+test_tortls_classify_client_ciphers(void *ignored)
+{
+ (void)ignored;
+ int i;
+ int ret;
+ SSL_CTX *ctx;
+ SSL *ssl;
+ tor_tls_t *tls;
+ STACK_OF(SSL_CIPHER) *ciphers;
+ SSL_CIPHER *tmp_cipher;
+
+ library_init();
+
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->magic = TOR_TLS_MAGIC;
+
+ ctx = SSL_CTX_new(TLSv1_method());
+ ssl = SSL_new(ctx);
+ tls->ssl = ssl;
+
+ ciphers = sk_SSL_CIPHER_new_null();
+
+ ret = tor_tls_classify_client_ciphers(ssl, NULL);
+ tt_int_op(ret, OP_EQ, -1);
+
+ SSL_set_ex_data(ssl, tor_tls_object_ex_data_index, tls);
+ tls->client_cipher_list_type = 42;
+
+ ret = tor_tls_classify_client_ciphers(ssl, NULL);
+ tt_int_op(ret, OP_EQ, 42);
+
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 1);
+
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, SSL_get_ciphers(ssl));
+ tt_int_op(ret, OP_EQ, 3);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 3);
+
+ SSL_CIPHER *one = get_cipher_by_name(TLS1_TXT_DHE_RSA_WITH_AES_128_SHA),
+ *two = get_cipher_by_name(TLS1_TXT_DHE_RSA_WITH_AES_256_SHA),
+ *three = get_cipher_by_name(SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA),
+ *four = NULL;
+ sk_SSL_CIPHER_push(ciphers, one);
+ sk_SSL_CIPHER_push(ciphers, two);
+ sk_SSL_CIPHER_push(ciphers, three);
+ sk_SSL_CIPHER_push(ciphers, four);
+
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 1);
+
+ sk_SSL_CIPHER_zero(ciphers);
+
+ one = get_cipher_by_name("ECDHE-RSA-AES256-GCM-SHA384");
+ tt_assert(one);
+ one->id = 0x00ff;
+ two = get_cipher_by_name("ECDHE-RSA-AES128-GCM-SHA256");
+ tt_assert(two);
+ two->id = 0x0000;
+ sk_SSL_CIPHER_push(ciphers, one);
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 3);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 3);
+
+ sk_SSL_CIPHER_push(ciphers, two);
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 3);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 3);
+
+ one->id = 0xC00A;
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 3);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 3);
+
+ sk_SSL_CIPHER_zero(ciphers);
+ for (i=0; v2_cipher_list[i]; i++) {
+ tmp_cipher = get_cipher_by_id(v2_cipher_list[i]);
+ tt_assert(tmp_cipher);
+ sk_SSL_CIPHER_push(ciphers, tmp_cipher);
+ }
+ tls->client_cipher_list_type = 0;
+ ret = tor_tls_classify_client_ciphers(ssl, ciphers);
+ tt_int_op(ret, OP_EQ, 2);
+ tt_int_op(tls->client_cipher_list_type, OP_EQ, 2);
+
+ done:
+ sk_SSL_CIPHER_free(ciphers);
+ SSL_free(tls->ssl);
+ tor_free(tls);
+ SSL_CTX_free(ctx);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+static void
+test_tortls_client_is_using_v2_ciphers(void *ignored)
+{
+ (void)ignored;
+
+#ifdef HAVE_SSL_GET_CLIENT_CIPHERS
+ tt_skip();
+ done:
+ (void)1;
+#else
+ int ret;
+ SSL_CTX *ctx;
+ SSL *ssl;
+ SSL_SESSION *sess;
+ STACK_OF(SSL_CIPHER) *ciphers;
+
+ library_init();
+
+ ctx = SSL_CTX_new(TLSv1_method());
+ ssl = SSL_new(ctx);
+ sess = SSL_SESSION_new();
+
+ ret = tor_tls_client_is_using_v2_ciphers(ssl);
+ tt_int_op(ret, OP_EQ, -1);
+
+ ssl->session = sess;
+ ret = tor_tls_client_is_using_v2_ciphers(ssl);
+ tt_int_op(ret, OP_EQ, 0);
+
+ ciphers = sk_SSL_CIPHER_new_null();
+ SSL_CIPHER *one = get_cipher_by_name("ECDHE-RSA-AES256-GCM-SHA384");
+ tt_assert(one);
+ one->id = 0x00ff;
+ sk_SSL_CIPHER_push(ciphers, one);
+ sess->ciphers = ciphers;
+ ret = tor_tls_client_is_using_v2_ciphers(ssl);
+ tt_int_op(ret, OP_EQ, 1);
+ done:
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+#endif /* defined(HAVE_SSL_GET_CLIENT_CIPHERS) */
+}
+
+#ifndef OPENSSL_OPAQUE
+static X509 *fixed_try_to_extract_certs_from_tls_cert_out_result = NULL;
+static X509 *fixed_try_to_extract_certs_from_tls_id_cert_out_result = NULL;
+
+static void
+fixed_try_to_extract_certs_from_tls(int severity, tor_tls_t *tls,
+ tor_x509_cert_impl_t **cert_out,
+ tor_x509_cert_impl_t **id_cert_out)
+{
+ (void) severity;
+ (void) tls;
+ *cert_out = fixed_try_to_extract_certs_from_tls_cert_out_result;
+ *id_cert_out = fixed_try_to_extract_certs_from_tls_id_cert_out_result;
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+#ifndef OPENSSL_OPAQUE
+static const char* notCompletelyValidCertString =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICVjCCAb8CAg37MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG\n"
+ "A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE\n"
+ "MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl\n"
+ "YiBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBmcmFuazRkZC5jb20wHhcNMTIw\n"
+ "ODIyMDUyNzIzWhcNMTcwODIxMDUyNzIzWjBKMQswCQYDVQQGEwJKUDEOMAwGA1UE\n"
+ "CAwFVG9reW8xETAPBgNVBAoMCEZyYW5rNEREMRgwFgYDVQQDDA93d3cuZXhhbXBs\n"
+ "ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMYBBrx5PlP0WNI/ZdzD\n"
+ "+6Pktmurn+F2kQYbtc7XQh8/LTBvCo+P6iZoLEmUA9e7EXLRxgU1CVqeAi7QcAn9\n"
+ "MwBlc8ksFJHB0rtf9pmf8Oza9E0Bynlq/4/Kb1x+d+AyhL7oK9tQwB24uHOueHi1\n"
+ "C/iVv8CSWKiYe6hzN1txYe8rAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAASPdjigJ\n"
+ "kXCqKWpnZ/Oc75EUcMi6HztaW8abUMlYXPIgkV2F7YanHOB7K4f7OOLjiz8DTPFf\n"
+ "jC9UeuErhaA/zzWi8ewMTFZW/WshOrm3fNvcMrMLKtH534JKvcdMg6qIdjTFINIr\n"
+ "evnAhf0cwULaebn+lMs8Pdl7y37+sfluVok=\n"
+ "-----END CERTIFICATE-----\n";
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+static const char* validCertString = "-----BEGIN CERTIFICATE-----\n"
+ "MIIDpTCCAY0CAg3+MA0GCSqGSIb3DQEBBQUAMF4xCzAJBgNVBAYTAlVTMREwDwYD\n"
+ "VQQIDAhJbGxpbm9pczEQMA4GA1UEBwwHQ2hpY2FnbzEUMBIGA1UECgwLVG9yIFRl\n"
+ "c3RpbmcxFDASBgNVBAMMC1RvciBUZXN0aW5nMB4XDTE1MDkwNjEzMzk1OVoXDTQz\n"
+ "MDEyMjEzMzk1OVowVjELMAkGA1UEBhMCVVMxEDAOBgNVBAcMB0NoaWNhZ28xFDAS\n"
+ "BgNVBAoMC1RvciBUZXN0aW5nMR8wHQYDVQQDDBZ0ZXN0aW5nLnRvcnByb2plY3Qu\n"
+ "b3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDoT6uyVVhWyOF3wkHjjYbd\n"
+ "nKaykyRv4JVtKQdZ4OpEErmX1zw4MmyzpQNV6iR4bQnWiyLfzyVJMZDIC/WILBfX\n"
+ "w2Pza/yuLgUvDc3twMuhOACzOQVO8PrEF/aVv2+hbCCy2udXvKhnYn+CCXl3ozc8\n"
+ "XcKYvujTXDyvGWY3xwAjlQIDAQABMA0GCSqGSIb3DQEBBQUAA4ICAQCUvnhzQWuQ\n"
+ "MrN+pERkE+zcTI/9dGS90rUMMLgu8VDNqTa0TUQh8uO0EQ6uDvI8Js6e8tgwS0BR\n"
+ "UBahqb7ZHv+rejGCBr5OudqD+x4STiiuPNJVs86JTLN8SpM9CHjIBH5WCCN2KOy3\n"
+ "mevNoRcRRyYJzSFULCunIK6FGulszigMYGscrO4oiTkZiHPh9KvWT40IMiHfL+Lw\n"
+ "EtEWiLex6064LcA2YQ1AMuSZyCexks63lcfaFmQbkYOKqXa1oLkIRuDsOaSVjTfe\n"
+ "vec+X6jvf12cFTKS5WIeqkKF2Irt+dJoiHEGTe5RscUMN/f+gqHPzfFz5dR23sxo\n"
+ "g+HC6MZHlFkLAOx3wW6epPS8A/m1mw3zMPoTnb2U2YYt8T0dJMMlUn/7Y1sEAa+a\n"
+ "dSTMaeUf6VnJ//11m454EZl1to9Z7oJOgqmFffSrdD4BGIWe8f7hhW6L1Enmqe/J\n"
+ "BKL3wbzZh80O1W0bndAwhnEEhlzneFY84cbBo9pmVxpODHkUcStpr5Z7pBDrcL21\n"
+ "Ss/aB/1YrsVXhdvJdOGxl3Mnl9dUY57CympLGlT8f0pPS6GAKOelECOhFMHmJd8L\n"
+ "dj3XQSmKtYHevZ6IvuMXSlB/fJvSjSlkCuLo5+kJoaqPuRu+i/S1qxeRy3CBwmnE\n"
+ "LdSNdcX4N79GQJ996PA8+mUCQG7YRtK+WA==\n"
+ "-----END CERTIFICATE-----\n";
+
+static const char* caCertString = "-----BEGIN CERTIFICATE-----\n"
+ "MIIFjzCCA3egAwIBAgIJAKd5WgyfPMYRMA0GCSqGSIb3DQEBCwUAMF4xCzAJBgNV\n"
+ "BAYTAlVTMREwDwYDVQQIDAhJbGxpbm9pczEQMA4GA1UEBwwHQ2hpY2FnbzEUMBIG\n"
+ "A1UECgwLVG9yIFRlc3RpbmcxFDASBgNVBAMMC1RvciBUZXN0aW5nMB4XDTE1MDkw\n"
+ "NjEzMzc0MVoXDTQzMDEyMjEzMzc0MVowXjELMAkGA1UEBhMCVVMxETAPBgNVBAgM\n"
+ "CElsbGlub2lzMRAwDgYDVQQHDAdDaGljYWdvMRQwEgYDVQQKDAtUb3IgVGVzdGlu\n"
+ "ZzEUMBIGA1UEAwwLVG9yIFRlc3RpbmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw\n"
+ "ggIKAoICAQCpLMUEiLW5leUgBZoEJms2V7lZRhIAjnJBhVMHD0e3UubNknmaQoxf\n"
+ "ARz3rvqOaRd0JlV+qM9qE0DjiYcCVP1cAfqAo9d83uS1vwY3YMVJzADlaIiHfyVW\n"
+ "uEgBy0vvkeUBqaua24dYlcwsemOiXYLu41yM1wkcGHW1AhBNHppY6cznb8TyLgNM\n"
+ "2x3SGUdzc5XMyAFx51faKGBA3wjs+Hg1PLY7d30nmCgEOBavpm5I1disM/0k+Mcy\n"
+ "YmAKEo/iHJX/rQzO4b9znP69juLlR8PDBUJEVIG/CYb6+uw8MjjUyiWXYoqfVmN2\n"
+ "hm/lH8b6rXw1a2Aa3VTeD0DxaWeacMYHY/i01fd5n7hCoDTRNdSw5KJ0L3Z0SKTu\n"
+ "0lzffKzDaIfyZGlpW5qdouACkWYzsaitQOePVE01PIdO30vUfzNTFDfy42ccx3Di\n"
+ "59UCu+IXB+eMtrBfsok0Qc63vtF1linJgjHW1z/8ujk8F7/qkOfODhk4l7wngc2A\n"
+ "EmwWFIFoGaiTEZHB9qteXr4unbXZ0AHpM02uGGwZEGohjFyebEb73M+J57WKKAFb\n"
+ "PqbLcGUksL1SHNBNAJcVLttX55sO4nbidOS/kA3m+F1R04MBTyQF9qA6YDDHqdI3\n"
+ "h/3pw0Z4fxVouTYT4/NfRnX4JTP4u+7Mpcoof28VME0qWqD1LnRhFQIDAQABo1Aw\n"
+ "TjAdBgNVHQ4EFgQUMoAgIXH7pZ3QMRwTjT+DM9Yo/v0wHwYDVR0jBBgwFoAUMoAg\n"
+ "IXH7pZ3QMRwTjT+DM9Yo/v0wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC\n"
+ "AgEAUJxacjXR9sT+Xs6ISFiUsyd0T6WVKMnV46xrYJHirGfx+krWHrjxMY+ZtxYD\n"
+ "DBDGlo11Qc4v6QrclNf5QUBfIiGQsP9Cm6hHcQ+Tpg9HHCgSqG1YNPwCPReCR4br\n"
+ "BLvLfrfkcBL2IWM0PdQdCze+59DBfipsULD2mEn9fjYRXQEwb2QWtQ9qRc20Yb/x\n"
+ "Q4b/+CvUodLkaq7B8MHz0BV8HHcBoph6DYaRmO/N+hPauIuSp6XyaGYcEefGKVKj\n"
+ "G2+fcsdyXsoijNdL8vNKwm4j2gVwCBnw16J00yfFoV46YcbfqEdJB2je0XSvwXqt\n"
+ "14AOTngxso2h9k9HLtrfpO1ZG/B5AcCMs1lzbZ2fp5DPHtjvvmvA2RJqgo3yjw4W\n"
+ "4DHAuTglYFlC3mDHNfNtcGP20JvepcQNzNP2UzwcpOc94hfKikOFw+gf9Vf1qd0y\n"
+ "h/Sk6OZHn2+JVUPiWHIQV98Vtoh4RmUZDJD+b55ia3fQGTGzt4z1XFzQYSva5sfs\n"
+ "wocS/papthqWldQU7x+3wofNd5CNU1x6WKXG/yw30IT/4F8ADJD6GeygNT8QJYvt\n"
+ "u/8lAkbOy6B9xGmSvr0Kk1oq9P2NshA6kalxp1Oz/DTNDdL4AeBXV3JmM6WWCjGn\n"
+ "Yy1RT69d0rwYc5u/vnqODz1IjvT90smsrkBumGt791FAFeg=\n"
+ "-----END CERTIFICATE-----\n";
+
+static X509 *
+read_cert_from(const char *str)
+{
+ BIO *bio = BIO_new(BIO_s_mem());
+ BIO_write(bio, str, (int) strlen(str));
+ X509 *res = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ return res;
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_verify(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ crypto_pk_t *k = NULL;
+ X509 *cert1 = NULL, *cert2 = NULL, *invalidCert = NULL,
+ *validCert = NULL, *caCert = NULL;
+
+ cert1 = tor_malloc_zero(sizeof(X509));
+ cert1->references = 10;
+
+ cert2 = tor_malloc_zero(sizeof(X509));
+ cert2->references = 10;
+
+ validCert = read_cert_from(validCertString);
+ caCert = read_cert_from(caCertString);
+ invalidCert = read_cert_from(notCompletelyValidCertString);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ ret = tor_tls_verify(LOG_WARN, tls, &k);
+ tt_int_op(ret, OP_EQ, -1);
+
+ MOCK(try_to_extract_certs_from_tls, fixed_try_to_extract_certs_from_tls);
+
+ fixed_try_to_extract_certs_from_tls_cert_out_result = cert1;
+ ret = tor_tls_verify(LOG_WARN, tls, &k);
+ tt_int_op(ret, OP_EQ, -1);
+
+ fixed_try_to_extract_certs_from_tls_id_cert_out_result = cert2;
+ ret = tor_tls_verify(LOG_WARN, tls, &k);
+ tt_int_op(ret, OP_EQ, -1);
+
+ fixed_try_to_extract_certs_from_tls_cert_out_result = invalidCert;
+ fixed_try_to_extract_certs_from_tls_id_cert_out_result = invalidCert;
+
+ ret = tor_tls_verify(LOG_WARN, tls, &k);
+ tt_int_op(ret, OP_EQ, -1);
+
+ fixed_try_to_extract_certs_from_tls_cert_out_result = validCert;
+ fixed_try_to_extract_certs_from_tls_id_cert_out_result = caCert;
+
+ ret = tor_tls_verify(LOG_WARN, tls, &k);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(k);
+
+ done:
+ UNMOCK(try_to_extract_certs_from_tls);
+ tor_free(cert1);
+ tor_free(cert2);
+ tor_free(tls);
+ tor_free(k);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_check_lifetime(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ X509 *validCert = read_cert_from(validCertString);
+ time_t now = time(NULL);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+ tls->ssl->session->peer = validCert;
+ ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), 0, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ ASN1_STRING_free(validCert->cert_info->validity->notBefore);
+ validCert->cert_info->validity->notBefore = ASN1_TIME_set(NULL, now-10);
+ ASN1_STRING_free(validCert->cert_info->validity->notAfter);
+ validCert->cert_info->validity->notAfter = ASN1_TIME_set(NULL, now+60);
+
+ ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), 0, -1000);
+ tt_int_op(ret, OP_EQ, -1);
+
+ ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), -1000, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ done:
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ X509_free(validCert);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+#ifndef OPENSSL_OPAQUE
+static int fixed_ssl_pending_result = 0;
+
+static int
+fixed_ssl_pending(const SSL *ignored)
+{
+ (void)ignored;
+ return fixed_ssl_pending_result;
+}
+
+static void
+test_tortls_get_pending_bytes(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ SSL_METHOD *method;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ method = tor_malloc_zero(sizeof(SSL_METHOD));
+ method->ssl_pending = fixed_ssl_pending;
+ tls->ssl->method = method;
+
+ fixed_ssl_pending_result = 42;
+ ret = tor_tls_get_pending_bytes(tls);
+ tt_int_op(ret, OP_EQ, 42);
+
+ done:
+ tor_free(method);
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_SSL_SESSION_get_master_key(void *ignored)
+{
+ (void)ignored;
+ size_t ret;
+ tor_tls_t *tls;
+ uint8_t *out;
+ out = tor_malloc_zero(1);
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+ tls->ssl->session->master_key_length = 1;
+
+#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY
+ tls->ssl->session->master_key[0] = 43;
+ ret = SSL_SESSION_get_master_key(tls->ssl->session, out, 0);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_int_op(out[0], OP_EQ, 0);
+
+ ret = SSL_SESSION_get_master_key(tls->ssl->session, out, 1);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_int_op(out[0], OP_EQ, 43);
+
+ done:
+#endif /* !defined(HAVE_SSL_SESSION_GET_MASTER_KEY) */
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ tor_free(out);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_get_tlssecrets(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ uint8_t *secret_out = tor_malloc_zero(DIGEST256_LEN);
+ tor_tls_t *tls;
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+ tls->ssl->session->master_key_length = 1;
+ tls->ssl->s3 = tor_malloc_zero(sizeof(SSL3_STATE));
+
+ ret = tor_tls_get_tlssecrets(tls, secret_out);
+ tt_int_op(ret, OP_EQ, 0);
+
+ done:
+ tor_free(secret_out);
+ tor_free(tls->ssl->s3);
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_get_buffer_sizes(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ size_t rbuf_c=-1, rbuf_b=-1, wbuf_c=-1, wbuf_b=-1;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->s3 = tor_malloc_zero(sizeof(SSL3_STATE));
+
+ tls->ssl->s3->rbuf.buf = NULL;
+ tls->ssl->s3->rbuf.len = 1;
+ tls->ssl->s3->rbuf.offset = 0;
+ tls->ssl->s3->rbuf.left = 42;
+
+ tls->ssl->s3->wbuf.buf = NULL;
+ tls->ssl->s3->wbuf.len = 2;
+ tls->ssl->s3->wbuf.offset = 0;
+ tls->ssl->s3->wbuf.left = 43;
+
+ ret = tor_tls_get_buffer_sizes(tls, &rbuf_c, &rbuf_b, &wbuf_c, &wbuf_b);
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)
+ tt_int_op(ret, OP_EQ, -1);
+#else
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(rbuf_c, OP_EQ, 0);
+ tt_int_op(wbuf_c, OP_EQ, 0);
+ tt_int_op(rbuf_b, OP_EQ, 42);
+ tt_int_op(wbuf_b, OP_EQ, 43);
+
+ tls->ssl->s3->rbuf.buf = tor_malloc_zero(1);
+ tls->ssl->s3->wbuf.buf = tor_malloc_zero(1);
+ ret = tor_tls_get_buffer_sizes(tls, &rbuf_c, &rbuf_b, &wbuf_c, &wbuf_b);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(rbuf_c, OP_EQ, 1);
+ tt_int_op(wbuf_c, OP_EQ, 2);
+
+#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) */
+
+ done:
+ tor_free(tls->ssl->s3->rbuf.buf);
+ tor_free(tls->ssl->s3->wbuf.buf);
+ tor_free(tls->ssl->s3);
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+#ifndef OPENSSL_OPAQUE
+typedef struct cert_pkey_st_local
+{
+ X509 *x509;
+ EVP_PKEY *privatekey;
+ const EVP_MD *digest;
+} CERT_PKEY_local;
+
+typedef struct sess_cert_st_local
+{
+ STACK_OF(X509) *cert_chain;
+ int peer_cert_type;
+ CERT_PKEY_local *peer_key;
+ CERT_PKEY_local peer_pkeys[8];
+ int references;
+} SESS_CERT_local;
+
+static void
+test_tortls_try_to_extract_certs_from_tls(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ X509 *cert = NULL, *id_cert = NULL, *c1 = NULL, *c2 = NULL;
+ SESS_CERT_local *sess = NULL;
+
+ c1 = read_cert_from(validCertString);
+ c2 = read_cert_from(caCertString);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+ sess = tor_malloc_zero(sizeof(SESS_CERT_local));
+ tls->ssl->session->sess_cert = (void *)sess;
+
+ try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert);
+ tt_assert(!cert);
+ tt_assert(!id_cert);
+
+ tls->ssl->session->peer = c1;
+ try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert);
+ tt_assert(cert == c1);
+ tt_assert(!id_cert);
+ X509_free(cert); /* decrease refcnt */
+
+ sess->cert_chain = sk_X509_new_null();
+ try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert);
+ tt_assert(cert == c1);
+ tt_assert(!id_cert);
+ X509_free(cert); /* decrease refcnt */
+
+ sk_X509_push(sess->cert_chain, c1);
+ sk_X509_push(sess->cert_chain, c2);
+
+ try_to_extract_certs_from_tls(LOG_WARN, tls, &cert, &id_cert);
+ tt_assert(cert == c1);
+ tt_assert(id_cert);
+ X509_free(cert); /* decrease refcnt */
+
+ done:
+ sk_X509_free(sess->cert_chain);
+ tor_free(sess);
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ X509_free(c1);
+ X509_free(c2);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_get_peer_cert(void *ignored)
+{
+ (void)ignored;
+ tor_x509_cert_t *ret;
+ tor_tls_t *tls;
+ X509 *cert = NULL;
+
+ cert = read_cert_from(validCertString);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+
+ ret = tor_tls_get_peer_cert(tls);
+ tt_assert(!ret);
+
+ tls->ssl->session->peer = cert;
+ ret = tor_tls_get_peer_cert(tls);
+ tt_assert(ret);
+ tt_assert(ret->cert == cert);
+
+ done:
+ tor_x509_cert_free(ret);
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ X509_free(cert);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_peer_has_cert(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ X509 *cert = NULL;
+
+ cert = read_cert_from(validCertString);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION));
+
+ ret = tor_tls_peer_has_cert(tls);
+ tt_assert(!ret);
+
+ tls->ssl->session->peer = cert;
+ ret = tor_tls_peer_has_cert(tls);
+ tt_assert(ret);
+
+ done:
+ tor_free(tls->ssl->session);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ X509_free(cert);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+static void
+test_tortls_get_write_overhead_ratio(void *ignored)
+{
+ (void)ignored;
+ double ret;
+
+ total_bytes_written_over_tls = 0;
+ ret = tls_get_write_overhead_ratio();
+ tt_double_op(fabs(ret - 1.0), OP_LT, 1E-12);
+
+ total_bytes_written_by_tls = 10;
+ total_bytes_written_over_tls = 1;
+ ret = tls_get_write_overhead_ratio();
+ tt_double_op(fabs(ret - 10.0), OP_LT, 1E-12);
+
+ total_bytes_written_by_tls = 10;
+ total_bytes_written_over_tls = 2;
+ ret = tls_get_write_overhead_ratio();
+ tt_double_op(fabs(ret - 5.0), OP_LT, 1E-12);
+
+ done:
+ (void)0;
+}
+
+static void
+test_tortls_is_server(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ int ret;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->isServer = 1;
+ ret = tor_tls_is_server(tls);
+ tt_int_op(ret, OP_EQ, 1);
+
+ done:
+ tor_free(tls);
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_session_secret_cb(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ SSL_CTX *ctx;
+ STACK_OF(SSL_CIPHER) *ciphers = NULL;
+ SSL_CIPHER *one;
+
+ library_init();
+
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ tls->magic = TOR_TLS_MAGIC;
+
+ ctx = SSL_CTX_new(TLSv1_method());
+ tls->ssl = SSL_new(ctx);
+ SSL_set_ex_data(tls->ssl, tor_tls_object_ex_data_index, tls);
+
+ SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL);
+
+ tor_tls_session_secret_cb(tls->ssl, NULL, NULL, NULL, NULL, NULL);
+ tt_assert(!tls->ssl->tls_session_secret_cb);
+
+ one = get_cipher_by_name("ECDHE-RSA-AES256-GCM-SHA384");
+ one->id = 0x00ff;
+ ciphers = sk_SSL_CIPHER_new_null();
+ sk_SSL_CIPHER_push(ciphers, one);
+
+ tls->client_cipher_list_type = 0;
+ tor_tls_session_secret_cb(tls->ssl, NULL, NULL, ciphers, NULL, NULL);
+ tt_assert(!tls->ssl->tls_session_secret_cb);
+
+ done:
+ sk_SSL_CIPHER_free(ciphers);
+ SSL_free(tls->ssl);
+ SSL_CTX_free(ctx);
+ tor_free(tls);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+#ifndef OPENSSL_OPAQUE
+/* TODO: It seems block_renegotiation and unblock_renegotiation and
+ * using different blags. This might not be correct */
+static void
+test_tortls_block_renegotiation(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->s3 = tor_malloc_zero(sizeof(SSL3_STATE));
+#ifndef SUPPORT_UNSAFE_RENEGOTIATION_FLAG
+#define SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0
+#endif
+
+ tls->ssl->s3->flags = SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
+
+ tor_tls_block_renegotiation(tls);
+
+#ifndef OPENSSL_1_1_API
+ tt_assert(!(tls->ssl->s3->flags &
+ SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION));
+#endif
+
+ done:
+ tor_free(tls->ssl->s3);
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+
+static void
+test_tortls_unblock_renegotiation(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tor_tls_unblock_renegotiation(tls);
+
+ tt_uint_op(SSL_get_options(tls->ssl) &
+ SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION, OP_EQ,
+ SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
+
+ done:
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_assert_renegotiation_unblocked(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tor_tls_unblock_renegotiation(tls);
+ tor_tls_assert_renegotiation_unblocked(tls);
+ /* No assertion here - this test will fail if tor_assert is turned on
+ * and things are bad. */
+
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+static void
+test_tortls_set_logged_address(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+
+ tor_tls_set_logged_address(tls, "foo bar");
+
+ tt_str_op(tls->address, OP_EQ, "foo bar");
+
+ tor_tls_set_logged_address(tls, "foo bar 2");
+ tt_str_op(tls->address, OP_EQ, "foo bar 2");
+
+ done:
+ tor_free(tls->address);
+ tor_free(tls);
+}
+
+#ifndef OPENSSL_OPAQUE
+static void
+example_cb(tor_tls_t *t, void *arg)
+{
+ (void)t;
+ (void)arg;
+}
+
+static void
+test_tortls_set_renegotiate_callback(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ const char *arg = "hello";
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+
+ tor_tls_set_renegotiate_callback(tls, example_cb, (void*)arg);
+ tt_assert(tls->negotiated_callback == example_cb);
+ tt_assert(tls->callback_arg == arg);
+ tt_assert(!tls->got_renegotiate);
+
+ /* Assumes V2_HANDSHAKE_SERVER */
+ tt_assert(tls->ssl->info_callback == tor_tls_server_info_callback);
+
+ tor_tls_set_renegotiate_callback(tls, NULL, (void*)arg);
+ tt_assert(tls->ssl->info_callback == tor_tls_debug_state_callback);
+
+ done:
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+#ifndef OPENSSL_OPAQUE
+static SSL_CIPHER *fixed_cipher1 = NULL;
+static SSL_CIPHER *fixed_cipher2 = NULL;
+static const SSL_CIPHER *
+fake_get_cipher(unsigned ncipher)
+{
+
+ switch (ncipher) {
+ case 1:
+ return fixed_cipher1;
+ case 2:
+ return fixed_cipher2;
+ default:
+ return NULL;
+ }
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_find_cipher_by_id(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ SSL *ssl;
+ SSL_CTX *ctx;
+ const SSL_METHOD *m = TLSv1_method();
+ SSL_METHOD *empty_method = tor_malloc_zero(sizeof(SSL_METHOD));
+
+ fixed_cipher1 = tor_malloc_zero(sizeof(SSL_CIPHER));
+ fixed_cipher2 = tor_malloc_zero(sizeof(SSL_CIPHER));
+ fixed_cipher2->id = 0xC00A;
+
+ library_init();
+
+ ctx = SSL_CTX_new(m);
+ ssl = SSL_new(ctx);
+
+ ret = find_cipher_by_id(ssl, NULL, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ ret = find_cipher_by_id(ssl, m, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ ret = find_cipher_by_id(ssl, m, 0xFFFF);
+ tt_int_op(ret, OP_EQ, 0);
+
+ ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ ret = find_cipher_by_id(ssl, empty_method, 0xFFFF);
+#ifdef HAVE_SSL_CIPHER_FIND
+ tt_int_op(ret, OP_EQ, 0);
+#else
+ tt_int_op(ret, OP_EQ, 1);
+#endif
+
+ empty_method->get_cipher = fake_get_cipher;
+ ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ empty_method->get_cipher = m->get_cipher;
+ empty_method->num_ciphers = m->num_ciphers;
+ ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ empty_method->get_cipher = fake_get_cipher;
+ empty_method->num_ciphers = m->num_ciphers;
+ ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
+ tt_int_op(ret, OP_EQ, 1);
+
+ empty_method->num_ciphers = fake_num_ciphers;
+ ret = find_cipher_by_id(ssl, empty_method, 0xC00A);
+#ifdef HAVE_SSL_CIPHER_FIND
+ tt_int_op(ret, OP_EQ, 1);
+#else
+ tt_int_op(ret, OP_EQ, 0);
+#endif
+
+ done:
+ tor_free(empty_method);
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+ tor_free(fixed_cipher1);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_debug_state_callback(void *ignored)
+{
+ (void)ignored;
+ SSL *ssl;
+ char *buf = tor_malloc_zero(1000);
+ int n;
+
+ setup_capture_of_logs(LOG_DEBUG);
+
+ ssl = tor_malloc_zero(sizeof(SSL));
+
+ tor_tls_debug_state_callback(ssl, 32, 45);
+
+ n = tor_snprintf(buf, 1000, "SSL %p is now in state unknown"
+ " state [type=32,val=45].\n", ssl);
+ /* tor's snprintf returns -1 on error */
+ tt_int_op(n, OP_NE, -1);
+ expect_log_msg(buf);
+
+ done:
+ teardown_capture_of_logs();
+ tor_free(buf);
+ tor_free(ssl);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_server_info_callback(void *ignored)
+{
+ (void)ignored;
+ tor_tls_t *tls;
+ SSL_CTX *ctx;
+ SSL *ssl;
+
+ library_init();
+
+ ctx = SSL_CTX_new(TLSv1_method());
+ ssl = SSL_new(ctx);
+
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->magic = TOR_TLS_MAGIC;
+ tls->ssl = ssl;
+
+ setup_full_capture_of_logs(LOG_WARN);
+ SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_A);
+ mock_clean_saved_logs();
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ expect_single_log_msg("Couldn't look up the tls for an SSL*. How odd!\n");
+
+ SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_B);
+ mock_clean_saved_logs();
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ expect_single_log_msg("Couldn't look up the tls for an SSL*. How odd!\n");
+
+ SSL_set_state(ssl, 99);
+ mock_clean_saved_logs();
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ expect_no_log_entry();
+ teardown_capture_of_logs();
+
+ SSL_set_ex_data(tls->ssl, tor_tls_object_ex_data_index, tls);
+ SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_B);
+ tls->negotiated_callback = 0;
+ tls->server_handshake_count = 120;
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ tt_int_op(tls->server_handshake_count, OP_EQ, 121);
+
+ tls->server_handshake_count = 127;
+ tls->negotiated_callback = (void *)1;
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ tt_int_op(tls->server_handshake_count, OP_EQ, 127);
+ tt_int_op(tls->got_renegotiate, OP_EQ, 1);
+
+ tls->ssl->session = SSL_SESSION_new();
+ tls->wasV2Handshake = 0;
+ tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
+ tt_int_op(tls->wasV2Handshake, OP_EQ, 0);
+
+ done:
+ teardown_capture_of_logs();
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+ tor_free(tls);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+#ifndef OPENSSL_OPAQUE
+static int fixed_ssl_read_result_index;
+static int fixed_ssl_read_result[5];
+static int fixed_ssl_shutdown_result;
+
+static int
+fixed_ssl_read(SSL *s, void *buf, int len)
+{
+ (void)s;
+ (void)buf;
+ (void)len;
+ return fixed_ssl_read_result[fixed_ssl_read_result_index++];
+}
+
+static int
+fixed_ssl_shutdown(SSL *s)
+{
+ (void)s;
+ return fixed_ssl_shutdown_result;
+}
+
+#ifndef LIBRESSL_VERSION_NUMBER
+static int fixed_ssl_state_to_set;
+static tor_tls_t *fixed_tls;
+
+static int
+setting_version_ssl_shutdown(SSL *s)
+{
+ s->version = SSL2_VERSION;
+ return fixed_ssl_shutdown_result;
+}
+
+static int
+setting_version_and_state_ssl_shutdown(SSL *s)
+{
+ fixed_tls->state = fixed_ssl_state_to_set;
+ s->version = SSL2_VERSION;
+ return fixed_ssl_shutdown_result;
+}
+#endif /* !defined(LIBRESSL_VERSION_NUMBER) */
+
+static int
+dummy_handshake_func(SSL *s)
+{
+ (void)s;
+ return 1;
+}
+
+static void
+test_tortls_shutdown(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ SSL_METHOD *method = give_me_a_test_method();
+ setup_capture_of_logs(LOG_WARN);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->ssl->method = method;
+ method->ssl_read = fixed_ssl_read;
+ method->ssl_shutdown = fixed_ssl_shutdown;
+
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, -9);
+
+ tls->state = TOR_TLS_ST_SENTCLOSE;
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = -1;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, -9);
+
+#ifndef LIBRESSL_VERSION_NUMBER
+ tls->ssl->handshake_func = dummy_handshake_func;
+
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = 42;
+ fixed_ssl_read_result[2] = 0;
+ fixed_ssl_shutdown_result = 1;
+ ERR_clear_error();
+ tls->ssl->version = SSL2_VERSION;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_DONE);
+ tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED);
+
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = 42;
+ fixed_ssl_read_result[2] = 0;
+ fixed_ssl_shutdown_result = 0;
+ ERR_clear_error();
+ tls->ssl->version = 0;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_DONE);
+ tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED);
+
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = 42;
+ fixed_ssl_read_result[2] = 0;
+ fixed_ssl_shutdown_result = 0;
+ ERR_clear_error();
+ tls->ssl->version = 0;
+ method->ssl_shutdown = setting_version_ssl_shutdown;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = 42;
+ fixed_ssl_read_result[2] = 0;
+ fixed_ssl_shutdown_result = 0;
+ fixed_tls = tls;
+ fixed_ssl_state_to_set = TOR_TLS_ST_GOTCLOSE;
+ ERR_clear_error();
+ tls->ssl->version = 0;
+ method->ssl_shutdown = setting_version_and_state_ssl_shutdown;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 10;
+ fixed_ssl_read_result[1] = 42;
+ fixed_ssl_read_result[2] = 0;
+ fixed_ssl_read_result[3] = -1;
+ fixed_ssl_shutdown_result = 0;
+ fixed_tls = tls;
+ fixed_ssl_state_to_set = 0;
+ ERR_clear_error();
+ tls->ssl->version = 0;
+ method->ssl_shutdown = setting_version_and_state_ssl_shutdown;
+ ret = tor_tls_shutdown(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+#endif /* !defined(LIBRESSL_VERSION_NUMBER) */
+
+ done:
+ teardown_capture_of_logs();
+ tor_free(method);
+ tor_free(tls->ssl);
+ tor_free(tls);
+}
+
+static int negotiated_callback_called;
+
+static void
+negotiated_callback_setter(tor_tls_t *t, void *arg)
+{
+ (void)t;
+ (void)arg;
+ negotiated_callback_called++;
+}
+
+static void
+test_tortls_read(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ char buf[100];
+ SSL_METHOD *method = give_me_a_test_method();
+ setup_capture_of_logs(LOG_WARN);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->state = TOR_TLS_ST_OPEN;
+
+ ret = tor_tls_read(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, -9);
+
+ /* These tests assume that V2_HANDSHAKE_SERVER is set */
+ tls->ssl->handshake_func = dummy_handshake_func;
+ tls->ssl->method = method;
+ method->ssl_read = fixed_ssl_read;
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 42;
+ tls->state = TOR_TLS_ST_OPEN;
+ ERR_clear_error();
+ ret = tor_tls_read(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, 42);
+
+ tls->state = TOR_TLS_ST_OPEN;
+ tls->got_renegotiate = 1;
+ fixed_ssl_read_result_index = 0;
+ ERR_clear_error();
+ ret = tor_tls_read(tls, buf, 10);
+ tt_int_op(tls->got_renegotiate, OP_EQ, 0);
+
+ tls->state = TOR_TLS_ST_OPEN;
+ tls->got_renegotiate = 1;
+ negotiated_callback_called = 0;
+ tls->negotiated_callback = negotiated_callback_setter;
+ fixed_ssl_read_result_index = 0;
+ ERR_clear_error();
+ ret = tor_tls_read(tls, buf, 10);
+ tt_int_op(negotiated_callback_called, OP_EQ, 1);
+
+#ifndef LIBRESSL_VERSION_NUMBER
+ fixed_ssl_read_result_index = 0;
+ fixed_ssl_read_result[0] = 0;
+ tls->ssl->version = SSL2_VERSION;
+ ERR_clear_error();
+ ret = tor_tls_read(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, TOR_TLS_CLOSE);
+ tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED);
+#endif /* !defined(LIBRESSL_VERSION_NUMBER) */
+ // TODO: fill up
+
+ done:
+ teardown_capture_of_logs();
+ tor_free(tls->ssl);
+ tor_free(tls);
+ tor_free(method);
+}
+
+static int fixed_ssl_write_result;
+
+static int
+fixed_ssl_write(SSL *s, const void *buf, int len)
+{
+ (void)s;
+ (void)buf;
+ (void)len;
+ return fixed_ssl_write_result;
+}
+
+static void
+test_tortls_write(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ SSL_METHOD *method = give_me_a_test_method();
+ char buf[100];
+ setup_capture_of_logs(LOG_WARN);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = tor_malloc_zero(sizeof(SSL));
+ tls->state = TOR_TLS_ST_OPEN;
+
+ ret = tor_tls_write(tls, buf, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ ret = tor_tls_write(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, -9);
+
+ tls->ssl->method = method;
+ tls->wantwrite_n = 1;
+ ret = tor_tls_write(tls, buf, 10);
+ tt_int_op(tls->wantwrite_n, OP_EQ, 0);
+
+ method->ssl_write = fixed_ssl_write;
+ tls->ssl->handshake_func = dummy_handshake_func;
+ fixed_ssl_write_result = 1;
+ ERR_clear_error();
+ ret = tor_tls_write(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, 1);
+
+ fixed_ssl_write_result = -1;
+ ERR_clear_error();
+ tls->ssl->rwstate = SSL_READING;
+ SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL);
+ SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_READ;
+ ret = tor_tls_write(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, TOR_TLS_WANTREAD);
+
+ ERR_clear_error();
+ tls->ssl->rwstate = SSL_READING;
+ SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL);
+ SSL_get_rbio(tls->ssl)->flags = BIO_FLAGS_WRITE;
+ ret = tor_tls_write(tls, buf, 10);
+ tt_int_op(ret, OP_EQ, TOR_TLS_WANTWRITE);
+
+ done:
+ teardown_capture_of_logs();
+ BIO_free(tls->ssl->rbio);
+ tor_free(tls->ssl);
+ tor_free(tls);
+ tor_free(method);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+#ifndef OPENSSL_OPAQUE
+static int fixed_ssl_accept_result;
+static int fixed_ssl_connect_result;
+
+static int
+setting_error_ssl_accept(SSL *ssl)
+{
+ (void)ssl;
+ ERR_put_error(ERR_LIB_BN, 2, -1, "somewhere.c", 99);
+ ERR_put_error(ERR_LIB_SYS, 2, -1, "somewhere.c", 99);
+ return fixed_ssl_accept_result;
+}
+
+static int
+setting_error_ssl_connect(SSL *ssl)
+{
+ (void)ssl;
+ ERR_put_error(ERR_LIB_BN, 2, -1, "somewhere.c", 99);
+ ERR_put_error(ERR_LIB_SYS, 2, -1, "somewhere.c", 99);
+ return fixed_ssl_connect_result;
+}
+
+static int
+fixed_ssl_accept(SSL *ssl)
+{
+ (void) ssl;
+ return fixed_ssl_accept_result;
+}
+
+static void
+test_tortls_handshake(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ SSL_CTX *ctx;
+ SSL_METHOD *method = give_me_a_test_method();
+ setup_capture_of_logs(LOG_INFO);
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ ctx = SSL_CTX_new(TLSv1_method());
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = SSL_new(ctx);
+ tls->state = TOR_TLS_ST_HANDSHAKE;
+
+ ret = tor_tls_handshake(tls);
+ tt_int_op(ret, OP_EQ, -9);
+
+ tls->isServer = 1;
+ tls->state = TOR_TLS_ST_HANDSHAKE;
+ ret = tor_tls_handshake(tls);
+ tt_int_op(ret, OP_EQ, -9);
+
+ tls->ssl->method = method;
+ method->ssl_accept = fixed_ssl_accept;
+ fixed_ssl_accept_result = 2;
+ ERR_clear_error();
+ tls->state = TOR_TLS_ST_HANDSHAKE;
+ ret = tor_tls_handshake(tls);
+ tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_OPEN);
+
+ method->ssl_accept = setting_error_ssl_accept;
+ fixed_ssl_accept_result = 1;
+ ERR_clear_error();
+ mock_clean_saved_logs();
+ tls->state = TOR_TLS_ST_HANDSHAKE;
+ ret = tor_tls_handshake(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+ expect_log_entry();
+ /* This fails on jessie. Investigate why! */
+#if 0
+ expect_log_msg("TLS error while handshaking: (null) (in bignum routines:"
+ "(null):SSLv3 write client hello B)\n");
+ expect_log_msg("TLS error while handshaking: (null) (in system library:"
+ "connect:SSLv3 write client hello B)\n");
+#endif /* 0 */
+ expect_log_severity(LOG_INFO);
+
+ tls->isServer = 0;
+ method->ssl_connect = setting_error_ssl_connect;
+ fixed_ssl_connect_result = 1;
+ ERR_clear_error();
+ mock_clean_saved_logs();
+ tls->state = TOR_TLS_ST_HANDSHAKE;
+ ret = tor_tls_handshake(tls);
+ tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
+ expect_log_entry();
+#if 0
+ /* See above */
+ expect_log_msg("TLS error while handshaking: "
+ "(null) (in bignum routines:(null):SSLv3 write client hello B)\n");
+ expect_log_msg("TLS error while handshaking: "
+ "(null) (in system library:connect:SSLv3 write client hello B)\n");
+#endif /* 0 */
+ expect_log_severity(LOG_WARN);
+
+ done:
+ teardown_capture_of_logs();
+ SSL_free(tls->ssl);
+ SSL_CTX_free(ctx);
+ tor_free(tls);
+ tor_free(method);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+#ifndef OPENSSL_OPAQUE
+static void
+test_tortls_finish_handshake(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_t *tls;
+ SSL_CTX *ctx;
+ SSL_METHOD *method = give_me_a_test_method();
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ X509 *c1 = read_cert_from(validCertString);
+ SESS_CERT_local *sess = NULL;
+
+ ctx = SSL_CTX_new(method);
+
+ tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->ssl = SSL_new(ctx);
+ tls->state = TOR_TLS_ST_OPEN;
+
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+
+ tls->isServer = 1;
+ tls->wasV2Handshake = 0;
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tls->wasV2Handshake, OP_EQ, 1);
+ expect_single_log_msg_containing("For some reason, wasV2Handshake didn't "
+ "get set.");
+ teardown_capture_of_logs();
+
+ tls->wasV2Handshake = 1;
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tls->wasV2Handshake, OP_EQ, 1);
+
+ tls->wasV2Handshake = 1;
+ tls->ssl->session = SSL_SESSION_new();
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tls->wasV2Handshake, OP_EQ, 0);
+
+ tls->isServer = 0;
+
+ sess = tor_malloc_zero(sizeof(SESS_CERT_local));
+ tls->ssl->session->sess_cert = (void *)sess;
+ sess->cert_chain = sk_X509_new_null();
+ sk_X509_push(sess->cert_chain, c1);
+ tls->ssl->session->peer = c1;
+ tls->wasV2Handshake = 0;
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(tls->wasV2Handshake, OP_EQ, 1);
+
+ method->num_ciphers = fake_num_ciphers;
+ ret = tor_tls_finish_handshake(tls);
+ tt_int_op(ret, OP_EQ, -9);
+
+ done:
+ if (sess)
+ sk_X509_free(sess->cert_chain);
+ if (tls->ssl && tls->ssl->session) {
+ tor_free(tls->ssl->session->sess_cert);
+ }
+ SSL_free(tls->ssl);
+ tor_free(tls);
+ SSL_CTX_free(ctx);
+ tor_free(method);
+ teardown_capture_of_logs();
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+static int fixed_crypto_pk_new_result_index;
+static crypto_pk_t *fixed_crypto_pk_new_result[5];
+
+static crypto_pk_t *
+fixed_crypto_pk_new(void)
+{
+ return fixed_crypto_pk_new_result[fixed_crypto_pk_new_result_index++];
+}
+
+#ifndef OPENSSL_OPAQUE
+static int fixed_crypto_pk_generate_key_with_bits_result_index;
+static int fixed_crypto_pk_generate_key_with_bits_result[5];
+static int fixed_tor_tls_create_certificate_result_index;
+static X509 *fixed_tor_tls_create_certificate_result[5];
+static int fixed_tor_x509_cert_new_result_index;
+static tor_x509_cert_t *fixed_tor_x509_cert_new_result[5];
+
+static int
+fixed_crypto_pk_generate_key_with_bits(crypto_pk_t *env, int bits)
+{
+ (void)env;
+ (void)bits;
+ return fixed_crypto_pk_generate_key_with_bits_result[
+ fixed_crypto_pk_generate_key_with_bits_result_index++];
+}
+
+static X509 *
+fixed_tor_tls_create_certificate(crypto_pk_t *rsa,
+ crypto_pk_t *rsa_sign,
+ const char *cname,
+ const char *cname_sign,
+ unsigned int cert_lifetime)
+{
+ (void)rsa;
+ (void)rsa_sign;
+ (void)cname;
+ (void)cname_sign;
+ (void)cert_lifetime;
+ return fixed_tor_tls_create_certificate_result[
+ fixed_tor_tls_create_certificate_result_index++];
+}
+
+static tor_x509_cert_t *
+fixed_tor_x509_cert_new(tor_x509_cert_impl_t *x509_cert)
+{
+ (void) x509_cert;
+ return fixed_tor_x509_cert_new_result[
+ fixed_tor_x509_cert_new_result_index++];
+}
+
+static void
+test_tortls_context_new(void *ignored)
+{
+ (void)ignored;
+ tor_tls_context_t *ret;
+ crypto_pk_t *pk1, *pk2, *pk3, *pk4, *pk5, *pk6, *pk7, *pk8, *pk9, *pk10,
+ *pk11, *pk12, *pk13, *pk14, *pk15, *pk16, *pk17, *pk18;
+
+ pk1 = crypto_pk_new();
+ pk2 = crypto_pk_new();
+ pk3 = crypto_pk_new();
+ pk4 = crypto_pk_new();
+ pk5 = crypto_pk_new();
+ pk6 = crypto_pk_new();
+ pk7 = crypto_pk_new();
+ pk8 = crypto_pk_new();
+ pk9 = crypto_pk_new();
+ pk10 = crypto_pk_new();
+ pk11 = crypto_pk_new();
+ pk12 = crypto_pk_new();
+ pk13 = crypto_pk_new();
+ pk14 = crypto_pk_new();
+ pk15 = crypto_pk_new();
+ pk16 = crypto_pk_new();
+ pk17 = crypto_pk_new();
+ pk18 = crypto_pk_new();
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = NULL;
+ MOCK(crypto_pk_new, fixed_crypto_pk_new);
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ /* note: we already override this in testing_common.c, so we
+ * run this unit test in a subprocess. */
+ MOCK(crypto_pk_generate_key_with_bits,
+ fixed_crypto_pk_generate_key_with_bits);
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk1;
+ fixed_crypto_pk_new_result[1] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result[0] = -1;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk2;
+ fixed_crypto_pk_new_result[1] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result[0] = 0;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk3;
+ fixed_crypto_pk_new_result[1] = pk4;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result[0] = 0;
+ fixed_crypto_pk_generate_key_with_bits_result[1] = -1;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ MOCK(tor_tls_create_certificate, fixed_tor_tls_create_certificate);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk5;
+ fixed_crypto_pk_new_result[1] = pk6;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_crypto_pk_generate_key_with_bits_result[1] = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = NULL;
+ fixed_tor_tls_create_certificate_result[1] = X509_new();
+ fixed_tor_tls_create_certificate_result[2] = X509_new();
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk7;
+ fixed_crypto_pk_new_result[1] = pk8;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = X509_new();
+ fixed_tor_tls_create_certificate_result[1] = NULL;
+ fixed_tor_tls_create_certificate_result[2] = X509_new();
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk9;
+ fixed_crypto_pk_new_result[1] = pk10;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = X509_new();
+ fixed_tor_tls_create_certificate_result[1] = X509_new();
+ fixed_tor_tls_create_certificate_result[2] = NULL;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ MOCK(tor_x509_cert_new, fixed_tor_x509_cert_new);
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk11;
+ fixed_crypto_pk_new_result[1] = pk12;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = X509_new();
+ fixed_tor_tls_create_certificate_result[1] = X509_new();
+ fixed_tor_tls_create_certificate_result[2] = X509_new();
+ fixed_tor_x509_cert_new_result_index = 0;
+ fixed_tor_x509_cert_new_result[0] = NULL;
+ fixed_tor_x509_cert_new_result[1] = NULL;
+ fixed_tor_x509_cert_new_result[2] = NULL;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk13;
+ fixed_crypto_pk_new_result[1] = pk14;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = X509_new();
+ fixed_tor_tls_create_certificate_result[1] = X509_new();
+ fixed_tor_tls_create_certificate_result[2] = X509_new();
+ fixed_tor_x509_cert_new_result_index = 0;
+ fixed_tor_x509_cert_new_result[0] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ fixed_tor_x509_cert_new_result[1] = NULL;
+ fixed_tor_x509_cert_new_result[2] = NULL;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk15;
+ fixed_crypto_pk_new_result[1] = pk16;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = X509_new();
+ fixed_tor_tls_create_certificate_result[1] = X509_new();
+ fixed_tor_tls_create_certificate_result[2] = X509_new();
+ fixed_tor_x509_cert_new_result_index = 0;
+ fixed_tor_x509_cert_new_result[0] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ fixed_tor_x509_cert_new_result[1] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ fixed_tor_x509_cert_new_result[2] = NULL;
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = pk17;
+ fixed_crypto_pk_new_result[1] = pk18;
+ fixed_crypto_pk_new_result[2] = NULL;
+ fixed_crypto_pk_generate_key_with_bits_result_index = 0;
+ fixed_tor_tls_create_certificate_result_index = 0;
+ fixed_tor_tls_create_certificate_result[0] = X509_new();
+ fixed_tor_tls_create_certificate_result[1] = X509_new();
+ fixed_tor_tls_create_certificate_result[2] = X509_new();
+ fixed_tor_x509_cert_new_result_index = 0;
+ fixed_tor_x509_cert_new_result[0] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ fixed_tor_x509_cert_new_result[1] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ fixed_tor_x509_cert_new_result[2] = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ ret = tor_tls_context_new(NULL, 0, 0, 0);
+ tt_assert(!ret);
+
+ done:
+ UNMOCK(tor_x509_cert_new);
+ UNMOCK(tor_tls_create_certificate);
+ UNMOCK(crypto_pk_generate_key_with_bits);
+ UNMOCK(crypto_pk_new);
+}
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+static int fixed_crypto_pk_get_evp_pkey_result_index = 0;
+static EVP_PKEY *fixed_crypto_pk_get_evp_pkey_result[5];
+
+static EVP_PKEY *
+fixed_crypto_pk_get_evp_pkey_(crypto_pk_t *env, int private)
+{
+ (void) env;
+ (void) private;
+ return fixed_crypto_pk_get_evp_pkey_result[
+ fixed_crypto_pk_get_evp_pkey_result_index++];
+}
+
+static void
+test_tortls_create_certificate(void *ignored)
+{
+ (void)ignored;
+ X509 *ret;
+ crypto_pk_t *pk1, *pk2;
+
+ pk1 = crypto_pk_new();
+ pk2 = crypto_pk_new();
+
+ MOCK(crypto_pk_get_openssl_evp_pkey_, fixed_crypto_pk_get_evp_pkey_);
+ fixed_crypto_pk_get_evp_pkey_result_index = 0;
+ fixed_crypto_pk_get_evp_pkey_result[0] = NULL;
+ ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_get_evp_pkey_result_index = 0;
+ fixed_crypto_pk_get_evp_pkey_result[0] = EVP_PKEY_new();
+ fixed_crypto_pk_get_evp_pkey_result[1] = NULL;
+ ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1);
+ tt_assert(!ret);
+
+ fixed_crypto_pk_get_evp_pkey_result_index = 0;
+ fixed_crypto_pk_get_evp_pkey_result[0] = EVP_PKEY_new();
+ fixed_crypto_pk_get_evp_pkey_result[1] = EVP_PKEY_new();
+ ret = tor_tls_create_certificate(pk1, pk2, "hello", "hello2", 1);
+ tt_assert(!ret);
+
+ done:
+ UNMOCK(crypto_pk_get_openssl_evp_pkey_);
+ crypto_pk_free(pk1);
+ crypto_pk_free(pk2);
+}
+
+static void
+test_tortls_cert_new(void *ignored)
+{
+ (void)ignored;
+ tor_x509_cert_t *ret;
+ X509 *cert = read_cert_from(validCertString);
+
+ ret = tor_x509_cert_new(NULL);
+ tt_assert(!ret);
+
+ ret = tor_x509_cert_new(cert);
+ tt_assert(ret);
+ tor_x509_cert_free(ret);
+ ret = NULL;
+
+#if 0
+ cert = read_cert_from(validCertString);
+ /* XXX this doesn't do what you think: it alters a copy of the pubkey. */
+ X509_get_pubkey(cert)->type = EVP_PKEY_DSA;
+ ret = tor_x509_cert_new(cert);
+ tt_assert(ret);
+#endif /* 0 */
+
+#ifndef OPENSSL_OPAQUE
+ cert = read_cert_from(validCertString);
+ X509_CINF_free(cert->cert_info);
+ cert->cert_info = NULL;
+ ret = tor_x509_cert_new(cert);
+ tt_assert(ret);
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+ done:
+ tor_x509_cert_free(ret);
+}
+
+static void
+test_tortls_cert_is_valid(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_x509_cert_t *cert = NULL, *scert = NULL;
+
+ scert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0);
+ tt_int_op(ret, OP_EQ, 0);
+ tor_free(scert);
+ tor_free(cert);
+
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0);
+ tt_int_op(ret, OP_EQ, 1);
+
+#ifndef OPENSSL_OPAQUE
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ ASN1_TIME_free(cert->cert->cert_info->validity->notAfter);
+ cert->cert->cert_info->validity->notAfter =
+ ASN1_TIME_set(NULL, time(NULL)-1000000);
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ X509_PUBKEY_free(cert->cert->cert_info->key);
+ cert->cert->cert_info->key = NULL;
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 1);
+ tt_int_op(ret, OP_EQ, 0);
+#endif /* !defined(OPENSSL_OPAQUE) */
+
+#if 0
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ /* This doesn't actually change the key in the cert. XXXXXX */
+ BN_one(EVP_PKEY_get1_RSA(X509_get_pubkey(cert->cert))->n);
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 1);
+ tt_int_op(ret, OP_EQ, 0);
+
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ /* This doesn't actually change the key in the cert. XXXXXX */
+ X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC;
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 1);
+ tt_int_op(ret, OP_EQ, 0);
+
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ /* This doesn't actually change the key in the cert. XXXXXX */
+ X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC;
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0);
+ tt_int_op(ret, OP_EQ, 1);
+
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+ cert = tor_x509_cert_new(read_cert_from(validCertString));
+ scert = tor_x509_cert_new(read_cert_from(caCertString));
+ /* This doesn't actually change the key in the cert. XXXXXX */
+ X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC;
+ X509_get_pubkey(cert->cert)->ameth = NULL;
+ ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0);
+ tt_int_op(ret, OP_EQ, 0);
+#endif /* 0 */
+
+ done:
+ tor_x509_cert_free(cert);
+ tor_x509_cert_free(scert);
+}
+
+static void
+test_tortls_context_init_one(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ tor_tls_context_t *old = NULL;
+
+ MOCK(crypto_pk_new, fixed_crypto_pk_new);
+
+ fixed_crypto_pk_new_result_index = 0;
+ fixed_crypto_pk_new_result[0] = NULL;
+ ret = tor_tls_context_init_one(&old, NULL, 0, 0, 0);
+ tt_int_op(ret, OP_EQ, -1);
+
+ done:
+ UNMOCK(crypto_pk_new);
+}
+
+#define LOCAL_TEST_CASE(name, flags) \
+ { #name, test_tortls_##name, (flags|TT_FORK), NULL, NULL }
+
+#ifdef OPENSSL_OPAQUE
+#define INTRUSIVE_TEST_CASE(name, flags) \
+ { #name, NULL, TT_SKIP, NULL, NULL }
+#else
+#define INTRUSIVE_TEST_CASE(name, flags) LOCAL_TEST_CASE(name, flags)
+#endif /* defined(OPENSSL_OPAQUE) */
+
+struct testcase_t tortls_openssl_tests[] = {
+ LOCAL_TEST_CASE(tor_tls_new, TT_FORK),
+ LOCAL_TEST_CASE(get_state_description, TT_FORK),
+ LOCAL_TEST_CASE(get_by_ssl, TT_FORK),
+ LOCAL_TEST_CASE(allocate_tor_tls_object_ex_data_index, TT_FORK),
+ LOCAL_TEST_CASE(log_one_error, TT_FORK),
+ INTRUSIVE_TEST_CASE(get_error, TT_FORK),
+ LOCAL_TEST_CASE(always_accept_verify_cb, 0),
+ INTRUSIVE_TEST_CASE(x509_cert_free, 0),
+ INTRUSIVE_TEST_CASE(cert_matches_key, 0),
+ INTRUSIVE_TEST_CASE(cert_get_key, 0),
+ LOCAL_TEST_CASE(get_my_client_auth_key, TT_FORK),
+ INTRUSIVE_TEST_CASE(get_ciphersuite_name, 0),
+ INTRUSIVE_TEST_CASE(classify_client_ciphers, 0),
+ LOCAL_TEST_CASE(client_is_using_v2_ciphers, 0),
+ INTRUSIVE_TEST_CASE(verify, 0),
+ INTRUSIVE_TEST_CASE(check_lifetime, 0),
+ INTRUSIVE_TEST_CASE(get_pending_bytes, 0),
+ INTRUSIVE_TEST_CASE(SSL_SESSION_get_master_key, 0),
+ INTRUSIVE_TEST_CASE(get_tlssecrets, 0),
+ INTRUSIVE_TEST_CASE(get_buffer_sizes, 0),
+ INTRUSIVE_TEST_CASE(try_to_extract_certs_from_tls, 0),
+ INTRUSIVE_TEST_CASE(get_peer_cert, 0),
+ INTRUSIVE_TEST_CASE(peer_has_cert, 0),
+ INTRUSIVE_TEST_CASE(shutdown, 0),
+ INTRUSIVE_TEST_CASE(finish_handshake, 0),
+ INTRUSIVE_TEST_CASE(handshake, 0),
+ INTRUSIVE_TEST_CASE(write, 0),
+ INTRUSIVE_TEST_CASE(read, 0),
+ INTRUSIVE_TEST_CASE(server_info_callback, 0),
+ LOCAL_TEST_CASE(get_write_overhead_ratio, TT_FORK),
+ LOCAL_TEST_CASE(is_server, 0),
+ INTRUSIVE_TEST_CASE(assert_renegotiation_unblocked, 0),
+ INTRUSIVE_TEST_CASE(block_renegotiation, 0),
+ INTRUSIVE_TEST_CASE(unblock_renegotiation, 0),
+ INTRUSIVE_TEST_CASE(set_renegotiate_callback, 0),
+ LOCAL_TEST_CASE(set_logged_address, 0),
+ INTRUSIVE_TEST_CASE(find_cipher_by_id, 0),
+ INTRUSIVE_TEST_CASE(session_secret_cb, 0),
+ INTRUSIVE_TEST_CASE(debug_state_callback, 0),
+ INTRUSIVE_TEST_CASE(context_new, TT_FORK /* redundant */),
+ LOCAL_TEST_CASE(create_certificate, 0),
+ LOCAL_TEST_CASE(cert_new, 0),
+ LOCAL_TEST_CASE(cert_is_valid, 0),
+ LOCAL_TEST_CASE(context_init_one, 0),
+ END_OF_TESTCASES
+};
1
0
commit aa45511250ed9509ca06b76497f66835796a7998
Author: Nick Mathewson <nickm(a)torproject.org>
Date: Thu Jul 19 12:03:01 2018 -0400
Implement RSA for NSS.
---
src/lib/crypt_ops/crypto_rsa.h | 16 +-
src/lib/crypt_ops/crypto_rsa_nss.c | 699 +++++++++++++++++++++++++++++++++
src/lib/crypt_ops/crypto_rsa_openssl.c | 2 +-
src/lib/crypt_ops/include.am | 7 +-
4 files changed, 712 insertions(+), 12 deletions(-)
diff --git a/src/lib/crypt_ops/crypto_rsa.h b/src/lib/crypt_ops/crypto_rsa.h
index bcb2b51dd..afc6c4201 100644
--- a/src/lib/crypt_ops/crypto_rsa.h
+++ b/src/lib/crypt_ops/crypto_rsa.h
@@ -110,14 +110,6 @@ int crypto_pk_get_common_digests(crypto_pk_t *pk,
int crypto_pk_base64_encode_private(const crypto_pk_t *pk, char **priv_out);
crypto_pk_t *crypto_pk_base64_decode_private(const char *str, size_t len);
-#ifdef TOR_UNIT_TESTS
-#ifdef ENABLE_NSS
-struct SECItemStr;
-STATIC int secitem_uint_cmp(const struct SECItemStr *a,
- const struct SECItemStr *b);
-#endif
-#endif
-
#ifdef ENABLE_OPENSSL
/* Prototypes for private functions only used by tortls.c, crypto.c, and the
* unit tests. */
@@ -132,4 +124,12 @@ MOCK_DECL(struct evp_pkey_st *, crypto_pk_get_openssl_evp_pkey_,(
void crypto_pk_assign_public(crypto_pk_t *dest, const crypto_pk_t *src);
void crypto_pk_assign_private(crypto_pk_t *dest, const crypto_pk_t *src);
+#ifdef TOR_UNIT_TESTS
+#ifdef ENABLE_NSS
+struct SECItemStr;
+STATIC int secitem_uint_cmp(const struct SECItemStr *a,
+ const struct SECItemStr *b);
+#endif
+#endif
+
#endif
diff --git a/src/lib/crypt_ops/crypto_rsa_nss.c b/src/lib/crypt_ops/crypto_rsa_nss.c
new file mode 100644
index 000000000..0411687b9
--- /dev/null
+++ b/src/lib/crypt_ops/crypto_rsa_nss.c
@@ -0,0 +1,699 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file crypto_rsa.c
+ * \brief NSS implementations of our RSA code.
+ **/
+
+#include "lib/crypt_ops/crypto_rsa.h"
+
+#include "lib/crypt_ops/crypto_nss_mgt.h"
+#include "lib/crypt_ops/crypto_util.h"
+#include "lib/ctime/di_ops.h"
+#include "lib/encoding/binascii.h"
+#include "lib/fs/files.h"
+#include "lib/intmath/cmp.h"
+#include "lib/intmath/muldiv.h"
+#include "lib/log/log.h"
+#include "lib/log/util_bug.h"
+
+#include <string.h>
+
+#include <keyhi.h>
+#include <pk11pub.h>
+#include <secder.h>
+
+#ifdef ENABLE_OPENSSL
+#include <openssl/rsa.h>
+#include <openssl/evp.h>
+#endif
+
+/** Declaration for crypto_pk_t structure. */
+struct crypto_pk_t
+{
+ SECKEYPrivateKey *seckey;
+ SECKEYPublicKey *pubkey;
+};
+
+/** Return true iff <b>key</b> contains the private-key portion of the RSA
+ * key. */
+int
+crypto_pk_key_is_private(const crypto_pk_t *key)
+{
+ return key && key->seckey;
+}
+
+#ifdef ENABLE_OPENSSL
+/** used by tortls.c: wrap an RSA* in a crypto_pk_t. Take ownership of the
+ * RSA object. */
+crypto_pk_t *
+crypto_new_pk_from_openssl_rsa_(RSA *rsa)
+{
+ crypto_pk_t *pk = NULL;
+ unsigned char *buf = NULL;
+ int len = i2d_RSAPublicKey(rsa, &buf);
+ RSA_free(rsa);
+
+ if (len < 0 || buf == NULL)
+ goto end;
+
+ pk = crypto_pk_asn1_decode((const char *)buf, len);
+
+ end:
+ if (buf)
+ OPENSSL_free(buf);
+ return pk;
+}
+
+/** Helper, used by tor-gencert.c. Return the RSA from a
+ * crypto_pk_t. */
+struct rsa_st *
+crypto_pk_get_openssl_rsa_(crypto_pk_t *pk)
+{
+ size_t buflen = crypto_pk_keysize(pk)*16;
+ unsigned char *buf = tor_malloc_zero(buflen);
+ const unsigned char *cp = buf;
+ RSA *rsa = NULL;
+
+ int used = crypto_pk_asn1_encode_private(pk, (char*)buf, buflen);
+ if (used < 0)
+ goto end;
+ rsa = d2i_RSAPrivateKey(NULL, &cp, used);
+
+ end:
+ memwipe(buf, 0, buflen);
+ tor_free(buf);
+ return rsa;
+}
+
+/** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_t. Iff
+ * private is set, include the private-key portion of the key. Return a valid
+ * pointer on success, and NULL on failure. */
+MOCK_IMPL(struct evp_pkey_st *,
+crypto_pk_get_openssl_evp_pkey_,(crypto_pk_t *pk, int private))
+{
+ size_t buflen = crypto_pk_keysize(pk)*16;
+ unsigned char *buf = tor_malloc_zero(buflen);
+ const unsigned char *cp = buf;
+ RSA *rsa = NULL;
+ EVP_PKEY *result = NULL;
+
+ if (private) {
+ int len = crypto_pk_asn1_encode_private(pk, (char*)buf, buflen);
+ if (len < 0)
+ goto end;
+ rsa = d2i_RSAPrivateKey(NULL, &cp, len);
+ } else {
+ int len = crypto_pk_asn1_encode(pk, (char*)buf, buflen);
+ if (len < 0)
+ goto end;
+ rsa = d2i_RSAPublicKey(NULL, &cp, len);
+ }
+ if (!rsa)
+ goto end;
+
+ if (!(result = EVP_PKEY_new()))
+ goto end;
+ if (!(EVP_PKEY_assign_RSA(result, rsa))) {
+ EVP_PKEY_free(result);
+ RSA_free(rsa);
+ result = NULL;
+ }
+
+ end:
+ memwipe(buf, 0, buflen);
+ tor_free(buf);
+ return result;
+}
+#endif
+
+/** Allocate and return storage for a public key. The key itself will not yet
+ * be set.
+ */
+MOCK_IMPL(crypto_pk_t *,
+crypto_pk_new,(void))
+{
+ crypto_pk_t *result = tor_malloc_zero(sizeof(crypto_pk_t));
+ return result;
+}
+
+/** Release the NSS objects held in <b>key</b> */
+static void
+crypto_pk_clear(crypto_pk_t *key)
+{
+ if (key->pubkey)
+ SECKEY_DestroyPublicKey(key->pubkey);
+ if (key->seckey)
+ SECKEY_DestroyPrivateKey(key->seckey);
+ memset(key, 0, sizeof(crypto_pk_t));
+}
+
+/** Release a reference to an asymmetric key; when all the references
+ * are released, free the key.
+ */
+void
+crypto_pk_free_(crypto_pk_t *key)
+{
+ if (!key)
+ return;
+
+ crypto_pk_clear(key);
+
+ tor_free(key);
+}
+
+/** Generate a <b>bits</b>-bit new public/private keypair in <b>env</b>.
+ * Return 0 on success, -1 on failure.
+ */
+MOCK_IMPL(int,
+crypto_pk_generate_key_with_bits,(crypto_pk_t *key, int bits))
+{
+ tor_assert(key);
+
+ PK11RSAGenParams params = {
+ .keySizeInBits = bits,
+ .pe = TOR_RSA_EXPONENT
+ };
+
+ int result = -1;
+ PK11SlotInfo *slot = PK11_GetBestSlot(CKM_RSA_PKCS_KEY_PAIR_GEN, NULL);
+ SECKEYPrivateKey *seckey = NULL;
+ SECKEYPublicKey *pubkey = NULL;
+
+ if (!slot) {
+ crypto_nss_log_errors(LOG_WARN, "getting slot for RSA keygen");
+ goto done;
+ }
+
+ seckey = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, ¶ms,
+ &pubkey,
+ PR_FALSE /*isPerm */,
+ PR_FALSE /*isSensitive*/,
+ NULL);
+ if (seckey == NULL || pubkey == NULL) {
+ crypto_nss_log_errors(LOG_WARN, "generating an RSA key");
+ goto done;
+ }
+
+ crypto_pk_clear(key);
+ key->seckey = seckey;
+ key->pubkey = pubkey;
+ seckey = NULL;
+ pubkey = NULL;
+
+ result = 0;
+ done:
+ if (slot)
+ PK11_FreeSlot(slot);
+ if (pubkey)
+ SECKEY_DestroyPublicKey(pubkey);
+ if (seckey)
+ SECKEY_DestroyPrivateKey(seckey);
+
+ return result;
+}
+
+/** Return true iff <b>env</b> has a valid key.
+ */
+int
+crypto_pk_check_key(crypto_pk_t *key)
+{
+ return key && key->pubkey;
+}
+
+/** Return true iff <b>env</b> contains a public key whose public exponent
+ * equals 65537.
+ */
+int
+crypto_pk_public_exponent_ok(crypto_pk_t *key)
+{
+ return key &&
+ key->pubkey &&
+ key->pubkey->keyType == rsaKey &&
+ DER_GetUInteger(&key->pubkey->u.rsa.publicExponent) == TOR_RSA_EXPONENT;
+}
+
+/** Compare two big-endian integers stored in a and b; return a tristate.
+ */
+STATIC int
+secitem_uint_cmp(const SECItem *a, const SECItem *b)
+{
+ const unsigned abits = SECKEY_BigIntegerBitLength(a);
+ const unsigned bbits = SECKEY_BigIntegerBitLength(b);
+
+ if (abits < bbits)
+ return -1;
+ else if (abits > bbits)
+ return 1;
+
+ /* okay, they have the same number of bits set. Get a pair of aligned
+ * pointers to their bytes that are set... */
+ const unsigned nbytes = CEIL_DIV(abits, 8);
+ tor_assert(nbytes <= a->len);
+ tor_assert(nbytes <= b->len);
+
+ const unsigned char *aptr = a->data + (a->len - nbytes);
+ const unsigned char *bptr = b->data + (b->len - nbytes);
+
+ /* And compare them. */
+ return fast_memcmp(aptr, bptr, nbytes);
+}
+
+/** Compare the public-key components of a and b. Return less than 0
+ * if a\<b, 0 if a==b, and greater than 0 if a\>b. A NULL key is
+ * considered to be less than all non-NULL keys, and equal to itself.
+ *
+ * Note that this may leak information about the keys through timing.
+ */
+int
+crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b)
+{
+ int result;
+ char a_is_non_null = (a != NULL) && (a->pubkey != NULL);
+ char b_is_non_null = (b != NULL) && (b->pubkey != NULL);
+ char an_argument_is_null = !a_is_non_null | !b_is_non_null;
+
+ result = tor_memcmp(&a_is_non_null, &b_is_non_null, sizeof(a_is_non_null));
+ if (an_argument_is_null)
+ return result;
+
+ // This is all Tor uses with this structure.
+ tor_assert(a->pubkey->keyType == rsaKey);
+ tor_assert(b->pubkey->keyType == rsaKey);
+
+ const SECItem *a_n, *a_e, *b_n, *b_e;
+ a_n = &a->pubkey->u.rsa.modulus;
+ b_n = &b->pubkey->u.rsa.modulus;
+ a_e = &a->pubkey->u.rsa.publicExponent;
+ b_e = &b->pubkey->u.rsa.publicExponent;
+
+ result = secitem_uint_cmp(a_n, b_n);
+ if (result)
+ return result;
+ return secitem_uint_cmp(a_e, b_e);
+}
+
+/** Return the size of the public key modulus in <b>env</b>, in bytes. */
+size_t
+crypto_pk_keysize(const crypto_pk_t *key)
+{
+ tor_assert(key);
+ tor_assert(key->pubkey);
+ return SECKEY_PublicKeyStrength(key->pubkey);
+}
+
+/** Return the size of the public key modulus of <b>env</b>, in bits. */
+int
+crypto_pk_num_bits(crypto_pk_t *key)
+{
+ tor_assert(key);
+ tor_assert(key->pubkey);
+ return SECKEY_PublicKeyStrengthInBits(key->pubkey);
+}
+
+/**
+ * Make a copy of <b>key</b> and return it.
+ */
+crypto_pk_t *
+crypto_pk_dup_key(crypto_pk_t *key)
+{
+ crypto_pk_t *result = crypto_pk_new();
+ if (key->pubkey)
+ result->pubkey = SECKEY_CopyPublicKey(key->pubkey);
+ if (key->seckey)
+ result->seckey = SECKEY_CopyPrivateKey(key->seckey);
+ return result;
+}
+
+/** For testing: replace dest with src. (Dest must have a refcount
+ * of 1) */
+void
+crypto_pk_assign_public(crypto_pk_t *dest, const crypto_pk_t *src)
+{
+ crypto_pk_clear(dest);
+ if (src->pubkey)
+ dest->pubkey = SECKEY_CopyPublicKey(src->pubkey);
+}
+
+/** For testing: replace dest with src. (Dest must have a refcount
+ * of 1) */
+void
+crypto_pk_assign_private(crypto_pk_t *dest, const crypto_pk_t *src)
+{
+ crypto_pk_clear(dest);
+ if (src->pubkey)
+ dest->pubkey = SECKEY_CopyPublicKey(src->pubkey);
+ if (src->seckey)
+ dest->seckey = SECKEY_CopyPrivateKey(src->seckey);
+}
+
+/** Make a real honest-to-goodness copy of <b>env</b>, and return it.
+ * Returns NULL on failure. */
+crypto_pk_t *
+crypto_pk_copy_full(crypto_pk_t *key)
+{
+ // These aren't reference-counted is nss, so it's fine to just
+ // use the same function.
+ return crypto_pk_dup_key(key);
+}
+
+static const CK_RSA_PKCS_OAEP_PARAMS oaep_params = {
+ .hashAlg = CKM_SHA_1,
+ .mgf = CKG_MGF1_SHA1,
+ .source = CKZ_DATA_SPECIFIED,
+ .pSourceData = NULL,
+ .ulSourceDataLen = 0
+};
+static const SECItem oaep_item = {
+ .type = siBuffer,
+ .data = (unsigned char *) &oaep_params,
+ .len = sizeof(oaep_params)
+};
+
+/** Return the mechanism code and parameters for a given padding method when
+ * used with RSA */
+static CK_MECHANISM_TYPE
+padding_to_mechanism(int padding, SECItem **item_out)
+{
+ switch (padding) {
+ case PK_PKCS1_OAEP_PADDING:
+ *item_out = (SECItem *)&oaep_item;
+ return CKM_RSA_PKCS_OAEP;
+ default:
+ tor_assert_unreached();
+ *item_out = NULL;
+ return CKM_INVALID_MECHANISM;
+ }
+}
+
+/** Encrypt <b>fromlen</b> bytes from <b>from</b> with the public key
+ * in <b>env</b>, using the padding method <b>padding</b>. On success,
+ * write the result to <b>to</b>, and return the number of bytes
+ * written. On failure, return -1.
+ *
+ * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
+ * at least the length of the modulus of <b>env</b>.
+ */
+int
+crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen,
+ const char *from, size_t fromlen, int padding)
+{
+ tor_assert(env);
+ tor_assert(to);
+ tor_assert(from);
+ tor_assert(tolen < INT_MAX);
+ tor_assert(fromlen < INT_MAX);
+
+ if (BUG(!crypto_pk_check_key(env)))
+ return -1;
+
+ unsigned int result_len = 0;
+ SECItem *item = NULL;
+ CK_MECHANISM_TYPE m = padding_to_mechanism(padding, &item);
+
+ SECStatus s = PK11_PubEncrypt(env->pubkey, m, item,
+ (unsigned char *)to, &result_len,
+ (unsigned int)tolen,
+ (const unsigned char *)from,
+ (unsigned int)fromlen,
+ NULL);
+ if (s != SECSuccess) {
+ crypto_nss_log_errors(LOG_WARN, "encrypting to an RSA key");
+ return -1;
+ }
+
+ return (int)result_len;
+}
+
+/** Decrypt <b>fromlen</b> bytes from <b>from</b> with the private key
+ * in <b>env</b>, using the padding method <b>padding</b>. On success,
+ * write the result to <b>to</b>, and return the number of bytes
+ * written. On failure, return -1.
+ *
+ * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
+ * at least the length of the modulus of <b>key</b>.
+ */
+int
+crypto_pk_private_decrypt(crypto_pk_t *key, char *to,
+ size_t tolen,
+ const char *from, size_t fromlen,
+ int padding, int warnOnFailure)
+{
+ tor_assert(key);
+ tor_assert(to);
+ tor_assert(from);
+ tor_assert(tolen < INT_MAX);
+ tor_assert(fromlen < INT_MAX);
+
+ if (!crypto_pk_key_is_private(key))
+ return -1; /* Not a private key. */
+
+ unsigned int result_len = 0;
+ SECItem *item = NULL;
+ CK_MECHANISM_TYPE m = padding_to_mechanism(padding, &item);
+ SECStatus s = PK11_PrivDecrypt(key->seckey, m, item,
+ (unsigned char *)to, &result_len,
+ (unsigned int)tolen,
+ (const unsigned char *)from,
+ (unsigned int)fromlen);
+
+ if (s != SECSuccess) {
+ const int severity = warnOnFailure ? LOG_WARN : LOG_INFO;
+ crypto_nss_log_errors(severity, "decrypting with an RSA key");
+ return -1;
+ }
+
+ return (int)result_len;
+}
+
+/** Check the signature in <b>from</b> (<b>fromlen</b> bytes long) with the
+ * public key in <b>key</b>, using PKCS1 padding. On success, write the
+ * signed data to <b>to</b>, and return the number of bytes written.
+ * On failure, return -1.
+ *
+ * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
+ * at least the length of the modulus of <b>key</b>.
+ */
+MOCK_IMPL(int,
+crypto_pk_public_checksig,(const crypto_pk_t *key, char *to,
+ size_t tolen,
+ const char *from, size_t fromlen))
+{
+ tor_assert(key);
+ tor_assert(to);
+ tor_assert(from);
+ tor_assert(tolen < INT_MAX);
+ tor_assert(fromlen < INT_MAX);
+ tor_assert(key->pubkey);
+
+ SECItem sig = {
+ .type = siBuffer,
+ .data = (unsigned char *) from,
+ .len = (unsigned int) fromlen,
+ };
+ SECItem dsig = {
+ .type = siBuffer,
+ .data = (unsigned char *) to,
+ .len = (unsigned int) tolen
+ };
+ SECStatus s;
+ s = PK11_VerifyRecover(key->pubkey, &sig, &dsig, NULL);
+ if (s != SECSuccess)
+ return -1;
+
+ return (int)dsig.len;
+}
+
+/** Sign <b>fromlen</b> bytes of data from <b>from</b> with the private key in
+ * <b>env</b>, using PKCS1 padding. On success, write the signature to
+ * <b>to</b>, and return the number of bytes written. On failure, return
+ * -1.
+ *
+ * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
+ * at least the length of the modulus of <b>env</b>.
+ */
+int
+crypto_pk_private_sign(const crypto_pk_t *key, char *to, size_t tolen,
+ const char *from, size_t fromlen)
+{
+ tor_assert(key);
+ tor_assert(to);
+ tor_assert(from);
+ tor_assert(tolen < INT_MAX);
+ tor_assert(fromlen < INT_MAX);
+
+ if (BUG(!crypto_pk_key_is_private(key)))
+ return -1;
+
+ SECItem sig = {
+ .type = siBuffer,
+ .data = (unsigned char *)to,
+ .len = (unsigned int) tolen
+ };
+ SECItem hash = {
+ .type = siBuffer,
+ .data = (unsigned char *)from,
+ .len = (unsigned int) fromlen
+ };
+ CK_MECHANISM_TYPE m = CKM_RSA_PKCS;
+ SECStatus s = PK11_SignWithMechanism(key->seckey, m, NULL,
+ &sig, &hash);
+
+ if (s != SECSuccess) {
+ crypto_nss_log_errors(LOG_WARN, "signing with an RSA key");
+ return -1;
+ }
+
+ return (int)sig.len;
+}
+
+/* "This has lead to people trading hard-to-find object identifiers and ASN.1
+ * definitions like baseball cards" - Peter Gutmann, "X.509 Style Guide". */
+static const unsigned char RSA_OID[] = {
+ /* RSADSI */ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ /* PKCS1 */ 0x01, 0x01,
+ /* RSA */ 0x01
+};
+
+/** ASN.1-encode the public portion of <b>pk</b> into <b>dest</b>.
+ * Return -1 on error, or the number of characters used on success.
+ */
+int
+crypto_pk_asn1_encode(const crypto_pk_t *pk, char *dest, size_t dest_len)
+{
+ tor_assert(pk);
+ if (pk->pubkey == NULL)
+ return -1;
+
+ CERTSubjectPublicKeyInfo *info;
+ info = SECKEY_CreateSubjectPublicKeyInfo(pk->pubkey);
+ if (! info)
+ return -1;
+
+ const SECItem *item = &info->subjectPublicKey;
+ size_t actual_len = (item->len) >> 3; /* bits to bytes */
+ size_t n_used = MIN(actual_len, dest_len);
+ memcpy(dest, item->data, n_used);
+
+ SECKEY_DestroySubjectPublicKeyInfo(info);
+ return (int) n_used;
+}
+
+/** Decode an ASN.1-encoded public key from <b>str</b>; return the result on
+ * success and NULL on failure.
+ */
+crypto_pk_t *
+crypto_pk_asn1_decode(const char *str, size_t len)
+{
+ tor_assert(str);
+ if (len >= INT_MAX)
+ return NULL;
+ CERTSubjectPublicKeyInfo info = {
+ .algorithm = {
+ .algorithm = {
+ .type = siDEROID,
+ .data = (unsigned char *)RSA_OID,
+ .len = sizeof(RSA_OID)
+ }
+ },
+ .subjectPublicKey = {
+ .type = siBuffer,
+ .data = (unsigned char *)str,
+ .len = (unsigned int)(len << 3) /* bytes to bits */
+ }
+ };
+
+ SECKEYPublicKey *pub = SECKEY_ExtractPublicKey(&info);
+ if (pub == NULL)
+ return NULL;
+
+ crypto_pk_t *result = crypto_pk_new();
+ result->pubkey = pub;
+ return result;
+}
+
+DISABLE_GCC_WARNING(unused-parameter)
+
+/** Given a crypto_pk_t <b>pk</b>, allocate a new buffer containing the Base64
+ * encoding of the DER representation of the private key into the
+ * <b>dest_len</b>-byte buffer in <b>dest</b>.
+ * Return the number of bytes written on success, -1 on failure.
+ */
+int
+crypto_pk_asn1_encode_private(const crypto_pk_t *pk,
+ char *dest, size_t destlen)
+{
+ tor_assert(destlen <= INT_MAX);
+ if (!crypto_pk_key_is_private(pk))
+ return -1;
+
+ SECKEYPrivateKeyInfo *info = PK11_ExportPrivKeyInfo(pk->seckey, NULL);
+ if (!info)
+ return -1;
+ SECItem *item = &info->privateKey;
+
+ if (destlen < item->len) {
+ SECKEY_DestroyPrivateKeyInfo(info, PR_TRUE);
+ return -1;
+ }
+ int result = (int)item->len;
+ memcpy(dest, item->data, item->len);
+ SECKEY_DestroyPrivateKeyInfo(info, PR_TRUE);
+
+ return result;
+}
+
+/** Given a buffer containing the DER representation of the
+ * private key <b>str</b>, decode and return the result on success, or NULL
+ * on failure.
+ */
+crypto_pk_t *
+crypto_pk_asn1_decode_private(const char *str, size_t len)
+{
+ tor_assert(str);
+ tor_assert(len < INT_MAX);
+
+ SECKEYPrivateKeyInfo info = {
+ .algorithm = {
+ .algorithm = {
+ .type = siBuffer,
+ .data = (unsigned char *)RSA_OID,
+ .len = sizeof(RSA_OID)
+ }
+ },
+ .privateKey = {
+ .type = siBuffer,
+ .data = (unsigned char *)str,
+ .len = (int)len,
+ }
+ };
+
+ PK11SlotInfo *slot = PK11_GetBestSlot(CKM_RSA_PKCS, NULL);
+ SECStatus s;
+ SECKEYPrivateKey *seckey = NULL;
+
+ s = PK11_ImportPrivateKeyInfoAndReturnKey(slot, &info,
+ NULL /* nickname */,
+ NULL /* publicValue */,
+ PR_FALSE /* isPerm */,
+ PR_FALSE /* isPrivate */,
+ KU_ALL /* keyUsage */,
+ &seckey, NULL);
+
+ crypto_pk_t *output = NULL;
+
+ if (s == SECSuccess && seckey) {
+ output = crypto_pk_new();
+ output->seckey = seckey;
+ output->pubkey = SECKEY_ConvertToPublicKey(seckey);
+ tor_assert(output->pubkey);
+ } else {
+ crypto_nss_log_errors(LOG_WARN, "decoding an RSA private key");
+ }
+
+ return output;
+}
diff --git a/src/lib/crypt_ops/crypto_rsa_openssl.c b/src/lib/crypt_ops/crypto_rsa_openssl.c
index 9a3729a87..7ff71f6dd 100644
--- a/src/lib/crypt_ops/crypto_rsa_openssl.c
+++ b/src/lib/crypt_ops/crypto_rsa_openssl.c
@@ -76,7 +76,7 @@ crypto_new_pk_from_openssl_rsa_(RSA *rsa)
RSA *
crypto_pk_get_openssl_rsa_(crypto_pk_t *env)
{
- return RSA_PrivateKeyDup(env->key);
+ return RSAPrivateKey_dup(env->key);
}
/** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_t. Iff
diff --git a/src/lib/crypt_ops/include.am b/src/lib/crypt_ops/include.am
index 69bd7c2db..195dac6bd 100644
--- a/src/lib/crypt_ops/include.am
+++ b/src/lib/crypt_ops/include.am
@@ -19,7 +19,6 @@ src_lib_libtor_crypt_ops_a_SOURCES = \
src/lib/crypt_ops/crypto_pwbox.c \
src/lib/crypt_ops/crypto_rand.c \
src/lib/crypt_ops/crypto_rsa.c \
- src/lib/crypt_ops/crypto_rsa_openssl.c \
src/lib/crypt_ops/crypto_s2k.c \
src/lib/crypt_ops/crypto_util.c \
src/lib/crypt_ops/digestset.c
@@ -28,10 +27,12 @@ if USE_NSS
src_lib_libtor_crypt_ops_a_SOURCES += \
src/lib/crypt_ops/aes_nss.c \
src/lib/crypt_ops/crypto_dh_nss.c \
- src/lib/crypt_ops/crypto_nss_mgt.c
+ src/lib/crypt_ops/crypto_nss_mgt.c \
+ src/lib/crypt_ops/crypto_rsa_nss.c
else
src_lib_libtor_crypt_ops_a_SOURCES += \
- src/lib/crypt_ops/aes_openssl.c
+ src/lib/crypt_ops/aes_openssl.c \
+ src/lib/crypt_ops/crypto_rsa_openssl.c
endif
if USE_OPENSSL
1
0
commit 96f8e1980204e83bb943fbff31e308a03b41160c
Author: Nick Mathewson <nickm(a)torproject.org>
Date: Sun Aug 12 17:46:53 2018 -0400
Implement PBKDF2 with NSS.
This was a gap that we left in the last commit.
---
src/lib/crypt_ops/crypto_s2k.c | 47 +++++++++++++++++++++++++++++++++++++++---
1 file changed, 44 insertions(+), 3 deletions(-)
diff --git a/src/lib/crypt_ops/crypto_s2k.c b/src/lib/crypt_ops/crypto_s2k.c
index 433fbb026..e0b2f40bb 100644
--- a/src/lib/crypt_ops/crypto_s2k.c
+++ b/src/lib/crypt_ops/crypto_s2k.c
@@ -20,10 +20,14 @@
#include "lib/crypt_ops/crypto_util.h"
#include "lib/ctime/di_ops.h"
#include "lib/log/util_bug.h"
+#include "lib/intmath/cmp.h"
#ifdef ENABLE_OPENSSL
#include <openssl/evp.h>
#endif
+#ifdef ENABLE_NSS
+#include <pk11pub.h>
+#endif
#if defined(HAVE_LIBSCRYPT_H) && defined(HAVE_LIBSCRYPT_SCRYPT)
#define HAVE_SCRYPT
@@ -267,13 +271,13 @@ secret_to_key_compute_key(uint8_t *key_out, size_t key_out_len,
return (int)key_out_len;
case S2K_TYPE_PBKDF2: {
-#ifdef ENABLE_OPENSSL
uint8_t log_iters;
if (spec_len < 1 || secret_len > INT_MAX || spec_len > INT_MAX)
return S2K_BAD_LEN;
log_iters = spec[spec_len-1];
if (log_iters > 31)
return S2K_BAD_PARAMS;
+#ifdef ENABLE_OPENSSL
rv = PKCS5_PBKDF2_HMAC_SHA1(secret, (int)secret_len,
spec, (int)spec_len-1,
(1<<log_iters),
@@ -282,8 +286,45 @@ secret_to_key_compute_key(uint8_t *key_out, size_t key_out_len,
return S2K_FAILED;
return (int)key_out_len;
#else
- // XXXXXXXXXXXXXXXXXXXXXXXX implement me.
- return S2K_NO_SCRYPT_SUPPORT;
+ SECItem passItem = { .type = siBuffer,
+ .data = (unsigned char *) secret,
+ .len = (int)secret_len };
+ SECItem saltItem = { .type = siBuffer,
+ .data = (unsigned char *) spec,
+ .len = (int)spec_len - 1 };
+ SECAlgorithmID *alg = NULL;
+ PK11SymKey *key = NULL;
+
+ rv = S2K_FAILED;
+ alg = PK11_CreatePBEV2AlgorithmID(
+ SEC_OID_PKCS5_PBKDF2, SEC_OID_HMAC_SHA1, SEC_OID_HMAC_SHA1,
+ (int)key_out_len, (1<<log_iters), &saltItem);
+ if (alg == NULL)
+ return S2K_FAILED;
+
+ key = PK11_PBEKeyGen(NULL /* slot */,
+ alg,
+ &passItem,
+ false,
+ NULL);
+
+ SECStatus st = PK11_ExtractKeyValue(key);
+ if (st != SECSuccess)
+ goto nss_pbkdf_err;
+
+ const SECItem *iptr = PK11_GetKeyData(key);
+ if (iptr == NULL)
+ goto nss_pbkdf_err;
+
+ rv = MIN((int)iptr->len, (int)key_out_len);
+ memcpy(key_out, iptr->data, rv);
+
+ nss_pbkdf_err:
+ if (key)
+ PK11_FreeSymKey(key);
+ if (alg)
+ SECOID_DestroyAlgorithmID(alg, PR_TRUE);
+ return rv;
#endif
}
1
0
commit 6a88d8f6b413efdac4b0176cfb78431be46ca9e0
Author: Nick Mathewson <nickm(a)torproject.org>
Date: Sun Aug 12 17:18:41 2018 -0400
When enabling NSS, disable OpenSSL.
We used to link both libraries at once, but now that I'm working on
TLS, there's nothing left to keep OpenSSL around for when NSS is
enabled.
Note that this patch causes a couple of places that still assumed
OpenSSL to be disabled when NSS is enabled
- tor-gencert
- pbkdf2
---
configure.ac | 14 +++++++++---
src/app/config/config.c | 12 +++++++++++
src/core/mainloop/main.c | 5 +++--
src/lib/crypt_ops/compat_openssl.h | 6 ++++++
src/lib/crypt_ops/crypto_dh.h | 2 +-
src/lib/crypt_ops/crypto_ed25519.c | 1 +
src/lib/crypt_ops/crypto_format.c | 1 +
src/lib/crypt_ops/crypto_hkdf.c | 2 ++
src/lib/crypt_ops/crypto_init.c | 42 ++++++++++++++++++++++++++++++++++++
src/lib/crypt_ops/crypto_init.h | 4 ++++
src/lib/crypt_ops/crypto_rand.c | 14 ++++++++----
src/lib/crypt_ops/crypto_rsa.c | 6 ++++--
src/lib/crypt_ops/crypto_s2k.c | 7 ++++++
src/lib/crypt_ops/crypto_util.c | 6 ++++--
src/lib/crypt_ops/include.am | 2 +-
src/lib/tls/tortls_internal.h | 44 +++++++++++++++++++++-----------------
src/lib/tls/tortls_nss.c | 42 ------------------------------------
src/test/bench.c | 8 +++++++
src/test/include.am | 2 +-
src/test/test.c | 2 ++
src/test/test_crypto.c | 4 ++++
src/test/test_crypto_slow.c | 2 ++
src/tools/include.am | 14 ++++++++++--
src/tools/tor-gencert.c | 2 ++
24 files changed, 164 insertions(+), 80 deletions(-)
diff --git a/configure.ac b/configure.ac
index aa9b2ba6b..f99697a44 100644
--- a/configure.ac
+++ b/configure.ac
@@ -67,14 +67,15 @@ AM_CONDITIONAL(LIBFUZZER_ENABLED, test "x$enable_libfuzzer" = "xyes")
AM_CONDITIONAL(OSS_FUZZ_ENABLED, test "x$enable_oss_fuzz" = "xyes")
AM_CONDITIONAL(USE_RUST, test "x$enable_rust" = "xyes")
AM_CONDITIONAL(USE_NSS, test "x$enable_nss" = "xyes")
-AM_CONDITIONAL(USE_OPENSSL, true)
+AM_CONDITIONAL(USE_OPENSSL, test "x$enable_nss" != "xyes")
if test "x$enable_nss" = "xyes"; then
AC_DEFINE(ENABLE_NSS, 1,
[Defined if we're building with NSS in addition to OpenSSL.])
+else
+ AC_DEFINE(ENABLE_OPENSSL, 1,
+ [Defined if we're building with OpenSSL or LibreSSL])
fi
-AC_DEFINE(ENABLE_OPENSSL, 1,
- [Defined if we're building with OpenSSL or LibreSSL])
if test "$enable_static_tor" = "yes"; then
enable_static_libevent="yes";
@@ -872,6 +873,8 @@ fi
dnl ------------------------------------------------------
dnl Where do you live, openssl? And how do we call you?
+if test "x$enable_nss" != "xyes"; then
+
tor_openssl_pkg_redhat="openssl"
tor_openssl_pkg_debian="libssl-dev"
tor_openssl_devpkg_redhat="openssl-devel"
@@ -971,6 +974,11 @@ AC_CHECK_SIZEOF(SHA_CTX, , [AC_INCLUDES_DEFAULT()
#include <openssl/sha.h>
])
+fi # enable_nss
+
+dnl ======================================================================
+dnl Can we use KIST?
+
dnl Define the set of checks for KIST scheduler support.
AC_DEFUN([CHECK_KIST_SUPPORT],[
dnl KIST needs struct tcp_info and for certain members to exist.
diff --git a/src/app/config/config.c b/src/app/config/config.c
index d2ed29562..d7c9f6d61 100644
--- a/src/app/config/config.c
+++ b/src/app/config/config.c
@@ -82,6 +82,11 @@
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_util.h"
#include "lib/crypt_ops/crypto_init.h"
+#ifdef ENABLE_NSS
+#include "lib/crypt_ops/crypto_nss_mgt.h"
+#else
+#include "lib/crypt_ops/crypto_openssl_mgt.h"
+#endif
#include "feature/dircache/dirserv.h"
#include "feature/relay/dns.h"
#include "core/or/dos.h"
@@ -5238,9 +5243,16 @@ options_init_from_torrc(int argc, char **argv)
printf("Libevent\t\t%-15s\t\t%s\n",
tor_libevent_get_header_version_str(),
tor_libevent_get_version_str());
+#ifdef ENABLE_OPENSSL
printf("OpenSSL \t\t%-15s\t\t%s\n",
crypto_openssl_get_header_version_str(),
crypto_openssl_get_version_str());
+#endif
+#ifdef ENABLE_NSS
+ printf("NSS \t\t%-15s\t\t%s\n",
+ crypto_nss_get_header_version_str(),
+ crypto_nss_get_version_str());
+#endif
if (tor_compress_supports_method(ZLIB_METHOD)) {
printf("Zlib \t\t%-15s\t\t%s\n",
tor_compress_version_str(ZLIB_METHOD),
diff --git a/src/core/mainloop/main.c b/src/core/mainloop/main.c
index ad8c1ead6..f40639d08 100644
--- a/src/core/mainloop/main.c
+++ b/src/core/mainloop/main.c
@@ -3504,10 +3504,11 @@ tor_init(int argc, char *argv[])
const char *version = get_version();
log_notice(LD_GENERAL, "Tor %s running on %s with Libevent %s, "
- "OpenSSL %s, Zlib %s, Liblzma %s, and Libzstd %s.", version,
+ "%s %s, Zlib %s, Liblzma %s, and Libzstd %s.", version,
get_uname(),
tor_libevent_get_version_str(),
- crypto_openssl_get_version_str(),
+ crypto_get_library_name(),
+ crypto_get_library_version_string(),
tor_compress_supports_method(ZLIB_METHOD) ?
tor_compress_version_str(ZLIB_METHOD) : "N/A",
tor_compress_supports_method(LZMA_METHOD) ?
diff --git a/src/lib/crypt_ops/compat_openssl.h b/src/lib/crypt_ops/compat_openssl.h
index 317c01134..f2f632ab4 100644
--- a/src/lib/crypt_ops/compat_openssl.h
+++ b/src/lib/crypt_ops/compat_openssl.h
@@ -7,6 +7,10 @@
#ifndef TOR_COMPAT_OPENSSL_H
#define TOR_COMPAT_OPENSSL_H
+#include "orconfig.h"
+
+#ifdef ENABLE_OPENSSL
+
#include <openssl/opensslv.h>
#include "lib/crypt_ops/crypto_openssl_mgt.h"
@@ -47,5 +51,7 @@
#define CONST_IF_OPENSSL_1_1_API const
#endif /* !defined(OPENSSL_1_1_API) */
+#endif /* defined(ENABLE_OPENSSL) */
+
#endif /* !defined(TOR_COMPAT_OPENSSL_H) */
diff --git a/src/lib/crypt_ops/crypto_dh.h b/src/lib/crypt_ops/crypto_dh.h
index 6e79a6404..3ee343a27 100644
--- a/src/lib/crypt_ops/crypto_dh.h
+++ b/src/lib/crypt_ops/crypto_dh.h
@@ -56,7 +56,7 @@ struct dh_st *crypto_dh_new_openssl_tls(void);
void crypto_dh_init_openssl(void);
void crypto_dh_free_all_openssl(void);
#endif
-#ifdef ENABLE_OPENSSL
+#ifdef ENABLE_NSS
void crypto_dh_init_nss(void);
void crypto_dh_free_all_nss(void);
#endif
diff --git a/src/lib/crypt_ops/crypto_ed25519.c b/src/lib/crypt_ops/crypto_ed25519.c
index 9d2c9e9fa..11c1f56ae 100644
--- a/src/lib/crypt_ops/crypto_ed25519.c
+++ b/src/lib/crypt_ops/crypto_ed25519.c
@@ -37,6 +37,7 @@
#include "ed25519/donna/ed25519_donna_tor.h"
#include <string.h>
+#include <errno.h>
static void pick_ed25519_impl(void);
diff --git a/src/lib/crypt_ops/crypto_format.c b/src/lib/crypt_ops/crypto_format.c
index 50916a8d6..09ec753a0 100644
--- a/src/lib/crypt_ops/crypto_format.c
+++ b/src/lib/crypt_ops/crypto_format.c
@@ -29,6 +29,7 @@
#include "lib/fs/files.h"
#include <string.h>
+#include <errno.h>
/** Write the <b>datalen</b> bytes from <b>data</b> to the file named
* <b>fname</b> in the tagged-data format. This format contains a
diff --git a/src/lib/crypt_ops/crypto_hkdf.c b/src/lib/crypt_ops/crypto_hkdf.c
index 1873632a9..a63d9131d 100644
--- a/src/lib/crypt_ops/crypto_hkdf.c
+++ b/src/lib/crypt_ops/crypto_hkdf.c
@@ -17,12 +17,14 @@
#include "lib/intmath/cmp.h"
#include "lib/log/util_bug.h"
+#ifdef ENABLE_OPENSSL
#include <openssl/opensslv.h>
#if defined(HAVE_ERR_LOAD_KDF_STRINGS)
#include <openssl/kdf.h>
#define HAVE_OPENSSL_HKDF 1
#endif
+#endif
#include <string.h>
diff --git a/src/lib/crypt_ops/crypto_init.c b/src/lib/crypt_ops/crypto_init.c
index 620fe8e1b..f9b077e9e 100644
--- a/src/lib/crypt_ops/crypto_init.c
+++ b/src/lib/crypt_ops/crypto_init.c
@@ -88,6 +88,10 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
#ifdef ENABLE_OPENSSL
if (crypto_openssl_late_init(useAccel, accelName, accelDir) < 0)
return -1;
+#else
+ (void)useAccel;
+ (void)accelName;
+ (void)accelDir;
#endif
#ifdef ENABLE_NSS
if (crypto_nss_late_init() < 0)
@@ -139,3 +143,41 @@ crypto_postfork(void)
crypto_nss_postfork();
#endif
}
+
+/** Return the name of the crypto library we're using. */
+const char *
+crypto_get_library_name(void)
+{
+#ifdef ENABLE_OPENSSL
+ return "OpenSSL";
+#endif
+#ifdef ENABLE_NSS
+ return "NSS";
+#endif
+}
+
+/** Return the version of the crypto library we are using, as given in the
+ * library. */
+const char *
+crypto_get_library_version_string(void)
+{
+#ifdef ENABLE_OPENSSL
+ return crypto_openssl_get_version_str();
+#endif
+#ifdef ENABLE_NSS
+ return crypto_nss_get_version_str();
+#endif
+}
+
+/** Return the version of the crypto library we're using, as given in the
+ * headers. */
+const char *
+crypto_get_header_version_string(void)
+{
+#ifdef ENABLE_OPENSSL
+ return crypto_openssl_get_header_version_str();
+#endif
+#ifdef ENABLE_NSS
+ return crypto_nss_get_header_version_str();
+#endif
+}
diff --git a/src/lib/crypt_ops/crypto_init.h b/src/lib/crypt_ops/crypto_init.h
index 3e32456b5..05b281720 100644
--- a/src/lib/crypt_ops/crypto_init.h
+++ b/src/lib/crypt_ops/crypto_init.h
@@ -26,4 +26,8 @@ void crypto_thread_cleanup(void);
int crypto_global_cleanup(void);
void crypto_postfork(void);
+const char *crypto_get_library_name(void);
+const char *crypto_get_library_version_string(void);
+const char *crypto_get_header_version_string(void);
+
#endif /* !defined(TOR_CRYPTO_H) */
diff --git a/src/lib/crypt_ops/crypto_rand.c b/src/lib/crypt_ops/crypto_rand.c
index 980671474..78471bf39 100644
--- a/src/lib/crypt_ops/crypto_rand.c
+++ b/src/lib/crypt_ops/crypto_rand.c
@@ -35,9 +35,11 @@
#include "lib/testsupport/testsupport.h"
#include "lib/fs/files.h"
+#include "lib/defs/digest_sizes.h"
+#include "lib/crypt_ops/crypto_digest.h"
+
#ifdef ENABLE_NSS
#include "lib/crypt_ops/crypto_nss_mgt.h"
-#include "lib/crypt_ops/crypto_digest.h"
#endif
#ifdef ENABLE_OPENSSL
@@ -80,6 +82,7 @@ ENABLE_GCC_WARNING(redundant-decls)
#endif
#include <string.h>
+#include <errno.h>
/**
* How many bytes of entropy we add at once.
@@ -335,7 +338,8 @@ crypto_strongest_rand_raw(uint8_t *out, size_t out_len)
void
crypto_strongest_rand(uint8_t *out, size_t out_len)
{
-#define DLEN SHA512_DIGEST_LENGTH
+#define DLEN DIGEST512_LEN
+
/* We're going to hash DLEN bytes from the system RNG together with some
* bytes from the PRNGs from our crypto librar(y/ies), in order to yield
* DLEN bytes.
@@ -360,11 +364,11 @@ crypto_strongest_rand(uint8_t *out, size_t out_len)
// LCOV_EXCL_STOP
}
if (out_len >= DLEN) {
- SHA512(inp, sizeof(inp), out);
+ crypto_digest512((char*)out, (char*)inp, sizeof(inp), DIGEST_SHA512);
out += DLEN;
out_len -= DLEN;
} else {
- SHA512(inp, sizeof(inp), tmp);
+ crypto_digest512((char*)tmp, (char*)inp, sizeof(inp), DIGEST_SHA512);
memcpy(out, tmp, out_len);
break;
}
@@ -699,6 +703,7 @@ smartlist_shuffle(smartlist_t *sl)
int
crypto_force_rand_ssleay(void)
{
+#ifdef ENABLE_OPENSSL
RAND_METHOD *default_method;
default_method = RAND_OpenSSL();
if (RAND_get_rand_method() != default_method) {
@@ -708,6 +713,7 @@ crypto_force_rand_ssleay(void)
RAND_set_rand_method(default_method);
return 1;
}
+#endif
return 0;
}
diff --git a/src/lib/crypt_ops/crypto_rsa.c b/src/lib/crypt_ops/crypto_rsa.c
index 0f80bc967..31497e650 100644
--- a/src/lib/crypt_ops/crypto_rsa.c
+++ b/src/lib/crypt_ops/crypto_rsa.c
@@ -37,11 +37,12 @@ crypto_get_rsa_padding_overhead(int padding)
{
switch (padding)
{
- case RSA_PKCS1_OAEP_PADDING: return PKCS1_OAEP_PADDING_OVERHEAD;
+ case PK_PKCS1_OAEP_PADDING: return PKCS1_OAEP_PADDING_OVERHEAD;
default: tor_assert(0); return -1; // LCOV_EXCL_LINE
}
}
+#ifdef ENABLE_OPENSSL
/** Given a padding method <b>padding</b>, return the correct OpenSSL constant.
*/
int
@@ -53,6 +54,7 @@ crypto_get_rsa_padding(int padding)
default: tor_assert(0); return -1; // LCOV_EXCL_LINE
}
}
+#endif
/** Compare the public-key components of a and b. Return non-zero iff
* a==b. A NULL key is considered to be distinct from all non-NULL
@@ -100,7 +102,7 @@ crypto_pk_obsolete_public_hybrid_encrypt(crypto_pk_t *env,
tor_assert(to);
tor_assert(fromlen < SIZE_T_CEILING);
- overhead = crypto_get_rsa_padding_overhead(crypto_get_rsa_padding(padding));
+ overhead = crypto_get_rsa_padding_overhead(padding);
pkeylen = crypto_pk_keysize(env);
if (!force && fromlen+overhead <= pkeylen) {
diff --git a/src/lib/crypt_ops/crypto_s2k.c b/src/lib/crypt_ops/crypto_s2k.c
index 0e151f0a6..433fbb026 100644
--- a/src/lib/crypt_ops/crypto_s2k.c
+++ b/src/lib/crypt_ops/crypto_s2k.c
@@ -21,7 +21,9 @@
#include "lib/ctime/di_ops.h"
#include "lib/log/util_bug.h"
+#ifdef ENABLE_OPENSSL
#include <openssl/evp.h>
+#endif
#if defined(HAVE_LIBSCRYPT_H) && defined(HAVE_LIBSCRYPT_SCRYPT)
#define HAVE_SCRYPT
@@ -265,6 +267,7 @@ secret_to_key_compute_key(uint8_t *key_out, size_t key_out_len,
return (int)key_out_len;
case S2K_TYPE_PBKDF2: {
+#ifdef ENABLE_OPENSSL
uint8_t log_iters;
if (spec_len < 1 || secret_len > INT_MAX || spec_len > INT_MAX)
return S2K_BAD_LEN;
@@ -278,6 +281,10 @@ secret_to_key_compute_key(uint8_t *key_out, size_t key_out_len,
if (rv < 0)
return S2K_FAILED;
return (int)key_out_len;
+#else
+ // XXXXXXXXXXXXXXXXXXXXXXXX implement me.
+ return S2K_NO_SCRYPT_SUPPORT;
+#endif
}
case S2K_TYPE_SCRYPT: {
diff --git a/src/lib/crypt_ops/crypto_util.c b/src/lib/crypt_ops/crypto_util.c
index a645321bf..7af80291e 100644
--- a/src/lib/crypt_ops/crypto_util.c
+++ b/src/lib/crypt_ops/crypto_util.c
@@ -23,12 +23,14 @@
#include <wincrypt.h>
#endif /* defined(_WIN32) */
-DISABLE_GCC_WARNING(redundant-decls)
+#include <stdlib.h>
+#ifdef ENABLE_OPENSSL
+DISABLE_GCC_WARNING(redundant-decls)
#include <openssl/err.h>
#include <openssl/crypto.h>
-
ENABLE_GCC_WARNING(redundant-decls)
+#endif
#include "lib/log/log.h"
#include "lib/log/util_bug.h"
diff --git a/src/lib/crypt_ops/include.am b/src/lib/crypt_ops/include.am
index 195dac6bd..1022096fd 100644
--- a/src/lib/crypt_ops/include.am
+++ b/src/lib/crypt_ops/include.am
@@ -9,7 +9,6 @@ src_lib_libtor_crypt_ops_a_SOURCES = \
src/lib/crypt_ops/crypto_cipher.c \
src/lib/crypt_ops/crypto_curve25519.c \
src/lib/crypt_ops/crypto_dh.c \
- src/lib/crypt_ops/crypto_dh_openssl.c \
src/lib/crypt_ops/crypto_digest.c \
src/lib/crypt_ops/crypto_ed25519.c \
src/lib/crypt_ops/crypto_format.c \
@@ -37,6 +36,7 @@ endif
if USE_OPENSSL
src_lib_libtor_crypt_ops_a_SOURCES += \
+ src/lib/crypt_ops/crypto_dh_openssl.c \
src/lib/crypt_ops/crypto_openssl_mgt.c
endif
diff --git a/src/lib/tls/tortls_internal.h b/src/lib/tls/tortls_internal.h
index f6afb348c..c58379e19 100644
--- a/src/lib/tls/tortls_internal.h
+++ b/src/lib/tls/tortls_internal.h
@@ -15,28 +15,38 @@ struct ssl_session_st;
int tor_errno_to_tls_error(int e);
int tor_tls_get_error(tor_tls_t *tls, int r, int extra,
const char *doing, int severity, int domain);
-tor_tls_t *tor_tls_get_by_ssl(const struct ssl_st *ssl);
-void tor_tls_allocate_tor_tls_object_ex_data_index(void);
MOCK_DECL(void, try_to_extract_certs_from_tls,
(int severity, tor_tls_t *tls,
tor_x509_cert_impl_t **cert_out,
tor_x509_cert_impl_t **id_cert_out));
-#ifdef TORTLS_OPENSSL_PRIVATE
-int always_accept_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx);
-int tor_tls_classify_client_ciphers(const struct ssl_st *ssl,
- STACK_OF(SSL_CIPHER) *peer_ciphers);
-#endif
+
+tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity,
+ unsigned int key_lifetime, unsigned flags, int is_client);
+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);
+
+#ifdef ENABLE_OPENSSL
+tor_tls_t *tor_tls_get_by_ssl(const struct ssl_st *ssl);
int tor_tls_client_is_using_v2_ciphers(const struct ssl_st *ssl);
-#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY
-size_t SSL_SESSION_get_master_key(struct ssl_session_st *s,
- uint8_t *out,
- size_t len);
-#endif
void tor_tls_debug_state_callback(const struct ssl_st *ssl,
int type, int val);
void tor_tls_server_info_callback(const struct ssl_st *ssl,
int type, int val);
+void tor_tls_allocate_tor_tls_object_ex_data_index(void);
+
+#if !defined(HAVE_SSL_SESSION_GET_MASTER_KEY)
+size_t SSL_SESSION_get_master_key(struct ssl_session_st *s,
+ uint8_t *out,
+ size_t len);
+#endif
+
#ifdef TORTLS_OPENSSL_PRIVATE
+int always_accept_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx);
+int tor_tls_classify_client_ciphers(const struct ssl_st *ssl,
+ STACK_OF(SSL_CIPHER) *peer_ciphers);
STATIC int tor_tls_session_secret_cb(struct ssl_st *ssl, void *secret,
int *secret_len,
STACK_OF(SSL_CIPHER) *peer_ciphers,
@@ -44,14 +54,8 @@ STATIC int tor_tls_session_secret_cb(struct ssl_st *ssl, void *secret,
void *arg);
STATIC int find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m,
uint16_t cipher);
-#endif /* defined(TORTLS_OPENSSL_PRIVATE) */
-tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity,
- unsigned int key_lifetime, unsigned flags, int is_client);
-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);
+#endif
+#endif
#ifdef TOR_UNIT_TESTS
extern int tor_tls_object_ex_data_index;
diff --git a/src/lib/tls/tortls_nss.c b/src/lib/tls/tortls_nss.c
index 078196ac5..98fecdaf1 100644
--- a/src/lib/tls/tortls_nss.c
+++ b/src/lib/tls/tortls_nss.c
@@ -47,19 +47,6 @@ tor_tls_get_error(tor_tls_t *tls, int r, int extra,
// XXXX
return -1;
}
-tor_tls_t *
-tor_tls_get_by_ssl(const struct ssl_st *ssl)
-{
- (void) ssl;
- // XXXX
- // XXXX refers to ssl_st.
- return NULL;
-}
-void
-tor_tls_allocate_tor_tls_object_ex_data_index(void)
-{
- // XXXX openssl only.
-}
MOCK_IMPL(void,
try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls,
tor_x509_cert_impl_t **cert_out,
@@ -71,36 +58,7 @@ try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls,
(void)severity;
// XXXX
}
-int
-tor_tls_client_is_using_v2_ciphers(const struct ssl_st *ssl)
-{
- (void) ssl;
- // XXXX
- // XXXX refers to ssl_st.
- return 0;
-}
-void
-tor_tls_debug_state_callback(const struct ssl_st *ssl,
- int type, int val)
-{
- (void) ssl;
- (void)type;
- (void)val;
- // XXXX
- // XXXX refers to ssl_st.
-}
-
-void
-tor_tls_server_info_callback(const struct ssl_st *ssl,
- int type, int val)
-{
- (void)ssl;
- (void)type;
- (void)val;
- // XXXX
- // XXXX refers to ssl_st.
-}
tor_tls_context_t *
tor_tls_context_new(crypto_pk_t *identity,
unsigned int key_lifetime, unsigned flags, int is_client)
diff --git a/src/test/bench.c b/src/test/bench.c
index 2b90ccf73..359405905 100644
--- a/src/test/bench.c
+++ b/src/test/bench.c
@@ -13,11 +13,14 @@
#include "core/or/or.h"
#include "core/crypto/onion_tap.h"
#include "core/crypto/relay_crypto.h"
+
+#ifdef ENABLE_OPENSSL
#include <openssl/opensslv.h>
#include <openssl/evp.h>
#include <openssl/ec.h>
#include <openssl/ecdh.h>
#include <openssl/obj_mac.h>
+#endif
#include "core/or/circuitlist.h"
#include "app/config/config.h"
@@ -580,6 +583,7 @@ bench_dh(void)
" %f millisec each.\n", NANOCOUNT(start, end, iters)/1e6);
}
+#ifdef ENABLE_OPENSSL
static void
bench_ecdh_impl(int nid, const char *name)
{
@@ -629,6 +633,7 @@ bench_ecdh_p224(void)
{
bench_ecdh_impl(NID_secp224r1, "P-224");
}
+#endif
typedef void (*bench_fn)(void);
@@ -652,8 +657,11 @@ static struct benchmark_t benchmarks[] = {
ENT(cell_aes),
ENT(cell_ops),
ENT(dh),
+
+#ifdef ENABLE_OPENSSL
ENT(ecdh_p256),
ENT(ecdh_p224),
+#endif
{NULL,NULL,0}
};
diff --git a/src/test/include.am b/src/test/include.am
index c2e08aa3d..05149b865 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -118,7 +118,6 @@ src_test_test_SOURCES += \
src/test/test_controller_events.c \
src/test/test_crypto.c \
src/test/test_crypto_ope.c \
- src/test/test_crypto_openssl.c \
src/test/test_data.c \
src/test/test_dir.c \
src/test/test_dir_common.c \
@@ -189,6 +188,7 @@ if USE_NSS
# ...
else
src_test_test_SOURCES += \
+ src/test/test_crypto_openssl.c \
src/test/test_tortls_openssl.c
endif
diff --git a/src/test/test.c b/src/test/test.c
index 3b63f1c07..962344305 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -866,7 +866,9 @@ struct testgroup_t testgroups[] = {
{ "control/event/", controller_event_tests },
{ "crypto/", crypto_tests },
{ "crypto/ope/", crypto_ope_tests },
+#ifdef ENABLE_OPENSSL
{ "crypto/openssl/", crypto_openssl_tests },
+#endif
{ "crypto/pem/", pem_tests },
{ "dir/", dir_tests },
{ "dir_handle_get/", dir_handle_get_tests },
diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c
index 90fb8d468..04077b42f 100644
--- a/src/test/test_crypto.c
+++ b/src/test/test_crypto.c
@@ -224,6 +224,9 @@ static void
test_crypto_openssl_version(void *arg)
{
(void)arg;
+#ifdef ENABLE_NSS
+ tt_skip();
+#else
const char *version = crypto_openssl_get_version_str();
const char *h_version = crypto_openssl_get_header_version_str();
tt_assert(version);
@@ -243,6 +246,7 @@ test_crypto_openssl_version(void *arg)
tt_int_op(a, OP_GE, 0);
tt_int_op(b, OP_GE, 0);
tt_int_op(c, OP_GE, 0);
+#endif
done:
;
diff --git a/src/test/test_crypto_slow.c b/src/test/test_crypto_slow.c
index 88b31ad9a..ca6b7b8d4 100644
--- a/src/test/test_crypto_slow.c
+++ b/src/test/test_crypto_slow.c
@@ -18,7 +18,9 @@
#include <libscrypt.h>
#endif
+#ifdef ENABLE_OPENSSL
#include <openssl/evp.h>
+#endif
/** Run unit tests for our secret-to-key passphrase hashing functionality. */
static void
diff --git a/src/tools/include.am b/src/tools/include.am
index cdd5616fb..73ec86935 100644
--- a/src/tools/include.am
+++ b/src/tools/include.am
@@ -1,7 +1,7 @@
-bin_PROGRAMS+= src/tools/tor-resolve src/tools/tor-gencert src/tools/tor-print-ed-signing-cert
+bin_PROGRAMS+= src/tools/tor-resolve src/tools/tor-print-ed-signing-cert
if COVERAGE_ENABLED
-noinst_PROGRAMS+= src/tools/tor-cov-resolve src/tools/tor-cov-gencert
+noinst_PROGRAMS+= src/tools/tor-cov-resolve
endif
src_tools_tor_resolve_SOURCES = src/tools/tor-resolve.c
@@ -20,6 +20,10 @@ src_tools_tor_cov_resolve_LDADD = \
@TOR_LIB_MATH@ @TOR_LIB_WS32@
endif
+if USE_NSS
+# ...
+else
+bin_PROGRAMS += src/tools/tor-gencert
src_tools_tor_gencert_SOURCES = src/tools/tor-gencert.c
src_tools_tor_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB)
src_tools_tor_gencert_LDADD = \
@@ -28,6 +32,7 @@ src_tools_tor_gencert_LDADD = \
$(rust_ldadd) \
@TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ $(TOR_LIBS_CRYPTLIB) \
@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ @CURVE25519_LIBS@
+endif
src_tools_tor_print_ed_signing_cert_SOURCES = src/tools/tor-print-ed-signing-cert.c
src_tools_tor_print_ed_signing_cert_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
@@ -38,7 +43,11 @@ src_tools_tor_print_ed_signing_cert_LDADD = \
@TOR_LIB_MATH@ $(TOR_LIBS_CRYPTLIB) \
@TOR_LIB_WS32@ @TOR_LIB_USERENV@
+if USE_NSS
+# ...
+else
if COVERAGE_ENABLED
+noinst_PROGRAMS += src/tools/tor-cov-gencert
src_tools_tor_cov_gencert_SOURCES = src/tools/tor-gencert.c
src_tools_tor_cov_gencert_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_tools_tor_cov_gencert_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
@@ -49,6 +58,7 @@ src_tools_tor_cov_gencert_LDADD = \
@TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ $(TOR_LIBS_CRYPTLIB) \
@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
endif
+endif
if BUILD_LIBTORRUNNER
noinst_LIBRARIES += src/tools/libtorrunner.a
diff --git a/src/tools/tor-gencert.c b/src/tools/tor-gencert.c
index e0ac3dec8..a498c205b 100644
--- a/src/tools/tor-gencert.c
+++ b/src/tools/tor-gencert.c
@@ -17,6 +17,7 @@
#include "lib/crypt_ops/crypto_init.h"
#include "lib/crypt_ops/crypto_openssl_mgt.h"
+#ifdef ENABLE_OPENSSL
/* Some versions of OpenSSL declare X509_STORE_CTX_set_verify_cb twice in
* x509.h and x509_vfy.h. Suppress the GCC warning so we can build with
* -Wredundant-decl. */
@@ -30,6 +31,7 @@ DISABLE_GCC_WARNING(redundant-decls)
#include <openssl/err.h>
ENABLE_GCC_WARNING(redundant-decls)
+#endif
#include <errno.h>
1
0
[tor/master] Extract the non-generic part of tor_tls_context_decref().
by nickm@torproject.org 05 Sep '18
by nickm@torproject.org 05 Sep '18
05 Sep '18
commit 108d9879eb814bc06095a1819d98b1c7c9a38e88
Author: Nick Mathewson <nickm(a)torproject.org>
Date: Sun Aug 12 17:54:06 2018 -0400
Extract the non-generic part of tor_tls_context_decref().
---
src/lib/tls/tortls.c | 19 +++++++++++++++++++
src/lib/tls/tortls_internal.h | 7 +++++++
src/lib/tls/tortls_nss.c | 7 +++++++
src/lib/tls/tortls_openssl.c | 28 +++++++++-------------------
4 files changed, 42 insertions(+), 19 deletions(-)
diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c
index 4b35177df..395f0148e 100644
--- a/src/lib/tls/tortls.c
+++ b/src/lib/tls/tortls.c
@@ -35,6 +35,25 @@ tor_tls_context_incref(tor_tls_context_t *ctx)
++ctx->refcnt;
}
+/** Remove a reference to <b>ctx</b>, and free it if it has no more
+ * references. */
+void
+tor_tls_context_decref(tor_tls_context_t *ctx)
+{
+ tor_assert(ctx);
+ if (--ctx->refcnt == 0) {
+ tor_tls_context_impl_free(ctx->ctx);
+ tor_x509_cert_free(ctx->my_link_cert);
+ tor_x509_cert_free(ctx->my_id_cert);
+ tor_x509_cert_free(ctx->my_auth_cert);
+ crypto_pk_free(ctx->link_key);
+ crypto_pk_free(ctx->auth_key);
+ /* LCOV_EXCL_BR_START since ctx will never be NULL here */
+ tor_free(ctx);
+ /* LCOV_EXCL_BR_STOP */
+ }
+}
+
/** Free all global TLS structures. */
void
tor_tls_free_all(void)
diff --git a/src/lib/tls/tortls_internal.h b/src/lib/tls/tortls_internal.h
index c58379e19..b997ee3e4 100644
--- a/src/lib/tls/tortls_internal.h
+++ b/src/lib/tls/tortls_internal.h
@@ -29,6 +29,13 @@ int tor_tls_context_init_one(tor_tls_context_t **ppcontext,
int is_client);
#ifdef ENABLE_OPENSSL
+void tor_tls_context_impl_free(struct ssl_ctx_st *);
+#else
+struct ssl_ctx_st; // XXXX replace
+void tor_tls_context_impl_free(struct ssl_ctx_st *);
+#endif
+
+#ifdef ENABLE_OPENSSL
tor_tls_t *tor_tls_get_by_ssl(const struct ssl_st *ssl);
int tor_tls_client_is_using_v2_ciphers(const struct ssl_st *ssl);
void tor_tls_debug_state_callback(const struct ssl_st *ssl,
diff --git a/src/lib/tls/tortls_nss.c b/src/lib/tls/tortls_nss.c
index 98fecdaf1..3ab5c753d 100644
--- a/src/lib/tls/tortls_nss.c
+++ b/src/lib/tls/tortls_nss.c
@@ -85,6 +85,13 @@ tor_tls_context_init_one(tor_tls_context_t **ppcontext,
// XXXX
return -1;
}
+void
+tor_tls_context_impl_free(struct ssl_ctx_st *ctx)
+{
+ (void)ctx;
+ // XXXX
+ // XXXX openssl type.
+}
void
tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz)
diff --git a/src/lib/tls/tortls_openssl.c b/src/lib/tls/tortls_openssl.c
index 333b86481..5f5431235 100644
--- a/src/lib/tls/tortls_openssl.c
+++ b/src/lib/tls/tortls_openssl.c
@@ -30,6 +30,7 @@
#include "lib/crypt_ops/crypto_util.h"
#include "lib/crypt_ops/compat_openssl.h"
#include "lib/tls/x509.h"
+#include "lib/tls/x509_internal.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. */
@@ -488,25 +489,6 @@ static const char CLIENT_CIPHER_LIST[] =
#undef CIPHER
#undef XCIPHER
-/** Remove a reference to <b>ctx</b>, and free it if it has no more
- * references. */
-void
-tor_tls_context_decref(tor_tls_context_t *ctx)
-{
- tor_assert(ctx);
- if (--ctx->refcnt == 0) {
- SSL_CTX_free(ctx->ctx);
- tor_x509_cert_free(ctx->my_link_cert);
- tor_x509_cert_free(ctx->my_id_cert);
- tor_x509_cert_free(ctx->my_auth_cert);
- crypto_pk_free(ctx->link_key);
- crypto_pk_free(ctx->auth_key);
- /* LCOV_EXCL_BR_START since ctx will never be NULL here */
- tor_free(ctx);
- /* LCOV_EXCL_BR_STOP */
- }
-}
-
/** Set *<b>link_cert_out</b> and *<b>id_cert_out</b> to the link certificate
* and ID certificate that we're currently using for our V3 in-protocol
* handshake's certificate chain. If <b>server</b> is true, provide the certs
@@ -599,6 +581,14 @@ tor_tls_context_init_one(tor_tls_context_t **ppcontext,
return ((new_ctx != NULL) ? 0 : -1);
}
+void
+tor_tls_context_impl_free(struct ssl_ctx_st *ctx)
+{
+ if (!ctx)
+ return;
+ SSL_CTX_free(ctx);
+}
+
/** The group we should use for ecdhe when none was selected. */
#define NID_tor_default_ecdhe_group NID_X9_62_prime256v1
1
0
commit b9ca8f2356a98630a262951486cd10436963e169
Author: Nick Mathewson <nickm(a)torproject.org>
Date: Sun Aug 12 18:01:14 2018 -0400
Extract internal-only parts of x509.h
---
src/lib/tls/include.am | 3 ++-
src/lib/tls/x509.c | 1 +
src/lib/tls/x509.h | 11 -----------
src/lib/tls/x509_internal.h | 28 ++++++++++++++++++++++++++++
src/lib/tls/x509_nss.c | 1 +
src/lib/tls/x509_openssl.c | 1 +
src/test/test_tortls.c | 1 +
src/test/test_tortls_openssl.c | 1 +
8 files changed, 35 insertions(+), 12 deletions(-)
diff --git a/src/lib/tls/include.am b/src/lib/tls/include.am
index 173104903..b25e2e16b 100644
--- a/src/lib/tls/include.am
+++ b/src/lib/tls/include.am
@@ -34,4 +34,5 @@ noinst_HEADERS += \
src/lib/tls/tortls.h \
src/lib/tls/tortls_internal.h \
src/lib/tls/tortls_st.h \
- src/lib/tls/x509.h
+ src/lib/tls/x509.h \
+ src/lib/tls/x509_internal.h
diff --git a/src/lib/tls/x509.c b/src/lib/tls/x509.c
index dbf1dd927..fc6139ace 100644
--- a/src/lib/tls/x509.c
+++ b/src/lib/tls/x509.c
@@ -11,6 +11,7 @@
#define TOR_X509_PRIVATE
#include "lib/tls/x509.h"
+#include "lib/tls/x509_internal.h"
#include "lib/log/util_bug.h"
#include "lib/crypt_ops/crypto_rand.h"
diff --git a/src/lib/tls/x509.h b/src/lib/tls/x509.h
index f75d15d7e..ccaa92184 100644
--- a/src/lib/tls/x509.h
+++ b/src/lib/tls/x509.h
@@ -40,15 +40,6 @@ void tor_tls_pick_certificate_lifetime(time_t now,
time_t *start_time_out,
time_t *end_time_out);
-MOCK_DECL(tor_x509_cert_impl_t *, 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,
- (tor_x509_cert_impl_t *x509_cert));
-
#ifdef TOR_UNIT_TESTS
tor_x509_cert_t *tor_x509_cert_replace_expiration(
const tor_x509_cert_t *inp,
@@ -63,8 +54,6 @@ void tor_x509_cert_free_(tor_x509_cert_t *cert);
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);
-const tor_x509_cert_impl_t *tor_x509_cert_get_impl(
- const tor_x509_cert_t *cert);
void tor_x509_cert_get_der(const tor_x509_cert_t *cert,
const uint8_t **encoded_out, size_t *size_out);
diff --git a/src/lib/tls/x509_internal.h b/src/lib/tls/x509_internal.h
new file mode 100644
index 000000000..2cca393d2
--- /dev/null
+++ b/src/lib/tls/x509_internal.h
@@ -0,0 +1,28 @@
+/* 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_INTERNAL_H
+#define TOR_X509_INTERNAL_H
+
+/**
+ * \file x509.h
+ * \brief Internal headers for tortls.c
+ **/
+
+#include "lib/crypt_ops/crypto_rsa.h"
+#include "lib/testsupport/testsupport.h"
+
+MOCK_DECL(tor_x509_cert_impl_t *, 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,
+ (tor_x509_cert_impl_t *x509_cert));
+const tor_x509_cert_impl_t *tor_x509_cert_get_impl(
+ const tor_x509_cert_t *cert);
+
+#endif
diff --git a/src/lib/tls/x509_nss.c b/src/lib/tls/x509_nss.c
index e856c9518..e0087eae6 100644
--- a/src/lib/tls/x509_nss.c
+++ b/src/lib/tls/x509_nss.c
@@ -11,6 +11,7 @@
#define TOR_X509_PRIVATE
#include "lib/tls/x509.h"
+#include "lib/tls/x509_internal.h"
#include "lib/tls/tortls.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_util.h"
diff --git a/src/lib/tls/x509_openssl.c b/src/lib/tls/x509_openssl.c
index c003d4704..43d33d781 100644
--- a/src/lib/tls/x509_openssl.c
+++ b/src/lib/tls/x509_openssl.c
@@ -11,6 +11,7 @@
#define TOR_X509_PRIVATE
#include "lib/tls/x509.h"
+#include "lib/tls/x509_internal.h"
#include "lib/tls/tortls.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_util.h"
diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c
index d20bc5fa6..eedf0dd3c 100644
--- a/src/test/test_tortls.c
+++ b/src/test/test_tortls.c
@@ -19,6 +19,7 @@
#include "app/config/config.h"
#include "lib/crypt_ops/compat_openssl.h"
#include "lib/tls/x509.h"
+#include "lib/tls/x509_internal.h"
#include "lib/tls/tortls.h"
#include "lib/tls/tortls_st.h"
#include "lib/tls/tortls_internal.h"
diff --git a/src/test/test_tortls_openssl.c b/src/test/test_tortls_openssl.c
index a9336a6f1..12a05b303 100644
--- a/src/test/test_tortls_openssl.c
+++ b/src/test/test_tortls_openssl.c
@@ -36,6 +36,7 @@ ENABLE_GCC_WARNING(redundant-decls)
#include "app/config/config.h"
#include "lib/crypt_ops/compat_openssl.h"
#include "lib/tls/x509.h"
+#include "lib/tls/x509_internal.h"
#include "lib/tls/tortls.h"
#include "lib/tls/tortls_st.h"
#include "lib/tls/tortls_internal.h"
1
0
05 Sep '18
commit 91c1e88b7a6d41f93f88cd8754746c836b25721f
Author: Nick Mathewson <nickm(a)torproject.org>
Date: Sat Aug 11 19:54:11 2018 -0400
Refactor some of the certificate-manipulation logic
---
src/lib/tls/tortls.c | 5 ++--
src/lib/tls/x509.c | 68 ++++++++++++++++++++++++++++++++--------------------
src/lib/tls/x509.h | 14 +++++++----
3 files changed, 55 insertions(+), 32 deletions(-)
diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c
index cb507057e..4c5dedb57 100644
--- a/src/lib/tls/tortls.c
+++ b/src/lib/tls/tortls.c
@@ -1856,8 +1856,9 @@ tor_tls_check_lifetime(int severity, tor_tls_t *tls,
if (!(cert = SSL_get_peer_certificate(tls->ssl)))
goto done;
- if (check_cert_lifetime_internal(severity, cert, now,
- past_tolerance, future_tolerance) < 0)
+ if (tor_x509_check_cert_lifetime_internal(severity, cert, now,
+ past_tolerance,
+ future_tolerance) < 0)
goto done;
r = 0;
diff --git a/src/lib/tls/x509.c b/src/lib/tls/x509.c
index 27cba1be6..8bed6f5a1 100644
--- a/src/lib/tls/x509.c
+++ b/src/lib/tls/x509.c
@@ -85,6 +85,42 @@ tor_x509_name_new(const char *cname)
/* LCOV_EXCL_STOP */
}
+/** Choose the start and end times for a certificate */
+void
+tor_tls_pick_certificate_lifetime(time_t now,
+ unsigned int cert_lifetime,
+ time_t *start_time_out,
+ time_t *end_time_out)
+{
+ time_t start_time, end_time;
+ /* 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. */
+ /* 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;
+
+ *start_time_out = start_time;
+ *end_time_out = end_time;
+}
+
/** 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
@@ -113,30 +149,10 @@ tor_tls_create_certificate,(crypto_pk_t *rsa,
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_tls_pick_certificate_lifetime(now, cert_lifetime,
+ &start_time, &end_time);
tor_assert(rsa);
tor_assert(cname);
@@ -410,7 +426,7 @@ tor_tls_cert_is_valid(int severity,
/* okay, the signature checked out right. Now let's check the check the
* lifetime. */
- if (check_cert_lifetime_internal(severity, cert->cert, now,
+ if (tor_x509_check_cert_lifetime_internal(severity, cert->cert, now,
48*60*60, 30*24*60*60) < 0)
goto bad;
@@ -509,9 +525,9 @@ log_cert_lifetime(int severity, const X509 *cert, const char *problem,
* <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)
+tor_x509_check_cert_lifetime_internal(int severity, const X509 *cert,
+ time_t now,
+ int past_tolerance, int future_tolerance)
{
time_t t;
diff --git a/src/lib/tls/x509.h b/src/lib/tls/x509.h
index 4dadba06d..e3dfcf393 100644
--- a/src/lib/tls/x509.h
+++ b/src/lib/tls/x509.h
@@ -33,6 +33,11 @@ struct tor_x509_cert_t {
};
#endif
+void tor_tls_pick_certificate_lifetime(time_t now,
+ unsigned cert_lifetime,
+ time_t *start_time_out,
+ time_t *end_time_out);
+
MOCK_DECL(tor_x509_cert_impl_t *, tor_tls_create_certificate,
(crypto_pk_t *rsa,
crypto_pk_t *rsa_sign,
@@ -74,9 +79,10 @@ int tor_tls_cert_is_valid(int severity,
time_t now,
int check_rsa_1024);
-int check_cert_lifetime_internal(int severity,
- const tor_x509_cert_impl_t *cert,
- time_t now,
- int past_tolerance, int future_tolerance);
+int tor_x509_check_cert_lifetime_internal(int severity,
+ const tor_x509_cert_impl_t *cert,
+ time_t now,
+ int past_tolerance,
+ int future_tolerance);
#endif
1
0
[tor/master] Make some x509 functions generic; remove some fields NSS doesn't need
by nickm@torproject.org 05 Sep '18
by nickm@torproject.org 05 Sep '18
05 Sep '18
commit 5245a296c58eb8aba712e94a78d5bcaa2a2f25fb
Author: Nick Mathewson <nickm(a)torproject.org>
Date: Sun Aug 12 19:40:47 2018 -0400
Make some x509 functions generic; remove some fields NSS doesn't need
---
src/lib/tls/x509.c | 79 +++++++++++++++++++++++++++++++++-------
src/lib/tls/x509.h | 2 ++
src/lib/tls/x509_internal.h | 7 ++++
src/lib/tls/x509_nss.c | 35 +++++++++++-------
src/lib/tls/x509_openssl.c | 82 +++++++++++++-----------------------------
src/test/test_link_handshake.c | 8 +++--
6 files changed, 127 insertions(+), 86 deletions(-)
diff --git a/src/lib/tls/x509.c b/src/lib/tls/x509.c
index fc6139ace..d2270f910 100644
--- a/src/lib/tls/x509.c
+++ b/src/lib/tls/x509.c
@@ -14,6 +14,7 @@
#include "lib/tls/x509_internal.h"
#include "lib/log/util_bug.h"
#include "lib/crypt_ops/crypto_rand.h"
+#include "lib/crypt_ops/crypto_util.h"
/** Choose the start and end times for a certificate */
void
@@ -51,19 +52,6 @@ tor_tls_pick_certificate_lifetime(time_t now,
*end_time_out = end_time;
}
-/** 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 the underlying implementation for <b>cert</b> */
const tor_x509_cert_impl_t *
tor_x509_cert_get_impl(const tor_x509_cert_t *cert)
@@ -90,3 +78,68 @@ tor_x509_cert_get_cert_digests(const tor_x509_cert_t *cert)
return &cert->cert_digests;
}
+/** Free all storage held in <b>cert</b> */
+void
+tor_x509_cert_free_(tor_x509_cert_t *cert)
+{
+ if (! cert)
+ return;
+ if (cert->cert)
+ tor_x509_cert_impl_free_(cert->cert);
+#ifdef ENABLE_OPENSSL
+ tor_free(cert->encoded);
+#endif
+ 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,(tor_x509_cert_impl_t *x509_cert))
+{
+ tor_x509_cert_t *cert;
+
+ if (!x509_cert)
+ return NULL;
+
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
+ cert->cert = x509_cert;
+
+ if (tor_x509_cert_set_cached_der_encoding(cert) < 0)
+ goto err;
+
+ {
+ const uint8_t *encoded=NULL;
+ size_t encoded_len=0;
+ tor_x509_cert_get_der(cert, &encoded, &encoded_len);
+ tor_assert(encoded);
+ crypto_common_digests(&cert->cert_digests, (char *)encoded, encoded_len);
+ }
+
+ {
+ crypto_pk_t *pk = tor_tls_cert_get_key(cert);
+ if (pk) {
+ if (crypto_pk_get_common_digests(pk, &cert->pkey_digests) < 0) {
+ crypto_pk_free(pk);
+ goto err;
+ }
+ }
+ cert->pkey_digests_set = 1;
+ crypto_pk_free(pk);
+ }
+
+ 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.");
+ tor_x509_cert_impl_free_(x509_cert);
+ return NULL;
+ /* LCOV_EXCL_STOP */
+}
diff --git a/src/lib/tls/x509.h b/src/lib/tls/x509.h
index ccaa92184..8316df75a 100644
--- a/src/lib/tls/x509.h
+++ b/src/lib/tls/x509.h
@@ -27,8 +27,10 @@ typedef struct x509_st tor_x509_cert_impl_t;
/** Structure that we use for a single certificate. */
struct tor_x509_cert_t {
tor_x509_cert_impl_t *cert;
+#ifdef ENABLE_OPENSSL
uint8_t *encoded;
size_t encoded_len;
+#endif
unsigned pkey_digests_set : 1;
common_digests_t cert_digests;
common_digests_t pkey_digests;
diff --git a/src/lib/tls/x509_internal.h b/src/lib/tls/x509_internal.h
index 2cca393d2..86f5a0de5 100644
--- a/src/lib/tls/x509_internal.h
+++ b/src/lib/tls/x509_internal.h
@@ -25,4 +25,11 @@ MOCK_DECL(tor_x509_cert_t *, tor_x509_cert_new,
const tor_x509_cert_impl_t *tor_x509_cert_get_impl(
const tor_x509_cert_t *cert);
+void tor_x509_cert_impl_free_(tor_x509_cert_impl_t *cert);
+#ifdef ENABLE_OPENSSL
+int tor_x509_cert_set_cached_der_encoding(tor_x509_cert_t *cert);
+#else
+#define tor_x509_cert_set_cached_der_encoding(cert) (0)
+#endif
+
#endif
diff --git a/src/lib/tls/x509_nss.c b/src/lib/tls/x509_nss.c
index e0087eae6..ac9e6658d 100644
--- a/src/lib/tls/x509_nss.c
+++ b/src/lib/tls/x509_nss.c
@@ -17,6 +17,9 @@
#include "lib/crypt_ops/crypto_util.h"
#include "lib/log/util_bug.h"
+#include <pk11pub.h>
+#include <cert.h>
+
MOCK_IMPL(tor_x509_cert_impl_t *,
tor_tls_create_certificate,(crypto_pk_t *rsa,
crypto_pk_t *rsa_sign,
@@ -33,12 +36,27 @@ tor_tls_create_certificate,(crypto_pk_t *rsa,
return NULL;
}
-MOCK_IMPL(tor_x509_cert_t *,
-tor_x509_cert_new,(tor_x509_cert_impl_t *x509_cert))
+/** 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(x509_cert);
- // XXXX
- return NULL;
+ tor_assert(cert);
+ tor_assert(cert->cert);
+ tor_assert(encoded_out);
+ tor_assert(size_out);
+
+ const SECItem *item = &cert->cert->derCert;
+ *encoded_out = item->data;
+ *size_out = (size_t)item->len;
+}
+
+void
+tor_x509_cert_impl_free_(tor_x509_cert_impl_t *cert)
+{
+ if (cert)
+ CERT_DestroyCertificate(cert);
}
tor_x509_cert_t *
@@ -49,13 +67,6 @@ tor_x509_cert_dup(const tor_x509_cert_t *cert)
return NULL;
}
-void
-tor_x509_cert_free_(tor_x509_cert_t *cert)
-{
- (void)cert;
- // XXXX
-}
-
tor_x509_cert_t *
tor_x509_cert_decode(const uint8_t *certificate,
size_t certificate_len)
diff --git a/src/lib/tls/x509_openssl.c b/src/lib/tls/x509_openssl.c
index 43d33d781..43bd6b4d4 100644
--- a/src/lib/tls/x509_openssl.c
+++ b/src/lib/tls/x509_openssl.c
@@ -182,75 +182,41 @@ tor_tls_create_certificate,(crypto_pk_t *rsa,
#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))
+/** Set the 'encoded' and 'encoded_len' fields of "cert" from cert->cert. */
+int
+tor_x509_cert_set_cached_der_encoding(tor_x509_cert_t *cert)
{
- tor_x509_cert_t *cert;
- EVP_PKEY *pkey;
- RSA *rsa;
- int length;
unsigned char *buf = NULL;
+ int length = i2d_X509(cert->cert, &buf);
- 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;
+ return -1;
}
cert->encoded_len = (size_t) length;
cert->encoded = tor_malloc(length);
memcpy(cert->encoded, buf, length);
OPENSSL_free(buf);
+ return 0;
+}
- 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);
- }
+void
+tor_x509_cert_impl_free_(tor_x509_cert_impl_t *cert)
+{
+ if (cert)
+ X509_free(cert);
+}
- 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 */
+/** 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 new copy of <b>cert</b>. */
diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c
index e4722b4df..677276599 100644
--- a/src/test/test_link_handshake.c
+++ b/src/test/test_link_handshake.c
@@ -811,9 +811,11 @@ CERTS_FAIL(expired_rsa_id, /* both */
tor_x509_cert_t *newc;
time_t new_end = time(NULL) - 86400 * 10;
newc = tor_x509_cert_replace_expiration(idc, new_end, d->key2);
- certs_cell_cert_setlen_body(cert, newc->encoded_len);
- memcpy(certs_cell_cert_getarray_body(cert),
- newc->encoded, newc->encoded_len);
+ const uint8_t *encoded;
+ size_t encoded_len;
+ tor_x509_cert_get_der(newc, &encoded, &encoded_len);
+ certs_cell_cert_setlen_body(cert, encoded_len);
+ memcpy(certs_cell_cert_getarray_body(cert), encoded, encoded_len);
REENCODE();
tor_x509_cert_free(newc);
})
1
0
commit 5205c7fd903cb5bd751812bddb5497ac76e1f30b
Author: Nick Mathewson <nickm(a)torproject.org>
Date: Fri Aug 17 11:24:50 2018 -0400
Initial NSS support for TLS.
This is enough to get a chutney network to bootstrap, though a bunch
of work remains.
---
src/lib/tls/tortls.c | 122 +++++++++
src/lib/tls/tortls.h | 23 +-
src/lib/tls/tortls_internal.h | 16 +-
src/lib/tls/tortls_nss.c | 621 +++++++++++++++++++++++++++++++++---------
src/lib/tls/tortls_openssl.c | 137 +---------
src/lib/tls/tortls_st.h | 17 +-
src/test/test_tortls.c | 26 +-
7 files changed, 656 insertions(+), 306 deletions(-)
diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c
index 0b14b69f4..cc9738599 100644
--- a/src/lib/tls/tortls.c
+++ b/src/lib/tls/tortls.c
@@ -4,6 +4,7 @@
/* See LICENSE for licensing information */
#define TORTLS_PRIVATE
+#define TOR_X509_PRIVATE
#include "lib/tls/x509.h"
#include "lib/tls/x509_internal.h"
#include "lib/tls/tortls.h"
@@ -14,6 +15,8 @@
#include "lib/crypt_ops/crypto_rsa.h"
#include "lib/crypt_ops/crypto_rand.h"
+#include <time.h>
+
/** Global TLS contexts. We keep them here because nobody else needs
* to touch them.
*
@@ -31,6 +34,26 @@ tor_tls_context_get(int is_server)
return is_server ? server_tls_context : client_tls_context;
}
+/** Convert an errno (or a WSAerrno on windows) into a TOR_TLS_* error
+ * code. */
+int
+tor_errno_to_tls_error(int e)
+{
+ switch (e) {
+ case SOCK_ERRNO(ECONNRESET): // most common
+ return TOR_TLS_ERROR_CONNRESET;
+ case SOCK_ERRNO(ETIMEDOUT):
+ return TOR_TLS_ERROR_TIMEOUT;
+ case SOCK_ERRNO(EHOSTUNREACH):
+ case SOCK_ERRNO(ENETUNREACH):
+ return TOR_TLS_ERROR_NO_ROUTE;
+ case SOCK_ERRNO(ECONNREFUSED):
+ return TOR_TLS_ERROR_CONNREFUSED; // least common
+ default:
+ return TOR_TLS_ERROR_MISC;
+ }
+}
+
/** Set *<b>link_cert_out</b> and *<b>id_cert_out</b> to the link certificate
* and ID certificate that we're currently using for our V3 in-protocol
* handshake's certificate chain. If <b>server</b> is true, provide the certs
@@ -334,3 +357,102 @@ tor_tls_is_server(tor_tls_t *tls)
tor_assert(tls);
return tls->isServer;
}
+
+/** Release resources associated with a TLS object. Does not close the
+ * underlying file descriptor.
+ */
+void
+tor_tls_free_(tor_tls_t *tls)
+{
+ if (!tls)
+ return;
+ tor_assert(tls->ssl);
+ {
+ size_t r,w;
+ tor_tls_get_n_raw_bytes(tls,&r,&w); /* ensure written_by_tls is updated */
+ }
+ tor_tls_impl_free_(tls->ssl);
+ tls->ssl = NULL;
+#ifdef ENABLE_OPENSSL
+ tls->negotiated_callback = NULL;
+#endif
+ if (tls->context)
+ tor_tls_context_decref(tls->context);
+ tor_free(tls->address);
+ tls->magic = 0x99999999;
+ tor_free(tls);
+}
+
+/** If the provided tls connection is authenticated and has a
+ * certificate chain that is currently valid and signed, then set
+ * *<b>identity_key</b> to the identity certificate's key and return
+ * 0. Else, return -1 and log complaints with log-level <b>severity</b>.
+ */
+int
+tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity)
+{
+ tor_x509_cert_impl_t *cert = NULL, *id_cert = NULL;
+ tor_x509_cert_t *peer_x509 = NULL, *id_x509 = NULL;
+ tor_assert(tls);
+ tor_assert(identity);
+ int rv = -1;
+
+ try_to_extract_certs_from_tls(severity, tls, &cert, &id_cert);
+ if (!cert)
+ goto done;
+ if (!id_cert) {
+ log_fn(severity,LD_PROTOCOL,"No distinct identity certificate found");
+ goto done;
+ }
+ peer_x509 = tor_x509_cert_new(cert);
+ id_x509 = tor_x509_cert_new(id_cert);
+ cert = id_cert = NULL; /* Prevent double-free */
+
+ if (! tor_tls_cert_is_valid(severity, peer_x509, id_x509, time(NULL), 0)) {
+ goto done;
+ }
+
+ *identity = tor_tls_cert_get_key(id_x509);
+ rv = 0;
+
+ done:
+ if (cert)
+ tor_x509_cert_impl_free_(cert);
+ if (id_cert)
+ tor_x509_cert_impl_free_(id_cert);
+ tor_x509_cert_free(peer_x509);
+ tor_x509_cert_free(id_x509);
+
+ return rv;
+}
+
+/** Check whether the certificate set on the connection <b>tls</b> is expired
+ * give or take <b>past_tolerance</b> seconds, or not-yet-valid give or take
+ * <b>future_tolerance</b> seconds. Return 0 for valid, -1 for failure.
+ *
+ * NOTE: you should call tor_tls_verify before tor_tls_check_lifetime.
+ */
+int
+tor_tls_check_lifetime(int severity, tor_tls_t *tls,
+ time_t now,
+ int past_tolerance, int future_tolerance)
+{
+ tor_x509_cert_t *cert;
+ int r = -1;
+
+ if (!(cert = tor_tls_get_peer_cert(tls)))
+ goto done;
+
+ if (tor_x509_check_cert_lifetime_internal(severity, cert->cert, now,
+ past_tolerance,
+ future_tolerance) < 0)
+ goto done;
+
+ r = 0;
+ done:
+ tor_x509_cert_free(cert);
+ /* Not expected to get invoked */
+ tls_log_errors(tls, LOG_WARN, LD_NET, "checking certificate lifetime");
+
+ return r;
+}
diff --git a/src/lib/tls/tortls.h b/src/lib/tls/tortls.h
index 306d321cd..7bbb42b2f 100644
--- a/src/lib/tls/tortls.h
+++ b/src/lib/tls/tortls.h
@@ -13,10 +13,25 @@
#include "lib/crypt_ops/crypto_rsa.h"
#include "lib/testsupport/testsupport.h"
+#include "lib/net/nettypes.h"
/* Opaque structure to hold a TLS connection. */
typedef struct tor_tls_t tor_tls_t;
+#ifdef TORTLS_PRIVATE
+#ifdef ENABLE_OPENSSL
+struct ssl_st;
+struct ssl_ctx_st;
+struct ssl_session_st;
+typedef struct ssl_ctx_st tor_tls_context_impl_t;
+typedef struct ssl_st tor_tls_impl_t;
+#else
+struct PRFileDesc;
+typedef struct PRFileDesc tor_tls_context_impl_t;
+typedef struct PRFileDesc tor_tls_impl_t;
+#endif
+#endif
+
struct tor_x509_cert_t;
/* Possible return values for most tor_tls_* functions. */
@@ -73,7 +88,7 @@ int tor_tls_context_init(unsigned flags,
void tor_tls_context_incref(tor_tls_context_t *ctx);
void tor_tls_context_decref(tor_tls_context_t *ctx);
tor_tls_context_t *tor_tls_context_get(int is_server);
-tor_tls_t *tor_tls_new(int sock, int is_server);
+tor_tls_t *tor_tls_new(tor_socket_t sock, int is_server);
void tor_tls_set_logged_address(tor_tls_t *tls, const char *address);
void tor_tls_set_renegotiate_callback(tor_tls_t *tls,
void (*cb)(tor_tls_t *, void *arg),
@@ -121,13 +136,17 @@ MOCK_DECL(int,tor_tls_export_key_material,(
size_t context_len,
const char *label));
+#ifdef ENABLE_OPENSSL
/* Log and abort if there are unhandled TLS errors in OpenSSL's error stack.
*/
#define check_no_tls_errors() check_no_tls_errors_(__FILE__,__LINE__)
-
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);
+#else
+#define check_no_tls_errors() STMT_NIL
+#endif
int tor_tls_get_my_certs(int server,
const struct tor_x509_cert_t **link_cert_out,
diff --git a/src/lib/tls/tortls_internal.h b/src/lib/tls/tortls_internal.h
index 2920e9658..b9e01e0c5 100644
--- a/src/lib/tls/tortls_internal.h
+++ b/src/lib/tls/tortls_internal.h
@@ -6,15 +6,11 @@
#ifndef TORTLS_INTERNAL_H
#define TORTLS_INTERNAL_H
-#ifdef ENABLE_OPENSSL
-struct ssl_st;
-struct ssl_ctx_st;
-struct ssl_session_st;
-#endif
-
int tor_errno_to_tls_error(int e);
+#ifdef ENABLE_OPENSSL
int tor_tls_get_error(tor_tls_t *tls, int r, int extra,
const char *doing, int severity, int domain);
+#endif
MOCK_DECL(void, try_to_extract_certs_from_tls,
(int severity, tor_tls_t *tls,
tor_x509_cert_impl_t **cert_out,
@@ -31,13 +27,9 @@ int tor_tls_context_init_certificates(tor_tls_context_t *result,
crypto_pk_t *identity,
unsigned key_lifetime,
unsigned flags);
+void tor_tls_impl_free_(tor_tls_impl_t *ssl);
-#ifdef ENABLE_OPENSSL
-void tor_tls_context_impl_free(struct ssl_ctx_st *);
-#else
-struct ssl_ctx_st; // XXXX replace
-void tor_tls_context_impl_free(struct ssl_ctx_st *);
-#endif
+void tor_tls_context_impl_free(tor_tls_context_impl_t *);
#ifdef ENABLE_OPENSSL
tor_tls_t *tor_tls_get_by_ssl(const struct ssl_st *ssl);
diff --git a/src/lib/tls/tortls_nss.c b/src/lib/tls/tortls_nss.c
index 35dbc27d9..d2b81ad08 100644
--- a/src/lib/tls/tortls_nss.c
+++ b/src/lib/tls/tortls_nss.c
@@ -12,6 +12,7 @@
#include "orconfig.h"
#define TORTLS_PRIVATE
+#define TOR_X509_PRIVATE
#ifdef _WIN32 /*wrkard for dtls1.h >= 0.9.8m of "#include <winsock.h>"*/
#include <winsock2.h>
@@ -22,6 +23,9 @@
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_dh.h"
#include "lib/crypt_ops/crypto_util.h"
+#include "lib/crypt_ops/crypto_nss_mgt.h"
+#include "lib/string/printf.h"
+
#include "lib/tls/x509.h"
#include "lib/tls/x509_internal.h"
#include "lib/tls/tortls.h"
@@ -29,26 +33,16 @@
#include "lib/tls/tortls_internal.h"
#include "lib/log/util_bug.h"
-int
-tor_errno_to_tls_error(int e)
-{
- (void)e;
- // XXXX
- return -1;
-}
-int
-tor_tls_get_error(tor_tls_t *tls, int r, int extra,
- const char *doing, int severity, int domain)
-{
- (void)tls;
- (void)r;
- (void)extra;
- (void)doing;
- (void)severity;
- (void)domain;
- // XXXX
- return -1;
-}
+#include <prio.h>
+// For access to raw sockets.
+#include <private/pprio.h>
+#include <ssl.h>
+#include <sslt.h>
+#include <sslproto.h>
+#include <certt.h>
+
+static SECStatus always_accept_cert_cb(void *, PRFileDesc *, PRBool, PRBool);
+
MOCK_IMPL(void,
try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls,
tor_x509_cert_impl_t **cert_out,
@@ -57,14 +51,109 @@ try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls,
tor_assert(tls);
tor_assert(cert_out);
tor_assert(id_cert_out);
- (void)severity;
- // XXXX
+ (void) severity;
+
+ *cert_out = *id_cert_out = NULL;
+
+ CERTCertificate *peer = SSL_PeerCertificate(tls->ssl);
+ if (!peer)
+ return;
+ *cert_out = peer; /* Now owns pointer. */
+
+ CERTCertList *chain = SSL_PeerCertificateChain(tls->ssl);
+ CERTCertListNode *c = CERT_LIST_HEAD(chain);
+ for (; !CERT_LIST_END(c, chain); c = CERT_LIST_NEXT(c)) {
+ if (CERT_CompareCerts(c->cert, peer) == PR_FALSE) {
+ *id_cert_out = CERT_DupCertificate(c->cert);
+ break;
+ }
+ }
+ CERT_DestroyCertList(chain);
+}
+
+static bool
+we_like_ssl_cipher(SSLCipherAlgorithm ca)
+{
+ switch (ca) {
+ case ssl_calg_null: return false;
+ case ssl_calg_rc4: return false;
+ case ssl_calg_rc2: return false;
+ case ssl_calg_des: return false;
+ case ssl_calg_3des: return false; /* ???? */
+ case ssl_calg_idea: return false;
+ case ssl_calg_fortezza: return false;
+ case ssl_calg_camellia: return false;
+ case ssl_calg_seed: return false;
+
+ case ssl_calg_aes: return true;
+ case ssl_calg_aes_gcm: return true;
+ case ssl_calg_chacha20: return true;
+ default: return true;
+ }
+}
+static bool
+we_like_ssl_kea(SSLKEAType kt)
+{
+ switch (kt) {
+ case ssl_kea_null: return false;
+ case ssl_kea_rsa: return false; /* ??? */
+ case ssl_kea_fortezza: return false;
+ case ssl_kea_ecdh_psk: return false;
+ case ssl_kea_dh_psk: return false;
+
+ case ssl_kea_dh: return true;
+ case ssl_kea_ecdh: return true;
+ case ssl_kea_tls13_any: return true;
+
+ case ssl_kea_size: return true; /* prevent a warning. */
+ default: return true;
+ }
+}
+
+static bool
+we_like_mac_algorithm(SSLMACAlgorithm ma)
+{
+ switch (ma) {
+ case ssl_mac_null: return false;
+ case ssl_mac_md5: return false;
+ case ssl_hmac_md5: return false;
+
+ case ssl_mac_sha: return true;
+ case ssl_hmac_sha: return true;
+ case ssl_hmac_sha256: return true;
+ case ssl_mac_aead: return true;
+ case ssl_hmac_sha384: return true;
+ default: return true;
+ }
+}
+
+static bool
+we_like_auth_type(SSLAuthType at)
+{
+ switch (at) {
+ case ssl_auth_null: return false;
+ case ssl_auth_rsa_decrypt: return false;
+ case ssl_auth_dsa: return false;
+ case ssl_auth_kea: return false;
+
+ case ssl_auth_ecdsa: return true;
+ case ssl_auth_ecdh_rsa: return true;
+ case ssl_auth_ecdh_ecdsa: return true;
+ case ssl_auth_rsa_sign: return true;
+ case ssl_auth_rsa_pss: return true;
+ case ssl_auth_psk: return true;
+ case ssl_auth_tls13_any: return true;
+
+ case ssl_auth_size: return true; /* prevent a warning. */
+ default: return true;
+ }
}
tor_tls_context_t *
tor_tls_context_new(crypto_pk_t *identity,
unsigned int key_lifetime, unsigned flags, int is_client)
{
+ SECStatus s;
tor_assert(identity);
tor_tls_context_t *ctx = tor_malloc_zero(sizeof(tor_tls_context_t));
@@ -77,7 +166,128 @@ tor_tls_context_new(crypto_pk_t *identity,
}
}
- // XXXX write the main body.
+ {
+ /* Create the "model" PRFileDesc that we will use to base others on. */
+ PRFileDesc *tcp = PR_NewTCPSocket();
+ if (!tcp)
+ goto err;
+
+ ctx->ctx = SSL_ImportFD(NULL, tcp);
+ if (!ctx->ctx) {
+ PR_Close(tcp);
+ goto err;
+ }
+ }
+
+ // Configure the certificate.
+ if (!is_client) {
+ s = SSL_ConfigServerCert(ctx->ctx,
+ ctx->my_link_cert->cert,
+ (SECKEYPrivateKey *)
+ crypto_pk_get_nss_privkey(ctx->link_key),
+ NULL, /* ExtraServerCertData */
+ 0 /* DataLen */);
+ if (s != SECSuccess)
+ goto err;
+ }
+
+ // We need a certificate from the other side.
+ if (is_client) {
+ // XXXX does this do anything?
+ s = SSL_OptionSet(ctx->ctx, SSL_REQUIRE_CERTIFICATE, PR_TRUE);
+ if (s != SECSuccess)
+ goto err;
+ }
+
+ // Always accept other side's cert; we'll check it ourselves in goofy
+ // tor ways.
+ s = SSL_AuthCertificateHook(ctx->ctx, always_accept_cert_cb, NULL);
+
+ // We allow simultaneous read and write.
+ s = SSL_OptionSet(ctx->ctx, SSL_ENABLE_FDX, PR_TRUE);
+ if (s != SECSuccess)
+ goto err;
+ // XXXX SSL_ROLLBACK_DETECTION??
+ // XXXX SSL_ENABLE_ALPN??
+
+ // Force client-mode or server_mode.
+ s = SSL_OptionSet(ctx->ctx,
+ is_client ? SSL_HANDSHAKE_AS_CLIENT : SSL_HANDSHAKE_AS_SERVER,
+ PR_TRUE);
+ if (s != SECSuccess)
+ goto err;
+
+ // Disable everything before TLS 1.0; support everything else.
+ {
+ SSLVersionRange vrange;
+ memset(&vrange, 0, sizeof(vrange));
+ s = SSL_VersionRangeGetSupported(ssl_variant_stream, &vrange);
+ if (s != SECSuccess)
+ goto err;
+ if (vrange.min < SSL_LIBRARY_VERSION_TLS_1_0)
+ vrange.min = SSL_LIBRARY_VERSION_TLS_1_0;
+ s = SSL_VersionRangeSet(ctx->ctx, &vrange);
+ if (s != SECSuccess)
+ goto err;
+ }
+
+ // Only support strong ciphers.
+ {
+ const PRUint16 *ciphers = SSL_GetImplementedCiphers();
+ const PRUint16 n_ciphers = SSL_GetNumImplementedCiphers();
+ PRUint16 i;
+ for (i = 0; i < n_ciphers; ++i) {
+ SSLCipherSuiteInfo info;
+ memset(&info, 0, sizeof(info));
+ s = SSL_GetCipherSuiteInfo(ciphers[i], &info, sizeof(info));
+ if (s != SECSuccess)
+ goto err;
+ if (BUG(info.cipherSuite != ciphers[i]))
+ goto err;
+ int disable = info.effectiveKeyBits < 128 ||
+ info.macBits < 128 ||
+ !we_like_ssl_cipher(info.symCipher) ||
+ !we_like_ssl_kea(info.keaType) ||
+ !we_like_mac_algorithm(info.macAlgorithm) ||
+ !we_like_auth_type(info.authType)/* Requires NSS 3.24 */;
+
+ s = SSL_CipherPrefSet(ctx->ctx, ciphers[i],
+ disable ? PR_FALSE : PR_TRUE);
+ if (s != SECSuccess)
+ goto err;
+ }
+ }
+
+ // Only use DH and ECDH keys once.
+ s = SSL_OptionSet(ctx->ctx, SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE);
+ if (s != SECSuccess)
+ goto err;
+
+ // don't cache sessions.
+ s = SSL_OptionSet(ctx->ctx, SSL_NO_CACHE, PR_TRUE);
+ if (s != SECSuccess)
+ goto err;
+
+ // Enable DH.
+ s = SSL_OptionSet(ctx->ctx, SSL_ENABLE_SERVER_DHE, PR_TRUE);
+ if (s != SECSuccess)
+ goto err;
+
+ // Set DH and ECDH groups.
+ SSLNamedGroup groups[] = {
+ ssl_grp_ec_curve25519,
+ ssl_grp_ec_secp256r1,
+ ssl_grp_ec_secp224r1,
+ ssl_grp_ffdhe_2048,
+ };
+ s = SSL_NamedGroupConfig(ctx->ctx, groups, ARRAY_LENGTH(groups));
+ if (s != SECSuccess)
+ goto err;
+
+ // These features are off by default, so we don't need to disable them:
+ // Session tickets
+ // Renegotiation
+ // Compression
goto done;
err:
@@ -88,11 +298,9 @@ tor_tls_context_new(crypto_pk_t *identity,
}
void
-tor_tls_context_impl_free(struct ssl_ctx_st *ctx)
+tor_tls_context_impl_free(tor_tls_context_impl_t *ctx)
{
- (void)ctx;
- // XXXX
- // XXXX openssl type.
+ PR_Close(ctx);
}
void
@@ -101,33 +309,82 @@ tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz)
(void)tls;
(void)buf;
(void)sz;
- // XXXX
+ // AFAICT, NSS doesn't expose its internal state.
+ buf[0]=0;
}
void
tor_tls_init(void)
{
- // XXXX
+ /* We don't have any global setup to do yet, but that will change */
}
+
void
tls_log_errors(tor_tls_t *tls, int severity, int domain,
const char *doing)
{
+ /* XXXX This implementation isn't right for NSS -- it logs the last error
+ whether anything actually failed or not. */
+
(void)tls;
- (void)severity;
- (void)domain;
- (void)doing;
- // XXXX
+ PRErrorCode code = PORT_GetError();
+
+ const char *string = PORT_ErrorToString(code);
+ const char *name = PORT_ErrorToName(code);
+ char buf[16];
+ if (!string)
+ string = "<unrecognized>";
+ if (!name) {
+ tor_snprintf(buf, sizeof(buf), "%d", code);
+ name = buf;
+ }
+
+ if (doing) {
+ log_fn(severity, domain, "TLS error %s while %s: %s", name, doing, string);
+ } else {
+ log_fn(severity, domain, "TLS error %s: %s", name, string);
+ }
}
tor_tls_t *
-tor_tls_new(int sock, int is_server)
+tor_tls_new(tor_socket_t sock, int is_server)
{
(void)sock;
- (void)is_server;
- // XXXX
- return NULL;
+ tor_tls_context_t *ctx = tor_tls_context_get(is_server);
+
+ PRFileDesc *tcp = PR_ImportTCPSocket(sock);
+ if (!tcp)
+ return NULL;
+
+ PRFileDesc *ssl = SSL_ImportFD(ctx->ctx, tcp);
+ if (!ssl) {
+ PR_Close(tcp);
+ return NULL;
+ }
+
+ tor_tls_t *tls = tor_malloc_zero(sizeof(tor_tls_t));
+ tls->magic = TOR_TLS_MAGIC;
+ tls->context = ctx;
+ tor_tls_context_incref(ctx);
+ tls->ssl = ssl;
+ tls->socket = sock;
+ tls->state = TOR_TLS_ST_HANDSHAKE;
+ tls->isServer = !!is_server;
+
+ if (!is_server) {
+ /* Set a random SNI */
+ char *fake_hostname = crypto_random_hostname(4,25, "www.",".com");
+ SSL_SetURL(tls->ssl, fake_hostname);
+ tor_free(fake_hostname);
+ }
+ SECStatus s = SSL_ResetHandshake(ssl, is_server ? PR_TRUE : PR_FALSE);
+ if (s != SECSuccess) {
+ crypto_nss_log_errors(LOG_WARN, "resetting handshake state");
+ }
+
+ return tls;
}
+
void
tor_tls_set_renegotiate_callback(tor_tls_t *tls,
void (*cb)(tor_tls_t *, void *arg),
@@ -136,131 +393,175 @@ tor_tls_set_renegotiate_callback(tor_tls_t *tls,
tor_assert(tls);
(void)cb;
(void)arg;
- // XXXX;
+
+ /* We don't support renegotiation-based TLS with NSS. */
}
void
-tor_tls_free_(tor_tls_t *tls)
+tor_tls_impl_free_(tor_tls_impl_t *tls)
{
- (void)tls;
- // XXXX
+ // XXXX This will close the underlying fd, which our OpenSSL version does
+ // not do!
+
+ PR_Close(tls);
}
int
tor_tls_peer_has_cert(tor_tls_t *tls)
{
- (void)tls;
- // XXXX
- return -1;
+ CERTCertificate *cert = SSL_PeerCertificate(tls->ssl);
+ int result = (cert != NULL);
+ CERT_DestroyCertificate(cert);
+ return result;
}
+
MOCK_IMPL(tor_x509_cert_t *,
tor_tls_get_peer_cert,(tor_tls_t *tls))
{
- tor_assert(tls);
- // XXXX
- return NULL;
+ CERTCertificate *cert = SSL_PeerCertificate(tls->ssl);
+ if (cert)
+ return tor_x509_cert_new(cert);
+ else
+ return NULL;
}
+
MOCK_IMPL(tor_x509_cert_t *,
tor_tls_get_own_cert,(tor_tls_t *tls))
{
tor_assert(tls);
- // XXXX
- return NULL;
-}
-int
-tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity)
-{
- tor_assert(tls);
- tor_assert(identity);
- (void)severity;
- // XXXX
- return -1;
-}
-int
-tor_tls_check_lifetime(int severity,
- tor_tls_t *tls, time_t now,
- int past_tolerance,
- int future_tolerance)
-{
- tor_assert(tls);
- (void)severity;
- (void)now;
- (void)past_tolerance;
- (void)future_tolerance;
- // XXXX
- return -1;
+ CERTCertificate *cert = SSL_LocalCertificate(tls->ssl);
+ if (cert)
+ return tor_x509_cert_new(cert);
+ else
+ return NULL;
}
+
MOCK_IMPL(int,
tor_tls_read, (tor_tls_t *tls, char *cp, size_t len))
{
tor_assert(tls);
tor_assert(cp);
- (void)len;
- // XXXX
- return -1;
+ tor_assert(len < INT_MAX);
+
+ PRInt32 rv = PR_Read(tls->ssl, cp, (int)len);
+ // log_debug(LD_NET, "PR_Read(%zu) returned %d", n, (int)rv);
+ if (rv > 0) {
+ tls->n_read_since_last_check += rv;
+ return rv;
+ }
+ if (rv == 0)
+ return TOR_TLS_CLOSE;
+ PRErrorCode err = PORT_GetError();
+ if (err == PR_WOULD_BLOCK_ERROR) {
+ return TOR_TLS_WANTREAD; // XXXX ????
+ } else {
+ crypto_nss_log_errors(LOG_NOTICE, "reading"); // XXXX
+ return TOR_TLS_ERROR_MISC; // ????
+ }
}
+
int
tor_tls_write(tor_tls_t *tls, const char *cp, size_t n)
{
tor_assert(tls);
- tor_assert(cp);
- (void)n;
- // XXXX
- return -1;
+ tor_assert(cp || n == 0);
+ tor_assert(n < INT_MAX);
+
+ PRInt32 rv = PR_Write(tls->ssl, cp, (int)n);
+ // log_debug(LD_NET, "PR_Write(%zu) returned %d", n, (int)rv);
+ if (rv > 0) {
+ tls->n_written_since_last_check += rv;
+ return rv;
+ }
+ if (rv == 0)
+ return TOR_TLS_ERROR_MISC;
+ PRErrorCode err = PORT_GetError();
+
+ if (err == PR_WOULD_BLOCK_ERROR) {
+ return TOR_TLS_WANTWRITE; // XXXX ????
+ } else {
+ crypto_nss_log_errors(LOG_NOTICE, "writing"); // XXXX
+ return TOR_TLS_ERROR_MISC; // ????
+ }
}
+
int
tor_tls_handshake(tor_tls_t *tls)
{
tor_assert(tls);
- // XXXX
- return -1;
+ tor_assert(tls->state == TOR_TLS_ST_HANDSHAKE);
+
+ SECStatus s = SSL_ForceHandshake(tls->ssl);
+ if (s == SECSuccess) {
+ tls->state = TOR_TLS_ST_OPEN;
+ log_debug(LD_NET, "SSL handshake is supposedly complete.");
+ return tor_tls_finish_handshake(tls);
+ }
+ if (PORT_GetError() == PR_WOULD_BLOCK_ERROR)
+ return TOR_TLS_WANTREAD; /* XXXX What about wantwrite? */
+
+ return TOR_TLS_ERROR_MISC; // XXXX
}
+
int
tor_tls_finish_handshake(tor_tls_t *tls)
{
tor_assert(tls);
- // XXXX
- return -1;
+ // We don't need to do any of the weird handshake nonsense stuff on NSS,
+ // since we only support recent handshakes.
+ return TOR_TLS_DONE;
}
+
void
tor_tls_unblock_renegotiation(tor_tls_t *tls)
{
tor_assert(tls);
- // XXXX
+ /* We don't support renegotiation with NSS. */
}
+
void
tor_tls_block_renegotiation(tor_tls_t *tls)
{
tor_assert(tls);
- // XXXX
+ /* We don't support renegotiation with NSS. */
}
+
void
tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls)
{
tor_assert(tls);
- // XXXX
+ /* We don't support renegotiation with NSS. */
}
+
int
tor_tls_shutdown(tor_tls_t *tls)
{
tor_assert(tls);
- // XXXX
+ /* XXXX This is not actually used, so I'm not implementing it. We can
+ * XXXX remove this function entirely someday. */
return -1;
}
+
int
tor_tls_get_pending_bytes(tor_tls_t *tls)
{
tor_assert(tls);
- // XXXX
- return -1;
+ int n = SSL_DataPending(tls->ssl);
+ if (n < 0) {
+ crypto_nss_log_errors(LOG_WARN, "Looking up pending bytes");
+ return 0;
+ }
+ return (int)n;
}
+
size_t
tor_tls_get_forced_write_size(tor_tls_t *tls)
{
tor_assert(tls);
- // XXXX
+ /* NSS doesn't have the same "forced write" restriction as openssl. */
return 0;
}
+
void
tor_tls_get_n_raw_bytes(tor_tls_t *tls,
size_t *n_read, size_t *n_written)
@@ -268,7 +569,13 @@ tor_tls_get_n_raw_bytes(tor_tls_t *tls,
tor_assert(tls);
tor_assert(n_read);
tor_assert(n_written);
- // XXXX
+ /* XXXX We don't curently have a way to measure this information correctly
+ * in NSS; we could do that with a PRIO layer, but it'll take a little
+ * coding. For now, we just track the number of bytes sent _in_ the TLS
+ * stream. Doing this will make our rate-limiting slightly inaccurate. */
+ *n_read = tls->n_read_since_last_check;
+ *n_written = tls->n_written_since_last_check;
+ tls->n_read_since_last_check = tls->n_written_since_last_check = 0;
}
int
@@ -281,54 +588,70 @@ tor_tls_get_buffer_sizes(tor_tls_t *tls,
tor_assert(rbuf_bytes);
tor_assert(wbuf_capacity);
tor_assert(wbuf_bytes);
- // XXXX
+
+ /* This is an acceptable way to say "we can't measure this." */
return -1;
}
+
MOCK_IMPL(double,
tls_get_write_overhead_ratio, (void))
{
- // XXXX
- return 0.0;
+ /* XXX We don't currently have a way to measure this in NSS; we could do that
+ * XXX with a PRIO layer, but it'll take a little coding. */
+ return 0.95;
}
int
tor_tls_used_v1_handshake(tor_tls_t *tls)
{
tor_assert(tls);
- // XXXX
- return -1;
-}
-int
-tor_tls_get_num_server_handshakes(tor_tls_t *tls)
-{
- tor_assert(tls);
- // XXXX
- return -1;
+ /* We don't support or allow the V1 handshake with NSS.
+ */
+ return 0;
}
+
int
tor_tls_server_got_renegotiate(tor_tls_t *tls)
{
tor_assert(tls);
- // XXXX
- return -1;
+ return 0; /* We don't support renegotiation with NSS */
}
+
MOCK_IMPL(int,
tor_tls_cert_matches_key,(const tor_tls_t *tls,
const struct tor_x509_cert_t *cert))
{
tor_assert(tls);
tor_assert(cert);
- // XXXX
- return 0;
+ int rv = 0;
+
+ CERTCertificate *peercert = SSL_PeerCertificate(tls->ssl);
+ if (!peercert)
+ goto done;
+ CERTSubjectPublicKeyInfo *peer_info = &peercert->subjectPublicKeyInfo;
+ CERTSubjectPublicKeyInfo *cert_info = &cert->cert->subjectPublicKeyInfo;
+ rv = SECOID_CompareAlgorithmID(&peer_info->algorithm,
+ &cert_info->algorithm) == 0 &&
+ SECITEM_ItemsAreEqual(&peer_info->subjectPublicKey,
+ &cert_info->subjectPublicKey);
+
+ done:
+ if (peercert)
+ CERT_DestroyCertificate(peercert);
+ return rv;
}
+
MOCK_IMPL(int,
tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out))
{
tor_assert(tls);
tor_assert(secrets_out);
- // XXXX
+
+ /* There's no way to get this information out of NSS. */
+
return -1;
}
+
MOCK_IMPL(int,
tor_tls_export_key_material,(tor_tls_t *tls, uint8_t *secrets_out,
const uint8_t *context,
@@ -339,42 +662,72 @@ tor_tls_export_key_material,(tor_tls_t *tls, uint8_t *secrets_out,
tor_assert(secrets_out);
tor_assert(context);
tor_assert(label);
- (void)context_len;
- // XXXX
- return -1;
-}
+ tor_assert(strlen(label) <= UINT_MAX);
+ tor_assert(context_len <= UINT_MAX);
-void
-check_no_tls_errors_(const char *fname, int line)
-{
- (void)fname;
- (void)line;
- // XXXX
-}
-void
-tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
- int severity, int domain, const char *doing)
-{
- tor_assert(tls);
- (void)err;
- (void)severity;
- (void)domain;
- (void)doing;
- // XXXX
+ SECStatus s;
+ s = SSL_ExportKeyingMaterial(tls->ssl,
+ label, (unsigned)strlen(label),
+ PR_TRUE, context, (unsigned)context_len,
+ secrets_out, DIGEST256_LEN);
+
+ return (s == SECSuccess) ? 0 : -1;
}
const char *
tor_tls_get_ciphersuite_name(tor_tls_t *tls)
{
tor_assert(tls);
- // XXXX
- return NULL;
+
+ SSLChannelInfo channel_info;
+ SSLCipherSuiteInfo cipher_info;
+
+ memset(&channel_info, 0, sizeof(channel_info));
+ memset(&cipher_info, 0, sizeof(cipher_info));
+
+ SECStatus s = SSL_GetChannelInfo(tls->ssl,
+ &channel_info, sizeof(channel_info));
+ if (s != SECSuccess)
+ return NULL;
+
+ s = SSL_GetCipherSuiteInfo(channel_info.cipherSuite,
+ &cipher_info, sizeof(cipher_info));
+ if (s != SECSuccess)
+ return NULL;
+
+ return cipher_info.cipherSuiteName;
}
+/** The group we should use for ecdhe when none was selected. */
+#define SEC_OID_TOR_DEFAULT_ECDHE_GROUP SEC_OID_ANSIX962_EC_PRIME256V1
+
int
evaluate_ecgroup_for_tls(const char *ecgroup)
{
- (void)ecgroup;
- // XXXX
- return -1;
+ SECOidTag tag;
+
+ if (!ecgroup)
+ tag = SEC_OID_TOR_DEFAULT_ECDHE_GROUP;
+ else if (!strcasecmp(ecgroup, "P256"))
+ tag = SEC_OID_ANSIX962_EC_PRIME256V1;
+ else if (!strcasecmp(ecgroup, "P224"))
+ tag = SEC_OID_SECG_EC_SECP224R1;
+ else
+ return 0;
+
+ /* I don't think we need any additional tests here for NSS */
+ (void) tag;
+
+ return 1;
+}
+
+static SECStatus
+always_accept_cert_cb(void *arg, PRFileDesc *ssl, PRBool checkSig,
+ PRBool isServer)
+{
+ (void)arg;
+ (void)ssl;
+ (void)checkSig;
+ (void)isServer;
+ return SECSuccess;
}
diff --git a/src/lib/tls/tortls_openssl.c b/src/lib/tls/tortls_openssl.c
index fb6328bcd..c4e9e7770 100644
--- a/src/lib/tls/tortls_openssl.c
+++ b/src/lib/tls/tortls_openssl.c
@@ -244,26 +244,6 @@ tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing)
}
}
-/** Convert an errno (or a WSAerrno on windows) into a TOR_TLS_* error
- * code. */
-int
-tor_errno_to_tls_error(int e)
-{
- switch (e) {
- case SOCK_ERRNO(ECONNRESET): // most common
- return TOR_TLS_ERROR_CONNRESET;
- case SOCK_ERRNO(ETIMEDOUT):
- return TOR_TLS_ERROR_TIMEOUT;
- case SOCK_ERRNO(EHOSTUNREACH):
- case SOCK_ERRNO(ENETUNREACH):
- return TOR_TLS_ERROR_NO_ROUTE;
- case SOCK_ERRNO(ECONNREFUSED):
- return TOR_TLS_ERROR_CONNREFUSED; // least common
- default:
- return TOR_TLS_ERROR_MISC;
- }
-}
-
#define CATCH_SYSCALL 1
#define CATCH_ZERO 2
@@ -952,8 +932,6 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
/* Check whether we're watching for renegotiates. If so, this is one! */
if (tls->negotiated_callback)
tls->got_renegotiate = 1;
- if (tls->server_handshake_count < 127) /*avoid any overflow possibility*/
- ++tls->server_handshake_count;
} else {
log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!");
return;
@@ -1167,30 +1145,13 @@ tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls)
#endif /* defined(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) && ... */
}
-/** Release resources associated with a TLS object. Does not close the
- * underlying file descriptor.
- */
void
-tor_tls_free_(tor_tls_t *tls)
+tor_tls_impl_free_(tor_tls_impl_t *ssl)
{
- if (!tls)
- return;
- tor_assert(tls->ssl);
- {
- size_t r,w;
- tor_tls_get_n_raw_bytes(tls,&r,&w); /* ensure written_by_tls is updated */
- }
#ifdef SSL_set_tlsext_host_name
- SSL_set_tlsext_host_name(tls->ssl, NULL);
+ SSL_set_tlsext_host_name(ssl, NULL);
#endif
- SSL_free(tls->ssl);
- tls->ssl = NULL;
- tls->negotiated_callback = NULL;
- if (tls->context)
- tor_tls_context_decref(tls->context);
- tor_free(tls->address);
- tls->magic = 0x99999999;
- tor_free(tls);
+ SSL_free(ssl);
}
/** Underlying function for TLS reading. Reads up to <b>len</b>
@@ -1509,90 +1470,6 @@ try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls,
*id_cert_out = id_cert;
}
-/** If the provided tls connection is authenticated and has a
- * certificate chain that is currently valid and signed, then set
- * *<b>identity_key</b> to the identity certificate's key and return
- * 0. Else, return -1 and log complaints with log-level <b>severity</b>.
- */
-int
-tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity_key)
-{
- X509 *cert = NULL, *id_cert = NULL;
- EVP_PKEY *id_pkey = NULL;
- RSA *rsa;
- int r = -1;
-
- check_no_tls_errors();
- *identity_key = NULL;
-
- try_to_extract_certs_from_tls(severity, tls, &cert, &id_cert);
- if (!cert)
- goto done;
- if (!id_cert) {
- log_fn(severity,LD_PROTOCOL,"No distinct identity certificate found");
- goto done;
- }
- tls_log_errors(tls, severity, LD_HANDSHAKE, "before verifying certificate");
-
- if (!(id_pkey = X509_get_pubkey(id_cert)) ||
- X509_verify(cert, id_pkey) <= 0) {
- log_fn(severity,LD_PROTOCOL,"X509_verify on cert and pkey returned <= 0");
- tls_log_errors(tls, severity, LD_HANDSHAKE, "verifying certificate");
- goto done;
- }
-
- rsa = EVP_PKEY_get1_RSA(id_pkey);
- if (!rsa)
- goto done;
- *identity_key = crypto_new_pk_from_openssl_rsa_(rsa);
-
- r = 0;
-
- done:
- if (cert)
- X509_free(cert);
- if (id_pkey)
- EVP_PKEY_free(id_pkey);
-
- /* This should never get invoked, but let's make sure in case OpenSSL
- * acts unexpectedly. */
- tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "finishing tor_tls_verify");
-
- return r;
-}
-
-/** Check whether the certificate set on the connection <b>tls</b> is expired
- * give or take <b>past_tolerance</b> seconds, or not-yet-valid give or take
- * <b>future_tolerance</b> seconds. Return 0 for valid, -1 for failure.
- *
- * NOTE: you should call tor_tls_verify before tor_tls_check_lifetime.
- */
-int
-tor_tls_check_lifetime(int severity, tor_tls_t *tls,
- time_t now,
- int past_tolerance, int future_tolerance)
-{
- X509 *cert;
- int r = -1;
-
- if (!(cert = SSL_get_peer_certificate(tls->ssl)))
- goto done;
-
- if (tor_x509_check_cert_lifetime_internal(severity, cert, now,
- past_tolerance,
- future_tolerance) < 0)
- goto done;
-
- r = 0;
- done:
- if (cert)
- X509_free(cert);
- /* Not expected to get invoked */
- tls_log_errors(tls, LOG_WARN, LD_NET, "checking certificate lifetime");
-
- return r;
-}
-
/** Return the number of bytes available for reading from <b>tls</b>.
*/
int
@@ -1690,14 +1567,6 @@ tor_tls_used_v1_handshake(tor_tls_t *tls)
return ! tls->wasV2Handshake;
}
-/** Return the number of server handshakes that we've noticed doing on
- * <b>tls</b>. */
-int
-tor_tls_get_num_server_handshakes(tor_tls_t *tls)
-{
- return tls->server_handshake_count;
-}
-
/** Return true iff the server TLS connection <b>tls</b> got the renegotiation
* request it was waiting for. */
int
diff --git a/src/lib/tls/tortls_st.h b/src/lib/tls/tortls_st.h
index 897be497e..a1b59a37a 100644
--- a/src/lib/tls/tortls_st.h
+++ b/src/lib/tls/tortls_st.h
@@ -6,6 +6,8 @@
#ifndef TOR_TORTLS_ST_H
#define TOR_TORTLS_ST_H
+#include "lib/net/socket.h"
+
#define TOR_TLS_MAGIC 0x71571571
typedef enum {
@@ -17,7 +19,7 @@ typedef enum {
struct tor_tls_context_t {
int refcnt;
- struct ssl_ctx_st *ctx;
+ tor_tls_context_impl_t *ctx;
struct tor_x509_cert_t *my_link_cert;
struct tor_x509_cert_t *my_id_cert;
struct tor_x509_cert_t *my_auth_cert;
@@ -31,8 +33,9 @@ struct tor_tls_context_t {
struct tor_tls_t {
uint32_t magic;
tor_tls_context_t *context; /** A link to the context object for this tls. */
- struct ssl_st *ssl; /**< An OpenSSL SSL object. */
- int socket; /**< The underlying file descriptor for this TLS connection. */
+ tor_tls_impl_t *ssl; /**< An OpenSSL SSL object or NSS PRFileDesc. */
+ tor_socket_t socket; /**< The underlying file descriptor for this TLS
+ * connection. */
char *address; /**< An address to log when describing this connection. */
tor_tls_state_bitfield_t state : 3; /**< The current SSL state,
* depending on which operations
@@ -45,11 +48,10 @@ struct tor_tls_t {
* one certificate). */
/** True iff we should call negotiated_callback when we're done reading. */
unsigned int got_renegotiate:1;
+#ifdef ENABLE_OPENSSL
/** Return value from tor_tls_classify_client_ciphers, or 0 if we haven't
* called that function yet. */
int8_t client_cipher_list_type;
- /** Incremented every time we start the server side of a handshake. */
- uint8_t server_handshake_count;
size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last
* time. */
/** Last values retrieved from BIO_number_read()/write(); see
@@ -62,6 +64,11 @@ struct tor_tls_t {
void (*negotiated_callback)(tor_tls_t *tls, void *arg);
/** Argument to pass to negotiated_callback. */
void *callback_arg;
+#endif
+#ifdef ENABLE_NSS
+ size_t n_read_since_last_check;
+ size_t n_written_since_last_check;
+#endif
};
#endif
diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c
index eedf0dd3c..0e4b5afaf 100644
--- a/src/test/test_tortls.c
+++ b/src/test/test_tortls.c
@@ -72,6 +72,7 @@ test_tortls_err_to_string(void *data)
(void)1;
}
+#ifdef ENABLE_OPENSSL
static int
mock_tls_cert_matches_key(const tor_tls_t *tls, const tor_x509_cert_t *cert)
{
@@ -105,6 +106,7 @@ test_tortls_tor_tls_get_error(void *data)
crypto_pk_free(key2);
tor_tls_free(tls);
}
+#endif
static void
test_tortls_x509_cert_get_id_digests(void *ignored)
@@ -165,6 +167,7 @@ test_tortls_get_my_certs(void *ignored)
(void)1;
}
+#ifdef ENABLE_OPENSSL
static void
test_tortls_get_forced_write_size(void *ignored)
{
@@ -204,23 +207,6 @@ test_tortls_used_v1_handshake(void *ignored)
}
static void
-test_tortls_get_num_server_handshakes(void *ignored)
-{
- (void)ignored;
- int ret;
- tor_tls_t *tls;
-
- tls = tor_malloc_zero(sizeof(tor_tls_t));
-
- tls->server_handshake_count = 3;
- ret = tor_tls_get_num_server_handshakes(tls);
- tt_int_op(ret, OP_EQ, 3);
-
- done:
- tor_free(tls);
-}
-
-static void
test_tortls_server_got_renegotiate(void *ignored)
{
(void)ignored;
@@ -236,6 +222,7 @@ test_tortls_server_got_renegotiate(void *ignored)
done:
tor_free(tls);
}
+#endif
static void
test_tortls_evaluate_ecgroup_for_tls(void *ignored)
@@ -266,13 +253,14 @@ test_tortls_evaluate_ecgroup_for_tls(void *ignored)
struct testcase_t tortls_tests[] = {
LOCAL_TEST_CASE(errno_to_tls_error, 0),
LOCAL_TEST_CASE(err_to_string, 0),
- LOCAL_TEST_CASE(tor_tls_get_error, 0),
LOCAL_TEST_CASE(x509_cert_get_id_digests, 0),
LOCAL_TEST_CASE(get_my_certs, TT_FORK),
+#ifdef ENABLE_OPENSSL
+ LOCAL_TEST_CASE(tor_tls_get_error, 0),
LOCAL_TEST_CASE(get_forced_write_size, 0),
LOCAL_TEST_CASE(used_v1_handshake, TT_FORK),
- LOCAL_TEST_CASE(get_num_server_handshakes, 0),
LOCAL_TEST_CASE(server_got_renegotiate, 0),
+#endif
LOCAL_TEST_CASE(evaluate_ecgroup_for_tls, 0),
END_OF_TESTCASES
};
1
0
commit c567b8fcb4e4851d6db19946cce8c4d5e75535f5
Author: Nick Mathewson <nickm(a)torproject.org>
Date: Mon Aug 13 14:54:35 2018 -0400
NSS support for x509 certs
7 unit tests are failing at this point, but they're all TLS-related.
---
src/lib/crypt_ops/crypto_nss_mgt.c | 9 +
src/lib/crypt_ops/crypto_rsa.h | 10 +
src/lib/crypt_ops/crypto_rsa_nss.c | 27 +++
src/lib/tls/tortls.c | 157 ++++++++++++++++
src/lib/tls/tortls_internal.h | 4 +
src/lib/tls/tortls_nss.c | 61 +++----
src/lib/tls/tortls_openssl.c | 157 ++--------------
src/lib/tls/x509.h | 6 -
src/lib/tls/x509_internal.h | 17 ++
src/lib/tls/x509_nss.c | 362 ++++++++++++++++++++++++++++++++++---
src/lib/tls/x509_openssl.c | 3 +-
src/test/test_link_handshake.c | 24 ++-
12 files changed, 620 insertions(+), 217 deletions(-)
diff --git a/src/lib/crypt_ops/crypto_nss_mgt.c b/src/lib/crypt_ops/crypto_nss_mgt.c
index 85b18e00c..187f556bd 100644
--- a/src/lib/crypt_ops/crypto_nss_mgt.c
+++ b/src/lib/crypt_ops/crypto_nss_mgt.c
@@ -69,6 +69,15 @@ crypto_nss_early_init(void)
crypto_nss_log_errors(LOG_ERR, "setting cipher policy");
tor_assert_unreached();
}
+
+ /* We need to override the default here, or NSS will reject all the
+ * legacy Tor certificates. */
+ SECStatus rv = NSS_OptionSet(NSS_RSA_MIN_KEY_SIZE, 1024);
+ if (rv != SECSuccess) {
+ log_err(LD_CRYPTO, "Unable to set NSS min RSA key size");
+ crypto_nss_log_errors(LOG_ERR, "setting cipher option.");
+ tor_assert_unreached();
+ }
}
void
diff --git a/src/lib/crypt_ops/crypto_rsa.h b/src/lib/crypt_ops/crypto_rsa.h
index afc6c4201..aaf32ec1b 100644
--- a/src/lib/crypt_ops/crypto_rsa.h
+++ b/src/lib/crypt_ops/crypto_rsa.h
@@ -121,6 +121,16 @@ MOCK_DECL(struct evp_pkey_st *, crypto_pk_get_openssl_evp_pkey_,(
crypto_pk_t *env,int private));
#endif
+#ifdef ENABLE_NSS
+struct SECKEYPublicKeyStr;
+struct SECKEYPrivateKeyStr;
+crypto_pk_t *crypto_pk_new_from_nss_pubkey(struct SECKEYPublicKeyStr *pub);
+const struct SECKEYPublicKeyStr *crypto_pk_get_nss_pubkey(
+ const crypto_pk_t *key);
+const struct SECKEYPrivateKeyStr *crypto_pk_get_nss_privkey(
+ const crypto_pk_t *key);
+#endif
+
void crypto_pk_assign_public(crypto_pk_t *dest, const crypto_pk_t *src);
void crypto_pk_assign_private(crypto_pk_t *dest, const crypto_pk_t *src);
diff --git a/src/lib/crypt_ops/crypto_rsa_nss.c b/src/lib/crypt_ops/crypto_rsa_nss.c
index 0411687b9..517faa5c7 100644
--- a/src/lib/crypt_ops/crypto_rsa_nss.c
+++ b/src/lib/crypt_ops/crypto_rsa_nss.c
@@ -47,6 +47,33 @@ crypto_pk_key_is_private(const crypto_pk_t *key)
return key && key->seckey;
}
+/** used by tortls.c: wrap a SecKEYPublicKey in a crypto_pk_t. Take ownership
+ * of the RSA object. */
+crypto_pk_t *
+crypto_pk_new_from_nss_pubkey(struct SECKEYPublicKeyStr *pub)
+{
+ crypto_pk_t *result = tor_malloc_zero(sizeof(crypto_pk_t));
+ result->pubkey = pub;
+ return result;
+}
+
+/** Return the SECKEYPublicKey for the provided crypto_pk_t. */
+const SECKEYPublicKey *
+crypto_pk_get_nss_pubkey(const crypto_pk_t *key)
+{
+ tor_assert(key);
+ return key->pubkey;
+}
+
+/** Return the SECKEYPrivateKey for the provided crypto_pk_t, or NULL if it
+ * does not exist. */
+const SECKEYPrivateKey *
+crypto_pk_get_nss_privkey(const crypto_pk_t *key)
+{
+ tor_assert(key);
+ return key->seckey;
+}
+
#ifdef ENABLE_OPENSSL
/** used by tortls.c: wrap an RSA* in a crypto_pk_t. Take ownership of the
* RSA object. */
diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c
index 395f0148e..0b14b69f4 100644
--- a/src/lib/tls/tortls.c
+++ b/src/lib/tls/tortls.c
@@ -5,11 +5,14 @@
#define TORTLS_PRIVATE
#include "lib/tls/x509.h"
+#include "lib/tls/x509_internal.h"
#include "lib/tls/tortls.h"
#include "lib/tls/tortls_st.h"
#include "lib/tls/tortls_internal.h"
#include "lib/log/util_bug.h"
#include "lib/intmath/cmp.h"
+#include "lib/crypt_ops/crypto_rsa.h"
+#include "lib/crypt_ops/crypto_rand.h"
/** Global TLS contexts. We keep them here because nobody else needs
* to touch them.
@@ -28,6 +31,39 @@ tor_tls_context_get(int is_server)
return is_server ? server_tls_context : client_tls_context;
}
+/** Set *<b>link_cert_out</b> and *<b>id_cert_out</b> to the link certificate
+ * and ID certificate that we're currently using for our V3 in-protocol
+ * handshake's certificate chain. If <b>server</b> is true, provide the certs
+ * that we use in server mode (auth, ID); otherwise, provide the certs that we
+ * use in client mode. (link, ID) */
+int
+tor_tls_get_my_certs(int server,
+ const tor_x509_cert_t **link_cert_out,
+ const tor_x509_cert_t **id_cert_out)
+{
+ tor_tls_context_t *ctx = tor_tls_context_get(server);
+ if (! ctx)
+ return -1;
+ if (link_cert_out)
+ *link_cert_out = server ? ctx->my_link_cert : ctx->my_auth_cert;
+ if (id_cert_out)
+ *id_cert_out = ctx->my_id_cert;
+ return 0;
+}
+
+/**
+ * Return the authentication key that we use to authenticate ourselves as a
+ * client in the V3 in-protocol handshake.
+ */
+crypto_pk_t *
+tor_tls_get_my_client_auth_key(void)
+{
+ tor_tls_context_t *context = tor_tls_context_get(0);
+ if (! context)
+ return NULL;
+ return context->auth_key;
+}
+
/** Increase the reference count of <b>ctx</b>. */
void
tor_tls_context_incref(tor_tls_context_t *ctx)
@@ -158,6 +194,127 @@ tor_tls_context_init(unsigned flags,
return MIN(rv1, rv2);
}
+/** Create a new global TLS context.
+ *
+ * You can call this function multiple times. Each time you call it,
+ * it generates new certificates; all new connections will use
+ * the new SSL context.
+ */
+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)
+{
+ tor_tls_context_t *new_ctx = tor_tls_context_new(identity,
+ key_lifetime,
+ flags,
+ is_client);
+ tor_tls_context_t *old_ctx = *ppcontext;
+
+ if (new_ctx != NULL) {
+ *ppcontext = new_ctx;
+
+ /* Free the old context if one existed. */
+ if (old_ctx != NULL) {
+ /* This is safe even if there are open connections: we reference-
+ * count tor_tls_context_t objects. */
+ tor_tls_context_decref(old_ctx);
+ }
+ }
+
+ return ((new_ctx != NULL) ? 0 : -1);
+}
+
+/** Size of the RSA key to use for our TLS link keys */
+#define RSA_LINK_KEY_BITS 2048
+
+/** How long do identity certificates live? (sec) */
+#define IDENTITY_CERT_LIFETIME (365*24*60*60)
+
+/**
+ * Initialize the certificates and keys for a TLS context <b>result</b>
+ *
+ * Other arguments as for tor_tls_context_new().
+ */
+int
+tor_tls_context_init_certificates(tor_tls_context_t *result,
+ crypto_pk_t *identity,
+ unsigned key_lifetime,
+ unsigned flags)
+{
+ (void)flags;
+ int rv = -1;
+ char *nickname = NULL, *nn2 = NULL;
+ crypto_pk_t *rsa = NULL, *rsa_auth = NULL;
+ tor_x509_cert_impl_t *cert = NULL, *idcert = NULL, *authcert = NULL;
+
+ nickname = crypto_random_hostname(8, 20, "www.", ".net");
+
+#ifdef DISABLE_V3_LINKPROTO_SERVERSIDE
+ nn2 = crypto_random_hostname(8, 20, "www.", ".net");
+#else
+ nn2 = crypto_random_hostname(8, 20, "www.", ".com");
+#endif
+
+ /* Generate short-term RSA key for use with TLS. */
+ if (!(rsa = crypto_pk_new()))
+ goto error;
+ if (crypto_pk_generate_key_with_bits(rsa, RSA_LINK_KEY_BITS)<0)
+ goto error;
+
+ /* Generate short-term RSA key for use in the in-protocol ("v3")
+ * authentication handshake. */
+ if (!(rsa_auth = crypto_pk_new()))
+ goto error;
+ if (crypto_pk_generate_key(rsa_auth)<0)
+ goto error;
+
+ /* Create a link certificate signed by identity key. */
+ cert = tor_tls_create_certificate(rsa, identity, nickname, nn2,
+ key_lifetime);
+ /* Create self-signed certificate for identity key. */
+ idcert = tor_tls_create_certificate(identity, identity, nn2, nn2,
+ IDENTITY_CERT_LIFETIME);
+ /* Create an authentication certificate signed by identity key. */
+ authcert = tor_tls_create_certificate(rsa_auth, identity, nickname, nn2,
+ key_lifetime);
+ if (!cert || !idcert || !authcert) {
+ log_warn(LD_CRYPTO, "Error creating certificate");
+ goto error;
+ }
+
+ result->my_link_cert = tor_x509_cert_new(cert);
+ cert = NULL;
+ result->my_id_cert = tor_x509_cert_new(idcert);
+ idcert = NULL;
+ result->my_auth_cert = tor_x509_cert_new(authcert);
+ authcert = NULL;
+ if (!result->my_link_cert || !result->my_id_cert || !result->my_auth_cert)
+ goto error;
+ result->link_key = rsa;
+ rsa = NULL;
+ result->auth_key = rsa_auth;
+ rsa_auth = NULL;
+
+ rv = 0;
+ error:
+
+ tor_free(nickname);
+ tor_free(nn2);
+
+ if (cert)
+ tor_x509_cert_impl_free_(cert);
+ if (idcert)
+ tor_x509_cert_impl_free_(idcert);
+ if (authcert)
+ tor_x509_cert_impl_free_(authcert);
+ crypto_pk_free(rsa);
+ crypto_pk_free(rsa_auth);
+
+ return rv;
+}
/** Make future log messages about <b>tls</b> display the address
* <b>address</b>.
*/
diff --git a/src/lib/tls/tortls_internal.h b/src/lib/tls/tortls_internal.h
index b997ee3e4..2920e9658 100644
--- a/src/lib/tls/tortls_internal.h
+++ b/src/lib/tls/tortls_internal.h
@@ -27,6 +27,10 @@ int tor_tls_context_init_one(tor_tls_context_t **ppcontext,
unsigned int key_lifetime,
unsigned int flags,
int is_client);
+int tor_tls_context_init_certificates(tor_tls_context_t *result,
+ crypto_pk_t *identity,
+ unsigned key_lifetime,
+ unsigned flags);
#ifdef ENABLE_OPENSSL
void tor_tls_context_impl_free(struct ssl_ctx_st *);
diff --git a/src/lib/tls/tortls_nss.c b/src/lib/tls/tortls_nss.c
index 3ab5c753d..35dbc27d9 100644
--- a/src/lib/tls/tortls_nss.c
+++ b/src/lib/tls/tortls_nss.c
@@ -23,7 +23,9 @@
#include "lib/crypt_ops/crypto_dh.h"
#include "lib/crypt_ops/crypto_util.h"
#include "lib/tls/x509.h"
+#include "lib/tls/x509_internal.h"
#include "lib/tls/tortls.h"
+#include "lib/tls/tortls_st.h"
#include "lib/tls/tortls_internal.h"
#include "lib/log/util_bug.h"
@@ -64,27 +66,27 @@ tor_tls_context_new(crypto_pk_t *identity,
unsigned int key_lifetime, unsigned flags, int is_client)
{
tor_assert(identity);
- tor_assert(key_lifetime);
- (void)flags;
- (void)is_client;
- // XXXX
- return NULL;
-}
-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)
-{
- tor_assert(ppcontext);
- tor_assert(identity);
- tor_assert(key_lifetime);
- (void)flags;
- (void)is_client;
- // XXXX
- return -1;
+
+ tor_tls_context_t *ctx = tor_malloc_zero(sizeof(tor_tls_context_t));
+ ctx->refcnt = 1;
+
+ if (! is_client) {
+ if (tor_tls_context_init_certificates(ctx, identity,
+ key_lifetime, flags) < 0) {
+ goto err;
+ }
+ }
+
+ // XXXX write the main body.
+
+ goto done;
+ err:
+ tor_tls_context_decref(ctx);
+ ctx = NULL;
+ done:
+ return ctx;
}
+
void
tor_tls_context_impl_free(struct ssl_ctx_st *ctx)
{
@@ -361,25 +363,6 @@ tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
// XXXX
}
-int
-tor_tls_get_my_certs(int server,
- const struct tor_x509_cert_t **link_cert_out,
- const struct tor_x509_cert_t **id_cert_out)
-{
- tor_assert(link_cert_out);
- tor_assert(id_cert_out);
- (void)server;
- // XXXX
- return -1;
-}
-
-crypto_pk_t *
-tor_tls_get_my_client_auth_key(void)
-{
- // XXXX
- return NULL;
-}
-
const char *
tor_tls_get_ciphersuite_name(tor_tls_t *tls)
{
diff --git a/src/lib/tls/tortls_openssl.c b/src/lib/tls/tortls_openssl.c
index 5f5431235..fb6328bcd 100644
--- a/src/lib/tls/tortls_openssl.c
+++ b/src/lib/tls/tortls_openssl.c
@@ -18,6 +18,7 @@
#define TORTLS_PRIVATE
#define TORTLS_OPENSSL_PRIVATE
+#define TOR_X509_PRIVATE
#ifdef _WIN32 /*wrkard for dtls1.h >= 0.9.8m of "#include <winsock.h>"*/
#include <winsock2.h>
@@ -75,9 +76,6 @@ ENABLE_GCC_WARNING(redundant-decls)
#define LEGAL_NICKNAME_CHARACTERS \
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
-/** How long do identity certificates live? (sec) */
-#define IDENTITY_CERT_LIFETIME (365*24*60*60)
-
#define ADDR(tls) (((tls) && (tls)->address) ? tls->address : "peer")
#if OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,0,'f')
@@ -489,39 +487,6 @@ static const char CLIENT_CIPHER_LIST[] =
#undef CIPHER
#undef XCIPHER
-/** Set *<b>link_cert_out</b> and *<b>id_cert_out</b> to the link certificate
- * and ID certificate that we're currently using for our V3 in-protocol
- * handshake's certificate chain. If <b>server</b> is true, provide the certs
- * that we use in server mode (auth, ID); otherwise, provide the certs that we
- * use in client mode. (link, ID) */
-int
-tor_tls_get_my_certs(int server,
- const tor_x509_cert_t **link_cert_out,
- const tor_x509_cert_t **id_cert_out)
-{
- tor_tls_context_t *ctx = tor_tls_context_get(server);
- if (! ctx)
- return -1;
- if (link_cert_out)
- *link_cert_out = server ? ctx->my_link_cert : ctx->my_auth_cert;
- if (id_cert_out)
- *id_cert_out = ctx->my_id_cert;
- return 0;
-}
-
-/**
- * Return the authentication key that we use to authenticate ourselves as a
- * client in the V3 in-protocol handshake.
- */
-crypto_pk_t *
-tor_tls_get_my_client_auth_key(void)
-{
- tor_tls_context_t *context = tor_tls_context_get(0);
- if (! context)
- return NULL;
- return context->auth_key;
-}
-
/** 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.
*/
@@ -548,39 +513,6 @@ tor_tls_cert_matches_key,(const tor_tls_t *tls, const tor_x509_cert_t *cert))
return result;
}
-/** Create a new global TLS context.
- *
- * You can call this function multiple times. Each time you call it,
- * it generates new certificates; all new connections will use
- * the new SSL context.
- */
-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)
-{
- tor_tls_context_t *new_ctx = tor_tls_context_new(identity,
- key_lifetime,
- flags,
- is_client);
- tor_tls_context_t *old_ctx = *ppcontext;
-
- if (new_ctx != NULL) {
- *ppcontext = new_ctx;
-
- /* Free the old context if one existed. */
- if (old_ctx != NULL) {
- /* This is safe even if there are open connections: we reference-
- * count tor_tls_context_t objects. */
- tor_tls_context_decref(old_ctx);
- }
- }
-
- return ((new_ctx != NULL) ? 0 : -1);
-}
-
void
tor_tls_context_impl_free(struct ssl_ctx_st *ctx)
{
@@ -592,8 +524,6 @@ tor_tls_context_impl_free(struct ssl_ctx_st *ctx)
/** The group we should use for ecdhe when none was selected. */
#define NID_tor_default_ecdhe_group NID_X9_62_prime256v1
-#define RSA_LINK_KEY_BITS 2048
-
/** Create a new TLS context for use with Tor TLS handshakes.
* <b>identity</b> should be set to the identity key used to sign the
* certificate.
@@ -602,57 +532,19 @@ tor_tls_context_t *
tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
unsigned flags, int is_client)
{
- crypto_pk_t *rsa = NULL, *rsa_auth = NULL;
EVP_PKEY *pkey = NULL;
tor_tls_context_t *result = NULL;
- X509 *cert = NULL, *idcert = NULL, *authcert = NULL;
- char *nickname = NULL, *nn2 = NULL;
tor_tls_init();
- nickname = crypto_random_hostname(8, 20, "www.", ".net");
-#ifdef DISABLE_V3_LINKPROTO_SERVERSIDE
- nn2 = crypto_random_hostname(8, 20, "www.", ".net");
-#else
- nn2 = crypto_random_hostname(8, 20, "www.", ".com");
-#endif
-
- /* Generate short-term RSA key for use with TLS. */
- if (!(rsa = crypto_pk_new()))
- goto error;
- if (crypto_pk_generate_key_with_bits(rsa, RSA_LINK_KEY_BITS)<0)
- goto error;
- if (!is_client) {
- /* Generate short-term RSA key for use in the in-protocol ("v3")
- * authentication handshake. */
- if (!(rsa_auth = crypto_pk_new()))
- goto error;
- if (crypto_pk_generate_key(rsa_auth)<0)
- goto error;
- /* Create a link certificate signed by identity key. */
- cert = tor_tls_create_certificate(rsa, identity, nickname, nn2,
- key_lifetime);
- /* Create self-signed certificate for identity key. */
- idcert = tor_tls_create_certificate(identity, identity, nn2, nn2,
- IDENTITY_CERT_LIFETIME);
- /* Create an authentication certificate signed by identity key. */
- authcert = tor_tls_create_certificate(rsa_auth, identity, nickname, nn2,
- key_lifetime);
- if (!cert || !idcert || !authcert) {
- log_warn(LD_CRYPTO, "Error creating certificate");
- goto error;
- }
- }
result = tor_malloc_zero(sizeof(tor_tls_context_t));
result->refcnt = 1;
- if (!is_client) {
- result->my_link_cert = tor_x509_cert_new(X509_dup(cert));
- result->my_id_cert = tor_x509_cert_new(X509_dup(idcert));
- result->my_auth_cert = tor_x509_cert_new(X509_dup(authcert));
- if (!result->my_link_cert || !result->my_id_cert || !result->my_auth_cert)
+
+ if (! is_client) {
+ if (tor_tls_context_init_certificates(result, identity, key_lifetime,
+ flags) < 0) {
goto error;
- result->link_key = crypto_pk_dup_key(rsa);
- result->auth_key = crypto_pk_dup_key(rsa_auth);
+ }
}
#if 0
@@ -727,22 +619,21 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
SSL_CTX_set_mode(result->ctx, SSL_MODE_RELEASE_BUFFERS);
#endif
if (! is_client) {
- if (cert && !SSL_CTX_use_certificate(result->ctx,cert))
+ if (result->my_link_cert &&
+ !SSL_CTX_use_certificate(result->ctx,
+ result->my_link_cert->cert)) {
goto error;
- X509_free(cert); /* We just added a reference to cert. */
- cert=NULL;
- if (idcert) {
+ }
+ if (result->my_id_cert) {
X509_STORE *s = SSL_CTX_get_cert_store(result->ctx);
tor_assert(s);
- X509_STORE_add_cert(s, idcert);
- X509_free(idcert); /* The context now owns the reference to idcert */
- idcert = NULL;
+ X509_STORE_add_cert(s, X509_dup(result->my_id_cert->cert));
}
}
SSL_CTX_set_session_cache_mode(result->ctx, SSL_SESS_CACHE_OFF);
if (!is_client) {
- tor_assert(rsa);
- if (!(pkey = crypto_pk_get_openssl_evp_pkey_(rsa,1)))
+ tor_assert(result->link_key);
+ if (!(pkey = crypto_pk_get_openssl_evp_pkey_(result->link_key,1)))
goto error;
if (!SSL_CTX_use_PrivateKey(result->ctx, pkey))
goto error;
@@ -751,6 +642,7 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
if (!SSL_CTX_check_private_key(result->ctx))
goto error;
}
+
{
DH *dh = crypto_dh_new_openssl_tls();
tor_assert(dh);
@@ -777,33 +669,14 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
/* let us realloc bufs that we're writing from */
SSL_CTX_set_mode(result->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
- if (rsa)
- crypto_pk_free(rsa);
- if (rsa_auth)
- crypto_pk_free(rsa_auth);
- X509_free(authcert);
- tor_free(nickname);
- tor_free(nn2);
return result;
error:
tls_log_errors(NULL, LOG_WARN, LD_NET, "creating TLS context");
- tor_free(nickname);
- tor_free(nn2);
if (pkey)
EVP_PKEY_free(pkey);
- if (rsa)
- crypto_pk_free(rsa);
- if (rsa_auth)
- crypto_pk_free(rsa_auth);
if (result)
tor_tls_context_decref(result);
- if (cert)
- X509_free(cert);
- if (idcert)
- X509_free(idcert);
- if (authcert)
- X509_free(authcert);
return NULL;
}
diff --git a/src/lib/tls/x509.h b/src/lib/tls/x509.h
index 8316df75a..e7440a192 100644
--- a/src/lib/tls/x509.h
+++ b/src/lib/tls/x509.h
@@ -72,10 +72,4 @@ int tor_tls_cert_is_valid(int severity,
time_t now,
int check_rsa_1024);
-int tor_x509_check_cert_lifetime_internal(int severity,
- const tor_x509_cert_impl_t *cert,
- time_t now,
- int past_tolerance,
- int future_tolerance);
-
#endif
diff --git a/src/lib/tls/x509_internal.h b/src/lib/tls/x509_internal.h
index 86f5a0de5..4b49f1dec 100644
--- a/src/lib/tls/x509_internal.h
+++ b/src/lib/tls/x509_internal.h
@@ -14,6 +14,17 @@
#include "lib/crypt_ops/crypto_rsa.h"
#include "lib/testsupport/testsupport.h"
+/**
+ * How skewed do we allow our clock to be with respect to certificates that
+ * seem to be expired? (seconds)
+ */
+#define TOR_X509_PAST_SLOP (2*24*60*60)
+/**
+ * How skewed do we allow our clock to be with respect to certificates that
+ * seem to come from the future? (seconds)
+ */
+#define TOR_X509_FUTURE_SLOP (30*24*60*60)
+
MOCK_DECL(tor_x509_cert_impl_t *, tor_tls_create_certificate,
(crypto_pk_t *rsa,
crypto_pk_t *rsa_sign,
@@ -25,6 +36,12 @@ MOCK_DECL(tor_x509_cert_t *, tor_x509_cert_new,
const tor_x509_cert_impl_t *tor_x509_cert_get_impl(
const tor_x509_cert_t *cert);
+int tor_x509_check_cert_lifetime_internal(int severity,
+ const tor_x509_cert_impl_t *cert,
+ time_t now,
+ int past_tolerance,
+ int future_tolerance);
+
void tor_x509_cert_impl_free_(tor_x509_cert_impl_t *cert);
#ifdef ENABLE_OPENSSL
int tor_x509_cert_set_cached_der_encoding(tor_x509_cert_t *cert);
diff --git a/src/lib/tls/x509_nss.c b/src/lib/tls/x509_nss.c
index ac9e6658d..35b3d2542 100644
--- a/src/lib/tls/x509_nss.c
+++ b/src/lib/tls/x509_nss.c
@@ -15,10 +15,144 @@
#include "lib/tls/tortls.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_util.h"
+#include "lib/crypt_ops/crypto_nss_mgt.h"
#include "lib/log/util_bug.h"
+#include "lib/encoding/time_fmt.h"
+#include "lib/string/printf.h"
#include <pk11pub.h>
+#include <cryptohi.h>
#include <cert.h>
+#include <keyhi.h>
+#include <time.h>
+
+/* Units of PRTime per second.
+ *
+ * (PRTime is based in microseconds since the Unix
+ * epoch.) */
+#define PRTIME_PER_SEC (1000*1000)
+
+static tor_x509_cert_impl_t *tor_x509_cert_decode_internal(
+ const uint8_t *certificate, int certificate_len);
+
+static tor_x509_cert_impl_t *
+tor_tls_create_certificate_internal(crypto_pk_t *rsa,
+ crypto_pk_t *rsa_sign,
+ CERTName *subject_dn,
+ CERTName *issuer_dn,
+ time_t start_time,
+ time_t end_time)
+{
+ if (! crypto_pk_key_is_private(rsa_sign)) {
+ return NULL;
+ }
+
+ const SECKEYPublicKey *subject_key = crypto_pk_get_nss_pubkey(rsa);
+ const SECKEYPrivateKey *signing_key = crypto_pk_get_nss_privkey(rsa_sign);
+ SECStatus s;
+
+ CERTSubjectPublicKeyInfo *subject_spki = NULL;
+ CERTCertificateRequest *request = NULL;
+ CERTValidity *validity = NULL;
+ CERTCertificate *cert = NULL;
+ SECItem der = { .data = NULL, .len = 0 };
+ SECItem signed_der = { .data = NULL, .len = 0 };
+
+ CERTCertificate *result_cert = NULL;
+
+ validity = CERT_CreateValidity(((PRTime)start_time) * PRTIME_PER_SEC,
+ ((PRTime)end_time) * PRTIME_PER_SEC);
+ if (! validity) {
+ crypto_nss_log_errors(LOG_WARN, "creating a validity object");
+ goto err;
+ }
+
+ unsigned long serial_number;
+ crypto_rand((char*)&serial_number, sizeof(serial_number));
+
+ subject_spki = SECKEY_CreateSubjectPublicKeyInfo(subject_key);
+ if (!subject_spki)
+ goto err;
+
+ /* Make a CSR ... */
+ // XXX do we need to set any attributes?
+ request = CERT_CreateCertificateRequest(subject_dn,
+ subject_spki,
+ NULL /* attributes */);
+ if (!request)
+ goto err;
+
+ /* Put it into a certificate ... */
+ cert = CERT_CreateCertificate(serial_number,
+ issuer_dn,
+ validity,
+ request);
+ if (!cert)
+ goto err;
+
+ /* version 3 cert */
+ *cert->version.data = 2; /* 2 means version 3. */
+ cert->version.len = 1;
+
+ // XXX do we need to set anything else on the cert?
+
+ /* Sign it. */
+ KeyType privkey_type = SECKEY_GetPrivateKeyType(signing_key);
+ SECOidTag oid_tag = SEC_GetSignatureAlgorithmOidTag(privkey_type,
+ SEC_OID_SHA256);
+ if (oid_tag == SEC_OID_UNKNOWN)
+ goto err;
+ s = SECOID_SetAlgorithmID(cert->arena, &cert->signature, oid_tag, NULL);
+ if (s != SECSuccess)
+ goto err;
+
+ void *tmp;
+ tmp = SEC_ASN1EncodeItem(cert->arena, &der, cert,
+ SEC_ASN1_GET(CERT_CertificateTemplate));
+ if (!tmp)
+ goto err;
+
+ s = SEC_DerSignDataWithAlgorithmID(cert->arena,
+ &signed_der,
+ der.data, der.len,
+ (SECKEYPrivateKey *)signing_key,//const
+ &cert->signature);
+
+ if (s != SECSuccess)
+ goto err;
+
+ /* Re-parse it, to make sure all the certificates we actually use
+ * appear via being decoded. */
+ result_cert = tor_x509_cert_decode_internal(signed_der.data, signed_der.len);
+
+#if 1
+ {
+ // Can we check the cert we just signed?
+ tor_assert(result_cert);
+ SECKEYPublicKey *issuer_pk = (SECKEYPublicKey *)
+ crypto_pk_get_nss_pubkey(rsa_sign);
+ SECStatus cert_ok = CERT_VerifySignedDataWithPublicKey(
+ &result_cert->signatureWrap, issuer_pk, NULL);
+ tor_assert(cert_ok == SECSuccess);
+ }
+#endif
+
+ err:
+ if (subject_spki)
+ SECKEY_DestroySubjectPublicKeyInfo(subject_spki);
+ if (request)
+ CERT_DestroyCertificateRequest(request);
+ if (validity)
+ CERT_DestroyValidity(validity);
+
+ // unnecessary, since these are allocated in the cert's arena.
+ //SECITEM_FreeItem(&der, PR_FALSE);
+ //SECITEM_FreeItem(&signed_der, PR_FALSE);
+ if (cert)
+ CERT_DestroyCertificate(cert);
+
+ return result_cert;
+}
MOCK_IMPL(tor_x509_cert_impl_t *,
tor_tls_create_certificate,(crypto_pk_t *rsa,
@@ -31,9 +165,39 @@ tor_tls_create_certificate,(crypto_pk_t *rsa,
tor_assert(rsa_sign);
tor_assert(cname);
tor_assert(cname_sign);
- (void) cert_lifetime;
- // XXXX
- return NULL;
+
+ char *cname_rfc_1485 = NULL, *cname_sign_rfc_1485 = NULL;
+ CERTName *subject_dn = NULL, *issuer_dn = NULL;
+ time_t start_time;
+ time_t end_time;
+ CERTCertificate *result = NULL;
+
+ tor_asprintf(&cname_rfc_1485, "CN=%s", cname);
+ tor_asprintf(&cname_sign_rfc_1485, "CN=%s", cname_sign);
+
+ subject_dn = CERT_AsciiToName(cname_rfc_1485);
+ issuer_dn = CERT_AsciiToName(cname_sign_rfc_1485);
+ if (!subject_dn || !issuer_dn)
+ goto err;
+
+ tor_tls_pick_certificate_lifetime(time(NULL), cert_lifetime,
+ &start_time, &end_time);
+
+ result = tor_tls_create_certificate_internal(rsa,
+ rsa_sign,
+ subject_dn,
+ issuer_dn,
+ start_time,
+ end_time);
+ err:
+ tor_free(cname_rfc_1485);
+ tor_free(cname_sign_rfc_1485);
+ if (subject_dn)
+ CERT_DestroyName(subject_dn);
+ if (issuer_dn)
+ CERT_DestroyName(issuer_dn);
+
+ return result;
}
/** Set *<b>encoded_out</b> and *<b>size_out</b> to <b>cert</b>'s encoded DER
@@ -63,26 +227,63 @@ tor_x509_cert_t *
tor_x509_cert_dup(const tor_x509_cert_t *cert)
{
tor_assert(cert);
- // XXXX
- return NULL;
+ return tor_x509_cert_new(CERT_DupCertificate(cert->cert));
+}
+
+/**
+ * As tor_x509_cert_decode, but return the NSS certificate type
+*/
+static tor_x509_cert_impl_t *
+tor_x509_cert_decode_internal(const uint8_t *certificate,
+ int certificate_len)
+{
+ tor_assert(certificate);
+ if (certificate_len > INT_MAX)
+ return NULL;
+
+ SECItem der = { .type = siBuffer,
+ .data = (unsigned char *)certificate,
+ .len = certificate_len };
+ CERTCertDBHandle *certdb = CERT_GetDefaultCertDB();
+ tor_assert(certdb);
+ return CERT_NewTempCertificate(certdb,
+ &der,
+ NULL /* nickname */,
+ PR_FALSE, /* isPerm */
+ PR_TRUE /* CopyDER */);
}
tor_x509_cert_t *
tor_x509_cert_decode(const uint8_t *certificate,
size_t certificate_len)
{
- tor_assert(certificate);
- (void) certificate_len;
- // XXXX
- return NULL;
+ CERTCertificate *cert = tor_x509_cert_decode_internal(certificate,
+ (int)certificate_len);
+ if (! cert) {
+ crypto_nss_log_errors(LOG_INFO, "decoding certificate");
+ return NULL;
+ }
+
+ tor_x509_cert_t *newcert = tor_x509_cert_new(cert);
+
+ return newcert;
}
crypto_pk_t *
tor_tls_cert_get_key(tor_x509_cert_t *cert)
{
tor_assert(cert);
- // XXXXX
- return NULL;
+ CERTSubjectPublicKeyInfo *spki = &cert->cert->subjectPublicKeyInfo;
+ SECKEYPublicKey *pub = SECKEY_ExtractPublicKey(spki); // we own this pointer
+ if (pub == NULL)
+ return NULL;
+
+ if (SECKEY_GetPublicKeyType(pub) != rsaKey) {
+ SECKEY_DestroyPublicKey(pub);
+ return NULL;
+ }
+
+ return crypto_pk_new_from_nss_pubkey(pub);
}
int
@@ -92,14 +293,80 @@ tor_tls_cert_is_valid(int severity,
time_t now,
int check_rsa_1024)
{
+ int result = 0;
+
tor_assert(cert);
tor_assert(signing_cert);
- (void)severity;
- (void)now;
- (void)check_rsa_1024;
- // XXXXX
- return 0;
+ SECKEYPublicKey *pk = CERT_ExtractPublicKey(signing_cert->cert);
+ if (pk == NULL) {
+ log_fn(severity, LD_CRYPTO,
+ "Invalid certificate: could not extract issuer key");
+ goto fail;
+ }
+
+ SECStatus s = CERT_VerifySignedDataWithPublicKey(&cert->cert->signatureWrap,
+ pk, NULL);
+ if (s != SECSuccess) {
+ log_fn(severity, LD_CRYPTO,
+ "Invalid certificate: could not validate signature.");
+ goto fail;
+ }
+
+ if (tor_x509_check_cert_lifetime_internal(severity,
+ cert->cert,
+ now,
+ TOR_X509_PAST_SLOP,
+ TOR_X509_FUTURE_SLOP) < 0)
+ goto fail;
+
+ if (check_rsa_1024) {
+ /* We require that this is a 1024-bit RSA key, for legacy reasons .:p */
+ if (SECKEY_GetPublicKeyType(pk) != rsaKey ||
+ SECKEY_PublicKeyStrengthInBits(pk) != 1024) {
+ log_fn(severity, LD_CRYPTO, "Invalid certificate: Key is not RSA1024.");
+ goto fail;
+ }
+ } else {
+ /* We require that this key is at least minimally strong. */
+ unsigned min_bits = (SECKEY_GetPublicKeyType(pk) == ecKey) ? 128: 1024;
+ if (SECKEY_PublicKeyStrengthInBits(pk) < min_bits) {
+ log_fn(severity, LD_CRYPTO, "Invalid certificate: Key is too weak.");
+ goto fail;
+ }
+ }
+
+ /* The certificate is valid. */
+ result = 1;
+
+ fail:
+ if (pk)
+ SECKEY_DestroyPublicKey(pk);
+ return result;
+}
+
+static void
+log_cert_lifetime(int severity,
+ const char *status,
+ time_t now,
+ PRTime notBefore,
+ PRTime notAfter)
+{
+ log_fn(severity, LD_GENERAL,
+ "Certificate %s. Either their clock is set wrong, or your clock "
+ "is incorrect.", status);
+
+ char nowbuf[ISO_TIME_LEN+1];
+ char nbbuf[ISO_TIME_LEN+1];
+ char nabuf[ISO_TIME_LEN+1];
+
+ format_iso_time(nowbuf, now);
+ format_iso_time(nbbuf, notBefore / PRTIME_PER_SEC);
+ format_iso_time(nabuf, notAfter / PRTIME_PER_SEC);
+
+ log_fn(severity, LD_GENERAL,
+ "(The certificate is valid from %s until %s. Your time is %s.)",
+ nbbuf, nabuf, nowbuf);
}
int
@@ -110,12 +377,33 @@ tor_x509_check_cert_lifetime_internal(int severity,
int future_tolerance)
{
tor_assert(cert);
- (void)severity;
- (void)now;
- (void)past_tolerance;
- (void)future_tolerance;
- // XXXX
- return -1;
+
+ PRTime notBefore=0, notAfter=0;
+ int64_t t;
+ SECStatus r = CERT_GetCertTimes(cert, ¬Before, ¬After);
+ if (r != SECSuccess) {
+ log_fn(severity, LD_CRYPTO,
+ "Couldn't get validity times from certificate");
+ return -1;
+ }
+
+ t = ((int64_t)now) + future_tolerance;
+ t *= PRTIME_PER_SEC;
+ if (notBefore > t) {
+ log_cert_lifetime(severity, "not yet valid", now,
+ notBefore, notAfter);
+ return -1;
+ }
+
+ t = ((int64_t)now) - past_tolerance;
+ t *= PRTIME_PER_SEC;
+ if (notAfter < t) {
+ log_cert_lifetime(severity, "already expired", now,
+ notBefore, notAfter);
+ return -1;
+ }
+
+ return 0;
}
#ifdef TOR_UNIT_TESTS
@@ -126,9 +414,33 @@ tor_x509_cert_replace_expiration(const tor_x509_cert_t *inp,
{
tor_assert(inp);
tor_assert(signing_key);
- (void)new_expiration_time;
- // XXXX
- return NULL;
+ PRTime notBefore=0, notAfter=0;
+ SECStatus r = CERT_GetCertTimes(inp->cert, ¬Before, ¬After);
+ if (r != SECSuccess)
+ return NULL;
+
+ time_t start_time = notBefore / PRTIME_PER_SEC;
+ if (new_expiration_time < start_time) {
+ /* This prevents an NSS error. */
+ start_time = new_expiration_time - 10;
+ }
+
+ crypto_pk_t *subject_key = tor_tls_cert_get_key((tor_x509_cert_t *)inp);
+ if (!subject_key)
+ return NULL;
+
+ CERTCertificate *newcert;
+
+ newcert = tor_tls_create_certificate_internal(subject_key,
+ signing_key,
+ &inp->cert->subject,
+ &inp->cert->issuer,
+ start_time,
+ new_expiration_time);
+
+ crypto_pk_free(subject_key);
+
+ return newcert ? tor_x509_cert_new(newcert) : NULL;
}
#endif
diff --git a/src/lib/tls/x509_openssl.c b/src/lib/tls/x509_openssl.c
index 43bd6b4d4..28a30b66e 100644
--- a/src/lib/tls/x509_openssl.c
+++ b/src/lib/tls/x509_openssl.c
@@ -319,7 +319,8 @@ tor_tls_cert_is_valid(int severity,
/* okay, the signature checked out right. Now let's check the check the
* lifetime. */
if (tor_x509_check_cert_lifetime_internal(severity, cert->cert, now,
- 48*60*60, 30*24*60*60) < 0)
+ TOR_X509_PAST_SLOP,
+ TOR_X509_FUTURE_SLOP) < 0)
goto bad;
cert_key = X509_get_pubkey(cert->cert);
diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c
index 677276599..df3fa67eb 100644
--- a/src/test/test_link_handshake.c
+++ b/src/test/test_link_handshake.c
@@ -795,11 +795,26 @@ CERTS_FAIL(bad_rsa_id_cert, /*ed25519*/
{
require_failure_message = "legacy RSA ID certificate was not valid";
certs_cell_cert_t *cert = certs_cell_get_certs(d->ccell, 1);
- uint8_t *body = certs_cell_cert_getarray_body(cert);
- ssize_t body_len = certs_cell_cert_getlen_body(cert);
- /* Frob a byte in the signature */
- body[body_len - 13] ^= 7;
+ uint8_t *body;
+ /* Frob a byte in the signature, after making a new cert. (NSS won't let
+ * us just frob the old cert, since it will see that the issuer & serial
+ * number are the same, which will make it fail at an earlier stage than
+ * signature verification.) */
+ const tor_x509_cert_t *idc;
+ tor_x509_cert_t *newc;
+ tor_tls_get_my_certs(1, NULL, &idc);
+ time_t new_end = time(NULL) + 86400 * 10;
+ newc = tor_x509_cert_replace_expiration(idc, new_end, d->key2);
+ const uint8_t *encoded;
+ size_t encoded_len;
+ tor_x509_cert_get_der(newc, &encoded, &encoded_len);
+ certs_cell_cert_setlen_body(cert, encoded_len);
+ certs_cell_cert_set_cert_len(cert, encoded_len);
+ body = certs_cell_cert_getarray_body(cert);
+ memcpy(body, encoded, encoded_len);
+ body[encoded_len - 13] ^= 7;
REENCODE();
+ tor_x509_cert_free(newc);
})
CERTS_FAIL(expired_rsa_id, /* both */
{
@@ -815,6 +830,7 @@ CERTS_FAIL(expired_rsa_id, /* both */
size_t encoded_len;
tor_x509_cert_get_der(newc, &encoded, &encoded_len);
certs_cell_cert_setlen_body(cert, encoded_len);
+ certs_cell_cert_set_cert_len(cert, encoded_len);
memcpy(certs_cell_cert_getarray_body(cert), encoded, encoded_len);
REENCODE();
tor_x509_cert_free(newc);
1
0