Browse Source

Merge branch 'nss_dh_squashed' into nss_dh_squashed_merged

Nick Mathewson 5 years ago
parent
commit
fdaa483098
60 changed files with 2208 additions and 1107 deletions
  1. 8 0
      Makefile.am
  2. 3 0
      changes/feature26815
  3. 11 0
      changes/feature26816
  4. 2 0
      config.rust.in
  5. 22 0
      configure.ac
  6. 4 4
      src/app/include.am
  7. 1 1
      src/core/crypto/onion_ntor.c
  8. 1 1
      src/core/crypto/relay_crypto.c
  9. 1 0
      src/core/mainloop/main.c
  10. 2 1
      src/core/or/or.h
  11. 8 0
      src/ext/tinytest.c
  12. 1 1
      src/feature/nodelist/parsecommon.c
  13. 1 0
      src/feature/relay/router.c
  14. 1 1
      src/feature/rend/rendmid.c
  15. 106 0
      src/lib/crypt_ops/aes_nss.c
  16. 2 2
      src/lib/crypt_ops/aes_openssl.c
  17. 0 509
      src/lib/crypt_ops/crypto.c
  18. 190 0
      src/lib/crypt_ops/crypto_cipher.c
  19. 4 21
      src/lib/crypt_ops/crypto_cipher.h
  20. 43 441
      src/lib/crypt_ops/crypto_dh.c
  21. 20 3
      src/lib/crypt_ops/crypto_dh.h
  22. 207 0
      src/lib/crypt_ops/crypto_dh_nss.c
  23. 471 0
      src/lib/crypt_ops/crypto_dh_openssl.c
  24. 249 9
      src/lib/crypt_ops/crypto_digest.c
  25. 3 0
      src/lib/crypt_ops/crypto_digest.h
  26. 141 0
      src/lib/crypt_ops/crypto_init.c
  27. 29 0
      src/lib/crypt_ops/crypto_init.h
  28. 102 0
      src/lib/crypt_ops/crypto_nss_mgt.c
  29. 33 0
      src/lib/crypt_ops/crypto_nss_mgt.h
  30. 226 2
      src/lib/crypt_ops/crypto_openssl_mgt.c
  31. 11 4
      src/lib/crypt_ops/crypto_openssl_mgt.h
  32. 1 1
      src/lib/crypt_ops/crypto_pwbox.c
  33. 102 8
      src/lib/crypt_ops/crypto_rand.c
  34. 34 14
      src/lib/crypt_ops/crypto_rsa.c
  35. 5 0
      src/lib/crypt_ops/crypto_rsa.h
  36. 1 1
      src/lib/crypt_ops/crypto_s2k.c
  37. 0 23
      src/lib/crypt_ops/crypto_util.c
  38. 0 9
      src/lib/crypt_ops/crypto_util.h
  39. 25 4
      src/lib/crypt_ops/include.am
  40. 1 1
      src/lib/include.libdonna.am
  41. 4 1
      src/lib/tls/include.am
  42. 4 4
      src/lib/tls/tortls.c
  43. 1 0
      src/rust/build.rs
  44. 3 3
      src/test/bench.c
  45. 10 5
      src/test/fuzz/fuzzing_common.c
  46. 2 2
      src/test/fuzz/include.am
  47. 11 11
      src/test/include.am
  48. 5 0
      src/test/test-timers.c
  49. 57 0
      src/test/test_crypto.c
  50. 1 1
      src/test/test_hs_client.c
  51. 6 1
      src/test/test_hs_ntor_cl.c
  52. 1 1
      src/test/test_introduce.c
  53. 6 2
      src/test/test_ntor_cl.c
  54. 1 1
      src/test/test_relaycell.c
  55. 1 1
      src/test/test_routerkeys.c
  56. 1 1
      src/test/test_util_slow.c
  57. 1 0
      src/test/test_workqueue.c
  58. 8 1
      src/test/testing_common.c
  59. 5 5
      src/tools/include.am
  60. 8 6
      src/tools/tor-gencert.c

