Browse Source

Merge branch 'nss_squashed' into nss_merge

Nick Mathewson 5 years ago
parent
commit
0db5c54957
100 changed files with 8623 additions and 3905 deletions
  1. 8 0
      Makefile.am
  2. 7 0
      changes/NSS
  3. 3 0
      changes/feature26815
  4. 11 0
      changes/feature26816
  5. 2 0
      config.rust.in
  6. 30 0
      configure.ac
  7. 15 1
      src/app/config/config.c
  8. 4 4
      src/app/include.am
  9. 1 1
      src/core/crypto/onion_ntor.c
  10. 1 1
      src/core/crypto/relay_crypto.c
  11. 17 8
      src/core/mainloop/connection.c
  12. 4 2
      src/core/mainloop/main.c
  13. 1 0
      src/core/or/channeltls.c
  14. 1 0
      src/core/or/connection_or.c
  15. 2 1
      src/core/or/or.h
  16. 11 0
      src/ext/tinytest.c
  17. 2 2
      src/feature/control/control.c
  18. 1 1
      src/feature/nodelist/parsecommon.c
  19. 1 0
      src/feature/nodelist/torcert.c
  20. 2 1
      src/feature/relay/router.c
  21. 1 0
      src/feature/relay/routerkeys.c
  22. 1 1
      src/feature/rend/rendmid.c
  23. 1 1
      src/feature/rend/rendservice.c
  24. 106 0
      src/lib/crypt_ops/aes_nss.c
  25. 2 2
      src/lib/crypt_ops/aes_openssl.c
  26. 6 0
      src/lib/crypt_ops/compat_openssl.h
  27. 0 509
      src/lib/crypt_ops/crypto.c
  28. 190 0
      src/lib/crypt_ops/crypto_cipher.c
  29. 4 21
      src/lib/crypt_ops/crypto_cipher.h
  30. 43 441
      src/lib/crypt_ops/crypto_dh.c
  31. 20 3
      src/lib/crypt_ops/crypto_dh.h
  32. 207 0
      src/lib/crypt_ops/crypto_dh_nss.c
  33. 471 0
      src/lib/crypt_ops/crypto_dh_openssl.c
  34. 251 11
      src/lib/crypt_ops/crypto_digest.c
  35. 5 1
      src/lib/crypt_ops/crypto_digest.h
  36. 1 0
      src/lib/crypt_ops/crypto_ed25519.c
  37. 1 0
      src/lib/crypt_ops/crypto_format.c
  38. 2 0
      src/lib/crypt_ops/crypto_hkdf.c
  39. 193 0
      src/lib/crypt_ops/crypto_init.c
  40. 34 0
      src/lib/crypt_ops/crypto_init.h
  41. 132 0
      src/lib/crypt_ops/crypto_nss_mgt.c
  42. 34 0
      src/lib/crypt_ops/crypto_nss_mgt.h
  43. 1 1
      src/lib/crypt_ops/crypto_ope.c
  44. 226 2
      src/lib/crypt_ops/crypto_openssl_mgt.c
  45. 11 4
      src/lib/crypt_ops/crypto_openssl_mgt.h
  46. 1 1
      src/lib/crypt_ops/crypto_pwbox.c
  47. 111 11
      src/lib/crypt_ops/crypto_rand.c
  48. 211 726
      src/lib/crypt_ops/crypto_rsa.c
  49. 39 9
      src/lib/crypt_ops/crypto_rsa.h
  50. 738 0
      src/lib/crypt_ops/crypto_rsa_nss.c
  51. 590 0
      src/lib/crypt_ops/crypto_rsa_openssl.c
  52. 49 1
      src/lib/crypt_ops/crypto_s2k.c
  53. 4 25
      src/lib/crypt_ops/crypto_util.c
  54. 0 9
      src/lib/crypt_ops/crypto_util.h
  55. 27 5
      src/lib/crypt_ops/include.am
  56. 1 0
      src/lib/encoding/.may_include
  57. 2 0
      src/lib/encoding/include.am
  58. 106 0
      src/lib/encoding/pem.c
  59. 26 0
      src/lib/encoding/pem.h
  60. 1 1
      src/lib/include.libdonna.am
  61. 16 9
      src/lib/process/daemon.c
  62. 2 2
      src/lib/process/daemon.h
  63. 21 3
      src/lib/tls/include.am
  64. 143 998
      src/lib/tls/tortls.c
  65. 36 174
      src/lib/tls/tortls.h
  66. 76 0
      src/lib/tls/tortls_internal.h
  67. 741 0
      src/lib/tls/tortls_nss.c
  68. 1708 0
      src/lib/tls/tortls_openssl.c
  69. 74 0
      src/lib/tls/tortls_st.h
  70. 143 0
      src/lib/tls/x509.c
  71. 75 0
      src/lib/tls/x509.h
  72. 53 0
      src/lib/tls/x509_internal.h
  73. 450 0
      src/lib/tls/x509_nss.c
  74. 461 0
      src/lib/tls/x509_openssl.c
  75. 1 0
      src/rust/build.rs
  76. 11 3
      src/test/bench.c
  77. 10 5
      src/test/fuzz/fuzzing_common.c
  78. 2 2
      src/test/fuzz/include.am
  79. 23 12
      src/test/include.am
  80. 19 18
      src/test/log_test_helpers.h
  81. 5 0
      src/test/test-timers.c
  82. 7 0
      src/test/test.c
  83. 3 0
      src/test/test.h
  84. 1 1
      src/test/test_controller.c
  85. 122 5
      src/test/test_crypto.c
  86. 3 1
      src/test/test_crypto_ope.c
  87. 2 0
      src/test/test_crypto_slow.c
  88. 1 1
      src/test/test_dir_handle_get.c
  89. 1 1
      src/test/test_hs_client.c
  90. 6 1
      src/test/test_hs_ntor_cl.c
  91. 1 1
      src/test/test_introduce.c
  92. 27 7
      src/test/test_link_handshake.c
  93. 6 2
      src/test/test_ntor_cl.c
  94. 122 0
      src/test/test_pem.c
  95. 1 1
      src/test/test_relaycell.c
  96. 2 0
      src/test/test_router.c
  97. 1 1
      src/test/test_routerkeys.c
  98. 1 1
      src/test/test_shared_random.c
  99. 260 849
      src/test/test_tortls.c
  100. 13 0
      src/test/test_tortls.h

+ 8 - 0
Makefile.am

@@ -133,6 +133,14 @@ TOR_INTERNAL_TESTING_LIBS = \
 	src/lib/libtor-trace.a
 endif
 
+TOR_LDFLAGS_CRYPTLIB=@TOR_LDFLAGS_openssl@
+TOR_LIBS_CRYPTLIB=@TOR_OPENSSL_LIBS@
+TOR_CFLAGS_CRYPTLIB=
+if USE_NSS
+TOR_CFLAGS_CRYPTLIB+=@NSS_CFLAGS@
+TOR_LIBS_CRYPTLIB+=@NSS_LIBS@
+endif
+
 # All libraries used to link tor-cov
 
 include src/include.am

+ 7 - 0
changes/NSS

@@ -0,0 +1,7 @@
+  o Major features (portability, cryptography, experimental, TLS):
+    - Tor now has the option to compile with the NSS library instead of
+      OpenSSL. This feature is experimental, and we expect that bugs may
+      remain. It is mainly intended for environments where Tor's performance
+      is not CPU-bound, and where NSS is already known to be installed.
+      To try it out, configure Tor with the --enable-nss flag.
+      Closes ticket 26631.

+ 3 - 0
changes/feature26815

@@ -0,0 +1,3 @@
+  o Major features (experimental, library support):
+    - When built with --enable-nss, Tor now uses the NSS library for digests,
+      AES, and pseudorandom numbers. Closes ticket 26815.

+ 11 - 0
changes/feature26816

@@ -0,0 +1,11 @@
+  o Major features (experimental, library support):
+    - Tor now has _partial_ support for using the NSS cryptography library in
+      place of OpenSSL.  When Tor is configured with --enable-nss, it will
+      use NSS for several (but not yet all) of its cryptography.  (It still
+      relies on OpenSSL for the rest.)  Eventually, if all goes as planned,
+      "--enable-nss" will produce a version of Tor that does not depend on
+      OpenSSL.  Implements ticket 26816.
+
+      WARNING: This feature is experimental.  Don't use it for real security
+      yet, until the code has had much more review, and more bugs have been
+      shaken out.

+ 2 - 0
config.rust.in

@@ -5,6 +5,7 @@
 
 BUILDDIR=@BUILDDIR@
 TOR_LDFLAGS_zlib=@TOR_LDFLAGS_zlib@
+TOR_LDFLAGS_nss=@TOR_LDFLAGS_nss@
 TOR_LDFLAGS_openssl=@TOR_LDFLAGS_openssl@
 TOR_LDFLAGS_libevent=@TOR_LDFLAGS_libevent@
 TOR_ZLIB_LIBS=@TOR_ZLIB_LIBS@
@@ -20,3 +21,4 @@ TOR_LZMA_LIBS=@TOR_LZMA_LIBS@
 TOR_ZSTD_LIBS=@TOR_ZSTD_LIBS@
 LIBS=@LIBS@
 LDFLAGS=@LDFLAGS@
+NSS_LIBS=@NSS_LIBS@

+ 30 - 0
configure.ac

