diff --git a/Makefile.am b/Makefile.am index 3df35ad3f3..adf02122ff 100644 --- a/Makefile.am +++ b/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 diff --git a/changes/feature26815 b/changes/feature26815 new file mode 100644 index 0000000000..10e7e478f9 --- /dev/null +++ b/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. diff --git a/changes/feature26816 b/changes/feature26816 new file mode 100644 index 0000000000..3c25597d85 --- /dev/null +++ b/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. diff --git a/config.rust.in b/config.rust.in index 4ca5351aec..11b671b980 100644 --- a/config.rust.in +++ b/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@ \ No newline at end of file diff --git a/configure.ac b/configure.ac index 32eed75bf9..aa9b2ba6bd 100644 --- a/configure.ac +++ b/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? diff --git a/src/app/include.am b/src/app/include.am index 7cf20191ab..97d53ec0fd 100644 --- a/src/app/include.am +++ b/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@ diff --git a/src/core/crypto/onion_ntor.c b/src/core/crypto/onion_ntor.c index ea7261cb53..3614e0c9b1 100644 --- a/src/core/crypto/onion_ntor.c +++ b/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" diff --git a/src/core/crypto/relay_crypto.c b/src/core/crypto/relay_crypto.c index b2388d2c45..311160a669 100644 --- a/src/core/crypto/relay_crypto.c +++ b/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" diff --git a/src/core/mainloop/main.c b/src/core/mainloop/main.c index 048397a2da..ad8c1ead67 100644 --- a/src/core/mainloop/main.c +++ b/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 diff --git a/src/core/or/or.h b/src/core/or/or.h index 9aca030c74..13376366ac 100644 --- a/src/core/or/or.h +++ b/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" diff --git a/src/ext/tinytest.c b/src/ext/tinytest.c index df2cc0b584..2a475bd917 100644 --- a/src/ext/tinytest.c +++ b/src/ext/tinytest.c @@ -25,6 +25,7 @@ #ifdef TINYTEST_LOCAL #include "tinytest_local.h" #endif +#define TINYTEST_POSTFORK #include #include @@ -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]; diff --git a/src/feature/nodelist/parsecommon.c b/src/feature/nodelist/parsecommon.c index 3aaf8ac501..e05657aca0 100644 --- a/src/feature/nodelist/parsecommon.c +++ b/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 diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index 0cc4887232..7685760ac6 100644 --- a/src/feature/relay/router.c +++ b/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 diff --git a/src/feature/rend/rendmid.c b/src/feature/rend/rendmid.c index 22cd6c3435..f433e7ce5b 100644 --- a/src/feature/rend/rendmid.c +++ b/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" diff --git a/src/lib/crypt_ops/aes_nss.c b/src/lib/crypt_ops/aes_nss.c new file mode 100644 index 0000000000..272edc5592 --- /dev/null +++ b/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 +#include +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 *)¶ms, + .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(¶ms, 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; +} diff --git a/src/lib/crypt_ops/aes.c b/src/lib/crypt_ops/aes_openssl.c similarity index 99% rename from src/lib/crypt_ops/aes.c rename to src/lib/crypt_ops/aes_openssl.c index ff9d4d855c..387f5d3df0 100644 --- a/src/lib/crypt_ops/aes.c +++ b/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" diff --git a/src/lib/crypt_ops/crypto.c b/src/lib/crypt_ops/crypto.c deleted file mode 100644 index 5bc2da76ab..0000000000 --- a/src/lib/crypt_ops/crypto.c +++ /dev/null @@ -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 -#include -#include -/* 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 -#include -#include -#include -#include -#include -#include -#include - -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 -#endif -#ifdef HAVE_UNISTD_H -#include -#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 - -/** 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 bits 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 key and an IV of all - * zero bytes and key length bits. 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 key (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 in to the outlen-byte buffer out, 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 && outfromlen bytes from from using the cipher - * env; on success, store the result to to 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 fromlen bytes from from using the cipher - * env; on success, store the result to to 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 len bytes on from using the cipher in env; - * 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 fromlen bytes (at least 1) from from with the key in - * key to the buffer in to of length - * tolen. tolen must be at least fromlen 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 fromlen bytes (at least 1+CIPHER_IV_LEN) from from - * with the key in key to the buffer in to of length - * tolen. tolen must be at least fromlen 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; -} - -/** @} */ diff --git a/src/lib/crypt_ops/crypto_cipher.c b/src/lib/crypt_ops/crypto_cipher.c new file mode 100644 index 0000000000..6b762e374d --- /dev/null +++ b/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 + +/** Allocate and return a new symmetric cipher using the provided key and iv. + * The key is bits 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 key and an IV of all + * zero bytes and key length bits. 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 key (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 fromlen bytes from from using the cipher + * env; on success, store the result to to 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 fromlen bytes from from using the cipher + * env; on success, store the result to to 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 len bytes on from using the cipher in env; + * 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 fromlen bytes (at least 1) from from with the key in + * key to the buffer in to of length + * tolen. tolen must be at least fromlen 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 fromlen bytes (at least 1+CIPHER_IV_LEN) from from + * with the key in key to the buffer in to of length + * tolen. tolen must be at least fromlen 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); +} diff --git a/src/lib/crypt_ops/crypto.h b/src/lib/crypt_ops/crypto_cipher.h similarity index 75% rename from src/lib/crypt_ops/crypto.h rename to src/lib/crypt_ops/crypto_cipher.h index 3a0b330be6..f9444d03fc 100644 --- a/src/lib/crypt_ops/crypto.h +++ b/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 #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) */ diff --git a/src/lib/crypt_ops/crypto_dh.c b/src/lib/crypt_ops/crypto_dh.c index 3e82996935..673ef311f9 100644 --- a/src/lib/crypt_ops/crypto_dh.c +++ b/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 - -ENABLE_GCC_WARNING(redundant-decls) - -#include -#include - -/** 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) +crypto_dh_init(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 dh, 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 dh, in bytes. - */ -int -crypto_dh_get_bytes(crypto_dh_t *dh) -{ - tor_assert(dh); - return DH_size(dh->dh); -} - -/** Generate \ 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 pubkey_len-byte value into pubkey. Return 0 on - * success, -1 on failure. pubkey_len 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; -} diff --git a/src/lib/crypt_ops/crypto_dh.h b/src/lib/crypt_ops/crypto_dh.h index 88e8a919a8..6e79a6404c 100644 --- a/src/lib/crypt_ops/crypto_dh.h +++ b/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) */ diff --git a/src/lib/crypt_ops/crypto_dh_nss.c b/src/lib/crypt_ops/crypto_dh_nss.c new file mode 100644 index 0000000000..647dfb4e57 --- /dev/null +++ b/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 +#include +#include + +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; +} diff --git a/src/lib/crypt_ops/crypto_dh_openssl.c b/src/lib/crypt_ops/crypto_dh_openssl.c new file mode 100644 index 0000000000..54946458d5 --- /dev/null +++ b/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 + +ENABLE_GCC_WARNING(redundant-decls) + +#include +#include + +#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 hex 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 dh, 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 dh, in bytes. + */ +int +crypto_dh_get_bytes(crypto_dh_t *dh) +{ + tor_assert(dh); + return DH_size(dh->dh); +} + +/** Generate \ 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 pubkey_len-byte value into pubkey. Return 0 on + * success, -1 on failure. pubkey_len 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 + * pubkey_len-byte value in pubkey) generate + * g^xy as a big-endian integer in secret_out. + * 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; +} diff --git a/src/lib/crypt_ops/crypto_digest.c b/src/lib/crypt_ops/crypto_digest.c index ee5c362e38..2bc5f08342 100644 --- a/src/lib/crypt_ops/crypto_digest.c +++ b/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 +ENABLE_GCC_WARNING(strict-prototypes) +#else + +#include "lib/crypt_ops/crypto_openssl_mgt.h" + DISABLE_GCC_WARNING(redundant-decls) #include #include 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 digest in checkpoint. @@ -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, + ¶mItem); + 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 msg_len bytes in msg using a diff --git a/src/lib/crypt_ops/crypto_digest.h b/src/lib/crypt_ops/crypto_digest.h index 9facf3b981..59713d2b9f 100644 --- a/src/lib/crypt_ops/crypto_digest.h +++ b/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; diff --git a/src/lib/crypt_ops/crypto_init.c b/src/lib/crypt_ops/crypto_init.c new file mode 100644 index 0000000000..620fe8e1be --- /dev/null +++ b/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 +} diff --git a/src/lib/crypt_ops/crypto_init.h b/src/lib/crypt_ops/crypto_init.h new file mode 100644 index 0000000000..3e32456b5c --- /dev/null +++ b/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) */ diff --git a/src/lib/crypt_ops/crypto_nss_mgt.c b/src/lib/crypt_ops/crypto_nss_mgt.c new file mode 100644 index 0000000000..6bcaeabd5a --- /dev/null +++ b/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 +#include +#include + +#include +#include +#include + +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(); +} diff --git a/src/lib/crypt_ops/crypto_nss_mgt.h b/src/lib/crypt_ops/crypto_nss_mgt.h new file mode 100644 index 0000000000..c4c94f4d89 --- /dev/null +++ b/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) */ diff --git a/src/lib/crypt_ops/crypto_openssl_mgt.c b/src/lib/crypt_ops/crypto_openssl_mgt.c index 01de6a9d9e..125da0786b 100644 --- a/src/lib/crypt_ops/crypto_openssl_mgt.c +++ b/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 #include #include +#include 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 severity. Use + * doing 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 * raw_version. 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(); +} diff --git a/src/lib/crypt_ops/crypto_openssl_mgt.h b/src/lib/crypt_ops/crypto_openssl_mgt.h index a2c53302e1..3b288fb9d8 100644 --- a/src/lib/crypt_ops/crypto_openssl_mgt.h +++ b/src/lib/crypt_ops/crypto_openssl_mgt.h @@ -14,6 +14,8 @@ #define TOR_CRYPTO_OPENSSL_H #include "orconfig.h" + +#ifdef ENABLE_OPENSSL #include /* @@ -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); -/* Tor OpenSSL utility functions */ -void crypto_openssl_free_all(void); +void crypto_openssl_thread_cleanup(void); +void crypto_openssl_global_cleanup(void); + +#endif /* ENABLE_OPENSSL */ #endif /* !defined(TOR_CRYPTO_OPENSSL_H) */ diff --git a/src/lib/crypt_ops/crypto_pwbox.c b/src/lib/crypt_ops/crypto_pwbox.c index c001e295da..2377f216a0 100644 --- a/src/lib/crypt_ops/crypto_pwbox.c +++ b/src/lib/crypt_ops/crypto_pwbox.c @@ -11,7 +11,7 @@ #include #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" diff --git a/src/lib/crypt_ops/crypto_rand.c b/src/lib/crypt_ops/crypto_rand.c index fb9d0c2c6c..9806714747 100644 --- a/src/lib/crypt_ops/crypto_rand.c +++ b/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 ENABLE_GCC_WARNING(redundant-decls) +#endif + +#ifdef ENABLE_NSS +#include +#include +#include +#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 n bytes of strong random data to to. 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 } /** diff --git a/src/lib/crypt_ops/crypto_rsa.c b/src/lib/crypt_ops/crypto_rsa.c index 5ec69d7319..ffe2bd2ceb 100644 --- a/src/lib/crypt_ops/crypto_rsa.c +++ b/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 in to the outlen-byte buffer out, 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 && outsig against * datalen bytes of data at data, using the public key * in env. Return 0 if sig 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; } diff --git a/src/lib/crypt_ops/crypto_rsa.h b/src/lib/crypt_ops/crypto_rsa.h index 51fc974821..45412d21ec 100644 --- a/src/lib/crypt_ops/crypto_rsa.h +++ b/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)); diff --git a/src/lib/crypt_ops/crypto_s2k.c b/src/lib/crypt_ops/crypto_s2k.c index ab91d92f0e..0e151f0a6c 100644 --- a/src/lib/crypt_ops/crypto_s2k.c +++ b/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" diff --git a/src/lib/crypt_ops/crypto_util.c b/src/lib/crypt_ops/crypto_util.c index 79988c6a91..a645321bfb 100644 --- a/src/lib/crypt_ops/crypto_util.c +++ b/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 severity. Use - * doing 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) */ diff --git a/src/lib/crypt_ops/crypto_util.h b/src/lib/crypt_ops/crypto_util.h index 3ce34e6f23..e032263225 100644 --- a/src/lib/crypt_ops/crypto_util.h +++ b/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) */ - diff --git a/src/lib/crypt_ops/include.am b/src/lib/crypt_ops/include.am index 017d7956d0..8647a91e8c 100644 --- a/src/lib/crypt_ops/include.am +++ b/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 \ diff --git a/src/lib/include.libdonna.am b/src/lib/include.libdonna.am index 5b92dc58a0..60a3f5097e 100644 --- a/src/lib/include.libdonna.am +++ b/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=\ diff --git a/src/lib/tls/include.am b/src/lib/tls/include.am index b3b013f4dd..9cc57ca777 100644 --- a/src/lib/tls/include.am +++ b/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 \ diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c index 073cae02ba..875ed95f8c 100644 --- a/src/lib/tls/tortls.c +++ b/src/lib/tls/tortls.c @@ -24,7 +24,7 @@ #include #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; diff --git a/src/rust/build.rs b/src/rust/build.rs index 2ac24b334b..78773915a5 100644 --- a/src/rust/build.rs +++ b/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"); diff --git a/src/test/bench.c b/src/test/bench.c index 7919a4224f..2b90ccf734 100644 --- a/src/test/bench.c +++ b/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; diff --git a/src/test/fuzz/fuzzing_common.c b/src/test/fuzz/fuzzing_common.c index 0b3483bf66..1401e4c28d 100644 --- a/src/test/fuzz/fuzzing_common.c +++ b/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); diff --git a/src/test/fuzz/include.am b/src/test/fuzz/include.am index 09594d8272..27eeced8c5 100644 --- a/src/test/fuzz/include.am +++ b/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@ \ diff --git a/src/test/include.am b/src/test/include.am index 81b089b8f7..46990597fd 100644 --- a/src/test/include.am +++ b/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) diff --git a/src/test/test-timers.c b/src/test/test-timers.c index f9276c25d6..923f51ecce 100644 --- a/src/test/test-timers.c +++ b/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; diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index 2124e22196..029e98a630 100644 --- a/src/test/test_crypto.c +++ b/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 #endif +#if defined(ENABLE_OPENSSL) +#include "lib/crypt_ops/compat_openssl.h" +DISABLE_GCC_WARNING(redundant-decls) +#include +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 diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c index 57da03ca28..7fcc1db195 100644 --- a/src/test/test_hs_client.c +++ b/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" diff --git a/src/test/test_hs_ntor_cl.c b/src/test/test_hs_ntor_cl.c index 03e34968be..a4915c4f8a 100644 --- a/src/test/test_hs_ntor_cl.c +++ b/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")) { diff --git a/src/test/test_introduce.c b/src/test/test_introduce.c index cdfb70bdff..4d2d909945 100644 --- a/src/test/test_introduce.c +++ b/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" diff --git a/src/test/test_ntor_cl.c b/src/test/test_ntor_cl.c index 744b42c9d9..3f914523a3 100644 --- a/src/test/test_ntor_cl.c +++ b/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; } } - diff --git a/src/test/test_relaycell.c b/src/test/test_relaycell.c index eb30cab0ec..a6c152f738 100644 --- a/src/test/test_relaycell.c +++ b/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" diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c index 1a1bf63ba0..82a80130e0 100644 --- a/src/test/test_routerkeys.c +++ b/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" diff --git a/src/test/test_util_slow.c b/src/test/test_util_slow.c index c5b24487b1..c7b3e3e2a4 100644 --- a/src/test/test_util_slow.c +++ b/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" diff --git a/src/test/test_workqueue.c b/src/test/test_workqueue.c index 4550bad1f0..9d48d92773 100644 --- a/src/test/test_workqueue.c +++ b/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 diff --git a/src/test/testing_common.c b/src/test/testing_common.c index 3880bca9c5..6957342c62 100644 --- a/src/test/testing_common.c +++ b/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 #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; diff --git a/src/tools/include.am b/src/tools/include.am index d5924dda5f..cdd5616fb1 100644 --- a/src/tools/include.am +++ b/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 diff --git a/src/tools/tor-gencert.c b/src/tools/tor-gencert.c index ff924152a6..63e24d922c 100644 --- a/src/tools/tor-gencert.c +++ b/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 -#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; }