+ 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

+ 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@

+ 22 - 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,15 @@ 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, true)
+
+if test "x$enable_nss" = "xyes"; then
+  AC_DEFINE(ENABLE_NSS, 1,
+  	    [Defined if we're building with NSS in addition to OpenSSL.])
+fi
+AC_DEFINE(ENABLE_OPENSSL, 1,
+          [Defined if we're building with OpenSSL or LibreSSL])
 
 if test "$enable_static_tor" = "yes"; then
   enable_static_libevent="yes";
@@ -847,6 +858,17 @@ 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?
 

+ 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"

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

+ 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"

+ 8 - 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,12 @@ testcase_run_bare_(const struct testcase_t *testcase)
 
 #ifndef NO_FORKING
 
+#ifdef TINYTEST_POSTFORK
+void tinytest_postfork(void);
+#else
+static void tinytest_postfork(void) { }
+#endif
+
 static enum outcome
 testcase_run_forked_(const struct testgroup_t *group,
 		     const struct testcase_t *testcase)
@@ -187,6 +194,7 @@ testcase_run_forked_(const struct testgroup_t *group,
 		int test_r, write_r;
 		char b[1];
 		close(outcome_pipe[0]);
+		tinytest_postfork();
 		test_r = testcase_run_bare_(testcase);
 		assert(0<=(int)test_r && (int)test_r<=2);
 		b[0] = "NYS"[test_r];

+ 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/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

+ 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"

+ 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"

+ 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_OPENSSL
+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 = 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;
+}

+ 249 - 9
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 */
 
@@ -42,8 +121,13 @@ 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

+ 3 - 0
src/lib/crypt_ops/crypto_digest.h

@@ -51,6 +51,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;
 

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

@@ -0,0 +1,141 @@
+/* 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 OpenSSL's crypto been initialized? */
+static int crypto_early_initialized_ = 0;
+
+/** Boolean: has OpenSSL's crypto been initialized? */
+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();
+#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;
+#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_postfork(void)
+{
+#ifdef ENABLE_NSS
+  crypto_nss_postfork();
+#endif
+}

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

@@ -0,0 +1,29 @@
+/* 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_postfork(void);
+
+#endif /* !defined(TOR_CRYPTO_H) */

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

@@ -0,0 +1,102 @@
+/* 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 <nss.h>
+#include <pk11func.h>
+#include <ssl.h>
+
+#include <prerror.h>
+#include <prtypes.h>
+#include <prinit.h>
+
+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(void)
+{
+  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();
+  }
+}
+
+void
+crypto_nss_log_errors(int severity, const char *doing)
+{
+  PRErrorCode code = PR_GetError();
+  /* XXXX how do I convert errors to strings? */
+  if (doing) {
+    tor_log(severity, LD_CRYPTO, "NSS error %u while %s", code, doing);
+  } else {
+    tor_log(severity, LD_CRYPTO, "NSS error %u", code);
+  }
+}
+
+int
+crypto_nss_late_init(void)
+{
+  /* Possibly, SSL_OptionSetDefault? */
+
+  return 0;
+}
+
+void
+crypto_nss_global_cleanup(void)
+{
+  NSS_Shutdown();
+}
+
+void
+crypto_nss_postfork(void)
+{
+  crypto_nss_global_cleanup();
+  crypto_nss_early_init();
+}

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

@@ -0,0 +1,33 @@
+/* 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(void);
+int crypto_nss_late_init(void);
+
+void crypto_nss_global_cleanup(void);
+
+void crypto_nss_postfork(void);
+#endif
+
+#endif /* !defined(TOR_CRYPTO_NSS_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"

+ 102 - 8
src/lib/crypt_ops/crypto_rand.c

@@ -35,9 +35,22 @@
 #include "lib/testsupport/testsupport.h"
 #include "lib/fs/files.h"
 
+#ifdef ENABLE_NSS
+#include "lib/crypt_ops/crypto_nss_mgt.h"
+#include "lib/crypt_ops/crypto_digest.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
@@ -324,14 +337,21 @@ crypto_strongest_rand(uint8_t *out, size_t out_len)
 {
 #define DLEN SHA512_DIGEST_LENGTH
   /* 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.");
@@ -354,12 +374,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 +404,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 +474,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
 }
 
 /**

+ 34 - 14
src/lib/crypt_ops/crypto_rsa.c

@@ -9,7 +9,7 @@
  * \brief Block of functions related with RSA utilities and operations.
  **/
 
-#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_digest.h"
 #include "lib/crypt_ops/crypto_format.h"
@@ -207,7 +207,7 @@ crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits))
   }
 
   if (!env->key) {
-    crypto_log_errors(LOG_WARN, "generating RSA key");
+    crypto_openssl_log_errors(LOG_WARN, "generating RSA key");
     return -1;
   }
 