@@ -53,6 +53,8 @@ AC_ARG_ENABLE(restart-debugging,
    AS_HELP_STRING(--enable-restart-debugging, [Build Tor with support for debugging in-process restart. Developers only.]))
 AC_ARG_ENABLE(zstd-advanced-apis,
    AS_HELP_STRING(--disable-zstd-advanced-apis, [Build without support for zstd's "static-only" APIs.]))
+AC_ARG_ENABLE(nss,
+   AS_HELP_STRING(--enable-nss, [Use Mozilla's NSS TLS library. (EXPERIMENTAL)]))
 
 if test "x$enable_coverage" != "xyes" -a "x$enable_asserts_in_tests" = "xno" ; then
     AC_MSG_ERROR([Can't disable assertions outside of coverage build])
@@ -64,6 +66,16 @@ AM_CONDITIONAL(DISABLE_ASSERTS_IN_UNIT_TESTS, test "x$enable_asserts_in_tests" =
 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, test "x$enable_nss" != "xyes")
+
+if test "x$enable_nss" = "xyes"; then
+  AC_DEFINE(ENABLE_NSS, 1,
+  	    [Defined if we're building with NSS.])
+else
+  AC_DEFINE(ENABLE_OPENSSL, 1,
+            [Defined if we're building with OpenSSL or LibreSSL])
+fi
 
 if test "$enable_static_tor" = "yes"; then
   enable_static_libevent="yes";
@@ -840,9 +852,22 @@ fi
 LIBS="$save_LIBS"
 AC_SUBST(TOR_LIB_MATH)
 
+dnl ------------------------------------------------------
+dnl Hello, NSS.  You're new around here.
+if test "x$enable_nss" = "xyes"; then
+  PKG_CHECK_MODULES(NSS,
+     [nss],
+     [have_nss=yes],
+     [have_nss=no; AC_MSG_ERROR([You asked for NSS but I can't find it.])])
+  AC_SUBST(NSS_CFLAGS)
+  AC_SUBST(NSS_LIBS)
+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"
@@ -942,6 +967,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.

+ 15 - 1
src/app/config/config.c

@@ -81,6 +81,12 @@
 #include "core/mainloop/cpuworker.h"
 #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"
@@ -1412,7 +1418,8 @@ options_act_reversible(const or_options_t *old_options, char **msg)
    * processes. */
   if (running_tor && options->RunAsDaemon) {
     /* No need to roll back, since you can't change the value. */
-    start_daemon();
+    if (start_daemon())
+      crypto_postfork();
   }
 
 #ifdef HAVE_SYSTEMD
@@ -5152,9 +5159,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),

+ 4 - 4
src/app/include.am

@@ -14,10 +14,10 @@ src_app_tor_SOURCES = src/app/main/tor_main.c
 # This seems to matter nowhere but on windows, but I assure you that it
 # matters a lot there, and is quite hard to debug if you forget to do it.
 
-src_app_tor_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@
+src_app_tor_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) @TOR_LDFLAGS_libevent@
 src_app_tor_LDADD = $(TOR_INTERNAL_LIBS) \
 	$(rust_ldadd) \
-	@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
+	@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ $(TOR_LIBS_CRYPTLIB) \
 	@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
 	@CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@ \
 	@TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@
@@ -26,9 +26,9 @@ if COVERAGE_ENABLED
 src_app_tor_cov_SOURCES = $(src_app_tor_SOURCES)
 src_app_tor_cov_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
 src_app_tor_cov_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
-src_app_tor_cov_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@
+src_app_tor_cov_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) @TOR_LDFLAGS_libevent@
 src_app_tor_cov_LDADD = $(TOR_INTERNAL_TESTING_LIBS) \
-	@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
+	@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ $(TOR_LIBS_CRYPTLIB) \
 	@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ \
 	@CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@ \
 	@TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@

+ 1 - 1
src/core/crypto/onion_ntor.c

@@ -22,7 +22,7 @@
 
 #define ONION_NTOR_PRIVATE
 
-#include "lib/crypt_ops/crypto.h"
+#include "lib/crypt_ops/crypto_cipher.h"
 #include "lib/crypt_ops/crypto_digest.h"
 #include "lib/crypt_ops/crypto_hkdf.h"
 #include "lib/crypt_ops/crypto_util.h"

+ 1 - 1
src/core/crypto/relay_crypto.c

@@ -7,7 +7,7 @@
 #include "core/or/or.h"
 #include "core/or/circuitlist.h"
 #include "app/config/config.h"
-#include "lib/crypt_ops/crypto.h"
+#include "lib/crypt_ops/crypto_cipher.h"
 #include "lib/crypt_ops/crypto_util.h"
 #include "core/crypto/hs_ntor.h" // for HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN
 #include "core/or/relay.h"

+ 17 - 8
src/core/mainloop/connection.c

@@ -449,6 +449,20 @@ connection_new(int type, int socket_family)
   }
 }
 
+static void
+connection_close_and_invalidate_socket(connection_t *conn)
+{
+  if (connection_speaks_cells(conn)) {
+    or_connection_t *or_conn = TO_OR_CONN(conn);
+    tor_tls_free(or_conn->tls);
+    or_conn->tls = NULL;
+    or_conn->base_.s = TOR_INVALID_SOCKET;
+  } else {
+    tor_close_socket(conn->s);
+    conn->s = TOR_INVALID_SOCKET;
+  }
+}
+
 /** Initializes conn. (you must call connection_add() to link it into the main
  * array).
  *
@@ -614,9 +628,8 @@ connection_free_minimal(connection_t *conn)
   tor_free(conn->address);
 
   if (connection_speaks_cells(conn)) {
+    connection_close_and_invalidate_socket(conn);
     or_connection_t *or_conn = TO_OR_CONN(conn);
-    tor_tls_free(or_conn->tls);
-    or_conn->tls = NULL;
     or_handshake_state_free(or_conn->handshake_state);
     or_conn->handshake_state = NULL;
     tor_free(or_conn->nickname);
@@ -692,9 +705,7 @@ connection_free_minimal(connection_t *conn)
   }
 
   if (SOCKET_OK(conn->s)) {
-    log_debug(LD_NET,"closing fd %d.",(int)conn->s);
-    tor_close_socket(conn->s);
-    conn->s = TOR_INVALID_SOCKET;
+    connection_close_and_invalidate_socket(conn);
   }
 
   if (conn->type == CONN_TYPE_OR &&
@@ -820,9 +831,7 @@ connection_close_immediate(connection_t *conn)
   conn->read_blocked_on_bw = 0;
   conn->write_blocked_on_bw = 0;
 
-  if (SOCKET_OK(conn->s))
-    tor_close_socket(conn->s);
-  conn->s = TOR_INVALID_SOCKET;
+  connection_close_and_invalidate_socket(conn);
   if (conn->linked)
     conn->linked_conn_is_closed = 1;
   if (conn->outbuf)

+ 4 - 2
src/core/mainloop/main.c

@@ -121,6 +121,7 @@
 #include "lib/evloop/compat_libevent.h"
 #include "lib/encoding/confline.h"
 #include "lib/evloop/timers.h"
+#include "lib/crypt_ops/crypto_init.h"
 
 #include <event2/event.h>
 
@@ -3503,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) ?

+ 1 - 0
src/core/or/channeltls.c

@@ -70,6 +70,7 @@
 #include "core/or/var_cell_st.h"
 
 #include "lib/tls/tortls.h"
+#include "lib/tls/x509.h"
 
 /** How many CELL_PADDING cells have we received, ever? */
 uint64_t stats_n_padding_cells_processed = 0;

+ 1 - 0
src/core/or/connection_or.c

@@ -73,6 +73,7 @@
 #include "lib/crypt_ops/crypto_format.h"
 
 #include "lib/tls/tortls.h"
+#include "lib/tls/x509.h"
 
 static int connection_tls_finish_handshake(or_connection_t *conn);
 static int connection_or_launch_v3_or_handshake(or_connection_t *conn);

+ 2 - 1
src/core/or/or.h

@@ -27,7 +27,8 @@
 #include "lib/cc/torint.h"
 #include "lib/container/map.h"
 #include "lib/container/smartlist.h"
-#include "lib/crypt_ops/crypto.h"
+#include "lib/crypt_ops/crypto_cipher.h"
+#include "lib/crypt_ops/crypto_rsa.h"
 #include "lib/ctime/di_ops.h"
 #include "lib/defs/dh_sizes.h"
 #include "lib/encoding/binascii.h"

+ 11 - 0
src/ext/tinytest.c

@@ -25,6 +25,7 @@
 #ifdef TINYTEST_LOCAL
 #include "tinytest_local.h"
 #endif
+#define TINYTEST_POSTFORK
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -118,6 +119,14 @@ testcase_run_bare_(const struct testcase_t *testcase)
 
 #ifndef NO_FORKING
 
+#ifdef TINYTEST_POSTFORK
+void tinytest_prefork(void);
+void tinytest_postfork(void);
+#else
+static void tinytest_prefork(void) { }
+static void tinytest_postfork(void) { }
+#endif
+
 static enum outcome
 testcase_run_forked_(const struct testgroup_t *group,
 		     const struct testcase_t *testcase)
@@ -178,10 +187,12 @@ testcase_run_forked_(const struct testgroup_t *group,
 
 	if (opt_verbosity>0)
 		printf("[forking] ");
+	tinytest_prefork();
 	pid = fork();
 #ifdef FORK_BREAKS_GCOV
 	vproc_transaction_begin(0);
 #endif
+	tinytest_postfork();
 	if (!pid) {
 		/* child. */
 		int test_r, write_r;

+ 2 - 2
src/feature/control/control.c

@@ -4994,7 +4994,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
 
   if (!strcasecmp(key_type_rsa1024, key_type)) {
     /* "RSA:<Base64 Blob>" - Loading a pre-existing RSA1024 key. */
-    pk = crypto_pk_base64_decode(key_blob, strlen(key_blob));
+    pk = crypto_pk_base64_decode_private(key_blob, strlen(key_blob));
     if (!pk) {
       err_msg = tor_strdup("512 Failed to decode RSA key\r\n");
       goto err;
@@ -5029,7 +5029,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
         goto err;
       }
       if (!discard_pk) {
-        if (crypto_pk_base64_encode(pk, &key_new_blob)) {
+        if (crypto_pk_base64_encode_private(pk, &key_new_blob)) {
           crypto_pk_free(pk);
           tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n",
                        key_type_rsa1024);

+ 1 - 1
src/feature/nodelist/parsecommon.c

@@ -14,7 +14,7 @@
 #include "lib/string/util_string.h"
 #include "lib/string/printf.h"
 #include "lib/memarea/memarea.h"
-#include "lib/crypt_ops/crypto.h"
+#include "lib/crypt_ops/crypto_rsa.h"
 
 #include <string.h>
 

+ 1 - 0
src/feature/nodelist/torcert.c

@@ -33,6 +33,7 @@
 #include "lib/log/log.h"
 #include "trunnel/link_handshake.h"
 #include "lib/tls/tortls.h"
+#include "lib/tls/x509.h"
 
 #include "core/or/or_handshake_certs_st.h"
 

+ 2 - 1
src/feature/relay/router.c

@@ -55,6 +55,7 @@
 #include "lib/tls/tortls.h"
 #include "lib/encoding/confline.h"
 #include "lib/crypt_ops/crypto_format.h"
+#include "lib/crypt_ops/crypto_init.h"
 
 /**
  * \file router.c
@@ -579,7 +580,7 @@ init_key_from_file(const char *fname, int generate, int severity,
           tor_log(severity, LD_GENERAL,"Error generating onion key");
           goto error;
         }
-        if (crypto_pk_check_key(prkey) <= 0) {
+        if (! crypto_pk_is_valid_private_key(prkey)) {
           tor_log(severity, LD_GENERAL,"Generated key seems invalid");
           goto error;
         }

+ 1 - 0
src/feature/relay/routerkeys.c

@@ -24,6 +24,7 @@
 #include "lib/crypt_ops/crypto_util.h"
 #include "lib/term/getpass.h"
 #include "lib/tls/tortls.h"
+#include "lib/tls/x509.h"
 #include "lib/crypt_ops/crypto_format.h"
 
 #define ENC_KEY_HEADER "Boxed Ed25519 key"

+ 1 - 1
src/feature/rend/rendmid.c

@@ -12,7 +12,7 @@
 #include "core/or/circuitlist.h"
 #include "core/or/circuituse.h"
 #include "app/config/config.h"
-#include "lib/crypt_ops/crypto.h"
+#include "lib/crypt_ops/crypto_cipher.h"
 #include "core/or/dos.h"
 #include "core/or/relay.h"
 #include "feature/rend/rendmid.h"

+ 1 - 1
src/feature/rend/rendservice.c

@@ -1652,7 +1652,7 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname)
         crypto_pk_free(prkey);
         goto err;
       }
-      if (crypto_pk_check_key(prkey) <= 0) {
+      if (! crypto_pk_is_valid_private_key(prkey)) {
         log_warn(LD_BUG,"Generated client key seems invalid");
         crypto_pk_free(prkey);
         goto err;

+ 106 - 0
src/lib/crypt_ops/aes_nss.c

@@ -0,0 +1,106 @@
+/* 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 aes_nss.c
+ * \brief Use NSS to implement AES_CTR.
+ **/
+
+#include "orconfig.h"
+#include "lib/crypt_ops/aes.h"
+#include "lib/crypt_ops/crypto_nss_mgt.h"
+#include "lib/crypt_ops/crypto_util.h"
+#include "lib/log/util_bug.h"
+
+DISABLE_GCC_WARNING(strict-prototypes)
+#include <pk11pub.h>
+#include <secerr.h>
+ENABLE_GCC_WARNING(strict-prototypes)
+
+aes_cnt_cipher_t *
+aes_new_cipher(const uint8_t *key, const uint8_t *iv,
+               int key_bits)
+{
+  const CK_MECHANISM_TYPE ckm = CKM_AES_CTR;
+  SECItem keyItem = { .type = siBuffer,
+                      .data = (unsigned char *)key,
+                      .len = (key_bits / 8) };
+  CK_AES_CTR_PARAMS params;
+  params.ulCounterBits = 128;
+  memcpy(params.cb, iv, 16);
+  SECItem ivItem = { .type = siBuffer,
+                     .data = (unsigned char *)&params,
+                     .len = sizeof(params) };
+  PK11SlotInfo *slot = NULL;
+  PK11SymKey *keyObj = NULL;
+  SECItem *ivObj = NULL;
+  PK11Context *result = NULL;
+
+  slot = PK11_GetBestSlot(ckm, NULL);
+  if (!slot)
+    goto err;
+
+  keyObj = PK11_ImportSymKey(slot, ckm, PK11_OriginUnwrap,
+                             CKA_ENCRYPT, &keyItem, NULL);
+  if (!keyObj)
+    goto err;
+
+  ivObj = PK11_ParamFromIV(ckm, &ivItem);
+  if (!ivObj)
+    goto err;
+
+  PORT_SetError(SEC_ERROR_IO);
+  result = PK11_CreateContextBySymKey(ckm, CKA_ENCRYPT, keyObj, ivObj);
+
+ err:
+  memwipe(&params, 0, sizeof(params));
+  if (ivObj)
+    SECITEM_FreeItem(ivObj, PR_TRUE);
+  if (keyObj)
+    PK11_FreeSymKey(keyObj);
+  if (slot)
+    PK11_FreeSlot(slot);
+
+  tor_assert(result);
+  return (aes_cnt_cipher_t *)result;
+}
+
+void
+aes_cipher_free_(aes_cnt_cipher_t *cipher)
+{
+  if (!cipher)
+    return;
+  PK11_DestroyContext((PK11Context*) cipher, PR_TRUE);
+}
+
+void
+aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data_, size_t len_)
+{
+  tor_assert(len_ <= INT_MAX);
+
+  SECStatus s;
+  PK11Context *ctx = (PK11Context*)cipher;
+  unsigned char *data = (unsigned char *)data_;
+  int len = (int) len_;
+  int result_len = 0;
+
+  s = PK11_CipherOp(ctx, data, &result_len, len, data, len);
+  tor_assert(s == SECSuccess);
+  tor_assert(result_len == len);
+}
+
+int
+evaluate_evp_for_aes(int force_value)
+{
+  (void)force_value;
+  return 0;
+}
+
+int
+evaluate_ctr_for_aes(void)
+{
+  return 0;
+}

+ 2 - 2
src/lib/crypt_ops/aes.c → src/lib/crypt_ops/aes_openssl.c

@@ -5,8 +5,8 @@
 /* See LICENSE for licensing information */
 
 /**
- * \file aes.c
- * \brief Implements a counter-mode stream cipher on top of AES.
+ * \file aes_openssl.c
+ * \brief Use OpenSSL to implement AES_CTR.
  **/
 
 #include "orconfig.h"

+ 6 - 0
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) */
 

+ 0 - 509
src/lib/crypt_ops/crypto.c

@@ -1,509 +0,0 @@
-/* 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.c
- * \brief Wrapper functions to present a consistent interface to
- * public-key and symmetric cryptography operations from OpenSSL and
- * other places.
- **/
-
-#include "orconfig.h"
-
-#ifdef _WIN32
-#include <winsock2.h>
-#include <windows.h>
-#include <wincrypt.h>
-/* Windows defines this; so does OpenSSL 0.9.8h and later. We don't actually
- * use either definition. */
-#undef OCSP_RESPONSE
-#endif /* defined(_WIN32) */
-
-#define CRYPTO_PRIVATE
-#include "lib/crypt_ops/compat_openssl.h"
-#include "lib/crypt_ops/crypto.h"
-#include "lib/crypt_ops/crypto_curve25519.h"
-#include "lib/crypt_ops/crypto_digest.h"
-#include "lib/crypt_ops/crypto_dh.h"
-#include "lib/crypt_ops/crypto_ed25519.h"
-#include "lib/crypt_ops/crypto_format.h"
-#include "lib/crypt_ops/crypto_rand.h"
-#include "lib/crypt_ops/crypto_rsa.h"
-#include "lib/crypt_ops/crypto_util.h"
-
-DISABLE_GCC_WARNING(redundant-decls)
-
-#include <openssl/err.h>
-#include <openssl/evp.h>
-#include <openssl/engine.h>
-#include <openssl/bn.h>
-#include <openssl/dh.h>
-#include <openssl/conf.h>
-#include <openssl/hmac.h>
-#include <openssl/ssl.h>
-
-ENABLE_GCC_WARNING(redundant-decls)
-
-#if __GNUC__ && GCC_VERSION >= 402
-#if GCC_VERSION >= 406
-#pragma GCC diagnostic pop
-#else
-#pragma GCC diagnostic warning "-Wredundant-decls"
-#endif
-#endif /* __GNUC__ && GCC_VERSION >= 402 */
-
-#ifdef HAVE_CTYPE_H
-#include <ctype.h>
-#endif
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#include "lib/log/log.h"
-#include "lib/log/util_bug.h"
-#include "lib/cc/torint.h"
-#include "lib/crypt_ops/aes.h"
-#include "lib/encoding/binascii.h"
-
-#include "keccak-tiny/keccak-tiny.h"
-
-#include "siphash.h"
-
-#include <string.h>
-
-/** Boolean: has OpenSSL's crypto been initialized? */
-static int crypto_early_initialized_ = 0;
-
-/** Boolean: has OpenSSL's crypto been initialized? */
-static int crypto_global_initialized_ = 0;
-
-#ifndef DISABLE_ENGINES
-/** Log any OpenSSL engines we're using at NOTICE. */
-static void
-log_engine(const char *fn, ENGINE *e)
-{
-  if (e) {
-    const char *name, *id;
-    name = ENGINE_get_name(e);
-    id = ENGINE_get_id(e);
-    log_notice(LD_CRYPTO, "Default OpenSSL engine for %s is %s [%s]",
-               fn, name?name:"?", id?id:"?");
-  } else {
-    log_info(LD_CRYPTO, "Using default implementation for %s", fn);
-  }
-}
-#endif /* !defined(DISABLE_ENGINES) */
-
-#ifndef DISABLE_ENGINES
-/** Try to load an engine in a shared library via fully qualified path.
- */
-static ENGINE *
-try_load_engine(const char *path, const char *engine)
-{
-  ENGINE *e = ENGINE_by_id("dynamic");
-  if (e) {
-    if (!ENGINE_ctrl_cmd_string(e, "ID", engine, 0) ||
-        !ENGINE_ctrl_cmd_string(e, "DIR_LOAD", "2", 0) ||
-        !ENGINE_ctrl_cmd_string(e, "DIR_ADD", path, 0) ||
-        !ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0)) {
-      ENGINE_free(e);
-      e = NULL;
-    }
-  }
-  return e;
-}
-#endif /* !defined(DISABLE_ENGINES) */
-
-static int have_seeded_siphash = 0;
-
-/** Set up the siphash key if we haven't already done so. */
-int
-crypto_init_siphash_key(void)
-{
-  struct sipkey key;
-  if (have_seeded_siphash)
-    return 0;
-
-  crypto_rand((char*) &key, sizeof(key));
-  siphash_set_global_key(&key);
-  have_seeded_siphash = 1;
-  return 0;
-}
-
-/** Initialize the crypto library.  Return 0 on success, -1 on failure.
- */
-int
-crypto_early_init(void)
-{
-  if (!crypto_early_initialized_) {
-
-    crypto_early_initialized_ = 1;
-
-#ifdef OPENSSL_1_1_API
-    OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS |
-                     OPENSSL_INIT_LOAD_CRYPTO_STRINGS |
-                     OPENSSL_INIT_ADD_ALL_CIPHERS |
-                     OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);
-#else
-    ERR_load_crypto_strings();
-    OpenSSL_add_all_algorithms();
-#endif
-
-    setup_openssl_threading();
-
-    unsigned long version_num = OpenSSL_version_num();
-    const char *version_str = OpenSSL_version(OPENSSL_VERSION);
-    if (version_num == OPENSSL_VERSION_NUMBER &&
-        !strcmp(version_str, OPENSSL_VERSION_TEXT)) {
-      log_info(LD_CRYPTO, "OpenSSL version matches version from headers "
-                 "(%lx: %s).", version_num, version_str);
-    } else {
-      log_warn(LD_CRYPTO, "OpenSSL version from headers does not match the "
-               "version we're running with. If you get weird crashes, that "
-               "might be why. (Compiled with %lx: %s; running with %lx: %s).",
-               (unsigned long)OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_TEXT,
-               version_num, version_str);
-    }
-
-    crypto_force_rand_ssleay();
-
-    if (crypto_seed_rng() < 0)
-      return -1;
-    if (crypto_init_siphash_key() < 0)
-      return -1;
-
-    curve25519_init();
-    ed25519_init();
-  }
-  return 0;
-}
-
-/** Initialize the crypto library.  Return 0 on success, -1 on failure.
- */
-int
-crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
-{
-  if (!crypto_global_initialized_) {
-    if (crypto_early_init() < 0)
-      return -1;
-
-    crypto_global_initialized_ = 1;
-
-    if (useAccel > 0) {
-#ifdef DISABLE_ENGINES
-      (void)accelName;
-      (void)accelDir;
-      log_warn(LD_CRYPTO, "No OpenSSL hardware acceleration support enabled.");
-#else
-      ENGINE *e = NULL;
-
-      log_info(LD_CRYPTO, "Initializing OpenSSL engine support.");
-      ENGINE_load_builtin_engines();
-      ENGINE_register_all_complete();
-
-      if (accelName) {
-        if (accelDir) {
-          log_info(LD_CRYPTO, "Trying to load dynamic OpenSSL engine \"%s\""
-                   " via path \"%s\".", accelName, accelDir);
-          e = try_load_engine(accelName, accelDir);
-        } else {
-          log_info(LD_CRYPTO, "Initializing dynamic OpenSSL engine \"%s\""
-                   " acceleration support.", accelName);
-          e = ENGINE_by_id(accelName);
-        }
-        if (!e) {
-          log_warn(LD_CRYPTO, "Unable to load dynamic OpenSSL engine \"%s\".",
-                   accelName);
-        } else {
-          log_info(LD_CRYPTO, "Loaded dynamic OpenSSL engine \"%s\".",
-                   accelName);
-        }
-      }
-      if (e) {
-        log_info(LD_CRYPTO, "Loaded OpenSSL hardware acceleration engine,"
-                 " setting default ciphers.");
-        ENGINE_set_default(e, ENGINE_METHOD_ALL);
-      }
-      /* Log, if available, the intersection of the set of algorithms
-         used by Tor and the set of algorithms available in the engine */
-      log_engine("RSA", ENGINE_get_default_RSA());
-      log_engine("DH", ENGINE_get_default_DH());
-#ifdef OPENSSL_1_1_API
-      log_engine("EC", ENGINE_get_default_EC());
-#else
-      log_engine("ECDH", ENGINE_get_default_ECDH());
-      log_engine("ECDSA", ENGINE_get_default_ECDSA());
-#endif /* defined(OPENSSL_1_1_API) */
-      log_engine("RAND", ENGINE_get_default_RAND());
-      log_engine("RAND (which we will not use)", ENGINE_get_default_RAND());
-      log_engine("SHA1", ENGINE_get_digest_engine(NID_sha1));
-      log_engine("3DES-CBC", ENGINE_get_cipher_engine(NID_des_ede3_cbc));
-      log_engine("AES-128-ECB", ENGINE_get_cipher_engine(NID_aes_128_ecb));
-      log_engine("AES-128-CBC", ENGINE_get_cipher_engine(NID_aes_128_cbc));
-#ifdef NID_aes_128_ctr
-      log_engine("AES-128-CTR", ENGINE_get_cipher_engine(NID_aes_128_ctr));
-#endif
-#ifdef NID_aes_128_gcm
-      log_engine("AES-128-GCM", ENGINE_get_cipher_engine(NID_aes_128_gcm));
-#endif
-      log_engine("AES-256-CBC", ENGINE_get_cipher_engine(NID_aes_256_cbc));
-#ifdef NID_aes_256_gcm
-      log_engine("AES-256-GCM", ENGINE_get_cipher_engine(NID_aes_256_gcm));
-#endif
-
-#endif /* defined(DISABLE_ENGINES) */
-    } else {
-      log_info(LD_CRYPTO, "NOT using OpenSSL engine support.");
-    }
-
-    if (crypto_force_rand_ssleay()) {
-      if (crypto_seed_rng() < 0)
-        return -1;
-    }
-
-    evaluate_evp_for_aes(-1);
-    evaluate_ctr_for_aes();
-  }
-  return 0;
-}
-
-/** Free crypto resources held by this thread. */
-void
-crypto_thread_cleanup(void)
-{
-#ifndef NEW_THREAD_API
-  ERR_remove_thread_state(NULL);
-#endif
-}
-
-/** Allocate and return a new symmetric cipher using the provided key and iv.
- * The key is <b>bits</b> bits long; the IV is CIPHER_IV_LEN bytes.  Both
- * must be provided. Key length must be 128, 192, or 256 */
-crypto_cipher_t *
-crypto_cipher_new_with_iv_and_bits(const uint8_t *key,
-                                   const uint8_t *iv,
-                                   int bits)
-{
-  tor_assert(key);
-  tor_assert(iv);
-
-  return aes_new_cipher((const uint8_t*)key, (const uint8_t*)iv, bits);
-}
-
-/** Allocate and return a new symmetric cipher using the provided key and iv.
- * The key is CIPHER_KEY_LEN bytes; the IV is CIPHER_IV_LEN bytes.  Both
- * must be provided.
- */
-crypto_cipher_t *
-crypto_cipher_new_with_iv(const char *key, const char *iv)
-{
-  return crypto_cipher_new_with_iv_and_bits((uint8_t*)key, (uint8_t*)iv,
-                                            128);
-}
-
-/** Return a new crypto_cipher_t with the provided <b>key</b> and an IV of all
- * zero bytes and key length <b>bits</b>.  Key length must be 128, 192, or
- * 256. */
-crypto_cipher_t *
-crypto_cipher_new_with_bits(const char *key, int bits)
-{
-  char zeroiv[CIPHER_IV_LEN];
-  memset(zeroiv, 0, sizeof(zeroiv));
-  return crypto_cipher_new_with_iv_and_bits((uint8_t*)key, (uint8_t*)zeroiv,
-                                            bits);
-}
-
-/** Return a new crypto_cipher_t with the provided <b>key</b> (of
- * CIPHER_KEY_LEN bytes) and an IV of all zero bytes.  */
-crypto_cipher_t *
-crypto_cipher_new(const char *key)
-{
-  return crypto_cipher_new_with_bits(key, 128);
-}
-
-/** Free a symmetric cipher.
- */
-void
-crypto_cipher_free_(crypto_cipher_t *env)
-{
-  if (!env)
-    return;
-
-  aes_cipher_free(env);
-}
-
-/** Copy <b>in</b> to the <b>outlen</b>-byte buffer <b>out</b>, adding spaces
- * every four characters. */
-void
-crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in)
-{
-  int n = 0;
-  char *end = out+outlen;
-  tor_assert(outlen < SIZE_T_CEILING);
-
-  while (*in && out<end) {
-    *out++ = *in++;
-    if (++n == 4 && *in && out<end) {
-      n = 0;
-      *out++ = ' ';
-    }
-  }
-  tor_assert(out<end);
-  *out = '\0';
-}
-
-/* symmetric crypto */
-
-/** Encrypt <b>fromlen</b> bytes from <b>from</b> using the cipher
- * <b>env</b>; on success, store the result to <b>to</b> and return 0.
- * Does not check for failure.
- */
-int
-crypto_cipher_encrypt(crypto_cipher_t *env, char *to,
-                      const char *from, size_t fromlen)
-{
-  tor_assert(env);
-  tor_assert(env);
-  tor_assert(from);
-  tor_assert(fromlen);
-  tor_assert(to);
-  tor_assert(fromlen < SIZE_T_CEILING);
-
-  memcpy(to, from, fromlen);
-  aes_crypt_inplace(env, to, fromlen);
-  return 0;
-}
-
-/** Decrypt <b>fromlen</b> bytes from <b>from</b> using the cipher
- * <b>env</b>; on success, store the result to <b>to</b> and return 0.
- * Does not check for failure.
- */
-int
-crypto_cipher_decrypt(crypto_cipher_t *env, char *to,
-                      const char *from, size_t fromlen)
-{
-  tor_assert(env);
-  tor_assert(from);
-  tor_assert(to);
-  tor_assert(fromlen < SIZE_T_CEILING);
-
-  memcpy(to, from, fromlen);
-  aes_crypt_inplace(env, to, fromlen);
-  return 0;
-}
-
-/** Encrypt <b>len</b> bytes on <b>from</b> using the cipher in <b>env</b>;
- * on success. Does not check for failure.
- */
-void
-crypto_cipher_crypt_inplace(crypto_cipher_t *env, char *buf, size_t len)
-{
-  tor_assert(len < SIZE_T_CEILING);
-  aes_crypt_inplace(env, buf, len);
-}
-
-/** Encrypt <b>fromlen</b> bytes (at least 1) from <b>from</b> with the key in
- * <b>key</b> to the buffer in <b>to</b> of length
- * <b>tolen</b>. <b>tolen</b> must be at least <b>fromlen</b> plus
- * CIPHER_IV_LEN bytes for the initialization vector. On success, return the
- * number of bytes written, on failure, return -1.
- */
-int
-crypto_cipher_encrypt_with_iv(const char *key,
-                              char *to, size_t tolen,
-                              const char *from, size_t fromlen)
-{
-  crypto_cipher_t *cipher;
-  tor_assert(from);
-  tor_assert(to);
-  tor_assert(fromlen < INT_MAX);
-
-  if (fromlen < 1)
-    return -1;
-  if (tolen < fromlen + CIPHER_IV_LEN)
-    return -1;
-
-  char iv[CIPHER_IV_LEN];
-  crypto_rand(iv, sizeof(iv));
-  cipher = crypto_cipher_new_with_iv(key, iv);
-
-  memcpy(to, iv, CIPHER_IV_LEN);
-  crypto_cipher_encrypt(cipher, to+CIPHER_IV_LEN, from, fromlen);
-  crypto_cipher_free(cipher);
-  memwipe(iv, 0, sizeof(iv));
-  return (int)(fromlen + CIPHER_IV_LEN);
-}
-
-/** Decrypt <b>fromlen</b> bytes (at least 1+CIPHER_IV_LEN) from <b>from</b>
- * with the key in <b>key</b> to the buffer in <b>to</b> of length
- * <b>tolen</b>. <b>tolen</b> must be at least <b>fromlen</b> minus
- * CIPHER_IV_LEN bytes for the initialization vector. On success, return the
- * number of bytes written, on failure, return -1.
- */
-int
-crypto_cipher_decrypt_with_iv(const char *key,
-                              char *to, size_t tolen,
-                              const char *from, size_t fromlen)
-{
-  crypto_cipher_t *cipher;
-  tor_assert(key);
-  tor_assert(from);
-  tor_assert(to);
-  tor_assert(fromlen < INT_MAX);
-
-  if (fromlen <= CIPHER_IV_LEN)
-    return -1;
-  if (tolen < fromlen - CIPHER_IV_LEN)
-    return -1;
-
-  cipher = crypto_cipher_new_with_iv(key, from);
-
-  crypto_cipher_encrypt(cipher, to, from+CIPHER_IV_LEN, fromlen-CIPHER_IV_LEN);
-  crypto_cipher_free(cipher);
-  return (int)(fromlen - CIPHER_IV_LEN);
-}
-
-/** @{ */
-/** Uninitialize the crypto library. Return 0 on success. Does not detect
- * failure.
- */
-int
-crypto_global_cleanup(void)
-{
-#ifndef OPENSSL_1_1_API
-  EVP_cleanup();
-#endif
-#ifndef NEW_THREAD_API
-  ERR_remove_thread_state(NULL);
-#endif
-#ifndef OPENSSL_1_1_API
-  ERR_free_strings();
-#endif
-
-  crypto_dh_free_all();
-
-#ifndef DISABLE_ENGINES
-#ifndef OPENSSL_1_1_API
-  ENGINE_cleanup();
-#endif
-#endif
-
-  CONF_modules_unload(1);
-#ifndef OPENSSL_1_1_API
-  CRYPTO_cleanup_all_ex_data();
-#endif
-
-  crypto_openssl_free_all();
-
-  crypto_early_initialized_ = 0;
-  crypto_global_initialized_ = 0;
-  have_seeded_siphash = 0;
-  siphash_unset_global_key();
-
-  return 0;
-}
-
-/** @} */

+ 190 - 0
src/lib/crypt_ops/crypto_cipher.c

@@ -0,0 +1,190 @@
+/* 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_cipher.c
+ * \brief Symmetric cryptography (low-level) with AES.
+ **/
+
+#include "orconfig.h"
+
+#include "lib/crypt_ops/crypto_cipher.h"
+#include "lib/crypt_ops/crypto_rand.h"
+#include "lib/crypt_ops/crypto_util.h"
+
+#include "lib/log/log.h"
+#include "lib/log/util_bug.h"
+#include "lib/cc/torint.h"
+#include "lib/crypt_ops/aes.h"
+
+#include <string.h>
+
+/** Allocate and return a new symmetric cipher using the provided key and iv.
+ * The key is <b>bits</b> bits long; the IV is CIPHER_IV_LEN bytes.  Both
+ * must be provided. Key length must be 128, 192, or 256 */
+crypto_cipher_t *
+crypto_cipher_new_with_iv_and_bits(const uint8_t *key,
+                                   const uint8_t *iv,
+                                   int bits)
+{
+  tor_assert(key);
+  tor_assert(iv);
+
+  return aes_new_cipher((const uint8_t*)key, (const uint8_t*)iv, bits);
+}
+
+/** Allocate and return a new symmetric cipher using the provided key and iv.
+ * The key is CIPHER_KEY_LEN bytes; the IV is CIPHER_IV_LEN bytes.  Both
+ * must be provided.
+ */
+crypto_cipher_t *
+crypto_cipher_new_with_iv(const char *key, const char *iv)
+{
+  return crypto_cipher_new_with_iv_and_bits((uint8_t*)key, (uint8_t*)iv,
+                                            128);
+}
+
+/** Return a new crypto_cipher_t with the provided <b>key</b> and an IV of all
+ * zero bytes and key length <b>bits</b>.  Key length must be 128, 192, or
+ * 256. */
+crypto_cipher_t *
+crypto_cipher_new_with_bits(const char *key, int bits)
+{
+  char zeroiv[CIPHER_IV_LEN];
+  memset(zeroiv, 0, sizeof(zeroiv));
+  return crypto_cipher_new_with_iv_and_bits((uint8_t*)key, (uint8_t*)zeroiv,
+                                            bits);
+}
+
+/** Return a new crypto_cipher_t with the provided <b>key</b> (of
+ * CIPHER_KEY_LEN bytes) and an IV of all zero bytes.  */
+crypto_cipher_t *
+crypto_cipher_new(const char *key)
+{
+  return crypto_cipher_new_with_bits(key, 128);
+}
+
+/** Free a symmetric cipher.
+ */
+void
+crypto_cipher_free_(crypto_cipher_t *env)
+{
+  if (!env)
+    return;
+
+  aes_cipher_free(env);
+}
+
+/* symmetric crypto */
+
+/** Encrypt <b>fromlen</b> bytes from <b>from</b> using the cipher
+ * <b>env</b>; on success, store the result to <b>to</b> and return 0.
+ * Does not check for failure.
+ */
+int
+crypto_cipher_encrypt(crypto_cipher_t *env, char *to,
+                      const char *from, size_t fromlen)
+{
+  tor_assert(env);
+  tor_assert(env);
+  tor_assert(from);
+  tor_assert(fromlen);
+  tor_assert(to);
+  tor_assert(fromlen < SIZE_T_CEILING);
+
+  memcpy(to, from, fromlen);
+  aes_crypt_inplace(env, to, fromlen);
+  return 0;
+}
+
+/** Decrypt <b>fromlen</b> bytes from <b>from</b> using the cipher
+ * <b>env</b>; on success, store the result to <b>to</b> and return 0.
+ * Does not check for failure.
+ */
+int
+crypto_cipher_decrypt(crypto_cipher_t *env, char *to,
+                      const char *from, size_t fromlen)
+{
+  tor_assert(env);
+  tor_assert(from);
+  tor_assert(to);
+  tor_assert(fromlen < SIZE_T_CEILING);
+
+  memcpy(to, from, fromlen);
+  aes_crypt_inplace(env, to, fromlen);
+  return 0;
+}
+
+/** Encrypt <b>len</b> bytes on <b>from</b> using the cipher in <b>env</b>;
+ * on success. Does not check for failure.
+ */
+void
+crypto_cipher_crypt_inplace(crypto_cipher_t *env, char *buf, size_t len)
+{
+  tor_assert(len < SIZE_T_CEILING);
+  aes_crypt_inplace(env, buf, len);
+}
+
+/** Encrypt <b>fromlen</b> bytes (at least 1) from <b>from</b> with the key in
+ * <b>key</b> to the buffer in <b>to</b> of length
+ * <b>tolen</b>. <b>tolen</b> must be at least <b>fromlen</b> plus
+ * CIPHER_IV_LEN bytes for the initialization vector. On success, return the
+ * number of bytes written, on failure, return -1.
+ */
+int
+crypto_cipher_encrypt_with_iv(const char *key,
+                              char *to, size_t tolen,
+                              const char *from, size_t fromlen)
+{
+  crypto_cipher_t *cipher;
+  tor_assert(from);
+  tor_assert(to);
+  tor_assert(fromlen < INT_MAX);
+
+  if (fromlen < 1)
+    return -1;
+  if (tolen < fromlen + CIPHER_IV_LEN)
+    return -1;
+
+  char iv[CIPHER_IV_LEN];
+  crypto_rand(iv, sizeof(iv));
+  cipher = crypto_cipher_new_with_iv(key, iv);
+
+  memcpy(to, iv, CIPHER_IV_LEN);
+  crypto_cipher_encrypt(cipher, to+CIPHER_IV_LEN, from, fromlen);
+  crypto_cipher_free(cipher);
+  memwipe(iv, 0, sizeof(iv));
+  return (int)(fromlen + CIPHER_IV_LEN);
+}
+
+/** Decrypt <b>fromlen</b> bytes (at least 1+CIPHER_IV_LEN) from <b>from</b>
+ * with the key in <b>key</b> to the buffer in <b>to</b> of length
+ * <b>tolen</b>. <b>tolen</b> must be at least <b>fromlen</b> minus
+ * CIPHER_IV_LEN bytes for the initialization vector. On success, return the
+ * number of bytes written, on failure, return -1.
+ */
+int
+crypto_cipher_decrypt_with_iv(const char *key,
+                              char *to, size_t tolen,
+                              const char *from, size_t fromlen)
+{
+  crypto_cipher_t *cipher;
+  tor_assert(key);
+  tor_assert(from);
+  tor_assert(to);
+  tor_assert(fromlen < INT_MAX);
+
+  if (fromlen <= CIPHER_IV_LEN)
+    return -1;
+  if (tolen < fromlen - CIPHER_IV_LEN)
+    return -1;
+
+  cipher = crypto_cipher_new_with_iv(key, from);
+
+  crypto_cipher_encrypt(cipher, to, from+CIPHER_IV_LEN, fromlen-CIPHER_IV_LEN);
+  crypto_cipher_free(cipher);
+  return (int)(fromlen - CIPHER_IV_LEN);
+}

+ 4 - 21
src/lib/crypt_ops/crypto.h → src/lib/crypt_ops/crypto_cipher.h

@@ -5,19 +5,18 @@
 /* See LICENSE for licensing information */
 
 /**
- * \file crypto.h
+ * \file crypto_cipher.h
  *
- * \brief Headers for crypto.c
+ * \brief Headers for crypto_cipher.c
  **/
 
-#ifndef TOR_CRYPTO_H
-#define TOR_CRYPTO_H
+#ifndef TOR_CRYPTO_CIPHER_H
+#define TOR_CRYPTO_CIPHER_H
 
 #include "orconfig.h"
 
 #include <stdio.h>
 #include "lib/cc/torint.h"
-#include "lib/crypt_ops/crypto_rsa.h"
 
 /** Length of our symmetric cipher's keys of 128-bit. */
 #define CIPHER_KEY_LEN 16
@@ -26,22 +25,8 @@
 /** Length of our symmetric cipher's keys of 256-bit. */
 #define CIPHER256_KEY_LEN 32
 
-/** Length of encoded public key fingerprints, including space; but not
- * including terminating NUL. */
-#define FINGERPRINT_LEN 49
-
 typedef struct aes_cnt_cipher crypto_cipher_t;
 
-/* global state */
-int crypto_init_siphash_key(void);
-int crypto_early_init(void) ATTR_WUR;
-int crypto_global_init(int hardwareAccel,
-                       const char *accelName,
-                       const char *accelPath) ATTR_WUR;
-
-void crypto_thread_cleanup(void);
-int crypto_global_cleanup(void);
-
 /* environment setup */
 crypto_cipher_t *crypto_cipher_new(const char *key);
 crypto_cipher_t *crypto_cipher_new_with_bits(const char *key, int bits);
@@ -69,6 +54,4 @@ int crypto_cipher_decrypt_with_iv(const char *key,
                                   char *to, size_t tolen,
                                   const char *from, size_t fromlen);
 
-void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in);
-
 #endif /* !defined(TOR_CRYPTO_H) */

+ 43 - 441
src/lib/crypt_ops/crypto_dh.c

@@ -7,6 +7,8 @@
 /**
  * \file crypto_dh.c
  * \brief Block of functions related with DH utilities and operations.
+ *    over Z_p.  We aren't using this for any new crypto -- EC is more
+ *    efficient.
  **/
 
 #include "lib/crypt_ops/compat_openssl.h"
@@ -17,411 +19,50 @@
 #include "lib/log/log.h"
 #include "lib/log/util_bug.h"
 
-DISABLE_GCC_WARNING(redundant-decls)
-
-#include <openssl/dh.h>
-
-ENABLE_GCC_WARNING(redundant-decls)
-
-#include <openssl/bn.h>
-#include <string.h>
-
-/** A structure to hold the first half (x, g^x) of a Diffie-Hellman handshake
- * while we're waiting for the second.*/
-struct crypto_dh_t {
-  DH *dh; /**< The openssl DH object */
-};
-
-static int tor_check_dh_key(int severity, const BIGNUM *bn);
-
-/** Used by tortls.c: Get the DH* from a crypto_dh_t.
- */
-DH *
-crypto_dh_get_dh_(crypto_dh_t *dh)
-{
-  return dh->dh;
-}
-
 /** Our DH 'g' parameter */
-#define DH_GENERATOR 2
-
-/** Shared P parameter for our circuit-crypto DH key exchanges. */
-static BIGNUM *dh_param_p = NULL;
-/** Shared P parameter for our TLS DH key exchanges. */
-static BIGNUM *dh_param_p_tls = NULL;
-/** Shared G parameter for our DH key exchanges. */
-static BIGNUM *dh_param_g = NULL;
-
-/** Validate a given set of Diffie-Hellman parameters.  This is moderately
- * computationally expensive (milliseconds), so should only be called when
- * the DH parameters change. Returns 0 on success, * -1 on failure.
+const unsigned DH_GENERATOR = 2;
+/** This is the 1024-bit safe prime that Apache uses for its DH stuff; see
+ * modules/ssl/ssl_engine_dh.c; Apache also uses a generator of 2 with this
+ * prime.
  */
-static int
-crypto_validate_dh_params(const BIGNUM *p, const BIGNUM *g)
-{
-  DH *dh = NULL;
-  int ret = -1;
-
-  /* Copy into a temporary DH object, just so that DH_check() can be called. */
-  if (!(dh = DH_new()))
-      goto out;
-#ifdef OPENSSL_1_1_API
-  BIGNUM *dh_p, *dh_g;
-  if (!(dh_p = BN_dup(p)))
-    goto out;
-  if (!(dh_g = BN_dup(g)))
-    goto out;
-  if (!DH_set0_pqg(dh, dh_p, NULL, dh_g))
-    goto out;
-#else /* !(defined(OPENSSL_1_1_API)) */
-  if (!(dh->p = BN_dup(p)))
-    goto out;
-  if (!(dh->g = BN_dup(g)))
-    goto out;
-#endif /* defined(OPENSSL_1_1_API) */
-
-  /* Perform the validation. */
-  int codes = 0;
-  if (!DH_check(dh, &codes))
-    goto out;
-  if (BN_is_word(g, DH_GENERATOR_2)) {
-    /* Per https://wiki.openssl.org/index.php/Diffie-Hellman_parameters
-     *
-     * OpenSSL checks the prime is congruent to 11 when g = 2; while the
-     * IETF's primes are congruent to 23 when g = 2.
-     */
-    BN_ULONG residue = BN_mod_word(p, 24);
-    if (residue == 11 || residue == 23)
-      codes &= ~DH_NOT_SUITABLE_GENERATOR;
-  }
-  if (codes != 0) /* Specifics on why the params suck is irrelevant. */
-    goto out;
-
-  /* Things are probably not evil. */
-  ret = 0;
-
- out:
-  if (dh)
-    DH_free(dh);
-  return ret;
-}
-
-/** Set the global Diffie-Hellman generator, used for both TLS and internal
- * DH stuff.
+const char TLS_DH_PRIME[] =
+  "D67DE440CBBBDC1936D693D34AFD0AD50C84D239A45F520BB88174CB98"
+  "BCE951849F912E639C72FB13B4B4D7177E16D55AC179BA420B2A29FE324A"
+  "467A635E81FF5901377BEDDCFD33168A461AAD3B72DAE8860078045B07A7"
+  "DBCA7874087D1510EA9FCC9DDD330507DD62DB88AEAA747DE0F4D6E2BD68"
+  "B0E7393E0F24218EB3";
+/**
+ * This is from rfc2409, section 6.2.  It's a safe prime, and
+ * supposedly it equals:
+ * 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }.
  */
-static void
-crypto_set_dh_generator(void)
-{
-  BIGNUM *generator;
-  int r;
+const char OAKLEY_PRIME_2[] =
+  "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+  "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+  "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+  "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+  "49286651ECE65381FFFFFFFFFFFFFFFF";
 
-  if (dh_param_g)
-    return;
-
-  generator = BN_new();
-  tor_assert(generator);
-
-  r = BN_set_word(generator, DH_GENERATOR);
-  tor_assert(r);
-
-  dh_param_g = generator;
-}
-
-/** Set the global TLS Diffie-Hellman modulus.  Use the Apache mod_ssl DH
- * modulus. */
 void
-crypto_set_tls_dh_prime(void)
-{
-  BIGNUM *tls_prime = NULL;
-  int r;
-
-  /* If the space is occupied, free the previous TLS DH prime */
-  if (BUG(dh_param_p_tls)) {
-    /* LCOV_EXCL_START
-     *
-     * We shouldn't be calling this twice.
-     */
-    BN_clear_free(dh_param_p_tls);
-    dh_param_p_tls = NULL;
-    /* LCOV_EXCL_STOP */
-  }
-
-  tls_prime = BN_new();
-  tor_assert(tls_prime);
-
-  /* This is the 1024-bit safe prime that Apache uses for its DH stuff; see
-   * modules/ssl/ssl_engine_dh.c; Apache also uses a generator of 2 with this
-   * prime.
-   */
-  r = BN_hex2bn(&tls_prime,
-               "D67DE440CBBBDC1936D693D34AFD0AD50C84D239A45F520BB88174CB98"
-               "BCE951849F912E639C72FB13B4B4D7177E16D55AC179BA420B2A29FE324A"
-               "467A635E81FF5901377BEDDCFD33168A461AAD3B72DAE8860078045B07A7"
-               "DBCA7874087D1510EA9FCC9DDD330507DD62DB88AEAA747DE0F4D6E2BD68"
-               "B0E7393E0F24218EB3");
-  tor_assert(r);
-
-  tor_assert(tls_prime);
-
-  dh_param_p_tls = tls_prime;
-  crypto_set_dh_generator();
-  tor_assert(0 == crypto_validate_dh_params(dh_param_p_tls, dh_param_g));
-}
-
-/** Initialize dh_param_p and dh_param_g if they are not already
- * set. */
-static void
-init_dh_param(void)
-{
-  BIGNUM *circuit_dh_prime;
-  int r;
-  if (BUG(dh_param_p && dh_param_g))
-    return; // LCOV_EXCL_LINE This function isn't supposed to be called twice.
-
-  circuit_dh_prime = BN_new();
-  tor_assert(circuit_dh_prime);
-
-  /* This is from rfc2409, section 6.2.  It's a safe prime, and
-     supposedly it equals:
-        2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }.
-  */
-  r = BN_hex2bn(&circuit_dh_prime,
-                "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
-                "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
-                "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
-                "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
-                "49286651ECE65381FFFFFFFFFFFFFFFF");
-  tor_assert(r);
-
-  /* Set the new values as the global DH parameters. */
-  dh_param_p = circuit_dh_prime;
-  crypto_set_dh_generator();
-  tor_assert(0 == crypto_validate_dh_params(dh_param_p, dh_param_g));
-
-  if (!dh_param_p_tls) {
-    crypto_set_tls_dh_prime();
-  }
-}
-
-/** Number of bits to use when choosing the x or y value in a Diffie-Hellman
- * handshake.  Since we exponentiate by this value, choosing a smaller one
- * lets our handhake go faster.
- */
-#define DH_PRIVATE_KEY_BITS 320
-
-/** Allocate and return a new DH object for a key exchange. Returns NULL on
- * failure.
- */
-crypto_dh_t *
-crypto_dh_new(int dh_type)
-{
-  crypto_dh_t *res = tor_malloc_zero(sizeof(crypto_dh_t));
-
-  tor_assert(dh_type == DH_TYPE_CIRCUIT || dh_type == DH_TYPE_TLS ||
-             dh_type == DH_TYPE_REND);
-
-  if (!dh_param_p)
-    init_dh_param();
-
-  if (!(res->dh = DH_new()))
-    goto err;
-
-#ifdef OPENSSL_1_1_API
-  BIGNUM *dh_p = NULL, *dh_g = NULL;
-
-  if (dh_type == DH_TYPE_TLS) {
-    dh_p = BN_dup(dh_param_p_tls);
-  } else {
-    dh_p = BN_dup(dh_param_p);
-  }
-  if (!dh_p)
-    goto err;
-
-  dh_g = BN_dup(dh_param_g);
-  if (!dh_g) {
-    BN_free(dh_p);
-    goto err;
-  }
-
-  if (!DH_set0_pqg(res->dh, dh_p, NULL, dh_g)) {
-    goto err;
-  }
-
-  if (!DH_set_length(res->dh, DH_PRIVATE_KEY_BITS))
-    goto err;
-#else /* !(defined(OPENSSL_1_1_API)) */
-  if (dh_type == DH_TYPE_TLS) {
-    if (!(res->dh->p = BN_dup(dh_param_p_tls)))
-      goto err;
-  } else {
-    if (!(res->dh->p = BN_dup(dh_param_p)))
-      goto err;
-  }
-
-  if (!(res->dh->g = BN_dup(dh_param_g)))
-    goto err;
-
-  res->dh->length = DH_PRIVATE_KEY_BITS;
-#endif /* defined(OPENSSL_1_1_API) */
-
-  return res;
-
-  /* LCOV_EXCL_START
-   * This error condition is only reached when an allocation fails */
- err:
-  crypto_log_errors(LOG_WARN, "creating DH object");
-  if (res->dh) DH_free(res->dh); /* frees p and g too */
-  tor_free(res);
-  return NULL;
-  /* LCOV_EXCL_STOP */
-}
-
-/** Return a copy of <b>dh</b>, sharing its internal state. */
-crypto_dh_t *
-crypto_dh_dup(const crypto_dh_t *dh)
+crypto_dh_init(void)
 {
-  crypto_dh_t *dh_new = tor_malloc_zero(sizeof(crypto_dh_t));
-  tor_assert(dh);
-  tor_assert(dh->dh);
-  dh_new->dh = dh->dh;
-  DH_up_ref(dh->dh);
-  return dh_new;
-}
-
-/** Return the length of the DH key in <b>dh</b>, in bytes.
- */
-int
-crypto_dh_get_bytes(crypto_dh_t *dh)
-{
-  tor_assert(dh);
-  return DH_size(dh->dh);
-}
-
-/** Generate \<x,g^x\> for our part of the key exchange.  Return 0 on
- * success, -1 on failure.
- */
-int
-crypto_dh_generate_public(crypto_dh_t *dh)
-{
-#ifndef OPENSSL_1_1_API
- again:
+#ifdef ENABLE_OPENSSL
+  crypto_dh_init_openssl();
 #endif
-  if (!DH_generate_key(dh->dh)) {
-    /* LCOV_EXCL_START
-     * To test this we would need some way to tell openssl to break DH. */
-    crypto_log_errors(LOG_WARN, "generating DH key");
-    return -1;
-    /* LCOV_EXCL_STOP */
-  }
-#ifdef OPENSSL_1_1_API
-  /* OpenSSL 1.1.x doesn't appear to let you regenerate a DH key, without
-   * recreating the DH object.  I have no idea what sort of aliasing madness
-   * can occur here, so do the check, and just bail on failure.
-   */
-  const BIGNUM *pub_key, *priv_key;
-  DH_get0_key(dh->dh, &pub_key, &priv_key);
-  if (tor_check_dh_key(LOG_WARN, pub_key)<0) {
-    log_warn(LD_CRYPTO, "Weird! Our own DH key was invalid.  I guess once-in-"
-             "the-universe chances really do happen.  Treating as a failure.");
-    return -1;
-  }
-#else /* !(defined(OPENSSL_1_1_API)) */
-  if (tor_check_dh_key(LOG_WARN, dh->dh->pub_key)<0) {
-    /* LCOV_EXCL_START
-     * If this happens, then openssl's DH implementation is busted. */
-    log_warn(LD_CRYPTO, "Weird! Our own DH key was invalid.  I guess once-in-"
-             "the-universe chances really do happen.  Trying again.");
-    /* Free and clear the keys, so OpenSSL will actually try again. */
-    BN_clear_free(dh->dh->pub_key);
-    BN_clear_free(dh->dh->priv_key);
-    dh->dh->pub_key = dh->dh->priv_key = NULL;
-    goto again;
-    /* LCOV_EXCL_STOP */
-  }
-#endif /* defined(OPENSSL_1_1_API) */
-  return 0;
-}
-
-/** Generate g^x as necessary, and write the g^x for the key exchange
- * as a <b>pubkey_len</b>-byte value into <b>pubkey</b>. Return 0 on
- * success, -1 on failure.  <b>pubkey_len</b> must be \>= DH1024_KEY_LEN.
- */
-int
-crypto_dh_get_public(crypto_dh_t *dh, char *pubkey, size_t pubkey_len)
-{
-  int bytes;
-  tor_assert(dh);
-
-  const BIGNUM *dh_pub;
-
-#ifdef OPENSSL_1_1_API
-  const BIGNUM *dh_priv;
-  DH_get0_key(dh->dh, &dh_pub, &dh_priv);
-#else
-  dh_pub = dh->dh->pub_key;
-#endif /* defined(OPENSSL_1_1_API) */
-
-  if (!dh_pub) {
-    if (crypto_dh_generate_public(dh)<0)
-      return -1;
-    else {
-#ifdef OPENSSL_1_1_API
-      DH_get0_key(dh->dh, &dh_pub, &dh_priv);
-#else
-      dh_pub = dh->dh->pub_key;
+#ifdef ENABLE_NSS
+  crypto_dh_init_nss();
 #endif
-    }
-  }
-
-  tor_assert(dh_pub);
-  bytes = BN_num_bytes(dh_pub);
-  tor_assert(bytes >= 0);
-  if (pubkey_len < (size_t)bytes) {
-    log_warn(LD_CRYPTO,
-             "Weird! pubkey_len (%d) was smaller than DH1024_KEY_LEN (%d)",
-             (int) pubkey_len, bytes);
-    return -1;
-  }
-
-  memset(pubkey, 0, pubkey_len);
-  BN_bn2bin(dh_pub, (unsigned char*)(pubkey+(pubkey_len-bytes)));
-
-  return 0;
 }
 
-/** Check for bad Diffie-Hellman public keys (g^x).  Return 0 if the key is
- * okay (in the subgroup [2,p-2]), or -1 if it's bad.
- * See http://www.cl.cam.ac.uk/ftp/users/rja14/psandqs.ps.gz for some tips.
- */
-static int
-tor_check_dh_key(int severity, const BIGNUM *bn)
+void
+crypto_dh_free_all(void)
 {
-  BIGNUM *x;
-  char *s;
-  tor_assert(bn);
-  x = BN_new();
-  tor_assert(x);
-  if (BUG(!dh_param_p))
-    init_dh_param(); //LCOV_EXCL_LINE we already checked whether we did this.
-  BN_set_word(x, 1);
-  if (BN_cmp(bn,x)<=0) {
-    log_fn(severity, LD_CRYPTO, "DH key must be at least 2.");
-    goto err;
-  }
-  BN_copy(x,dh_param_p);
-  BN_sub_word(x, 1);
-  if (BN_cmp(bn,x)>=0) {
-    log_fn(severity, LD_CRYPTO, "DH key must be at most p-2.");
-    goto err;
-  }
-  BN_clear_free(x);
-  return 0;
- err:
-  BN_clear_free(x);
-  s = BN_bn2hex(bn);
-  log_fn(severity, LD_CRYPTO, "Rejecting insecure DH key [%s]", s);
-  OPENSSL_free(s);
-  return -1;
+#ifdef ENABLE_OPENSSL
+  crypto_dh_free_all_openssl();
+#endif
+#ifdef ENABLE_NSS
+  crypto_dh_free_all_nss();
+#endif
 }
 
 /** Given a DH key exchange object, and our peer's value of g^y (as a
@@ -439,31 +80,20 @@ crypto_dh_compute_secret(int severity, crypto_dh_t *dh,
                          const char *pubkey, size_t pubkey_len,
                          char *secret_out, size_t secret_bytes_out)
 {
-  char *secret_tmp = NULL;
-  BIGNUM *pubkey_bn = NULL;
-  size_t secret_len=0, secret_tmp_len=0;
-  int result=0;
-  tor_assert(dh);
   tor_assert(secret_bytes_out/DIGEST_LEN <= 255);
-  tor_assert(pubkey_len < INT_MAX);
 
-  if (!(pubkey_bn = BN_bin2bn((const unsigned char*)pubkey,
-                              (int)pubkey_len, NULL)))
-    goto error;
-  if (tor_check_dh_key(severity, pubkey_bn)<0) {
-    /* Check for invalid public keys. */
-    log_fn(severity, LD_CRYPTO,"Rejected invalid g^x");
-    goto error;
-  }
+  unsigned char *secret_tmp = NULL;
+  size_t secret_len=0, secret_tmp_len=0;
   secret_tmp_len = crypto_dh_get_bytes(dh);
   secret_tmp = tor_malloc(secret_tmp_len);
-  result = DH_compute_key((unsigned char*)secret_tmp, pubkey_bn, dh->dh);
-  if (result < 0) {
-    log_warn(LD_CRYPTO,"DH_compute_key() failed.");
+
+  ssize_t result = crypto_dh_handshake(severity, dh, pubkey, pubkey_len,
+                                   secret_tmp, secret_tmp_len);
+  if (result < 0)
     goto error;
-  }
+
   secret_len = result;
-  if (crypto_expand_key_material_TAP((uint8_t*)secret_tmp, secret_len,
+  if (crypto_expand_key_material_TAP(secret_tmp, secret_len,
                                      (uint8_t*)secret_out, secret_bytes_out)<0)
     goto error;
   secret_len = secret_bytes_out;
@@ -472,9 +102,6 @@ crypto_dh_compute_secret(int severity, crypto_dh_t *dh,
  error:
   result = -1;
  done:
-  crypto_log_errors(LOG_WARN, "completing DH handshake");
-  if (pubkey_bn)
-    BN_clear_free(pubkey_bn);
   if (secret_tmp) {
     memwipe(secret_tmp, 0, secret_tmp_len);
     tor_free(secret_tmp);
@@ -484,28 +111,3 @@ crypto_dh_compute_secret(int severity, crypto_dh_t *dh,
   else
     return secret_len;
 }
-
-/** Free a DH key exchange object.
- */
-void
-crypto_dh_free_(crypto_dh_t *dh)
-{
-  if (!dh)
-    return;
-  tor_assert(dh->dh);
-  DH_free(dh->dh);
-  tor_free(dh);
-}
-
-void
-crypto_dh_free_all(void)
-{
-  if (dh_param_p)
-    BN_clear_free(dh_param_p);
-  if (dh_param_p_tls)
-    BN_clear_free(dh_param_p_tls);
-  if (dh_param_g)
-    BN_clear_free(dh_param_g);
-
-  dh_param_p = dh_param_p_tls = dh_param_g = NULL;
-}

+ 20 - 3
src/lib/crypt_ops/crypto_dh.h

@@ -19,11 +19,15 @@
 
 typedef struct crypto_dh_t crypto_dh_t;
 
+extern const unsigned DH_GENERATOR;
+extern const char TLS_DH_PRIME[];
+extern const char OAKLEY_PRIME_2[];
+
 /* Key negotiation */
 #define DH_TYPE_CIRCUIT 1
 #define DH_TYPE_REND 2
 #define DH_TYPE_TLS 3
-void crypto_set_tls_dh_prime(void);
+void crypto_dh_init(void);
 crypto_dh_t *crypto_dh_new(int dh_type);
 crypto_dh_t *crypto_dh_dup(const crypto_dh_t *dh);
 int crypto_dh_get_bytes(crypto_dh_t *dh);
@@ -36,12 +40,25 @@ ssize_t crypto_dh_compute_secret(int severity, crypto_dh_t *dh,
 void crypto_dh_free_(crypto_dh_t *dh);
 #define crypto_dh_free(dh) FREE_AND_NULL(crypto_dh_t, crypto_dh_free_, (dh))
 
-/* Crypto DH free */
+ssize_t crypto_dh_handshake(int severity, crypto_dh_t *dh,
+                            const char *pubkey, size_t pubkey_len,
+                            unsigned char *secret_out,
+                            size_t secret_bytes_out);
+
 void crypto_dh_free_all(void);
 
 /* Prototypes for private functions only used by tortls.c, crypto.c, and the
  * unit tests. */
 struct dh_st;
-struct dh_st *crypto_dh_get_dh_(crypto_dh_t *dh);
+struct dh_st *crypto_dh_new_openssl_tls(void);
+
+#ifdef ENABLE_OPENSSL
+void crypto_dh_init_openssl(void);
+void crypto_dh_free_all_openssl(void);
+#endif
+#ifdef ENABLE_NSS
+void crypto_dh_init_nss(void);
+void crypto_dh_free_all_nss(void);
+#endif
 
 #endif /* !defined(TOR_CRYPTO_DH_H) */

+ 207 - 0
src/lib/crypt_ops/crypto_dh_nss.c

@@ -0,0 +1,207 @@
+/* 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_dh_nss.h
+ *
+ * \brief NSS implementation of Diffie-Hellman over Z_p.
+ **/
+
+#include "lib/crypt_ops/crypto_dh.h"
+#include "lib/crypt_ops/crypto_nss_mgt.h"
+
+#include "lib/encoding/binascii.h"
+#include "lib/log/util_bug.h"
+#include "lib/malloc/malloc.h"
+
+#include <cryptohi.h>
+#include <keyhi.h>
+#include <pk11pub.h>
+
+static int dh_initialized = 0;
+static SECKEYDHParams tls_dh_param, circuit_dh_param;
+static unsigned char tls_dh_prime_data[DH1024_KEY_LEN];
+static unsigned char circuit_dh_prime_data[DH1024_KEY_LEN];
+static unsigned char dh_generator_data[1];
+
+void
+crypto_dh_init_nss(void)
+{
+  if (dh_initialized)
+    return;
+
+  int r;
+  r = base16_decode((char*)tls_dh_prime_data,
+                    sizeof(tls_dh_prime_data),
+                    TLS_DH_PRIME, strlen(TLS_DH_PRIME));
+  tor_assert(r == DH1024_KEY_LEN);
+  r = base16_decode((char*)circuit_dh_prime_data,
+                    sizeof(circuit_dh_prime_data),
+                    OAKLEY_PRIME_2, strlen(OAKLEY_PRIME_2));
+  tor_assert(r == DH1024_KEY_LEN);
+  dh_generator_data[0] = DH_GENERATOR;
+
+  tls_dh_param.prime.data = tls_dh_prime_data;
+  tls_dh_param.prime.len = DH1024_KEY_LEN;
+  tls_dh_param.base.data = dh_generator_data;
+  tls_dh_param.base.len = 1;
+
+  circuit_dh_param.prime.data = circuit_dh_prime_data;
+  circuit_dh_param.prime.len = DH1024_KEY_LEN;
+  circuit_dh_param.base.data = dh_generator_data;
+  circuit_dh_param.base.len = 1;
+}
+
+void
+crypto_dh_free_all_nss(void)
+{
+  dh_initialized = 0;
+}
+
+struct crypto_dh_t {
+  int dh_type; // XXXX let's remove this later on.
+  SECKEYPrivateKey *seckey;
+  SECKEYPublicKey *pubkey;
+};
+
+crypto_dh_t *
+crypto_dh_new(int dh_type)
+{
+  crypto_dh_t *r = tor_malloc_zero(sizeof(crypto_dh_t));
+  r->dh_type = dh_type;
+  return r;
+}
+
+crypto_dh_t *
+crypto_dh_dup(const crypto_dh_t *dh)
+{
+  tor_assert(dh);
+  crypto_dh_t *r = crypto_dh_new(dh->dh_type);
+  if (dh->seckey)
+    r->seckey = SECKEY_CopyPrivateKey(dh->seckey);
+  if (dh->pubkey)
+    r->pubkey = SECKEY_CopyPublicKey(dh->pubkey);
+  return r;
+}
+
+int
+crypto_dh_get_bytes(crypto_dh_t *dh)
+{
+  (void)dh;
+  return DH1024_KEY_LEN;
+}
+
+int
+crypto_dh_generate_public(crypto_dh_t *dh)
+{
+  tor_assert(dh);
+  SECKEYDHParams *p;
+  if (dh->dh_type == DH_TYPE_TLS)
+    p = &tls_dh_param;
+  else
+    p = &circuit_dh_param;
+
+  dh->seckey = SECKEY_CreateDHPrivateKey(p, &dh->pubkey, NULL);
+  if (!dh->seckey || !dh->pubkey)
+    return -1;
+  else
+    return 0;
+}
+int
+crypto_dh_get_public(crypto_dh_t *dh, char *pubkey_out,
+                     size_t pubkey_out_len)
+{
+  tor_assert(dh);
+  tor_assert(pubkey_out);
+  if (!dh->pubkey) {
+    if (crypto_dh_generate_public(dh) < 0)
+      return -1;
+  }
+
+  const SECItem *item = &dh->pubkey->u.dh.publicValue;
+
+  if (item->len > pubkey_out_len)
+    return -1;
+
+  /* Left-pad the result with 0s. */
+  memset(pubkey_out, 0, pubkey_out_len);
+  memcpy(pubkey_out + pubkey_out_len - item->len,
+         item->data,
+         item->len);
+
+  return 0;
+}
+
+void
+crypto_dh_free_(crypto_dh_t *dh)
+{
+  if (!dh)
+    return;
+  if (dh->seckey)
+    SECKEY_DestroyPrivateKey(dh->seckey);
+  if (dh->pubkey)
+    SECKEY_DestroyPublicKey(dh->pubkey);
+  tor_free(dh);
+}
+
+ssize_t
+crypto_dh_handshake(int severity, crypto_dh_t *dh,
+                    const char *pubkey, size_t pubkey_len,
+                    unsigned char *secret_out,
+                    size_t secret_bytes_out)
+{
+  tor_assert(dh);
+  if (pubkey_len > DH1024_KEY_LEN)
+    return -1;
+  if (!dh->pubkey || !dh->seckey)
+    return -1;
+  if (secret_bytes_out < DH1024_KEY_LEN)
+    return -1;
+
+  SECKEYPublicKey peer_key;
+  memset(&peer_key, 0, sizeof(peer_key));
+  peer_key.keyType = dhKey;
+  peer_key.pkcs11ID = CK_INVALID_HANDLE;
+
+  if (dh->dh_type == DH_TYPE_TLS)
+    peer_key.u.dh.prime.data = tls_dh_prime_data; // should never use this code
+  else
+    peer_key.u.dh.prime.data = circuit_dh_prime_data;
+  peer_key.u.dh.prime.len = DH1024_KEY_LEN;
+  peer_key.u.dh.base.data = dh_generator_data;
+  peer_key.u.dh.base.len = 1;
+  peer_key.u.dh.publicValue.data = (unsigned char *)pubkey;
+  peer_key.u.dh.publicValue.len = (int) pubkey_len;
+
+  PK11SymKey *sym = PK11_PubDerive(dh->seckey, &peer_key,
+                       PR_FALSE, NULL, NULL, CKM_DH_PKCS_DERIVE,
+                       CKM_GENERIC_SECRET_KEY_GEN /* ??? */,
+                       CKA_DERIVE, 0, NULL);
+  if (! sym) {
+    crypto_nss_log_errors(severity, "deriving a DH shared secret");
+    return -1;
+  }
+
+  SECStatus s = PK11_ExtractKeyValue(sym);
+  if (s != SECSuccess) {
+    crypto_nss_log_errors(severity, "extracting a DH shared secret");
+    PK11_FreeSymKey(sym);
+    return -1;
+  }
+
+  SECItem *result = PK11_GetKeyData(sym);
+  tor_assert(result); // This cannot fail.
+  if (BUG(result->len > secret_bytes_out)) {
+    PK11_FreeSymKey(sym);
+    return -1;
+  }
+
+  ssize_t len = result->len;
+  memcpy(secret_out, result->data, len);
+  PK11_FreeSymKey(sym);
+
+  return len;
+}

+ 471 - 0
src/lib/crypt_ops/crypto_dh_openssl.c

@@ -0,0 +1,471 @@
+/* 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_dh_openssl.c
+ * \brief Implement Tor's Z_p diffie-hellman stuff for OpenSSL.
+ **/
+
+#include "lib/crypt_ops/compat_openssl.h"
+#include "lib/crypt_ops/crypto_dh.h"
+#include "lib/crypt_ops/crypto_digest.h"
+#include "lib/crypt_ops/crypto_hkdf.h"
+#include "lib/crypt_ops/crypto_util.h"
+#include "lib/log/log.h"
+#include "lib/log/util_bug.h"
+
+DISABLE_GCC_WARNING(redundant-decls)
+
+#include <openssl/dh.h>
+
+ENABLE_GCC_WARNING(redundant-decls)
+
+#include <openssl/bn.h>
+#include <string.h>
+
+#ifndef ENABLE_NSS
+static int tor_check_dh_key(int severity, const BIGNUM *bn);
+
+/** A structure to hold the first half (x, g^x) of a Diffie-Hellman handshake
+ * while we're waiting for the second.*/
+struct crypto_dh_t {
+  DH *dh; /**< The openssl DH object */
+};
+#endif
+
+static DH *new_openssl_dh_from_params(BIGNUM *p, BIGNUM *g);
+
+/** Shared P parameter for our circuit-crypto DH key exchanges. */
+static BIGNUM *dh_param_p = NULL;
+/** Shared P parameter for our TLS DH key exchanges. */
+static BIGNUM *dh_param_p_tls = NULL;
+/** Shared G parameter for our DH key exchanges. */
+static BIGNUM *dh_param_g = NULL;
+
+/** Validate a given set of Diffie-Hellman parameters.  This is moderately
+ * computationally expensive (milliseconds), so should only be called when
+ * the DH parameters change. Returns 0 on success, * -1 on failure.
+ */
+static int
+crypto_validate_dh_params(const BIGNUM *p, const BIGNUM *g)
+{
+  DH *dh = NULL;
+  int ret = -1;
+
+  /* Copy into a temporary DH object, just so that DH_check() can be called. */
+  if (!(dh = DH_new()))
+      goto out;
+#ifdef OPENSSL_1_1_API
+  BIGNUM *dh_p, *dh_g;
+  if (!(dh_p = BN_dup(p)))
+    goto out;
+  if (!(dh_g = BN_dup(g)))
+    goto out;
+  if (!DH_set0_pqg(dh, dh_p, NULL, dh_g))
+    goto out;
+#else /* !(defined(OPENSSL_1_1_API)) */
+  if (!(dh->p = BN_dup(p)))
+    goto out;
+  if (!(dh->g = BN_dup(g)))
+    goto out;
+#endif /* defined(OPENSSL_1_1_API) */
+
+  /* Perform the validation. */
+  int codes = 0;
+  if (!DH_check(dh, &codes))
+    goto out;
+  if (BN_is_word(g, DH_GENERATOR_2)) {
+    /* Per https://wiki.openssl.org/index.php/Diffie-Hellman_parameters
+     *
+     * OpenSSL checks the prime is congruent to 11 when g = 2; while the
+     * IETF's primes are congruent to 23 when g = 2.
+     */
+    BN_ULONG residue = BN_mod_word(p, 24);
+    if (residue == 11 || residue == 23)
+      codes &= ~DH_NOT_SUITABLE_GENERATOR;
+  }
+  if (codes != 0) /* Specifics on why the params suck is irrelevant. */
+    goto out;
+
+  /* Things are probably not evil. */
+  ret = 0;
+
+ out:
+  if (dh)
+    DH_free(dh);
+  return ret;
+}
+
+/**
+ * Helper: convert <b>hex<b> to a bignum, and return it.  Assert that the
+ * operation was successful.
+ */
+static BIGNUM *
+bignum_from_hex(const char *hex)
+{
+  BIGNUM *result = BN_new();
+  tor_assert(result);
+
+  int r = BN_hex2bn(&result, hex);
+  tor_assert(r);
+  tor_assert(result);
+  return result;
+}
+
+/** Set the global Diffie-Hellman generator, used for both TLS and internal
+ * DH stuff.
+ */
+static void
+crypto_set_dh_generator(void)
+{
+  BIGNUM *generator;
+  int r;
+
+  if (dh_param_g)
+    return;
+
+  generator = BN_new();
+  tor_assert(generator);
+
+  r = BN_set_word(generator, DH_GENERATOR);
+  tor_assert(r);
+
+  dh_param_g = generator;
+}
+
+/** Initialize our DH parameters. Idempotent. */
+void
+crypto_dh_init_openssl(void)
+{
+  if (dh_param_p && dh_param_g && dh_param_p_tls)
+    return;
+
+  tor_assert(dh_param_g == NULL);
+  tor_assert(dh_param_p == NULL);
+  tor_assert(dh_param_p_tls == NULL);
+
+  crypto_set_dh_generator();
+  dh_param_p = bignum_from_hex(OAKLEY_PRIME_2);
+  dh_param_p_tls = bignum_from_hex(TLS_DH_PRIME);
+
+  tor_assert(0 == crypto_validate_dh_params(dh_param_p, dh_param_g));
+  tor_assert(0 == crypto_validate_dh_params(dh_param_p_tls, dh_param_g));
+}
+
+/** Number of bits to use when choosing the x or y value in a Diffie-Hellman
+ * handshake.  Since we exponentiate by this value, choosing a smaller one
+ * lets our handhake go faster.
+ */
+#define DH_PRIVATE_KEY_BITS 320
+
+/** Used by tortls.c: Get the DH* for use with TLS.
+ */
+DH *
+crypto_dh_new_openssl_tls(void)
+{
+  return new_openssl_dh_from_params(dh_param_p_tls, dh_param_g);
+}
+
+#ifndef ENABLE_NSS
+/** Allocate and return a new DH object for a key exchange. Returns NULL on
+ * failure.
+ */
+crypto_dh_t *
+crypto_dh_new(int dh_type)
+{
+  crypto_dh_t *res = tor_malloc_zero(sizeof(crypto_dh_t));
+
+  tor_assert(dh_type == DH_TYPE_CIRCUIT || dh_type == DH_TYPE_TLS ||
+             dh_type == DH_TYPE_REND);
+
+  if (!dh_param_p)
+    crypto_dh_init();
+
+  BIGNUM *dh_p = NULL;
+  if (dh_type == DH_TYPE_TLS) {
+    dh_p = dh_param_p_tls;
+  } else {
+    dh_p = dh_param_p;
+  }
+
+  res->dh = new_openssl_dh_from_params(dh_p, dh_param_g);
+  if (res->dh == NULL)
+    tor_free(res); // sets res to NULL.
+  return res;
+}
+#endif
+
+/** Create and return a new openssl DH from a given prime and generator. */
+static DH *
+new_openssl_dh_from_params(BIGNUM *p, BIGNUM *g)
+{
+  DH *res_dh;
+  if (!(res_dh = DH_new()))
+    goto err;
+
+  BIGNUM *dh_p = NULL, *dh_g = NULL;
+  dh_p = BN_dup(p);
+  if (!dh_p)
+    goto err;
+
+  dh_g = BN_dup(g);
+  if (!dh_g) {
+    BN_free(dh_p);
+    goto err;
+  }
+
+#ifdef OPENSSL_1_1_API
+
+  if (!DH_set0_pqg(res_dh, dh_p, NULL, dh_g)) {
+    goto err;
+  }
+
+  if (!DH_set_length(res_dh, DH_PRIVATE_KEY_BITS))
+    goto err;
+#else /* !(defined(OPENSSL_1_1_API)) */
+  res_dh->p = dh_p;
+  res_dh->g = dh_g;
+  res_dh->length = DH_PRIVATE_KEY_BITS;
+#endif /* defined(OPENSSL_1_1_API) */
+
+  return res_dh;
+
+  /* LCOV_EXCL_START
+   * This error condition is only reached when an allocation fails */
+ err:
+  crypto_openssl_log_errors(LOG_WARN, "creating DH object");
+  if (res_dh) DH_free(res_dh); /* frees p and g too */
+  return NULL;
+  /* LCOV_EXCL_STOP */
+}
+
+#ifndef ENABLE_NSS
+/** Return a copy of <b>dh</b>, sharing its internal state. */
+crypto_dh_t *
+crypto_dh_dup(const crypto_dh_t *dh)
+{
+  crypto_dh_t *dh_new = tor_malloc_zero(sizeof(crypto_dh_t));
+  tor_assert(dh);
+  tor_assert(dh->dh);
+  dh_new->dh = dh->dh;
+  DH_up_ref(dh->dh);
+  return dh_new;
+}
+
+/** Return the length of the DH key in <b>dh</b>, in bytes.
+ */
+int
+crypto_dh_get_bytes(crypto_dh_t *dh)
+{
+  tor_assert(dh);
+  return DH_size(dh->dh);
+}
+
+/** Generate \<x,g^x\> for our part of the key exchange.  Return 0 on
+ * success, -1 on failure.
+ */
+int
+crypto_dh_generate_public(crypto_dh_t *dh)
+{
+#ifndef OPENSSL_1_1_API
+ again:
+#endif
+  if (!DH_generate_key(dh->dh)) {
+    /* LCOV_EXCL_START
+     * To test this we would need some way to tell openssl to break DH. */
+    crypto_openssl_log_errors(LOG_WARN, "generating DH key");
+    return -1;
+    /* LCOV_EXCL_STOP */
+  }
+#ifdef OPENSSL_1_1_API
+  /* OpenSSL 1.1.x doesn't appear to let you regenerate a DH key, without
+   * recreating the DH object.  I have no idea what sort of aliasing madness
+   * can occur here, so do the check, and just bail on failure.
+   */
+  const BIGNUM *pub_key, *priv_key;
+  DH_get0_key(dh->dh, &pub_key, &priv_key);
+  if (tor_check_dh_key(LOG_WARN, pub_key)<0) {
+    log_warn(LD_CRYPTO, "Weird! Our own DH key was invalid.  I guess once-in-"
+             "the-universe chances really do happen.  Treating as a failure.");
+    return -1;
+  }
+#else /* !(defined(OPENSSL_1_1_API)) */
+  if (tor_check_dh_key(LOG_WARN, dh->dh->pub_key)<0) {
+    /* LCOV_EXCL_START
+     * If this happens, then openssl's DH implementation is busted. */
+    log_warn(LD_CRYPTO, "Weird! Our own DH key was invalid.  I guess once-in-"
+             "the-universe chances really do happen.  Trying again.");
+    /* Free and clear the keys, so OpenSSL will actually try again. */
+    BN_clear_free(dh->dh->pub_key);
+    BN_clear_free(dh->dh->priv_key);
+    dh->dh->pub_key = dh->dh->priv_key = NULL;
+    goto again;
+    /* LCOV_EXCL_STOP */
+  }
+#endif /* defined(OPENSSL_1_1_API) */
+  return 0;
+}
+
+/** Generate g^x as necessary, and write the g^x for the key exchange
+ * as a <b>pubkey_len</b>-byte value into <b>pubkey</b>. Return 0 on
+ * success, -1 on failure.  <b>pubkey_len</b> must be \>= DH1024_KEY_LEN.
+ */
+int
+crypto_dh_get_public(crypto_dh_t *dh, char *pubkey, size_t pubkey_len)
+{
+  int bytes;
+  tor_assert(dh);
+
+  const BIGNUM *dh_pub;
+
+#ifdef OPENSSL_1_1_API
+  const BIGNUM *dh_priv;
+  DH_get0_key(dh->dh, &dh_pub, &dh_priv);
+#else
+  dh_pub = dh->dh->pub_key;
+#endif /* defined(OPENSSL_1_1_API) */
+
+  if (!dh_pub) {
+    if (crypto_dh_generate_public(dh)<0)
+      return -1;
+    else {
+#ifdef OPENSSL_1_1_API
+      DH_get0_key(dh->dh, &dh_pub, &dh_priv);
+#else
+      dh_pub = dh->dh->pub_key;
+#endif
+    }
+  }
+
+  tor_assert(dh_pub);
+  bytes = BN_num_bytes(dh_pub);
+  tor_assert(bytes >= 0);
+  if (pubkey_len < (size_t)bytes) {
+    log_warn(LD_CRYPTO,
+             "Weird! pubkey_len (%d) was smaller than DH1024_KEY_LEN (%d)",
+             (int) pubkey_len, bytes);
+    return -1;
+  }
+
+  memset(pubkey, 0, pubkey_len);
+  BN_bn2bin(dh_pub, (unsigned char*)(pubkey+(pubkey_len-bytes)));
+
+  return 0;
+}
+
+/** Check for bad Diffie-Hellman public keys (g^x).  Return 0 if the key is
+ * okay (in the subgroup [2,p-2]), or -1 if it's bad.
+ * See http://www.cl.cam.ac.uk/ftp/users/rja14/psandqs.ps.gz for some tips.
+ */
+static int
+tor_check_dh_key(int severity, const BIGNUM *bn)
+{
+  BIGNUM *x;
+  char *s;
+  tor_assert(bn);
+  x = BN_new();
+  tor_assert(x);
+  if (BUG(!dh_param_p))
+    crypto_dh_init(); //LCOV_EXCL_LINE we already checked whether we did this.
+  BN_set_word(x, 1);
+  if (BN_cmp(bn,x)<=0) {
+    log_fn(severity, LD_CRYPTO, "DH key must be at least 2.");
+    goto err;
+  }
+  BN_copy(x,dh_param_p);
+  BN_sub_word(x, 1);
+  if (BN_cmp(bn,x)>=0) {
+    log_fn(severity, LD_CRYPTO, "DH key must be at most p-2.");
+    goto err;
+  }
+  BN_clear_free(x);
+  return 0;
+ err:
+  BN_clear_free(x);
+  s = BN_bn2hex(bn);
+  log_fn(severity, LD_CRYPTO, "Rejecting insecure DH key [%s]", s);
+  OPENSSL_free(s);
+  return -1;
+}
+
+/** Given a DH key exchange object, and our peer's value of g^y (as a
+ * <b>pubkey_len</b>-byte value in <b>pubkey</b>) generate
+ * g^xy as a big-endian integer in <b>secret_out</b>.
+ * Return the number of bytes generated on success,
+ * or -1 on failure.
+ *
+ * This function MUST validate that g^y is actually in the group.
+ */
+ssize_t
+crypto_dh_handshake(int severity, crypto_dh_t *dh,
+                    const char *pubkey, size_t pubkey_len,
+                    unsigned char *secret_out, size_t secret_bytes_out)
+{
+  BIGNUM *pubkey_bn = NULL;
+  size_t secret_len=0;
+  int result=0;
+
+  tor_assert(dh);
+  tor_assert(secret_bytes_out/DIGEST_LEN <= 255);
+  tor_assert(pubkey_len < INT_MAX);
+
+  if (BUG(crypto_dh_get_bytes(dh) > (int)secret_bytes_out)) {
+    goto error;
+  }
+
+  if (!(pubkey_bn = BN_bin2bn((const unsigned char*)pubkey,
+                              (int)pubkey_len, NULL)))
+    goto error;
+  if (tor_check_dh_key(severity, pubkey_bn)<0) {
+    /* Check for invalid public keys. */
+    log_fn(severity, LD_CRYPTO,"Rejected invalid g^x");
+    goto error;
+  }
+  result = DH_compute_key(secret_out, pubkey_bn, dh->dh);
+  if (result < 0) {
+    log_warn(LD_CRYPTO,"DH_compute_key() failed.");
+    goto error;
+  }
+  secret_len = result;
+
+  goto done;
+ error:
+  result = -1;
+ done:
+  crypto_openssl_log_errors(LOG_WARN, "completing DH handshake");
+  if (pubkey_bn)
+    BN_clear_free(pubkey_bn);
+  if (result < 0)
+    return result;
+  else
+    return secret_len;
+}
+
+/** Free a DH key exchange object.
+ */
+void
+crypto_dh_free_(crypto_dh_t *dh)
+{
+  if (!dh)
+    return;
+  tor_assert(dh->dh);
+  DH_free(dh->dh);
+  tor_free(dh);
+}
+#endif
+
+void
+crypto_dh_free_all_openssl(void)
+{
+  if (dh_param_p)
+    BN_clear_free(dh_param_p);
+  if (dh_param_p_tls)
+    BN_clear_free(dh_param_p_tls);
+  if (dh_param_g)
+    BN_clear_free(dh_param_g);
+
+  dh_param_p = dh_param_p_tls = dh_param_g = NULL;
+}

+ 251 - 11
src/lib/crypt_ops/crypto_digest.c

@@ -12,7 +12,6 @@
 
 #include "lib/container/smartlist.h"
 #include "lib/crypt_ops/crypto_digest.h"
-#include "lib/crypt_ops/crypto_openssl_mgt.h"
 #include "lib/crypt_ops/crypto_util.h"
 #include "lib/log/log.h"
 #include "lib/log/util_bug.h"
@@ -24,12 +23,92 @@
 
 #include "lib/arch/bytes.h"
 
+#ifdef ENABLE_NSS
+DISABLE_GCC_WARNING(strict-prototypes)
+#include <pk11pub.h>
+ENABLE_GCC_WARNING(strict-prototypes)
+#else
+
+#include "lib/crypt_ops/crypto_openssl_mgt.h"
+
 DISABLE_GCC_WARNING(redundant-decls)
 
 #include <openssl/hmac.h>
 #include <openssl/sha.h>
 
 ENABLE_GCC_WARNING(redundant-decls)
+#endif
+
+#ifdef ENABLE_NSS
+/**
+ * Convert a digest_algorithm_t (used by tor) to a HashType (used by NSS).
+ * On failure, return SEC_OID_UNKNOWN. */
+static SECOidTag
+digest_alg_to_nss_oid(digest_algorithm_t alg)
+{
+  switch (alg) {
+    case DIGEST_SHA1: return SEC_OID_SHA1;
+    case DIGEST_SHA256: return SEC_OID_SHA256;
+    case DIGEST_SHA512: return SEC_OID_SHA512;
+    case DIGEST_SHA3_256: /* Fall through */
+    case DIGEST_SHA3_512: /* Fall through */
+    default:
+      return SEC_OID_UNKNOWN;
+  }
+}
+
+/* Helper: get an unkeyed digest via pk11wrap */
+static int
+digest_nss_internal(SECOidTag alg,
+                    char *digest, unsigned len_out,
+                    const char *msg, size_t msg_len)
+{
+  if (alg == SEC_OID_UNKNOWN)
+    return -1;
+  tor_assert(msg_len <= UINT_MAX);
+
+  int rv = -1;
+  SECStatus s;
+  PK11Context *ctx = PK11_CreateDigestContext(alg);
+  if (!ctx)
+    return -1;
+
+  s = PK11_DigestBegin(ctx);
+  if (s != SECSuccess)
+    goto done;
+
+  s = PK11_DigestOp(ctx, (const unsigned char *)msg, (unsigned int)msg_len);
+  if (s != SECSuccess)
+    goto done;
+
+  unsigned int len = 0;
+  s = PK11_DigestFinal(ctx, (unsigned char *)digest, &len, len_out);
+  if (s != SECSuccess)
+    goto done;
+
+  rv = 0;
+ done:
+  PK11_DestroyContext(ctx, PR_TRUE);
+  return rv;
+}
+
+/** True iff alg is implemented in our crypto library, and we want to use that
+ * implementation */
+static bool
+library_supports_digest(digest_algorithm_t alg)
+{
+  switch (alg) {
+    case DIGEST_SHA1: /* Fall through */
+    case DIGEST_SHA256: /* Fall through */
+    case DIGEST_SHA512: /* Fall through */
+      return true;
+    case DIGEST_SHA3_256: /* Fall through */
+    case DIGEST_SHA3_512: /* Fall through */
+    default:
+      return false;
+  }
+}
+#endif
 
 /* Crypto digest functions */
 
@@ -37,13 +116,18 @@ ENABLE_GCC_WARNING(redundant-decls)
  * <b>m</b>.  Write the DIGEST_LEN byte result into <b>digest</b>.
  * Return 0 on success, -1 on failure.
  */
-int
-crypto_digest(char *digest, const char *m, size_t len)
+MOCK_IMPL(int,
+crypto_digest,(char *digest, const char *m, size_t len))
 {
   tor_assert(m);
   tor_assert(digest);
-  if (SHA1((const unsigned char*)m,len,(unsigned char*)digest) == NULL)
+#ifdef ENABLE_NSS
+  return digest_nss_internal(SEC_OID_SHA1, digest, DIGEST_LEN, m, len);
+#else
+  if (SHA1((const unsigned char*)m,len,(unsigned char*)digest) == NULL) {
     return -1;
+  }
+#endif
   return 0;
 }
 
@@ -59,11 +143,16 @@ crypto_digest256(char *digest, const char *m, size_t len,
   tor_assert(algorithm == DIGEST_SHA256 || algorithm == DIGEST_SHA3_256);
 
   int ret = 0;
-  if (algorithm == DIGEST_SHA256)
+  if (algorithm == DIGEST_SHA256) {
+#ifdef ENABLE_NSS
+    return digest_nss_internal(SEC_OID_SHA256, digest, DIGEST256_LEN, m, len);
+#else
     ret = (SHA256((const uint8_t*)m,len,(uint8_t*)digest) != NULL);
-  else
+#endif
+  } else {
     ret = (sha3_256((uint8_t *)digest, DIGEST256_LEN,(const uint8_t *)m, len)
            > -1);
+  }
 
   if (!ret)
     return -1;
@@ -82,12 +171,17 @@ crypto_digest512(char *digest, const char *m, size_t len,
   tor_assert(algorithm == DIGEST_SHA512 || algorithm == DIGEST_SHA3_512);
 
   int ret = 0;
-  if (algorithm == DIGEST_SHA512)
+  if (algorithm == DIGEST_SHA512) {
+#ifdef ENABLE_NSS
+    return digest_nss_internal(SEC_OID_SHA512, digest, DIGEST512_LEN, m, len);
+#else
     ret = (SHA512((const unsigned char*)m,len,(unsigned char*)digest)
            != NULL);
-  else
+#endif
+  } else {
     ret = (sha3_512((uint8_t*)digest, DIGEST512_LEN, (const uint8_t*)m, len)
            > -1);
+  }
 
   if (!ret)
     return -1;
@@ -181,9 +275,13 @@ struct crypto_digest_t {
     * that space for other members might not even be allocated!
     */
   union {
+#ifdef ENABLE_NSS
+    PK11Context *ctx;
+#else
     SHA_CTX sha1; /**< state for SHA1 */
     SHA256_CTX sha2; /**< state for SHA256 */
     SHA512_CTX sha512; /**< state for SHA512 */
+#endif
     keccak_state sha3; /**< state for SHA3-[256,512] */
   } d;
 };
@@ -214,12 +312,19 @@ crypto_digest_alloc_bytes(digest_algorithm_t alg)
 #define END_OF_FIELD(f) (offsetof(crypto_digest_t, f) + \
                          STRUCT_FIELD_SIZE(crypto_digest_t, f))
   switch (alg) {
+#ifdef ENABLE_NSS
+    case DIGEST_SHA1: /* Fall through */
+    case DIGEST_SHA256: /* Fall through */
+    case DIGEST_SHA512:
+      return END_OF_FIELD(d.ctx);
+#else
     case DIGEST_SHA1:
       return END_OF_FIELD(d.sha1);
     case DIGEST_SHA256:
       return END_OF_FIELD(d.sha2);
     case DIGEST_SHA512:
       return END_OF_FIELD(d.sha512);
+#endif
     case DIGEST_SHA3_256:
     case DIGEST_SHA3_512:
       return END_OF_FIELD(d.sha3);
@@ -243,6 +348,21 @@ crypto_digest_new_internal(digest_algorithm_t algorithm)
 
   switch (algorithm)
     {
+#ifdef ENABLE_NSS
+    case DIGEST_SHA1: /* fall through */
+    case DIGEST_SHA256: /* fall through */
+    case DIGEST_SHA512:
+      r->d.ctx = PK11_CreateDigestContext(digest_alg_to_nss_oid(algorithm));
+      if (BUG(!r->d.ctx)) {
+        tor_free(r);
+        return NULL;
+      }
+      if (BUG(SECSuccess != PK11_DigestBegin(r->d.ctx))) {
+        crypto_digest_free(r);
+        return NULL;
+      }
+      break;
+#else
     case DIGEST_SHA1:
       SHA1_Init(&r->d.sha1);
       break;
@@ -252,6 +372,7 @@ crypto_digest_new_internal(digest_algorithm_t algorithm)
     case DIGEST_SHA512:
       SHA512_Init(&r->d.sha512);
       break;
+#endif
     case DIGEST_SHA3_256:
       keccak_digest_init(&r->d.sha3, 256);
       break;
@@ -302,6 +423,11 @@ crypto_digest_free_(crypto_digest_t *digest)
 {
   if (!digest)
     return;
+#ifdef ENABLE_NSS
+  if (library_supports_digest(digest->algorithm)) {
+    PK11_DestroyContext(digest->d.ctx, PR_TRUE);
+  }
+#endif
   size_t bytes = crypto_digest_alloc_bytes(digest->algorithm);
   memwipe(digest, 0, bytes);
   tor_free(digest);
@@ -324,6 +450,17 @@ crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
    * just doing it ourselves. Hashes are fast.
    */
   switch (digest->algorithm) {
+#ifdef ENABLE_NSS
+    case DIGEST_SHA1: /* fall through */
+    case DIGEST_SHA256: /* fall through */
+    case DIGEST_SHA512:
+      tor_assert(len <= UINT_MAX);
+      SECStatus s = PK11_DigestOp(digest->d.ctx,
+                                  (const unsigned char *)data,
+                                  (unsigned int)len);
+      tor_assert(s == SECSuccess);
+      break;
+#else
     case DIGEST_SHA1:
       SHA1_Update(&digest->d.sha1, (void*)data, len);
       break;
@@ -333,6 +470,7 @@ crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
     case DIGEST_SHA512:
       SHA512_Update(&digest->d.sha512, (void*)data, len);
       break;
+#endif
     case DIGEST_SHA3_256: /* FALLSTHROUGH */
     case DIGEST_SHA3_512:
       keccak_digest_update(&digest->d.sha3, (const uint8_t *)data, len);
@@ -357,7 +495,6 @@ crypto_digest_get_digest(crypto_digest_t *digest,
                          char *out, size_t out_len)
 {
   unsigned char r[DIGEST512_LEN];
-  crypto_digest_t tmpenv;
   tor_assert(digest);
   tor_assert(out);
   tor_assert(out_len <= crypto_digest_algorithm_get_length(digest->algorithm));
@@ -370,7 +507,26 @@ crypto_digest_get_digest(crypto_digest_t *digest,
     return;
   }
 
+#ifdef ENABLE_NSS
+  /* Copy into a temporary buffer since DigestFinal (alters) the context */
+  unsigned char buf[1024];
+  unsigned int saved_len = 0;
+  unsigned rlen;
+  unsigned char *saved = PK11_SaveContextAlloc(digest->d.ctx,
+                                               buf, sizeof(buf),
+                                               &saved_len);
+  tor_assert(saved);
+  SECStatus s = PK11_DigestFinal(digest->d.ctx, r, &rlen, sizeof(r));
+  tor_assert(s == SECSuccess);
+  tor_assert(rlen >= out_len);
+  s = PK11_RestoreContext(digest->d.ctx, saved, saved_len);
+  tor_assert(s == SECSuccess);
+  if (saved != buf) {
+    PORT_ZFree(saved, saved_len);
+  }
+#else
   const size_t alloc_bytes = crypto_digest_alloc_bytes(digest->algorithm);
+  crypto_digest_t tmpenv;
   /* memcpy into a temporary ctx, since SHA*_Final clears the context */
   memcpy(&tmpenv, digest, alloc_bytes);
   switch (digest->algorithm) {
@@ -393,6 +549,7 @@ crypto_digest_get_digest(crypto_digest_t *digest,
       break;
 //LCOV_EXCL_STOP
   }
+#endif
   memcpy(out, r, out_len);
   memwipe(r, 0, sizeof(r));
 }
@@ -408,7 +565,13 @@ crypto_digest_dup(const crypto_digest_t *digest)
 {
   tor_assert(digest);
   const size_t alloc_bytes = crypto_digest_alloc_bytes(digest->algorithm);
-  return tor_memdup(digest, alloc_bytes);
+  crypto_digest_t *result = tor_memdup(digest, alloc_bytes);
+#ifdef ENABLE_NSS
+  if (library_supports_digest(digest->algorithm)) {
+    result->d.ctx = PK11_CloneContext(digest->d.ctx);
+  }
+#endif
+  return result;
 }
 
 /** Temporarily save the state of <b>digest</b> in <b>checkpoint</b>.
@@ -420,6 +583,18 @@ crypto_digest_checkpoint(crypto_digest_checkpoint_t *checkpoint,
 {
   const size_t bytes = crypto_digest_alloc_bytes(digest->algorithm);
   tor_assert(bytes <= sizeof(checkpoint->mem));
+#ifdef ENABLE_NSS
+  if (library_supports_digest(digest->algorithm)) {
+    unsigned char *allocated;
+    allocated = PK11_SaveContextAlloc(digest->d.ctx,
+                                      (unsigned char *)checkpoint->mem,
+                                      sizeof(checkpoint->mem),
+                                      &checkpoint->bytes_used);
+    /* No allocation is allowed here. */
+    tor_assert(allocated == checkpoint->mem);
+    return;
+  }
+#endif
   memcpy(checkpoint->mem, digest, bytes);
 }
 
@@ -431,6 +606,15 @@ crypto_digest_restore(crypto_digest_t *digest,
                       const crypto_digest_checkpoint_t *checkpoint)
 {
   const size_t bytes = crypto_digest_alloc_bytes(digest->algorithm);
+#ifdef ENABLE_NSS
+  if (library_supports_digest(digest->algorithm)) {
+    SECStatus s = PK11_RestoreContext(digest->d.ctx,
+                                      (unsigned char *)checkpoint->mem,
+                                      checkpoint->bytes_used);
+    tor_assert(s == SECSuccess);
+    return;
+  }
+#endif
   memcpy(digest, checkpoint->mem, bytes);
 }
 
@@ -446,6 +630,13 @@ crypto_digest_assign(crypto_digest_t *into,
   tor_assert(from);
   tor_assert(into->algorithm == from->algorithm);
   const size_t alloc_bytes = crypto_digest_alloc_bytes(from->algorithm);
+#ifdef ENABLE_NSS
+  if (library_supports_digest(from->algorithm)) {
+    PK11_DestroyContext(into->d.ctx, PR_TRUE);
+    into->d.ctx = PK11_CloneContext(from->d.ctx);
+    return;
+  }
+#endif
   memcpy(into,from,alloc_bytes);
 }
 
@@ -496,14 +687,63 @@ crypto_hmac_sha256(char *hmac_out,
                    const char *key, size_t key_len,
                    const char *msg, size_t msg_len)
 {
-  unsigned char *rv = NULL;
   /* If we've got OpenSSL >=0.9.8 we can use its hmac implementation. */
   tor_assert(key_len < INT_MAX);
   tor_assert(msg_len < INT_MAX);
   tor_assert(hmac_out);
+#ifdef ENABLE_NSS
+  PK11SlotInfo *slot = NULL;
+  PK11SymKey *symKey = NULL;
+  PK11Context *hmac = NULL;
+
+  int ok = 0;
+  SECStatus s;
+  SECItem keyItem, paramItem;
+  keyItem.data = (unsigned char *)key;
+  keyItem.len = (unsigned)key_len;
+  paramItem.type = siBuffer;
+  paramItem.data = NULL;
+  paramItem.len = 0;
+
+  slot = PK11_GetBestSlot(CKM_SHA256_HMAC, NULL);
+  if (!slot)
+    goto done;
+  symKey = PK11_ImportSymKey(slot, CKM_SHA256_HMAC,
+                             PK11_OriginUnwrap, CKA_SIGN, &keyItem, NULL);
+  if (!symKey)
+    goto done;
+
+  hmac = PK11_CreateContextBySymKey(CKM_SHA256_HMAC, CKA_SIGN, symKey,
+                                    &paramItem);
+  if (!hmac)
+    goto done;
+  s = PK11_DigestBegin(hmac);
+  if (s != SECSuccess)
+    goto done;
+  s = PK11_DigestOp(hmac, (const unsigned char *)msg, (unsigned int)msg_len);
+  if (s != SECSuccess)
+    goto done;
+  unsigned int len=0;
+  s = PK11_DigestFinal(hmac, (unsigned char *)hmac_out, &len, DIGEST256_LEN);
+  if (s != SECSuccess || len != DIGEST256_LEN)
+    goto done;
+  ok = 1;
+
+ done:
+  if (hmac)
+    PK11_DestroyContext(hmac, PR_TRUE);
+  if (symKey)
+    PK11_FreeSymKey(symKey);
+  if (slot)
+    PK11_FreeSlot(slot);
+
+  tor_assert(ok);
+#else
+  unsigned char *rv = NULL;
   rv = HMAC(EVP_sha256(), key, (int)key_len, (unsigned char*)msg, (int)msg_len,
             (unsigned char*)hmac_out, NULL);
   tor_assert(rv);
+#endif
 }
 
 /** Compute a MAC using SHA3-256 of <b>msg_len</b> bytes in <b>msg</b> using a

+ 5 - 1
src/lib/crypt_ops/crypto_digest.h

@@ -16,6 +16,7 @@
 #include "lib/cc/torint.h"
 #include "lib/defs/digest_sizes.h"
 #include "lib/malloc/malloc.h"
+#include "lib/testsupport/testsupport.h"
 
 /** Length of a sha1 message digest when encoded in base32 with trailing =
  * signs removed. */
@@ -51,6 +52,9 @@ typedef enum {
 /** Structure used to temporarily save the a digest object. Only implemented
  * for SHA1 digest for now. */
 typedef struct crypto_digest_checkpoint_t {
+#ifdef ENABLE_NSS
+  unsigned int bytes_used;
+#endif
   uint8_t mem[DIGEST_CHECKPOINT_BYTES];
 } crypto_digest_checkpoint_t;
 
@@ -72,7 +76,7 @@ typedef struct crypto_xof_t crypto_xof_t;
 struct smartlist_t;
 
 /* SHA-1 and other digests */
-int crypto_digest(char *digest, const char *m, size_t len);
+MOCK_DECL(int, crypto_digest,(char *digest, const char *m, size_t len));
 int crypto_digest256(char *digest, const char *m, size_t len,
                      digest_algorithm_t algorithm);
 int crypto_digest512(char *digest, const char *m, size_t len,

+ 1 - 0
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);
 

+ 1 - 0
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

+ 2 - 0
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>
 

+ 193 - 0
src/lib/crypt_ops/crypto_init.c

@@ -0,0 +1,193 @@
+/* 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_init.c
+ *
+ * \brief Initialize and shut down Tor's crypto library and subsystem.
+ **/
+
+#include "orconfig.h"
+
+#include "lib/crypt_ops/crypto_init.h"
+
+#include "lib/crypt_ops/crypto_curve25519.h"
+#include "lib/crypt_ops/crypto_dh.h"
+#include "lib/crypt_ops/crypto_ed25519.h"
+#include "lib/crypt_ops/crypto_openssl_mgt.h"
+#include "lib/crypt_ops/crypto_nss_mgt.h"
+#include "lib/crypt_ops/crypto_rand.h"
+
+#include "siphash.h"
+
+/** Boolean: has our crypto library been initialized? (early phase) */
+static int crypto_early_initialized_ = 0;
+
+/** Boolean: has our crypto library been initialized? (late phase) */
+static int crypto_global_initialized_ = 0;
+
+static int have_seeded_siphash = 0;
+
+/** Set up the siphash key if we haven't already done so. */
+int
+crypto_init_siphash_key(void)
+{
+  struct sipkey key;
+  if (have_seeded_siphash)
+    return 0;
+
+  crypto_rand((char*) &key, sizeof(key));
+  siphash_set_global_key(&key);
+  have_seeded_siphash = 1;
+  return 0;
+}
+
+/** Initialize the crypto library.  Return 0 on success, -1 on failure.
+ */
+int
+crypto_early_init(void)
+{
+  if (!crypto_early_initialized_) {
+
+    crypto_early_initialized_ = 1;
+
+#ifdef ENABLE_OPENSSL
+    crypto_openssl_early_init();
+#endif
+#ifdef ENABLE_NSS
+    crypto_nss_early_init(0);
+#endif
+
+    if (crypto_seed_rng() < 0)
+      return -1;
+    if (crypto_init_siphash_key() < 0)
+      return -1;
+
+    curve25519_init();
+    ed25519_init();
+  }
+  return 0;
+}
+
+/** Initialize the crypto library.  Return 0 on success, -1 on failure.
+ */
+int
+crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
+{
+  if (!crypto_global_initialized_) {
+    if (crypto_early_init() < 0)
+      return -1;
+
+    crypto_global_initialized_ = 1;
+
+    crypto_dh_init();
+
+#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)
+      return -1;
+#endif
+  }
+  return 0;
+}
+
+/** Free crypto resources held by this thread. */
+void
+crypto_thread_cleanup(void)
+{
+#ifdef ENABLE_OPENSSL
+  crypto_openssl_thread_cleanup();
+#endif
+}
+
+/**
+ * Uninitialize the crypto library. Return 0 on success. Does not detect
+ * failure.
+ */
+int
+crypto_global_cleanup(void)
+{
+  crypto_dh_free_all();
+
+#ifdef ENABLE_OPENSSL
+  crypto_openssl_global_cleanup();
+#endif
+#ifdef ENABLE_NSS
+  crypto_nss_global_cleanup();
+#endif
+
+  crypto_early_initialized_ = 0;
+  crypto_global_initialized_ = 0;
+  have_seeded_siphash = 0;
+  siphash_unset_global_key();
+
+  return 0;
+}
+
+/** Run operations that the crypto library requires to be happy again
+ * after forking. */
+void
+crypto_prefork(void)
+{
+#ifdef ENABLE_NSS
+  crypto_nss_prefork();
+#endif
+}
+
+/** Run operations that the crypto library requires to be happy again
+ * after forking. */
+void
+crypto_postfork(void)
+{
+#ifdef ENABLE_NSS
+  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
+}

+ 34 - 0
src/lib/crypt_ops/crypto_init.h

@@ -0,0 +1,34 @@
+/* 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_init.h
+ *
+ * \brief Headers for crypto_init.c
+ **/
+
+#ifndef TOR_CRYPTO_INIT_H
+#define TOR_CRYPTO_INIT_H
+
+#include "orconfig.h"
+#include "lib/cc/compat_compiler.h"
+
+int crypto_init_siphash_key(void);
+int crypto_early_init(void) ATTR_WUR;
+int crypto_global_init(int hardwareAccel,
+                       const char *accelName,
+                       const char *accelPath) ATTR_WUR;
+
+void crypto_thread_cleanup(void);
+int crypto_global_cleanup(void);
+void crypto_prefork(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) */

+ 132 - 0
src/lib/crypt_ops/crypto_nss_mgt.c

@@ -0,0 +1,132 @@
+/* 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_nss_mgt.c
+ *
+ * \brief Manage the NSS library (if used)
+ **/
+
+#include "lib/crypt_ops/crypto_nss_mgt.h"
+
+#include "lib/log/log.h"
+#include "lib/log/util_bug.h"
+#include "lib/string/printf.h"
+
+DISABLE_GCC_WARNING(strict-prototypes)
+#include <nss.h>
+#include <pk11func.h>
+#include <ssl.h>
+
+#include <prerror.h>
+#include <prtypes.h>
+#include <prinit.h>
+ENABLE_GCC_WARNING(strict-prototypes)
+
+const char *
+crypto_nss_get_version_str(void)
+{
+  return NSS_GetVersion();
+}
+const char *
+crypto_nss_get_header_version_str(void)
+{
+  return NSS_VERSION;
+}
+
+/** A password function that always returns NULL. */
+static char *
+nss_password_func_always_fail(PK11SlotInfo *slot,
+                              PRBool retry,
+                              void *arg)
+{
+  (void) slot;
+  (void) retry;
+  (void) arg;
+  return NULL;
+}
+
+void
+crypto_nss_early_init(int nss_only)
+{
+  if (! nss_only) {
+    PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+    PK11_SetPasswordFunc(nss_password_func_always_fail);
+  }
+
+  /* Eventually we should use NSS_Init() instead -- but that wants a
+     directory. The documentation says that we can't use this if we want
+     to use OpenSSL. */
+  if (NSS_NoDB_Init(NULL) == SECFailure) {
+    log_err(LD_CRYPTO, "Unable to initialize NSS.");
+    crypto_nss_log_errors(LOG_ERR, "initializing NSS");
+    tor_assert_unreached();
+  }
+
+  if (NSS_SetDomesticPolicy() == SECFailure) {
+    log_err(LD_CRYPTO, "Unable to set NSS cipher policy.");
+    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
+crypto_nss_log_errors(int severity, const char *doing)
+{
+  PRErrorCode code = PR_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) {
+    tor_log(severity, LD_CRYPTO, "NSS error %s while %s: %s",
+            name, doing, string);
+  } else {
+    tor_log(severity, LD_CRYPTO, "NSS error %s: %s", name, string);
+  }
+}
+
+int
+crypto_nss_late_init(void)
+{
+  /* Possibly, SSL_OptionSetDefault? */
+
+  return 0;
+}
+
+void
+crypto_nss_global_cleanup(void)
+{
+  NSS_Shutdown();
+  PL_ArenaFinish();
+  PR_Cleanup();
+}
+
+void
+crypto_nss_prefork(void)
+{
+  NSS_Shutdown();
+}
+
+void
+crypto_nss_postfork(void)
+{
+  crypto_nss_early_init(1);
+}

+ 34 - 0
src/lib/crypt_ops/crypto_nss_mgt.h

@@ -0,0 +1,34 @@
+/* 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_nss_mgt.h
+ *
+ * \brief Headers for crypto_nss_mgt.c
+ **/
+
+#ifndef TOR_CRYPTO_NSS_MGT_H
+#define TOR_CRYPTO_NSS_MGT_H
+
+#include "orconfig.h"
+
+#ifdef ENABLE_NSS
+/* global nss state */
+const char *crypto_nss_get_version_str(void);
+const char *crypto_nss_get_header_version_str(void);
+
+void crypto_nss_log_errors(int severity, const char *doing);
+
+void crypto_nss_early_init(int nss_only);
+int crypto_nss_late_init(void);
+
+void crypto_nss_global_cleanup(void);
+
+void crypto_nss_prefork(void);
+void crypto_nss_postfork(void);
+#endif
+
+#endif /* !defined(TOR_CRYPTO_NSS_H) */

+ 1 - 1
src/lib/crypt_ops/crypto_ope.c

@@ -18,8 +18,8 @@
 
 #define CRYPTO_OPE_PRIVATE
 #include "lib/crypt_ops/crypto_ope.h"
-#include "lib/crypt_ops/crypto.h"
 #include "lib/crypt_ops/crypto_util.h"
+#include "lib/crypt_ops/crypto_cipher.h"
 #include "lib/log/util_bug.h"
 #include "lib/malloc/malloc.h"
 #include "lib/arch/bytes.h"

+ 226 - 2
src/lib/crypt_ops/crypto_openssl_mgt.c

@@ -12,8 +12,12 @@
 
 #include "lib/crypt_ops/compat_openssl.h"
 #include "lib/crypt_ops/crypto_openssl_mgt.h"
+#include "lib/crypt_ops/crypto_rand.h"
+#include "lib/crypt_ops/aes.h"
 #include "lib/string/util_string.h"
 #include "lib/lock/compat_mutex.h"
+#include "lib/log/log.h"
+#include "lib/log/util_bug.h"
 #include "lib/testsupport/testsupport.h"
 #include "lib/thread/threads.h"
 
@@ -30,6 +34,7 @@ DISABLE_GCC_WARNING(redundant-decls)
 #include <openssl/conf.h>
 #include <openssl/hmac.h>
 #include <openssl/crypto.h>
+#include <openssl/ssl.h>
 
 ENABLE_GCC_WARNING(redundant-decls)
 
@@ -49,6 +54,27 @@ STATIC void openssl_locking_cb_(int mode, int n, const char *file, int line);
 STATIC void tor_set_openssl_thread_id(CRYPTO_THREADID *threadid);
 #endif
 
+/** Log all pending crypto errors at level <b>severity</b>.  Use
+ * <b>doing</b> to describe our current activities.
+ */
+void
+crypto_openssl_log_errors(int severity, const char *doing)
+{
+  unsigned long err;
+  const char *msg, *lib, *func;
+  while ((err = ERR_get_error()) != 0) {
+    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 (BUG(!doing)) doing = "(null)";
+    tor_log(severity, LD_CRYPTO, "crypto error while %s: %s (in %s:%s)",
+              doing, msg, lib, func);
+  }
+}
+
 /* Returns a trimmed and human-readable version of an openssl version string
 * <b>raw_version</b>. They are usually in the form of 'OpenSSL 1.0.0b 10
 * May 2012' and this will parse them into a form similar to '1.0.0b' */
@@ -127,7 +153,7 @@ tor_set_openssl_thread_id(CRYPTO_THREADID *threadid)
 
 /** Helper: Construct mutexes, and set callbacks to help OpenSSL handle being
  * multithreaded. Returns 0. */
-int
+static int
 setup_openssl_threading(void)
 {
 #ifndef NEW_THREAD_API
@@ -144,7 +170,7 @@ setup_openssl_threading(void)
 }
 
 /** free OpenSSL variables */
-void
+static void
 crypto_openssl_free_all(void)
 {
   tor_free(crypto_openssl_version_str);
@@ -164,3 +190,201 @@ crypto_openssl_free_all(void)
   }
 #endif /* !defined(NEW_THREAD_API) */
 }
+
+/** Perform early (pre-configuration) initialization tasks for OpenSSL. */
+void
+crypto_openssl_early_init(void)
+{
+#ifdef OPENSSL_1_1_API
+    OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS |
+                     OPENSSL_INIT_LOAD_CRYPTO_STRINGS |
+                     OPENSSL_INIT_ADD_ALL_CIPHERS |
+                     OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);
+#else
+    ERR_load_crypto_strings();
+    OpenSSL_add_all_algorithms();
+#endif
+
+    setup_openssl_threading();
+
+    unsigned long version_num = OpenSSL_version_num();
+    const char *version_str = OpenSSL_version(OPENSSL_VERSION);
+    if (version_num == OPENSSL_VERSION_NUMBER &&
+        !strcmp(version_str, OPENSSL_VERSION_TEXT)) {
+      log_info(LD_CRYPTO, "OpenSSL version matches version from headers "
+                 "(%lx: %s).", version_num, version_str);
+    } else {
+      log_warn(LD_CRYPTO, "OpenSSL version from headers does not match the "
+               "version we're running with. If you get weird crashes, that "
+               "might be why. (Compiled with %lx: %s; running with %lx: %s).",
+               (unsigned long)OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_TEXT,
+               version_num, version_str);
+    }
+
+    crypto_force_rand_ssleay();
+}
+
+#ifndef DISABLE_ENGINES
+/** Try to load an engine in a shared library via fully qualified path.
+ */
+static ENGINE *
+try_load_engine(const char *path, const char *engine)
+{
+  ENGINE *e = ENGINE_by_id("dynamic");
+  if (e) {
+    if (!ENGINE_ctrl_cmd_string(e, "ID", engine, 0) ||
+        !ENGINE_ctrl_cmd_string(e, "DIR_LOAD", "2", 0) ||
+        !ENGINE_ctrl_cmd_string(e, "DIR_ADD", path, 0) ||
+        !ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0)) {
+      ENGINE_free(e);
+      e = NULL;
+    }
+  }
+  return e;
+}
+#endif /* !defined(DISABLE_ENGINES) */
+
+#ifndef DISABLE_ENGINES
+/** Log any OpenSSL engines we're using at NOTICE. */
+static void
+log_engine(const char *fn, ENGINE *e)
+{
+  if (e) {
+    const char *name, *id;
+    name = ENGINE_get_name(e);
+    id = ENGINE_get_id(e);
+    log_notice(LD_CRYPTO, "Default OpenSSL engine for %s is %s [%s]",
+               fn, name?name:"?", id?id:"?");
+  } else {
+    log_info(LD_CRYPTO, "Using default implementation for %s", fn);
+  }
+}
+#endif /* !defined(DISABLE_ENGINES) */
+
+/** Initialize engines for openssl (if enabled). */
+static void
+crypto_openssl_init_engines(const char *accelName,
+                            const char *accelDir)
+{
+#ifdef DISABLE_ENGINES
+  (void)accelName;
+  (void)accelDir;
+  log_warn(LD_CRYPTO, "No OpenSSL hardware acceleration support enabled.");
+#else
+  ENGINE *e = NULL;
+
+  log_info(LD_CRYPTO, "Initializing OpenSSL engine support.");
+  ENGINE_load_builtin_engines();
+  ENGINE_register_all_complete();
+
+  if (accelName) {
+    if (accelDir) {
+      log_info(LD_CRYPTO, "Trying to load dynamic OpenSSL engine \"%s\""
+               " via path \"%s\".", accelName, accelDir);
+      e = try_load_engine(accelName, accelDir);
+    } else {
+      log_info(LD_CRYPTO, "Initializing dynamic OpenSSL engine \"%s\""
+               " acceleration support.", accelName);
+      e = ENGINE_by_id(accelName);
+    }
+    if (!e) {
+      log_warn(LD_CRYPTO, "Unable to load dynamic OpenSSL engine \"%s\".",
+               accelName);
+    } else {
+      log_info(LD_CRYPTO, "Loaded dynamic OpenSSL engine \"%s\".",
+               accelName);
+    }
+  }
+  if (e) {
+    log_info(LD_CRYPTO, "Loaded OpenSSL hardware acceleration engine,"
+             " setting default ciphers.");
+    ENGINE_set_default(e, ENGINE_METHOD_ALL);
+  }
+  /* Log, if available, the intersection of the set of algorithms
+     used by Tor and the set of algorithms available in the engine */
+  log_engine("RSA", ENGINE_get_default_RSA());
+  log_engine("DH", ENGINE_get_default_DH());
+#ifdef OPENSSL_1_1_API
+  log_engine("EC", ENGINE_get_default_EC());
+#else
+  log_engine("ECDH", ENGINE_get_default_ECDH());
+  log_engine("ECDSA", ENGINE_get_default_ECDSA());
+#endif /* defined(OPENSSL_1_1_API) */
+  log_engine("RAND", ENGINE_get_default_RAND());
+  log_engine("RAND (which we will not use)", ENGINE_get_default_RAND());
+  log_engine("SHA1", ENGINE_get_digest_engine(NID_sha1));
+  log_engine("3DES-CBC", ENGINE_get_cipher_engine(NID_des_ede3_cbc));
+  log_engine("AES-128-ECB", ENGINE_get_cipher_engine(NID_aes_128_ecb));
+  log_engine("AES-128-CBC", ENGINE_get_cipher_engine(NID_aes_128_cbc));
+#ifdef NID_aes_128_ctr
+  log_engine("AES-128-CTR", ENGINE_get_cipher_engine(NID_aes_128_ctr));
+#endif
+#ifdef NID_aes_128_gcm
+  log_engine("AES-128-GCM", ENGINE_get_cipher_engine(NID_aes_128_gcm));
+#endif
+  log_engine("AES-256-CBC", ENGINE_get_cipher_engine(NID_aes_256_cbc));
+#ifdef NID_aes_256_gcm
+  log_engine("AES-256-GCM", ENGINE_get_cipher_engine(NID_aes_256_gcm));
+#endif
+
+#endif /* defined(DISABLE_ENGINES) */
+}
+
+/** Perform late (post-init) initialization tasks for OpenSSL */
+int
+crypto_openssl_late_init(int useAccel, const char *accelName,
+                         const char *accelDir)
+{
+  if (useAccel > 0) {
+    crypto_openssl_init_engines(accelName, accelDir);
+  } else {
+    log_info(LD_CRYPTO, "NOT using OpenSSL engine support.");
+  }
+
+  if (crypto_force_rand_ssleay()) {
+    if (crypto_seed_rng() < 0)
+      return -1;
+  }
+
+  evaluate_evp_for_aes(-1);
+  evaluate_ctr_for_aes();
+
+  return 0;
+}
+
+/** Free crypto resources held by this thread. */
+void
+crypto_openssl_thread_cleanup(void)
+{
+#ifndef NEW_THREAD_API
+  ERR_remove_thread_state(NULL);
+#endif
+}
+
+/** Clean up global resources held by openssl. */
+void
+crypto_openssl_global_cleanup(void)
+{
+  #ifndef OPENSSL_1_1_API
+  EVP_cleanup();
+#endif
+#ifndef NEW_THREAD_API
+  ERR_remove_thread_state(NULL);
+#endif
+#ifndef OPENSSL_1_1_API
+  ERR_free_strings();
+#endif
+
+#ifndef DISABLE_ENGINES
+#ifndef OPENSSL_1_1_API
+  ENGINE_cleanup();
+#endif
+#endif
+
+  CONF_modules_unload(1);
+#ifndef OPENSSL_1_1_API
+  CRYPTO_cleanup_all_ex_data();
+#endif
+
+  crypto_openssl_free_all();
+}

+ 11 - 4
src/lib/crypt_ops/crypto_openssl_mgt.h

@@ -14,6 +14,8 @@
 #define TOR_CRYPTO_OPENSSL_H
 
 #include "orconfig.h"
+
+#ifdef ENABLE_OPENSSL
 #include <openssl/engine.h>
 
 /*
@@ -69,14 +71,19 @@
 #define NEW_THREAD_API
 #endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) && ... */
 
+void crypto_openssl_log_errors(int severity, const char *doing);
+
 /* global openssl state */
 const char * crypto_openssl_get_version_str(void);
 const char * crypto_openssl_get_header_version_str(void);
 
-/* OpenSSL threading setup function */
-int setup_openssl_threading(void);
+void crypto_openssl_early_init(void);
+int crypto_openssl_late_init(int useAccel, const char *accelName,
+                             const char *accelDir);
+
+void crypto_openssl_thread_cleanup(void);
+void crypto_openssl_global_cleanup(void);
 
-/* Tor OpenSSL utility functions */
-void crypto_openssl_free_all(void);
+#endif /* ENABLE_OPENSSL */
 
 #endif /* !defined(TOR_CRYPTO_OPENSSL_H) */

+ 1 - 1
src/lib/crypt_ops/crypto_pwbox.c

@@ -11,7 +11,7 @@
 #include <string.h>
 
 #include "lib/arch/bytes.h"
-#include "lib/crypt_ops/crypto.h"
+#include "lib/crypt_ops/crypto_cipher.h"
 #include "lib/crypt_ops/crypto_digest.h"
 #include "lib/crypt_ops/crypto_pwbox.h"
 #include "lib/crypt_ops/crypto_rand.h"

+ 111 - 11
src/lib/crypt_ops/crypto_rand.c

@@ -35,9 +35,24 @@
 #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"
+#endif
+
+#ifdef ENABLE_OPENSSL
 DISABLE_GCC_WARNING(redundant-decls)
 #include <openssl/rand.h>
 ENABLE_GCC_WARNING(redundant-decls)
+#endif
+
+#ifdef ENABLE_NSS
+#include <pk11pub.h>
+#include <secerr.h>
+#include <prerror.h>
+#endif
 
 #if __GNUC__ && GCC_VERSION >= 402
 #if GCC_VERSION >= 406
@@ -67,6 +82,7 @@ ENABLE_GCC_WARNING(redundant-decls)
 #endif
 
 #include <string.h>
+#include <errno.h>
 
 /**
  * How many bytes of entropy we add at once.
@@ -322,16 +338,24 @@ 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 openssl PRNG, in order to yield DLEN bytes.
+   * bytes from the PRNGs from our crypto librar(y/ies), in order to yield
+   * DLEN bytes.
    */
-  uint8_t inp[DLEN*2];
+  uint8_t inp[DLEN*3];
   uint8_t tmp[DLEN];
   tor_assert(out);
   while (out_len) {
-    crypto_rand((char*) inp, DLEN);
-    if (crypto_strongest_rand_raw(inp+DLEN, DLEN) < 0) {
+    memset(inp, 0, sizeof(inp));
+#ifdef ENABLE_OPENSSL
+    RAND_bytes(inp, DLEN);
+#endif
+#ifdef ENABLE_NSS
+    PK11_GenerateRandom(inp+DLEN, DLEN);
+#endif
+    if (crypto_strongest_rand_raw(inp+DLEN*2, DLEN) < 0) {
       // LCOV_EXCL_START
       log_err(LD_CRYPTO, "Failed to load strong entropy when generating an "
               "important key. Exiting.");
@@ -340,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;
     }
@@ -354,12 +378,13 @@ crypto_strongest_rand(uint8_t *out, size_t out_len)
 #undef DLEN
 }
 
+#ifdef ENABLE_OPENSSL
 /**
  * Seed OpenSSL's random number generator with bytes from the operating
  * system.  Return 0 on success, -1 on failure.
  **/
-int
-crypto_seed_rng(void)
+static int
+crypto_seed_openssl_rng(void)
 {
   int rand_poll_ok = 0, load_entropy_ok = 0;
   uint8_t buf[ADD_ENTROPY];
@@ -383,6 +408,52 @@ crypto_seed_rng(void)
   else
     return -1;
 }
+#endif
+
+#ifdef ENABLE_NSS
+/**
+ * Seed OpenSSL's random number generator with bytes from the operating
+ * system.  Return 0 on success, -1 on failure.
+ **/
+static int
+crypto_seed_nss_rng(void)
+{
+  uint8_t buf[ADD_ENTROPY];
+
+  int load_entropy_ok = !crypto_strongest_rand_raw(buf, sizeof(buf));
+  if (load_entropy_ok) {
+    if (PK11_RandomUpdate(buf, sizeof(buf)) != SECSuccess) {
+      load_entropy_ok = 0;
+    }
+  }
+
+  memwipe(buf, 0, sizeof(buf));
+
+  return load_entropy_ok ? 0 : -1;
+}
+#endif
+
+/**
+ * Seed the RNG for any and all crypto libraries that we're using with bytes
+ * from the operating system.  Return 0 on success, -1 on failure.
+ */
+int
+crypto_seed_rng(void)
+{
+  int seeded = 0;
+#ifdef ENABLE_NSS
+  if (crypto_seed_nss_rng() < 0)
+    return -1;
+  ++seeded;
+#endif
+#ifdef ENABLE_OPENSSL
+  if (crypto_seed_openssl_rng() < 0)
+    return -1;
+  ++seeded;
+#endif
+  tor_assert(seeded);
+  return 0;
+}
 
 /**
  * Write <b>n</b> bytes of strong random data to <b>to</b>. Supports mocking
@@ -407,17 +478,44 @@ crypto_rand, (char *to, size_t n))
 void
 crypto_rand_unmocked(char *to, size_t n)
 {
-  int r;
   if (n == 0)
     return;
 
   tor_assert(n < INT_MAX);
   tor_assert(to);
-  r = RAND_bytes((unsigned char*)to, (int)n);
+
+#ifdef ENABLE_NSS
+  SECStatus s = PK11_GenerateRandom((unsigned char*)to, (int)n);
+  if (s != SECSuccess) {
+    /* NSS rather sensibly might refuse to generate huge amounts of random
+     * data at once.  Unfortunately, our unit test do this in a couple of
+     * places.  To solve this issue, we use our XOF to stretch a shorter
+     * output when a longer one is needed.
+     *
+     * Yes, this is secure. */
+
+    /* This is longer than it needs to be; 1600 bits == 200 bytes is the
+     * state-size of SHA3. */
+#define BUFLEN 512
+    tor_assert(PR_GetError() == SEC_ERROR_INVALID_ARGS && n > BUFLEN);
+    unsigned char buf[BUFLEN];
+    s = PK11_GenerateRandom(buf, BUFLEN);
+    tor_assert(s == SECSuccess);
+    crypto_xof_t *xof = crypto_xof_new();
+    crypto_xof_add_bytes(xof, buf, BUFLEN);
+    crypto_xof_squeeze_bytes(xof, (unsigned char *)to, n);
+    crypto_xof_free(xof);
+    memwipe(buf, 0, BUFLEN);
+
+#undef BUFLEN
+  }
+#else
+  int r = RAND_bytes((unsigned char*)to, (int)n);
   /* We consider a PRNG failure non-survivable. Let's assert so that we get a
    * stack trace about where it happened.
    */
   tor_assert(r >= 0);
+#endif
 }
 
 /**
@@ -605,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) {
@@ -614,6 +713,7 @@ crypto_force_rand_ssleay(void)
     RAND_set_rand_method(default_method);
     return 1;
   }
+#endif
   return 0;
 }
 

File diff suppressed because it is too large
+ 211 - 726
src/lib/crypt_ops/crypto_rsa.c


+ 39 - 9
src/lib/crypt_ops/crypto_rsa.h

@@ -29,6 +29,13 @@
 /** Number of bytes added for PKCS1-OAEP padding. */
 #define PKCS1_OAEP_PADDING_OVERHEAD 42
 
+/** Length of encoded public key fingerprints, including space; but not
+ * including terminating NUL. */
+#define FINGERPRINT_LEN 49
+
+/** Value of 'e' to use in our public keys */
+#define TOR_RSA_EXPONENT 65537
+
 /** A public key, or a public/private key-pair. */
 typedef struct crypto_pk_t crypto_pk_t;
 
@@ -57,7 +64,7 @@ int crypto_pk_read_private_key_from_string(crypto_pk_t *env,
 int crypto_pk_write_private_key_to_filename(crypto_pk_t *env,
                                             const char *fname);
 
-int crypto_pk_check_key(crypto_pk_t *env);
+int crypto_pk_is_valid_private_key(const crypto_pk_t *env);
 int crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b);
 int crypto_pk_eq_keys(const crypto_pk_t *a, const crypto_pk_t *b);
 size_t crypto_pk_keysize(const crypto_pk_t *env);
@@ -65,7 +72,7 @@ int crypto_pk_num_bits(crypto_pk_t *env);
 crypto_pk_t *crypto_pk_dup_key(crypto_pk_t *orig);
 crypto_pk_t *crypto_pk_copy_full(crypto_pk_t *orig);
 int crypto_pk_key_is_private(const crypto_pk_t *key);
-int crypto_pk_public_exponent_ok(crypto_pk_t *env);
+int crypto_pk_public_exponent_ok(const crypto_pk_t *env);
 int crypto_pk_obsolete_public_hybrid_encrypt(crypto_pk_t *env, char *to,
                                     size_t tolen,
                                     const char *from, size_t fromlen,
@@ -86,8 +93,12 @@ int crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen,
                            const char *from, size_t fromlen);
 int crypto_pk_asn1_encode(const crypto_pk_t *pk, char *dest, size_t dest_len);
 crypto_pk_t *crypto_pk_asn1_decode(const char *str, size_t len);
+int crypto_pk_asn1_encode_private(const crypto_pk_t *pk,
+                                  char *dest, size_t dest_len);
+crypto_pk_t *crypto_pk_asn1_decode_private(const char *str, size_t len);
 int crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out,int add_space);
 int crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out);
+void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in);
 
 MOCK_DECL(int, crypto_pk_public_checksig_digest,(crypto_pk_t *env,
           const char *data, size_t datalen, const char *sig, size_t siglen));
@@ -96,20 +107,39 @@ int crypto_pk_private_sign_digest(crypto_pk_t *env, char *to, size_t tolen,
 int crypto_pk_get_digest(const crypto_pk_t *pk, char *digest_out);
 int crypto_pk_get_common_digests(crypto_pk_t *pk,
                                  common_digests_t *digests_out);
-int crypto_pk_base64_encode(const crypto_pk_t *pk, char **priv_out);
-crypto_pk_t *crypto_pk_base64_decode(const char *str, size_t len);
+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 ENABLE_OPENSSL
 /* Prototypes for private functions only used by tortls.c, crypto.c, and the
  * unit tests. */
 struct rsa_st;
-struct rsa_st *crypto_pk_get_rsa_(crypto_pk_t *env);
-crypto_pk_t *crypto_new_pk_from_rsa_(struct rsa_st *rsa);
-MOCK_DECL(struct evp_pkey_st *, crypto_pk_get_evp_pkey_,(crypto_pk_t *env,
-                                                         int private));
 struct evp_pkey_st;
+struct rsa_st *crypto_pk_get_openssl_rsa_(crypto_pk_t *env);
+crypto_pk_t *crypto_new_pk_from_openssl_rsa_(struct rsa_st *rsa);
+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);
 
 #ifdef TOR_UNIT_TESTS
-void crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src);
+#ifdef ENABLE_NSS
+struct SECItemStr;
+STATIC int secitem_uint_cmp(const struct SECItemStr *a,
+                            const struct SECItemStr *b);
+#endif
 #endif
 
 #endif

+ 738 - 0
src/lib/crypt_ops/crypto_rsa_nss.c

@@ -0,0 +1,738 @@
+/* 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;
+}
+
+/** 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. */
+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, &params,
+                                &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> is a valid private key.
+ */
+int
+crypto_pk_is_valid_private_key(const crypto_pk_t *key)
+{
+  /* We don't need to do validation here, since unlike OpenSSL, NSS won't let
+   * us load private keys without validating them. */
+  return key && key->seckey;
+}
+
+/** Return true iff <b>env</b> contains a public key whose public exponent
+ * equals 65537.
+ */
+int
+crypto_pk_public_exponent_ok(const 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(! env->pubkey))
+    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);
+  PK11SlotInfo *slot = PK11_GetBestSlot(CKM_RSA_PKCS, NULL);
+  if (!slot)
+    return NULL;
+
+  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,
+                            }
+  };
+
+  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");
+  }
+
+  if (! crypto_pk_is_valid_private_key(output)) {
+    crypto_pk_free(output);
+    output = NULL;
+  }
+
+  if (slot)
+    PK11_FreeSlot(slot);
+
+  return output;
+}

+ 590 - 0
src/lib/crypt_ops/crypto_rsa_openssl.c

@@ -0,0 +1,590 @@
+/* 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 OpenSSL implementations of our RSA code.
+ **/
+
+#include "lib/crypt_ops/compat_openssl.h"
+#include "lib/crypt_ops/crypto_rsa.h"
+#include "lib/crypt_ops/crypto_util.h"
+#include "lib/ctime/di_ops.h"
+#include "lib/log/util_bug.h"
+#include "lib/fs/files.h"
+
+DISABLE_GCC_WARNING(redundant-decls)
+
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+#include <openssl/engine.h>
+#include <openssl/rand.h>
+#include <openssl/bn.h>
+#include <openssl/conf.h>
+
+ENABLE_GCC_WARNING(redundant-decls)
+
+#include "lib/log/log.h"
+#include "lib/encoding/binascii.h"
+
+#include <string.h>
+
+/** Declaration for crypto_pk_t structure. */
+struct crypto_pk_t
+{
+  int refs; /**< reference count, so we don't have to copy keys */
+  RSA *key; /**< The key itself */
+};
+
+/** 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 *k)
+{
+#ifdef OPENSSL_1_1_API
+  if (!k || !k->key)
+    return 0;
+
+  const BIGNUM *p, *q;
+  RSA_get0_factors(k->key, &p, &q);
+  return p != NULL; /* XXX/yawning: Should we check q? */
+#else /* !(defined(OPENSSL_1_1_API)) */
+  return k && k->key && k->key->p;
+#endif /* defined(OPENSSL_1_1_API) */
+}
+
+/** used by tortls.c: wrap an RSA* in a crypto_pk_t. Takes ownership of
+ * its argument. */
+crypto_pk_t *
+crypto_new_pk_from_openssl_rsa_(RSA *rsa)
+{
+  crypto_pk_t *env;
+  tor_assert(rsa);
+  env = tor_malloc(sizeof(crypto_pk_t));
+  env->refs = 1;
+  env->key = rsa;
+  return env;
+}
+
+/** Helper, used by tor-gencert.c.  Return a copy of the private RSA from a
+ * crypto_pk_t. */
+RSA *
+crypto_pk_get_openssl_rsa_(crypto_pk_t *env)
+{
+  return RSAPrivateKey_dup(env->key);
+}
+
+/** 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(EVP_PKEY *,
+crypto_pk_get_openssl_evp_pkey_,(crypto_pk_t *env, int private))
+{
+  RSA *key = NULL;
+  EVP_PKEY *pkey = NULL;
+  tor_assert(env->key);
+  if (private) {
+    if (!(key = RSAPrivateKey_dup(env->key)))
+      goto error;
+  } else {
+    if (!(key = RSAPublicKey_dup(env->key)))
+      goto error;
+  }
+  if (!(pkey = EVP_PKEY_new()))
+    goto error;
+  if (!(EVP_PKEY_assign_RSA(pkey, key)))
+    goto error;
+  return pkey;
+ error:
+  if (pkey)
+    EVP_PKEY_free(pkey);
+  if (key)
+    RSA_free(key);
+  return NULL;
+}
+
+/** 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))
+{
+  RSA *rsa;
+
+  rsa = RSA_new();
+  tor_assert(rsa);
+  return crypto_new_pk_from_openssl_rsa_(rsa);
+}
+
+/** Release a reference to an asymmetric key; when all the references
+ * are released, free the key.
+ */
+void
+crypto_pk_free_(crypto_pk_t *env)
+{
+  if (!env)
+    return;
+
+  if (--env->refs > 0)
+    return;
+  tor_assert(env->refs == 0);
+
+  if (env->key)
+    RSA_free(env->key);
+
+  tor_free(env);
+}
+
+/** 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 *env, int bits))
+{
+  tor_assert(env);
+
+  if (env->key) {
+    RSA_free(env->key);
+    env->key = NULL;
+  }
+
+  {
+    BIGNUM *e = BN_new();
+    RSA *r = NULL;
+    if (!e)
+      goto done;
+    if (! BN_set_word(e, TOR_RSA_EXPONENT))
+      goto done;
+    r = RSA_new();
+    if (!r)
+      goto done;
+    if (RSA_generate_key_ex(r, bits, e, NULL) == -1)
+      goto done;
+
+    env->key = r;
+    r = NULL;
+  done:
+    if (e)
+      BN_clear_free(e);
+    if (r)
+      RSA_free(r);
+  }
+
+  if (!env->key) {
+    crypto_openssl_log_errors(LOG_WARN, "generating RSA key");
+    return -1;
+  }
+
+  return 0;
+}
+
+/** Return true if <b>env</b> has a valid key; false otherwise.
+ */
+int
+crypto_pk_is_valid_private_key(const crypto_pk_t *env)
+{
+  int r;
+  tor_assert(env);
+
+  r = RSA_check_key(env->key);
+  if (r <= 0) {
+    crypto_openssl_log_errors(LOG_WARN,"checking RSA key");
+    return 0;
+  } else {
+    return 1;
+  }
+}
+
+/** Return true iff <b>env</b> contains a public key whose public exponent
+ * equals TOR_RSA_EXPONENT.
+ */
+int
+crypto_pk_public_exponent_ok(const crypto_pk_t *env)
+{
+  tor_assert(env);
+  tor_assert(env->key);
+
+  const BIGNUM *e;
+
+#ifdef OPENSSL_1_1_API
+  const BIGNUM *n, *d;
+  RSA_get0_key(env->key, &n, &e, &d);
+#else
+  e = env->key->e;
+#endif /* defined(OPENSSL_1_1_API) */
+  return BN_is_word(e, TOR_RSA_EXPONENT);
+}
+
+/** 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->key != NULL);
+  char b_is_non_null = (b != NULL) && (b->key != 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;
+
+  const BIGNUM *a_n, *a_e;
+  const BIGNUM *b_n, *b_e;
+
+#ifdef OPENSSL_1_1_API
+  const BIGNUM *a_d, *b_d;
+  RSA_get0_key(a->key, &a_n, &a_e, &a_d);
+  RSA_get0_key(b->key, &b_n, &b_e, &b_d);
+#else
+  a_n = a->key->n;
+  a_e = a->key->e;
+  b_n = b->key->n;
+  b_e = b->key->e;
+#endif /* defined(OPENSSL_1_1_API) */
+
+  tor_assert(a_n != NULL && a_e != NULL);
+  tor_assert(b_n != NULL && b_e != NULL);
+
+  result = BN_cmp(a_n, b_n);
+  if (result)
+    return result;
+  return BN_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 *env)
+{
+  tor_assert(env);
+  tor_assert(env->key);
+
+  return (size_t) RSA_size((RSA*)env->key);
+}
+
+/** Return the size of the public key modulus of <b>env</b>, in bits. */
+int
+crypto_pk_num_bits(crypto_pk_t *env)
+{
+  tor_assert(env);
+  tor_assert(env->key);
+
+#ifdef OPENSSL_1_1_API
+  /* It's so stupid that there's no other way to check that n is valid
+   * before calling RSA_bits().
+   */
+  const BIGNUM *n, *e, *d;
+  RSA_get0_key(env->key, &n, &e, &d);
+  tor_assert(n != NULL);
+
+  return RSA_bits(env->key);
+#else /* !(defined(OPENSSL_1_1_API)) */
+  tor_assert(env->key->n);
+  return BN_num_bits(env->key->n);
+#endif /* defined(OPENSSL_1_1_API) */
+}
+
+/** Increase the reference count of <b>env</b>, and return it.
+ */
+crypto_pk_t *
+crypto_pk_dup_key(crypto_pk_t *env)
+{
+  tor_assert(env);
+  tor_assert(env->key);
+
+  env->refs++;
+  return env;
+}
+
+/** Replace dest with src (private key only).  (Dest must have a refcount
+ * of 1)
+ */
+void
+crypto_pk_assign_private(crypto_pk_t *dest, const crypto_pk_t *src)
+{
+  tor_assert(dest);
+  tor_assert(dest->refs == 1);
+  tor_assert(src);
+  RSA_free(dest->key);
+  dest->key = RSAPrivateKey_dup(src->key);
+}
+
+/** Replace dest with src (public key only).  (Dest must have a refcount
+ * of 1)
+ */
+void
+crypto_pk_assign_public(crypto_pk_t *dest, const crypto_pk_t *src)
+{
+  tor_assert(dest);
+  tor_assert(dest->refs == 1);
+  tor_assert(src);
+  RSA_free(dest->key);
+  dest->key = RSAPublicKey_dup(src->key);
+}
+
+/** 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 *env)
+{
+  RSA *new_key;
+  int privatekey = 0;
+  tor_assert(env);
+  tor_assert(env->key);
+
+  if (crypto_pk_key_is_private(env)) {
+    new_key = RSAPrivateKey_dup(env->key);
+    privatekey = 1;
+  } else {
+    new_key = RSAPublicKey_dup(env->key);
+  }
+  if (!new_key) {
+    /* LCOV_EXCL_START
+     *
+     * We can't cause RSA*Key_dup() to fail, so we can't really test this.
+     */
+    log_err(LD_CRYPTO, "Unable to duplicate a %s key: openssl failed.",
+            privatekey?"private":"public");
+    crypto_openssl_log_errors(LOG_ERR,
+                      privatekey ? "Duplicating a private key" :
+                      "Duplicating a public key");
+    tor_fragile_assert();
+    return NULL;
+    /* LCOV_EXCL_STOP */
+  }
+
+  return crypto_new_pk_from_openssl_rsa_(new_key);
+}
+
+/** 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)
+{
+  int r;
+  tor_assert(env);
+  tor_assert(from);
+  tor_assert(to);
+  tor_assert(fromlen<INT_MAX);
+  tor_assert(tolen >= crypto_pk_keysize(env));
+
+  r = RSA_public_encrypt((int)fromlen,
+                         (unsigned char*)from, (unsigned char*)to,
+                         env->key, crypto_get_rsa_padding(padding));
+  if (r<0) {
+    crypto_openssl_log_errors(LOG_WARN, "performing RSA encryption");
+    return -1;
+  }
+  return r;
+}
+
+/** 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>env</b>.
+ */
+int
+crypto_pk_private_decrypt(crypto_pk_t *env, char *to,
+                          size_t tolen,
+                          const char *from, size_t fromlen,
+                          int padding, int warnOnFailure)
+{
+  int r;
+  tor_assert(env);
+  tor_assert(from);
+  tor_assert(to);
+  tor_assert(env->key);
+  tor_assert(fromlen<INT_MAX);
+  tor_assert(tolen >= crypto_pk_keysize(env));
+  if (!crypto_pk_key_is_private(env))
+    /* Not a private key */
+    return -1;
+
+  r = RSA_private_decrypt((int)fromlen,
+                          (unsigned char*)from, (unsigned char*)to,
+                          env->key, crypto_get_rsa_padding(padding));
+
+  if (r<0) {
+    crypto_openssl_log_errors(warnOnFailure?LOG_WARN:LOG_DEBUG,
+                      "performing RSA decryption");
+    return -1;
+  }
+  return r;
+}
+
+/** Check the signature in <b>from</b> (<b>fromlen</b> bytes long) with the
+ * public key in <b>env</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>env</b>.
+ */
+MOCK_IMPL(int,
+crypto_pk_public_checksig,(const crypto_pk_t *env, char *to,
+                           size_t tolen,
+                           const char *from, size_t fromlen))
+{
+  int r;
+  tor_assert(env);
+  tor_assert(from);
+  tor_assert(to);
+  tor_assert(fromlen < INT_MAX);
+  tor_assert(tolen >= crypto_pk_keysize(env));
+  r = RSA_public_decrypt((int)fromlen,
+                         (unsigned char*)from, (unsigned char*)to,
+                         env->key, RSA_PKCS1_PADDING);
+
+  if (r<0) {
+    crypto_openssl_log_errors(LOG_INFO, "checking RSA signature");
+    return -1;
+  }
+  return r;
+}
+
+/** 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 *env, char *to, size_t tolen,
+                       const char *from, size_t fromlen)
+{
+  int r;
+  tor_assert(env);
+  tor_assert(from);
+  tor_assert(to);
+  tor_assert(fromlen < INT_MAX);
+  tor_assert(tolen >= crypto_pk_keysize(env));
+  if (!crypto_pk_key_is_private(env))
+    /* Not a private key */
+    return -1;
+
+  r = RSA_private_encrypt((int)fromlen,
+                          (unsigned char*)from, (unsigned char*)to,
+                          (RSA*)env->key, RSA_PKCS1_PADDING);
+  if (r<0) {
+    crypto_openssl_log_errors(LOG_WARN, "generating RSA signature");
+    return -1;
+  }
+  return r;
+}
+
+/** 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)
+{
+  int len;
+  unsigned char *buf = NULL;
+
+  len = i2d_RSAPublicKey(pk->key, &buf);
+  if (len < 0 || buf == NULL)
+    return -1;
+
+  if ((size_t)len > dest_len || dest_len > SIZE_T_CEILING) {
+    OPENSSL_free(buf);
+    return -1;
+  }
+  /* We don't encode directly into 'dest', because that would be illegal
+   * type-punning.  (C99 is smarter than me, C99 is smarter than me...)
+   */
+  memcpy(dest,buf,len);
+  OPENSSL_free(buf);
+  return len;
+}
+
+/** 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)
+{
+  RSA *rsa;
+  unsigned char *buf;
+  const unsigned char *cp;
+  cp = buf = tor_malloc(len);
+  memcpy(buf,str,len);
+  rsa = d2i_RSAPublicKey(NULL, &cp, len);
+  tor_free(buf);
+  if (!rsa) {
+    crypto_openssl_log_errors(LOG_WARN,"decoding public key");
+    return NULL;
+  }
+  return crypto_new_pk_from_openssl_rsa_(rsa);
+}
+
+/** ASN.1-encode the private 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_private(const crypto_pk_t *pk, char *dest,
+                              size_t dest_len)
+{
+  int len;
+  unsigned char *buf = NULL;
+
+  len = i2d_RSAPrivateKey(pk->key, &buf);
+  if (len < 0 || buf == NULL)
+    return -1;
+
+  if ((size_t)len > dest_len || dest_len > SIZE_T_CEILING) {
+    OPENSSL_free(buf);
+    return -1;
+  }
+  /* We don't encode directly into 'dest', because that would be illegal
+   * type-punning.  (C99 is smarter than me, C99 is smarter than me...)
+   */
+  memcpy(dest,buf,len);
+  OPENSSL_free(buf);
+  return len;
+}
+
+/** Decode an ASN.1-encoded private key from <b>str</b>; return the result on
+ * success and NULL on failure.
+ */
+crypto_pk_t *
+crypto_pk_asn1_decode_private(const char *str, size_t len)
+{
+  RSA *rsa;
+  unsigned char *buf;
+  const unsigned char *cp;
+  cp = buf = tor_malloc(len);
+  memcpy(buf,str,len);
+  rsa = d2i_RSAPrivateKey(NULL, &cp, len);
+  tor_free(buf);
+  if (!rsa) {
+    crypto_openssl_log_errors(LOG_WARN,"decoding public key");
+    return NULL;
+  }
+  crypto_pk_t *result = crypto_new_pk_from_openssl_rsa_(rsa);
+  if (! crypto_pk_is_valid_private_key(result)) {
+    crypto_pk_free(result);
+    return NULL;
+  }
+  return result;
+}

+ 49 - 1
src/lib/crypt_ops/crypto_s2k.c

@@ -12,7 +12,7 @@
 
 #define CRYPTO_S2K_PRIVATE
 
-#include "lib/crypt_ops/crypto.h"
+#include "lib/crypt_ops/crypto_cipher.h"
 #include "lib/crypt_ops/crypto_digest.h"
 #include "lib/crypt_ops/crypto_hkdf.h"
 #include "lib/crypt_ops/crypto_rand.h"
@@ -20,8 +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
@@ -271,6 +277,7 @@ secret_to_key_compute_key(uint8_t *key_out, size_t key_out_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),
@@ -278,6 +285,47 @@ 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
+      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
     }
 
     case S2K_TYPE_SCRYPT: {

+ 4 - 25
src/lib/crypt_ops/crypto_util.c

@@ -10,7 +10,6 @@
  * \brief Common cryptographic utilities.
  **/
 
-#ifndef CRYPTO_UTIL_PRIVATE
 #define CRYPTO_UTIL_PRIVATE
 
 #include "lib/crypt_ops/crypto_util.h"
@@ -24,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"
@@ -105,25 +106,3 @@ memwipe(void *mem, uint8_t byte, size_t sz)
    **/
   memset(mem, byte, sz);
 }
-
-/** Log all pending crypto errors at level <b>severity</b>.  Use
- * <b>doing</b> to describe our current activities.
- */
-void
-crypto_log_errors(int severity, const char *doing)
-{
-  unsigned long err;
-  const char *msg, *lib, *func;
-  while ((err = ERR_get_error()) != 0) {
-    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 (BUG(!doing)) doing = "(null)";
-    tor_log(severity, LD_CRYPTO, "crypto error while %s: %s (in %s:%s)",
-              doing, msg, lib, func);
-  }
-}
-#endif /* !defined(CRYPTO_UTIL_PRIVATE) */