@@ -252,7 +252,7 @@ crypto_pk_read_private_key_from_string(crypto_pk_t *env,
   BIO_free(b);
 
   if (!env->key) {
-    crypto_log_errors(LOG_WARN, "Error parsing private key");
+    crypto_openssl_log_errors(LOG_WARN, "Error parsing private key");
     return -1;
   }
   return 0;
@@ -316,7 +316,7 @@ crypto_pk_write_key_to_string_impl(crypto_pk_t *env, char **dest,
     r = PEM_write_bio_RSAPrivateKey(b, env->key, NULL,NULL,0,NULL,NULL);
 
   if (!r) {
-    crypto_log_errors(LOG_WARN, "writing RSA key to string");
+    crypto_openssl_log_errors(LOG_WARN, "writing RSA key to string");
     BIO_free(b);
     return -1;
   }
@@ -382,7 +382,7 @@ crypto_pk_read_public_key_from_string(crypto_pk_t *env, const char *src,
   env->key = PEM_read_bio_RSAPublicKey(b, NULL, pem_no_password_cb, NULL);
   BIO_free(b);
   if (!env->key) {
-    crypto_log_errors(LOG_WARN, "reading public key from string");
+    crypto_openssl_log_errors(LOG_WARN, "reading public key from string");
     return -1;
   }
 
@@ -408,7 +408,7 @@ crypto_pk_write_private_key_to_filename(crypto_pk_t *env,
     return -1;
   if (PEM_write_bio_RSAPrivateKey(bio, env->key, NULL,NULL,0,NULL,NULL)
       == 0) {
-    crypto_log_errors(LOG_WARN, "writing private key");
+    crypto_openssl_log_errors(LOG_WARN, "writing private key");
     BIO_free(bio);
     return -1;
   }
@@ -434,7 +434,7 @@ crypto_pk_check_key(crypto_pk_t *env)
 
   r = RSA_check_key(env->key);
   if (r <= 0)
-    crypto_log_errors(LOG_WARN,"checking RSA key");
+    crypto_openssl_log_errors(LOG_WARN,"checking RSA key");
   return r;
 }
 
@@ -601,7 +601,7 @@ crypto_pk_copy_full(crypto_pk_t *env)
      */
     log_err(LD_CRYPTO, "Unable to duplicate a %s key: openssl failed.",
             privatekey?"private":"public");
-    crypto_log_errors(LOG_ERR,
+    crypto_openssl_log_errors(LOG_ERR,
                       privatekey ? "Duplicating a private key" :
                       "Duplicating a public key");
     tor_fragile_assert();
@@ -777,7 +777,7 @@ crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen,
                          (unsigned char*)from, (unsigned char*)to,
                          env->key, crypto_get_rsa_padding(padding));
   if (r<0) {
-    crypto_log_errors(LOG_WARN, "performing RSA encryption");
+    crypto_openssl_log_errors(LOG_WARN, "performing RSA encryption");
     return -1;
   }
   return r;
@@ -813,7 +813,7 @@ crypto_pk_private_decrypt(crypto_pk_t *env, char *to,
                           env->key, crypto_get_rsa_padding(padding));
 
   if (r<0) {
-    crypto_log_errors(warnOnFailure?LOG_WARN:LOG_DEBUG,
+    crypto_openssl_log_errors(warnOnFailure?LOG_WARN:LOG_DEBUG,
                       "performing RSA decryption");
     return -1;
   }
@@ -844,7 +844,7 @@ crypto_pk_public_checksig,(const crypto_pk_t *env, char *to,
                          env->key, RSA_PKCS1_PADDING);
 
   if (r<0) {
-    crypto_log_errors(LOG_INFO, "checking RSA signature");
+    crypto_openssl_log_errors(LOG_INFO, "checking RSA signature");
     return -1;
   }
   return r;
@@ -876,7 +876,7 @@ crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen,
                           (unsigned char*)from, (unsigned char*)to,
                           (RSA*)env->key, RSA_PKCS1_PADDING);
   if (r<0) {
-    crypto_log_errors(LOG_WARN, "generating RSA signature");
+    crypto_openssl_log_errors(LOG_WARN, "generating RSA signature");
     return -1;
   }
   return r;
@@ -921,7 +921,7 @@ crypto_pk_asn1_decode(const char *str, size_t len)
   rsa = d2i_RSAPublicKey(NULL, &cp, len);
   tor_free(buf);
   if (!rsa) {
-    crypto_log_errors(LOG_WARN,"decoding public key");
+    crypto_openssl_log_errors(LOG_WARN,"decoding public key");
     return NULL;
   }
   return crypto_new_pk_from_rsa_(rsa);
@@ -976,6 +976,26 @@ crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out)
   return 0;
 }
 
+/** 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';
+}
+
 /** Check a siglen-byte long signature at <b>sig</b> against
  * <b>datalen</b> bytes of data at <b>data</b>, using the public key
  * in <b>env</b>. Return 0 if <b>sig</b> is a correct signature for
@@ -1145,7 +1165,7 @@ crypto_pk_base64_decode(const char *str, size_t len)
   const unsigned char *dp = (unsigned char*)der; /* Shut the compiler up. */
   RSA *rsa = d2i_RSAPrivateKey(NULL, &dp, der_len);
   if (!rsa) {
-    crypto_log_errors(LOG_WARN, "decoding private key");
+    crypto_openssl_log_errors(LOG_WARN, "decoding private key");
     goto out;
   }
 

+ 5 - 0
src/lib/crypt_ops/crypto_rsa.h

@@ -29,6 +29,10 @@
 /** 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
+
 /** A public key, or a public/private key-pair. */
 typedef struct crypto_pk_t crypto_pk_t;
 
@@ -88,6 +92,7 @@ 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_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));

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