+ 0 - 9
src/lib/crypt_ops/crypto_util.h

@@ -18,13 +18,4 @@
 /** OpenSSL-based utility functions. */
 void memwipe(void *mem, uint8_t byte, size_t sz);
 
-/** Log utility function */
-void crypto_log_errors(int severity, const char *doing);
-
-#ifdef CRYPTO_UTIL_PRIVATE
-#ifdef TOR_UNIT_TESTS
-#endif /* defined(TOR_UNIT_TESTS) */
-#endif /* defined(CRYPTO_UTIL_PRIVATE) */
-
 #endif /* !defined(TOR_CRYPTO_UTIL_H) */
-

+ 27 - 5
src/lib/crypt_ops/include.am

@@ -6,16 +6,15 @@ noinst_LIBRARIES += src/lib/libtor-crypt-ops-testing.a
 endif
 
 src_lib_libtor_crypt_ops_a_SOURCES =			\
-	src/lib/crypt_ops/aes.c				\
-	src/lib/crypt_ops/crypto.c			\
+	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_digest.c		\
 	src/lib/crypt_ops/crypto_ed25519.c		\
 	src/lib/crypt_ops/crypto_format.c		\
 	src/lib/crypt_ops/crypto_hkdf.c			\
+	src/lib/crypt_ops/crypto_init.c			\
 	src/lib/crypt_ops/crypto_ope.c          	\
-	src/lib/crypt_ops/crypto_openssl_mgt.c		\
 	src/lib/crypt_ops/crypto_pwbox.c		\
 	src/lib/crypt_ops/crypto_rand.c			\
 	src/lib/crypt_ops/crypto_rsa.c			\
@@ -23,10 +22,31 @@ src_lib_libtor_crypt_ops_a_SOURCES =			\
 	src/lib/crypt_ops/crypto_util.c                 \
 	src/lib/crypt_ops/digestset.c
 
+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_rsa_nss.c
+else
+src_lib_libtor_crypt_ops_a_SOURCES +=			\
+	src/lib/crypt_ops/aes_openssl.c			\
+	src/lib/crypt_ops/crypto_rsa_openssl.c
+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
+
+src_lib_libtor_crypt_ops_a_CFLAGS = $(AM_CFLAGS) $(TOR_CFLAGS_CRYPTLIB)
+
 src_lib_libtor_crypt_ops_testing_a_SOURCES = \
 	$(src_lib_libtor_crypt_ops_a_SOURCES)
 src_lib_libtor_crypt_ops_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
-src_lib_libtor_crypt_ops_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+src_lib_libtor_crypt_ops_testing_a_CFLAGS = \
+	$(AM_CFLAGS) $(TOR_CFLAGS_CRYPTLIB) $(TEST_CFLAGS)
 
 noinst_HEADERS +=					\
 	src/lib/crypt_ops/aes.h				\