+ 0 - 23
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"
@@ -105,25 +104,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) */
-

+ 25 - 4
src/lib/crypt_ops/include.am

@@ -6,14 +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_dh_openssl.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		\
@@ -23,10 +24,28 @@ 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
+else
+src_lib_libtor_crypt_ops_a_SOURCES +=			\
+	src/lib/crypt_ops/aes_openssl.c
+endif
+
+if USE_OPENSSL
+src_lib_libtor_crypt_ops_a_SOURCES +=			\
+	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 +55,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 - 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=\

+ 4 - 1
src/lib/tls/include.am

@@ -9,10 +9,13 @@ src_lib_libtor_tls_a_SOURCES =			\
 	src/lib/tls/buffers_tls.c		\
 	src/lib/tls/tortls.c
 
+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			\

+ 4 - 4
src/lib/tls/tortls.c

@@ -24,7 +24,7 @@
   #include <ws2tcpip.h>
 #endif
 
-#include "lib/crypt_ops/crypto.h"
+#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"
@@ -1280,10 +1280,10 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
       goto error;
   }
   {
-    crypto_dh_t *dh = crypto_dh_new(DH_TYPE_TLS);
+    DH *dh = crypto_dh_new_openssl_tls();
     tor_assert(dh);
-    SSL_CTX_set_tmp_dh(result->ctx, crypto_dh_get_dh_(dh));
-    crypto_dh_free(dh);
+    SSL_CTX_set_tmp_dh(result->ctx, dh);
+    DH_free(dh);
   }
   if (! is_client) {
     int nid;

+ 1 - 0
src/rust/build.rs

@@ -175,6 +175,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");

+ 3 - 3
src/test/bench.c

@@ -33,6 +33,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;
@@ -680,9 +681,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 +719,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@ \

+ 11 - 11
src/test/include.am

@@ -226,13 +226,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 +249,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 +277,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)
@@ -302,22 +302,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)
 

+ 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;

+ 57 - 0
src/test/test_crypto.c

@@ -16,6 +16,7 @@
 #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"
 
 #ifdef HAVE_SYS_STAT_H
@@ -25,6 +26,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 +45,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 +104,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,10 +174,50 @@ 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

+ 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"
 

+ 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;
   }
 }
-

+ 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"

+ 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_util_slow.c

@@ -6,7 +6,7 @@
 #include "orconfig.h"
 #define UTIL_PRIVATE
 #define SUBPROCESS_PRIVATE
-#include "lib/crypt_ops/crypto.h"
+#include "lib/crypt_ops/crypto_cipher.h"
 #include "lib/log/log.h"
 #include "lib/process/subprocess.h"
 #include "lib/process/waitpid.h"

+ 1 - 0
src/test/test_workqueue.c

@@ -12,6 +12,7 @@
 #include "lib/net/alertsock.h"
 #include "lib/evloop/compat_libevent.h"
 #include "lib/intmath/weakrng.h"
+#include "lib/crypt_ops/crypto_init.h"
 
 #include <stdio.h>
 

+ 8 - 1
src/test/testing_common.c

@@ -23,6 +23,7 @@
 #include "core/mainloop/main.h"
 #include "lib/compress/compress.h"
 #include "lib/evloop/compat_libevent.h"
+#include "lib/crypt_ops/crypto_init.h"
 
 #include <stdio.h>
 #ifdef HAVE_FCNTL_H
@@ -222,6 +223,13 @@ an_assertion_failed(void)
   tinytest_set_test_failed_();
 }
 
+void tinytest_postfork(void);
+void
+tinytest_postfork(void)
+{
+  crypto_postfork();
+}
+
 /** Main entry point for unit test code: parse the command line, and run
  * some unit tests. */
 int
@@ -284,7 +292,6 @@ main(int c, const char **v)
     printf("Can't initialize crypto subsystem; exiting.\n");
     return 1;
   }
-  crypto_set_tls_dh_prime();
   if (crypto_seed_rng() < 0) {
     printf("Couldn't seed RNG; exiting.\n");
     return 1;

+ 5 - 5
src/tools/include.am

@@ -21,12 +21,12 @@ src_tools_tor_cov_resolve_LDADD = \
 endif
 
 src_tools_tor_gencert_SOURCES = src/tools/tor-gencert.c
-src_tools_tor_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
+src_tools_tor_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB)
 src_tools_tor_gencert_LDADD = \
 	$(TOR_CRYPTO_LIBS) \
 	$(TOR_UTIL_LIBS) \
 	$(rust_ldadd) \
-	@TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \
+	@TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ $(TOR_LIBS_CRYPTLIB) \
 	@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ @CURVE25519_LIBS@
 
 src_tools_tor_print_ed_signing_cert_SOURCES = src/tools/tor-print-ed-signing-cert.c
@@ -35,18 +35,18 @@ src_tools_tor_print_ed_signing_cert_LDADD = \
 	src/trunnel/libor-trunnel.a \
         $(TOR_CRYPTO_LIBS) \
         $(TOR_UTIL_LIBS) \