@@ -36,8 +56,10 @@ noinst_HEADERS +=					\
 	src/lib/crypt_ops/crypto_digest.h		\
 	src/lib/crypt_ops/crypto_ed25519.h		\
 	src/lib/crypt_ops/crypto_format.h		\
-	src/lib/crypt_ops/crypto.h			\
+	src/lib/crypt_ops/crypto_cipher.h		\
 	src/lib/crypt_ops/crypto_hkdf.h			\
+	src/lib/crypt_ops/crypto_init.h			\
+	src/lib/crypt_ops/crypto_nss_mgt.h		\
 	src/lib/crypt_ops/crypto_openssl_mgt.h		\
 	src/lib/crypt_ops/crypto_ope.h          	\
 	src/lib/crypt_ops/crypto_pwbox.h		\

+ 1 - 0
src/lib/encoding/.may_include

@@ -1,5 +1,6 @@
 orconfig.h
 lib/cc/*.h
+lib/ctime/*.h
 lib/encoding/*.h
 lib/intmath/*.h
 lib/log/*.h

+ 2 - 0
src/lib/encoding/include.am

@@ -9,6 +9,7 @@ src_lib_libtor_encoding_a_SOURCES =			\
 	src/lib/encoding/confline.c			\
 	src/lib/encoding/cstring.c			\
 	src/lib/encoding/keyval.c			\
+	src/lib/encoding/pem.c				\
 	src/lib/encoding/time_fmt.c
 
 src_lib_libtor_encoding_testing_a_SOURCES = \
@@ -21,4 +22,5 @@ noinst_HEADERS +=					\
 	src/lib/encoding/confline.h			\
 	src/lib/encoding/cstring.h			\
 	src/lib/encoding/keyval.h			\
+	src/lib/encoding/pem.h				\
 	src/lib/encoding/time_fmt.h

+ 106 - 0
src/lib/encoding/pem.c

@@ -0,0 +1,106 @@
+/* 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 pem.c
+ *
+ * \brief Implement a trivial version of PEM encoding, for use with NSS.
+ *
+ * We deliberately do not support any encryption here.
+ **/
+
+#include "orconfig.h"
+
+#include "lib/encoding/pem.h"
+
+#include "lib/ctime/di_ops.h"
+#include "lib/encoding/binascii.h"
+#include "lib/log/util_bug.h"
+#include "lib/malloc/malloc.h"
+#include "lib/string/printf.h"
+#include "lib/string/util_string.h"
+
+#include <string.h>
+
+/**
+ * Return the length of a <b>src_len</b>-byte object when tagged with
+ * <b>objtype</b> and PEM-encoded.  Includes terminating NUL.
+ */
+size_t
+pem_encoded_size(size_t src_len, const char *objtype)
+{
+  return
+    strlen("-----BEGIN -----\n") +
+    strlen("-----END -----\n") +
+    strlen(objtype) * 2 +
+    base64_encode_size(src_len, BASE64_ENCODE_MULTILINE)
+    + 1;
+}
+
+/**
+ * PEM-encode the <b>srclen</b>-byte object at <b>src</b> into the
+ * <b>destlen<\b>-byte buffer at <b>dest</b>, tagging it with <b>objtype</b>.
+ * Return 0 on success and -1 on failure.
+ */
+int
+pem_encode(char *dest, size_t destlen, const uint8_t *src, size_t srclen,
+           const char *objtype)
+{
+  if (tor_snprintf(dest, destlen, "-----BEGIN %s-----\n", objtype) < 0)
+    return -1;
+
+  size_t offset = strlen(dest);
+
+  int n = base64_encode(dest + offset, destlen - offset,
+                        (const char *)src, srclen, BASE64_ENCODE_MULTILINE);
+  if (n < 0)
+    return -1;
+  offset += n;
+  if (BUG(offset > destlen))
+    return -1;
+
+  if (tor_snprintf(dest + offset, destlen - offset,
+                   "-----END %s-----\n", objtype) < 0)
+    return -1;
+
+  tor_assert(strlen(dest) + 1 <= pem_encoded_size(srclen, objtype));
+  return 0;
+}
+
+/**
+ * Given a PEM-encoded block of size <b>srclen</b> in <b>src</b>, if it has
+ * object type <b>objtype</b>, decode it into the <b>destlen</b>-byte buffer
+ * at <b>dest</b>.  Return the number of characters decoded on success, or -1
+ * on failure.
+ */
+int
+pem_decode(uint8_t *dest, size_t destlen, const char *src, size_t srclen,
+           const char *objtype)
+{
+  const char *eos = src + srclen;
+
+  src = eat_whitespace_eos(src, eos);
+
+  char *tag = NULL;
+  tor_asprintf(&tag, "-----BEGIN %s-----\n", objtype);
+  if ((size_t)(eos-src) < strlen(tag) || fast_memneq(src, tag, strlen(tag))) {
+    tor_free(tag);
+    return -1;
+  }
+  src += strlen(tag);
+  tor_free(tag);
+
+  // NOTE lack of trailing \n.  We do not enforce its presence.
+  tor_asprintf(&tag, "\n-----END %s-----", objtype);
+  const char *end_of_base64 = tor_memstr(src, eos-src, tag);
+  tor_free(tag);
+  if (end_of_base64 == NULL)
+    return -1;
+
+  /* Should we actually allow extra stuff at the end? */
+
+  return base64_decode((char*)dest, destlen, src, end_of_base64-src);
+}

+ 26 - 0
src/lib/encoding/pem.h

@@ -0,0 +1,26 @@
+/* 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 pem.h
+ *
+ * \brief Header for pem.c
+ **/
+
+#ifndef TOR_PEM_H
+#define TOR_PEM_H
+
+#include "orconfig.h"
+#include <stddef.h>
+#include "lib/cc/torint.h"
+
+size_t pem_encoded_size(size_t src_len, const char *objtype);
+int pem_encode(char *dest, size_t destlen, const uint8_t *src, size_t srclen,
+               const char *objtype);
+int pem_decode(uint8_t *dest, size_t destlen, const char *src, size_t srclen,
+               const char *objtype);
+
+#endif

+ 1 - 1
src/lib/include.libdonna.am

@@ -1,4 +1,4 @@
-src_lib_libcurve25519_donna_a_CFLAGS=
+src_lib_libcurve25519_donna_a_CFLAGS=$(TOR_CFLAGS_CRYPTO)
 
 if BUILD_CURVE25519_DONNA
 src_lib_libcurve25519_donna_a_SOURCES=\

+ 16 - 9
src/lib/process/daemon.c

@@ -41,15 +41,16 @@ static int daemon_filedes[2];
 /** Start putting the process into daemon mode: fork and drop all resources
  * except standard fds.  The parent process never returns, but stays around
  * until finish_daemon is called.  (Note: it's safe to call this more
- * than once: calls after the first are ignored.)
+ * than once: calls after the first are ignored.)  Return true if we actually
+ * forked and this is the child; false otherwise.
  */
-void
+int
 start_daemon(void)
 {
   pid_t pid;
 
   if (start_daemon_called)
-    return;
+    return 0;
   start_daemon_called = 1;
 
   if (pipe(daemon_filedes)) {
@@ -80,6 +81,7 @@ start_daemon(void)
       exit(0); // exit ok: during daemonize, daemonizing.
     else
       exit(1); /* child reported error. exit ok: daemonize failed. */
+    return 0; // LCOV_EXCL_LINE unreachable
   } else { /* Child */
     close(daemon_filedes[0]); /* we only write */
 
@@ -95,22 +97,23 @@ start_daemon(void)
     }
     set_main_thread(); /* We are now the main thread. */
 
-    return;
+    return 1;
   }
 }
 
 /** Finish putting the process into daemon mode: drop standard fds, and tell
  * the parent process to exit.  (Note: it's safe to call this more than once:
  * calls after the first are ignored.  Calls start_daemon first if it hasn't
- * been called already.)
+ * been called already.) Return true if we actually did a fork; false if we
+ * didn't.
  */
-void
+int
 finish_daemon(const char *desired_cwd)
 {
   int nullfd;
   char c = '.';
   if (finish_daemon_called)
-    return;
+    return 0;
   if (!start_daemon_called)
     start_daemon();
   finish_daemon_called = 1;
@@ -149,16 +152,20 @@ finish_daemon(const char *desired_cwd)
     log_err(LD_GENERAL,"write failed. Exiting.");
   }
   close(daemon_filedes[1]);
+
+  return 0;
 }
 #else /* !(!defined(_WIN32)) */
 /* defined(_WIN32) */
-void
+int
 start_daemon(void)
 {
+  return 0;
 }
-void
+int
 finish_daemon(const char *cp)
 {
   (void)cp;
+  return 0;
 }
 #endif /* !defined(_WIN32) */

+ 2 - 2
src/lib/process/daemon.h

@@ -11,7 +11,7 @@
 #ifndef TOR_DAEMON_H
 #define TOR_DAEMON_H
 
-void start_daemon(void);
-void finish_daemon(const char *desired_cwd);
+int start_daemon(void);
+int finish_daemon(const char *desired_cwd);
 
 #endif

+ 21 - 3
src/lib/tls/include.am

@@ -7,14 +7,32 @@ endif
 
 src_lib_libtor_tls_a_SOURCES =			\
 	src/lib/tls/buffers_tls.c		\
-	src/lib/tls/tortls.c
+	src/lib/tls/tortls.c			\
+	src/lib/tls/x509.c
+
+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 = \
 	$(src_lib_libtor_tls_a_SOURCES)
 src_lib_libtor_tls_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
-src_lib_libtor_tls_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+src_lib_libtor_tls_testing_a_CFLAGS = \
+	$(AM_CFLAGS) $(TOR_CFLAGS_CRYPTLIB) $(TEST_CFLAGS)
 
 noinst_HEADERS +=				\
 	src/lib/tls/ciphers.inc			\
 	src/lib/tls/buffers_tls.h		\
-	src/lib/tls/tortls.h
+	src/lib/tls/tortls.h			\
+	src/lib/tls/tortls_internal.h		\
+	src/lib/tls/tortls_st.h			\
+	src/lib/tls/x509.h			\
+	src/lib/tls/x509_internal.h

File diff suppressed because it is too large
+ 143 - 998
src/lib/tls/tortls.c


+ 36 - 174
src/lib/tls/tortls.h

@@ -12,14 +12,27 @@
  **/
 
 #include "lib/crypt_ops/crypto_rsa.h"
-#include "lib/crypt_ops/compat_openssl.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;
 
-/* Opaque structure to hold an X509 certificate. */
-typedef struct tor_x509_cert_t tor_x509_cert_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. */
 #define MIN_TOR_TLS_ERROR_VAL_     -9
@@ -52,164 +65,30 @@ typedef struct tor_x509_cert_t tor_x509_cert_t;
 
 #define TOR_TLS_IS_ERROR(rv) ((rv) < TOR_TLS_CLOSE)
 
-#ifdef TORTLS_PRIVATE
-#define TOR_TLS_MAGIC 0x71571571
-
-typedef enum {
-    TOR_TLS_ST_HANDSHAKE, TOR_TLS_ST_OPEN, TOR_TLS_ST_GOTCLOSE,
-    TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED, TOR_TLS_ST_RENEGOTIATE,
-    TOR_TLS_ST_BUFFEREVENT
-} tor_tls_state_t;
-#define tor_tls_state_bitfield_t ENUM_BF(tor_tls_state_t)
-
-struct x509_st;
-struct ssl_st;
-struct ssl_ctx_st;
-struct ssl_session_st;
-
 /** Holds a SSL_CTX object and related state used to configure TLS
  * connections.
  */
-typedef struct tor_tls_context_t {
-  int refcnt;
-  struct ssl_ctx_st *ctx;
-  tor_x509_cert_t *my_link_cert;
-  tor_x509_cert_t *my_id_cert;
-  tor_x509_cert_t *my_auth_cert;
-  crypto_pk_t *link_key;
-  crypto_pk_t *auth_key;
-} tor_tls_context_t;
-
-/** Structure that we use for a single certificate. */
-struct tor_x509_cert_t {
-  struct x509_st *cert;
-  uint8_t *encoded;
-  size_t encoded_len;
-  unsigned pkey_digests_set : 1;
-  common_digests_t cert_digests;
-  common_digests_t pkey_digests;
-};
-
-/** Holds a SSL object and its associated data.  Members are only
- * accessed from within tortls.c.
- */
-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. */
-  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
-                                       * have completed successfully. */
-  unsigned int isServer:1; /**< True iff this is a server-side connection */
-  unsigned int wasV2Handshake:1; /**< True iff the original handshake for
-                                  * this connection used the updated version
-                                  * of the connection protocol (client sends
-                                  * different cipher list, server sends only
-                                  * one certificate). */
-  /** True iff we should call negotiated_callback when we're done reading. */
-  unsigned int got_renegotiate:1;
-  /** 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
-   * tor_tls_get_n_raw_bytes() for usage.
-   */
-  unsigned long last_write_count;
-  unsigned long last_read_count;
-  /** If set, a callback to invoke whenever the client tries to renegotiate
-   * the handshake. */
-  void (*negotiated_callback)(tor_tls_t *tls, void *arg);
-  /** Argument to pass to negotiated_callback. */
-  void *callback_arg;
-};
-
-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);
-#ifdef TORTLS_OPENSSL_PRIVATE
-STATIC int always_accept_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx);
-STATIC int tor_tls_classify_client_ciphers(const struct ssl_st *ssl,
-                                           STACK_OF(SSL_CIPHER) *peer_ciphers);
-#endif
-STATIC int tor_tls_client_is_using_v2_ciphers(const struct ssl_st *ssl);
-MOCK_DECL(STATIC void, try_to_extract_certs_from_tls,
-          (int severity, tor_tls_t *tls, struct x509_st **cert_out,
-           struct x509_st **id_cert_out));
-#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY
-STATIC size_t SSL_SESSION_get_master_key(struct ssl_session_st *s,
-                                         uint8_t *out,
-                                         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) */
-MOCK_DECL(STATIC struct x509_st *, tor_tls_create_certificate,
-                                                   (crypto_pk_t *rsa,
-                                                    crypto_pk_t *rsa_sign,
-                                                    const char *cname,
-                                                    const char *cname_sign,
-                                                  unsigned int cert_lifetime));
-STATIC tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity,
-                   unsigned int key_lifetime, unsigned flags, int is_client);
-MOCK_DECL(STATIC tor_x509_cert_t *, tor_x509_cert_new,
-          (struct x509_st *x509_cert));
-STATIC int tor_tls_context_init_one(tor_tls_context_t **ppcontext,
-                                    crypto_pk_t *identity,
-                                    unsigned int key_lifetime,
-                                    unsigned int flags,
-                                    int is_client);
-STATIC void tls_log_errors(tor_tls_t *tls, int severity, int domain,
-                           const char *doing);
-
-#ifdef TOR_UNIT_TESTS
-extern int tor_tls_object_ex_data_index;
-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;
-
-STATIC tor_x509_cert_t *tor_x509_cert_replace_expiration(
-                                               const tor_x509_cert_t *inp,
-                                               time_t new_expiration_time,
-                                               crypto_pk_t *signing_key);
-#endif /* defined(TOR_UNIT_TESTS) */
-
-#endif /* defined(TORTLS_PRIVATE) */
+typedef struct tor_tls_context_t tor_tls_context_t;
 
-tor_x509_cert_t *tor_x509_cert_dup(const tor_x509_cert_t *cert);
 const char *tor_tls_err_to_string(int err);
 void tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz);
-
 void tor_tls_free_all(void);
 
 #define TOR_TLS_CTX_IS_PUBLIC_SERVER (1u<<0)
 #define TOR_TLS_CTX_USE_ECDHE_P256   (1u<<1)
 #define TOR_TLS_CTX_USE_ECDHE_P224   (1u<<2)
 