-	@TOR_LIB_MATH@ @TOR_OPENSSL_LIBS@ \
+	@TOR_LIB_MATH@ $(TOR_LIBS_CRYPTLIB) \
 	@TOR_LIB_WS32@ @TOR_LIB_USERENV@
 
 if COVERAGE_ENABLED
 src_tools_tor_cov_gencert_SOURCES = src/tools/tor-gencert.c
 src_tools_tor_cov_gencert_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
 src_tools_tor_cov_gencert_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
-src_tools_tor_cov_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
+src_tools_tor_cov_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB)
 src_tools_tor_cov_gencert_LDADD = \
     $(TOR_CRYPTO_TESTING_LIBS) \
     $(TOR_UTIL_TESTING_LIBS) \
-    @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \
+    @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ $(TOR_LIBS_CRYPTLIB) \
     @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
 endif
 

+ 8 - 6
src/tools/tor-gencert.c

@@ -14,6 +14,8 @@
 #endif
 
 #include "lib/cc/compat_compiler.h"
+#include "lib/crypt_ops/crypto_init.h"
+#include "lib/crypt_ops/crypto_openssl_mgt.h"
 
 /* Some versions of OpenSSL declare X509_STORE_CTX_set_verify_cb twice in
  * x509.h and x509_vfy.h. Suppress the GCC warning so we can build with
@@ -31,9 +33,9 @@ ENABLE_GCC_WARNING(redundant-decls)
 
 #include <errno.h>
 
-#include "lib/crypt_ops/crypto.h"
 #include "lib/crypt_ops/crypto_digest.h"
 #include "lib/crypt_ops/crypto_rand.h"
+#include "lib/crypt_ops/crypto_rsa.h"
 #include "lib/crypt_ops/crypto_util.h"
 #include "lib/encoding/binascii.h"
 #include "lib/encoding/time_fmt.h"
@@ -266,7 +268,7 @@ load_identity_key(void)
                IDENTITY_KEY_BITS);
     if (!(key = generate_key(IDENTITY_KEY_BITS))) {
       log_err(LD_GENERAL, "Couldn't generate identity key.");
-      crypto_log_errors(LOG_ERR, "Generating identity key");
+      crypto_openssl_log_errors(LOG_ERR, "Generating identity key");
       return 1;
     }
     identity_key = EVP_PKEY_new();
@@ -288,7 +290,7 @@ load_identity_key(void)
                                        NULL, NULL)) {
       log_err(LD_GENERAL, "Couldn't write identity key to %s",
               identity_key_file);
-      crypto_log_errors(LOG_ERR, "Writing identity key");
+      crypto_openssl_log_errors(LOG_ERR, "Writing identity key");
       abort_writing_to_file(open_file);
       return 1;
     }
@@ -353,7 +355,7 @@ generate_signing_key(void)
              SIGNING_KEY_BITS);
   if (!(key = generate_key(SIGNING_KEY_BITS))) {
     log_err(LD_GENERAL, "Couldn't generate signing key.");
-    crypto_log_errors(LOG_ERR, "Generating signing key");
+    crypto_openssl_log_errors(LOG_ERR, "Generating signing key");
     return 1;
   }
   signing_key = EVP_PKEY_new();
@@ -369,7 +371,7 @@ generate_signing_key(void)
 
   /* Write signing key with no encryption. */
   if (!PEM_write_RSAPrivateKey(f, key, NULL, NULL, 0, NULL, NULL)) {
-    crypto_log_errors(LOG_WARN, "writing signing key");
+    crypto_openssl_log_errors(LOG_WARN, "writing signing key");
     abort_writing_to_file(open_file);
     return 1;
   }
@@ -393,7 +395,7 @@ key_to_string(EVP_PKEY *key)
 
   b = BIO_new(BIO_s_mem());
   if (!PEM_write_bio_RSAPublicKey(b, rsa)) {
-    crypto_log_errors(LOG_WARN, "writing public key to string");
+    crypto_openssl_log_errors(LOG_WARN, "writing public key to string");
     RSA_free(rsa);
     return NULL;
   }