+void tor_tls_init(void);
+void tls_log_errors(tor_tls_t *tls, int severity, int domain,
+                    const char *doing);
 int tor_tls_context_init(unsigned flags,
                          crypto_pk_t *client_identity,
                          crypto_pk_t *server_identity,
                          unsigned int key_lifetime);
-tor_tls_t *tor_tls_new(int sock, int is_server);
+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(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),
@@ -218,13 +97,9 @@ int tor_tls_is_server(tor_tls_t *tls);
 void tor_tls_free_(tor_tls_t *tls);
 #define tor_tls_free(tls) FREE_AND_NULL(tor_tls_t, tor_tls_free_, (tls))
 int tor_tls_peer_has_cert(tor_tls_t *tls);
-MOCK_DECL(tor_x509_cert_t *,tor_tls_get_peer_cert,(tor_tls_t *tls));
-MOCK_DECL(tor_x509_cert_t *,tor_tls_get_own_cert,(tor_tls_t *tls));
+MOCK_DECL(struct tor_x509_cert_t *,tor_tls_get_peer_cert,(tor_tls_t *tls));
+MOCK_DECL(struct tor_x509_cert_t *,tor_tls_get_own_cert,(tor_tls_t *tls));
 int tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity);
-int tor_tls_check_lifetime(int severity,
-                           tor_tls_t *tls, time_t now,
-                           int past_tolerance,
-                           int future_tolerance);
 MOCK_DECL(int, tor_tls_read, (tor_tls_t *tls, char *cp, size_t len));
 int tor_tls_write(tor_tls_t *tls, const char *cp, size_t n);
 int tor_tls_handshake(tor_tls_t *tls);
@@ -232,7 +107,6 @@ int tor_tls_finish_handshake(tor_tls_t *tls);
 void tor_tls_unblock_renegotiation(tor_tls_t *tls);
 void tor_tls_block_renegotiation(tor_tls_t *tls);
 void tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls);
-int tor_tls_shutdown(tor_tls_t *tls);
 int tor_tls_get_pending_bytes(tor_tls_t *tls);
 size_t tor_tls_get_forced_write_size(tor_tls_t *tls);
 
@@ -248,6 +122,8 @@ MOCK_DECL(double, tls_get_write_overhead_ratio, (void));
 int tor_tls_used_v1_handshake(tor_tls_t *tls);
 int tor_tls_get_num_server_handshakes(tor_tls_t *tls);
 int tor_tls_server_got_renegotiate(tor_tls_t *tls);
+MOCK_DECL(int,tor_tls_cert_matches_key,(const tor_tls_t *tls,
+                                        const struct tor_x509_cert_t *cert));
 MOCK_DECL(int,tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out));
 MOCK_DECL(int,tor_tls_export_key_material,(
                      tor_tls_t *tls, uint8_t *secrets_out,
@@ -255,37 +131,23 @@ 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
 
-void tor_x509_cert_free_(tor_x509_cert_t *cert);
-#define tor_x509_cert_free(c) \
-  FREE_AND_NULL(tor_x509_cert_t, tor_x509_cert_free_, (c))
-tor_x509_cert_t *tor_x509_cert_decode(const uint8_t *certificate,
-                            size_t certificate_len);
-void tor_x509_cert_get_der(const tor_x509_cert_t *cert,
-                      const uint8_t **encoded_out, size_t *size_out);
-const common_digests_t *tor_x509_cert_get_id_digests(
-                      const tor_x509_cert_t *cert);
-const common_digests_t *tor_x509_cert_get_cert_digests(
-                      const tor_x509_cert_t *cert);
 int tor_tls_get_my_certs(int server,
-                         const tor_x509_cert_t **link_cert_out,
-                         const tor_x509_cert_t **id_cert_out);
+                         const struct tor_x509_cert_t **link_cert_out,
+                         const struct tor_x509_cert_t **id_cert_out);
 crypto_pk_t *tor_tls_get_my_client_auth_key(void);
-crypto_pk_t *tor_tls_cert_get_key(tor_x509_cert_t *cert);
-MOCK_DECL(int,tor_tls_cert_matches_key,(const tor_tls_t *tls,
-                                        const tor_x509_cert_t *cert));
-int tor_tls_cert_is_valid(int severity,
-                          const tor_x509_cert_t *cert,
-                          const tor_x509_cert_t *signing_cert,
-                          time_t now,
-                          int check_rsa_1024);
+
 const char *tor_tls_get_ciphersuite_name(tor_tls_t *tls);
 
 int evaluate_ecgroup_for_tls(const char *ecgroup);

+ 76 - 0
src/lib/tls/tortls_internal.h

@@ -0,0 +1,76 @@
+/* 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
+
+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,
+           tor_x509_cert_impl_t **id_cert_out));
+
+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);
+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);
+#define tor_tls_impl_free(tls) \
+  FREE_AND_NULL(tor_tls_impl_t, tor_tls_impl_free_, (tls))
+
+void tor_tls_context_impl_free_(tor_tls_context_impl_t *);
+#define tor_tls_context_impl_free(ctx) \
+  FREE_AND_NULL(tor_tls_context_impl_t, tor_tls_context_impl_free_, (ctx))
+
+#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,
+                                         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,
+                            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
+#endif
+
+#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) */

+ 741 - 0
src/lib/tls/tortls_nss.c

@@ -0,0 +1,741 @@
+/* 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
+#define TOR_X509_PRIVATE
+
+#ifdef _WIN32
+  #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/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"
+#include "lib/tls/tortls_st.h"
+#include "lib/tls/tortls_internal.h"
+#include "lib/log/util_bug.h"
+
+#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,
+                               tor_x509_cert_impl_t **id_cert_out))
+{
+  tor_assert(tls);
+  tor_assert(cert_out);
+  tor_assert(id_cert_out);
+  (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));
+  ctx->refcnt = 1;
+
+  if (! is_client) {
+    if (tor_tls_context_init_certificates(ctx, identity,
+                                          key_lifetime, flags) < 0) {
+      goto err;
+    }
+  }
+
+  {
+    /* 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:
+  tor_tls_context_decref(ctx);
+  ctx = NULL;
+ done:
+  return ctx;
+}
+
+void
+tor_tls_context_impl_free_(tor_tls_context_impl_t *ctx)
+{
+  if (!ctx)
+    return;
+  PR_Close(ctx);
+}
+
+void
+tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz)
+{
+  (void)tls;
+  (void)buf;
+  (void)sz;
+  // AFAICT, NSS doesn't expose its internal state.
+  buf[0]=0;
+}
+
+void
+tor_tls_init(void)
+{
+  /* 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)
+{
+  /* This implementation is a little different for NSS than it is for OpenSSL
+     -- it logs the last error whether anything actually failed or not. So we
+     have to only call it when something has gone wrong and we have a real
+     error to report. */
+
+  (void)tls;
+  PRErrorCode code = PORT_GetError();
+
+  const char *addr = tls ? tls->address : NULL;
+  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;
+  }
+
+  const char *with = addr ? " with " : "";
+  addr = addr ? addr : "";
+  if (doing) {
+    log_fn(severity, domain, "TLS error %s while %s%s%s: %s",
+           name, doing, with, addr, string);
+  } else {
+    log_fn(severity, domain, "TLS error %s%s%s: %s", name, string,
+           with, addr);
+  }
+}
+
+tor_tls_t *
+tor_tls_new(tor_socket_t sock, int is_server)
+{
+  (void)sock;
+  tor_tls_context_t *ctx = tor_tls_context_get(is_server);
+
+  PRFileDesc *tcp = NULL;
+  if (SOCKET_OK(sock)) {
+    tcp = PR_ImportTCPSocket(sock);
+  } else {
+    tcp = PR_NewTCPSocket();
+  }
+
+  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) {
+    tls_log_errors(tls, LOG_WARN, LD_CRYPTO, "resetting handshake state");
+  }
+
+  return tls;
+}
+
+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;
+
+  /* We don't support renegotiation-based TLS with NSS. */
+}
+
+void
+tor_tls_impl_free_(tor_tls_impl_t *tls)
+{
+  // XXXX This will close the underlying fd, which our OpenSSL version does
+  // not do!
+  if (!tls)
+    return;
+
+  PR_Close(tls);
+}
+
+int
+tor_tls_peer_has_cert(tor_tls_t *tls)
+{
+  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))
+{
+  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);
+  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);
+  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 {
+    tls_log_errors(tls, LOG_NOTICE, LD_CRYPTO, "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 || 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 {
+    tls_log_errors(tls, LOG_NOTICE, LD_CRYPTO, "writing"); // XXXX
+    return TOR_TLS_ERROR_MISC; // ????
+  }
+}
+
+int
+tor_tls_handshake(tor_tls_t *tls)
+{
+  tor_assert(tls);
+  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);
+  // 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);
+  /* We don't support renegotiation with NSS. */
+}
+
+void
+tor_tls_block_renegotiation(tor_tls_t *tls)
+{
+  tor_assert(tls);
+  /* We don't support renegotiation with NSS. */
+}
+
+void
+tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls)
+{
+  tor_assert(tls);
+  /* We don't support renegotiation with NSS. */
+}
+
+int
+tor_tls_get_pending_bytes(tor_tls_t *tls)
+{
+  tor_assert(tls);
+  int n = SSL_DataPending(tls->ssl);
+  if (n < 0) {
+    tls_log_errors(tls, LOG_WARN, LD_CRYPTO, "looking up pending bytes");
+    return 0;
+  }
+  return (int)n;
+}
+
+size_t
+tor_tls_get_forced_write_size(tor_tls_t *tls)
+{
+  tor_assert(tls);
+  /* 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)
+{
+  tor_assert(tls);
+  tor_assert(n_read);
+  tor_assert(n_written);
+  /* 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
+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);
+
+  /* This is an acceptable way to say "we can't measure this." */
+  return -1;
+}
+
+MOCK_IMPL(double,
+tls_get_write_overhead_ratio, (void))
+{
+  /* 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);
+  /* 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);
+  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);
+  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);
+
+  /* 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,
+                             size_t context_len,
+                             const char *label))
+{
+  tor_assert(tls);
+  tor_assert(secrets_out);
+  tor_assert(context);
+  tor_assert(label);
+  tor_assert(strlen(label) <= UINT_MAX);
+  tor_assert(context_len <= UINT_MAX);
+
+  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);
+
+  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)
+{
+  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;
+}

+ 1708 - 0
src/lib/tls/tortls_openssl.c

@@ -0,0 +1,1708 @@
+/* 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
+#define TOR_X509_PRIVATE
+
+#ifdef _WIN32
+  /* We need to include these here, or else the dtls1.h header will include
+   * <winsock.h> and mess things up, in at least some openssl versions. */
+  #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"
+#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. */
+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"
+
+#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);
+  }
+}
+
+#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
+
+/** 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))
+{
+  tor_x509_cert_t *peer = tor_tls_get_peer_cert((tor_tls_t *)tls);
+  if (!peer)
+    return 0;
+
+  X509 *peercert = peer->cert;
+  EVP_PKEY *link_key = NULL, *cert_key = NULL;
+  int result;
+
+  link_key = X509_get_pubkey(peercert);
+  cert_key = X509_get_pubkey(cert->cert);
+
+  result = link_key && cert_key && EVP_PKEY_cmp(cert_key, link_key) == 1;
+
+  tor_x509_cert_free(peer);
+  if (link_key)
+    EVP_PKEY_free(link_key);
+  if (cert_key)
+    EVP_PKEY_free(cert_key);
+
+  return result;
+}
+
+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
+
+/** 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)
+{
+  EVP_PKEY *pkey = NULL;
+  tor_tls_context_t *result = NULL;
+
+  tor_tls_init();
+
+  result = tor_malloc_zero(sizeof(tor_tls_context_t));
+  result->refcnt = 1;
+
+  if (! is_client) {
+    if (tor_tls_context_init_certificates(result, identity, key_lifetime,
+                                          flags) < 0) {
+      goto error;
+    }
+  }
+
+#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 (result->my_link_cert &&
+        !SSL_CTX_use_certificate(result->ctx,
+                                 result->my_link_cert->cert)) {
+      goto error;
+    }
+    if (result->my_id_cert) {
+      X509_STORE *s = SSL_CTX_get_cert_store(result->ctx);
+      tor_assert(s);
+      X509_STORE_add_cert(s, result->my_id_cert->cert);
+    }
+  }
+  SSL_CTX_set_session_cache_mode(result->ctx, SSL_SESS_CACHE_OFF);
+  if (!is_client) {
+    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;
+    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);
+
+  return result;
+
+ error:
+  tls_log_errors(NULL, LOG_WARN, LD_NET, "creating TLS context");
+  if (pkey)
+    EVP_PKEY_free(pkey);
+  if (result)
+    tor_tls_context_decref(result);
+  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;
+  } 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, 0);
+  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) && ... */
+}
+
+void
+tor_tls_impl_free_(tor_tls_impl_t *ssl)
+{
+  if (!ssl)
+    return;
+
+#ifdef SSL_set_tlsext_host_name
+  SSL_set_tlsext_host_name(ssl, NULL);
+#endif
+  SSL_free(ssl);
+}
+
+/** 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;
+}
+
+/** 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;
+}
+
+/** 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 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;
+}

+ 74 - 0
src/lib/tls/tortls_st.h

@@ -0,0 +1,74 @@
+/* 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_TORTLS_ST_H
+#define TOR_TORTLS_ST_H
+
+#include "lib/net/socket.h"
+
+#define TOR_TLS_MAGIC 0x71571571
+
+typedef enum {
+    TOR_TLS_ST_HANDSHAKE, TOR_TLS_ST_OPEN, TOR_TLS_ST_GOTCLOSE,
+    TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED, TOR_TLS_ST_RENEGOTIATE,
+    TOR_TLS_ST_BUFFEREVENT
+} tor_tls_state_t;
+#define tor_tls_state_bitfield_t ENUM_BF(tor_tls_state_t)
+
+struct tor_tls_context_t {
+  int refcnt;
+  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;
+  crypto_pk_t *link_key;
+  crypto_pk_t *auth_key;
+};
+
+/** Holds a SSL object and its associated data.  Members are only
+ * accessed from within tortls.c.
+ */
+struct tor_tls_t {
+  uint32_t magic;
+  tor_tls_context_t *context; /** A link to the context object for this tls. */
+  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
+                                       * have completed successfully. */
+  unsigned int isServer:1; /**< True iff this is a server-side connection */
+  unsigned int wasV2Handshake:1; /**< True iff the original handshake for
+                                  * this connection used the updated version
+                                  * of the connection protocol (client sends
+                                  * different cipher list, server sends only
+                                  * 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;
+  size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last
+                       * time. */
+  /** Last values retrieved from BIO_number_read()/write(); see
+   * tor_tls_get_n_raw_bytes() for usage.
+   */
+  unsigned long last_write_count;
+  unsigned long last_read_count;
+  /** If set, a callback to invoke whenever the client tries to renegotiate
+   * the handshake. */
+  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

+ 143 - 0
src/lib/tls/x509.c

@@ -0,0 +1,143 @@
+/* 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.
+ **/
+
+#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"
+#include "lib/crypt_ops/crypto_util.h"
+
+/** 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;
+}
+
+/** Return a set of digests for the public key in <b>cert</b>, or NULL if this
+ * cert's public key is not one we know how to take the digest of. */
+const common_digests_t *
+tor_x509_cert_get_id_digests(const tor_x509_cert_t *cert)
+{
+  if (cert->pkey_digests_set)
+    return &cert->pkey_digests;
+  else
+    return NULL;
+}
+
+/** Return a set of digests for the public key in <b>cert</b>. */
+const common_digests_t *
+tor_x509_cert_get_cert_digests(const tor_x509_cert_t *cert)
+{
+  return &cert->cert_digests;
+}
+
+/** Free all storage held in <b>cert</b> */
+void
+tor_x509_cert_free_(tor_x509_cert_t *cert)
+{
+  if (! cert)
+    return;
+  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) {
+        log_warn(LD_CRYPTO, "unable to compute digests of certificate key");
+        crypto_pk_free(pk);
+        goto err;
+      }
+    }
+    cert->pkey_digests_set = 1;
+    crypto_pk_free(pk);
+  }
+
+  return cert;
+ err:
+  log_err(LD_CRYPTO, "Couldn't wrap encoded X509 certificate.");
+  tor_x509_cert_free(cert);
+  return NULL;
+}
+
+/** 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);
+  tor_assert(cert->cert);
+  return tor_x509_cert_new(tor_x509_cert_impl_dup_(cert->cert));
+}

+ 75 - 0
src/lib/tls/x509.h

@@ -0,0 +1,75 @@
+/* Copyright (c) 2003, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_X509_H
+#define TOR_X509_H
+
+/**
+ * \file x509.h
+ * \brief Headers for tortls.c
+ **/
+
+#include "lib/crypt_ops/crypto_rsa.h"
+#include "lib/testsupport/testsupport.h"
+
+/* Opaque structure to hold an X509 certificate. */
+typedef struct tor_x509_cert_t tor_x509_cert_t;
+
+#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
+
+#ifdef TOR_X509_PRIVATE
+/** 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;
+};
+#endif
+
+void tor_tls_pick_certificate_lifetime(time_t now,
+                                       unsigned cert_lifetime,
+                                       time_t *start_time_out,
+                                       time_t *end_time_out);
+
+#ifdef TOR_UNIT_TESTS
+tor_x509_cert_t *tor_x509_cert_replace_expiration(
+                                               const tor_x509_cert_t *inp,
+                                               time_t new_expiration_time,
+                                               crypto_pk_t *signing_key);
+#endif
+
+tor_x509_cert_t *tor_x509_cert_dup(const tor_x509_cert_t *cert);
+
+void tor_x509_cert_free_(tor_x509_cert_t *cert);
+#define tor_x509_cert_free(c) \
+  FREE_AND_NULL(tor_x509_cert_t, tor_x509_cert_free_, (c))
+tor_x509_cert_t *tor_x509_cert_decode(const uint8_t *certificate,
+                            size_t certificate_len);
+void tor_x509_cert_get_der(const tor_x509_cert_t *cert,
+                      const uint8_t **encoded_out, size_t *size_out);
+
+const common_digests_t *tor_x509_cert_get_id_digests(
+                      const tor_x509_cert_t *cert);
+const common_digests_t *tor_x509_cert_get_cert_digests(
+                      const tor_x509_cert_t *cert);
+
+crypto_pk_t *tor_tls_cert_get_key(tor_x509_cert_t *cert);
+
+int tor_tls_cert_is_valid(int severity,
+                          const tor_x509_cert_t *cert,
+                          const tor_x509_cert_t *signing_cert,
+                          time_t now,
+                          int check_rsa_1024);
+
+#endif

+ 53 - 0
src/lib/tls/x509_internal.h

@@ -0,0 +1,53 @@
+/* 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"
+
+/**
+ * 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,
+                                                    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));
+
+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);
+#define tor_x509_cert_impl_free(cert) \
+  FREE_AND_NULL(tor_x509_cert_impl_t, tor_x509_cert_impl_free_, (cert))
+tor_x509_cert_impl_t *tor_x509_cert_impl_dup_(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

+ 450 - 0
src/lib/tls/x509_nss.c

@@ -0,0 +1,450 @@
+/* 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/x509_internal.h"
+#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 (BUG(! validity)) {
+    /* LCOV_EXCL_START */
+    crypto_nss_log_errors(LOG_WARN, "creating a validity object");
+    goto err;
+    /* LCOV_EXCL_STOP */
+  }
+
+  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,
+                            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);
+
+  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
+ * 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(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_impl_t *
+tor_x509_cert_impl_dup_(tor_x509_cert_impl_t *cert)
+{
+  if (cert)
+    return CERT_DupCertificate(cert);
+  else
+    return NULL;
+}
+
+/**
+ * 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)
+{
+  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);
+  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
+tor_tls_cert_is_valid(int severity,
+                      const tor_x509_cert_t *cert,
+                      const tor_x509_cert_t *signing_cert,
+                      time_t now,
+                      int check_rsa_1024)
+{
+  int result = 0;
+
+  tor_assert(cert);
+  tor_assert(signing_cert);
+
+  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
+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);
+
+  PRTime notBefore=0, notAfter=0;
+  int64_t t;
+  SECStatus r = CERT_GetCertTimes(cert, &notBefore, &notAfter);
+  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
+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);
+
+  PRTime notBefore=0, notAfter=0;
+  SECStatus r = CERT_GetCertTimes(inp->cert, &notBefore, &notAfter);
+  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

+ 461 - 0
src/lib/tls/x509_openssl.c

@@ -0,0 +1,461 @@
+/* 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/x509_internal.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
+}
+
+/** 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)
+{
+  unsigned char *buf = NULL;
+  int length = i2d_X509(cert->cert, &buf);
+
+  if (length <= 0 || buf == NULL) {
+    return -1;
+  }
+  cert->encoded_len = (size_t) length;
+  cert->encoded = tor_malloc(length);
+  memcpy(cert->encoded, buf, length);
+  OPENSSL_free(buf);
+  return 0;
+}
+
+void
+tor_x509_cert_impl_free_(tor_x509_cert_impl_t *cert)
+{
+  if (cert)
+    X509_free(cert);
+}
+
+tor_x509_cert_impl_t *
+tor_x509_cert_impl_dup_(tor_x509_cert_impl_t *cert)
+{
+  if (cert)
+    return X509_dup(cert);
+  else
+    return NULL;
+}
+
+/** Set *<b>encoded_out</b> and *<b>size_out</b> to <b>cert</b>'s encoded DER
+ * representation and length, respectively. */
+void
+tor_x509_cert_get_der(const tor_x509_cert_t *cert,
+                 const uint8_t **encoded_out, size_t *size_out)
+{
+  tor_assert(cert);
+  tor_assert(encoded_out);
+  tor_assert(size_out);
+  *encoded_out = cert->encoded;
+  *size_out = cert->encoded_len;
+}
+
+/** 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,
+                                            TOR_X509_PAST_SLOP,
+                                            TOR_X509_FUTURE_SLOP) < 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;
+    } else {
+      log_fn(severity, LD_CRYPTO, "Invalid certificate: Key is not RSA1024.");
+    }
+
+    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) */

+ 1 - 0
src/rust/build.rs

@@ -172,6 +172,7 @@ pub fn main() {
 
             cfg.from_cflags("TOR_ZLIB_LIBS");
             cfg.from_cflags("TOR_LIB_MATH");
+            cfg.from_cflags("NSS_LIBS");
             cfg.from_cflags("TOR_OPENSSL_LIBS");
             cfg.from_cflags("TOR_LIBEVENT_LIBS");
             cfg.from_cflags("TOR_LIB_WS32");

+ 11 - 3
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"
@@ -33,6 +36,7 @@
 #include "core/or/or_circuit_st.h"
 
 #include "lib/crypt_ops/digestset.h"
+#include "lib/crypt_ops/crypto_init.h"
 
 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID)
 static uint64_t nanostart;
@@ -579,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)
 {
@@ -628,6 +633,7 @@ bench_ecdh_p224(void)
 {
   bench_ecdh_impl(NID_secp224r1, "P-224");
 }
+#endif
 
 typedef void (*bench_fn)(void);
 
@@ -651,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}
 };
 
@@ -680,9 +689,9 @@ main(int argc, const char **argv)
 
   tor_threads_init();
   tor_compress_init();
+  init_logging(1);
 
   if (argc == 4 && !strcmp(argv[1], "diff")) {
-    init_logging(1);
     const int N = 200;
     char *f1 = read_file_to_str(argv[2], RFTS_BIN, NULL);
     char *f2 = read_file_to_str(argv[3], RFTS_BIN, NULL);
@@ -718,13 +727,12 @@ main(int argc, const char **argv)
 
   reset_perftime();
 
-  if (crypto_seed_rng() < 0) {
+  if (crypto_global_init(0, NULL, NULL) < 0) {
     printf("Couldn't seed RNG; exiting.\n");
     return 1;
   }
 
   init_protocol_warning_severity_level();
-  crypto_init_siphash_key();
   options = options_new();
   init_logging(1);
   options->command = CMD_RUN_UNITTESTS;

+ 10 - 5
src/test/fuzz/fuzzing_common.c

@@ -7,8 +7,8 @@
 #include "app/config/config.h"
 #include "test/fuzz/fuzzing.h"
 #include "lib/compress/compress.h"
-#include "lib/crypt_ops/crypto.h"
 #include "lib/crypt_ops/crypto_ed25519.h"
+#include "lib/crypt_ops/crypto_init.h"
 
 static or_options_t *mock_options = NULL;
 static const or_options_t *
@@ -96,15 +96,20 @@ global_init(void)
 {
   tor_threads_init();
   tor_compress_init();
-  {
-    struct sipkey sipkey = { 1337, 7331 };
-    siphash_set_global_key(&sipkey);
-  }
 
   /* Initialise logging first */
   init_logging(1);
   configure_backtrace_handler(get_version());
 
+  if (crypto_global_init(0, NULL, NULL) < 0)
+    abort();
+
+  {
+    struct sipkey sipkey = { 1337, 7331 };
+    siphash_unset_global_key();
+    siphash_set_global_key(&sipkey);
+  }
+
   /* set up the options. */
   mock_options = tor_malloc_zero(sizeof(or_options_t));
   MOCK(get_options, mock_get_options);

+ 2 - 2
src/test/fuzz/include.am

@@ -5,12 +5,12 @@ FUZZING_CPPFLAGS = \
 FUZZING_CFLAGS = \
 	$(AM_CFLAGS) $(TEST_CFLAGS)
 FUZZING_LDFLAG = \
-	@TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@
+	@TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) @TOR_LDFLAGS_libevent@
 FUZZING_LIBS = \
 	$(TOR_INTERNAL_TESTING_LIBS) \
 	$(rust_ldadd) \
 	@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \
-	@TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
+	@TOR_LIBEVENT_LIBS@ $(TOR_LIBS_CRYPTLIB) \
 	@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ @CURVE25519_LIBS@ \
 	@TOR_SYSTEMD_LIBS@ \
 	@TOR_LZMA_LIBS@ \

+ 23 - 12
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 \
@@ -151,6 +150,7 @@ src_test_test_SOURCES += \
 	src/test/test_oom.c \
 	src/test/test_oos.c \
 	src/test/test_options.c \
+	src/test/test_pem.c \
 	src/test/test_periodic_event.c \
 	src/test/test_policy.c \
 	src/test/test_procmon.c \
@@ -178,11 +178,21 @@ src_test_test_SOURCES += \
 	src/test/test_util_format.c \
 	src/test/test_util_process.c \
 	src/test/test_voting_schedule.c \
+	src/test/test_x509.c \
 	src/test/test_helpers.c \
 	src/test/test_dns.c \
 	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_crypto_openssl.c \
+	src/test/test_tortls_openssl.c
+endif
+
 endif
 
 src_test_test_slow_SOURCES =
@@ -226,13 +236,13 @@ src_test_test_switch_id_LDADD = \
 	@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_USERENV@ \
 	@TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@
 
-src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
+src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) \
 	@TOR_LDFLAGS_libevent@
 src_test_test_LDADD = \
 	$(TOR_INTERNAL_TESTING_LIBS) \
 	$(rust_ldadd) \
 	@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
-	@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
+	$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
 	@CURVE25519_LIBS@ \
 	@TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@
 
@@ -249,23 +259,23 @@ src_test_test_memwipe_LDADD = $(src_test_test_LDADD)
 # successfully with the libraries built with them.
 src_test_test_memwipe_LDFLAGS = $(src_test_test_LDFLAGS) @CFLAGS_BUGTRAP@
 
-src_test_bench_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
+src_test_bench_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) \
 	@TOR_LDFLAGS_libevent@
 src_test_bench_LDADD = \
 	$(TOR_INTERNAL_LIBS) \
 	$(rust_ldadd) \
 	@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
-	@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
+	$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
 	@CURVE25519_LIBS@ \
 	@TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@
 
-src_test_test_workqueue_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
+src_test_test_workqueue_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) \
 	@TOR_LDFLAGS_libevent@
 src_test_test_workqueue_LDADD = \
 	$(TOR_INTERNAL_TESTING_LIBS) \
 	$(rust_ldadd) \
 	@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
-	@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
+	$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
 	@CURVE25519_LIBS@ \
 	@TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@
 
@@ -277,7 +287,7 @@ src_test_test_timers_LDADD = \
 	$(TOR_UTIL_TESTING_LIBS) \
 	$(rust_ldadd) \
 	@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
-	@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
+	$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
 	@CURVE25519_LIBS@ \
 	@TOR_LZMA_LIBS@
 src_test_test_timers_LDFLAGS = $(src_test_test_LDFLAGS)
@@ -291,6 +301,7 @@ noinst_HEADERS+= \
 	src/test/test_helpers.h \
 	src/test/test_dir_common.h \
 	src/test/test_connection.h \
+	src/test/test_tortls.h \
 	src/test/test_descriptors.inc \
 	src/test/example_extrainfo.inc \
 	src/test/failing_routerdescs.inc \
@@ -302,22 +313,22 @@ noinst_HEADERS+= \
 noinst_PROGRAMS+= src/test/test-ntor-cl
 noinst_PROGRAMS+= src/test/test-hs-ntor-cl
 src_test_test_ntor_cl_SOURCES  = src/test/test_ntor_cl.c
-src_test_test_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
+src_test_test_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB)
 src_test_test_ntor_cl_LDADD = \
 	$(TOR_INTERNAL_LIBS) \
 	$(rust_ldadd) \
 	@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \
-	@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
+	$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
 	@CURVE25519_LIBS@ @TOR_LZMA_LIBS@
 src_test_test_ntor_cl_AM_CPPFLAGS =	       \
 	$(AM_CPPFLAGS)
 
 src_test_test_hs_ntor_cl_SOURCES  = src/test/test_hs_ntor_cl.c
-src_test_test_hs_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
+src_test_test_hs_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB)
 src_test_test_hs_ntor_cl_LDADD = \
 	$(TOR_INTERNAL_LIBS) \
 	@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \
-	@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
+	$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
 src_test_test_hs_ntor_cl_AM_CPPFLAGS =	       \
 	$(AM_CPPFLAGS)
 

+ 19 - 18
src/test/log_test_helpers.h

@@ -33,7 +33,7 @@ void mock_dump_saved_logs(void);
 #define assert_log_predicate(predicate, failure_msg)   \
   do {                                                 \
     if (!(predicate)) {                                \
-      tt_fail_msg((failure_msg));                      \
+      TT_FAIL(failure_msg);                            \
       mock_dump_saved_logs();                          \
       TT_EXIT_TEST_FUNCTION;                           \
     }                                                  \
@@ -41,74 +41,75 @@ void mock_dump_saved_logs(void);
 
 #define expect_log_msg(str)                             \
   assert_log_predicate(mock_saved_log_has_message(str), \
-                "expected log to contain " # str);
+                       ("expected log to contain \"%s\"", str));
 
 #define expect_log_msg_containing(str) \
   assert_log_predicate(mock_saved_log_has_message_containing(str), \
-                "expected log to contain " # str);
+                       ("expected log to contain \"%s\"", str));
 
 #define expect_log_msg_not_containing(str) \
   assert_log_predicate(mock_saved_log_has_message_not_containing(str), \
-                "expected log to not contain " # str);
+                       ("expected log to not contain \"%s\"", str));
 
 #define expect_log_msg_containing_either(str1, str2)                    \
   assert_log_predicate(mock_saved_log_has_message_containing(str1) ||   \
                        mock_saved_log_has_message_containing(str2),     \
-                       "expected log to contain " # str1 " or " # str2);
+           ("expected log to contain \"%s\" or \"%s\"", str1, str2));
 
 #define expect_log_msg_containing_either3(str1, str2, str3)             \
   assert_log_predicate(mock_saved_log_has_message_containing(str1) ||   \
                        mock_saved_log_has_message_containing(str2) ||   \
                        mock_saved_log_has_message_containing(str3),     \
-                       "expected log to contain " # str1 " or " # str2  \
-                       " or " # str3);
+           ("expected log to contain \"%s\" or \"%s\" or \"%s\"",       \
+                        str1, str2, str3))
 
 #define expect_log_msg_containing_either4(str1, str2, str3, str4)       \
   assert_log_predicate(mock_saved_log_has_message_containing(str1) ||   \
                        mock_saved_log_has_message_containing(str2) ||   \
                        mock_saved_log_has_message_containing(str3) ||   \
                        mock_saved_log_has_message_containing(str4),     \
-                       "expected log to contain " # str1 " or " # str2  \
-                       " or " # str3 " or " # str4);
+       ("expected log to contain \"%s\" or \"%s\" or \"%s\" or \"%s\"", \
+                        str1, str2, str3, str4))
 
 #define expect_single_log_msg(str) \
   do {                                                                  \
                                                                         \
     assert_log_predicate(mock_saved_log_has_message_containing(str) &&  \
                          mock_saved_log_n_entries() == 1,               \
-                  "expected log to contain exactly 1 message " # str); \
+                         ("expected log to contain exactly 1 message \"%s\"", \
+                          str));                                        \
   } while (0);
 
 #define expect_single_log_msg_containing(str) \
   do {                                                                  \
     assert_log_predicate(mock_saved_log_has_message_containing(str)&&   \
                          mock_saved_log_n_entries() == 1 ,              \
-            "expected log to contain 1 message, containing " # str);     \
+                    ("expected log to contain 1 message, containing \"%s\"",\
+                     str));                                             \
   } while (0);
 
 #define expect_no_log_msg(str) \
   assert_log_predicate(!mock_saved_log_has_message(str), \
-                "expected log to not contain " # str);
+                       ("expected log to not contain \"%s\"",str));
 
 #define expect_no_log_msg_containing(str) \
   assert_log_predicate(!mock_saved_log_has_message_containing(str), \
-                "expected log to not contain " # str);
+                       ("expected log to not contain \"%s\"", str));
 
 #define expect_log_severity(severity) \
   assert_log_predicate(mock_saved_log_has_severity(severity), \
-                "expected log to contain severity " # severity);
+                       ("expected log to contain severity " # severity));
 
 #define expect_no_log_severity(severity) \
   assert_log_predicate(!mock_saved_log_has_severity(severity), \
-                "expected log to not contain severity " # severity);
+                       ("expected log to not contain severity " # severity));
 
 #define expect_log_entry() \
   assert_log_predicate(mock_saved_log_has_entry(), \
-                "expected log to contain entries");
+                       ("expected log to contain entries"));
 
 #define expect_no_log_entry() \
   assert_log_predicate(!mock_saved_log_has_entry(), \
-                "expected log to not contain entries");
+                       ("expected log to not contain entries"));
 
 #endif /* !defined(TOR_LOG_TEST_HELPERS_H) */
-

+ 5 - 0
src/test/test-timers.c

@@ -9,6 +9,7 @@
 
 #include "lib/evloop/compat_libevent.h"
 #include "lib/evloop/timers.h"
+#include "lib/crypt_ops/crypto_init.h"
 #include "lib/crypt_ops/crypto_rand.h"
 #include "lib/log/util_bug.h"
 #include "lib/time/compat_time.h"
@@ -62,6 +63,10 @@ main(int argc, char **argv)
   memset(&cfg, 0, sizeof(cfg));
   tor_libevent_initialize(&cfg);
   timers_initialize();
+  init_logging(1);
+
+  if (crypto_global_init(0, NULL, NULL) < 0)
+    return 1;
 
   int i;
   int ret;

+ 7 - 0
src/test/test.c

@@ -860,7 +860,10 @@ 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 },
   { "dir/md/", microdesc_tests },
@@ -912,6 +915,10 @@ struct testgroup_t testgroups[] = {
   { "status/" , status_tests },
   { "storagedir/", storagedir_tests },
   { "tortls/", tortls_tests },
+#ifndef ENABLE_NSS
+  { "tortls/openssl/", tortls_openssl_tests },
+#endif
+  { "tortls/x509/", x509_tests },
   { "util/", util_tests },
   { "util/format/", util_format_tests },
   { "util/logging/", logging_tests },

+ 3 - 0
src/test/test.h

@@ -234,6 +234,7 @@ extern struct testcase_t nodelist_tests[];
 extern struct testcase_t oom_tests[];
 extern struct testcase_t oos_tests[];
 extern struct testcase_t options_tests[];
+extern struct testcase_t pem_tests[];
 extern struct testcase_t periodic_event_tests[];
 extern struct testcase_t policy_tests[];
 extern struct testcase_t procmon_tests[];
@@ -256,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[];
@@ -263,6 +265,7 @@ extern struct testcase_t voting_schedule_tests[];
 extern struct testcase_t dns_tests[];
 extern struct testcase_t handle_tests[];
 extern struct testcase_t sr_tests[];
+extern struct testcase_t x509_tests[];
 
 extern struct testcase_t slow_crypto_tests[];
 extern struct testcase_t slow_util_tests[];

+ 1 - 1
src/test/test_controller.c

@@ -161,7 +161,7 @@ test_add_onion_helper_keyarg_v2(void *arg)
   /* Test loading a RSA1024 key. */
   tor_free(err_msg);
   pk1 = pk_generate(0);
-  tt_int_op(0, OP_EQ, crypto_pk_base64_encode(pk1, &encoded));
+  tt_int_op(0, OP_EQ, crypto_pk_base64_encode_private(pk1, &encoded));
   tor_asprintf(&arg_str, "RSA1024:%s", encoded);
   ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
                                 &pk, &hs_version, &err_msg);

+ 122 - 5
src/test/test_crypto.c

@@ -16,7 +16,9 @@
 #include "lib/crypt_ops/crypto_format.h"
 #include "lib/crypt_ops/crypto_hkdf.h"
 #include "lib/crypt_ops/crypto_rand.h"
+#include "lib/crypt_ops/crypto_init.h"
 #include "ed25519_vectors.inc"
+#include "test/log_test_helpers.h"
 
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
@@ -25,6 +27,13 @@
 #include <unistd.h>
 #endif
 
+#if defined(ENABLE_OPENSSL)
+#include "lib/crypt_ops/compat_openssl.h"
+DISABLE_GCC_WARNING(redundant-decls)
+#include <openssl/dh.h>
+ENABLE_GCC_WARNING(redundant-decls)
+#endif
+
 /** Run unit tests for Diffie-Hellman functionality. */
 static void
 test_crypto_dh(void *arg)
@@ -37,6 +46,11 @@ test_crypto_dh(void *arg)
   char s1[DH1024_KEY_LEN];
   char s2[DH1024_KEY_LEN];
   ssize_t s1len, s2len;
+#ifdef ENABLE_OPENSSL
+  crypto_dh_t *dh3 = NULL;
+  DH *dh4 = NULL;
+  BIGNUM *pubkey_tmp = NULL;
+#endif
 
   (void)arg;
   tt_int_op(crypto_dh_get_bytes(dh1),OP_EQ, DH1024_KEY_LEN);
@@ -91,6 +105,10 @@ test_crypto_dh(void *arg)
     s1len = crypto_dh_compute_secret(LOG_WARN, dh1, "\x02", 1, s1, 50);
     tt_int_op(50, OP_EQ, s1len);
 
+    /* 2 a second time is still okay, though weird. */
+    s1len = crypto_dh_compute_secret(LOG_WARN, dh1, "\x02", 1, s1, 50);
+    tt_int_op(50, OP_EQ, s1len);
+
     const char P[] =
       "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
       "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
@@ -157,16 +175,59 @@ test_crypto_dh(void *arg)
     tt_int_op(s1len, OP_EQ, -1);
   }
 
+#if defined(ENABLE_OPENSSL)
+  {
+    /* Make sure that our crypto library can handshake with openssl. */
+    dh3 = crypto_dh_new(DH_TYPE_TLS);
+    tt_assert(!crypto_dh_get_public(dh3, p1, DH1024_KEY_LEN));
+
+    dh4 = crypto_dh_new_openssl_tls();
+    tt_assert(DH_generate_key(dh4));
+    const BIGNUM *pk=NULL;
+#ifdef OPENSSL_1_1_API
+    const BIGNUM *sk=NULL;
+    DH_get0_key(dh4, &pk, &sk);
+#else
+    pk = dh4->pub_key;
+#endif
+    tt_assert(pk);
+    tt_int_op(BN_num_bytes(pk), OP_LE, DH1024_KEY_LEN);
+    tt_int_op(BN_num_bytes(pk), OP_GT, 0);
+    memset(p2, 0, sizeof(p2));
+    /* right-pad. */
+    BN_bn2bin(pk, (unsigned char *)(p2+DH1024_KEY_LEN-BN_num_bytes(pk)));
+
+    s1len = crypto_dh_handshake(LOG_WARN, dh3, p2, DH1024_KEY_LEN,
+                                (unsigned char *)s1, sizeof(s1));
+    pubkey_tmp = BN_bin2bn((unsigned char *)p1, DH1024_KEY_LEN, NULL);
+    s2len = DH_compute_key((unsigned char *)s2, pubkey_tmp, dh4);
+
+    tt_int_op(s1len, OP_EQ, s2len);
+    tt_int_op(s1len, OP_GT, 0);
+    tt_mem_op(s1, OP_EQ, s2, s1len);
+  }
+#endif
+
  done:
   crypto_dh_free(dh1);
   crypto_dh_free(dh2);
   crypto_dh_free(dh1_dup);
+#ifdef ENABLE_OPENSSL
+  crypto_dh_free(dh3);
+  if (dh4)
+    DH_free(dh4);
+  if (pubkey_tmp)
+    BN_free(pubkey_tmp);
+#endif
 }
 
 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);
@@ -186,6 +247,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:
   ;
@@ -1363,22 +1425,22 @@ test_crypto_pk_base64(void *arg)
   /* Test Base64 encoding a key. */
   pk1 = pk_generate(0);
   tt_assert(pk1);
-  tt_int_op(0, OP_EQ, crypto_pk_base64_encode(pk1, &encoded));
+  tt_int_op(0, OP_EQ, crypto_pk_base64_encode_private(pk1, &encoded));
   tt_assert(encoded);
 
   /* Test decoding a valid key. */
-  pk2 = crypto_pk_base64_decode(encoded, strlen(encoded));
+  pk2 = crypto_pk_base64_decode_private(encoded, strlen(encoded));
   tt_assert(pk2);
   tt_int_op(crypto_pk_cmp_keys(pk1, pk2), OP_EQ, 0);
   crypto_pk_free(pk2);
 
   /* Test decoding a invalid key (not Base64). */
   static const char *invalid_b64 = "The key is in another castle!";
-  pk2 = crypto_pk_base64_decode(invalid_b64, strlen(invalid_b64));
+  pk2 = crypto_pk_base64_decode_private(invalid_b64, strlen(invalid_b64));
   tt_ptr_op(pk2, OP_EQ, NULL);
 
   /* Test decoding a truncated Base64 blob. */
-  pk2 = crypto_pk_base64_decode(encoded, strlen(encoded)/2);
+  pk2 = crypto_pk_base64_decode_private(encoded, strlen(encoded)/2);
   tt_ptr_op(pk2, OP_EQ, NULL);
 
  done:
@@ -1427,6 +1489,58 @@ test_crypto_pk_pem_encrypted(void *arg)
  done:
   crypto_pk_free(pk);
 }
+
+static void
+test_crypto_pk_invalid_private_key(void *arg)
+{
+  (void)arg;
+  /* Here is a simple invalid private key: it was produced by making
+   * a regular private key, and then adding 2 to the modulus. */
+  const char pem[] =
+    "-----BEGIN RSA PRIVATE KEY-----\n"
+    "MIIEpQIBAAKCAQEAskRyZrs+YAukvBmZlgo6/rCxyKF2xyUk073ap+2CgRUnSfGG\n"
+    "mflHlzqVq7tpH50DafpS+fFAbaEaNV/ac20QG0rUZi38HTB4qURWOu6n0Bws6E4l\n"
+    "UX/AkvDlWnuYH0pHHi2c3DGNFjwoJpjKuUTk+cRffVR8X3Kjr62SUDUaBNW0Kecz\n"
+    "3SYLbmgmZI16dFZ+g9sNM3znXZbhvb33WwPqpZSSPs37cPgF7eS6mAw/gUMx6zfE\n"
+    "HRmUnOQSzUdS05rvc/hsiCLhiIZ8hgfkD07XnTT1Ds8DwE55k7BUWY2wvwWCNLsH\n"
+    "qtqAxTr615XdkMxVkYgImpqPybarpfNYhFqkOwIDAQABAoIBACPC3VxEdbfYvhxJ\n"
+    "2mih9sG++nswAN7kUaX0cRe86rAwaShJPmJHApiQ1ROVTfpciiHJaLnhLraPWe2Z\n"
+    "I/6Bw3hmI4O399p3Lc1u+wlpdNqnvE6B1rSptx0DHE9xecvVH70rE0uM2Su7t6Y+\n"
+    "gnR2IKUGQs2mlCilm7aTUEWs0WJkkl4CG1dyxItuOSdNBjOEzXimJyiB10jEBFsp\n"
+    "SZeCF2FZ7AJbck5CVC42+oTsiDbZrHTHOn7v26rFGdONeHD1wOI1v7JwHFpCB923\n"
+    "aEHBzsPbMeq7DWG1rjzCYpcXHhTDBDBWSia4SEhyr2Nl7m7qxWWWwR+x4dqAj3rD\n"
+    "HeTmos0CgYEA6uf1CLpjPpOs5IaW1DQI8dJA/xFEAC/6GVgq4nFOGHZrm8G3L5o+\n"
+    "qvtQNMpDs2naWuZpqROFqv24o01DykHygR72GlPIY6uvmmf5tvJLoGnbFUay33L4\n"
+    "7b9dkNhuEIBNPzVDie0pgS77WgaPbYkVv5fnDwgPuVnkqfakEt7Pz2MCgYEAwkZ5\n"
+    "R1wLuTQEA2Poo6Gf4L8Bg6yNYI46LHDqDIs818iYLjtcnEEvbPfaoKNpOn7s7s4O\n"
+    "Pc+4HnT1aIQs0IKVLRTp+5a/9wfOkPZnobWOUHZk9UzBL3Hc1uy/qhp93iE3tSzx\n"
+    "v0O1pvR+hr3guTCZx8wZnDvaMgG3hlyPnVlHdrMCgYEAzQQxGbMC1ySv6quEjCP2\n"
+    "AogMbhE1lixJTUFj/EoDbNo9xKznIkauly/Lqqc1OysRhfA/G2+MY9YZBX1zwtyX\n"
+    "uBW7mPKynDrFgi9pBECnvJNmwET57Ic9ttIj6Tzbos83nAjyrzgr1zGX8dRz7ZeN\n"
+    "QbBj2vygLJbGOYinXkjUeh0CgYEAhN5aF9n2EqZmkEMGWtMxWy6HRJ0A3Cap1rcq\n"
+    "+4VHCXWhzwy+XAeg/e/N0MuyLlWcif7XcqLcE8h+BwtO8xQ8HmcNWApUJAls12wO\n"
+    "mGRpftJaXgIupdpD5aJpu1b++qrRRNIGTH9sf1D8L/8w8LcylZkbcuTkaAsQj45C\n"
+    "kqT64U0CgYEAq47IKS6xc3CDc17BqExR6t+1yRe+4ml+z1zcVbfUKony4pGvl1yo\n"
+    "rk0IYDN5Vd8h5xtXrkPdX9h+ywmohnelDKsayEuE+opyqEpSU4/96Bb22RZUoucb\n"
+    "LWkV5gZx5hFnDFtEd4vadMIiY4jVv/3JqiZDKwMVBJKlHRXJEEmIEBk=\n"
+    "-----END RSA PRIVATE KEY-----\n";
+
+  crypto_pk_t *pk = NULL;
+
+  pk = crypto_pk_new();
+  setup_capture_of_logs(LOG_WARN);
+  tt_int_op(-1, OP_EQ,
+            crypto_pk_read_private_key_from_string(pk, pem, strlen(pem)));
+#ifdef ENABLE_NSS
+  expect_single_log_msg_containing("received bad data");
+#else
+  expect_single_log_msg_containing("while checking RSA key");
+#endif
+ done:
+  teardown_capture_of_logs();
+  crypto_pk_free(pk);
+}
+
 #ifdef HAVE_TRUNCATE
 #define do_truncate truncate
 #else
@@ -1462,7 +1576,8 @@ test_crypto_digests(void *arg)
   (void)arg;
   k = crypto_pk_new();
   tt_assert(k);
-  r = crypto_pk_read_private_key_from_string(k, AUTHORITY_SIGNKEY_3, -1);
+  r = crypto_pk_read_private_key_from_string(k, AUTHORITY_SIGNKEY_3,
+                                             strlen(AUTHORITY_SIGNKEY_3));
   tt_assert(!r);
 
   r = crypto_pk_get_digest(k, digest);
@@ -3047,6 +3162,8 @@ struct testcase_t crypto_tests[] = {
   { "pk_fingerprints", test_crypto_pk_fingerprints, TT_FORK, NULL, NULL },
   { "pk_base64", test_crypto_pk_base64, TT_FORK, NULL, NULL },
   { "pk_pem_encrypted", test_crypto_pk_pem_encrypted, TT_FORK, NULL, NULL },
+  { "pk_invalid_private_key", test_crypto_pk_invalid_private_key, 0,
+    NULL, NULL },
   CRYPTO_LEGACY(digests),
   { "digest_names", test_crypto_digest_names, 0, NULL, NULL },
   { "sha3", test_crypto_sha3, TT_FORK, NULL, NULL},

+ 3 - 1
src/test/test_crypto_ope.c

@@ -7,9 +7,11 @@
 
 #define CRYPTO_OPE_PRIVATE
 
+#include "lib/cc/compat_compiler.h"
 #include "lib/crypt_ops/crypto_ope.h"
-#include "lib/crypt_ops/crypto.h"
+#include "lib/crypt_ops/crypto_cipher.h"
 #include "lib/encoding/binascii.h"
+#include "lib/malloc/malloc.h"
 #include "test/test.h"
 #include "tinytest.h"
 

+ 2 - 0
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

+ 1 - 1
src/test/test_dir_handle_get.c

@@ -2102,6 +2102,7 @@ test_dir_handle_get_status_vote_d(void* data)
 
     clear_dir_servers();
     dirvote_free_all();
+    routerlist_free_all();
 }
 
 static void
@@ -2638,4 +2639,3 @@ struct testcase_t dir_handle_get_tests[] = {
   DIR_HANDLE_CMD(parse_accept_encoding, 0),
   END_OF_TESTCASES
 };
-

+ 1 - 1
src/test/test_hs_client.c

@@ -21,7 +21,7 @@
 #include "test/hs_test_helpers.h"
 
 #include "app/config/config.h"
-#include "lib/crypt_ops/crypto.h"
+#include "lib/crypt_ops/crypto_cipher.h"
 #include "lib/crypt_ops/crypto_dh.h"
 #include "core/or/channeltls.h"
 #include "feature/dircache/directory.h"

+ 6 - 1
src/test/test_hs_ntor_cl.c

@@ -14,10 +14,11 @@
 
 #define ONION_NTOR_PRIVATE
 #include "core/or/or.h"
-#include "lib/crypt_ops/crypto.h"
+#include "lib/crypt_ops/crypto_cipher.h"
 #include "lib/crypt_ops/crypto_curve25519.h"
 #include "lib/crypt_ops/crypto_ed25519.h"
 #include "lib/crypt_ops/crypto_format.h"
+#include "lib/crypt_ops/crypto_init.h"
 #include "core/crypto/hs_ntor.h"
 #include "core/crypto/onion_ntor.h"
 
@@ -240,7 +241,11 @@ main(int argc, char **argv)
     return 1;
   }
 
+  init_logging(1);
   curve25519_init();
+  if (crypto_global_init(0, NULL, NULL) < 0)
+    return 1;
+
   if (!strcmp(argv[1], "client1")) {
     return client1(argc, argv);
   } else if (!strcmp(argv[1], "server1")) {

+ 1 - 1
src/test/test_introduce.c

@@ -2,7 +2,7 @@
 /* See LICENSE for licensing information */
 
 #include "orconfig.h"
-#include "lib/crypt_ops/crypto.h"
+#include "lib/crypt_ops/crypto_cipher.h"
 #include "core/or/or.h"
 #include "test/test.h"
 

+ 27 - 7
src/test/test_link_handshake.c

@@ -24,7 +24,9 @@
 #include "core/or/or_handshake_state_st.h"
 #include "core/or/var_cell_st.h"
 
+#define TOR_X509_PRIVATE
 #include "lib/tls/tortls.h"
+#include "lib/tls/x509.h"
 
 #include "test/test.h"
 #include "test/log_test_helpers.h"
@@ -793,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 */
   {
@@ -809,9 +826,12 @@ 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);
+    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);
   })

+ 6 - 2
src/test/test_ntor_cl.c

@@ -7,8 +7,9 @@
 
 #define ONION_NTOR_PRIVATE
 #include "core/or/or.h"
-#include "lib/crypt_ops/crypto.h"
+#include "lib/crypt_ops/crypto_cipher.h"
 #include "lib/crypt_ops/crypto_curve25519.h"
+#include "lib/crypt_ops/crypto_init.h"
 #include "core/crypto/onion_ntor.h"
 
 #define N_ARGS(n) STMT_BEGIN {                                  \
@@ -153,7 +154,11 @@ main(int argc, char **argv)
     return 1;
   }
 
+  init_logging(1);
   curve25519_init();
+  if (crypto_global_init(0, NULL, NULL) < 0)
+    return 1;
+
   if (!strcmp(argv[1], "client1")) {
     return client1(argc, argv);
   } else if (!strcmp(argv[1], "server1")) {
@@ -165,4 +170,3 @@ main(int argc, char **argv)
     return 1;
   }
 }
-

+ 122 - 0
src/test/test_pem.c

@@ -0,0 +1,122 @@
+/* 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 */
+
+#include "orconfig.h"
+
+#include "lib/encoding/pem.h"
+#include "lib/cc/compat_compiler.h"
+#include "lib/malloc/malloc.h"
+
+#include "test/test.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static const char example_pre[] =
+    "Lest you get the wrong impression, we wombats "
+    "are not in the habit of tunneling madly about, without any supplies "
+    "or even a map."; /* -- Ursula Vernon, _Digger_ */
+static const char expected[] =
+    "-----BEGIN WOMBAT QUOTE-----\n"
+    "TGVzdCB5b3UgZ2V0IHRoZSB3cm9uZyBpbXByZXNzaW9uLCB3ZSB3b21iYXRzIGFy\n"
+    "ZSBub3QgaW4gdGhlIGhhYml0IG9mIHR1bm5lbGluZyBtYWRseSBhYm91dCwgd2l0\n"
+    "aG91dCBhbnkgc3VwcGxpZXMgb3IgZXZlbiBhIG1hcC4=\n"
+    "-----END WOMBAT QUOTE-----\n";
+
+static void
+test_crypto_pem_encode(void *arg)
+{
+  (void)arg;
+
+  char buf[4096];
+
+  int n = (int) pem_encoded_size(strlen(example_pre), "WOMBAT QUOTE");
+
+  int n2 = pem_encode(buf, sizeof(buf),
+                      (const unsigned char *)example_pre, strlen(example_pre),
+                      "WOMBAT QUOTE");
+  tt_int_op(strlen(buf)+1, OP_EQ, n);
+  tt_int_op(n2, OP_EQ, 0);
+  tt_str_op(buf, OP_EQ, expected);
+
+  /* Now make sure it succeeds if the buffer is exactly the length we want. */
+  memset(buf, 0, sizeof(buf));
+  n2 = pem_encode(buf, n, (const unsigned char *)example_pre,
+                      strlen(example_pre), "WOMBAT QUOTE");
+  tt_int_op(n2, OP_EQ, 0);
+  tt_str_op(buf, OP_EQ, expected);
+
+  /* Make sure it fails if the buffer is too short. */
+  memset(buf, 0, sizeof(buf));
+  n2 = pem_encode(buf, n - 1, (const unsigned char *)example_pre,
+                  strlen(example_pre), "WOMBAT QUOTE");
+  tt_int_op(n2, OP_EQ, -1);
+
+ done:
+  ;
+}
+
+static void
+test_crypto_pem_decode(void *arg)
+{
+  (void)arg;
+
+  unsigned char buf[4096];
+
+  /* Try a straightforward decoding. */
+  int n = pem_decode(buf, sizeof(buf),
+                     expected, strlen(expected),
+                     "WOMBAT QUOTE");
+  tt_int_op(n, OP_EQ, strlen(example_pre));
+  tt_mem_op(buf, OP_EQ, example_pre, n);
+
+  /* Succeed if the buffer is exactly the right size. */
+  memset(buf, 0xff, sizeof(buf));
+  n = pem_decode(buf, strlen(example_pre),
+                 expected, strlen(expected),
+                 "WOMBAT QUOTE");
+  tt_int_op(n, OP_EQ, strlen(example_pre));
+  tt_mem_op(buf, OP_EQ, example_pre, n);
+  tt_int_op(buf[n], OP_EQ, 0xff);
+
+  /* Verify that it fails if the buffer is too small. */
+  memset(buf, 0xff, sizeof(buf));
+  n = pem_decode(buf, strlen(example_pre) - 1,
+                 expected, strlen(expected),
+                 "WOMBAT QUOTE");
+  tt_int_op(n, OP_EQ, -1);
+
+  /* Verify that it fails with an incorrect tag. */
+  memset(buf, 0xff, sizeof(buf));
+  n = pem_decode(buf, sizeof(buf),
+                 expected, strlen(expected),
+                 "QUOKKA VOTE");
+  tt_int_op(n, OP_EQ, -1);
+
+  /* Try truncated buffers of different sizes. */
+  size_t i;
+  for (i = 0; i <= strlen(expected); ++i) {
+    char *truncated = tor_memdup(expected, i);
+    n = pem_decode(buf, sizeof(buf),
+                   truncated, i,
+                   "WOMBAT QUOTE");
+    tor_free(truncated);
+    if (i < strlen(expected) - 1) {
+      tt_int_op(n, OP_EQ, -1);
+    } else {
+      tt_int_op(n, OP_EQ, strlen(example_pre));
+    }
+  }
+
+ done:
+  ;
+}
+
+struct testcase_t pem_tests[] = {
+  { "encode", test_crypto_pem_encode, 0, NULL, NULL },
+  { "decode", test_crypto_pem_decode, 0, NULL, NULL },
+  END_OF_TESTCASES
+};

+ 1 - 1
src/test/test_relaycell.c

@@ -9,7 +9,7 @@
 #include "core/mainloop/main.h"
 #include "app/config/config.h"
 #include "core/mainloop/connection.h"
-#include "lib/crypt_ops/crypto.h"
+#include "lib/crypt_ops/crypto_cipher.h"
 #include "core/or/circuitbuild.h"
 #include "core/or/circuitlist.h"
 #include "core/or/connection_edge.h"

+ 2 - 0
src/test/test_router.c

@@ -56,6 +56,8 @@ NS(router_get_my_routerinfo)(void)
                               &mock_routerinfo->onion_pkey_len);
     mock_routerinfo->bandwidthrate = 9001;
     mock_routerinfo->bandwidthburst = 9002;
+    crypto_pk_free(ident_key);
+    crypto_pk_free(tap_key);
   }
 
   return mock_routerinfo;

+ 1 - 1
src/test/test_routerkeys.c

@@ -9,7 +9,7 @@
 #include "app/config/config.h"
 #include "feature/relay/router.h"
 #include "feature/relay/routerkeys.h"
-#include "lib/crypt_ops/crypto.h"
+#include "lib/crypt_ops/crypto_cipher.h"
 #include "lib/crypt_ops/crypto_format.h"
 #include "feature/nodelist/torcert.h"
 #include "test/test.h"

+ 1 - 1
src/test/test_shared_random.c

@@ -1318,7 +1318,7 @@ test_keep_commit(void *arg)
     expect_log_msg_containing("doesn't match the commit value.");
     expect_log_msg_containing("has an invalid reveal value.");
     assert_log_predicate(mock_saved_log_n_entries() == 2,
-                         "expected 2 log entries");
+                         ("expected 2 log entries"));
     teardown_capture_of_logs();
     memcpy(commit->hashed_reveal, place_holder.hashed_reveal,
            sizeof(commit->hashed_reveal));

File diff suppressed because it is too large
+ 260 - 849
src/test/test_tortls.c


+ 13 - 0
src/test/test_tortls.h

@@ -0,0 +1,13 @@
+/* Copyright (c) 2010-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TEST_TORTLS_H
+#define TEST_TORTLS_H
+
+tor_x509_cert_impl_t *read_cert_from(const char *str);
+
+extern const char *notCompletelyValidCertString;
+extern const char *validCertString;
+extern const char *caCertString;
+
+#endif

Some files were not shown because too many files changed in this diff