mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-12-06 19:41:15 +01:00
Merge branch 'nss_squashed' into nss_merge
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
o Major features (portability, cryptography, experimental, TLS):
|
||||
- Tor now has the option to compile with the NSS library instead of
|
||||
OpenSSL. This feature is experimental, and we expect that bugs may
|
||||
remain. It is mainly intended for environments where Tor's performance
|
||||
is not CPU-bound, and where NSS is already known to be installed.
|
||||
To try it out, configure Tor with the --enable-nss flag.
|
||||
Closes ticket 26631.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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@
|
||||
@@ -53,6 +53,8 @@ AC_ARG_ENABLE(restart-debugging,
|
||||
AS_HELP_STRING(--enable-restart-debugging, [Build Tor with support for debugging in-process restart. Developers only.]))
|
||||
AC_ARG_ENABLE(zstd-advanced-apis,
|
||||
AS_HELP_STRING(--disable-zstd-advanced-apis, [Build without support for zstd's "static-only" APIs.]))
|
||||
AC_ARG_ENABLE(nss,
|
||||
AS_HELP_STRING(--enable-nss, [Use Mozilla's NSS TLS library. (EXPERIMENTAL)]))
|
||||
|
||||
if test "x$enable_coverage" != "xyes" -a "x$enable_asserts_in_tests" = "xno" ; then
|
||||
AC_MSG_ERROR([Can't disable assertions outside of coverage build])
|
||||
@@ -64,6 +66,16 @@ AM_CONDITIONAL(DISABLE_ASSERTS_IN_UNIT_TESTS, test "x$enable_asserts_in_tests" =
|
||||
AM_CONDITIONAL(LIBFUZZER_ENABLED, test "x$enable_libfuzzer" = "xyes")
|
||||
AM_CONDITIONAL(OSS_FUZZ_ENABLED, test "x$enable_oss_fuzz" = "xyes")
|
||||
AM_CONDITIONAL(USE_RUST, test "x$enable_rust" = "xyes")
|
||||
AM_CONDITIONAL(USE_NSS, test "x$enable_nss" = "xyes")
|
||||
AM_CONDITIONAL(USE_OPENSSL, test "x$enable_nss" != "xyes")
|
||||
|
||||
if test "x$enable_nss" = "xyes"; then
|
||||
AC_DEFINE(ENABLE_NSS, 1,
|
||||
[Defined if we're building with NSS.])
|
||||
else
|
||||
AC_DEFINE(ENABLE_OPENSSL, 1,
|
||||
[Defined if we're building with OpenSSL or LibreSSL])
|
||||
fi
|
||||
|
||||
if test "$enable_static_tor" = "yes"; then
|
||||
enable_static_libevent="yes";
|
||||
@@ -840,9 +852,22 @@ fi
|
||||
LIBS="$save_LIBS"
|
||||
AC_SUBST(TOR_LIB_MATH)
|
||||
|
||||
dnl ------------------------------------------------------
|
||||
dnl Hello, NSS. You're new around here.
|
||||
if test "x$enable_nss" = "xyes"; then
|
||||
PKG_CHECK_MODULES(NSS,
|
||||
[nss],
|
||||
[have_nss=yes],
|
||||
[have_nss=no; AC_MSG_ERROR([You asked for NSS but I can't find it.])])
|
||||
AC_SUBST(NSS_CFLAGS)
|
||||
AC_SUBST(NSS_LIBS)
|
||||
fi
|
||||
|
||||
dnl ------------------------------------------------------
|
||||
dnl Where do you live, openssl? And how do we call you?
|
||||
|
||||
if test "x$enable_nss" != "xyes"; then
|
||||
|
||||
tor_openssl_pkg_redhat="openssl"
|
||||
tor_openssl_pkg_debian="libssl-dev"
|
||||
tor_openssl_devpkg_redhat="openssl-devel"
|
||||
@@ -942,6 +967,11 @@ AC_CHECK_SIZEOF(SHA_CTX, , [AC_INCLUDES_DEFAULT()
|
||||
#include <openssl/sha.h>
|
||||
])
|
||||
|
||||
fi # enable_nss
|
||||
|
||||
dnl ======================================================================
|
||||
dnl Can we use KIST?
|
||||
|
||||
dnl Define the set of checks for KIST scheduler support.
|
||||
AC_DEFUN([CHECK_KIST_SUPPORT],[
|
||||
dnl KIST needs struct tcp_info and for certain members to exist.
|
||||
|
||||
+15
-1
@@ -81,6 +81,12 @@
|
||||
#include "core/mainloop/cpuworker.h"
|
||||
#include "lib/crypt_ops/crypto_rand.h"
|
||||
#include "lib/crypt_ops/crypto_util.h"
|
||||
#include "lib/crypt_ops/crypto_init.h"
|
||||
#ifdef ENABLE_NSS
|
||||
#include "lib/crypt_ops/crypto_nss_mgt.h"
|
||||
#else
|
||||
#include "lib/crypt_ops/crypto_openssl_mgt.h"
|
||||
#endif
|
||||
#include "feature/dircache/dirserv.h"
|
||||
#include "feature/relay/dns.h"
|
||||
#include "core/or/dos.h"
|
||||
@@ -1412,7 +1418,8 @@ options_act_reversible(const or_options_t *old_options, char **msg)
|
||||
* processes. */
|
||||
if (running_tor && options->RunAsDaemon) {
|
||||
/* No need to roll back, since you can't change the value. */
|
||||
start_daemon();
|
||||
if (start_daemon())
|
||||
crypto_postfork();
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYSTEMD
|
||||
@@ -5152,9 +5159,16 @@ options_init_from_torrc(int argc, char **argv)
|
||||
printf("Libevent\t\t%-15s\t\t%s\n",
|
||||
tor_libevent_get_header_version_str(),
|
||||
tor_libevent_get_version_str());
|
||||
#ifdef ENABLE_OPENSSL
|
||||
printf("OpenSSL \t\t%-15s\t\t%s\n",
|
||||
crypto_openssl_get_header_version_str(),
|
||||
crypto_openssl_get_version_str());
|
||||
#endif
|
||||
#ifdef ENABLE_NSS
|
||||
printf("NSS \t\t%-15s\t\t%s\n",
|
||||
crypto_nss_get_header_version_str(),
|
||||
crypto_nss_get_version_str());
|
||||
#endif
|
||||
if (tor_compress_supports_method(ZLIB_METHOD)) {
|
||||
printf("Zlib \t\t%-15s\t\t%s\n",
|
||||
tor_compress_version_str(ZLIB_METHOD),
|
||||
|
||||
+4
-4
@@ -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@
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -449,6 +449,20 @@ connection_new(int type, int socket_family)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
connection_close_and_invalidate_socket(connection_t *conn)
|
||||
{
|
||||
if (connection_speaks_cells(conn)) {
|
||||
or_connection_t *or_conn = TO_OR_CONN(conn);
|
||||
tor_tls_free(or_conn->tls);
|
||||
or_conn->tls = NULL;
|
||||
or_conn->base_.s = TOR_INVALID_SOCKET;
|
||||
} else {
|
||||
tor_close_socket(conn->s);
|
||||
conn->s = TOR_INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
|
||||
/** Initializes conn. (you must call connection_add() to link it into the main
|
||||
* array).
|
||||
*
|
||||
@@ -614,9 +628,8 @@ connection_free_minimal(connection_t *conn)
|
||||
tor_free(conn->address);
|
||||
|
||||
if (connection_speaks_cells(conn)) {
|
||||
connection_close_and_invalidate_socket(conn);
|
||||
or_connection_t *or_conn = TO_OR_CONN(conn);
|
||||
tor_tls_free(or_conn->tls);
|
||||
or_conn->tls = NULL;
|
||||
or_handshake_state_free(or_conn->handshake_state);
|
||||
or_conn->handshake_state = NULL;
|
||||
tor_free(or_conn->nickname);
|
||||
@@ -692,9 +705,7 @@ connection_free_minimal(connection_t *conn)
|
||||
}
|
||||
|
||||
if (SOCKET_OK(conn->s)) {
|
||||
log_debug(LD_NET,"closing fd %d.",(int)conn->s);
|
||||
tor_close_socket(conn->s);
|
||||
conn->s = TOR_INVALID_SOCKET;
|
||||
connection_close_and_invalidate_socket(conn);
|
||||
}
|
||||
|
||||
if (conn->type == CONN_TYPE_OR &&
|
||||
@@ -820,9 +831,7 @@ connection_close_immediate(connection_t *conn)
|
||||
conn->read_blocked_on_bw = 0;
|
||||
conn->write_blocked_on_bw = 0;
|
||||
|
||||
if (SOCKET_OK(conn->s))
|
||||
tor_close_socket(conn->s);
|
||||
conn->s = TOR_INVALID_SOCKET;
|
||||
connection_close_and_invalidate_socket(conn);
|
||||
if (conn->linked)
|
||||
conn->linked_conn_is_closed = 1;
|
||||
if (conn->outbuf)
|
||||
|
||||
@@ -121,6 +121,7 @@
|
||||
#include "lib/evloop/compat_libevent.h"
|
||||
#include "lib/encoding/confline.h"
|
||||
#include "lib/evloop/timers.h"
|
||||
#include "lib/crypt_ops/crypto_init.h"
|
||||
|
||||
#include <event2/event.h>
|
||||
|
||||
@@ -3503,10 +3504,11 @@ tor_init(int argc, char *argv[])
|
||||
const char *version = get_version();
|
||||
|
||||
log_notice(LD_GENERAL, "Tor %s running on %s with Libevent %s, "
|
||||
"OpenSSL %s, Zlib %s, Liblzma %s, and Libzstd %s.", version,
|
||||
"%s %s, Zlib %s, Liblzma %s, and Libzstd %s.", version,
|
||||
get_uname(),
|
||||
tor_libevent_get_version_str(),
|
||||
crypto_openssl_get_version_str(),
|
||||
crypto_get_library_name(),
|
||||
crypto_get_library_version_string(),
|
||||
tor_compress_supports_method(ZLIB_METHOD) ?
|
||||
tor_compress_version_str(ZLIB_METHOD) : "N/A",
|
||||
tor_compress_supports_method(LZMA_METHOD) ?
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
#include "core/or/var_cell_st.h"
|
||||
|
||||
#include "lib/tls/tortls.h"
|
||||
#include "lib/tls/x509.h"
|
||||
|
||||
/** How many CELL_PADDING cells have we received, ever? */
|
||||
uint64_t stats_n_padding_cells_processed = 0;
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
#include "lib/crypt_ops/crypto_format.h"
|
||||
|
||||
#include "lib/tls/tortls.h"
|
||||
#include "lib/tls/x509.h"
|
||||
|
||||
static int connection_tls_finish_handshake(or_connection_t *conn);
|
||||
static int connection_or_launch_v3_or_handshake(or_connection_t *conn);
|
||||
|
||||
+2
-1
@@ -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"
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#ifdef TINYTEST_LOCAL
|
||||
#include "tinytest_local.h"
|
||||
#endif
|
||||
#define TINYTEST_POSTFORK
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -118,6 +119,14 @@ testcase_run_bare_(const struct testcase_t *testcase)
|
||||
|
||||
#ifndef NO_FORKING
|
||||
|
||||
#ifdef TINYTEST_POSTFORK
|
||||
void tinytest_prefork(void);
|
||||
void tinytest_postfork(void);
|
||||
#else
|
||||
static void tinytest_prefork(void) { }
|
||||
static void tinytest_postfork(void) { }
|
||||
#endif
|
||||
|
||||
static enum outcome
|
||||
testcase_run_forked_(const struct testgroup_t *group,
|
||||
const struct testcase_t *testcase)
|
||||
@@ -178,10 +187,12 @@ testcase_run_forked_(const struct testgroup_t *group,
|
||||
|
||||
if (opt_verbosity>0)
|
||||
printf("[forking] ");
|
||||
tinytest_prefork();
|
||||
pid = fork();
|
||||
#ifdef FORK_BREAKS_GCOV
|
||||
vproc_transaction_begin(0);
|
||||
#endif
|
||||
tinytest_postfork();
|
||||
if (!pid) {
|
||||
/* child. */
|
||||
int test_r, write_r;
|
||||
|
||||
@@ -4994,7 +4994,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
|
||||
|
||||
if (!strcasecmp(key_type_rsa1024, key_type)) {
|
||||
/* "RSA:<Base64 Blob>" - Loading a pre-existing RSA1024 key. */
|
||||
pk = crypto_pk_base64_decode(key_blob, strlen(key_blob));
|
||||
pk = crypto_pk_base64_decode_private(key_blob, strlen(key_blob));
|
||||
if (!pk) {
|
||||
err_msg = tor_strdup("512 Failed to decode RSA key\r\n");
|
||||
goto err;
|
||||
@@ -5029,7 +5029,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
|
||||
goto err;
|
||||
}
|
||||
if (!discard_pk) {
|
||||
if (crypto_pk_base64_encode(pk, &key_new_blob)) {
|
||||
if (crypto_pk_base64_encode_private(pk, &key_new_blob)) {
|
||||
crypto_pk_free(pk);
|
||||
tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n",
|
||||
key_type_rsa1024);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include "lib/string/util_string.h"
|
||||
#include "lib/string/printf.h"
|
||||
#include "lib/memarea/memarea.h"
|
||||
#include "lib/crypt_ops/crypto.h"
|
||||
#include "lib/crypt_ops/crypto_rsa.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "lib/log/log.h"
|
||||
#include "trunnel/link_handshake.h"
|
||||
#include "lib/tls/tortls.h"
|
||||
#include "lib/tls/x509.h"
|
||||
|
||||
#include "core/or/or_handshake_certs_st.h"
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
#include "lib/tls/tortls.h"
|
||||
#include "lib/encoding/confline.h"
|
||||
#include "lib/crypt_ops/crypto_format.h"
|
||||
#include "lib/crypt_ops/crypto_init.h"
|
||||
|
||||
/**
|
||||
* \file router.c
|
||||
@@ -579,7 +580,7 @@ init_key_from_file(const char *fname, int generate, int severity,
|
||||
tor_log(severity, LD_GENERAL,"Error generating onion key");
|
||||
goto error;
|
||||
}
|
||||
if (crypto_pk_check_key(prkey) <= 0) {
|
||||
if (! crypto_pk_is_valid_private_key(prkey)) {
|
||||
tor_log(severity, LD_GENERAL,"Generated key seems invalid");
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "lib/crypt_ops/crypto_util.h"
|
||||
#include "lib/term/getpass.h"
|
||||
#include "lib/tls/tortls.h"
|
||||
#include "lib/tls/x509.h"
|
||||
#include "lib/crypt_ops/crypto_format.h"
|
||||
|
||||
#define ENC_KEY_HEADER "Boxed Ed25519 key"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1652,7 +1652,7 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname)
|
||||
crypto_pk_free(prkey);
|
||||
goto err;
|
||||
}
|
||||
if (crypto_pk_check_key(prkey) <= 0) {
|
||||
if (! crypto_pk_is_valid_private_key(prkey)) {
|
||||
log_warn(LD_BUG,"Generated client key seems invalid");
|
||||
crypto_pk_free(prkey);
|
||||
goto err;
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/* Copyright (c) 2001, Matej Pfajfar.
|
||||
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file aes_nss.c
|
||||
* \brief Use NSS to implement AES_CTR.
|
||||
**/
|
||||
|
||||
#include "orconfig.h"
|
||||
#include "lib/crypt_ops/aes.h"
|
||||
#include "lib/crypt_ops/crypto_nss_mgt.h"
|
||||
#include "lib/crypt_ops/crypto_util.h"
|
||||
#include "lib/log/util_bug.h"
|
||||
|
||||
DISABLE_GCC_WARNING(strict-prototypes)
|
||||
#include <pk11pub.h>
|
||||
#include <secerr.h>
|
||||
ENABLE_GCC_WARNING(strict-prototypes)
|
||||
|
||||
aes_cnt_cipher_t *
|
||||
aes_new_cipher(const uint8_t *key, const uint8_t *iv,
|
||||
int key_bits)
|
||||
{
|
||||
const CK_MECHANISM_TYPE ckm = CKM_AES_CTR;
|
||||
SECItem keyItem = { .type = siBuffer,
|
||||
.data = (unsigned char *)key,
|
||||
.len = (key_bits / 8) };
|
||||
CK_AES_CTR_PARAMS params;
|
||||
params.ulCounterBits = 128;
|
||||
memcpy(params.cb, iv, 16);
|
||||
SECItem ivItem = { .type = siBuffer,
|
||||
.data = (unsigned char *)¶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;
|
||||
}
|
||||
@@ -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"
|
||||
@@ -7,6 +7,10 @@
|
||||
#ifndef TOR_COMPAT_OPENSSL_H
|
||||
#define TOR_COMPAT_OPENSSL_H
|
||||
|
||||
#include "orconfig.h"
|
||||
|
||||
#ifdef ENABLE_OPENSSL
|
||||
|
||||
#include <openssl/opensslv.h>
|
||||
#include "lib/crypt_ops/crypto_openssl_mgt.h"
|
||||
|
||||
@@ -47,5 +51,7 @@
|
||||
#define CONST_IF_OPENSSL_1_1_API const
|
||||
#endif /* !defined(OPENSSL_1_1_API) */
|
||||
|
||||
#endif /* defined(ENABLE_OPENSSL) */
|
||||
|
||||
#endif /* !defined(TOR_COMPAT_OPENSSL_H) */
|
||||
|
||||
|
||||
@@ -1,509 +0,0 @@
|
||||
/* Copyright (c) 2001, Matej Pfajfar.
|
||||
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file crypto.c
|
||||
* \brief Wrapper functions to present a consistent interface to
|
||||
* public-key and symmetric cryptography operations from OpenSSL and
|
||||
* other places.
|
||||
**/
|
||||
|
||||
#include "orconfig.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <wincrypt.h>
|
||||
/* Windows defines this; so does OpenSSL 0.9.8h and later. We don't actually
|
||||
* use either definition. */
|
||||
#undef OCSP_RESPONSE
|
||||
#endif /* defined(_WIN32) */
|
||||
|
||||
#define CRYPTO_PRIVATE
|
||||
#include "lib/crypt_ops/compat_openssl.h"
|
||||
#include "lib/crypt_ops/crypto.h"
|
||||
#include "lib/crypt_ops/crypto_curve25519.h"
|
||||
#include "lib/crypt_ops/crypto_digest.h"
|
||||
#include "lib/crypt_ops/crypto_dh.h"
|
||||
#include "lib/crypt_ops/crypto_ed25519.h"
|
||||
#include "lib/crypt_ops/crypto_format.h"
|
||||
#include "lib/crypt_ops/crypto_rand.h"
|
||||
#include "lib/crypt_ops/crypto_rsa.h"
|
||||
#include "lib/crypt_ops/crypto_util.h"
|
||||
|
||||
DISABLE_GCC_WARNING(redundant-decls)
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/engine.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/dh.h>
|
||||
#include <openssl/conf.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
ENABLE_GCC_WARNING(redundant-decls)
|
||||
|
||||
#if __GNUC__ && GCC_VERSION >= 402
|
||||
#if GCC_VERSION >= 406
|
||||
#pragma GCC diagnostic pop
|
||||
#else
|
||||
#pragma GCC diagnostic warning "-Wredundant-decls"
|
||||
#endif
|
||||
#endif /* __GNUC__ && GCC_VERSION >= 402 */
|
||||
|
||||
#ifdef HAVE_CTYPE_H
|
||||
#include <ctype.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "lib/log/log.h"
|
||||
#include "lib/log/util_bug.h"
|
||||
#include "lib/cc/torint.h"
|
||||
#include "lib/crypt_ops/aes.h"
|
||||
#include "lib/encoding/binascii.h"
|
||||
|
||||
#include "keccak-tiny/keccak-tiny.h"
|
||||
|
||||
#include "siphash.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/** Boolean: has OpenSSL's crypto been initialized? */
|
||||
static int crypto_early_initialized_ = 0;
|
||||
|
||||
/** Boolean: has OpenSSL's crypto been initialized? */
|
||||
static int crypto_global_initialized_ = 0;
|
||||
|
||||
#ifndef DISABLE_ENGINES
|
||||
/** Log any OpenSSL engines we're using at NOTICE. */
|
||||
static void
|
||||
log_engine(const char *fn, ENGINE *e)
|
||||
{
|
||||
if (e) {
|
||||
const char *name, *id;
|
||||
name = ENGINE_get_name(e);
|
||||
id = ENGINE_get_id(e);
|
||||
log_notice(LD_CRYPTO, "Default OpenSSL engine for %s is %s [%s]",
|
||||
fn, name?name:"?", id?id:"?");
|
||||
} else {
|
||||
log_info(LD_CRYPTO, "Using default implementation for %s", fn);
|
||||
}
|
||||
}
|
||||
#endif /* !defined(DISABLE_ENGINES) */
|
||||
|
||||
#ifndef DISABLE_ENGINES
|
||||
/** Try to load an engine in a shared library via fully qualified path.
|
||||
*/
|
||||
static ENGINE *
|
||||
try_load_engine(const char *path, const char *engine)
|
||||
{
|
||||
ENGINE *e = ENGINE_by_id("dynamic");
|
||||
if (e) {
|
||||
if (!ENGINE_ctrl_cmd_string(e, "ID", engine, 0) ||
|
||||
!ENGINE_ctrl_cmd_string(e, "DIR_LOAD", "2", 0) ||
|
||||
!ENGINE_ctrl_cmd_string(e, "DIR_ADD", path, 0) ||
|
||||
!ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0)) {
|
||||
ENGINE_free(e);
|
||||
e = NULL;
|
||||
}
|
||||
}
|
||||
return e;
|
||||
}
|
||||
#endif /* !defined(DISABLE_ENGINES) */
|
||||
|
||||
static int have_seeded_siphash = 0;
|
||||
|
||||
/** Set up the siphash key if we haven't already done so. */
|
||||
int
|
||||
crypto_init_siphash_key(void)
|
||||
{
|
||||
struct sipkey key;
|
||||
if (have_seeded_siphash)
|
||||
return 0;
|
||||
|
||||
crypto_rand((char*) &key, sizeof(key));
|
||||
siphash_set_global_key(&key);
|
||||
have_seeded_siphash = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Initialize the crypto library. Return 0 on success, -1 on failure.
|
||||
*/
|
||||
int
|
||||
crypto_early_init(void)
|
||||
{
|
||||
if (!crypto_early_initialized_) {
|
||||
|
||||
crypto_early_initialized_ = 1;
|
||||
|
||||
#ifdef OPENSSL_1_1_API
|
||||
OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS |
|
||||
OPENSSL_INIT_LOAD_CRYPTO_STRINGS |
|
||||
OPENSSL_INIT_ADD_ALL_CIPHERS |
|
||||
OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);
|
||||
#else
|
||||
ERR_load_crypto_strings();
|
||||
OpenSSL_add_all_algorithms();
|
||||
#endif
|
||||
|
||||
setup_openssl_threading();
|
||||
|
||||
unsigned long version_num = OpenSSL_version_num();
|
||||
const char *version_str = OpenSSL_version(OPENSSL_VERSION);
|
||||
if (version_num == OPENSSL_VERSION_NUMBER &&
|
||||
!strcmp(version_str, OPENSSL_VERSION_TEXT)) {
|
||||
log_info(LD_CRYPTO, "OpenSSL version matches version from headers "
|
||||
"(%lx: %s).", version_num, version_str);
|
||||
} else {
|
||||
log_warn(LD_CRYPTO, "OpenSSL version from headers does not match the "
|
||||
"version we're running with. If you get weird crashes, that "
|
||||
"might be why. (Compiled with %lx: %s; running with %lx: %s).",
|
||||
(unsigned long)OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_TEXT,
|
||||
version_num, version_str);
|
||||
}
|
||||
|
||||
crypto_force_rand_ssleay();
|
||||
|
||||
if (crypto_seed_rng() < 0)
|
||||
return -1;
|
||||
if (crypto_init_siphash_key() < 0)
|
||||
return -1;
|
||||
|
||||
curve25519_init();
|
||||
ed25519_init();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Initialize the crypto library. Return 0 on success, -1 on failure.
|
||||
*/
|
||||
int
|
||||
crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
|
||||
{
|
||||
if (!crypto_global_initialized_) {
|
||||
if (crypto_early_init() < 0)
|
||||
return -1;
|
||||
|
||||
crypto_global_initialized_ = 1;
|
||||
|
||||
if (useAccel > 0) {
|
||||
#ifdef DISABLE_ENGINES
|
||||
(void)accelName;
|
||||
(void)accelDir;
|
||||
log_warn(LD_CRYPTO, "No OpenSSL hardware acceleration support enabled.");
|
||||
#else
|
||||
ENGINE *e = NULL;
|
||||
|
||||
log_info(LD_CRYPTO, "Initializing OpenSSL engine support.");
|
||||
ENGINE_load_builtin_engines();
|
||||
ENGINE_register_all_complete();
|
||||
|
||||
if (accelName) {
|
||||
if (accelDir) {
|
||||
log_info(LD_CRYPTO, "Trying to load dynamic OpenSSL engine \"%s\""
|
||||
" via path \"%s\".", accelName, accelDir);
|
||||
e = try_load_engine(accelName, accelDir);
|
||||
} else {
|
||||
log_info(LD_CRYPTO, "Initializing dynamic OpenSSL engine \"%s\""
|
||||
" acceleration support.", accelName);
|
||||
e = ENGINE_by_id(accelName);
|
||||
}
|
||||
if (!e) {
|
||||
log_warn(LD_CRYPTO, "Unable to load dynamic OpenSSL engine \"%s\".",
|
||||
accelName);
|
||||
} else {
|
||||
log_info(LD_CRYPTO, "Loaded dynamic OpenSSL engine \"%s\".",
|
||||
accelName);
|
||||
}
|
||||
}
|
||||
if (e) {
|
||||
log_info(LD_CRYPTO, "Loaded OpenSSL hardware acceleration engine,"
|
||||
" setting default ciphers.");
|
||||
ENGINE_set_default(e, ENGINE_METHOD_ALL);
|
||||
}
|
||||
/* Log, if available, the intersection of the set of algorithms
|
||||
used by Tor and the set of algorithms available in the engine */
|
||||
log_engine("RSA", ENGINE_get_default_RSA());
|
||||
log_engine("DH", ENGINE_get_default_DH());
|
||||
#ifdef OPENSSL_1_1_API
|
||||
log_engine("EC", ENGINE_get_default_EC());
|
||||
#else
|
||||
log_engine("ECDH", ENGINE_get_default_ECDH());
|
||||
log_engine("ECDSA", ENGINE_get_default_ECDSA());
|
||||
#endif /* defined(OPENSSL_1_1_API) */
|
||||
log_engine("RAND", ENGINE_get_default_RAND());
|
||||
log_engine("RAND (which we will not use)", ENGINE_get_default_RAND());
|
||||
log_engine("SHA1", ENGINE_get_digest_engine(NID_sha1));
|
||||
log_engine("3DES-CBC", ENGINE_get_cipher_engine(NID_des_ede3_cbc));
|
||||
log_engine("AES-128-ECB", ENGINE_get_cipher_engine(NID_aes_128_ecb));
|
||||
log_engine("AES-128-CBC", ENGINE_get_cipher_engine(NID_aes_128_cbc));
|
||||
#ifdef NID_aes_128_ctr
|
||||
log_engine("AES-128-CTR", ENGINE_get_cipher_engine(NID_aes_128_ctr));
|
||||
#endif
|
||||
#ifdef NID_aes_128_gcm
|
||||
log_engine("AES-128-GCM", ENGINE_get_cipher_engine(NID_aes_128_gcm));
|
||||
#endif
|
||||
log_engine("AES-256-CBC", ENGINE_get_cipher_engine(NID_aes_256_cbc));
|
||||
#ifdef NID_aes_256_gcm
|
||||
log_engine("AES-256-GCM", ENGINE_get_cipher_engine(NID_aes_256_gcm));
|
||||
#endif
|
||||
|
||||
#endif /* defined(DISABLE_ENGINES) */
|
||||
} else {
|
||||
log_info(LD_CRYPTO, "NOT using OpenSSL engine support.");
|
||||
}
|
||||
|
||||
if (crypto_force_rand_ssleay()) {
|
||||
if (crypto_seed_rng() < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
evaluate_evp_for_aes(-1);
|
||||
evaluate_ctr_for_aes();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Free crypto resources held by this thread. */
|
||||
void
|
||||
crypto_thread_cleanup(void)
|
||||
{
|
||||
#ifndef NEW_THREAD_API
|
||||
ERR_remove_thread_state(NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Allocate and return a new symmetric cipher using the provided key and iv.
|
||||
* The key is <b>bits</b> bits long; the IV is CIPHER_IV_LEN bytes. Both
|
||||
* must be provided. Key length must be 128, 192, or 256 */
|
||||
crypto_cipher_t *
|
||||
crypto_cipher_new_with_iv_and_bits(const uint8_t *key,
|
||||
const uint8_t *iv,
|
||||
int bits)
|
||||
{
|
||||
tor_assert(key);
|
||||
tor_assert(iv);
|
||||
|
||||
return aes_new_cipher((const uint8_t*)key, (const uint8_t*)iv, bits);
|
||||
}
|
||||
|
||||
/** Allocate and return a new symmetric cipher using the provided key and iv.
|
||||
* The key is CIPHER_KEY_LEN bytes; the IV is CIPHER_IV_LEN bytes. Both
|
||||
* must be provided.
|
||||
*/
|
||||
crypto_cipher_t *
|
||||
crypto_cipher_new_with_iv(const char *key, const char *iv)
|
||||
{
|
||||
return crypto_cipher_new_with_iv_and_bits((uint8_t*)key, (uint8_t*)iv,
|
||||
128);
|
||||
}
|
||||
|
||||
/** Return a new crypto_cipher_t with the provided <b>key</b> and an IV of all
|
||||
* zero bytes and key length <b>bits</b>. Key length must be 128, 192, or
|
||||
* 256. */
|
||||
crypto_cipher_t *
|
||||
crypto_cipher_new_with_bits(const char *key, int bits)
|
||||
{
|
||||
char zeroiv[CIPHER_IV_LEN];
|
||||
memset(zeroiv, 0, sizeof(zeroiv));
|
||||
return crypto_cipher_new_with_iv_and_bits((uint8_t*)key, (uint8_t*)zeroiv,
|
||||
bits);
|
||||
}
|
||||
|
||||
/** Return a new crypto_cipher_t with the provided <b>key</b> (of
|
||||
* CIPHER_KEY_LEN bytes) and an IV of all zero bytes. */
|
||||
crypto_cipher_t *
|
||||
crypto_cipher_new(const char *key)
|
||||
{
|
||||
return crypto_cipher_new_with_bits(key, 128);
|
||||
}
|
||||
|
||||
/** Free a symmetric cipher.
|
||||
*/
|
||||
void
|
||||
crypto_cipher_free_(crypto_cipher_t *env)
|
||||
{
|
||||
if (!env)
|
||||
return;
|
||||
|
||||
aes_cipher_free(env);
|
||||
}
|
||||
|
||||
/** Copy <b>in</b> to the <b>outlen</b>-byte buffer <b>out</b>, adding spaces
|
||||
* every four characters. */
|
||||
void
|
||||
crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in)
|
||||
{
|
||||
int n = 0;
|
||||
char *end = out+outlen;
|
||||
tor_assert(outlen < SIZE_T_CEILING);
|
||||
|
||||
while (*in && out<end) {
|
||||
*out++ = *in++;
|
||||
if (++n == 4 && *in && out<end) {
|
||||
n = 0;
|
||||
*out++ = ' ';
|
||||
}
|
||||
}
|
||||
tor_assert(out<end);
|
||||
*out = '\0';
|
||||
}
|
||||
|
||||
/* symmetric crypto */
|
||||
|
||||
/** Encrypt <b>fromlen</b> bytes from <b>from</b> using the cipher
|
||||
* <b>env</b>; on success, store the result to <b>to</b> and return 0.
|
||||
* Does not check for failure.
|
||||
*/
|
||||
int
|
||||
crypto_cipher_encrypt(crypto_cipher_t *env, char *to,
|
||||
const char *from, size_t fromlen)
|
||||
{
|
||||
tor_assert(env);
|
||||
tor_assert(env);
|
||||
tor_assert(from);
|
||||
tor_assert(fromlen);
|
||||
tor_assert(to);
|
||||
tor_assert(fromlen < SIZE_T_CEILING);
|
||||
|
||||
memcpy(to, from, fromlen);
|
||||
aes_crypt_inplace(env, to, fromlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Decrypt <b>fromlen</b> bytes from <b>from</b> using the cipher
|
||||
* <b>env</b>; on success, store the result to <b>to</b> and return 0.
|
||||
* Does not check for failure.
|
||||
*/
|
||||
int
|
||||
crypto_cipher_decrypt(crypto_cipher_t *env, char *to,
|
||||
const char *from, size_t fromlen)
|
||||
{
|
||||
tor_assert(env);
|
||||
tor_assert(from);
|
||||
tor_assert(to);
|
||||
tor_assert(fromlen < SIZE_T_CEILING);
|
||||
|
||||
memcpy(to, from, fromlen);
|
||||
aes_crypt_inplace(env, to, fromlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Encrypt <b>len</b> bytes on <b>from</b> using the cipher in <b>env</b>;
|
||||
* on success. Does not check for failure.
|
||||
*/
|
||||
void
|
||||
crypto_cipher_crypt_inplace(crypto_cipher_t *env, char *buf, size_t len)
|
||||
{
|
||||
tor_assert(len < SIZE_T_CEILING);
|
||||
aes_crypt_inplace(env, buf, len);
|
||||
}
|
||||
|
||||
/** Encrypt <b>fromlen</b> bytes (at least 1) from <b>from</b> with the key in
|
||||
* <b>key</b> to the buffer in <b>to</b> of length
|
||||
* <b>tolen</b>. <b>tolen</b> must be at least <b>fromlen</b> plus
|
||||
* CIPHER_IV_LEN bytes for the initialization vector. On success, return the
|
||||
* number of bytes written, on failure, return -1.
|
||||
*/
|
||||
int
|
||||
crypto_cipher_encrypt_with_iv(const char *key,
|
||||
char *to, size_t tolen,
|
||||
const char *from, size_t fromlen)
|
||||
{
|
||||
crypto_cipher_t *cipher;
|
||||
tor_assert(from);
|
||||
tor_assert(to);
|
||||
tor_assert(fromlen < INT_MAX);
|
||||
|
||||
if (fromlen < 1)
|
||||
return -1;
|
||||
if (tolen < fromlen + CIPHER_IV_LEN)
|
||||
return -1;
|
||||
|
||||
char iv[CIPHER_IV_LEN];
|
||||
crypto_rand(iv, sizeof(iv));
|
||||
cipher = crypto_cipher_new_with_iv(key, iv);
|
||||
|
||||
memcpy(to, iv, CIPHER_IV_LEN);
|
||||
crypto_cipher_encrypt(cipher, to+CIPHER_IV_LEN, from, fromlen);
|
||||
crypto_cipher_free(cipher);
|
||||
memwipe(iv, 0, sizeof(iv));
|
||||
return (int)(fromlen + CIPHER_IV_LEN);
|
||||
}
|
||||
|
||||
/** Decrypt <b>fromlen</b> bytes (at least 1+CIPHER_IV_LEN) from <b>from</b>
|
||||
* with the key in <b>key</b> to the buffer in <b>to</b> of length
|
||||
* <b>tolen</b>. <b>tolen</b> must be at least <b>fromlen</b> minus
|
||||
* CIPHER_IV_LEN bytes for the initialization vector. On success, return the
|
||||
* number of bytes written, on failure, return -1.
|
||||
*/
|
||||
int
|
||||
crypto_cipher_decrypt_with_iv(const char *key,
|
||||
char *to, size_t tolen,
|
||||
const char *from, size_t fromlen)
|
||||
{
|
||||
crypto_cipher_t *cipher;
|
||||
tor_assert(key);
|
||||
tor_assert(from);
|
||||
tor_assert(to);
|
||||
tor_assert(fromlen < INT_MAX);
|
||||
|
||||
if (fromlen <= CIPHER_IV_LEN)
|
||||
return -1;
|
||||
if (tolen < fromlen - CIPHER_IV_LEN)
|
||||
return -1;
|
||||
|
||||
cipher = crypto_cipher_new_with_iv(key, from);
|
||||
|
||||
crypto_cipher_encrypt(cipher, to, from+CIPHER_IV_LEN, fromlen-CIPHER_IV_LEN);
|
||||
crypto_cipher_free(cipher);
|
||||
return (int)(fromlen - CIPHER_IV_LEN);
|
||||
}
|
||||
|
||||
/** @{ */
|
||||
/** Uninitialize the crypto library. Return 0 on success. Does not detect
|
||||
* failure.
|
||||
*/
|
||||
int
|
||||
crypto_global_cleanup(void)
|
||||
{
|
||||
#ifndef OPENSSL_1_1_API
|
||||
EVP_cleanup();
|
||||
#endif
|
||||
#ifndef NEW_THREAD_API
|
||||
ERR_remove_thread_state(NULL);
|
||||
#endif
|
||||
#ifndef OPENSSL_1_1_API
|
||||
ERR_free_strings();
|
||||
#endif
|
||||
|
||||
crypto_dh_free_all();
|
||||
|
||||
#ifndef DISABLE_ENGINES
|
||||
#ifndef OPENSSL_1_1_API
|
||||
ENGINE_cleanup();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
CONF_modules_unload(1);
|
||||
#ifndef OPENSSL_1_1_API
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
#endif
|
||||
|
||||
crypto_openssl_free_all();
|
||||
|
||||
crypto_early_initialized_ = 0;
|
||||
crypto_global_initialized_ = 0;
|
||||
have_seeded_siphash = 0;
|
||||
siphash_unset_global_key();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,190 @@
|
||||
/* Copyright (c) 2001, Matej Pfajfar.
|
||||
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file crypto_cipher.c
|
||||
* \brief Symmetric cryptography (low-level) with AES.
|
||||
**/
|
||||
|
||||
#include "orconfig.h"
|
||||
|
||||
#include "lib/crypt_ops/crypto_cipher.h"
|
||||
#include "lib/crypt_ops/crypto_rand.h"
|
||||
#include "lib/crypt_ops/crypto_util.h"
|
||||
|
||||
#include "lib/log/log.h"
|
||||
#include "lib/log/util_bug.h"
|
||||
#include "lib/cc/torint.h"
|
||||
#include "lib/crypt_ops/aes.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/** Allocate and return a new symmetric cipher using the provided key and iv.
|
||||
* The key is <b>bits</b> bits long; the IV is CIPHER_IV_LEN bytes. Both
|
||||
* must be provided. Key length must be 128, 192, or 256 */
|
||||
crypto_cipher_t *
|
||||
crypto_cipher_new_with_iv_and_bits(const uint8_t *key,
|
||||
const uint8_t *iv,
|
||||
int bits)
|
||||
{
|
||||
tor_assert(key);
|
||||
tor_assert(iv);
|
||||
|
||||
return aes_new_cipher((const uint8_t*)key, (const uint8_t*)iv, bits);
|
||||
}
|
||||
|
||||
/** Allocate and return a new symmetric cipher using the provided key and iv.
|
||||
* The key is CIPHER_KEY_LEN bytes; the IV is CIPHER_IV_LEN bytes. Both
|
||||
* must be provided.
|
||||
*/
|
||||
crypto_cipher_t *
|
||||
crypto_cipher_new_with_iv(const char *key, const char *iv)
|
||||
{
|
||||
return crypto_cipher_new_with_iv_and_bits((uint8_t*)key, (uint8_t*)iv,
|
||||
128);
|
||||
}
|
||||
|
||||
/** Return a new crypto_cipher_t with the provided <b>key</b> and an IV of all
|
||||
* zero bytes and key length <b>bits</b>. Key length must be 128, 192, or
|
||||
* 256. */
|
||||
crypto_cipher_t *
|
||||
crypto_cipher_new_with_bits(const char *key, int bits)
|
||||
{
|
||||
char zeroiv[CIPHER_IV_LEN];
|
||||
memset(zeroiv, 0, sizeof(zeroiv));
|
||||
return crypto_cipher_new_with_iv_and_bits((uint8_t*)key, (uint8_t*)zeroiv,
|
||||
bits);
|
||||
}
|
||||
|
||||
/** Return a new crypto_cipher_t with the provided <b>key</b> (of
|
||||
* CIPHER_KEY_LEN bytes) and an IV of all zero bytes. */
|
||||
crypto_cipher_t *
|
||||
crypto_cipher_new(const char *key)
|
||||
{
|
||||
return crypto_cipher_new_with_bits(key, 128);
|
||||
}
|
||||
|
||||
/** Free a symmetric cipher.
|
||||
*/
|
||||
void
|
||||
crypto_cipher_free_(crypto_cipher_t *env)
|
||||
{
|
||||
if (!env)
|
||||
return;
|
||||
|
||||
aes_cipher_free(env);
|
||||
}
|
||||
|
||||
/* symmetric crypto */
|
||||
|
||||
/** Encrypt <b>fromlen</b> bytes from <b>from</b> using the cipher
|
||||
* <b>env</b>; on success, store the result to <b>to</b> and return 0.
|
||||
* Does not check for failure.
|
||||
*/
|
||||
int
|
||||
crypto_cipher_encrypt(crypto_cipher_t *env, char *to,
|
||||
const char *from, size_t fromlen)
|
||||
{
|
||||
tor_assert(env);
|
||||
tor_assert(env);
|
||||
tor_assert(from);
|
||||
tor_assert(fromlen);
|
||||
tor_assert(to);
|
||||
tor_assert(fromlen < SIZE_T_CEILING);
|
||||
|
||||
memcpy(to, from, fromlen);
|
||||
aes_crypt_inplace(env, to, fromlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Decrypt <b>fromlen</b> bytes from <b>from</b> using the cipher
|
||||
* <b>env</b>; on success, store the result to <b>to</b> and return 0.
|
||||
* Does not check for failure.
|
||||
*/
|
||||
int
|
||||
crypto_cipher_decrypt(crypto_cipher_t *env, char *to,
|
||||
const char *from, size_t fromlen)
|
||||
{
|
||||
tor_assert(env);
|
||||
tor_assert(from);
|
||||
tor_assert(to);
|
||||
tor_assert(fromlen < SIZE_T_CEILING);
|
||||
|
||||
memcpy(to, from, fromlen);
|
||||
aes_crypt_inplace(env, to, fromlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Encrypt <b>len</b> bytes on <b>from</b> using the cipher in <b>env</b>;
|
||||
* on success. Does not check for failure.
|
||||
*/
|
||||
void
|
||||
crypto_cipher_crypt_inplace(crypto_cipher_t *env, char *buf, size_t len)
|
||||
{
|
||||
tor_assert(len < SIZE_T_CEILING);
|
||||
aes_crypt_inplace(env, buf, len);
|
||||
}
|
||||
|
||||
/** Encrypt <b>fromlen</b> bytes (at least 1) from <b>from</b> with the key in
|
||||
* <b>key</b> to the buffer in <b>to</b> of length
|
||||
* <b>tolen</b>. <b>tolen</b> must be at least <b>fromlen</b> plus
|
||||
* CIPHER_IV_LEN bytes for the initialization vector. On success, return the
|
||||
* number of bytes written, on failure, return -1.
|
||||
*/
|
||||
int
|
||||
crypto_cipher_encrypt_with_iv(const char *key,
|
||||
char *to, size_t tolen,
|
||||
const char *from, size_t fromlen)
|
||||
{
|
||||
crypto_cipher_t *cipher;
|
||||
tor_assert(from);
|
||||
tor_assert(to);
|
||||
tor_assert(fromlen < INT_MAX);
|
||||
|
||||
if (fromlen < 1)
|
||||
return -1;
|
||||
if (tolen < fromlen + CIPHER_IV_LEN)
|
||||
return -1;
|
||||
|
||||
char iv[CIPHER_IV_LEN];
|
||||
crypto_rand(iv, sizeof(iv));
|
||||
cipher = crypto_cipher_new_with_iv(key, iv);
|
||||
|
||||
memcpy(to, iv, CIPHER_IV_LEN);
|
||||
crypto_cipher_encrypt(cipher, to+CIPHER_IV_LEN, from, fromlen);
|
||||
crypto_cipher_free(cipher);
|
||||
memwipe(iv, 0, sizeof(iv));
|
||||
return (int)(fromlen + CIPHER_IV_LEN);
|
||||
}
|
||||
|
||||
/** Decrypt <b>fromlen</b> bytes (at least 1+CIPHER_IV_LEN) from <b>from</b>
|
||||
* with the key in <b>key</b> to the buffer in <b>to</b> of length
|
||||
* <b>tolen</b>. <b>tolen</b> must be at least <b>fromlen</b> minus
|
||||
* CIPHER_IV_LEN bytes for the initialization vector. On success, return the
|
||||
* number of bytes written, on failure, return -1.
|
||||
*/
|
||||
int
|
||||
crypto_cipher_decrypt_with_iv(const char *key,
|
||||
char *to, size_t tolen,
|
||||
const char *from, size_t fromlen)
|
||||
{
|
||||
crypto_cipher_t *cipher;
|
||||
tor_assert(key);
|
||||
tor_assert(from);
|
||||
tor_assert(to);
|
||||
tor_assert(fromlen < INT_MAX);
|
||||
|
||||
if (fromlen <= CIPHER_IV_LEN)
|
||||
return -1;
|
||||
if (tolen < fromlen - CIPHER_IV_LEN)
|
||||
return -1;
|
||||
|
||||
cipher = crypto_cipher_new_with_iv(key, from);
|
||||
|
||||
crypto_cipher_encrypt(cipher, to, from+CIPHER_IV_LEN, fromlen-CIPHER_IV_LEN);
|
||||
crypto_cipher_free(cipher);
|
||||
return (int)(fromlen - CIPHER_IV_LEN);
|
||||
}
|
||||
@@ -5,19 +5,18 @@
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file crypto.h
|
||||
* \file crypto_cipher.h
|
||||
*
|
||||
* \brief Headers for crypto.c
|
||||
* \brief Headers for crypto_cipher.c
|
||||
**/
|
||||
|
||||
#ifndef TOR_CRYPTO_H
|
||||
#define TOR_CRYPTO_H
|
||||
#ifndef TOR_CRYPTO_CIPHER_H
|
||||
#define TOR_CRYPTO_CIPHER_H
|
||||
|
||||
#include "orconfig.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "lib/cc/torint.h"
|
||||
#include "lib/crypt_ops/crypto_rsa.h"
|
||||
|
||||
/** Length of our symmetric cipher's keys of 128-bit. */
|
||||
#define CIPHER_KEY_LEN 16
|
||||
@@ -26,22 +25,8 @@
|
||||
/** Length of our symmetric cipher's keys of 256-bit. */
|
||||
#define CIPHER256_KEY_LEN 32
|
||||
|
||||
/** Length of encoded public key fingerprints, including space; but not
|
||||
* including terminating NUL. */
|
||||
#define FINGERPRINT_LEN 49
|
||||
|
||||
typedef struct aes_cnt_cipher crypto_cipher_t;
|
||||
|
||||
/* global state */
|
||||
int crypto_init_siphash_key(void);
|
||||
int crypto_early_init(void) ATTR_WUR;
|
||||
int crypto_global_init(int hardwareAccel,
|
||||
const char *accelName,
|
||||
const char *accelPath) ATTR_WUR;
|
||||
|
||||
void crypto_thread_cleanup(void);
|
||||
int crypto_global_cleanup(void);
|
||||
|
||||
/* environment setup */
|
||||
crypto_cipher_t *crypto_cipher_new(const char *key);
|
||||
crypto_cipher_t *crypto_cipher_new_with_bits(const char *key, int bits);
|
||||
@@ -69,6 +54,4 @@ int crypto_cipher_decrypt_with_iv(const char *key,
|
||||
char *to, size_t tolen,
|
||||
const char *from, size_t fromlen);
|
||||
|
||||
void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in);
|
||||
|
||||
#endif /* !defined(TOR_CRYPTO_H) */
|
||||
+43
-441
@@ -7,6 +7,8 @@
|
||||
/**
|
||||
* \file crypto_dh.c
|
||||
* \brief Block of functions related with DH utilities and operations.
|
||||
* over Z_p. We aren't using this for any new crypto -- EC is more
|
||||
* efficient.
|
||||
**/
|
||||
|
||||
#include "lib/crypt_ops/compat_openssl.h"
|
||||
@@ -17,411 +19,50 @@
|
||||
#include "lib/log/log.h"
|
||||
#include "lib/log/util_bug.h"
|
||||
|
||||
DISABLE_GCC_WARNING(redundant-decls)
|
||||
|
||||
#include <openssl/dh.h>
|
||||
|
||||
ENABLE_GCC_WARNING(redundant-decls)
|
||||
|
||||
#include <openssl/bn.h>
|
||||
#include <string.h>
|
||||
|
||||
/** A structure to hold the first half (x, g^x) of a Diffie-Hellman handshake
|
||||
* while we're waiting for the second.*/
|
||||
struct crypto_dh_t {
|
||||
DH *dh; /**< The openssl DH object */
|
||||
};
|
||||
|
||||
static int tor_check_dh_key(int severity, const BIGNUM *bn);
|
||||
|
||||
/** Used by tortls.c: Get the DH* from a crypto_dh_t.
|
||||
*/
|
||||
DH *
|
||||
crypto_dh_get_dh_(crypto_dh_t *dh)
|
||||
{
|
||||
return dh->dh;
|
||||
}
|
||||
|
||||
/** Our DH 'g' parameter */
|
||||
#define DH_GENERATOR 2
|
||||
|
||||
/** Shared P parameter for our circuit-crypto DH key exchanges. */
|
||||
static BIGNUM *dh_param_p = NULL;
|
||||
/** Shared P parameter for our TLS DH key exchanges. */
|
||||
static BIGNUM *dh_param_p_tls = NULL;
|
||||
/** Shared G parameter for our DH key exchanges. */
|
||||
static BIGNUM *dh_param_g = NULL;
|
||||
|
||||
/** Validate a given set of Diffie-Hellman parameters. This is moderately
|
||||
* computationally expensive (milliseconds), so should only be called when
|
||||
* the DH parameters change. Returns 0 on success, * -1 on failure.
|
||||
const unsigned DH_GENERATOR = 2;
|
||||
/** This is the 1024-bit safe prime that Apache uses for its DH stuff; see
|
||||
* modules/ssl/ssl_engine_dh.c; Apache also uses a generator of 2 with this
|
||||
* prime.
|
||||
*/
|
||||
static int
|
||||
crypto_validate_dh_params(const BIGNUM *p, const BIGNUM *g)
|
||||
{
|
||||
DH *dh = NULL;
|
||||
int ret = -1;
|
||||
|
||||
/* Copy into a temporary DH object, just so that DH_check() can be called. */
|
||||
if (!(dh = DH_new()))
|
||||
goto out;
|
||||
#ifdef OPENSSL_1_1_API
|
||||
BIGNUM *dh_p, *dh_g;
|
||||
if (!(dh_p = BN_dup(p)))
|
||||
goto out;
|
||||
if (!(dh_g = BN_dup(g)))
|
||||
goto out;
|
||||
if (!DH_set0_pqg(dh, dh_p, NULL, dh_g))
|
||||
goto out;
|
||||
#else /* !(defined(OPENSSL_1_1_API)) */
|
||||
if (!(dh->p = BN_dup(p)))
|
||||
goto out;
|
||||
if (!(dh->g = BN_dup(g)))
|
||||
goto out;
|
||||
#endif /* defined(OPENSSL_1_1_API) */
|
||||
|
||||
/* Perform the validation. */
|
||||
int codes = 0;
|
||||
if (!DH_check(dh, &codes))
|
||||
goto out;
|
||||
if (BN_is_word(g, DH_GENERATOR_2)) {
|
||||
/* Per https://wiki.openssl.org/index.php/Diffie-Hellman_parameters
|
||||
*
|
||||
* OpenSSL checks the prime is congruent to 11 when g = 2; while the
|
||||
* IETF's primes are congruent to 23 when g = 2.
|
||||
*/
|
||||
BN_ULONG residue = BN_mod_word(p, 24);
|
||||
if (residue == 11 || residue == 23)
|
||||
codes &= ~DH_NOT_SUITABLE_GENERATOR;
|
||||
}
|
||||
if (codes != 0) /* Specifics on why the params suck is irrelevant. */
|
||||
goto out;
|
||||
|
||||
/* Things are probably not evil. */
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
if (dh)
|
||||
DH_free(dh);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Set the global Diffie-Hellman generator, used for both TLS and internal
|
||||
* DH stuff.
|
||||
const char TLS_DH_PRIME[] =
|
||||
"D67DE440CBBBDC1936D693D34AFD0AD50C84D239A45F520BB88174CB98"
|
||||
"BCE951849F912E639C72FB13B4B4D7177E16D55AC179BA420B2A29FE324A"
|
||||
"467A635E81FF5901377BEDDCFD33168A461AAD3B72DAE8860078045B07A7"
|
||||
"DBCA7874087D1510EA9FCC9DDD330507DD62DB88AEAA747DE0F4D6E2BD68"
|
||||
"B0E7393E0F24218EB3";
|
||||
/**
|
||||
* This is from rfc2409, section 6.2. It's a safe prime, and
|
||||
* supposedly it equals:
|
||||
* 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }.
|
||||
*/
|
||||
static void
|
||||
crypto_set_dh_generator(void)
|
||||
{
|
||||
BIGNUM *generator;
|
||||
int r;
|
||||
const char OAKLEY_PRIME_2[] =
|
||||
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
|
||||
"8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
|
||||
"302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
|
||||
"A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
|
||||
"49286651ECE65381FFFFFFFFFFFFFFFF";
|
||||
|
||||
if (dh_param_g)
|
||||
return;
|
||||
|
||||
generator = BN_new();
|
||||
tor_assert(generator);
|
||||
|
||||
r = BN_set_word(generator, DH_GENERATOR);
|
||||
tor_assert(r);
|
||||
|
||||
dh_param_g = generator;
|
||||
}
|
||||
|
||||
/** Set the global TLS Diffie-Hellman modulus. Use the Apache mod_ssl DH
|
||||
* modulus. */
|
||||
void
|
||||
crypto_set_tls_dh_prime(void)
|
||||
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 <b>dh</b>, sharing its internal state. */
|
||||
crypto_dh_t *
|
||||
crypto_dh_dup(const crypto_dh_t *dh)
|
||||
{
|
||||
crypto_dh_t *dh_new = tor_malloc_zero(sizeof(crypto_dh_t));
|
||||
tor_assert(dh);
|
||||
tor_assert(dh->dh);
|
||||
dh_new->dh = dh->dh;
|
||||
DH_up_ref(dh->dh);
|
||||
return dh_new;
|
||||
}
|
||||
|
||||
/** Return the length of the DH key in <b>dh</b>, in bytes.
|
||||
*/
|
||||
int
|
||||
crypto_dh_get_bytes(crypto_dh_t *dh)
|
||||
{
|
||||
tor_assert(dh);
|
||||
return DH_size(dh->dh);
|
||||
}
|
||||
|
||||
/** Generate \<x,g^x\> for our part of the key exchange. Return 0 on
|
||||
* success, -1 on failure.
|
||||
*/
|
||||
int
|
||||
crypto_dh_generate_public(crypto_dh_t *dh)
|
||||
{
|
||||
#ifndef OPENSSL_1_1_API
|
||||
again:
|
||||
#ifdef ENABLE_OPENSSL
|
||||
crypto_dh_init_openssl();
|
||||
#endif
|
||||
if (!DH_generate_key(dh->dh)) {
|
||||
/* LCOV_EXCL_START
|
||||
* To test this we would need some way to tell openssl to break DH. */
|
||||
crypto_log_errors(LOG_WARN, "generating DH key");
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
#ifdef OPENSSL_1_1_API
|
||||
/* OpenSSL 1.1.x doesn't appear to let you regenerate a DH key, without
|
||||
* recreating the DH object. I have no idea what sort of aliasing madness
|
||||
* can occur here, so do the check, and just bail on failure.
|
||||
*/
|
||||
const BIGNUM *pub_key, *priv_key;
|
||||
DH_get0_key(dh->dh, &pub_key, &priv_key);
|
||||
if (tor_check_dh_key(LOG_WARN, pub_key)<0) {
|
||||
log_warn(LD_CRYPTO, "Weird! Our own DH key was invalid. I guess once-in-"
|
||||
"the-universe chances really do happen. Treating as a failure.");
|
||||
return -1;
|
||||
}
|
||||
#else /* !(defined(OPENSSL_1_1_API)) */
|
||||
if (tor_check_dh_key(LOG_WARN, dh->dh->pub_key)<0) {
|
||||
/* LCOV_EXCL_START
|
||||
* If this happens, then openssl's DH implementation is busted. */
|
||||
log_warn(LD_CRYPTO, "Weird! Our own DH key was invalid. I guess once-in-"
|
||||
"the-universe chances really do happen. Trying again.");
|
||||
/* Free and clear the keys, so OpenSSL will actually try again. */
|
||||
BN_clear_free(dh->dh->pub_key);
|
||||
BN_clear_free(dh->dh->priv_key);
|
||||
dh->dh->pub_key = dh->dh->priv_key = NULL;
|
||||
goto again;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
#endif /* defined(OPENSSL_1_1_API) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Generate g^x as necessary, and write the g^x for the key exchange
|
||||
* as a <b>pubkey_len</b>-byte value into <b>pubkey</b>. Return 0 on
|
||||
* success, -1 on failure. <b>pubkey_len</b> must be \>= DH1024_KEY_LEN.
|
||||
*/
|
||||
int
|
||||
crypto_dh_get_public(crypto_dh_t *dh, char *pubkey, size_t pubkey_len)
|
||||
{
|
||||
int bytes;
|
||||
tor_assert(dh);
|
||||
|
||||
const BIGNUM *dh_pub;
|
||||
|
||||
#ifdef OPENSSL_1_1_API
|
||||
const BIGNUM *dh_priv;
|
||||
DH_get0_key(dh->dh, &dh_pub, &dh_priv);
|
||||
#else
|
||||
dh_pub = dh->dh->pub_key;
|
||||
#endif /* defined(OPENSSL_1_1_API) */
|
||||
|
||||
if (!dh_pub) {
|
||||
if (crypto_dh_generate_public(dh)<0)
|
||||
return -1;
|
||||
else {
|
||||
#ifdef OPENSSL_1_1_API
|
||||
DH_get0_key(dh->dh, &dh_pub, &dh_priv);
|
||||
#else
|
||||
dh_pub = dh->dh->pub_key;
|
||||
#ifdef ENABLE_NSS
|
||||
crypto_dh_init_nss();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
tor_assert(dh_pub);
|
||||
bytes = BN_num_bytes(dh_pub);
|
||||
tor_assert(bytes >= 0);
|
||||
if (pubkey_len < (size_t)bytes) {
|
||||
log_warn(LD_CRYPTO,
|
||||
"Weird! pubkey_len (%d) was smaller than DH1024_KEY_LEN (%d)",
|
||||
(int) pubkey_len, bytes);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(pubkey, 0, pubkey_len);
|
||||
BN_bn2bin(dh_pub, (unsigned char*)(pubkey+(pubkey_len-bytes)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Check for bad Diffie-Hellman public keys (g^x). Return 0 if the key is
|
||||
* okay (in the subgroup [2,p-2]), or -1 if it's bad.
|
||||
* See http://www.cl.cam.ac.uk/ftp/users/rja14/psandqs.ps.gz for some tips.
|
||||
*/
|
||||
static int
|
||||
tor_check_dh_key(int severity, const BIGNUM *bn)
|
||||
void
|
||||
crypto_dh_free_all(void)
|
||||
{
|
||||
BIGNUM *x;
|
||||
char *s;
|
||||
tor_assert(bn);
|
||||
x = BN_new();
|
||||
tor_assert(x);
|
||||
if (BUG(!dh_param_p))
|
||||
init_dh_param(); //LCOV_EXCL_LINE we already checked whether we did this.
|
||||
BN_set_word(x, 1);
|
||||
if (BN_cmp(bn,x)<=0) {
|
||||
log_fn(severity, LD_CRYPTO, "DH key must be at least 2.");
|
||||
goto err;
|
||||
}
|
||||
BN_copy(x,dh_param_p);
|
||||
BN_sub_word(x, 1);
|
||||
if (BN_cmp(bn,x)>=0) {
|
||||
log_fn(severity, LD_CRYPTO, "DH key must be at most p-2.");
|
||||
goto err;
|
||||
}
|
||||
BN_clear_free(x);
|
||||
return 0;
|
||||
err:
|
||||
BN_clear_free(x);
|
||||
s = BN_bn2hex(bn);
|
||||
log_fn(severity, LD_CRYPTO, "Rejecting insecure DH key [%s]", s);
|
||||
OPENSSL_free(s);
|
||||
return -1;
|
||||
#ifdef ENABLE_OPENSSL
|
||||
crypto_dh_free_all_openssl();
|
||||
#endif
|
||||
#ifdef ENABLE_NSS
|
||||
crypto_dh_free_all_nss();
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Given a DH key exchange object, and our peer's value of g^y (as a
|
||||
@@ -439,31 +80,20 @@ crypto_dh_compute_secret(int severity, crypto_dh_t *dh,
|
||||
const char *pubkey, size_t pubkey_len,
|
||||
char *secret_out, size_t secret_bytes_out)
|
||||
{
|
||||
char *secret_tmp = NULL;
|
||||
BIGNUM *pubkey_bn = NULL;
|
||||
size_t secret_len=0, secret_tmp_len=0;
|
||||
int result=0;
|
||||
tor_assert(dh);
|
||||
tor_assert(secret_bytes_out/DIGEST_LEN <= 255);
|
||||
tor_assert(pubkey_len < INT_MAX);
|
||||
|
||||
if (!(pubkey_bn = BN_bin2bn((const unsigned char*)pubkey,
|
||||
(int)pubkey_len, NULL)))
|
||||
goto error;
|
||||
if (tor_check_dh_key(severity, pubkey_bn)<0) {
|
||||
/* Check for invalid public keys. */
|
||||
log_fn(severity, LD_CRYPTO,"Rejected invalid g^x");
|
||||
goto error;
|
||||
}
|
||||
unsigned char *secret_tmp = NULL;
|
||||
size_t secret_len=0, secret_tmp_len=0;
|
||||
secret_tmp_len = crypto_dh_get_bytes(dh);
|
||||
secret_tmp = tor_malloc(secret_tmp_len);
|
||||
result = DH_compute_key((unsigned char*)secret_tmp, pubkey_bn, dh->dh);
|
||||
if (result < 0) {
|
||||
log_warn(LD_CRYPTO,"DH_compute_key() failed.");
|
||||
|
||||
ssize_t result = crypto_dh_handshake(severity, dh, pubkey, pubkey_len,
|
||||
secret_tmp, secret_tmp_len);
|
||||
if (result < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
secret_len = result;
|
||||
if (crypto_expand_key_material_TAP((uint8_t*)secret_tmp, secret_len,
|
||||
if (crypto_expand_key_material_TAP(secret_tmp, secret_len,
|
||||
(uint8_t*)secret_out, secret_bytes_out)<0)
|
||||
goto error;
|
||||
secret_len = secret_bytes_out;
|
||||
@@ -472,9 +102,6 @@ crypto_dh_compute_secret(int severity, crypto_dh_t *dh,
|
||||
error:
|
||||
result = -1;
|
||||
done:
|
||||
crypto_log_errors(LOG_WARN, "completing DH handshake");
|
||||
if (pubkey_bn)
|
||||
BN_clear_free(pubkey_bn);
|
||||
if (secret_tmp) {
|
||||
memwipe(secret_tmp, 0, secret_tmp_len);
|
||||
tor_free(secret_tmp);
|
||||
@@ -484,28 +111,3 @@ crypto_dh_compute_secret(int severity, crypto_dh_t *dh,
|
||||
else
|
||||
return secret_len;
|
||||
}
|
||||
|
||||
/** Free a DH key exchange object.
|
||||
*/
|
||||
void
|
||||
crypto_dh_free_(crypto_dh_t *dh)
|
||||
{
|
||||
if (!dh)
|
||||
return;
|
||||
tor_assert(dh->dh);
|
||||
DH_free(dh->dh);
|
||||
tor_free(dh);
|
||||
}
|
||||
|
||||
void
|
||||
crypto_dh_free_all(void)
|
||||
{
|
||||
if (dh_param_p)
|
||||
BN_clear_free(dh_param_p);
|
||||
if (dh_param_p_tls)
|
||||
BN_clear_free(dh_param_p_tls);
|
||||
if (dh_param_g)
|
||||
BN_clear_free(dh_param_g);
|
||||
|
||||
dh_param_p = dh_param_p_tls = dh_param_g = NULL;
|
||||
}
|
||||
|
||||
@@ -19,11 +19,15 @@
|
||||
|
||||
typedef struct crypto_dh_t crypto_dh_t;
|
||||
|
||||
extern const unsigned DH_GENERATOR;
|
||||
extern const char TLS_DH_PRIME[];
|
||||
extern const char OAKLEY_PRIME_2[];
|
||||
|
||||
/* Key negotiation */
|
||||
#define DH_TYPE_CIRCUIT 1
|
||||
#define DH_TYPE_REND 2
|
||||
#define DH_TYPE_TLS 3
|
||||
void crypto_set_tls_dh_prime(void);
|
||||
void crypto_dh_init(void);
|
||||
crypto_dh_t *crypto_dh_new(int dh_type);
|
||||
crypto_dh_t *crypto_dh_dup(const crypto_dh_t *dh);
|
||||
int crypto_dh_get_bytes(crypto_dh_t *dh);
|
||||
@@ -36,12 +40,25 @@ ssize_t crypto_dh_compute_secret(int severity, crypto_dh_t *dh,
|
||||
void crypto_dh_free_(crypto_dh_t *dh);
|
||||
#define crypto_dh_free(dh) FREE_AND_NULL(crypto_dh_t, crypto_dh_free_, (dh))
|
||||
|
||||
/* Crypto DH free */
|
||||
ssize_t crypto_dh_handshake(int severity, crypto_dh_t *dh,
|
||||
const char *pubkey, size_t pubkey_len,
|
||||
unsigned char *secret_out,
|
||||
size_t secret_bytes_out);
|
||||
|
||||
void crypto_dh_free_all(void);
|
||||
|
||||
/* Prototypes for private functions only used by tortls.c, crypto.c, and the
|
||||
* unit tests. */
|
||||
struct dh_st;
|
||||
struct dh_st *crypto_dh_get_dh_(crypto_dh_t *dh);
|
||||
struct dh_st *crypto_dh_new_openssl_tls(void);
|
||||
|
||||
#ifdef ENABLE_OPENSSL
|
||||
void crypto_dh_init_openssl(void);
|
||||
void crypto_dh_free_all_openssl(void);
|
||||
#endif
|
||||
#ifdef ENABLE_NSS
|
||||
void crypto_dh_init_nss(void);
|
||||
void crypto_dh_free_all_nss(void);
|
||||
#endif
|
||||
|
||||
#endif /* !defined(TOR_CRYPTO_DH_H) */
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
/* Copyright (c) 2001, Matej Pfajfar.
|
||||
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file crypto_dh_nss.h
|
||||
*
|
||||
* \brief NSS implementation of Diffie-Hellman over Z_p.
|
||||
**/
|
||||
|
||||
#include "lib/crypt_ops/crypto_dh.h"
|
||||
#include "lib/crypt_ops/crypto_nss_mgt.h"
|
||||
|
||||
#include "lib/encoding/binascii.h"
|
||||
#include "lib/log/util_bug.h"
|
||||
#include "lib/malloc/malloc.h"
|
||||
|
||||
#include <cryptohi.h>
|
||||
#include <keyhi.h>
|
||||
#include <pk11pub.h>
|
||||
|
||||
static int dh_initialized = 0;
|
||||
static SECKEYDHParams tls_dh_param, circuit_dh_param;
|
||||
static unsigned char tls_dh_prime_data[DH1024_KEY_LEN];
|
||||
static unsigned char circuit_dh_prime_data[DH1024_KEY_LEN];
|
||||
static unsigned char dh_generator_data[1];
|
||||
|
||||
void
|
||||
crypto_dh_init_nss(void)
|
||||
{
|
||||
if (dh_initialized)
|
||||
return;
|
||||
|
||||
int r;
|
||||
r = base16_decode((char*)tls_dh_prime_data,
|
||||
sizeof(tls_dh_prime_data),
|
||||
TLS_DH_PRIME, strlen(TLS_DH_PRIME));
|
||||
tor_assert(r == DH1024_KEY_LEN);
|
||||
r = base16_decode((char*)circuit_dh_prime_data,
|
||||
sizeof(circuit_dh_prime_data),
|
||||
OAKLEY_PRIME_2, strlen(OAKLEY_PRIME_2));
|
||||
tor_assert(r == DH1024_KEY_LEN);
|
||||
dh_generator_data[0] = DH_GENERATOR;
|
||||
|
||||
tls_dh_param.prime.data = tls_dh_prime_data;
|
||||
tls_dh_param.prime.len = DH1024_KEY_LEN;
|
||||
tls_dh_param.base.data = dh_generator_data;
|
||||
tls_dh_param.base.len = 1;
|
||||
|
||||
circuit_dh_param.prime.data = circuit_dh_prime_data;
|
||||
circuit_dh_param.prime.len = DH1024_KEY_LEN;
|
||||
circuit_dh_param.base.data = dh_generator_data;
|
||||
circuit_dh_param.base.len = 1;
|
||||
}
|
||||
|
||||
void
|
||||
crypto_dh_free_all_nss(void)
|
||||
{
|
||||
dh_initialized = 0;
|
||||
}
|
||||
|
||||
struct crypto_dh_t {
|
||||
int dh_type; // XXXX let's remove this later on.
|
||||
SECKEYPrivateKey *seckey;
|
||||
SECKEYPublicKey *pubkey;
|
||||
};
|
||||
|
||||
crypto_dh_t *
|
||||
crypto_dh_new(int dh_type)
|
||||
{
|
||||
crypto_dh_t *r = tor_malloc_zero(sizeof(crypto_dh_t));
|
||||
r->dh_type = dh_type;
|
||||
return r;
|
||||
}
|
||||
|
||||
crypto_dh_t *
|
||||
crypto_dh_dup(const crypto_dh_t *dh)
|
||||
{
|
||||
tor_assert(dh);
|
||||
crypto_dh_t *r = crypto_dh_new(dh->dh_type);
|
||||
if (dh->seckey)
|
||||
r->seckey = SECKEY_CopyPrivateKey(dh->seckey);
|
||||
if (dh->pubkey)
|
||||
r->pubkey = SECKEY_CopyPublicKey(dh->pubkey);
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_dh_get_bytes(crypto_dh_t *dh)
|
||||
{
|
||||
(void)dh;
|
||||
return DH1024_KEY_LEN;
|
||||
}
|
||||
|
||||
int
|
||||
crypto_dh_generate_public(crypto_dh_t *dh)
|
||||
{
|
||||
tor_assert(dh);
|
||||
SECKEYDHParams *p;
|
||||
if (dh->dh_type == DH_TYPE_TLS)
|
||||
p = &tls_dh_param;
|
||||
else
|
||||
p = &circuit_dh_param;
|
||||
|
||||
dh->seckey = SECKEY_CreateDHPrivateKey(p, &dh->pubkey, NULL);
|
||||
if (!dh->seckey || !dh->pubkey)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
int
|
||||
crypto_dh_get_public(crypto_dh_t *dh, char *pubkey_out,
|
||||
size_t pubkey_out_len)
|
||||
{
|
||||
tor_assert(dh);
|
||||
tor_assert(pubkey_out);
|
||||
if (!dh->pubkey) {
|
||||
if (crypto_dh_generate_public(dh) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
const SECItem *item = &dh->pubkey->u.dh.publicValue;
|
||||
|
||||
if (item->len > pubkey_out_len)
|
||||
return -1;
|
||||
|
||||
/* Left-pad the result with 0s. */
|
||||
memset(pubkey_out, 0, pubkey_out_len);
|
||||
memcpy(pubkey_out + pubkey_out_len - item->len,
|
||||
item->data,
|
||||
item->len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
crypto_dh_free_(crypto_dh_t *dh)
|
||||
{
|
||||
if (!dh)
|
||||
return;
|
||||
if (dh->seckey)
|
||||
SECKEY_DestroyPrivateKey(dh->seckey);
|
||||
if (dh->pubkey)
|
||||
SECKEY_DestroyPublicKey(dh->pubkey);
|
||||
tor_free(dh);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
crypto_dh_handshake(int severity, crypto_dh_t *dh,
|
||||
const char *pubkey, size_t pubkey_len,
|
||||
unsigned char *secret_out,
|
||||
size_t secret_bytes_out)
|
||||
{
|
||||
tor_assert(dh);
|
||||
if (pubkey_len > DH1024_KEY_LEN)
|
||||
return -1;
|
||||
if (!dh->pubkey || !dh->seckey)
|
||||
return -1;
|
||||
if (secret_bytes_out < DH1024_KEY_LEN)
|
||||
return -1;
|
||||
|
||||
SECKEYPublicKey peer_key;
|
||||
memset(&peer_key, 0, sizeof(peer_key));
|
||||
peer_key.keyType = dhKey;
|
||||
peer_key.pkcs11ID = CK_INVALID_HANDLE;
|
||||
|
||||
if (dh->dh_type == DH_TYPE_TLS)
|
||||
peer_key.u.dh.prime.data = tls_dh_prime_data; // should never use this code
|
||||
else
|
||||
peer_key.u.dh.prime.data = circuit_dh_prime_data;
|
||||
peer_key.u.dh.prime.len = DH1024_KEY_LEN;
|
||||
peer_key.u.dh.base.data = dh_generator_data;
|
||||
peer_key.u.dh.base.len = 1;
|
||||
peer_key.u.dh.publicValue.data = (unsigned char *)pubkey;
|
||||
peer_key.u.dh.publicValue.len = (int) pubkey_len;
|
||||
|
||||
PK11SymKey *sym = PK11_PubDerive(dh->seckey, &peer_key,
|
||||
PR_FALSE, NULL, NULL, CKM_DH_PKCS_DERIVE,
|
||||
CKM_GENERIC_SECRET_KEY_GEN /* ??? */,
|
||||
CKA_DERIVE, 0, NULL);
|
||||
if (! sym) {
|
||||
crypto_nss_log_errors(severity, "deriving a DH shared secret");
|
||||
return -1;
|
||||
}
|
||||
|
||||
SECStatus s = PK11_ExtractKeyValue(sym);
|
||||
if (s != SECSuccess) {
|
||||
crypto_nss_log_errors(severity, "extracting a DH shared secret");
|
||||
PK11_FreeSymKey(sym);
|
||||
return -1;
|
||||
}
|
||||
|
||||
SECItem *result = PK11_GetKeyData(sym);
|
||||
tor_assert(result); // This cannot fail.
|
||||
if (BUG(result->len > secret_bytes_out)) {
|
||||
PK11_FreeSymKey(sym);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t len = result->len;
|
||||
memcpy(secret_out, result->data, len);
|
||||
PK11_FreeSymKey(sym);
|
||||
|
||||
return len;
|
||||
}
|
||||
@@ -0,0 +1,471 @@
|
||||
/* Copyright (c) 2001, Matej Pfajfar.
|
||||
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file crypto_dh_openssl.c
|
||||
* \brief Implement Tor's Z_p diffie-hellman stuff for OpenSSL.
|
||||
**/
|
||||
|
||||
#include "lib/crypt_ops/compat_openssl.h"
|
||||
#include "lib/crypt_ops/crypto_dh.h"
|
||||
#include "lib/crypt_ops/crypto_digest.h"
|
||||
#include "lib/crypt_ops/crypto_hkdf.h"
|
||||
#include "lib/crypt_ops/crypto_util.h"
|
||||
#include "lib/log/log.h"
|
||||
#include "lib/log/util_bug.h"
|
||||
|
||||
DISABLE_GCC_WARNING(redundant-decls)
|
||||
|
||||
#include <openssl/dh.h>
|
||||
|
||||
ENABLE_GCC_WARNING(redundant-decls)
|
||||
|
||||
#include <openssl/bn.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef ENABLE_NSS
|
||||
static int tor_check_dh_key(int severity, const BIGNUM *bn);
|
||||
|
||||
/** A structure to hold the first half (x, g^x) of a Diffie-Hellman handshake
|
||||
* while we're waiting for the second.*/
|
||||
struct crypto_dh_t {
|
||||
DH *dh; /**< The openssl DH object */
|
||||
};
|
||||
#endif
|
||||
|
||||
static DH *new_openssl_dh_from_params(BIGNUM *p, BIGNUM *g);
|
||||
|
||||
/** Shared P parameter for our circuit-crypto DH key exchanges. */
|
||||
static BIGNUM *dh_param_p = NULL;
|
||||
/** Shared P parameter for our TLS DH key exchanges. */
|
||||
static BIGNUM *dh_param_p_tls = NULL;
|
||||
/** Shared G parameter for our DH key exchanges. */
|
||||
static BIGNUM *dh_param_g = NULL;
|
||||
|
||||
/** Validate a given set of Diffie-Hellman parameters. This is moderately
|
||||
* computationally expensive (milliseconds), so should only be called when
|
||||
* the DH parameters change. Returns 0 on success, * -1 on failure.
|
||||
*/
|
||||
static int
|
||||
crypto_validate_dh_params(const BIGNUM *p, const BIGNUM *g)
|
||||
{
|
||||
DH *dh = NULL;
|
||||
int ret = -1;
|
||||
|
||||
/* Copy into a temporary DH object, just so that DH_check() can be called. */
|
||||
if (!(dh = DH_new()))
|
||||
goto out;
|
||||
#ifdef OPENSSL_1_1_API
|
||||
BIGNUM *dh_p, *dh_g;
|
||||
if (!(dh_p = BN_dup(p)))
|
||||
goto out;
|
||||
if (!(dh_g = BN_dup(g)))
|
||||
goto out;
|
||||
if (!DH_set0_pqg(dh, dh_p, NULL, dh_g))
|
||||
goto out;
|
||||
#else /* !(defined(OPENSSL_1_1_API)) */
|
||||
if (!(dh->p = BN_dup(p)))
|
||||
goto out;
|
||||
if (!(dh->g = BN_dup(g)))
|
||||
goto out;
|
||||
#endif /* defined(OPENSSL_1_1_API) */
|
||||
|
||||
/* Perform the validation. */
|
||||
int codes = 0;
|
||||
if (!DH_check(dh, &codes))
|
||||
goto out;
|
||||
if (BN_is_word(g, DH_GENERATOR_2)) {
|
||||
/* Per https://wiki.openssl.org/index.php/Diffie-Hellman_parameters
|
||||
*
|
||||
* OpenSSL checks the prime is congruent to 11 when g = 2; while the
|
||||
* IETF's primes are congruent to 23 when g = 2.
|
||||
*/
|
||||
BN_ULONG residue = BN_mod_word(p, 24);
|
||||
if (residue == 11 || residue == 23)
|
||||
codes &= ~DH_NOT_SUITABLE_GENERATOR;
|
||||
}
|
||||
if (codes != 0) /* Specifics on why the params suck is irrelevant. */
|
||||
goto out;
|
||||
|
||||
/* Things are probably not evil. */
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
if (dh)
|
||||
DH_free(dh);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: convert <b>hex<b> to a bignum, and return it. Assert that the
|
||||
* operation was successful.
|
||||
*/
|
||||
static BIGNUM *
|
||||
bignum_from_hex(const char *hex)
|
||||
{
|
||||
BIGNUM *result = BN_new();
|
||||
tor_assert(result);
|
||||
|
||||
int r = BN_hex2bn(&result, hex);
|
||||
tor_assert(r);
|
||||
tor_assert(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Set the global Diffie-Hellman generator, used for both TLS and internal
|
||||
* DH stuff.
|
||||
*/
|
||||
static void
|
||||
crypto_set_dh_generator(void)
|
||||
{
|
||||
BIGNUM *generator;
|
||||
int r;
|
||||
|
||||
if (dh_param_g)
|
||||
return;
|
||||
|
||||
generator = BN_new();
|
||||
tor_assert(generator);
|
||||
|
||||
r = BN_set_word(generator, DH_GENERATOR);
|
||||
tor_assert(r);
|
||||
|
||||
dh_param_g = generator;
|
||||
}
|
||||
|
||||
/** Initialize our DH parameters. Idempotent. */
|
||||
void
|
||||
crypto_dh_init_openssl(void)
|
||||
{
|
||||
if (dh_param_p && dh_param_g && dh_param_p_tls)
|
||||
return;
|
||||
|
||||
tor_assert(dh_param_g == NULL);
|
||||
tor_assert(dh_param_p == NULL);
|
||||
tor_assert(dh_param_p_tls == NULL);
|
||||
|
||||
crypto_set_dh_generator();
|
||||
dh_param_p = bignum_from_hex(OAKLEY_PRIME_2);
|
||||
dh_param_p_tls = bignum_from_hex(TLS_DH_PRIME);
|
||||
|
||||
tor_assert(0 == crypto_validate_dh_params(dh_param_p, dh_param_g));
|
||||
tor_assert(0 == crypto_validate_dh_params(dh_param_p_tls, dh_param_g));
|
||||
}
|
||||
|
||||
/** Number of bits to use when choosing the x or y value in a Diffie-Hellman
|
||||
* handshake. Since we exponentiate by this value, choosing a smaller one
|
||||
* lets our handhake go faster.
|
||||
*/
|
||||
#define DH_PRIVATE_KEY_BITS 320
|
||||
|
||||
/** Used by tortls.c: Get the DH* for use with TLS.
|
||||
*/
|
||||
DH *
|
||||
crypto_dh_new_openssl_tls(void)
|
||||
{
|
||||
return new_openssl_dh_from_params(dh_param_p_tls, dh_param_g);
|
||||
}
|
||||
|
||||
#ifndef ENABLE_NSS
|
||||
/** Allocate and return a new DH object for a key exchange. Returns NULL on
|
||||
* failure.
|
||||
*/
|
||||
crypto_dh_t *
|
||||
crypto_dh_new(int dh_type)
|
||||
{
|
||||
crypto_dh_t *res = tor_malloc_zero(sizeof(crypto_dh_t));
|
||||
|
||||
tor_assert(dh_type == DH_TYPE_CIRCUIT || dh_type == DH_TYPE_TLS ||
|
||||
dh_type == DH_TYPE_REND);
|
||||
|
||||
if (!dh_param_p)
|
||||
crypto_dh_init();
|
||||
|
||||
BIGNUM *dh_p = NULL;
|
||||
if (dh_type == DH_TYPE_TLS) {
|
||||
dh_p = dh_param_p_tls;
|
||||
} else {
|
||||
dh_p = dh_param_p;
|
||||
}
|
||||
|
||||
res->dh = new_openssl_dh_from_params(dh_p, dh_param_g);
|
||||
if (res->dh == NULL)
|
||||
tor_free(res); // sets res to NULL.
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Create and return a new openssl DH from a given prime and generator. */
|
||||
static DH *
|
||||
new_openssl_dh_from_params(BIGNUM *p, BIGNUM *g)
|
||||
{
|
||||
DH *res_dh;
|
||||
if (!(res_dh = DH_new()))
|
||||
goto err;
|
||||
|
||||
BIGNUM *dh_p = NULL, *dh_g = NULL;
|
||||
dh_p = BN_dup(p);
|
||||
if (!dh_p)
|
||||
goto err;
|
||||
|
||||
dh_g = BN_dup(g);
|
||||
if (!dh_g) {
|
||||
BN_free(dh_p);
|
||||
goto err;
|
||||
}
|
||||
|
||||
#ifdef OPENSSL_1_1_API
|
||||
|
||||
if (!DH_set0_pqg(res_dh, dh_p, NULL, dh_g)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!DH_set_length(res_dh, DH_PRIVATE_KEY_BITS))
|
||||
goto err;
|
||||
#else /* !(defined(OPENSSL_1_1_API)) */
|
||||
res_dh->p = dh_p;
|
||||
res_dh->g = dh_g;
|
||||
res_dh->length = DH_PRIVATE_KEY_BITS;
|
||||
#endif /* defined(OPENSSL_1_1_API) */
|
||||
|
||||
return res_dh;
|
||||
|
||||
/* LCOV_EXCL_START
|
||||
* This error condition is only reached when an allocation fails */
|
||||
err:
|
||||
crypto_openssl_log_errors(LOG_WARN, "creating DH object");
|
||||
if (res_dh) DH_free(res_dh); /* frees p and g too */
|
||||
return NULL;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
#ifndef ENABLE_NSS
|
||||
/** Return a copy of <b>dh</b>, sharing its internal state. */
|
||||
crypto_dh_t *
|
||||
crypto_dh_dup(const crypto_dh_t *dh)
|
||||
{
|
||||
crypto_dh_t *dh_new = tor_malloc_zero(sizeof(crypto_dh_t));
|
||||
tor_assert(dh);
|
||||
tor_assert(dh->dh);
|
||||
dh_new->dh = dh->dh;
|
||||
DH_up_ref(dh->dh);
|
||||
return dh_new;
|
||||
}
|
||||
|
||||
/** Return the length of the DH key in <b>dh</b>, in bytes.
|
||||
*/
|
||||
int
|
||||
crypto_dh_get_bytes(crypto_dh_t *dh)
|
||||
{
|
||||
tor_assert(dh);
|
||||
return DH_size(dh->dh);
|
||||
}
|
||||
|
||||
/** Generate \<x,g^x\> for our part of the key exchange. Return 0 on
|
||||
* success, -1 on failure.
|
||||
*/
|
||||
int
|
||||
crypto_dh_generate_public(crypto_dh_t *dh)
|
||||
{
|
||||
#ifndef OPENSSL_1_1_API
|
||||
again:
|
||||
#endif
|
||||
if (!DH_generate_key(dh->dh)) {
|
||||
/* LCOV_EXCL_START
|
||||
* To test this we would need some way to tell openssl to break DH. */
|
||||
crypto_openssl_log_errors(LOG_WARN, "generating DH key");
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
#ifdef OPENSSL_1_1_API
|
||||
/* OpenSSL 1.1.x doesn't appear to let you regenerate a DH key, without
|
||||
* recreating the DH object. I have no idea what sort of aliasing madness
|
||||
* can occur here, so do the check, and just bail on failure.
|
||||
*/
|
||||
const BIGNUM *pub_key, *priv_key;
|
||||
DH_get0_key(dh->dh, &pub_key, &priv_key);
|
||||
if (tor_check_dh_key(LOG_WARN, pub_key)<0) {
|
||||
log_warn(LD_CRYPTO, "Weird! Our own DH key was invalid. I guess once-in-"
|
||||
"the-universe chances really do happen. Treating as a failure.");
|
||||
return -1;
|
||||
}
|
||||
#else /* !(defined(OPENSSL_1_1_API)) */
|
||||
if (tor_check_dh_key(LOG_WARN, dh->dh->pub_key)<0) {
|
||||
/* LCOV_EXCL_START
|
||||
* If this happens, then openssl's DH implementation is busted. */
|
||||
log_warn(LD_CRYPTO, "Weird! Our own DH key was invalid. I guess once-in-"
|
||||
"the-universe chances really do happen. Trying again.");
|
||||
/* Free and clear the keys, so OpenSSL will actually try again. */
|
||||
BN_clear_free(dh->dh->pub_key);
|
||||
BN_clear_free(dh->dh->priv_key);
|
||||
dh->dh->pub_key = dh->dh->priv_key = NULL;
|
||||
goto again;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
#endif /* defined(OPENSSL_1_1_API) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Generate g^x as necessary, and write the g^x for the key exchange
|
||||
* as a <b>pubkey_len</b>-byte value into <b>pubkey</b>. Return 0 on
|
||||
* success, -1 on failure. <b>pubkey_len</b> must be \>= DH1024_KEY_LEN.
|
||||
*/
|
||||
int
|
||||
crypto_dh_get_public(crypto_dh_t *dh, char *pubkey, size_t pubkey_len)
|
||||
{
|
||||
int bytes;
|
||||
tor_assert(dh);
|
||||
|
||||
const BIGNUM *dh_pub;
|
||||
|
||||
#ifdef OPENSSL_1_1_API
|
||||
const BIGNUM *dh_priv;
|
||||
DH_get0_key(dh->dh, &dh_pub, &dh_priv);
|
||||
#else
|
||||
dh_pub = dh->dh->pub_key;
|
||||
#endif /* defined(OPENSSL_1_1_API) */
|
||||
|
||||
if (!dh_pub) {
|
||||
if (crypto_dh_generate_public(dh)<0)
|
||||
return -1;
|
||||
else {
|
||||
#ifdef OPENSSL_1_1_API
|
||||
DH_get0_key(dh->dh, &dh_pub, &dh_priv);
|
||||
#else
|
||||
dh_pub = dh->dh->pub_key;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
tor_assert(dh_pub);
|
||||
bytes = BN_num_bytes(dh_pub);
|
||||
tor_assert(bytes >= 0);
|
||||
if (pubkey_len < (size_t)bytes) {
|
||||
log_warn(LD_CRYPTO,
|
||||
"Weird! pubkey_len (%d) was smaller than DH1024_KEY_LEN (%d)",
|
||||
(int) pubkey_len, bytes);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(pubkey, 0, pubkey_len);
|
||||
BN_bn2bin(dh_pub, (unsigned char*)(pubkey+(pubkey_len-bytes)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Check for bad Diffie-Hellman public keys (g^x). Return 0 if the key is
|
||||
* okay (in the subgroup [2,p-2]), or -1 if it's bad.
|
||||
* See http://www.cl.cam.ac.uk/ftp/users/rja14/psandqs.ps.gz for some tips.
|
||||
*/
|
||||
static int
|
||||
tor_check_dh_key(int severity, const BIGNUM *bn)
|
||||
{
|
||||
BIGNUM *x;
|
||||
char *s;
|
||||
tor_assert(bn);
|
||||
x = BN_new();
|
||||
tor_assert(x);
|
||||
if (BUG(!dh_param_p))
|
||||
crypto_dh_init(); //LCOV_EXCL_LINE we already checked whether we did this.
|
||||
BN_set_word(x, 1);
|
||||
if (BN_cmp(bn,x)<=0) {
|
||||
log_fn(severity, LD_CRYPTO, "DH key must be at least 2.");
|
||||
goto err;
|
||||
}
|
||||
BN_copy(x,dh_param_p);
|
||||
BN_sub_word(x, 1);
|
||||
if (BN_cmp(bn,x)>=0) {
|
||||
log_fn(severity, LD_CRYPTO, "DH key must be at most p-2.");
|
||||
goto err;
|
||||
}
|
||||
BN_clear_free(x);
|
||||
return 0;
|
||||
err:
|
||||
BN_clear_free(x);
|
||||
s = BN_bn2hex(bn);
|
||||
log_fn(severity, LD_CRYPTO, "Rejecting insecure DH key [%s]", s);
|
||||
OPENSSL_free(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Given a DH key exchange object, and our peer's value of g^y (as a
|
||||
* <b>pubkey_len</b>-byte value in <b>pubkey</b>) generate
|
||||
* g^xy as a big-endian integer in <b>secret_out</b>.
|
||||
* Return the number of bytes generated on success,
|
||||
* or -1 on failure.
|
||||
*
|
||||
* This function MUST validate that g^y is actually in the group.
|
||||
*/
|
||||
ssize_t
|
||||
crypto_dh_handshake(int severity, crypto_dh_t *dh,
|
||||
const char *pubkey, size_t pubkey_len,
|
||||
unsigned char *secret_out, size_t secret_bytes_out)
|
||||
{
|
||||
BIGNUM *pubkey_bn = NULL;
|
||||
size_t secret_len=0;
|
||||
int result=0;
|
||||
|
||||
tor_assert(dh);
|
||||
tor_assert(secret_bytes_out/DIGEST_LEN <= 255);
|
||||
tor_assert(pubkey_len < INT_MAX);
|
||||
|
||||
if (BUG(crypto_dh_get_bytes(dh) > (int)secret_bytes_out)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!(pubkey_bn = BN_bin2bn((const unsigned char*)pubkey,
|
||||
(int)pubkey_len, NULL)))
|
||||
goto error;
|
||||
if (tor_check_dh_key(severity, pubkey_bn)<0) {
|
||||
/* Check for invalid public keys. */
|
||||
log_fn(severity, LD_CRYPTO,"Rejected invalid g^x");
|
||||
goto error;
|
||||
}
|
||||
result = DH_compute_key(secret_out, pubkey_bn, dh->dh);
|
||||
if (result < 0) {
|
||||
log_warn(LD_CRYPTO,"DH_compute_key() failed.");
|
||||
goto error;
|
||||
}
|
||||
secret_len = result;
|
||||
|
||||
goto done;
|
||||
error:
|
||||
result = -1;
|
||||
done:
|
||||
crypto_openssl_log_errors(LOG_WARN, "completing DH handshake");
|
||||
if (pubkey_bn)
|
||||
BN_clear_free(pubkey_bn);
|
||||
if (result < 0)
|
||||
return result;
|
||||
else
|
||||
return secret_len;
|
||||
}
|
||||
|
||||
/** Free a DH key exchange object.
|
||||
*/
|
||||
void
|
||||
crypto_dh_free_(crypto_dh_t *dh)
|
||||
{
|
||||
if (!dh)
|
||||
return;
|
||||
tor_assert(dh->dh);
|
||||
DH_free(dh->dh);
|
||||
tor_free(dh);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
crypto_dh_free_all_openssl(void)
|
||||
{
|
||||
if (dh_param_p)
|
||||
BN_clear_free(dh_param_p);
|
||||
if (dh_param_p_tls)
|
||||
BN_clear_free(dh_param_p_tls);
|
||||
if (dh_param_g)
|
||||
BN_clear_free(dh_param_g);
|
||||
|
||||
dh_param_p = dh_param_p_tls = dh_param_g = NULL;
|
||||
}
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
#include "lib/container/smartlist.h"
|
||||
#include "lib/crypt_ops/crypto_digest.h"
|
||||
#include "lib/crypt_ops/crypto_openssl_mgt.h"
|
||||
#include "lib/crypt_ops/crypto_util.h"
|
||||
#include "lib/log/log.h"
|
||||
#include "lib/log/util_bug.h"
|
||||
@@ -24,12 +23,92 @@
|
||||
|
||||
#include "lib/arch/bytes.h"
|
||||
|
||||
#ifdef ENABLE_NSS
|
||||
DISABLE_GCC_WARNING(strict-prototypes)
|
||||
#include <pk11pub.h>
|
||||
ENABLE_GCC_WARNING(strict-prototypes)
|
||||
#else
|
||||
|
||||
#include "lib/crypt_ops/crypto_openssl_mgt.h"
|
||||
|
||||
DISABLE_GCC_WARNING(redundant-decls)
|
||||
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
ENABLE_GCC_WARNING(redundant-decls)
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NSS
|
||||
/**
|
||||
* Convert a digest_algorithm_t (used by tor) to a HashType (used by NSS).
|
||||
* On failure, return SEC_OID_UNKNOWN. */
|
||||
static SECOidTag
|
||||
digest_alg_to_nss_oid(digest_algorithm_t alg)
|
||||
{
|
||||
switch (alg) {
|
||||
case DIGEST_SHA1: return SEC_OID_SHA1;
|
||||
case DIGEST_SHA256: return SEC_OID_SHA256;
|
||||
case DIGEST_SHA512: return SEC_OID_SHA512;
|
||||
case DIGEST_SHA3_256: /* Fall through */
|
||||
case DIGEST_SHA3_512: /* Fall through */
|
||||
default:
|
||||
return SEC_OID_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper: get an unkeyed digest via pk11wrap */
|
||||
static int
|
||||
digest_nss_internal(SECOidTag alg,
|
||||
char *digest, unsigned len_out,
|
||||
const char *msg, size_t msg_len)
|
||||
{
|
||||
if (alg == SEC_OID_UNKNOWN)
|
||||
return -1;
|
||||
tor_assert(msg_len <= UINT_MAX);
|
||||
|
||||
int rv = -1;
|
||||
SECStatus s;
|
||||
PK11Context *ctx = PK11_CreateDigestContext(alg);
|
||||
if (!ctx)
|
||||
return -1;
|
||||
|
||||
s = PK11_DigestBegin(ctx);
|
||||
if (s != SECSuccess)
|
||||
goto done;
|
||||
|
||||
s = PK11_DigestOp(ctx, (const unsigned char *)msg, (unsigned int)msg_len);
|
||||
if (s != SECSuccess)
|
||||
goto done;
|
||||
|
||||
unsigned int len = 0;
|
||||
s = PK11_DigestFinal(ctx, (unsigned char *)digest, &len, len_out);
|
||||
if (s != SECSuccess)
|
||||
goto done;
|
||||
|
||||
rv = 0;
|
||||
done:
|
||||
PK11_DestroyContext(ctx, PR_TRUE);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/** True iff alg is implemented in our crypto library, and we want to use that
|
||||
* implementation */
|
||||
static bool
|
||||
library_supports_digest(digest_algorithm_t alg)
|
||||
{
|
||||
switch (alg) {
|
||||
case DIGEST_SHA1: /* Fall through */
|
||||
case DIGEST_SHA256: /* Fall through */
|
||||
case DIGEST_SHA512: /* Fall through */
|
||||
return true;
|
||||
case DIGEST_SHA3_256: /* Fall through */
|
||||
case DIGEST_SHA3_512: /* Fall through */
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Crypto digest functions */
|
||||
|
||||
@@ -37,13 +116,18 @@ ENABLE_GCC_WARNING(redundant-decls)
|
||||
* <b>m</b>. Write the DIGEST_LEN byte result into <b>digest</b>.
|
||||
* Return 0 on success, -1 on failure.
|
||||
*/
|
||||
int
|
||||
crypto_digest(char *digest, const char *m, size_t len)
|
||||
MOCK_IMPL(int,
|
||||
crypto_digest,(char *digest, const char *m, size_t len))
|
||||
{
|
||||
tor_assert(m);
|
||||
tor_assert(digest);
|
||||
if (SHA1((const unsigned char*)m,len,(unsigned char*)digest) == NULL)
|
||||
#ifdef ENABLE_NSS
|
||||
return digest_nss_internal(SEC_OID_SHA1, digest, DIGEST_LEN, m, len);
|
||||
#else
|
||||
if (SHA1((const unsigned char*)m,len,(unsigned char*)digest) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -59,11 +143,16 @@ crypto_digest256(char *digest, const char *m, size_t len,
|
||||
tor_assert(algorithm == DIGEST_SHA256 || algorithm == DIGEST_SHA3_256);
|
||||
|
||||
int ret = 0;
|
||||
if (algorithm == DIGEST_SHA256)
|
||||
if (algorithm == DIGEST_SHA256) {
|
||||
#ifdef ENABLE_NSS
|
||||
return digest_nss_internal(SEC_OID_SHA256, digest, DIGEST256_LEN, m, len);
|
||||
#else
|
||||
ret = (SHA256((const uint8_t*)m,len,(uint8_t*)digest) != NULL);
|
||||
else
|
||||
#endif
|
||||
} else {
|
||||
ret = (sha3_256((uint8_t *)digest, DIGEST256_LEN,(const uint8_t *)m, len)
|
||||
> -1);
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
return -1;
|
||||
@@ -82,12 +171,17 @@ crypto_digest512(char *digest, const char *m, size_t len,
|
||||
tor_assert(algorithm == DIGEST_SHA512 || algorithm == DIGEST_SHA3_512);
|
||||
|
||||
int ret = 0;
|
||||
if (algorithm == DIGEST_SHA512)
|
||||
if (algorithm == DIGEST_SHA512) {
|
||||
#ifdef ENABLE_NSS
|
||||
return digest_nss_internal(SEC_OID_SHA512, digest, DIGEST512_LEN, m, len);
|
||||
#else
|
||||
ret = (SHA512((const unsigned char*)m,len,(unsigned char*)digest)
|
||||
!= NULL);
|
||||
else
|
||||
#endif
|
||||
} else {
|
||||
ret = (sha3_512((uint8_t*)digest, DIGEST512_LEN, (const uint8_t*)m, len)
|
||||
> -1);
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
return -1;
|
||||
@@ -181,9 +275,13 @@ struct crypto_digest_t {
|
||||
* that space for other members might not even be allocated!
|
||||
*/
|
||||
union {
|
||||
#ifdef ENABLE_NSS
|
||||
PK11Context *ctx;
|
||||
#else
|
||||
SHA_CTX sha1; /**< state for SHA1 */
|
||||
SHA256_CTX sha2; /**< state for SHA256 */
|
||||
SHA512_CTX sha512; /**< state for SHA512 */
|
||||
#endif
|
||||
keccak_state sha3; /**< state for SHA3-[256,512] */
|
||||
} d;
|
||||
};
|
||||
@@ -214,12 +312,19 @@ crypto_digest_alloc_bytes(digest_algorithm_t alg)
|
||||
#define END_OF_FIELD(f) (offsetof(crypto_digest_t, f) + \
|
||||
STRUCT_FIELD_SIZE(crypto_digest_t, f))
|
||||
switch (alg) {
|
||||
#ifdef ENABLE_NSS
|
||||
case DIGEST_SHA1: /* Fall through */
|
||||
case DIGEST_SHA256: /* Fall through */
|
||||
case DIGEST_SHA512:
|
||||
return END_OF_FIELD(d.ctx);
|
||||
#else
|
||||
case DIGEST_SHA1:
|
||||
return END_OF_FIELD(d.sha1);
|
||||
case DIGEST_SHA256:
|
||||
return END_OF_FIELD(d.sha2);
|
||||
case DIGEST_SHA512:
|
||||
return END_OF_FIELD(d.sha512);
|
||||
#endif
|
||||
case DIGEST_SHA3_256:
|
||||
case DIGEST_SHA3_512:
|
||||
return END_OF_FIELD(d.sha3);
|
||||
@@ -243,6 +348,21 @@ crypto_digest_new_internal(digest_algorithm_t algorithm)
|
||||
|
||||
switch (algorithm)
|
||||
{
|
||||
#ifdef ENABLE_NSS
|
||||
case DIGEST_SHA1: /* fall through */
|
||||
case DIGEST_SHA256: /* fall through */
|
||||
case DIGEST_SHA512:
|
||||
r->d.ctx = PK11_CreateDigestContext(digest_alg_to_nss_oid(algorithm));
|
||||
if (BUG(!r->d.ctx)) {
|
||||
tor_free(r);
|
||||
return NULL;
|
||||
}
|
||||
if (BUG(SECSuccess != PK11_DigestBegin(r->d.ctx))) {
|
||||
crypto_digest_free(r);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
#else
|
||||
case DIGEST_SHA1:
|
||||
SHA1_Init(&r->d.sha1);
|
||||
break;
|
||||
@@ -252,6 +372,7 @@ crypto_digest_new_internal(digest_algorithm_t algorithm)
|
||||
case DIGEST_SHA512:
|
||||
SHA512_Init(&r->d.sha512);
|
||||
break;
|
||||
#endif
|
||||
case DIGEST_SHA3_256:
|
||||
keccak_digest_init(&r->d.sha3, 256);
|
||||
break;
|
||||
@@ -302,6 +423,11 @@ crypto_digest_free_(crypto_digest_t *digest)
|
||||
{
|
||||
if (!digest)
|
||||
return;
|
||||
#ifdef ENABLE_NSS
|
||||
if (library_supports_digest(digest->algorithm)) {
|
||||
PK11_DestroyContext(digest->d.ctx, PR_TRUE);
|
||||
}
|
||||
#endif
|
||||
size_t bytes = crypto_digest_alloc_bytes(digest->algorithm);
|
||||
memwipe(digest, 0, bytes);
|
||||
tor_free(digest);
|
||||
@@ -324,6 +450,17 @@ crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
|
||||
* just doing it ourselves. Hashes are fast.
|
||||
*/
|
||||
switch (digest->algorithm) {
|
||||
#ifdef ENABLE_NSS
|
||||
case DIGEST_SHA1: /* fall through */
|
||||
case DIGEST_SHA256: /* fall through */
|
||||
case DIGEST_SHA512:
|
||||
tor_assert(len <= UINT_MAX);
|
||||
SECStatus s = PK11_DigestOp(digest->d.ctx,
|
||||
(const unsigned char *)data,
|
||||
(unsigned int)len);
|
||||
tor_assert(s == SECSuccess);
|
||||
break;
|
||||
#else
|
||||
case DIGEST_SHA1:
|
||||
SHA1_Update(&digest->d.sha1, (void*)data, len);
|
||||
break;
|
||||
@@ -333,6 +470,7 @@ crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
|
||||
case DIGEST_SHA512:
|
||||
SHA512_Update(&digest->d.sha512, (void*)data, len);
|
||||
break;
|
||||
#endif
|
||||
case DIGEST_SHA3_256: /* FALLSTHROUGH */
|
||||
case DIGEST_SHA3_512:
|
||||
keccak_digest_update(&digest->d.sha3, (const uint8_t *)data, len);
|
||||
@@ -357,7 +495,6 @@ crypto_digest_get_digest(crypto_digest_t *digest,
|
||||
char *out, size_t out_len)
|
||||
{
|
||||
unsigned char r[DIGEST512_LEN];
|
||||
crypto_digest_t tmpenv;
|
||||
tor_assert(digest);
|
||||
tor_assert(out);
|
||||
tor_assert(out_len <= crypto_digest_algorithm_get_length(digest->algorithm));
|
||||
@@ -370,7 +507,26 @@ crypto_digest_get_digest(crypto_digest_t *digest,
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_NSS
|
||||
/* Copy into a temporary buffer since DigestFinal (alters) the context */
|
||||
unsigned char buf[1024];
|
||||
unsigned int saved_len = 0;
|
||||
unsigned rlen;
|
||||
unsigned char *saved = PK11_SaveContextAlloc(digest->d.ctx,
|
||||
buf, sizeof(buf),
|
||||
&saved_len);
|
||||
tor_assert(saved);
|
||||
SECStatus s = PK11_DigestFinal(digest->d.ctx, r, &rlen, sizeof(r));
|
||||
tor_assert(s == SECSuccess);
|
||||
tor_assert(rlen >= out_len);
|
||||
s = PK11_RestoreContext(digest->d.ctx, saved, saved_len);
|
||||
tor_assert(s == SECSuccess);
|
||||
if (saved != buf) {
|
||||
PORT_ZFree(saved, saved_len);
|
||||
}
|
||||
#else
|
||||
const size_t alloc_bytes = crypto_digest_alloc_bytes(digest->algorithm);
|
||||
crypto_digest_t tmpenv;
|
||||
/* memcpy into a temporary ctx, since SHA*_Final clears the context */
|
||||
memcpy(&tmpenv, digest, alloc_bytes);
|
||||
switch (digest->algorithm) {
|
||||
@@ -393,6 +549,7 @@ crypto_digest_get_digest(crypto_digest_t *digest,
|
||||
break;
|
||||
//LCOV_EXCL_STOP
|
||||
}
|
||||
#endif
|
||||
memcpy(out, r, out_len);
|
||||
memwipe(r, 0, sizeof(r));
|
||||
}
|
||||
@@ -408,7 +565,13 @@ crypto_digest_dup(const crypto_digest_t *digest)
|
||||
{
|
||||
tor_assert(digest);
|
||||
const size_t alloc_bytes = crypto_digest_alloc_bytes(digest->algorithm);
|
||||
return tor_memdup(digest, alloc_bytes);
|
||||
crypto_digest_t *result = tor_memdup(digest, alloc_bytes);
|
||||
#ifdef ENABLE_NSS
|
||||
if (library_supports_digest(digest->algorithm)) {
|
||||
result->d.ctx = PK11_CloneContext(digest->d.ctx);
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Temporarily save the state of <b>digest</b> in <b>checkpoint</b>.
|
||||
@@ -420,6 +583,18 @@ crypto_digest_checkpoint(crypto_digest_checkpoint_t *checkpoint,
|
||||
{
|
||||
const size_t bytes = crypto_digest_alloc_bytes(digest->algorithm);
|
||||
tor_assert(bytes <= sizeof(checkpoint->mem));
|
||||
#ifdef ENABLE_NSS
|
||||
if (library_supports_digest(digest->algorithm)) {
|
||||
unsigned char *allocated;
|
||||
allocated = PK11_SaveContextAlloc(digest->d.ctx,
|
||||
(unsigned char *)checkpoint->mem,
|
||||
sizeof(checkpoint->mem),
|
||||
&checkpoint->bytes_used);
|
||||
/* No allocation is allowed here. */
|
||||
tor_assert(allocated == checkpoint->mem);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
memcpy(checkpoint->mem, digest, bytes);
|
||||
}
|
||||
|
||||
@@ -431,6 +606,15 @@ crypto_digest_restore(crypto_digest_t *digest,
|
||||
const crypto_digest_checkpoint_t *checkpoint)
|
||||
{
|
||||
const size_t bytes = crypto_digest_alloc_bytes(digest->algorithm);
|
||||
#ifdef ENABLE_NSS
|
||||
if (library_supports_digest(digest->algorithm)) {
|
||||
SECStatus s = PK11_RestoreContext(digest->d.ctx,
|
||||
(unsigned char *)checkpoint->mem,
|
||||
checkpoint->bytes_used);
|
||||
tor_assert(s == SECSuccess);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
memcpy(digest, checkpoint->mem, bytes);
|
||||
}
|
||||
|
||||
@@ -446,6 +630,13 @@ crypto_digest_assign(crypto_digest_t *into,
|
||||
tor_assert(from);
|
||||
tor_assert(into->algorithm == from->algorithm);
|
||||
const size_t alloc_bytes = crypto_digest_alloc_bytes(from->algorithm);
|
||||
#ifdef ENABLE_NSS
|
||||
if (library_supports_digest(from->algorithm)) {
|
||||
PK11_DestroyContext(into->d.ctx, PR_TRUE);
|
||||
into->d.ctx = PK11_CloneContext(from->d.ctx);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
memcpy(into,from,alloc_bytes);
|
||||
}
|
||||
|
||||
@@ -496,14 +687,63 @@ crypto_hmac_sha256(char *hmac_out,
|
||||
const char *key, size_t key_len,
|
||||
const char *msg, size_t msg_len)
|
||||
{
|
||||
unsigned char *rv = NULL;
|
||||
/* If we've got OpenSSL >=0.9.8 we can use its hmac implementation. */
|
||||
tor_assert(key_len < INT_MAX);
|
||||
tor_assert(msg_len < INT_MAX);
|
||||
tor_assert(hmac_out);
|
||||
#ifdef ENABLE_NSS
|
||||
PK11SlotInfo *slot = NULL;
|
||||
PK11SymKey *symKey = NULL;
|
||||
PK11Context *hmac = NULL;
|
||||
|
||||
int ok = 0;
|
||||
SECStatus s;
|
||||
SECItem keyItem, paramItem;
|
||||
keyItem.data = (unsigned char *)key;
|
||||
keyItem.len = (unsigned)key_len;
|
||||
paramItem.type = siBuffer;
|
||||
paramItem.data = NULL;
|
||||
paramItem.len = 0;
|
||||
|
||||
slot = PK11_GetBestSlot(CKM_SHA256_HMAC, NULL);
|
||||
if (!slot)
|
||||
goto done;
|
||||
symKey = PK11_ImportSymKey(slot, CKM_SHA256_HMAC,
|
||||
PK11_OriginUnwrap, CKA_SIGN, &keyItem, NULL);
|
||||
if (!symKey)
|
||||
goto done;
|
||||
|
||||
hmac = PK11_CreateContextBySymKey(CKM_SHA256_HMAC, CKA_SIGN, symKey,
|
||||
¶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 <b>msg_len</b> bytes in <b>msg</b> using a
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "lib/cc/torint.h"
|
||||
#include "lib/defs/digest_sizes.h"
|
||||
#include "lib/malloc/malloc.h"
|
||||
#include "lib/testsupport/testsupport.h"
|
||||
|
||||
/** Length of a sha1 message digest when encoded in base32 with trailing =
|
||||
* signs removed. */
|
||||
@@ -51,6 +52,9 @@ typedef enum {
|
||||
/** Structure used to temporarily save the a digest object. Only implemented
|
||||
* for SHA1 digest for now. */
|
||||
typedef struct crypto_digest_checkpoint_t {
|
||||
#ifdef ENABLE_NSS
|
||||
unsigned int bytes_used;
|
||||
#endif
|
||||
uint8_t mem[DIGEST_CHECKPOINT_BYTES];
|
||||
} crypto_digest_checkpoint_t;
|
||||
|
||||
@@ -72,7 +76,7 @@ typedef struct crypto_xof_t crypto_xof_t;
|
||||
struct smartlist_t;
|
||||
|
||||
/* SHA-1 and other digests */
|
||||
int crypto_digest(char *digest, const char *m, size_t len);
|
||||
MOCK_DECL(int, crypto_digest,(char *digest, const char *m, size_t len));
|
||||
int crypto_digest256(char *digest, const char *m, size_t len,
|
||||
digest_algorithm_t algorithm);
|
||||
int crypto_digest512(char *digest, const char *m, size_t len,
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "ed25519/donna/ed25519_donna_tor.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
static void pick_ed25519_impl(void);
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "lib/fs/files.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
/** Write the <b>datalen</b> bytes from <b>data</b> to the file named
|
||||
* <b>fname</b> in the tagged-data format. This format contains a
|
||||
|
||||
@@ -17,12 +17,14 @@
|
||||
#include "lib/intmath/cmp.h"
|
||||
#include "lib/log/util_bug.h"
|
||||
|
||||
#ifdef ENABLE_OPENSSL
|
||||
#include <openssl/opensslv.h>
|
||||
|
||||
#if defined(HAVE_ERR_LOAD_KDF_STRINGS)
|
||||
#include <openssl/kdf.h>
|
||||
#define HAVE_OPENSSL_HKDF 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
/* Copyright (c) 2001, Matej Pfajfar.
|
||||
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file crypto_init.c
|
||||
*
|
||||
* \brief Initialize and shut down Tor's crypto library and subsystem.
|
||||
**/
|
||||
|
||||
#include "orconfig.h"
|
||||
|
||||
#include "lib/crypt_ops/crypto_init.h"
|
||||
|
||||
#include "lib/crypt_ops/crypto_curve25519.h"
|
||||
#include "lib/crypt_ops/crypto_dh.h"
|
||||
#include "lib/crypt_ops/crypto_ed25519.h"
|
||||
#include "lib/crypt_ops/crypto_openssl_mgt.h"
|
||||
#include "lib/crypt_ops/crypto_nss_mgt.h"
|
||||
#include "lib/crypt_ops/crypto_rand.h"
|
||||
|
||||
#include "siphash.h"
|
||||
|
||||
/** Boolean: has our crypto library been initialized? (early phase) */
|
||||
static int crypto_early_initialized_ = 0;
|
||||
|
||||
/** Boolean: has our crypto library been initialized? (late phase) */
|
||||
static int crypto_global_initialized_ = 0;
|
||||
|
||||
static int have_seeded_siphash = 0;
|
||||
|
||||
/** Set up the siphash key if we haven't already done so. */
|
||||
int
|
||||
crypto_init_siphash_key(void)
|
||||
{
|
||||
struct sipkey key;
|
||||
if (have_seeded_siphash)
|
||||
return 0;
|
||||
|
||||
crypto_rand((char*) &key, sizeof(key));
|
||||
siphash_set_global_key(&key);
|
||||
have_seeded_siphash = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Initialize the crypto library. Return 0 on success, -1 on failure.
|
||||
*/
|
||||
int
|
||||
crypto_early_init(void)
|
||||
{
|
||||
if (!crypto_early_initialized_) {
|
||||
|
||||
crypto_early_initialized_ = 1;
|
||||
|
||||
#ifdef ENABLE_OPENSSL
|
||||
crypto_openssl_early_init();
|
||||
#endif
|
||||
#ifdef ENABLE_NSS
|
||||
crypto_nss_early_init(0);
|
||||
#endif
|
||||
|
||||
if (crypto_seed_rng() < 0)
|
||||
return -1;
|
||||
if (crypto_init_siphash_key() < 0)
|
||||
return -1;
|
||||
|
||||
curve25519_init();
|
||||
ed25519_init();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Initialize the crypto library. Return 0 on success, -1 on failure.
|
||||
*/
|
||||
int
|
||||
crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
|
||||
{
|
||||
if (!crypto_global_initialized_) {
|
||||
if (crypto_early_init() < 0)
|
||||
return -1;
|
||||
|
||||
crypto_global_initialized_ = 1;
|
||||
|
||||
crypto_dh_init();
|
||||
|
||||
#ifdef ENABLE_OPENSSL
|
||||
if (crypto_openssl_late_init(useAccel, accelName, accelDir) < 0)
|
||||
return -1;
|
||||
#else
|
||||
(void)useAccel;
|
||||
(void)accelName;
|
||||
(void)accelDir;
|
||||
#endif
|
||||
#ifdef ENABLE_NSS
|
||||
if (crypto_nss_late_init() < 0)
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Free crypto resources held by this thread. */
|
||||
void
|
||||
crypto_thread_cleanup(void)
|
||||
{
|
||||
#ifdef ENABLE_OPENSSL
|
||||
crypto_openssl_thread_cleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninitialize the crypto library. Return 0 on success. Does not detect
|
||||
* failure.
|
||||
*/
|
||||
int
|
||||
crypto_global_cleanup(void)
|
||||
{
|
||||
crypto_dh_free_all();
|
||||
|
||||
#ifdef ENABLE_OPENSSL
|
||||
crypto_openssl_global_cleanup();
|
||||
#endif
|
||||
#ifdef ENABLE_NSS
|
||||
crypto_nss_global_cleanup();
|
||||
#endif
|
||||
|
||||
crypto_early_initialized_ = 0;
|
||||
crypto_global_initialized_ = 0;
|
||||
have_seeded_siphash = 0;
|
||||
siphash_unset_global_key();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Run operations that the crypto library requires to be happy again
|
||||
* after forking. */
|
||||
void
|
||||
crypto_prefork(void)
|
||||
{
|
||||
#ifdef ENABLE_NSS
|
||||
crypto_nss_prefork();
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Run operations that the crypto library requires to be happy again
|
||||
* after forking. */
|
||||
void
|
||||
crypto_postfork(void)
|
||||
{
|
||||
#ifdef ENABLE_NSS
|
||||
crypto_nss_postfork();
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Return the name of the crypto library we're using. */
|
||||
const char *
|
||||
crypto_get_library_name(void)
|
||||
{
|
||||
#ifdef ENABLE_OPENSSL
|
||||
return "OpenSSL";
|
||||
#endif
|
||||
#ifdef ENABLE_NSS
|
||||
return "NSS";
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Return the version of the crypto library we are using, as given in the
|
||||
* library. */
|
||||
const char *
|
||||
crypto_get_library_version_string(void)
|
||||
{
|
||||
#ifdef ENABLE_OPENSSL
|
||||
return crypto_openssl_get_version_str();
|
||||
#endif
|
||||
#ifdef ENABLE_NSS
|
||||
return crypto_nss_get_version_str();
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Return the version of the crypto library we're using, as given in the
|
||||
* headers. */
|
||||
const char *
|
||||
crypto_get_header_version_string(void)
|
||||
{
|
||||
#ifdef ENABLE_OPENSSL
|
||||
return crypto_openssl_get_header_version_str();
|
||||
#endif
|
||||
#ifdef ENABLE_NSS
|
||||
return crypto_nss_get_header_version_str();
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/* Copyright (c) 2001, Matej Pfajfar.
|
||||
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file crypto_init.h
|
||||
*
|
||||
* \brief Headers for crypto_init.c
|
||||
**/
|
||||
|
||||
#ifndef TOR_CRYPTO_INIT_H
|
||||
#define TOR_CRYPTO_INIT_H
|
||||
|
||||
#include "orconfig.h"
|
||||
#include "lib/cc/compat_compiler.h"
|
||||
|
||||
int crypto_init_siphash_key(void);
|
||||
int crypto_early_init(void) ATTR_WUR;
|
||||
int crypto_global_init(int hardwareAccel,
|
||||
const char *accelName,
|
||||
const char *accelPath) ATTR_WUR;
|
||||
|
||||
void crypto_thread_cleanup(void);
|
||||
int crypto_global_cleanup(void);
|
||||
void crypto_prefork(void);
|
||||
void crypto_postfork(void);
|
||||
|
||||
const char *crypto_get_library_name(void);
|
||||
const char *crypto_get_library_version_string(void);
|
||||
const char *crypto_get_header_version_string(void);
|
||||
|
||||
#endif /* !defined(TOR_CRYPTO_H) */
|
||||
@@ -0,0 +1,132 @@
|
||||
/* Copyright (c) 2001, Matej Pfajfar.
|
||||
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file crypto_nss_mgt.c
|
||||
*
|
||||
* \brief Manage the NSS library (if used)
|
||||
**/
|
||||
|
||||
#include "lib/crypt_ops/crypto_nss_mgt.h"
|
||||
|
||||
#include "lib/log/log.h"
|
||||
#include "lib/log/util_bug.h"
|
||||
#include "lib/string/printf.h"
|
||||
|
||||
DISABLE_GCC_WARNING(strict-prototypes)
|
||||
#include <nss.h>
|
||||
#include <pk11func.h>
|
||||
#include <ssl.h>
|
||||
|
||||
#include <prerror.h>
|
||||
#include <prtypes.h>
|
||||
#include <prinit.h>
|
||||
ENABLE_GCC_WARNING(strict-prototypes)
|
||||
|
||||
const char *
|
||||
crypto_nss_get_version_str(void)
|
||||
{
|
||||
return NSS_GetVersion();
|
||||
}
|
||||
const char *
|
||||
crypto_nss_get_header_version_str(void)
|
||||
{
|
||||
return NSS_VERSION;
|
||||
}
|
||||
|
||||
/** A password function that always returns NULL. */
|
||||
static char *
|
||||
nss_password_func_always_fail(PK11SlotInfo *slot,
|
||||
PRBool retry,
|
||||
void *arg)
|
||||
{
|
||||
(void) slot;
|
||||
(void) retry;
|
||||
(void) arg;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
crypto_nss_early_init(int nss_only)
|
||||
{
|
||||
if (! nss_only) {
|
||||
PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
|
||||
PK11_SetPasswordFunc(nss_password_func_always_fail);
|
||||
}
|
||||
|
||||
/* Eventually we should use NSS_Init() instead -- but that wants a
|
||||
directory. The documentation says that we can't use this if we want
|
||||
to use OpenSSL. */
|
||||
if (NSS_NoDB_Init(NULL) == SECFailure) {
|
||||
log_err(LD_CRYPTO, "Unable to initialize NSS.");
|
||||
crypto_nss_log_errors(LOG_ERR, "initializing NSS");
|
||||
tor_assert_unreached();
|
||||
}
|
||||
|
||||
if (NSS_SetDomesticPolicy() == SECFailure) {
|
||||
log_err(LD_CRYPTO, "Unable to set NSS cipher policy.");
|
||||
crypto_nss_log_errors(LOG_ERR, "setting cipher policy");
|
||||
tor_assert_unreached();
|
||||
}
|
||||
|
||||
/* We need to override the default here, or NSS will reject all the
|
||||
* legacy Tor certificates. */
|
||||
SECStatus rv = NSS_OptionSet(NSS_RSA_MIN_KEY_SIZE, 1024);
|
||||
if (rv != SECSuccess) {
|
||||
log_err(LD_CRYPTO, "Unable to set NSS min RSA key size");
|
||||
crypto_nss_log_errors(LOG_ERR, "setting cipher option.");
|
||||
tor_assert_unreached();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
crypto_nss_log_errors(int severity, const char *doing)
|
||||
{
|
||||
PRErrorCode code = PR_GetError();
|
||||
const char *string = PORT_ErrorToString(code);
|
||||
const char *name = PORT_ErrorToName(code);
|
||||
char buf[16];
|
||||
if (!string)
|
||||
string = "<unrecognized>";
|
||||
if (!name) {
|
||||
tor_snprintf(buf, sizeof(buf), "%d", code);
|
||||
name = buf;
|
||||
}
|
||||
if (doing) {
|
||||
tor_log(severity, LD_CRYPTO, "NSS error %s while %s: %s",
|
||||
name, doing, string);
|
||||
} else {
|
||||
tor_log(severity, LD_CRYPTO, "NSS error %s: %s", name, string);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
crypto_nss_late_init(void)
|
||||
{
|
||||
/* Possibly, SSL_OptionSetDefault? */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
crypto_nss_global_cleanup(void)
|
||||
{
|
||||
NSS_Shutdown();
|
||||
PL_ArenaFinish();
|
||||
PR_Cleanup();
|
||||
}
|
||||
|
||||
void
|
||||
crypto_nss_prefork(void)
|
||||
{
|
||||
NSS_Shutdown();
|
||||
}
|
||||
|
||||
void
|
||||
crypto_nss_postfork(void)
|
||||
{
|
||||
crypto_nss_early_init(1);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/* Copyright (c) 2001, Matej Pfajfar.
|
||||
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file crypto_nss_mgt.h
|
||||
*
|
||||
* \brief Headers for crypto_nss_mgt.c
|
||||
**/
|
||||
|
||||
#ifndef TOR_CRYPTO_NSS_MGT_H
|
||||
#define TOR_CRYPTO_NSS_MGT_H
|
||||
|
||||
#include "orconfig.h"
|
||||
|
||||
#ifdef ENABLE_NSS
|
||||
/* global nss state */
|
||||
const char *crypto_nss_get_version_str(void);
|
||||
const char *crypto_nss_get_header_version_str(void);
|
||||
|
||||
void crypto_nss_log_errors(int severity, const char *doing);
|
||||
|
||||
void crypto_nss_early_init(int nss_only);
|
||||
int crypto_nss_late_init(void);
|
||||
|
||||
void crypto_nss_global_cleanup(void);
|
||||
|
||||
void crypto_nss_prefork(void);
|
||||
void crypto_nss_postfork(void);
|
||||
#endif
|
||||
|
||||
#endif /* !defined(TOR_CRYPTO_NSS_H) */
|
||||
@@ -18,8 +18,8 @@
|
||||
|
||||
#define CRYPTO_OPE_PRIVATE
|
||||
#include "lib/crypt_ops/crypto_ope.h"
|
||||
#include "lib/crypt_ops/crypto.h"
|
||||
#include "lib/crypt_ops/crypto_util.h"
|
||||
#include "lib/crypt_ops/crypto_cipher.h"
|
||||
#include "lib/log/util_bug.h"
|
||||
#include "lib/malloc/malloc.h"
|
||||
#include "lib/arch/bytes.h"
|
||||
|
||||
@@ -12,8 +12,12 @@
|
||||
|
||||
#include "lib/crypt_ops/compat_openssl.h"
|
||||
#include "lib/crypt_ops/crypto_openssl_mgt.h"
|
||||
#include "lib/crypt_ops/crypto_rand.h"
|
||||
#include "lib/crypt_ops/aes.h"
|
||||
#include "lib/string/util_string.h"
|
||||
#include "lib/lock/compat_mutex.h"
|
||||
#include "lib/log/log.h"
|
||||
#include "lib/log/util_bug.h"
|
||||
#include "lib/testsupport/testsupport.h"
|
||||
#include "lib/thread/threads.h"
|
||||
|
||||
@@ -30,6 +34,7 @@ DISABLE_GCC_WARNING(redundant-decls)
|
||||
#include <openssl/conf.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
ENABLE_GCC_WARNING(redundant-decls)
|
||||
|
||||
@@ -49,6 +54,27 @@ STATIC void openssl_locking_cb_(int mode, int n, const char *file, int line);
|
||||
STATIC void tor_set_openssl_thread_id(CRYPTO_THREADID *threadid);
|
||||
#endif
|
||||
|
||||
/** Log all pending crypto errors at level <b>severity</b>. Use
|
||||
* <b>doing</b> to describe our current activities.
|
||||
*/
|
||||
void
|
||||
crypto_openssl_log_errors(int severity, const char *doing)
|
||||
{
|
||||
unsigned long err;
|
||||
const char *msg, *lib, *func;
|
||||
while ((err = ERR_get_error()) != 0) {
|
||||
msg = (const char*)ERR_reason_error_string(err);
|
||||
lib = (const char*)ERR_lib_error_string(err);
|
||||
func = (const char*)ERR_func_error_string(err);
|
||||
if (!msg) msg = "(null)";
|
||||
if (!lib) lib = "(null)";
|
||||
if (!func) func = "(null)";
|
||||
if (BUG(!doing)) doing = "(null)";
|
||||
tor_log(severity, LD_CRYPTO, "crypto error while %s: %s (in %s:%s)",
|
||||
doing, msg, lib, func);
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns a trimmed and human-readable version of an openssl version string
|
||||
* <b>raw_version</b>. They are usually in the form of 'OpenSSL 1.0.0b 10
|
||||
* May 2012' and this will parse them into a form similar to '1.0.0b' */
|
||||
@@ -127,7 +153,7 @@ tor_set_openssl_thread_id(CRYPTO_THREADID *threadid)
|
||||
|
||||
/** Helper: Construct mutexes, and set callbacks to help OpenSSL handle being
|
||||
* multithreaded. Returns 0. */
|
||||
int
|
||||
static int
|
||||
setup_openssl_threading(void)
|
||||
{
|
||||
#ifndef NEW_THREAD_API
|
||||
@@ -144,7 +170,7 @@ setup_openssl_threading(void)
|
||||
}
|
||||
|
||||
/** free OpenSSL variables */
|
||||
void
|
||||
static void
|
||||
crypto_openssl_free_all(void)
|
||||
{
|
||||
tor_free(crypto_openssl_version_str);
|
||||
@@ -164,3 +190,201 @@ crypto_openssl_free_all(void)
|
||||
}
|
||||
#endif /* !defined(NEW_THREAD_API) */
|
||||
}
|
||||
|
||||
/** Perform early (pre-configuration) initialization tasks for OpenSSL. */
|
||||
void
|
||||
crypto_openssl_early_init(void)
|
||||
{
|
||||
#ifdef OPENSSL_1_1_API
|
||||
OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS |
|
||||
OPENSSL_INIT_LOAD_CRYPTO_STRINGS |
|
||||
OPENSSL_INIT_ADD_ALL_CIPHERS |
|
||||
OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);
|
||||
#else
|
||||
ERR_load_crypto_strings();
|
||||
OpenSSL_add_all_algorithms();
|
||||
#endif
|
||||
|
||||
setup_openssl_threading();
|
||||
|
||||
unsigned long version_num = OpenSSL_version_num();
|
||||
const char *version_str = OpenSSL_version(OPENSSL_VERSION);
|
||||
if (version_num == OPENSSL_VERSION_NUMBER &&
|
||||
!strcmp(version_str, OPENSSL_VERSION_TEXT)) {
|
||||
log_info(LD_CRYPTO, "OpenSSL version matches version from headers "
|
||||
"(%lx: %s).", version_num, version_str);
|
||||
} else {
|
||||
log_warn(LD_CRYPTO, "OpenSSL version from headers does not match the "
|
||||
"version we're running with. If you get weird crashes, that "
|
||||
"might be why. (Compiled with %lx: %s; running with %lx: %s).",
|
||||
(unsigned long)OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_TEXT,
|
||||
version_num, version_str);
|
||||
}
|
||||
|
||||
crypto_force_rand_ssleay();
|
||||
}
|
||||
|
||||
#ifndef DISABLE_ENGINES
|
||||
/** Try to load an engine in a shared library via fully qualified path.
|
||||
*/
|
||||
static ENGINE *
|
||||
try_load_engine(const char *path, const char *engine)
|
||||
{
|
||||
ENGINE *e = ENGINE_by_id("dynamic");
|
||||
if (e) {
|
||||
if (!ENGINE_ctrl_cmd_string(e, "ID", engine, 0) ||
|
||||
!ENGINE_ctrl_cmd_string(e, "DIR_LOAD", "2", 0) ||
|
||||
!ENGINE_ctrl_cmd_string(e, "DIR_ADD", path, 0) ||
|
||||
!ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0)) {
|
||||
ENGINE_free(e);
|
||||
e = NULL;
|
||||
}
|
||||
}
|
||||
return e;
|
||||
}
|
||||
#endif /* !defined(DISABLE_ENGINES) */
|
||||
|
||||
#ifndef DISABLE_ENGINES
|
||||
/** Log any OpenSSL engines we're using at NOTICE. */
|
||||
static void
|
||||
log_engine(const char *fn, ENGINE *e)
|
||||
{
|
||||
if (e) {
|
||||
const char *name, *id;
|
||||
name = ENGINE_get_name(e);
|
||||
id = ENGINE_get_id(e);
|
||||
log_notice(LD_CRYPTO, "Default OpenSSL engine for %s is %s [%s]",
|
||||
fn, name?name:"?", id?id:"?");
|
||||
} else {
|
||||
log_info(LD_CRYPTO, "Using default implementation for %s", fn);
|
||||
}
|
||||
}
|
||||
#endif /* !defined(DISABLE_ENGINES) */
|
||||
|
||||
/** Initialize engines for openssl (if enabled). */
|
||||
static void
|
||||
crypto_openssl_init_engines(const char *accelName,
|
||||
const char *accelDir)
|
||||
{
|
||||
#ifdef DISABLE_ENGINES
|
||||
(void)accelName;
|
||||
(void)accelDir;
|
||||
log_warn(LD_CRYPTO, "No OpenSSL hardware acceleration support enabled.");
|
||||
#else
|
||||
ENGINE *e = NULL;
|
||||
|
||||
log_info(LD_CRYPTO, "Initializing OpenSSL engine support.");
|
||||
ENGINE_load_builtin_engines();
|
||||
ENGINE_register_all_complete();
|
||||
|
||||
if (accelName) {
|
||||
if (accelDir) {
|
||||
log_info(LD_CRYPTO, "Trying to load dynamic OpenSSL engine \"%s\""
|
||||
" via path \"%s\".", accelName, accelDir);
|
||||
e = try_load_engine(accelName, accelDir);
|
||||
} else {
|
||||
log_info(LD_CRYPTO, "Initializing dynamic OpenSSL engine \"%s\""
|
||||
" acceleration support.", accelName);
|
||||
e = ENGINE_by_id(accelName);
|
||||
}
|
||||
if (!e) {
|
||||
log_warn(LD_CRYPTO, "Unable to load dynamic OpenSSL engine \"%s\".",
|
||||
accelName);
|
||||
} else {
|
||||
log_info(LD_CRYPTO, "Loaded dynamic OpenSSL engine \"%s\".",
|
||||
accelName);
|
||||
}
|
||||
}
|
||||
if (e) {
|
||||
log_info(LD_CRYPTO, "Loaded OpenSSL hardware acceleration engine,"
|
||||
" setting default ciphers.");
|
||||
ENGINE_set_default(e, ENGINE_METHOD_ALL);
|
||||
}
|
||||
/* Log, if available, the intersection of the set of algorithms
|
||||
used by Tor and the set of algorithms available in the engine */
|
||||
log_engine("RSA", ENGINE_get_default_RSA());
|
||||
log_engine("DH", ENGINE_get_default_DH());
|
||||
#ifdef OPENSSL_1_1_API
|
||||
log_engine("EC", ENGINE_get_default_EC());
|
||||
#else
|
||||
log_engine("ECDH", ENGINE_get_default_ECDH());
|
||||
log_engine("ECDSA", ENGINE_get_default_ECDSA());
|
||||
#endif /* defined(OPENSSL_1_1_API) */
|
||||
log_engine("RAND", ENGINE_get_default_RAND());
|
||||
log_engine("RAND (which we will not use)", ENGINE_get_default_RAND());
|
||||
log_engine("SHA1", ENGINE_get_digest_engine(NID_sha1));
|
||||
log_engine("3DES-CBC", ENGINE_get_cipher_engine(NID_des_ede3_cbc));
|
||||
log_engine("AES-128-ECB", ENGINE_get_cipher_engine(NID_aes_128_ecb));
|
||||
log_engine("AES-128-CBC", ENGINE_get_cipher_engine(NID_aes_128_cbc));
|
||||
#ifdef NID_aes_128_ctr
|
||||
log_engine("AES-128-CTR", ENGINE_get_cipher_engine(NID_aes_128_ctr));
|
||||
#endif
|
||||
#ifdef NID_aes_128_gcm
|
||||
log_engine("AES-128-GCM", ENGINE_get_cipher_engine(NID_aes_128_gcm));
|
||||
#endif
|
||||
log_engine("AES-256-CBC", ENGINE_get_cipher_engine(NID_aes_256_cbc));
|
||||
#ifdef NID_aes_256_gcm
|
||||
log_engine("AES-256-GCM", ENGINE_get_cipher_engine(NID_aes_256_gcm));
|
||||
#endif
|
||||
|
||||
#endif /* defined(DISABLE_ENGINES) */
|
||||
}
|
||||
|
||||
/** Perform late (post-init) initialization tasks for OpenSSL */
|
||||
int
|
||||
crypto_openssl_late_init(int useAccel, const char *accelName,
|
||||
const char *accelDir)
|
||||
{
|
||||
if (useAccel > 0) {
|
||||
crypto_openssl_init_engines(accelName, accelDir);
|
||||
} else {
|
||||
log_info(LD_CRYPTO, "NOT using OpenSSL engine support.");
|
||||
}
|
||||
|
||||
if (crypto_force_rand_ssleay()) {
|
||||
if (crypto_seed_rng() < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
evaluate_evp_for_aes(-1);
|
||||
evaluate_ctr_for_aes();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Free crypto resources held by this thread. */
|
||||
void
|
||||
crypto_openssl_thread_cleanup(void)
|
||||
{
|
||||
#ifndef NEW_THREAD_API
|
||||
ERR_remove_thread_state(NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Clean up global resources held by openssl. */
|
||||
void
|
||||
crypto_openssl_global_cleanup(void)
|
||||
{
|
||||
#ifndef OPENSSL_1_1_API
|
||||
EVP_cleanup();
|
||||
#endif
|
||||
#ifndef NEW_THREAD_API
|
||||
ERR_remove_thread_state(NULL);
|
||||
#endif
|
||||
#ifndef OPENSSL_1_1_API
|
||||
ERR_free_strings();
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_ENGINES
|
||||
#ifndef OPENSSL_1_1_API
|
||||
ENGINE_cleanup();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
CONF_modules_unload(1);
|
||||
#ifndef OPENSSL_1_1_API
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
#endif
|
||||
|
||||
crypto_openssl_free_all();
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#define TOR_CRYPTO_OPENSSL_H
|
||||
|
||||
#include "orconfig.h"
|
||||
|
||||
#ifdef ENABLE_OPENSSL
|
||||
#include <openssl/engine.h>
|
||||
|
||||
/*
|
||||
@@ -69,14 +71,19 @@
|
||||
#define NEW_THREAD_API
|
||||
#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) && ... */
|
||||
|
||||
void crypto_openssl_log_errors(int severity, const char *doing);
|
||||
|
||||
/* global openssl state */
|
||||
const char * crypto_openssl_get_version_str(void);
|
||||
const char * crypto_openssl_get_header_version_str(void);
|
||||
|
||||
/* OpenSSL threading setup function */
|
||||
int setup_openssl_threading(void);
|
||||
void crypto_openssl_early_init(void);
|
||||
int crypto_openssl_late_init(int useAccel, const char *accelName,
|
||||
const char *accelDir);
|
||||
|
||||
/* 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) */
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/arch/bytes.h"
|
||||
#include "lib/crypt_ops/crypto.h"
|
||||
#include "lib/crypt_ops/crypto_cipher.h"
|
||||
#include "lib/crypt_ops/crypto_digest.h"
|
||||
#include "lib/crypt_ops/crypto_pwbox.h"
|
||||
#include "lib/crypt_ops/crypto_rand.h"
|
||||
|
||||
+111
-11
@@ -35,9 +35,24 @@
|
||||
#include "lib/testsupport/testsupport.h"
|
||||
#include "lib/fs/files.h"
|
||||
|
||||
#include "lib/defs/digest_sizes.h"
|
||||
#include "lib/crypt_ops/crypto_digest.h"
|
||||
|
||||
#ifdef ENABLE_NSS
|
||||
#include "lib/crypt_ops/crypto_nss_mgt.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_OPENSSL
|
||||
DISABLE_GCC_WARNING(redundant-decls)
|
||||
#include <openssl/rand.h>
|
||||
ENABLE_GCC_WARNING(redundant-decls)
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NSS
|
||||
#include <pk11pub.h>
|
||||
#include <secerr.h>
|
||||
#include <prerror.h>
|
||||
#endif
|
||||
|
||||
#if __GNUC__ && GCC_VERSION >= 402
|
||||
#if GCC_VERSION >= 406
|
||||
@@ -67,6 +82,7 @@ ENABLE_GCC_WARNING(redundant-decls)
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
/**
|
||||
* How many bytes of entropy we add at once.
|
||||
@@ -322,16 +338,24 @@ crypto_strongest_rand_raw(uint8_t *out, size_t out_len)
|
||||
void
|
||||
crypto_strongest_rand(uint8_t *out, size_t out_len)
|
||||
{
|
||||
#define DLEN SHA512_DIGEST_LENGTH
|
||||
#define DLEN DIGEST512_LEN
|
||||
|
||||
/* We're going to hash DLEN bytes from the system RNG together with some
|
||||
* bytes from the openssl PRNG, in order to yield DLEN bytes.
|
||||
* bytes from the PRNGs from our crypto librar(y/ies), in order to yield
|
||||
* DLEN bytes.
|
||||
*/
|
||||
uint8_t inp[DLEN*2];
|
||||
uint8_t inp[DLEN*3];
|
||||
uint8_t tmp[DLEN];
|
||||
tor_assert(out);
|
||||
while (out_len) {
|
||||
crypto_rand((char*) inp, DLEN);
|
||||
if (crypto_strongest_rand_raw(inp+DLEN, DLEN) < 0) {
|
||||
memset(inp, 0, sizeof(inp));
|
||||
#ifdef ENABLE_OPENSSL
|
||||
RAND_bytes(inp, DLEN);
|
||||
#endif
|
||||
#ifdef ENABLE_NSS
|
||||
PK11_GenerateRandom(inp+DLEN, DLEN);
|
||||
#endif
|
||||
if (crypto_strongest_rand_raw(inp+DLEN*2, DLEN) < 0) {
|
||||
// LCOV_EXCL_START
|
||||
log_err(LD_CRYPTO, "Failed to load strong entropy when generating an "
|
||||
"important key. Exiting.");
|
||||
@@ -340,11 +364,11 @@ crypto_strongest_rand(uint8_t *out, size_t out_len)
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
if (out_len >= DLEN) {
|
||||
SHA512(inp, sizeof(inp), out);
|
||||
crypto_digest512((char*)out, (char*)inp, sizeof(inp), DIGEST_SHA512);
|
||||
out += DLEN;
|
||||
out_len -= DLEN;
|
||||
} else {
|
||||
SHA512(inp, sizeof(inp), tmp);
|
||||
crypto_digest512((char*)tmp, (char*)inp, sizeof(inp), DIGEST_SHA512);
|
||||
memcpy(out, tmp, out_len);
|
||||
break;
|
||||
}
|
||||
@@ -354,12 +378,13 @@ crypto_strongest_rand(uint8_t *out, size_t out_len)
|
||||
#undef DLEN
|
||||
}
|
||||
|
||||
#ifdef ENABLE_OPENSSL
|
||||
/**
|
||||
* Seed OpenSSL's random number generator with bytes from the operating
|
||||
* system. Return 0 on success, -1 on failure.
|
||||
**/
|
||||
int
|
||||
crypto_seed_rng(void)
|
||||
static int
|
||||
crypto_seed_openssl_rng(void)
|
||||
{
|
||||
int rand_poll_ok = 0, load_entropy_ok = 0;
|
||||
uint8_t buf[ADD_ENTROPY];
|
||||
@@ -383,6 +408,52 @@ crypto_seed_rng(void)
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NSS
|
||||
/**
|
||||
* Seed OpenSSL's random number generator with bytes from the operating
|
||||
* system. Return 0 on success, -1 on failure.
|
||||
**/
|
||||
static int
|
||||
crypto_seed_nss_rng(void)
|
||||
{
|
||||
uint8_t buf[ADD_ENTROPY];
|
||||
|
||||
int load_entropy_ok = !crypto_strongest_rand_raw(buf, sizeof(buf));
|
||||
if (load_entropy_ok) {
|
||||
if (PK11_RandomUpdate(buf, sizeof(buf)) != SECSuccess) {
|
||||
load_entropy_ok = 0;
|
||||
}
|
||||
}
|
||||
|
||||
memwipe(buf, 0, sizeof(buf));
|
||||
|
||||
return load_entropy_ok ? 0 : -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Seed the RNG for any and all crypto libraries that we're using with bytes
|
||||
* from the operating system. Return 0 on success, -1 on failure.
|
||||
*/
|
||||
int
|
||||
crypto_seed_rng(void)
|
||||
{
|
||||
int seeded = 0;
|
||||
#ifdef ENABLE_NSS
|
||||
if (crypto_seed_nss_rng() < 0)
|
||||
return -1;
|
||||
++seeded;
|
||||
#endif
|
||||
#ifdef ENABLE_OPENSSL
|
||||
if (crypto_seed_openssl_rng() < 0)
|
||||
return -1;
|
||||
++seeded;
|
||||
#endif
|
||||
tor_assert(seeded);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write <b>n</b> bytes of strong random data to <b>to</b>. Supports mocking
|
||||
@@ -407,17 +478,44 @@ crypto_rand, (char *to, size_t n))
|
||||
void
|
||||
crypto_rand_unmocked(char *to, size_t n)
|
||||
{
|
||||
int r;
|
||||
if (n == 0)
|
||||
return;
|
||||
|
||||
tor_assert(n < INT_MAX);
|
||||
tor_assert(to);
|
||||
r = RAND_bytes((unsigned char*)to, (int)n);
|
||||
|
||||
#ifdef ENABLE_NSS
|
||||
SECStatus s = PK11_GenerateRandom((unsigned char*)to, (int)n);
|
||||
if (s != SECSuccess) {
|
||||
/* NSS rather sensibly might refuse to generate huge amounts of random
|
||||
* data at once. Unfortunately, our unit test do this in a couple of
|
||||
* places. To solve this issue, we use our XOF to stretch a shorter
|
||||
* output when a longer one is needed.
|
||||
*
|
||||
* Yes, this is secure. */
|
||||
|
||||
/* This is longer than it needs to be; 1600 bits == 200 bytes is the
|
||||
* state-size of SHA3. */
|
||||
#define BUFLEN 512
|
||||
tor_assert(PR_GetError() == SEC_ERROR_INVALID_ARGS && n > BUFLEN);
|
||||
unsigned char buf[BUFLEN];
|
||||
s = PK11_GenerateRandom(buf, BUFLEN);
|
||||
tor_assert(s == SECSuccess);
|
||||
crypto_xof_t *xof = crypto_xof_new();
|
||||
crypto_xof_add_bytes(xof, buf, BUFLEN);
|
||||
crypto_xof_squeeze_bytes(xof, (unsigned char *)to, n);
|
||||
crypto_xof_free(xof);
|
||||
memwipe(buf, 0, BUFLEN);
|
||||
|
||||
#undef BUFLEN
|
||||
}
|
||||
#else
|
||||
int r = RAND_bytes((unsigned char*)to, (int)n);
|
||||
/* We consider a PRNG failure non-survivable. Let's assert so that we get a
|
||||
* stack trace about where it happened.
|
||||
*/
|
||||
tor_assert(r >= 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -605,6 +703,7 @@ smartlist_shuffle(smartlist_t *sl)
|
||||
int
|
||||
crypto_force_rand_ssleay(void)
|
||||
{
|
||||
#ifdef ENABLE_OPENSSL
|
||||
RAND_METHOD *default_method;
|
||||
default_method = RAND_OpenSSL();
|
||||
if (RAND_get_rand_method() != default_method) {
|
||||
@@ -614,6 +713,7 @@ crypto_force_rand_ssleay(void)
|
||||
RAND_set_rand_method(default_method);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+239
-762
File diff suppressed because it is too large
Load Diff
@@ -29,6 +29,13 @@
|
||||
/** Number of bytes added for PKCS1-OAEP padding. */
|
||||
#define PKCS1_OAEP_PADDING_OVERHEAD 42
|
||||
|
||||
/** Length of encoded public key fingerprints, including space; but not
|
||||
* including terminating NUL. */
|
||||
#define FINGERPRINT_LEN 49
|
||||
|
||||
/** Value of 'e' to use in our public keys */
|
||||
#define TOR_RSA_EXPONENT 65537
|
||||
|
||||
/** A public key, or a public/private key-pair. */
|
||||
typedef struct crypto_pk_t crypto_pk_t;
|
||||
|
||||
@@ -57,7 +64,7 @@ int crypto_pk_read_private_key_from_string(crypto_pk_t *env,
|
||||
int crypto_pk_write_private_key_to_filename(crypto_pk_t *env,
|
||||
const char *fname);
|
||||
|
||||
int crypto_pk_check_key(crypto_pk_t *env);
|
||||
int crypto_pk_is_valid_private_key(const crypto_pk_t *env);
|
||||
int crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b);
|
||||
int crypto_pk_eq_keys(const crypto_pk_t *a, const crypto_pk_t *b);
|
||||
size_t crypto_pk_keysize(const crypto_pk_t *env);
|
||||
@@ -65,7 +72,7 @@ int crypto_pk_num_bits(crypto_pk_t *env);
|
||||
crypto_pk_t *crypto_pk_dup_key(crypto_pk_t *orig);
|
||||
crypto_pk_t *crypto_pk_copy_full(crypto_pk_t *orig);
|
||||
int crypto_pk_key_is_private(const crypto_pk_t *key);
|
||||
int crypto_pk_public_exponent_ok(crypto_pk_t *env);
|
||||
int crypto_pk_public_exponent_ok(const crypto_pk_t *env);
|
||||
int crypto_pk_obsolete_public_hybrid_encrypt(crypto_pk_t *env, char *to,
|
||||
size_t tolen,
|
||||
const char *from, size_t fromlen,
|
||||
@@ -86,8 +93,12 @@ int crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen,
|
||||
const char *from, size_t fromlen);
|
||||
int crypto_pk_asn1_encode(const crypto_pk_t *pk, char *dest, size_t dest_len);
|
||||
crypto_pk_t *crypto_pk_asn1_decode(const char *str, size_t len);
|
||||
int crypto_pk_asn1_encode_private(const crypto_pk_t *pk,
|
||||
char *dest, size_t dest_len);
|
||||
crypto_pk_t *crypto_pk_asn1_decode_private(const char *str, size_t len);
|
||||
int crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out,int add_space);
|
||||
int crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out);
|
||||
void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in);
|
||||
|
||||
MOCK_DECL(int, crypto_pk_public_checksig_digest,(crypto_pk_t *env,
|
||||
const char *data, size_t datalen, const char *sig, size_t siglen));
|
||||
@@ -96,20 +107,39 @@ int crypto_pk_private_sign_digest(crypto_pk_t *env, char *to, size_t tolen,
|
||||
int crypto_pk_get_digest(const crypto_pk_t *pk, char *digest_out);
|
||||
int crypto_pk_get_common_digests(crypto_pk_t *pk,
|
||||
common_digests_t *digests_out);
|
||||
int crypto_pk_base64_encode(const crypto_pk_t *pk, char **priv_out);
|
||||
crypto_pk_t *crypto_pk_base64_decode(const char *str, size_t len);
|
||||
int crypto_pk_base64_encode_private(const crypto_pk_t *pk, char **priv_out);
|
||||
crypto_pk_t *crypto_pk_base64_decode_private(const char *str, size_t len);
|
||||
|
||||
#ifdef ENABLE_OPENSSL
|
||||
/* Prototypes for private functions only used by tortls.c, crypto.c, and the
|
||||
* unit tests. */
|
||||
struct rsa_st;
|
||||
struct rsa_st *crypto_pk_get_rsa_(crypto_pk_t *env);
|
||||
crypto_pk_t *crypto_new_pk_from_rsa_(struct rsa_st *rsa);
|
||||
MOCK_DECL(struct evp_pkey_st *, crypto_pk_get_evp_pkey_,(crypto_pk_t *env,
|
||||
int private));
|
||||
struct evp_pkey_st;
|
||||
struct rsa_st *crypto_pk_get_openssl_rsa_(crypto_pk_t *env);
|
||||
crypto_pk_t *crypto_new_pk_from_openssl_rsa_(struct rsa_st *rsa);
|
||||
MOCK_DECL(struct evp_pkey_st *, crypto_pk_get_openssl_evp_pkey_,(
|
||||
crypto_pk_t *env,int private));
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NSS
|
||||
struct SECKEYPublicKeyStr;
|
||||
struct SECKEYPrivateKeyStr;
|
||||
crypto_pk_t *crypto_pk_new_from_nss_pubkey(struct SECKEYPublicKeyStr *pub);
|
||||
const struct SECKEYPublicKeyStr *crypto_pk_get_nss_pubkey(
|
||||
const crypto_pk_t *key);
|
||||
const struct SECKEYPrivateKeyStr *crypto_pk_get_nss_privkey(
|
||||
const crypto_pk_t *key);
|
||||
#endif
|
||||
|
||||
void crypto_pk_assign_public(crypto_pk_t *dest, const crypto_pk_t *src);
|
||||
void crypto_pk_assign_private(crypto_pk_t *dest, const crypto_pk_t *src);
|
||||
|
||||
#ifdef TOR_UNIT_TESTS
|
||||
void crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src);
|
||||
#ifdef ENABLE_NSS
|
||||
struct SECItemStr;
|
||||
STATIC int secitem_uint_cmp(const struct SECItemStr *a,
|
||||
const struct SECItemStr *b);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,738 @@
|
||||
/* Copyright (c) 2001, Matej Pfajfar.
|
||||
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file crypto_rsa.c
|
||||
* \brief NSS implementations of our RSA code.
|
||||
**/
|
||||
|
||||
#include "lib/crypt_ops/crypto_rsa.h"
|
||||
|
||||
#include "lib/crypt_ops/crypto_nss_mgt.h"
|
||||
#include "lib/crypt_ops/crypto_util.h"
|
||||
#include "lib/ctime/di_ops.h"
|
||||
#include "lib/encoding/binascii.h"
|
||||
#include "lib/fs/files.h"
|
||||
#include "lib/intmath/cmp.h"
|
||||
#include "lib/intmath/muldiv.h"
|
||||
#include "lib/log/log.h"
|
||||
#include "lib/log/util_bug.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <keyhi.h>
|
||||
#include <pk11pub.h>
|
||||
#include <secder.h>
|
||||
|
||||
#ifdef ENABLE_OPENSSL
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/evp.h>
|
||||
#endif
|
||||
|
||||
/** Declaration for crypto_pk_t structure. */
|
||||
struct crypto_pk_t
|
||||
{
|
||||
SECKEYPrivateKey *seckey;
|
||||
SECKEYPublicKey *pubkey;
|
||||
};
|
||||
|
||||
/** Return true iff <b>key</b> contains the private-key portion of the RSA
|
||||
* key. */
|
||||
int
|
||||
crypto_pk_key_is_private(const crypto_pk_t *key)
|
||||
{
|
||||
return key && key->seckey;
|
||||
}
|
||||
|
||||
/** used by tortls.c: wrap a SecKEYPublicKey in a crypto_pk_t. Take ownership
|
||||
* of the RSA object. */
|
||||
crypto_pk_t *
|
||||
crypto_pk_new_from_nss_pubkey(struct SECKEYPublicKeyStr *pub)
|
||||
{
|
||||
crypto_pk_t *result = tor_malloc_zero(sizeof(crypto_pk_t));
|
||||
result->pubkey = pub;
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Return the SECKEYPublicKey for the provided crypto_pk_t. */
|
||||
const SECKEYPublicKey *
|
||||
crypto_pk_get_nss_pubkey(const crypto_pk_t *key)
|
||||
{
|
||||
tor_assert(key);
|
||||
return key->pubkey;
|
||||
}
|
||||
|
||||
/** Return the SECKEYPrivateKey for the provided crypto_pk_t, or NULL if it
|
||||
* does not exist. */
|
||||
const SECKEYPrivateKey *
|
||||
crypto_pk_get_nss_privkey(const crypto_pk_t *key)
|
||||
{
|
||||
tor_assert(key);
|
||||
return key->seckey;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_OPENSSL
|
||||
/** used by tortls.c: wrap an RSA* in a crypto_pk_t. Take ownership of the
|
||||
* RSA object. */
|
||||
crypto_pk_t *
|
||||
crypto_new_pk_from_openssl_rsa_(RSA *rsa)
|
||||
{
|
||||
crypto_pk_t *pk = NULL;
|
||||
unsigned char *buf = NULL;
|
||||
int len = i2d_RSAPublicKey(rsa, &buf);
|
||||
RSA_free(rsa);
|
||||
|
||||
if (len < 0 || buf == NULL)
|
||||
goto end;
|
||||
|
||||
pk = crypto_pk_asn1_decode((const char *)buf, len);
|
||||
|
||||
end:
|
||||
if (buf)
|
||||
OPENSSL_free(buf);
|
||||
return pk;
|
||||
}
|
||||
|
||||
/** Helper, used by tor-gencert.c. Return the RSA from a
|
||||
* crypto_pk_t. */
|
||||
struct rsa_st *
|
||||
crypto_pk_get_openssl_rsa_(crypto_pk_t *pk)
|
||||
{
|
||||
size_t buflen = crypto_pk_keysize(pk)*16;
|
||||
unsigned char *buf = tor_malloc_zero(buflen);
|
||||
const unsigned char *cp = buf;
|
||||
RSA *rsa = NULL;
|
||||
|
||||
int used = crypto_pk_asn1_encode_private(pk, (char*)buf, buflen);
|
||||
if (used < 0)
|
||||
goto end;
|
||||
rsa = d2i_RSAPrivateKey(NULL, &cp, used);
|
||||
|
||||
end:
|
||||
memwipe(buf, 0, buflen);
|
||||
tor_free(buf);
|
||||
return rsa;
|
||||
}
|
||||
|
||||
/** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_t. Iff
|
||||
* private is set, include the private-key portion of the key. Return a valid
|
||||
* pointer on success, and NULL on failure. */
|
||||
MOCK_IMPL(struct evp_pkey_st *,
|
||||
crypto_pk_get_openssl_evp_pkey_,(crypto_pk_t *pk, int private))
|
||||
{
|
||||
size_t buflen = crypto_pk_keysize(pk)*16;
|
||||
unsigned char *buf = tor_malloc_zero(buflen);
|
||||
const unsigned char *cp = buf;
|
||||
RSA *rsa = NULL;
|
||||
EVP_PKEY *result = NULL;
|
||||
|
||||
if (private) {
|
||||
int len = crypto_pk_asn1_encode_private(pk, (char*)buf, buflen);
|
||||
if (len < 0)
|
||||
goto end;
|
||||
rsa = d2i_RSAPrivateKey(NULL, &cp, len);
|
||||
} else {
|
||||
int len = crypto_pk_asn1_encode(pk, (char*)buf, buflen);
|
||||
if (len < 0)
|
||||
goto end;
|
||||
rsa = d2i_RSAPublicKey(NULL, &cp, len);
|
||||
}
|
||||
if (!rsa)
|
||||
goto end;
|
||||
|
||||
if (!(result = EVP_PKEY_new()))
|
||||
goto end;
|
||||
if (!(EVP_PKEY_assign_RSA(result, rsa))) {
|
||||
EVP_PKEY_free(result);
|
||||
RSA_free(rsa);
|
||||
result = NULL;
|
||||
}
|
||||
|
||||
end:
|
||||
memwipe(buf, 0, buflen);
|
||||
tor_free(buf);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Allocate and return storage for a public key. The key itself will not yet
|
||||
* be set.
|
||||
*/
|
||||
MOCK_IMPL(crypto_pk_t *,
|
||||
crypto_pk_new,(void))
|
||||
{
|
||||
crypto_pk_t *result = tor_malloc_zero(sizeof(crypto_pk_t));
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Release the NSS objects held in <b>key</b> */
|
||||
static void
|
||||
crypto_pk_clear(crypto_pk_t *key)
|
||||
{
|
||||
if (key->pubkey)
|
||||
SECKEY_DestroyPublicKey(key->pubkey);
|
||||
if (key->seckey)
|
||||
SECKEY_DestroyPrivateKey(key->seckey);
|
||||
memset(key, 0, sizeof(crypto_pk_t));
|
||||
}
|
||||
|
||||
/** Release a reference to an asymmetric key; when all the references
|
||||
* are released, free the key.
|
||||
*/
|
||||
void
|
||||
crypto_pk_free_(crypto_pk_t *key)
|
||||
{
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
crypto_pk_clear(key);
|
||||
|
||||
tor_free(key);
|
||||
}
|
||||
|
||||
/** Generate a <b>bits</b>-bit new public/private keypair in <b>env</b>.
|
||||
* Return 0 on success, -1 on failure.
|
||||
*/
|
||||
MOCK_IMPL(int,
|
||||
crypto_pk_generate_key_with_bits,(crypto_pk_t *key, int bits))
|
||||
{
|
||||
tor_assert(key);
|
||||
|
||||
PK11RSAGenParams params = {
|
||||
.keySizeInBits = bits,
|
||||
.pe = TOR_RSA_EXPONENT
|
||||
};
|
||||
|
||||
int result = -1;
|
||||
PK11SlotInfo *slot = PK11_GetBestSlot(CKM_RSA_PKCS_KEY_PAIR_GEN, NULL);
|
||||
SECKEYPrivateKey *seckey = NULL;
|
||||
SECKEYPublicKey *pubkey = NULL;
|
||||
|
||||
if (!slot) {
|
||||
crypto_nss_log_errors(LOG_WARN, "getting slot for RSA keygen");
|
||||
goto done;
|
||||
}
|
||||
|
||||
seckey = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, ¶ms,
|
||||
&pubkey,
|
||||
PR_FALSE /*isPerm */,
|
||||
PR_FALSE /*isSensitive*/,
|
||||
NULL);
|
||||
if (seckey == NULL || pubkey == NULL) {
|
||||
crypto_nss_log_errors(LOG_WARN, "generating an RSA key");
|
||||
goto done;
|
||||
}
|
||||
|
||||
crypto_pk_clear(key);
|
||||
key->seckey = seckey;
|
||||
key->pubkey = pubkey;
|
||||
seckey = NULL;
|
||||
pubkey = NULL;
|
||||
|
||||
result = 0;
|
||||
done:
|
||||
if (slot)
|
||||
PK11_FreeSlot(slot);
|
||||
if (pubkey)
|
||||
SECKEY_DestroyPublicKey(pubkey);
|
||||
if (seckey)
|
||||
SECKEY_DestroyPrivateKey(seckey);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Return true iff <b>env</b> is a valid private key.
|
||||
*/
|
||||
int
|
||||
crypto_pk_is_valid_private_key(const crypto_pk_t *key)
|
||||
{
|
||||
/* We don't need to do validation here, since unlike OpenSSL, NSS won't let
|
||||
* us load private keys without validating them. */
|
||||
return key && key->seckey;
|
||||
}
|
||||
|
||||
/** Return true iff <b>env</b> contains a public key whose public exponent
|
||||
* equals 65537.
|
||||
*/
|
||||
int
|
||||
crypto_pk_public_exponent_ok(const crypto_pk_t *key)
|
||||
{
|
||||
return key &&
|
||||
key->pubkey &&
|
||||
key->pubkey->keyType == rsaKey &&
|
||||
DER_GetUInteger(&key->pubkey->u.rsa.publicExponent) == TOR_RSA_EXPONENT;
|
||||
}
|
||||
|
||||
/** Compare two big-endian integers stored in a and b; return a tristate.
|
||||
*/
|
||||
STATIC int
|
||||
secitem_uint_cmp(const SECItem *a, const SECItem *b)
|
||||
{
|
||||
const unsigned abits = SECKEY_BigIntegerBitLength(a);
|
||||
const unsigned bbits = SECKEY_BigIntegerBitLength(b);
|
||||
|
||||
if (abits < bbits)
|
||||
return -1;
|
||||
else if (abits > bbits)
|
||||
return 1;
|
||||
|
||||
/* okay, they have the same number of bits set. Get a pair of aligned
|
||||
* pointers to their bytes that are set... */
|
||||
const unsigned nbytes = CEIL_DIV(abits, 8);
|
||||
tor_assert(nbytes <= a->len);
|
||||
tor_assert(nbytes <= b->len);
|
||||
|
||||
const unsigned char *aptr = a->data + (a->len - nbytes);
|
||||
const unsigned char *bptr = b->data + (b->len - nbytes);
|
||||
|
||||
/* And compare them. */
|
||||
return fast_memcmp(aptr, bptr, nbytes);
|
||||
}
|
||||
|
||||
/** Compare the public-key components of a and b. Return less than 0
|
||||
* if a\<b, 0 if a==b, and greater than 0 if a\>b. A NULL key is
|
||||
* considered to be less than all non-NULL keys, and equal to itself.
|
||||
*
|
||||
* Note that this may leak information about the keys through timing.
|
||||
*/
|
||||
int
|
||||
crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b)
|
||||
{
|
||||
int result;
|
||||
char a_is_non_null = (a != NULL) && (a->pubkey != NULL);
|
||||
char b_is_non_null = (b != NULL) && (b->pubkey != NULL);
|
||||
char an_argument_is_null = !a_is_non_null | !b_is_non_null;
|
||||
|
||||
result = tor_memcmp(&a_is_non_null, &b_is_non_null, sizeof(a_is_non_null));
|
||||
if (an_argument_is_null)
|
||||
return result;
|
||||
|
||||
// This is all Tor uses with this structure.
|
||||
tor_assert(a->pubkey->keyType == rsaKey);
|
||||
tor_assert(b->pubkey->keyType == rsaKey);
|
||||
|
||||
const SECItem *a_n, *a_e, *b_n, *b_e;
|
||||
a_n = &a->pubkey->u.rsa.modulus;
|
||||
b_n = &b->pubkey->u.rsa.modulus;
|
||||
a_e = &a->pubkey->u.rsa.publicExponent;
|
||||
b_e = &b->pubkey->u.rsa.publicExponent;
|
||||
|
||||
result = secitem_uint_cmp(a_n, b_n);
|
||||
if (result)
|
||||
return result;
|
||||
return secitem_uint_cmp(a_e, b_e);
|
||||
}
|
||||
|
||||
/** Return the size of the public key modulus in <b>env</b>, in bytes. */
|
||||
size_t
|
||||
crypto_pk_keysize(const crypto_pk_t *key)
|
||||
{
|
||||
tor_assert(key);
|
||||
tor_assert(key->pubkey);
|
||||
return SECKEY_PublicKeyStrength(key->pubkey);
|
||||
}
|
||||
|
||||
/** Return the size of the public key modulus of <b>env</b>, in bits. */
|
||||
int
|
||||
crypto_pk_num_bits(crypto_pk_t *key)
|
||||
{
|
||||
tor_assert(key);
|
||||
tor_assert(key->pubkey);
|
||||
return SECKEY_PublicKeyStrengthInBits(key->pubkey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a copy of <b>key</b> and return it.
|
||||
*/
|
||||
crypto_pk_t *
|
||||
crypto_pk_dup_key(crypto_pk_t *key)
|
||||
{
|
||||
crypto_pk_t *result = crypto_pk_new();
|
||||
if (key->pubkey)
|
||||
result->pubkey = SECKEY_CopyPublicKey(key->pubkey);
|
||||
if (key->seckey)
|
||||
result->seckey = SECKEY_CopyPrivateKey(key->seckey);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** For testing: replace dest with src. (Dest must have a refcount
|
||||
* of 1) */
|
||||
void
|
||||
crypto_pk_assign_public(crypto_pk_t *dest, const crypto_pk_t *src)
|
||||
{
|
||||
crypto_pk_clear(dest);
|
||||
if (src->pubkey)
|
||||
dest->pubkey = SECKEY_CopyPublicKey(src->pubkey);
|
||||
}
|
||||
|
||||
/** For testing: replace dest with src. (Dest must have a refcount
|
||||
* of 1) */
|
||||
void
|
||||
crypto_pk_assign_private(crypto_pk_t *dest, const crypto_pk_t *src)
|
||||
{
|
||||
crypto_pk_clear(dest);
|
||||
if (src->pubkey)
|
||||
dest->pubkey = SECKEY_CopyPublicKey(src->pubkey);
|
||||
if (src->seckey)
|
||||
dest->seckey = SECKEY_CopyPrivateKey(src->seckey);
|
||||
}
|
||||
|
||||
/** Make a real honest-to-goodness copy of <b>env</b>, and return it.
|
||||
* Returns NULL on failure. */
|
||||
crypto_pk_t *
|
||||
crypto_pk_copy_full(crypto_pk_t *key)
|
||||
{
|
||||
// These aren't reference-counted is nss, so it's fine to just
|
||||
// use the same function.
|
||||
return crypto_pk_dup_key(key);
|
||||
}
|
||||
|
||||
static const CK_RSA_PKCS_OAEP_PARAMS oaep_params = {
|
||||
.hashAlg = CKM_SHA_1,
|
||||
.mgf = CKG_MGF1_SHA1,
|
||||
.source = CKZ_DATA_SPECIFIED,
|
||||
.pSourceData = NULL,
|
||||
.ulSourceDataLen = 0
|
||||
};
|
||||
static const SECItem oaep_item = {
|
||||
.type = siBuffer,
|
||||
.data = (unsigned char *) &oaep_params,
|
||||
.len = sizeof(oaep_params)
|
||||
};
|
||||
|
||||
/** Return the mechanism code and parameters for a given padding method when
|
||||
* used with RSA */
|
||||
static CK_MECHANISM_TYPE
|
||||
padding_to_mechanism(int padding, SECItem **item_out)
|
||||
{
|
||||
switch (padding) {
|
||||
case PK_PKCS1_OAEP_PADDING:
|
||||
*item_out = (SECItem *)&oaep_item;
|
||||
return CKM_RSA_PKCS_OAEP;
|
||||
default:
|
||||
tor_assert_unreached();
|
||||
*item_out = NULL;
|
||||
return CKM_INVALID_MECHANISM;
|
||||
}
|
||||
}
|
||||
|
||||
/** Encrypt <b>fromlen</b> bytes from <b>from</b> with the public key
|
||||
* in <b>env</b>, using the padding method <b>padding</b>. On success,
|
||||
* write the result to <b>to</b>, and return the number of bytes
|
||||
* written. On failure, return -1.
|
||||
*
|
||||
* <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
|
||||
* at least the length of the modulus of <b>env</b>.
|
||||
*/
|
||||
int
|
||||
crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen,
|
||||
const char *from, size_t fromlen, int padding)
|
||||
{
|
||||
tor_assert(env);
|
||||
tor_assert(to);
|
||||
tor_assert(from);
|
||||
tor_assert(tolen < INT_MAX);
|
||||
tor_assert(fromlen < INT_MAX);
|
||||
|
||||
if (BUG(! env->pubkey))
|
||||
return -1;
|
||||
|
||||
unsigned int result_len = 0;
|
||||
SECItem *item = NULL;
|
||||
CK_MECHANISM_TYPE m = padding_to_mechanism(padding, &item);
|
||||
|
||||
SECStatus s = PK11_PubEncrypt(env->pubkey, m, item,
|
||||
(unsigned char *)to, &result_len,
|
||||
(unsigned int)tolen,
|
||||
(const unsigned char *)from,
|
||||
(unsigned int)fromlen,
|
||||
NULL);
|
||||
if (s != SECSuccess) {
|
||||
crypto_nss_log_errors(LOG_WARN, "encrypting to an RSA key");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int)result_len;
|
||||
}
|
||||
|
||||
/** Decrypt <b>fromlen</b> bytes from <b>from</b> with the private key
|
||||
* in <b>env</b>, using the padding method <b>padding</b>. On success,
|
||||
* write the result to <b>to</b>, and return the number of bytes
|
||||
* written. On failure, return -1.
|
||||
*
|
||||
* <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
|
||||
* at least the length of the modulus of <b>key</b>.
|
||||
*/
|
||||
int
|
||||
crypto_pk_private_decrypt(crypto_pk_t *key, char *to,
|
||||
size_t tolen,
|
||||
const char *from, size_t fromlen,
|
||||
int padding, int warnOnFailure)
|
||||
{
|
||||
tor_assert(key);
|
||||
tor_assert(to);
|
||||
tor_assert(from);
|
||||
tor_assert(tolen < INT_MAX);
|
||||
tor_assert(fromlen < INT_MAX);
|
||||
|
||||
if (!crypto_pk_key_is_private(key))
|
||||
return -1; /* Not a private key. */
|
||||
|
||||
unsigned int result_len = 0;
|
||||
SECItem *item = NULL;
|
||||
CK_MECHANISM_TYPE m = padding_to_mechanism(padding, &item);
|
||||
SECStatus s = PK11_PrivDecrypt(key->seckey, m, item,
|
||||
(unsigned char *)to, &result_len,
|
||||
(unsigned int)tolen,
|
||||
(const unsigned char *)from,
|
||||
(unsigned int)fromlen);
|
||||
|
||||
if (s != SECSuccess) {
|
||||
const int severity = warnOnFailure ? LOG_WARN : LOG_INFO;
|
||||
crypto_nss_log_errors(severity, "decrypting with an RSA key");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int)result_len;
|
||||
}
|
||||
|
||||
/** Check the signature in <b>from</b> (<b>fromlen</b> bytes long) with the
|
||||
* public key in <b>key</b>, using PKCS1 padding. On success, write the
|
||||
* signed data to <b>to</b>, and return the number of bytes written.
|
||||
* On failure, return -1.
|
||||
*
|
||||
* <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
|
||||
* at least the length of the modulus of <b>key</b>.
|
||||
*/
|
||||
MOCK_IMPL(int,
|
||||
crypto_pk_public_checksig,(const crypto_pk_t *key, char *to,
|
||||
size_t tolen,
|
||||
const char *from, size_t fromlen))
|
||||
{
|
||||
tor_assert(key);
|
||||
tor_assert(to);
|
||||
tor_assert(from);
|
||||
tor_assert(tolen < INT_MAX);
|
||||
tor_assert(fromlen < INT_MAX);
|
||||
tor_assert(key->pubkey);
|
||||
|
||||
SECItem sig = {
|
||||
.type = siBuffer,
|
||||
.data = (unsigned char *) from,
|
||||
.len = (unsigned int) fromlen,
|
||||
};
|
||||
SECItem dsig = {
|
||||
.type = siBuffer,
|
||||
.data = (unsigned char *) to,
|
||||
.len = (unsigned int) tolen
|
||||
};
|
||||
SECStatus s;
|
||||
s = PK11_VerifyRecover(key->pubkey, &sig, &dsig, NULL);
|
||||
if (s != SECSuccess)
|
||||
return -1;
|
||||
|
||||
return (int)dsig.len;
|
||||
}
|
||||
|
||||
/** Sign <b>fromlen</b> bytes of data from <b>from</b> with the private key in
|
||||
* <b>env</b>, using PKCS1 padding. On success, write the signature to
|
||||
* <b>to</b>, and return the number of bytes written. On failure, return
|
||||
* -1.
|
||||
*
|
||||
* <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
|
||||
* at least the length of the modulus of <b>env</b>.
|
||||
*/
|
||||
int
|
||||
crypto_pk_private_sign(const crypto_pk_t *key, char *to, size_t tolen,
|
||||
const char *from, size_t fromlen)
|
||||
{
|
||||
tor_assert(key);
|
||||
tor_assert(to);
|
||||
tor_assert(from);
|
||||
tor_assert(tolen < INT_MAX);
|
||||
tor_assert(fromlen < INT_MAX);
|
||||
|
||||
if (BUG(!crypto_pk_key_is_private(key)))
|
||||
return -1;
|
||||
|
||||
SECItem sig = {
|
||||
.type = siBuffer,
|
||||
.data = (unsigned char *)to,
|
||||
.len = (unsigned int) tolen
|
||||
};
|
||||
SECItem hash = {
|
||||
.type = siBuffer,
|
||||
.data = (unsigned char *)from,
|
||||
.len = (unsigned int) fromlen
|
||||
};
|
||||
CK_MECHANISM_TYPE m = CKM_RSA_PKCS;
|
||||
SECStatus s = PK11_SignWithMechanism(key->seckey, m, NULL,
|
||||
&sig, &hash);
|
||||
|
||||
if (s != SECSuccess) {
|
||||
crypto_nss_log_errors(LOG_WARN, "signing with an RSA key");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int)sig.len;
|
||||
}
|
||||
|
||||
/* "This has lead to people trading hard-to-find object identifiers and ASN.1
|
||||
* definitions like baseball cards" - Peter Gutmann, "X.509 Style Guide". */
|
||||
static const unsigned char RSA_OID[] = {
|
||||
/* RSADSI */ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
|
||||
/* PKCS1 */ 0x01, 0x01,
|
||||
/* RSA */ 0x01
|
||||
};
|
||||
|
||||
/** ASN.1-encode the public portion of <b>pk</b> into <b>dest</b>.
|
||||
* Return -1 on error, or the number of characters used on success.
|
||||
*/
|
||||
int
|
||||
crypto_pk_asn1_encode(const crypto_pk_t *pk, char *dest, size_t dest_len)
|
||||
{
|
||||
tor_assert(pk);
|
||||
if (pk->pubkey == NULL)
|
||||
return -1;
|
||||
|
||||
CERTSubjectPublicKeyInfo *info;
|
||||
info = SECKEY_CreateSubjectPublicKeyInfo(pk->pubkey);
|
||||
if (! info)
|
||||
return -1;
|
||||
|
||||
const SECItem *item = &info->subjectPublicKey;
|
||||
size_t actual_len = (item->len) >> 3; /* bits to bytes */
|
||||
size_t n_used = MIN(actual_len, dest_len);
|
||||
memcpy(dest, item->data, n_used);
|
||||
|
||||
SECKEY_DestroySubjectPublicKeyInfo(info);
|
||||
return (int) n_used;
|
||||
}
|
||||
|
||||
/** Decode an ASN.1-encoded public key from <b>str</b>; return the result on
|
||||
* success and NULL on failure.
|
||||
*/
|
||||
crypto_pk_t *
|
||||
crypto_pk_asn1_decode(const char *str, size_t len)
|
||||
{
|
||||
tor_assert(str);
|
||||
if (len >= INT_MAX)
|
||||
return NULL;
|
||||
CERTSubjectPublicKeyInfo info = {
|
||||
.algorithm = {
|
||||
.algorithm = {
|
||||
.type = siDEROID,
|
||||
.data = (unsigned char *)RSA_OID,
|
||||
.len = sizeof(RSA_OID)
|
||||
}
|
||||
},
|
||||
.subjectPublicKey = {
|
||||
.type = siBuffer,
|
||||
.data = (unsigned char *)str,
|
||||
.len = (unsigned int)(len << 3) /* bytes to bits */
|
||||
}
|
||||
};
|
||||
|
||||
SECKEYPublicKey *pub = SECKEY_ExtractPublicKey(&info);
|
||||
if (pub == NULL)
|
||||
return NULL;
|
||||
|
||||
crypto_pk_t *result = crypto_pk_new();
|
||||
result->pubkey = pub;
|
||||
return result;
|
||||
}
|
||||
|
||||
DISABLE_GCC_WARNING(unused-parameter)
|
||||
|
||||
/** Given a crypto_pk_t <b>pk</b>, allocate a new buffer containing the Base64
|
||||
* encoding of the DER representation of the private key into the
|
||||
* <b>dest_len</b>-byte buffer in <b>dest</b>.
|
||||
* Return the number of bytes written on success, -1 on failure.
|
||||
*/
|
||||
int
|
||||
crypto_pk_asn1_encode_private(const crypto_pk_t *pk,
|
||||
char *dest, size_t destlen)
|
||||
{
|
||||
tor_assert(destlen <= INT_MAX);
|
||||
if (!crypto_pk_key_is_private(pk))
|
||||
return -1;
|
||||
|
||||
SECKEYPrivateKeyInfo *info = PK11_ExportPrivKeyInfo(pk->seckey, NULL);
|
||||
if (!info)
|
||||
return -1;
|
||||
SECItem *item = &info->privateKey;
|
||||
|
||||
if (destlen < item->len) {
|
||||
SECKEY_DestroyPrivateKeyInfo(info, PR_TRUE);
|
||||
return -1;
|
||||
}
|
||||
int result = (int)item->len;
|
||||
memcpy(dest, item->data, item->len);
|
||||
SECKEY_DestroyPrivateKeyInfo(info, PR_TRUE);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Given a buffer containing the DER representation of the
|
||||
* private key <b>str</b>, decode and return the result on success, or NULL
|
||||
* on failure.
|
||||
*/
|
||||
crypto_pk_t *
|
||||
crypto_pk_asn1_decode_private(const char *str, size_t len)
|
||||
{
|
||||
tor_assert(str);
|
||||
tor_assert(len < INT_MAX);
|
||||
PK11SlotInfo *slot = PK11_GetBestSlot(CKM_RSA_PKCS, NULL);
|
||||
if (!slot)
|
||||
return NULL;
|
||||
|
||||
SECKEYPrivateKeyInfo info = {
|
||||
.algorithm = {
|
||||
.algorithm = {
|
||||
.type = siBuffer,
|
||||
.data = (unsigned char *)RSA_OID,
|
||||
.len = sizeof(RSA_OID)
|
||||
}
|
||||
},
|
||||
.privateKey = {
|
||||
.type = siBuffer,
|
||||
.data = (unsigned char *)str,
|
||||
.len = (int)len,
|
||||
}
|
||||
};
|
||||
|
||||
SECStatus s;
|
||||
SECKEYPrivateKey *seckey = NULL;
|
||||
|
||||
s = PK11_ImportPrivateKeyInfoAndReturnKey(slot, &info,
|
||||
NULL /* nickname */,
|
||||
NULL /* publicValue */,
|
||||
PR_FALSE /* isPerm */,
|
||||
PR_FALSE /* isPrivate */,
|
||||
KU_ALL /* keyUsage */,
|
||||
&seckey, NULL);
|
||||
|
||||
crypto_pk_t *output = NULL;
|
||||
|
||||
if (s == SECSuccess && seckey) {
|
||||
output = crypto_pk_new();
|
||||
output->seckey = seckey;
|
||||
output->pubkey = SECKEY_ConvertToPublicKey(seckey);
|
||||
tor_assert(output->pubkey);
|
||||
} else {
|
||||
crypto_nss_log_errors(LOG_WARN, "decoding an RSA private key");
|
||||
}
|
||||
|
||||
if (! crypto_pk_is_valid_private_key(output)) {
|
||||
crypto_pk_free(output);
|
||||
output = NULL;
|
||||
}
|
||||
|
||||
if (slot)
|
||||
PK11_FreeSlot(slot);
|
||||
|
||||
return output;
|
||||
}
|
||||
@@ -0,0 +1,590 @@
|
||||
/* Copyright (c) 2001, Matej Pfajfar.
|
||||
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file crypto_rsa.c
|
||||
* \brief OpenSSL implementations of our RSA code.
|
||||
**/
|
||||
|
||||
#include "lib/crypt_ops/compat_openssl.h"
|
||||
#include "lib/crypt_ops/crypto_rsa.h"
|
||||
#include "lib/crypt_ops/crypto_util.h"
|
||||
#include "lib/ctime/di_ops.h"
|
||||
#include "lib/log/util_bug.h"
|
||||
#include "lib/fs/files.h"
|
||||
|
||||
DISABLE_GCC_WARNING(redundant-decls)
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/engine.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/conf.h>
|
||||
|
||||
ENABLE_GCC_WARNING(redundant-decls)
|
||||
|
||||
#include "lib/log/log.h"
|
||||
#include "lib/encoding/binascii.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/** Declaration for crypto_pk_t structure. */
|
||||
struct crypto_pk_t
|
||||
{
|
||||
int refs; /**< reference count, so we don't have to copy keys */
|
||||
RSA *key; /**< The key itself */
|
||||
};
|
||||
|
||||
/** Return true iff <b>key</b> contains the private-key portion of the RSA
|
||||
* key. */
|
||||
int
|
||||
crypto_pk_key_is_private(const crypto_pk_t *k)
|
||||
{
|
||||
#ifdef OPENSSL_1_1_API
|
||||
if (!k || !k->key)
|
||||
return 0;
|
||||
|
||||
const BIGNUM *p, *q;
|
||||
RSA_get0_factors(k->key, &p, &q);
|
||||
return p != NULL; /* XXX/yawning: Should we check q? */
|
||||
#else /* !(defined(OPENSSL_1_1_API)) */
|
||||
return k && k->key && k->key->p;
|
||||
#endif /* defined(OPENSSL_1_1_API) */
|
||||
}
|
||||
|
||||
/** used by tortls.c: wrap an RSA* in a crypto_pk_t. Takes ownership of
|
||||
* its argument. */
|
||||
crypto_pk_t *
|
||||
crypto_new_pk_from_openssl_rsa_(RSA *rsa)
|
||||
{
|
||||
crypto_pk_t *env;
|
||||
tor_assert(rsa);
|
||||
env = tor_malloc(sizeof(crypto_pk_t));
|
||||
env->refs = 1;
|
||||
env->key = rsa;
|
||||
return env;
|
||||
}
|
||||
|
||||
/** Helper, used by tor-gencert.c. Return a copy of the private RSA from a
|
||||
* crypto_pk_t. */
|
||||
RSA *
|
||||
crypto_pk_get_openssl_rsa_(crypto_pk_t *env)
|
||||
{
|
||||
return RSAPrivateKey_dup(env->key);
|
||||
}
|
||||
|
||||
/** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_t. Iff
|
||||
* private is set, include the private-key portion of the key. Return a valid
|
||||
* pointer on success, and NULL on failure. */
|
||||
MOCK_IMPL(EVP_PKEY *,
|
||||
crypto_pk_get_openssl_evp_pkey_,(crypto_pk_t *env, int private))
|
||||
{
|
||||
RSA *key = NULL;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
tor_assert(env->key);
|
||||
if (private) {
|
||||
if (!(key = RSAPrivateKey_dup(env->key)))
|
||||
goto error;
|
||||
} else {
|
||||
if (!(key = RSAPublicKey_dup(env->key)))
|
||||
goto error;
|
||||
}
|
||||
if (!(pkey = EVP_PKEY_new()))
|
||||
goto error;
|
||||
if (!(EVP_PKEY_assign_RSA(pkey, key)))
|
||||
goto error;
|
||||
return pkey;
|
||||
error:
|
||||
if (pkey)
|
||||
EVP_PKEY_free(pkey);
|
||||
if (key)
|
||||
RSA_free(key);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** Allocate and return storage for a public key. The key itself will not yet
|
||||
* be set.
|
||||
*/
|
||||
MOCK_IMPL(crypto_pk_t *,
|
||||
crypto_pk_new,(void))
|
||||
{
|
||||
RSA *rsa;
|
||||
|
||||
rsa = RSA_new();
|
||||
tor_assert(rsa);
|
||||
return crypto_new_pk_from_openssl_rsa_(rsa);
|
||||
}
|
||||
|
||||
/** Release a reference to an asymmetric key; when all the references
|
||||
* are released, free the key.
|
||||
*/
|
||||
void
|
||||
crypto_pk_free_(crypto_pk_t *env)
|
||||
{
|
||||
if (!env)
|
||||
return;
|
||||
|
||||
if (--env->refs > 0)
|
||||
return;
|
||||
tor_assert(env->refs == 0);
|
||||
|
||||
if (env->key)
|
||||
RSA_free(env->key);
|
||||
|
||||
tor_free(env);
|
||||
}
|
||||
|
||||
/** Generate a <b>bits</b>-bit new public/private keypair in <b>env</b>.
|
||||
* Return 0 on success, -1 on failure.
|
||||
*/
|
||||
MOCK_IMPL(int,
|
||||
crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits))
|
||||
{
|
||||
tor_assert(env);
|
||||
|
||||
if (env->key) {
|
||||
RSA_free(env->key);
|
||||
env->key = NULL;
|
||||
}
|
||||
|
||||
{
|
||||
BIGNUM *e = BN_new();
|
||||
RSA *r = NULL;
|
||||
if (!e)
|
||||
goto done;
|
||||
if (! BN_set_word(e, TOR_RSA_EXPONENT))
|
||||
goto done;
|
||||
r = RSA_new();
|
||||
if (!r)
|
||||
goto done;
|
||||
if (RSA_generate_key_ex(r, bits, e, NULL) == -1)
|
||||
goto done;
|
||||
|
||||
env->key = r;
|
||||
r = NULL;
|
||||
done:
|
||||
if (e)
|
||||
BN_clear_free(e);
|
||||
if (r)
|
||||
RSA_free(r);
|
||||
}
|
||||
|
||||
if (!env->key) {
|
||||
crypto_openssl_log_errors(LOG_WARN, "generating RSA key");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Return true if <b>env</b> has a valid key; false otherwise.
|
||||
*/
|
||||
int
|
||||
crypto_pk_is_valid_private_key(const crypto_pk_t *env)
|
||||
{
|
||||
int r;
|
||||
tor_assert(env);
|
||||
|
||||
r = RSA_check_key(env->key);
|
||||
if (r <= 0) {
|
||||
crypto_openssl_log_errors(LOG_WARN,"checking RSA key");
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/** Return true iff <b>env</b> contains a public key whose public exponent
|
||||
* equals TOR_RSA_EXPONENT.
|
||||
*/
|
||||
int
|
||||
crypto_pk_public_exponent_ok(const crypto_pk_t *env)
|
||||
{
|
||||
tor_assert(env);
|
||||
tor_assert(env->key);
|
||||
|
||||
const BIGNUM *e;
|
||||
|
||||
#ifdef OPENSSL_1_1_API
|
||||
const BIGNUM *n, *d;
|
||||
RSA_get0_key(env->key, &n, &e, &d);
|
||||
#else
|
||||
e = env->key->e;
|
||||
#endif /* defined(OPENSSL_1_1_API) */
|
||||
return BN_is_word(e, TOR_RSA_EXPONENT);
|
||||
}
|
||||
|
||||
/** Compare the public-key components of a and b. Return less than 0
|
||||
* if a\<b, 0 if a==b, and greater than 0 if a\>b. A NULL key is
|
||||
* considered to be less than all non-NULL keys, and equal to itself.
|
||||
*
|
||||
* Note that this may leak information about the keys through timing.
|
||||
*/
|
||||
int
|
||||
crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b)
|
||||
{
|
||||
int result;
|
||||
char a_is_non_null = (a != NULL) && (a->key != NULL);
|
||||
char b_is_non_null = (b != NULL) && (b->key != NULL);
|
||||
char an_argument_is_null = !a_is_non_null | !b_is_non_null;
|
||||
|
||||
result = tor_memcmp(&a_is_non_null, &b_is_non_null, sizeof(a_is_non_null));
|
||||
if (an_argument_is_null)
|
||||
return result;
|
||||
|
||||
const BIGNUM *a_n, *a_e;
|
||||
const BIGNUM *b_n, *b_e;
|
||||
|
||||
#ifdef OPENSSL_1_1_API
|
||||
const BIGNUM *a_d, *b_d;
|
||||
RSA_get0_key(a->key, &a_n, &a_e, &a_d);
|
||||
RSA_get0_key(b->key, &b_n, &b_e, &b_d);
|
||||
#else
|
||||
a_n = a->key->n;
|
||||
a_e = a->key->e;
|
||||
b_n = b->key->n;
|
||||
b_e = b->key->e;
|
||||
#endif /* defined(OPENSSL_1_1_API) */
|
||||
|
||||
tor_assert(a_n != NULL && a_e != NULL);
|
||||
tor_assert(b_n != NULL && b_e != NULL);
|
||||
|
||||
result = BN_cmp(a_n, b_n);
|
||||
if (result)
|
||||
return result;
|
||||
return BN_cmp(a_e, b_e);
|
||||
}
|
||||
|
||||
/** Return the size of the public key modulus in <b>env</b>, in bytes. */
|
||||
size_t
|
||||
crypto_pk_keysize(const crypto_pk_t *env)
|
||||
{
|
||||
tor_assert(env);
|
||||
tor_assert(env->key);
|
||||
|
||||
return (size_t) RSA_size((RSA*)env->key);
|
||||
}
|
||||
|
||||
/** Return the size of the public key modulus of <b>env</b>, in bits. */
|
||||
int
|
||||
crypto_pk_num_bits(crypto_pk_t *env)
|
||||
{
|
||||
tor_assert(env);
|
||||
tor_assert(env->key);
|
||||
|
||||
#ifdef OPENSSL_1_1_API
|
||||
/* It's so stupid that there's no other way to check that n is valid
|
||||
* before calling RSA_bits().
|
||||
*/
|
||||
const BIGNUM *n, *e, *d;
|
||||
RSA_get0_key(env->key, &n, &e, &d);
|
||||
tor_assert(n != NULL);
|
||||
|
||||
return RSA_bits(env->key);
|
||||
#else /* !(defined(OPENSSL_1_1_API)) */
|
||||
tor_assert(env->key->n);
|
||||
return BN_num_bits(env->key->n);
|
||||
#endif /* defined(OPENSSL_1_1_API) */
|
||||
}
|
||||
|
||||
/** Increase the reference count of <b>env</b>, and return it.
|
||||
*/
|
||||
crypto_pk_t *
|
||||
crypto_pk_dup_key(crypto_pk_t *env)
|
||||
{
|
||||
tor_assert(env);
|
||||
tor_assert(env->key);
|
||||
|
||||
env->refs++;
|
||||
return env;
|
||||
}
|
||||
|
||||
/** Replace dest with src (private key only). (Dest must have a refcount
|
||||
* of 1)
|
||||
*/
|
||||
void
|
||||
crypto_pk_assign_private(crypto_pk_t *dest, const crypto_pk_t *src)
|
||||
{
|
||||
tor_assert(dest);
|
||||
tor_assert(dest->refs == 1);
|
||||
tor_assert(src);
|
||||
RSA_free(dest->key);
|
||||
dest->key = RSAPrivateKey_dup(src->key);
|
||||
}
|
||||
|
||||
/** Replace dest with src (public key only). (Dest must have a refcount
|
||||
* of 1)
|
||||
*/
|
||||
void
|
||||
crypto_pk_assign_public(crypto_pk_t *dest, const crypto_pk_t *src)
|
||||
{
|
||||
tor_assert(dest);
|
||||
tor_assert(dest->refs == 1);
|
||||
tor_assert(src);
|
||||
RSA_free(dest->key);
|
||||
dest->key = RSAPublicKey_dup(src->key);
|
||||
}
|
||||
|
||||
/** Make a real honest-to-goodness copy of <b>env</b>, and return it.
|
||||
* Returns NULL on failure. */
|
||||
crypto_pk_t *
|
||||
crypto_pk_copy_full(crypto_pk_t *env)
|
||||
{
|
||||
RSA *new_key;
|
||||
int privatekey = 0;
|
||||
tor_assert(env);
|
||||
tor_assert(env->key);
|
||||
|
||||
if (crypto_pk_key_is_private(env)) {
|
||||
new_key = RSAPrivateKey_dup(env->key);
|
||||
privatekey = 1;
|
||||
} else {
|
||||
new_key = RSAPublicKey_dup(env->key);
|
||||
}
|
||||
if (!new_key) {
|
||||
/* LCOV_EXCL_START
|
||||
*
|
||||
* We can't cause RSA*Key_dup() to fail, so we can't really test this.
|
||||
*/
|
||||
log_err(LD_CRYPTO, "Unable to duplicate a %s key: openssl failed.",
|
||||
privatekey?"private":"public");
|
||||
crypto_openssl_log_errors(LOG_ERR,
|
||||
privatekey ? "Duplicating a private key" :
|
||||
"Duplicating a public key");
|
||||
tor_fragile_assert();
|
||||
return NULL;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
return crypto_new_pk_from_openssl_rsa_(new_key);
|
||||
}
|
||||
|
||||
/** Encrypt <b>fromlen</b> bytes from <b>from</b> with the public key
|
||||
* in <b>env</b>, using the padding method <b>padding</b>. On success,
|
||||
* write the result to <b>to</b>, and return the number of bytes
|
||||
* written. On failure, return -1.
|
||||
*
|
||||
* <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
|
||||
* at least the length of the modulus of <b>env</b>.
|
||||
*/
|
||||
int
|
||||
crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen,
|
||||
const char *from, size_t fromlen, int padding)
|
||||
{
|
||||
int r;
|
||||
tor_assert(env);
|
||||
tor_assert(from);
|
||||
tor_assert(to);
|
||||
tor_assert(fromlen<INT_MAX);
|
||||
tor_assert(tolen >= crypto_pk_keysize(env));
|
||||
|
||||
r = RSA_public_encrypt((int)fromlen,
|
||||
(unsigned char*)from, (unsigned char*)to,
|
||||
env->key, crypto_get_rsa_padding(padding));
|
||||
if (r<0) {
|
||||
crypto_openssl_log_errors(LOG_WARN, "performing RSA encryption");
|
||||
return -1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/** Decrypt <b>fromlen</b> bytes from <b>from</b> with the private key
|
||||
* in <b>env</b>, using the padding method <b>padding</b>. On success,
|
||||
* write the result to <b>to</b>, and return the number of bytes
|
||||
* written. On failure, return -1.
|
||||
*
|
||||
* <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
|
||||
* at least the length of the modulus of <b>env</b>.
|
||||
*/
|
||||
int
|
||||
crypto_pk_private_decrypt(crypto_pk_t *env, char *to,
|
||||
size_t tolen,
|
||||
const char *from, size_t fromlen,
|
||||
int padding, int warnOnFailure)
|
||||
{
|
||||
int r;
|
||||
tor_assert(env);
|
||||
tor_assert(from);
|
||||
tor_assert(to);
|
||||
tor_assert(env->key);
|
||||
tor_assert(fromlen<INT_MAX);
|
||||
tor_assert(tolen >= crypto_pk_keysize(env));
|
||||
if (!crypto_pk_key_is_private(env))
|
||||
/* Not a private key */
|
||||
return -1;
|
||||
|
||||
r = RSA_private_decrypt((int)fromlen,
|
||||
(unsigned char*)from, (unsigned char*)to,
|
||||
env->key, crypto_get_rsa_padding(padding));
|
||||
|
||||
if (r<0) {
|
||||
crypto_openssl_log_errors(warnOnFailure?LOG_WARN:LOG_DEBUG,
|
||||
"performing RSA decryption");
|
||||
return -1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/** Check the signature in <b>from</b> (<b>fromlen</b> bytes long) with the
|
||||
* public key in <b>env</b>, using PKCS1 padding. On success, write the
|
||||
* signed data to <b>to</b>, and return the number of bytes written.
|
||||
* On failure, return -1.
|
||||
*
|
||||
* <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
|
||||
* at least the length of the modulus of <b>env</b>.
|
||||
*/
|
||||
MOCK_IMPL(int,
|
||||
crypto_pk_public_checksig,(const crypto_pk_t *env, char *to,
|
||||
size_t tolen,
|
||||
const char *from, size_t fromlen))
|
||||
{
|
||||
int r;
|
||||
tor_assert(env);
|
||||
tor_assert(from);
|
||||
tor_assert(to);
|
||||
tor_assert(fromlen < INT_MAX);
|
||||
tor_assert(tolen >= crypto_pk_keysize(env));
|
||||
r = RSA_public_decrypt((int)fromlen,
|
||||
(unsigned char*)from, (unsigned char*)to,
|
||||
env->key, RSA_PKCS1_PADDING);
|
||||
|
||||
if (r<0) {
|
||||
crypto_openssl_log_errors(LOG_INFO, "checking RSA signature");
|
||||
return -1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/** Sign <b>fromlen</b> bytes of data from <b>from</b> with the private key in
|
||||
* <b>env</b>, using PKCS1 padding. On success, write the signature to
|
||||
* <b>to</b>, and return the number of bytes written. On failure, return
|
||||
* -1.
|
||||
*
|
||||
* <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
|
||||
* at least the length of the modulus of <b>env</b>.
|
||||
*/
|
||||
int
|
||||
crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen,
|
||||
const char *from, size_t fromlen)
|
||||
{
|
||||
int r;
|
||||
tor_assert(env);
|
||||
tor_assert(from);
|
||||
tor_assert(to);
|
||||
tor_assert(fromlen < INT_MAX);
|
||||
tor_assert(tolen >= crypto_pk_keysize(env));
|
||||
if (!crypto_pk_key_is_private(env))
|
||||
/* Not a private key */
|
||||
return -1;
|
||||
|
||||
r = RSA_private_encrypt((int)fromlen,
|
||||
(unsigned char*)from, (unsigned char*)to,
|
||||
(RSA*)env->key, RSA_PKCS1_PADDING);
|
||||
if (r<0) {
|
||||
crypto_openssl_log_errors(LOG_WARN, "generating RSA signature");
|
||||
return -1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/** ASN.1-encode the public portion of <b>pk</b> into <b>dest</b>.
|
||||
* Return -1 on error, or the number of characters used on success.
|
||||
*/
|
||||
int
|
||||
crypto_pk_asn1_encode(const crypto_pk_t *pk, char *dest, size_t dest_len)
|
||||
{
|
||||
int len;
|
||||
unsigned char *buf = NULL;
|
||||
|
||||
len = i2d_RSAPublicKey(pk->key, &buf);
|
||||
if (len < 0 || buf == NULL)
|
||||
return -1;
|
||||
|
||||
if ((size_t)len > dest_len || dest_len > SIZE_T_CEILING) {
|
||||
OPENSSL_free(buf);
|
||||
return -1;
|
||||
}
|
||||
/* We don't encode directly into 'dest', because that would be illegal
|
||||
* type-punning. (C99 is smarter than me, C99 is smarter than me...)
|
||||
*/
|
||||
memcpy(dest,buf,len);
|
||||
OPENSSL_free(buf);
|
||||
return len;
|
||||
}
|
||||
|
||||
/** Decode an ASN.1-encoded public key from <b>str</b>; return the result on
|
||||
* success and NULL on failure.
|
||||
*/
|
||||
crypto_pk_t *
|
||||
crypto_pk_asn1_decode(const char *str, size_t len)
|
||||
{
|
||||
RSA *rsa;
|
||||
unsigned char *buf;
|
||||
const unsigned char *cp;
|
||||
cp = buf = tor_malloc(len);
|
||||
memcpy(buf,str,len);
|
||||
rsa = d2i_RSAPublicKey(NULL, &cp, len);
|
||||
tor_free(buf);
|
||||
if (!rsa) {
|
||||
crypto_openssl_log_errors(LOG_WARN,"decoding public key");
|
||||
return NULL;
|
||||
}
|
||||
return crypto_new_pk_from_openssl_rsa_(rsa);
|
||||
}
|
||||
|
||||
/** ASN.1-encode the private portion of <b>pk</b> into <b>dest</b>.
|
||||
* Return -1 on error, or the number of characters used on success.
|
||||
*/
|
||||
int
|
||||
crypto_pk_asn1_encode_private(const crypto_pk_t *pk, char *dest,
|
||||
size_t dest_len)
|
||||
{
|
||||
int len;
|
||||
unsigned char *buf = NULL;
|
||||
|
||||
len = i2d_RSAPrivateKey(pk->key, &buf);
|
||||
if (len < 0 || buf == NULL)
|
||||
return -1;
|
||||
|
||||
if ((size_t)len > dest_len || dest_len > SIZE_T_CEILING) {
|
||||
OPENSSL_free(buf);
|
||||
return -1;
|
||||
}
|
||||
/* We don't encode directly into 'dest', because that would be illegal
|
||||
* type-punning. (C99 is smarter than me, C99 is smarter than me...)
|
||||
*/
|
||||
memcpy(dest,buf,len);
|
||||
OPENSSL_free(buf);
|
||||
return len;
|
||||
}
|
||||
|
||||
/** Decode an ASN.1-encoded private key from <b>str</b>; return the result on
|
||||
* success and NULL on failure.
|
||||
*/
|
||||
crypto_pk_t *
|
||||
crypto_pk_asn1_decode_private(const char *str, size_t len)
|
||||
{
|
||||
RSA *rsa;
|
||||
unsigned char *buf;
|
||||
const unsigned char *cp;
|
||||
cp = buf = tor_malloc(len);
|
||||
memcpy(buf,str,len);
|
||||
rsa = d2i_RSAPrivateKey(NULL, &cp, len);
|
||||
tor_free(buf);
|
||||
if (!rsa) {
|
||||
crypto_openssl_log_errors(LOG_WARN,"decoding public key");
|
||||
return NULL;
|
||||
}
|
||||
crypto_pk_t *result = crypto_new_pk_from_openssl_rsa_(rsa);
|
||||
if (! crypto_pk_is_valid_private_key(result)) {
|
||||
crypto_pk_free(result);
|
||||
return NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#define CRYPTO_S2K_PRIVATE
|
||||
|
||||
#include "lib/crypt_ops/crypto.h"
|
||||
#include "lib/crypt_ops/crypto_cipher.h"
|
||||
#include "lib/crypt_ops/crypto_digest.h"
|
||||
#include "lib/crypt_ops/crypto_hkdf.h"
|
||||
#include "lib/crypt_ops/crypto_rand.h"
|
||||
@@ -20,8 +20,14 @@
|
||||
#include "lib/crypt_ops/crypto_util.h"
|
||||
#include "lib/ctime/di_ops.h"
|
||||
#include "lib/log/util_bug.h"
|
||||
#include "lib/intmath/cmp.h"
|
||||
|
||||
#ifdef ENABLE_OPENSSL
|
||||
#include <openssl/evp.h>
|
||||
#endif
|
||||
#ifdef ENABLE_NSS
|
||||
#include <pk11pub.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_LIBSCRYPT_H) && defined(HAVE_LIBSCRYPT_SCRYPT)
|
||||
#define HAVE_SCRYPT
|
||||
@@ -271,6 +277,7 @@ secret_to_key_compute_key(uint8_t *key_out, size_t key_out_len,
|
||||
log_iters = spec[spec_len-1];
|
||||
if (log_iters > 31)
|
||||
return S2K_BAD_PARAMS;
|
||||
#ifdef ENABLE_OPENSSL
|
||||
rv = PKCS5_PBKDF2_HMAC_SHA1(secret, (int)secret_len,
|
||||
spec, (int)spec_len-1,
|
||||
(1<<log_iters),
|
||||
@@ -278,6 +285,47 @@ secret_to_key_compute_key(uint8_t *key_out, size_t key_out_len,
|
||||
if (rv < 0)
|
||||
return S2K_FAILED;
|
||||
return (int)key_out_len;
|
||||
#else
|
||||
SECItem passItem = { .type = siBuffer,
|
||||
.data = (unsigned char *) secret,
|
||||
.len = (int)secret_len };
|
||||
SECItem saltItem = { .type = siBuffer,
|
||||
.data = (unsigned char *) spec,
|
||||
.len = (int)spec_len - 1 };
|
||||
SECAlgorithmID *alg = NULL;
|
||||
PK11SymKey *key = NULL;
|
||||
|
||||
rv = S2K_FAILED;
|
||||
alg = PK11_CreatePBEV2AlgorithmID(
|
||||
SEC_OID_PKCS5_PBKDF2, SEC_OID_HMAC_SHA1, SEC_OID_HMAC_SHA1,
|
||||
(int)key_out_len, (1<<log_iters), &saltItem);
|
||||
if (alg == NULL)
|
||||
return S2K_FAILED;
|
||||
|
||||
key = PK11_PBEKeyGen(NULL /* slot */,
|
||||
alg,
|
||||
&passItem,
|
||||
false,
|
||||
NULL);
|
||||
|
||||
SECStatus st = PK11_ExtractKeyValue(key);
|
||||
if (st != SECSuccess)
|
||||
goto nss_pbkdf_err;
|
||||
|
||||
const SECItem *iptr = PK11_GetKeyData(key);
|
||||
if (iptr == NULL)
|
||||
goto nss_pbkdf_err;
|
||||
|
||||
rv = MIN((int)iptr->len, (int)key_out_len);
|
||||
memcpy(key_out, iptr->data, rv);
|
||||
|
||||
nss_pbkdf_err:
|
||||
if (key)
|
||||
PK11_FreeSymKey(key);
|
||||
if (alg)
|
||||
SECOID_DestroyAlgorithmID(alg, PR_TRUE);
|
||||
return rv;
|
||||
#endif
|
||||
}
|
||||
|
||||
case S2K_TYPE_SCRYPT: {
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
* \brief Common cryptographic utilities.
|
||||
**/
|
||||
|
||||
#ifndef CRYPTO_UTIL_PRIVATE
|
||||
#define CRYPTO_UTIL_PRIVATE
|
||||
|
||||
#include "lib/crypt_ops/crypto_util.h"
|
||||
@@ -24,12 +23,14 @@
|
||||
#include <wincrypt.h>
|
||||
#endif /* defined(_WIN32) */
|
||||
|
||||
DISABLE_GCC_WARNING(redundant-decls)
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef ENABLE_OPENSSL
|
||||
DISABLE_GCC_WARNING(redundant-decls)
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/crypto.h>
|
||||
|
||||
ENABLE_GCC_WARNING(redundant-decls)
|
||||
#endif
|
||||
|
||||
#include "lib/log/log.h"
|
||||
#include "lib/log/util_bug.h"
|
||||
@@ -105,25 +106,3 @@ memwipe(void *mem, uint8_t byte, size_t sz)
|
||||
**/
|
||||
memset(mem, byte, sz);
|
||||
}
|
||||
|
||||
/** Log all pending crypto errors at level <b>severity</b>. Use
|
||||
* <b>doing</b> to describe our current activities.
|
||||
*/
|
||||
void
|
||||
crypto_log_errors(int severity, const char *doing)
|
||||
{
|
||||
unsigned long err;
|
||||
const char *msg, *lib, *func;
|
||||
while ((err = ERR_get_error()) != 0) {
|
||||
msg = (const char*)ERR_reason_error_string(err);
|
||||
lib = (const char*)ERR_lib_error_string(err);
|
||||
func = (const char*)ERR_func_error_string(err);
|
||||
if (!msg) msg = "(null)";
|
||||
if (!lib) lib = "(null)";
|
||||
if (!func) func = "(null)";
|
||||
if (BUG(!doing)) doing = "(null)";
|
||||
tor_log(severity, LD_CRYPTO, "crypto error while %s: %s (in %s:%s)",
|
||||
doing, msg, lib, func);
|
||||
}
|
||||
}
|
||||
#endif /* !defined(CRYPTO_UTIL_PRIVATE) */
|
||||
|
||||
@@ -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) */
|
||||
|
||||
|
||||
@@ -6,16 +6,15 @@ noinst_LIBRARIES += src/lib/libtor-crypt-ops-testing.a
|
||||
endif
|
||||
|
||||
src_lib_libtor_crypt_ops_a_SOURCES = \
|
||||
src/lib/crypt_ops/aes.c \
|
||||
src/lib/crypt_ops/crypto.c \
|
||||
src/lib/crypt_ops/crypto_cipher.c \
|
||||
src/lib/crypt_ops/crypto_curve25519.c \
|
||||
src/lib/crypt_ops/crypto_dh.c \
|
||||
src/lib/crypt_ops/crypto_digest.c \
|
||||
src/lib/crypt_ops/crypto_ed25519.c \
|
||||
src/lib/crypt_ops/crypto_format.c \
|
||||
src/lib/crypt_ops/crypto_hkdf.c \
|
||||
src/lib/crypt_ops/crypto_init.c \
|
||||
src/lib/crypt_ops/crypto_ope.c \
|
||||
src/lib/crypt_ops/crypto_openssl_mgt.c \
|
||||
src/lib/crypt_ops/crypto_pwbox.c \
|
||||
src/lib/crypt_ops/crypto_rand.c \
|
||||
src/lib/crypt_ops/crypto_rsa.c \
|
||||
@@ -23,10 +22,31 @@ src_lib_libtor_crypt_ops_a_SOURCES = \
|
||||
src/lib/crypt_ops/crypto_util.c \
|
||||
src/lib/crypt_ops/digestset.c
|
||||
|
||||
if USE_NSS
|
||||
src_lib_libtor_crypt_ops_a_SOURCES += \
|
||||
src/lib/crypt_ops/aes_nss.c \
|
||||
src/lib/crypt_ops/crypto_dh_nss.c \
|
||||
src/lib/crypt_ops/crypto_nss_mgt.c \
|
||||
src/lib/crypt_ops/crypto_rsa_nss.c
|
||||
else
|
||||
src_lib_libtor_crypt_ops_a_SOURCES += \
|
||||
src/lib/crypt_ops/aes_openssl.c \
|
||||
src/lib/crypt_ops/crypto_rsa_openssl.c
|
||||
endif
|
||||
|
||||
if USE_OPENSSL
|
||||
src_lib_libtor_crypt_ops_a_SOURCES += \
|
||||
src/lib/crypt_ops/crypto_dh_openssl.c \
|
||||
src/lib/crypt_ops/crypto_openssl_mgt.c
|
||||
endif
|
||||
|
||||
src_lib_libtor_crypt_ops_a_CFLAGS = $(AM_CFLAGS) $(TOR_CFLAGS_CRYPTLIB)
|
||||
|
||||
src_lib_libtor_crypt_ops_testing_a_SOURCES = \
|
||||
$(src_lib_libtor_crypt_ops_a_SOURCES)
|
||||
src_lib_libtor_crypt_ops_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
|
||||
src_lib_libtor_crypt_ops_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
|
||||
src_lib_libtor_crypt_ops_testing_a_CFLAGS = \
|
||||
$(AM_CFLAGS) $(TOR_CFLAGS_CRYPTLIB) $(TEST_CFLAGS)
|
||||
|
||||
noinst_HEADERS += \
|
||||
src/lib/crypt_ops/aes.h \
|
||||
@@ -36,8 +56,10 @@ noinst_HEADERS += \
|
||||
src/lib/crypt_ops/crypto_digest.h \
|
||||
src/lib/crypt_ops/crypto_ed25519.h \
|
||||
src/lib/crypt_ops/crypto_format.h \
|
||||
src/lib/crypt_ops/crypto.h \
|
||||
src/lib/crypt_ops/crypto_cipher.h \
|
||||
src/lib/crypt_ops/crypto_hkdf.h \
|
||||
src/lib/crypt_ops/crypto_init.h \
|
||||
src/lib/crypt_ops/crypto_nss_mgt.h \
|
||||
src/lib/crypt_ops/crypto_openssl_mgt.h \
|
||||
src/lib/crypt_ops/crypto_ope.h \
|
||||
src/lib/crypt_ops/crypto_pwbox.h \
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
orconfig.h
|
||||
lib/cc/*.h
|
||||
lib/ctime/*.h
|
||||
lib/encoding/*.h
|
||||
lib/intmath/*.h
|
||||
lib/log/*.h
|
||||
|
||||
@@ -9,6 +9,7 @@ src_lib_libtor_encoding_a_SOURCES = \
|
||||
src/lib/encoding/confline.c \
|
||||
src/lib/encoding/cstring.c \
|
||||
src/lib/encoding/keyval.c \
|
||||
src/lib/encoding/pem.c \
|
||||
src/lib/encoding/time_fmt.c
|
||||
|
||||
src_lib_libtor_encoding_testing_a_SOURCES = \
|
||||
@@ -21,4 +22,5 @@ noinst_HEADERS += \
|
||||
src/lib/encoding/confline.h \
|
||||
src/lib/encoding/cstring.h \
|
||||
src/lib/encoding/keyval.h \
|
||||
src/lib/encoding/pem.h \
|
||||
src/lib/encoding/time_fmt.h
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/* Copyright (c) 2001, Matej Pfajfar.
|
||||
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file pem.c
|
||||
*
|
||||
* \brief Implement a trivial version of PEM encoding, for use with NSS.
|
||||
*
|
||||
* We deliberately do not support any encryption here.
|
||||
**/
|
||||
|
||||
#include "orconfig.h"
|
||||
|
||||
#include "lib/encoding/pem.h"
|
||||
|
||||
#include "lib/ctime/di_ops.h"
|
||||
#include "lib/encoding/binascii.h"
|
||||
#include "lib/log/util_bug.h"
|
||||
#include "lib/malloc/malloc.h"
|
||||
#include "lib/string/printf.h"
|
||||
#include "lib/string/util_string.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Return the length of a <b>src_len</b>-byte object when tagged with
|
||||
* <b>objtype</b> and PEM-encoded. Includes terminating NUL.
|
||||
*/
|
||||
size_t
|
||||
pem_encoded_size(size_t src_len, const char *objtype)
|
||||
{
|
||||
return
|
||||
strlen("-----BEGIN -----\n") +
|
||||
strlen("-----END -----\n") +
|
||||
strlen(objtype) * 2 +
|
||||
base64_encode_size(src_len, BASE64_ENCODE_MULTILINE)
|
||||
+ 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* PEM-encode the <b>srclen</b>-byte object at <b>src</b> into the
|
||||
* <b>destlen<\b>-byte buffer at <b>dest</b>, tagging it with <b>objtype</b>.
|
||||
* Return 0 on success and -1 on failure.
|
||||
*/
|
||||
int
|
||||
pem_encode(char *dest, size_t destlen, const uint8_t *src, size_t srclen,
|
||||
const char *objtype)
|
||||
{
|
||||
if (tor_snprintf(dest, destlen, "-----BEGIN %s-----\n", objtype) < 0)
|
||||
return -1;
|
||||
|
||||
size_t offset = strlen(dest);
|
||||
|
||||
int n = base64_encode(dest + offset, destlen - offset,
|
||||
(const char *)src, srclen, BASE64_ENCODE_MULTILINE);
|
||||
if (n < 0)
|
||||
return -1;
|
||||
offset += n;
|
||||
if (BUG(offset > destlen))
|
||||
return -1;
|
||||
|
||||
if (tor_snprintf(dest + offset, destlen - offset,
|
||||
"-----END %s-----\n", objtype) < 0)
|
||||
return -1;
|
||||
|
||||
tor_assert(strlen(dest) + 1 <= pem_encoded_size(srclen, objtype));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a PEM-encoded block of size <b>srclen</b> in <b>src</b>, if it has
|
||||
* object type <b>objtype</b>, decode it into the <b>destlen</b>-byte buffer
|
||||
* at <b>dest</b>. Return the number of characters decoded on success, or -1
|
||||
* on failure.
|
||||
*/
|
||||
int
|
||||
pem_decode(uint8_t *dest, size_t destlen, const char *src, size_t srclen,
|
||||
const char *objtype)
|
||||
{
|
||||
const char *eos = src + srclen;
|
||||
|
||||
src = eat_whitespace_eos(src, eos);
|
||||
|
||||
char *tag = NULL;
|
||||
tor_asprintf(&tag, "-----BEGIN %s-----\n", objtype);
|
||||
if ((size_t)(eos-src) < strlen(tag) || fast_memneq(src, tag, strlen(tag))) {
|
||||
tor_free(tag);
|
||||
return -1;
|
||||
}
|
||||
src += strlen(tag);
|
||||
tor_free(tag);
|
||||
|
||||
// NOTE lack of trailing \n. We do not enforce its presence.
|
||||
tor_asprintf(&tag, "\n-----END %s-----", objtype);
|
||||
const char *end_of_base64 = tor_memstr(src, eos-src, tag);
|
||||
tor_free(tag);
|
||||
if (end_of_base64 == NULL)
|
||||
return -1;
|
||||
|
||||
/* Should we actually allow extra stuff at the end? */
|
||||
|
||||
return base64_decode((char*)dest, destlen, src, end_of_base64-src);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/* Copyright (c) 2001, Matej Pfajfar.
|
||||
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file pem.h
|
||||
*
|
||||
* \brief Header for pem.c
|
||||
**/
|
||||
|
||||
#ifndef TOR_PEM_H
|
||||
#define TOR_PEM_H
|
||||
|
||||
#include "orconfig.h"
|
||||
#include <stddef.h>
|
||||
#include "lib/cc/torint.h"
|
||||
|
||||
size_t pem_encoded_size(size_t src_len, const char *objtype);
|
||||
int pem_encode(char *dest, size_t destlen, const uint8_t *src, size_t srclen,
|
||||
const char *objtype);
|
||||
int pem_decode(uint8_t *dest, size_t destlen, const char *src, size_t srclen,
|
||||
const char *objtype);
|
||||
|
||||
#endif
|
||||
@@ -1,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=\
|
||||
|
||||
@@ -41,15 +41,16 @@ static int daemon_filedes[2];
|
||||
/** Start putting the process into daemon mode: fork and drop all resources
|
||||
* except standard fds. The parent process never returns, but stays around
|
||||
* until finish_daemon is called. (Note: it's safe to call this more
|
||||
* than once: calls after the first are ignored.)
|
||||
* than once: calls after the first are ignored.) Return true if we actually
|
||||
* forked and this is the child; false otherwise.
|
||||
*/
|
||||
void
|
||||
int
|
||||
start_daemon(void)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
if (start_daemon_called)
|
||||
return;
|
||||
return 0;
|
||||
start_daemon_called = 1;
|
||||
|
||||
if (pipe(daemon_filedes)) {
|
||||
@@ -80,6 +81,7 @@ start_daemon(void)
|
||||
exit(0); // exit ok: during daemonize, daemonizing.
|
||||
else
|
||||
exit(1); /* child reported error. exit ok: daemonize failed. */
|
||||
return 0; // LCOV_EXCL_LINE unreachable
|
||||
} else { /* Child */
|
||||
close(daemon_filedes[0]); /* we only write */
|
||||
|
||||
@@ -95,22 +97,23 @@ start_daemon(void)
|
||||
}
|
||||
set_main_thread(); /* We are now the main thread. */
|
||||
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/** Finish putting the process into daemon mode: drop standard fds, and tell
|
||||
* the parent process to exit. (Note: it's safe to call this more than once:
|
||||
* calls after the first are ignored. Calls start_daemon first if it hasn't
|
||||
* been called already.)
|
||||
* been called already.) Return true if we actually did a fork; false if we
|
||||
* didn't.
|
||||
*/
|
||||
void
|
||||
int
|
||||
finish_daemon(const char *desired_cwd)
|
||||
{
|
||||
int nullfd;
|
||||
char c = '.';
|
||||
if (finish_daemon_called)
|
||||
return;
|
||||
return 0;
|
||||
if (!start_daemon_called)
|
||||
start_daemon();
|
||||
finish_daemon_called = 1;
|
||||
@@ -149,16 +152,20 @@ finish_daemon(const char *desired_cwd)
|
||||
log_err(LD_GENERAL,"write failed. Exiting.");
|
||||
}
|
||||
close(daemon_filedes[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else /* !(!defined(_WIN32)) */
|
||||
/* defined(_WIN32) */
|
||||
void
|
||||
int
|
||||
start_daemon(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
void
|
||||
int
|
||||
finish_daemon(const char *cp)
|
||||
{
|
||||
(void)cp;
|
||||
return 0;
|
||||
}
|
||||
#endif /* !defined(_WIN32) */
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#ifndef TOR_DAEMON_H
|
||||
#define TOR_DAEMON_H
|
||||
|
||||
void start_daemon(void);
|
||||
void finish_daemon(const char *desired_cwd);
|
||||
int start_daemon(void);
|
||||
int finish_daemon(const char *desired_cwd);
|
||||
|
||||
#endif
|
||||
|
||||
+21
-3
@@ -7,14 +7,32 @@ endif
|
||||
|
||||
src_lib_libtor_tls_a_SOURCES = \
|
||||
src/lib/tls/buffers_tls.c \
|
||||
src/lib/tls/tortls.c
|
||||
src/lib/tls/tortls.c \
|
||||
src/lib/tls/x509.c
|
||||
|
||||
if USE_NSS
|
||||
src_lib_libtor_tls_a_SOURCES += \
|
||||
src/lib/tls/tortls_nss.c \
|
||||
src/lib/tls/x509_nss.c
|
||||
else
|
||||
src_lib_libtor_tls_a_SOURCES += \
|
||||
src/lib/tls/tortls_openssl.c \
|
||||
src/lib/tls/x509_openssl.c
|
||||
endif
|
||||
|
||||
src_lib_libtor_tls_a_CFLAGS = $(AM_CFLAGS) $(TOR_CFLAGS_CRYPTLIB)
|
||||
|
||||
src_lib_libtor_tls_testing_a_SOURCES = \
|
||||
$(src_lib_libtor_tls_a_SOURCES)
|
||||
src_lib_libtor_tls_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
|
||||
src_lib_libtor_tls_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
|
||||
src_lib_libtor_tls_testing_a_CFLAGS = \
|
||||
$(AM_CFLAGS) $(TOR_CFLAGS_CRYPTLIB) $(TEST_CFLAGS)
|
||||
|
||||
noinst_HEADERS += \
|
||||
src/lib/tls/ciphers.inc \
|
||||
src/lib/tls/buffers_tls.h \
|
||||
src/lib/tls/tortls.h
|
||||
src/lib/tls/tortls.h \
|
||||
src/lib/tls/tortls_internal.h \
|
||||
src/lib/tls/tortls_st.h \
|
||||
src/lib/tls/x509.h \
|
||||
src/lib/tls/x509_internal.h
|
||||
|
||||
+171
-2408
File diff suppressed because it is too large
Load Diff
+36
-174
@@ -12,14 +12,27 @@
|
||||
**/
|
||||
|
||||
#include "lib/crypt_ops/crypto_rsa.h"
|
||||
#include "lib/crypt_ops/compat_openssl.h"
|
||||
#include "lib/testsupport/testsupport.h"
|
||||
#include "lib/net/nettypes.h"
|
||||
|
||||
/* Opaque structure to hold a TLS connection. */
|
||||
typedef struct tor_tls_t tor_tls_t;
|
||||
|
||||
/* Opaque structure to hold an X509 certificate. */
|
||||
typedef struct tor_x509_cert_t tor_x509_cert_t;
|
||||
#ifdef TORTLS_PRIVATE
|
||||
#ifdef ENABLE_OPENSSL
|
||||
struct ssl_st;
|
||||
struct ssl_ctx_st;
|
||||
struct ssl_session_st;
|
||||
typedef struct ssl_ctx_st tor_tls_context_impl_t;
|
||||
typedef struct ssl_st tor_tls_impl_t;
|
||||
#else
|
||||
struct PRFileDesc;
|
||||
typedef struct PRFileDesc tor_tls_context_impl_t;
|
||||
typedef struct PRFileDesc tor_tls_impl_t;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct tor_x509_cert_t;
|
||||
|
||||
/* Possible return values for most tor_tls_* functions. */
|
||||
#define MIN_TOR_TLS_ERROR_VAL_ -9
|
||||
@@ -52,164 +65,30 @@ typedef struct tor_x509_cert_t tor_x509_cert_t;
|
||||
|
||||
#define TOR_TLS_IS_ERROR(rv) ((rv) < TOR_TLS_CLOSE)
|
||||
|
||||
#ifdef TORTLS_PRIVATE
|
||||
#define TOR_TLS_MAGIC 0x71571571
|
||||
|
||||
typedef enum {
|
||||
TOR_TLS_ST_HANDSHAKE, TOR_TLS_ST_OPEN, TOR_TLS_ST_GOTCLOSE,
|
||||
TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED, TOR_TLS_ST_RENEGOTIATE,
|
||||
TOR_TLS_ST_BUFFEREVENT
|
||||
} tor_tls_state_t;
|
||||
#define tor_tls_state_bitfield_t ENUM_BF(tor_tls_state_t)
|
||||
|
||||
struct x509_st;
|
||||
struct ssl_st;
|
||||
struct ssl_ctx_st;
|
||||
struct ssl_session_st;
|
||||
|
||||
/** Holds a SSL_CTX object and related state used to configure TLS
|
||||
* connections.
|
||||
*/
|
||||
typedef struct tor_tls_context_t {
|
||||
int refcnt;
|
||||
struct ssl_ctx_st *ctx;
|
||||
tor_x509_cert_t *my_link_cert;
|
||||
tor_x509_cert_t *my_id_cert;
|
||||
tor_x509_cert_t *my_auth_cert;
|
||||
crypto_pk_t *link_key;
|
||||
crypto_pk_t *auth_key;
|
||||
} tor_tls_context_t;
|
||||
typedef struct tor_tls_context_t tor_tls_context_t;
|
||||
|
||||
/** Structure that we use for a single certificate. */
|
||||
struct tor_x509_cert_t {
|
||||
struct x509_st *cert;
|
||||
uint8_t *encoded;
|
||||
size_t encoded_len;
|
||||
unsigned pkey_digests_set : 1;
|
||||
common_digests_t cert_digests;
|
||||
common_digests_t pkey_digests;
|
||||
};
|
||||
|
||||
/** Holds a SSL object and its associated data. Members are only
|
||||
* accessed from within tortls.c.
|
||||
*/
|
||||
struct tor_tls_t {
|
||||
uint32_t magic;
|
||||
tor_tls_context_t *context; /** A link to the context object for this tls. */
|
||||
struct ssl_st *ssl; /**< An OpenSSL SSL object. */
|
||||
int socket; /**< The underlying file descriptor for this TLS connection. */
|
||||
char *address; /**< An address to log when describing this connection. */
|
||||
tor_tls_state_bitfield_t state : 3; /**< The current SSL state,
|
||||
* depending on which operations
|
||||
* have completed successfully. */
|
||||
unsigned int isServer:1; /**< True iff this is a server-side connection */
|
||||
unsigned int wasV2Handshake:1; /**< True iff the original handshake for
|
||||
* this connection used the updated version
|
||||
* of the connection protocol (client sends
|
||||
* different cipher list, server sends only
|
||||
* one certificate). */
|
||||
/** True iff we should call negotiated_callback when we're done reading. */
|
||||
unsigned int got_renegotiate:1;
|
||||
/** Return value from tor_tls_classify_client_ciphers, or 0 if we haven't
|
||||
* called that function yet. */
|
||||
int8_t client_cipher_list_type;
|
||||
/** Incremented every time we start the server side of a handshake. */
|
||||
uint8_t server_handshake_count;
|
||||
size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last
|
||||
* time. */
|
||||
/** Last values retrieved from BIO_number_read()/write(); see
|
||||
* tor_tls_get_n_raw_bytes() for usage.
|
||||
*/
|
||||
unsigned long last_write_count;
|
||||
unsigned long last_read_count;
|
||||
/** If set, a callback to invoke whenever the client tries to renegotiate
|
||||
* the handshake. */
|
||||
void (*negotiated_callback)(tor_tls_t *tls, void *arg);
|
||||
/** Argument to pass to negotiated_callback. */
|
||||
void *callback_arg;
|
||||
};
|
||||
|
||||
STATIC int tor_errno_to_tls_error(int e);
|
||||
STATIC int tor_tls_get_error(tor_tls_t *tls, int r, int extra,
|
||||
const char *doing, int severity, int domain);
|
||||
STATIC tor_tls_t *tor_tls_get_by_ssl(const struct ssl_st *ssl);
|
||||
STATIC void tor_tls_allocate_tor_tls_object_ex_data_index(void);
|
||||
#ifdef TORTLS_OPENSSL_PRIVATE
|
||||
STATIC int always_accept_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx);
|
||||
STATIC int tor_tls_classify_client_ciphers(const struct ssl_st *ssl,
|
||||
STACK_OF(SSL_CIPHER) *peer_ciphers);
|
||||
#endif
|
||||
STATIC int tor_tls_client_is_using_v2_ciphers(const struct ssl_st *ssl);
|
||||
MOCK_DECL(STATIC void, try_to_extract_certs_from_tls,
|
||||
(int severity, tor_tls_t *tls, struct x509_st **cert_out,
|
||||
struct x509_st **id_cert_out));
|
||||
#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY
|
||||
STATIC size_t SSL_SESSION_get_master_key(struct ssl_session_st *s,
|
||||
uint8_t *out,
|
||||
size_t len);
|
||||
#endif
|
||||
STATIC void tor_tls_debug_state_callback(const struct ssl_st *ssl,
|
||||
int type, int val);
|
||||
STATIC void tor_tls_server_info_callback(const struct ssl_st *ssl,
|
||||
int type, int val);
|
||||
#ifdef TORTLS_OPENSSL_PRIVATE
|
||||
STATIC int tor_tls_session_secret_cb(struct ssl_st *ssl, void *secret,
|
||||
int *secret_len,
|
||||
STACK_OF(SSL_CIPHER) *peer_ciphers,
|
||||
CONST_IF_OPENSSL_1_1_API SSL_CIPHER **cipher,
|
||||
void *arg);
|
||||
STATIC int find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m,
|
||||
uint16_t cipher);
|
||||
#endif /* defined(TORTLS_OPENSSL_PRIVATE) */
|
||||
MOCK_DECL(STATIC struct x509_st *, tor_tls_create_certificate,
|
||||
(crypto_pk_t *rsa,
|
||||
crypto_pk_t *rsa_sign,
|
||||
const char *cname,
|
||||
const char *cname_sign,
|
||||
unsigned int cert_lifetime));
|
||||
STATIC tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity,
|
||||
unsigned int key_lifetime, unsigned flags, int is_client);
|
||||
MOCK_DECL(STATIC tor_x509_cert_t *, tor_x509_cert_new,
|
||||
(struct x509_st *x509_cert));
|
||||
STATIC int tor_tls_context_init_one(tor_tls_context_t **ppcontext,
|
||||
crypto_pk_t *identity,
|
||||
unsigned int key_lifetime,
|
||||
unsigned int flags,
|
||||
int is_client);
|
||||
STATIC void tls_log_errors(tor_tls_t *tls, int severity, int domain,
|
||||
const char *doing);
|
||||
|
||||
#ifdef TOR_UNIT_TESTS
|
||||
extern int tor_tls_object_ex_data_index;
|
||||
extern tor_tls_context_t *server_tls_context;
|
||||
extern tor_tls_context_t *client_tls_context;
|
||||
extern uint16_t v2_cipher_list[];
|
||||
extern uint64_t total_bytes_written_over_tls;
|
||||
extern uint64_t total_bytes_written_by_tls;
|
||||
|
||||
STATIC tor_x509_cert_t *tor_x509_cert_replace_expiration(
|
||||
const tor_x509_cert_t *inp,
|
||||
time_t new_expiration_time,
|
||||
crypto_pk_t *signing_key);
|
||||
#endif /* defined(TOR_UNIT_TESTS) */
|
||||
|
||||
#endif /* defined(TORTLS_PRIVATE) */
|
||||
|
||||
tor_x509_cert_t *tor_x509_cert_dup(const tor_x509_cert_t *cert);
|
||||
const char *tor_tls_err_to_string(int err);
|
||||
void tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz);
|
||||
|
||||
void tor_tls_free_all(void);
|
||||
|
||||
#define TOR_TLS_CTX_IS_PUBLIC_SERVER (1u<<0)
|
||||
#define TOR_TLS_CTX_USE_ECDHE_P256 (1u<<1)
|
||||
#define TOR_TLS_CTX_USE_ECDHE_P224 (1u<<2)
|
||||
|
||||
void tor_tls_init(void);
|
||||
void tls_log_errors(tor_tls_t *tls, int severity, int domain,
|
||||
const char *doing);
|
||||
int tor_tls_context_init(unsigned flags,
|
||||
crypto_pk_t *client_identity,
|
||||
crypto_pk_t *server_identity,
|
||||
unsigned int key_lifetime);
|
||||
tor_tls_t *tor_tls_new(int sock, int is_server);
|
||||
void tor_tls_context_incref(tor_tls_context_t *ctx);
|
||||
void tor_tls_context_decref(tor_tls_context_t *ctx);
|
||||
tor_tls_context_t *tor_tls_context_get(int is_server);
|
||||
tor_tls_t *tor_tls_new(tor_socket_t sock, int is_server);
|
||||
void tor_tls_set_logged_address(tor_tls_t *tls, const char *address);
|
||||
void tor_tls_set_renegotiate_callback(tor_tls_t *tls,
|
||||
void (*cb)(tor_tls_t *, void *arg),
|
||||
@@ -218,13 +97,9 @@ int tor_tls_is_server(tor_tls_t *tls);
|
||||
void tor_tls_free_(tor_tls_t *tls);
|
||||
#define tor_tls_free(tls) FREE_AND_NULL(tor_tls_t, tor_tls_free_, (tls))
|
||||
int tor_tls_peer_has_cert(tor_tls_t *tls);
|
||||
MOCK_DECL(tor_x509_cert_t *,tor_tls_get_peer_cert,(tor_tls_t *tls));
|
||||
MOCK_DECL(tor_x509_cert_t *,tor_tls_get_own_cert,(tor_tls_t *tls));
|
||||
MOCK_DECL(struct tor_x509_cert_t *,tor_tls_get_peer_cert,(tor_tls_t *tls));
|
||||
MOCK_DECL(struct tor_x509_cert_t *,tor_tls_get_own_cert,(tor_tls_t *tls));
|
||||
int tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity);
|
||||
int tor_tls_check_lifetime(int severity,
|
||||
tor_tls_t *tls, time_t now,
|
||||
int past_tolerance,
|
||||
int future_tolerance);
|
||||
MOCK_DECL(int, tor_tls_read, (tor_tls_t *tls, char *cp, size_t len));
|
||||
int tor_tls_write(tor_tls_t *tls, const char *cp, size_t n);
|
||||
int tor_tls_handshake(tor_tls_t *tls);
|
||||
@@ -232,7 +107,6 @@ int tor_tls_finish_handshake(tor_tls_t *tls);
|
||||
void tor_tls_unblock_renegotiation(tor_tls_t *tls);
|
||||
void tor_tls_block_renegotiation(tor_tls_t *tls);
|
||||
void tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls);
|
||||
int tor_tls_shutdown(tor_tls_t *tls);
|
||||
int tor_tls_get_pending_bytes(tor_tls_t *tls);
|
||||
size_t tor_tls_get_forced_write_size(tor_tls_t *tls);
|
||||
|
||||
@@ -248,6 +122,8 @@ MOCK_DECL(double, tls_get_write_overhead_ratio, (void));
|
||||
int tor_tls_used_v1_handshake(tor_tls_t *tls);
|
||||
int tor_tls_get_num_server_handshakes(tor_tls_t *tls);
|
||||
int tor_tls_server_got_renegotiate(tor_tls_t *tls);
|
||||
MOCK_DECL(int,tor_tls_cert_matches_key,(const tor_tls_t *tls,
|
||||
const struct tor_x509_cert_t *cert));
|
||||
MOCK_DECL(int,tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out));
|
||||
MOCK_DECL(int,tor_tls_export_key_material,(
|
||||
tor_tls_t *tls, uint8_t *secrets_out,
|
||||
@@ -255,37 +131,23 @@ MOCK_DECL(int,tor_tls_export_key_material,(
|
||||
size_t context_len,
|
||||
const char *label));
|
||||
|
||||
#ifdef ENABLE_OPENSSL
|
||||
/* Log and abort if there are unhandled TLS errors in OpenSSL's error stack.
|
||||
*/
|
||||
#define check_no_tls_errors() check_no_tls_errors_(__FILE__,__LINE__)
|
||||
|
||||
void check_no_tls_errors_(const char *fname, int line);
|
||||
|
||||
void tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
|
||||
int severity, int domain, const char *doing);
|
||||
#else
|
||||
#define check_no_tls_errors() STMT_NIL
|
||||
#endif
|
||||
|
||||
void tor_x509_cert_free_(tor_x509_cert_t *cert);
|
||||
#define tor_x509_cert_free(c) \
|
||||
FREE_AND_NULL(tor_x509_cert_t, tor_x509_cert_free_, (c))
|
||||
tor_x509_cert_t *tor_x509_cert_decode(const uint8_t *certificate,
|
||||
size_t certificate_len);
|
||||
void tor_x509_cert_get_der(const tor_x509_cert_t *cert,
|
||||
const uint8_t **encoded_out, size_t *size_out);
|
||||
const common_digests_t *tor_x509_cert_get_id_digests(
|
||||
const tor_x509_cert_t *cert);
|
||||
const common_digests_t *tor_x509_cert_get_cert_digests(
|
||||
const tor_x509_cert_t *cert);
|
||||
int tor_tls_get_my_certs(int server,
|
||||
const tor_x509_cert_t **link_cert_out,
|
||||
const tor_x509_cert_t **id_cert_out);
|
||||
const struct tor_x509_cert_t **link_cert_out,
|
||||
const struct tor_x509_cert_t **id_cert_out);
|
||||
crypto_pk_t *tor_tls_get_my_client_auth_key(void);
|
||||
crypto_pk_t *tor_tls_cert_get_key(tor_x509_cert_t *cert);
|
||||
MOCK_DECL(int,tor_tls_cert_matches_key,(const tor_tls_t *tls,
|
||||
const tor_x509_cert_t *cert));
|
||||
int tor_tls_cert_is_valid(int severity,
|
||||
const tor_x509_cert_t *cert,
|
||||
const tor_x509_cert_t *signing_cert,
|
||||
time_t now,
|
||||
int check_rsa_1024);
|
||||
|
||||
const char *tor_tls_get_ciphersuite_name(tor_tls_t *tls);
|
||||
|
||||
int evaluate_ecgroup_for_tls(const char *ecgroup);
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/* Copyright (c) 2003, Roger Dingledine
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
#ifndef TORTLS_INTERNAL_H
|
||||
#define TORTLS_INTERNAL_H
|
||||
|
||||
int tor_errno_to_tls_error(int e);
|
||||
#ifdef ENABLE_OPENSSL
|
||||
int tor_tls_get_error(tor_tls_t *tls, int r, int extra,
|
||||
const char *doing, int severity, int domain);
|
||||
#endif
|
||||
MOCK_DECL(void, try_to_extract_certs_from_tls,
|
||||
(int severity, tor_tls_t *tls,
|
||||
tor_x509_cert_impl_t **cert_out,
|
||||
tor_x509_cert_impl_t **id_cert_out));
|
||||
|
||||
tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity,
|
||||
unsigned int key_lifetime, unsigned flags, int is_client);
|
||||
int tor_tls_context_init_one(tor_tls_context_t **ppcontext,
|
||||
crypto_pk_t *identity,
|
||||
unsigned int key_lifetime,
|
||||
unsigned int flags,
|
||||
int is_client);
|
||||
int tor_tls_context_init_certificates(tor_tls_context_t *result,
|
||||
crypto_pk_t *identity,
|
||||
unsigned key_lifetime,
|
||||
unsigned flags);
|
||||
void tor_tls_impl_free_(tor_tls_impl_t *ssl);
|
||||
#define tor_tls_impl_free(tls) \
|
||||
FREE_AND_NULL(tor_tls_impl_t, tor_tls_impl_free_, (tls))
|
||||
|
||||
void tor_tls_context_impl_free_(tor_tls_context_impl_t *);
|
||||
#define tor_tls_context_impl_free(ctx) \
|
||||
FREE_AND_NULL(tor_tls_context_impl_t, tor_tls_context_impl_free_, (ctx))
|
||||
|
||||
#ifdef ENABLE_OPENSSL
|
||||
tor_tls_t *tor_tls_get_by_ssl(const struct ssl_st *ssl);
|
||||
int tor_tls_client_is_using_v2_ciphers(const struct ssl_st *ssl);
|
||||
void tor_tls_debug_state_callback(const struct ssl_st *ssl,
|
||||
int type, int val);
|
||||
void tor_tls_server_info_callback(const struct ssl_st *ssl,
|
||||
int type, int val);
|
||||
void tor_tls_allocate_tor_tls_object_ex_data_index(void);
|
||||
|
||||
#if !defined(HAVE_SSL_SESSION_GET_MASTER_KEY)
|
||||
size_t SSL_SESSION_get_master_key(struct ssl_session_st *s,
|
||||
uint8_t *out,
|
||||
size_t len);
|
||||
#endif
|
||||
|
||||
#ifdef TORTLS_OPENSSL_PRIVATE
|
||||
int always_accept_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx);
|
||||
int tor_tls_classify_client_ciphers(const struct ssl_st *ssl,
|
||||
STACK_OF(SSL_CIPHER) *peer_ciphers);
|
||||
STATIC int tor_tls_session_secret_cb(struct ssl_st *ssl, void *secret,
|
||||
int *secret_len,
|
||||
STACK_OF(SSL_CIPHER) *peer_ciphers,
|
||||
CONST_IF_OPENSSL_1_1_API SSL_CIPHER **cipher,
|
||||
void *arg);
|
||||
STATIC int find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m,
|
||||
uint16_t cipher);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef TOR_UNIT_TESTS
|
||||
extern int tor_tls_object_ex_data_index;
|
||||
extern tor_tls_context_t *server_tls_context;
|
||||
extern tor_tls_context_t *client_tls_context;
|
||||
extern uint16_t v2_cipher_list[];
|
||||
extern uint64_t total_bytes_written_over_tls;
|
||||
extern uint64_t total_bytes_written_by_tls;
|
||||
#endif /* defined(TOR_UNIT_TESTS) */
|
||||
|
||||
#endif /* defined(TORTLS_INTERNAL_H) */
|
||||
@@ -0,0 +1,741 @@
|
||||
/* Copyright (c) 2003, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file tortls_nss.c
|
||||
* \brief Wrapper functions to present a consistent interface to
|
||||
* TLS and SSL X.509 functions from NSS.
|
||||
**/
|
||||
|
||||
#include "orconfig.h"
|
||||
|
||||
#define TORTLS_PRIVATE
|
||||
#define TOR_X509_PRIVATE
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
#include "lib/crypt_ops/crypto_cipher.h"
|
||||
#include "lib/crypt_ops/crypto_rand.h"
|
||||
#include "lib/crypt_ops/crypto_dh.h"
|
||||
#include "lib/crypt_ops/crypto_util.h"
|
||||
#include "lib/crypt_ops/crypto_nss_mgt.h"
|
||||
#include "lib/string/printf.h"
|
||||
|
||||
#include "lib/tls/x509.h"
|
||||
#include "lib/tls/x509_internal.h"
|
||||
#include "lib/tls/tortls.h"
|
||||
#include "lib/tls/tortls_st.h"
|
||||
#include "lib/tls/tortls_internal.h"
|
||||
#include "lib/log/util_bug.h"
|
||||
|
||||
#include <prio.h>
|
||||
// For access to raw sockets.
|
||||
#include <private/pprio.h>
|
||||
#include <ssl.h>
|
||||
#include <sslt.h>
|
||||
#include <sslproto.h>
|
||||
#include <certt.h>
|
||||
|
||||
static SECStatus always_accept_cert_cb(void *, PRFileDesc *, PRBool, PRBool);
|
||||
|
||||
MOCK_IMPL(void,
|
||||
try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls,
|
||||
tor_x509_cert_impl_t **cert_out,
|
||||
tor_x509_cert_impl_t **id_cert_out))
|
||||
{
|
||||
tor_assert(tls);
|
||||
tor_assert(cert_out);
|
||||
tor_assert(id_cert_out);
|
||||
(void) severity;
|
||||
|
||||
*cert_out = *id_cert_out = NULL;
|
||||
|
||||
CERTCertificate *peer = SSL_PeerCertificate(tls->ssl);
|
||||
if (!peer)
|
||||
return;
|
||||
*cert_out = peer; /* Now owns pointer. */
|
||||
|
||||
CERTCertList *chain = SSL_PeerCertificateChain(tls->ssl);
|
||||
CERTCertListNode *c = CERT_LIST_HEAD(chain);
|
||||
for (; !CERT_LIST_END(c, chain); c = CERT_LIST_NEXT(c)) {
|
||||
if (CERT_CompareCerts(c->cert, peer) == PR_FALSE) {
|
||||
*id_cert_out = CERT_DupCertificate(c->cert);
|
||||
break;
|
||||
}
|
||||
}
|
||||
CERT_DestroyCertList(chain);
|
||||
}
|
||||
|
||||
static bool
|
||||
we_like_ssl_cipher(SSLCipherAlgorithm ca)
|
||||
{
|
||||
switch (ca) {
|
||||
case ssl_calg_null: return false;
|
||||
case ssl_calg_rc4: return false;
|
||||
case ssl_calg_rc2: return false;
|
||||
case ssl_calg_des: return false;
|
||||
case ssl_calg_3des: return false; /* ???? */
|
||||
case ssl_calg_idea: return false;
|
||||
case ssl_calg_fortezza: return false;
|
||||
case ssl_calg_camellia: return false;
|
||||
case ssl_calg_seed: return false;
|
||||
|
||||
case ssl_calg_aes: return true;
|
||||
case ssl_calg_aes_gcm: return true;
|
||||
case ssl_calg_chacha20: return true;
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
static bool
|
||||
we_like_ssl_kea(SSLKEAType kt)
|
||||
{
|
||||
switch (kt) {
|
||||
case ssl_kea_null: return false;
|
||||
case ssl_kea_rsa: return false; /* ??? */
|
||||
case ssl_kea_fortezza: return false;
|
||||
case ssl_kea_ecdh_psk: return false;
|
||||
case ssl_kea_dh_psk: return false;
|
||||
|
||||
case ssl_kea_dh: return true;
|
||||
case ssl_kea_ecdh: return true;
|
||||
case ssl_kea_tls13_any: return true;
|
||||
|
||||
case ssl_kea_size: return true; /* prevent a warning. */
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
we_like_mac_algorithm(SSLMACAlgorithm ma)
|
||||
{
|
||||
switch (ma) {
|
||||
case ssl_mac_null: return false;
|
||||
case ssl_mac_md5: return false;
|
||||
case ssl_hmac_md5: return false;
|
||||
|
||||
case ssl_mac_sha: return true;
|
||||
case ssl_hmac_sha: return true;
|
||||
case ssl_hmac_sha256: return true;
|
||||
case ssl_mac_aead: return true;
|
||||
case ssl_hmac_sha384: return true;
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
we_like_auth_type(SSLAuthType at)
|
||||
{
|
||||
switch (at) {
|
||||
case ssl_auth_null: return false;
|
||||
case ssl_auth_rsa_decrypt: return false;
|
||||
case ssl_auth_dsa: return false;
|
||||
case ssl_auth_kea: return false;
|
||||
|
||||
case ssl_auth_ecdsa: return true;
|
||||
case ssl_auth_ecdh_rsa: return true;
|
||||
case ssl_auth_ecdh_ecdsa: return true;
|
||||
case ssl_auth_rsa_sign: return true;
|
||||
case ssl_auth_rsa_pss: return true;
|
||||
case ssl_auth_psk: return true;
|
||||
case ssl_auth_tls13_any: return true;
|
||||
|
||||
case ssl_auth_size: return true; /* prevent a warning. */
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
|
||||
tor_tls_context_t *
|
||||
tor_tls_context_new(crypto_pk_t *identity,
|
||||
unsigned int key_lifetime, unsigned flags, int is_client)
|
||||
{
|
||||
SECStatus s;
|
||||
tor_assert(identity);
|
||||
|
||||
tor_tls_context_t *ctx = tor_malloc_zero(sizeof(tor_tls_context_t));
|
||||
ctx->refcnt = 1;
|
||||
|
||||
if (! is_client) {
|
||||
if (tor_tls_context_init_certificates(ctx, identity,
|
||||
key_lifetime, flags) < 0) {
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
/* Create the "model" PRFileDesc that we will use to base others on. */
|
||||
PRFileDesc *tcp = PR_NewTCPSocket();
|
||||
if (!tcp)
|
||||
goto err;
|
||||
|
||||
ctx->ctx = SSL_ImportFD(NULL, tcp);
|
||||
if (!ctx->ctx) {
|
||||
PR_Close(tcp);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the certificate.
|
||||
if (!is_client) {
|
||||
s = SSL_ConfigServerCert(ctx->ctx,
|
||||
ctx->my_link_cert->cert,
|
||||
(SECKEYPrivateKey *)
|
||||
crypto_pk_get_nss_privkey(ctx->link_key),
|
||||
NULL, /* ExtraServerCertData */
|
||||
0 /* DataLen */);
|
||||
if (s != SECSuccess)
|
||||
goto err;
|
||||
}
|
||||
|
||||
// We need a certificate from the other side.
|
||||
if (is_client) {
|
||||
// XXXX does this do anything?
|
||||
s = SSL_OptionSet(ctx->ctx, SSL_REQUIRE_CERTIFICATE, PR_TRUE);
|
||||
if (s != SECSuccess)
|
||||
goto err;
|
||||
}
|
||||
|
||||
// Always accept other side's cert; we'll check it ourselves in goofy
|
||||
// tor ways.
|
||||
s = SSL_AuthCertificateHook(ctx->ctx, always_accept_cert_cb, NULL);
|
||||
|
||||
// We allow simultaneous read and write.
|
||||
s = SSL_OptionSet(ctx->ctx, SSL_ENABLE_FDX, PR_TRUE);
|
||||
if (s != SECSuccess)
|
||||
goto err;
|
||||
// XXXX SSL_ROLLBACK_DETECTION??
|
||||
// XXXX SSL_ENABLE_ALPN??
|
||||
|
||||
// Force client-mode or server_mode.
|
||||
s = SSL_OptionSet(ctx->ctx,
|
||||
is_client ? SSL_HANDSHAKE_AS_CLIENT : SSL_HANDSHAKE_AS_SERVER,
|
||||
PR_TRUE);
|
||||
if (s != SECSuccess)
|
||||
goto err;
|
||||
|
||||
// Disable everything before TLS 1.0; support everything else.
|
||||
{
|
||||
SSLVersionRange vrange;
|
||||
memset(&vrange, 0, sizeof(vrange));
|
||||
s = SSL_VersionRangeGetSupported(ssl_variant_stream, &vrange);
|
||||
if (s != SECSuccess)
|
||||
goto err;
|
||||
if (vrange.min < SSL_LIBRARY_VERSION_TLS_1_0)
|
||||
vrange.min = SSL_LIBRARY_VERSION_TLS_1_0;
|
||||
s = SSL_VersionRangeSet(ctx->ctx, &vrange);
|
||||
if (s != SECSuccess)
|
||||
goto err;
|
||||
}
|
||||
|
||||
// Only support strong ciphers.
|
||||
{
|
||||
const PRUint16 *ciphers = SSL_GetImplementedCiphers();
|
||||
const PRUint16 n_ciphers = SSL_GetNumImplementedCiphers();
|
||||
PRUint16 i;
|
||||
for (i = 0; i < n_ciphers; ++i) {
|
||||
SSLCipherSuiteInfo info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
s = SSL_GetCipherSuiteInfo(ciphers[i], &info, sizeof(info));
|
||||
if (s != SECSuccess)
|
||||
goto err;
|
||||
if (BUG(info.cipherSuite != ciphers[i]))
|
||||
goto err;
|
||||
int disable = info.effectiveKeyBits < 128 ||
|
||||
info.macBits < 128 ||
|
||||
!we_like_ssl_cipher(info.symCipher) ||
|
||||
!we_like_ssl_kea(info.keaType) ||
|
||||
!we_like_mac_algorithm(info.macAlgorithm) ||
|
||||
!we_like_auth_type(info.authType)/* Requires NSS 3.24 */;
|
||||
|
||||
s = SSL_CipherPrefSet(ctx->ctx, ciphers[i],
|
||||
disable ? PR_FALSE : PR_TRUE);
|
||||
if (s != SECSuccess)
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
// Only use DH and ECDH keys once.
|
||||
s = SSL_OptionSet(ctx->ctx, SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE);
|
||||
if (s != SECSuccess)
|
||||
goto err;
|
||||
|
||||
// don't cache sessions.
|
||||
s = SSL_OptionSet(ctx->ctx, SSL_NO_CACHE, PR_TRUE);
|
||||
if (s != SECSuccess)
|
||||
goto err;
|
||||
|
||||
// Enable DH.
|
||||
s = SSL_OptionSet(ctx->ctx, SSL_ENABLE_SERVER_DHE, PR_TRUE);
|
||||
if (s != SECSuccess)
|
||||
goto err;
|
||||
|
||||
// Set DH and ECDH groups.
|
||||
SSLNamedGroup groups[] = {
|
||||
ssl_grp_ec_curve25519,
|
||||
ssl_grp_ec_secp256r1,
|
||||
ssl_grp_ec_secp224r1,
|
||||
ssl_grp_ffdhe_2048,
|
||||
};
|
||||
s = SSL_NamedGroupConfig(ctx->ctx, groups, ARRAY_LENGTH(groups));
|
||||
if (s != SECSuccess)
|
||||
goto err;
|
||||
|
||||
// These features are off by default, so we don't need to disable them:
|
||||
// Session tickets
|
||||
// Renegotiation
|
||||
// Compression
|
||||
|
||||
goto done;
|
||||
err:
|
||||
tor_tls_context_decref(ctx);
|
||||
ctx = NULL;
|
||||
done:
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void
|
||||
tor_tls_context_impl_free_(tor_tls_context_impl_t *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
return;
|
||||
PR_Close(ctx);
|
||||
}
|
||||
|
||||
void
|
||||
tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz)
|
||||
{
|
||||
(void)tls;
|
||||
(void)buf;
|
||||
(void)sz;
|
||||
// AFAICT, NSS doesn't expose its internal state.
|
||||
buf[0]=0;
|
||||
}
|
||||
|
||||
void
|
||||
tor_tls_init(void)
|
||||
{
|
||||
/* We don't have any global setup to do yet, but that will change */
|
||||
}
|
||||
|
||||
void
|
||||
tls_log_errors(tor_tls_t *tls, int severity, int domain,
|
||||
const char *doing)
|
||||
{
|
||||
/* This implementation is a little different for NSS than it is for OpenSSL
|
||||
-- it logs the last error whether anything actually failed or not. So we
|
||||
have to only call it when something has gone wrong and we have a real
|
||||
error to report. */
|
||||
|
||||
(void)tls;
|
||||
PRErrorCode code = PORT_GetError();
|
||||
|
||||
const char *addr = tls ? tls->address : NULL;
|
||||
const char *string = PORT_ErrorToString(code);
|
||||
const char *name = PORT_ErrorToName(code);
|
||||
char buf[16];
|
||||
if (!string)
|
||||
string = "<unrecognized>";
|
||||
if (!name) {
|
||||
tor_snprintf(buf, sizeof(buf), "%d", code);
|
||||
name = buf;
|
||||
}
|
||||
|
||||
const char *with = addr ? " with " : "";
|
||||
addr = addr ? addr : "";
|
||||
if (doing) {
|
||||
log_fn(severity, domain, "TLS error %s while %s%s%s: %s",
|
||||
name, doing, with, addr, string);
|
||||
} else {
|
||||
log_fn(severity, domain, "TLS error %s%s%s: %s", name, string,
|
||||
with, addr);
|
||||
}
|
||||
}
|
||||
|
||||
tor_tls_t *
|
||||
tor_tls_new(tor_socket_t sock, int is_server)
|
||||
{
|
||||
(void)sock;
|
||||
tor_tls_context_t *ctx = tor_tls_context_get(is_server);
|
||||
|
||||
PRFileDesc *tcp = NULL;
|
||||
if (SOCKET_OK(sock)) {
|
||||
tcp = PR_ImportTCPSocket(sock);
|
||||
} else {
|
||||
tcp = PR_NewTCPSocket();
|
||||
}
|
||||
|
||||
if (!tcp)
|
||||
return NULL;
|
||||
|
||||
PRFileDesc *ssl = SSL_ImportFD(ctx->ctx, tcp);
|
||||
if (!ssl) {
|
||||
PR_Close(tcp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tor_tls_t *tls = tor_malloc_zero(sizeof(tor_tls_t));
|
||||
tls->magic = TOR_TLS_MAGIC;
|
||||
tls->context = ctx;
|
||||
tor_tls_context_incref(ctx);
|
||||
tls->ssl = ssl;
|
||||
tls->socket = sock;
|
||||
tls->state = TOR_TLS_ST_HANDSHAKE;
|
||||
tls->isServer = !!is_server;
|
||||
|
||||
if (!is_server) {
|
||||
/* Set a random SNI */
|
||||
char *fake_hostname = crypto_random_hostname(4,25, "www.",".com");
|
||||
SSL_SetURL(tls->ssl, fake_hostname);
|
||||
tor_free(fake_hostname);
|
||||
}
|
||||
SECStatus s = SSL_ResetHandshake(ssl, is_server ? PR_TRUE : PR_FALSE);
|
||||
if (s != SECSuccess) {
|
||||
tls_log_errors(tls, LOG_WARN, LD_CRYPTO, "resetting handshake state");
|
||||
}
|
||||
|
||||
return tls;
|
||||
}
|
||||
|
||||
void
|
||||
tor_tls_set_renegotiate_callback(tor_tls_t *tls,
|
||||
void (*cb)(tor_tls_t *, void *arg),
|
||||
void *arg)
|
||||
{
|
||||
tor_assert(tls);
|
||||
(void)cb;
|
||||
(void)arg;
|
||||
|
||||
/* We don't support renegotiation-based TLS with NSS. */
|
||||
}
|
||||
|
||||
void
|
||||
tor_tls_impl_free_(tor_tls_impl_t *tls)
|
||||
{
|
||||
// XXXX This will close the underlying fd, which our OpenSSL version does
|
||||
// not do!
|
||||
if (!tls)
|
||||
return;
|
||||
|
||||
PR_Close(tls);
|
||||
}
|
||||
|
||||
int
|
||||
tor_tls_peer_has_cert(tor_tls_t *tls)
|
||||
{
|
||||
CERTCertificate *cert = SSL_PeerCertificate(tls->ssl);
|
||||
int result = (cert != NULL);
|
||||
CERT_DestroyCertificate(cert);
|
||||
return result;
|
||||
}
|
||||
|
||||
MOCK_IMPL(tor_x509_cert_t *,
|
||||
tor_tls_get_peer_cert,(tor_tls_t *tls))
|
||||
{
|
||||
CERTCertificate *cert = SSL_PeerCertificate(tls->ssl);
|
||||
if (cert)
|
||||
return tor_x509_cert_new(cert);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MOCK_IMPL(tor_x509_cert_t *,
|
||||
tor_tls_get_own_cert,(tor_tls_t *tls))
|
||||
{
|
||||
tor_assert(tls);
|
||||
CERTCertificate *cert = SSL_LocalCertificate(tls->ssl);
|
||||
if (cert)
|
||||
return tor_x509_cert_new(cert);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MOCK_IMPL(int,
|
||||
tor_tls_read, (tor_tls_t *tls, char *cp, size_t len))
|
||||
{
|
||||
tor_assert(tls);
|
||||
tor_assert(cp);
|
||||
tor_assert(len < INT_MAX);
|
||||
|
||||
PRInt32 rv = PR_Read(tls->ssl, cp, (int)len);
|
||||
// log_debug(LD_NET, "PR_Read(%zu) returned %d", n, (int)rv);
|
||||
if (rv > 0) {
|
||||
tls->n_read_since_last_check += rv;
|
||||
return rv;
|
||||
}
|
||||
if (rv == 0)
|
||||
return TOR_TLS_CLOSE;
|
||||
PRErrorCode err = PORT_GetError();
|
||||
if (err == PR_WOULD_BLOCK_ERROR) {
|
||||
return TOR_TLS_WANTREAD; // XXXX ????
|
||||
} else {
|
||||
tls_log_errors(tls, LOG_NOTICE, LD_CRYPTO, "reading"); // XXXX
|
||||
return TOR_TLS_ERROR_MISC; // ????
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
tor_tls_write(tor_tls_t *tls, const char *cp, size_t n)
|
||||
{
|
||||
tor_assert(tls);
|
||||
tor_assert(cp || n == 0);
|
||||
tor_assert(n < INT_MAX);
|
||||
|
||||
PRInt32 rv = PR_Write(tls->ssl, cp, (int)n);
|
||||
// log_debug(LD_NET, "PR_Write(%zu) returned %d", n, (int)rv);
|
||||
if (rv > 0) {
|
||||
tls->n_written_since_last_check += rv;
|
||||
return rv;
|
||||
}
|
||||
if (rv == 0)
|
||||
return TOR_TLS_ERROR_MISC;
|
||||
PRErrorCode err = PORT_GetError();
|
||||
|
||||
if (err == PR_WOULD_BLOCK_ERROR) {
|
||||
return TOR_TLS_WANTWRITE; // XXXX ????
|
||||
} else {
|
||||
tls_log_errors(tls, LOG_NOTICE, LD_CRYPTO, "writing"); // XXXX
|
||||
return TOR_TLS_ERROR_MISC; // ????
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
tor_tls_handshake(tor_tls_t *tls)
|
||||
{
|
||||
tor_assert(tls);
|
||||
tor_assert(tls->state == TOR_TLS_ST_HANDSHAKE);
|
||||
|
||||
SECStatus s = SSL_ForceHandshake(tls->ssl);
|
||||
if (s == SECSuccess) {
|
||||
tls->state = TOR_TLS_ST_OPEN;
|
||||
log_debug(LD_NET, "SSL handshake is supposedly complete.");
|
||||
return tor_tls_finish_handshake(tls);
|
||||
}
|
||||
if (PORT_GetError() == PR_WOULD_BLOCK_ERROR)
|
||||
return TOR_TLS_WANTREAD; /* XXXX What about wantwrite? */
|
||||
|
||||
return TOR_TLS_ERROR_MISC; // XXXX
|
||||
}
|
||||
|
||||
int
|
||||
tor_tls_finish_handshake(tor_tls_t *tls)
|
||||
{
|
||||
tor_assert(tls);
|
||||
// We don't need to do any of the weird handshake nonsense stuff on NSS,
|
||||
// since we only support recent handshakes.
|
||||
return TOR_TLS_DONE;
|
||||
}
|
||||
|
||||
void
|
||||
tor_tls_unblock_renegotiation(tor_tls_t *tls)
|
||||
{
|
||||
tor_assert(tls);
|
||||
/* We don't support renegotiation with NSS. */
|
||||
}
|
||||
|
||||
void
|
||||
tor_tls_block_renegotiation(tor_tls_t *tls)
|
||||
{
|
||||
tor_assert(tls);
|
||||
/* We don't support renegotiation with NSS. */
|
||||
}
|
||||
|
||||
void
|
||||
tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls)
|
||||
{
|
||||
tor_assert(tls);
|
||||
/* We don't support renegotiation with NSS. */
|
||||
}
|
||||
|
||||
int
|
||||
tor_tls_get_pending_bytes(tor_tls_t *tls)
|
||||
{
|
||||
tor_assert(tls);
|
||||
int n = SSL_DataPending(tls->ssl);
|
||||
if (n < 0) {
|
||||
tls_log_errors(tls, LOG_WARN, LD_CRYPTO, "looking up pending bytes");
|
||||
return 0;
|
||||
}
|
||||
return (int)n;
|
||||
}
|
||||
|
||||
size_t
|
||||
tor_tls_get_forced_write_size(tor_tls_t *tls)
|
||||
{
|
||||
tor_assert(tls);
|
||||
/* NSS doesn't have the same "forced write" restriction as openssl. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
tor_tls_get_n_raw_bytes(tor_tls_t *tls,
|
||||
size_t *n_read, size_t *n_written)
|
||||
{
|
||||
tor_assert(tls);
|
||||
tor_assert(n_read);
|
||||
tor_assert(n_written);
|
||||
/* XXXX We don't curently have a way to measure this information correctly
|
||||
* in NSS; we could do that with a PRIO layer, but it'll take a little
|
||||
* coding. For now, we just track the number of bytes sent _in_ the TLS
|
||||
* stream. Doing this will make our rate-limiting slightly inaccurate. */
|
||||
*n_read = tls->n_read_since_last_check;
|
||||
*n_written = tls->n_written_since_last_check;
|
||||
tls->n_read_since_last_check = tls->n_written_since_last_check = 0;
|
||||
}
|
||||
|
||||
int
|
||||
tor_tls_get_buffer_sizes(tor_tls_t *tls,
|
||||
size_t *rbuf_capacity, size_t *rbuf_bytes,
|
||||
size_t *wbuf_capacity, size_t *wbuf_bytes)
|
||||
{
|
||||
tor_assert(tls);
|
||||
tor_assert(rbuf_capacity);
|
||||
tor_assert(rbuf_bytes);
|
||||
tor_assert(wbuf_capacity);
|
||||
tor_assert(wbuf_bytes);
|
||||
|
||||
/* This is an acceptable way to say "we can't measure this." */
|
||||
return -1;
|
||||
}
|
||||
|
||||
MOCK_IMPL(double,
|
||||
tls_get_write_overhead_ratio, (void))
|
||||
{
|
||||
/* XXX We don't currently have a way to measure this in NSS; we could do that
|
||||
* XXX with a PRIO layer, but it'll take a little coding. */
|
||||
return 0.95;
|
||||
}
|
||||
|
||||
int
|
||||
tor_tls_used_v1_handshake(tor_tls_t *tls)
|
||||
{
|
||||
tor_assert(tls);
|
||||
/* We don't support or allow the V1 handshake with NSS.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
tor_tls_server_got_renegotiate(tor_tls_t *tls)
|
||||
{
|
||||
tor_assert(tls);
|
||||
return 0; /* We don't support renegotiation with NSS */
|
||||
}
|
||||
|
||||
MOCK_IMPL(int,
|
||||
tor_tls_cert_matches_key,(const tor_tls_t *tls,
|
||||
const struct tor_x509_cert_t *cert))
|
||||
{
|
||||
tor_assert(tls);
|
||||
tor_assert(cert);
|
||||
int rv = 0;
|
||||
|
||||
CERTCertificate *peercert = SSL_PeerCertificate(tls->ssl);
|
||||
if (!peercert)
|
||||
goto done;
|
||||
CERTSubjectPublicKeyInfo *peer_info = &peercert->subjectPublicKeyInfo;
|
||||
CERTSubjectPublicKeyInfo *cert_info = &cert->cert->subjectPublicKeyInfo;
|
||||
rv = SECOID_CompareAlgorithmID(&peer_info->algorithm,
|
||||
&cert_info->algorithm) == 0 &&
|
||||
SECITEM_ItemsAreEqual(&peer_info->subjectPublicKey,
|
||||
&cert_info->subjectPublicKey);
|
||||
|
||||
done:
|
||||
if (peercert)
|
||||
CERT_DestroyCertificate(peercert);
|
||||
return rv;
|
||||
}
|
||||
|
||||
MOCK_IMPL(int,
|
||||
tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out))
|
||||
{
|
||||
tor_assert(tls);
|
||||
tor_assert(secrets_out);
|
||||
|
||||
/* There's no way to get this information out of NSS. */
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
MOCK_IMPL(int,
|
||||
tor_tls_export_key_material,(tor_tls_t *tls, uint8_t *secrets_out,
|
||||
const uint8_t *context,
|
||||
size_t context_len,
|
||||
const char *label))
|
||||
{
|
||||
tor_assert(tls);
|
||||
tor_assert(secrets_out);
|
||||
tor_assert(context);
|
||||
tor_assert(label);
|
||||
tor_assert(strlen(label) <= UINT_MAX);
|
||||
tor_assert(context_len <= UINT_MAX);
|
||||
|
||||
SECStatus s;
|
||||
s = SSL_ExportKeyingMaterial(tls->ssl,
|
||||
label, (unsigned)strlen(label),
|
||||
PR_TRUE, context, (unsigned)context_len,
|
||||
secrets_out, DIGEST256_LEN);
|
||||
|
||||
return (s == SECSuccess) ? 0 : -1;
|
||||
}
|
||||
|
||||
const char *
|
||||
tor_tls_get_ciphersuite_name(tor_tls_t *tls)
|
||||
{
|
||||
tor_assert(tls);
|
||||
|
||||
SSLChannelInfo channel_info;
|
||||
SSLCipherSuiteInfo cipher_info;
|
||||
|
||||
memset(&channel_info, 0, sizeof(channel_info));
|
||||
memset(&cipher_info, 0, sizeof(cipher_info));
|
||||
|
||||
SECStatus s = SSL_GetChannelInfo(tls->ssl,
|
||||
&channel_info, sizeof(channel_info));
|
||||
if (s != SECSuccess)
|
||||
return NULL;
|
||||
|
||||
s = SSL_GetCipherSuiteInfo(channel_info.cipherSuite,
|
||||
&cipher_info, sizeof(cipher_info));
|
||||
if (s != SECSuccess)
|
||||
return NULL;
|
||||
|
||||
return cipher_info.cipherSuiteName;
|
||||
}
|
||||
|
||||
/** The group we should use for ecdhe when none was selected. */
|
||||
#define SEC_OID_TOR_DEFAULT_ECDHE_GROUP SEC_OID_ANSIX962_EC_PRIME256V1
|
||||
|
||||
int
|
||||
evaluate_ecgroup_for_tls(const char *ecgroup)
|
||||
{
|
||||
SECOidTag tag;
|
||||
|
||||
if (!ecgroup)
|
||||
tag = SEC_OID_TOR_DEFAULT_ECDHE_GROUP;
|
||||
else if (!strcasecmp(ecgroup, "P256"))
|
||||
tag = SEC_OID_ANSIX962_EC_PRIME256V1;
|
||||
else if (!strcasecmp(ecgroup, "P224"))
|
||||
tag = SEC_OID_SECG_EC_SECP224R1;
|
||||
else
|
||||
return 0;
|
||||
|
||||
/* I don't think we need any additional tests here for NSS */
|
||||
(void) tag;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SECStatus
|
||||
always_accept_cert_cb(void *arg, PRFileDesc *ssl, PRBool checkSig,
|
||||
PRBool isServer)
|
||||
{
|
||||
(void)arg;
|
||||
(void)ssl;
|
||||
(void)checkSig;
|
||||
(void)isServer;
|
||||
return SECSuccess;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,74 @@
|
||||
/* Copyright (c) 2003, Roger Dingledine
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
#ifndef TOR_TORTLS_ST_H
|
||||
#define TOR_TORTLS_ST_H
|
||||
|
||||
#include "lib/net/socket.h"
|
||||
|
||||
#define TOR_TLS_MAGIC 0x71571571
|
||||
|
||||
typedef enum {
|
||||
TOR_TLS_ST_HANDSHAKE, TOR_TLS_ST_OPEN, TOR_TLS_ST_GOTCLOSE,
|
||||
TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED, TOR_TLS_ST_RENEGOTIATE,
|
||||
TOR_TLS_ST_BUFFEREVENT
|
||||
} tor_tls_state_t;
|
||||
#define tor_tls_state_bitfield_t ENUM_BF(tor_tls_state_t)
|
||||
|
||||
struct tor_tls_context_t {
|
||||
int refcnt;
|
||||
tor_tls_context_impl_t *ctx;
|
||||
struct tor_x509_cert_t *my_link_cert;
|
||||
struct tor_x509_cert_t *my_id_cert;
|
||||
struct tor_x509_cert_t *my_auth_cert;
|
||||
crypto_pk_t *link_key;
|
||||
crypto_pk_t *auth_key;
|
||||
};
|
||||
|
||||
/** Holds a SSL object and its associated data. Members are only
|
||||
* accessed from within tortls.c.
|
||||
*/
|
||||
struct tor_tls_t {
|
||||
uint32_t magic;
|
||||
tor_tls_context_t *context; /** A link to the context object for this tls. */
|
||||
tor_tls_impl_t *ssl; /**< An OpenSSL SSL object or NSS PRFileDesc. */
|
||||
tor_socket_t socket; /**< The underlying file descriptor for this TLS
|
||||
* connection. */
|
||||
char *address; /**< An address to log when describing this connection. */
|
||||
tor_tls_state_bitfield_t state : 3; /**< The current SSL state,
|
||||
* depending on which operations
|
||||
* have completed successfully. */
|
||||
unsigned int isServer:1; /**< True iff this is a server-side connection */
|
||||
unsigned int wasV2Handshake:1; /**< True iff the original handshake for
|
||||
* this connection used the updated version
|
||||
* of the connection protocol (client sends
|
||||
* different cipher list, server sends only
|
||||
* one certificate). */
|
||||
/** True iff we should call negotiated_callback when we're done reading. */
|
||||
unsigned int got_renegotiate:1;
|
||||
#ifdef ENABLE_OPENSSL
|
||||
/** Return value from tor_tls_classify_client_ciphers, or 0 if we haven't
|
||||
* called that function yet. */
|
||||
int8_t client_cipher_list_type;
|
||||
size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last
|
||||
* time. */
|
||||
/** Last values retrieved from BIO_number_read()/write(); see
|
||||
* tor_tls_get_n_raw_bytes() for usage.
|
||||
*/
|
||||
unsigned long last_write_count;
|
||||
unsigned long last_read_count;
|
||||
/** If set, a callback to invoke whenever the client tries to renegotiate
|
||||
* the handshake. */
|
||||
void (*negotiated_callback)(tor_tls_t *tls, void *arg);
|
||||
/** Argument to pass to negotiated_callback. */
|
||||
void *callback_arg;
|
||||
#endif
|
||||
#ifdef ENABLE_NSS
|
||||
size_t n_read_since_last_check;
|
||||
size_t n_written_since_last_check;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,143 @@
|
||||
/* Copyright (c) 2003, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file x509_openssl.c
|
||||
* \brief Wrapper functions to present a consistent interface to
|
||||
* X.509 functions.
|
||||
**/
|
||||
|
||||
#define TOR_X509_PRIVATE
|
||||
#include "lib/tls/x509.h"
|
||||
#include "lib/tls/x509_internal.h"
|
||||
#include "lib/log/util_bug.h"
|
||||
#include "lib/crypt_ops/crypto_rand.h"
|
||||
#include "lib/crypt_ops/crypto_util.h"
|
||||
|
||||
/** Choose the start and end times for a certificate */
|
||||
void
|
||||
tor_tls_pick_certificate_lifetime(time_t now,
|
||||
unsigned int cert_lifetime,
|
||||
time_t *start_time_out,
|
||||
time_t *end_time_out)
|
||||
{
|
||||
time_t start_time, end_time;
|
||||
/* Make sure we're part-way through the certificate lifetime, rather
|
||||
* than having it start right now. Don't choose quite uniformly, since
|
||||
* then we might pick a time where we're about to expire. Lastly, be
|
||||
* sure to start on a day boundary. */
|
||||
/* Our certificate lifetime will be cert_lifetime no matter what, but if we
|
||||
* start cert_lifetime in the past, we'll have 0 real lifetime. instead we
|
||||
* start up to (cert_lifetime - min_real_lifetime - start_granularity) in
|
||||
* the past. */
|
||||
const time_t min_real_lifetime = 24*3600;
|
||||
const time_t start_granularity = 24*3600;
|
||||
time_t earliest_start_time;
|
||||
/* Don't actually start in the future! */
|
||||
if (cert_lifetime <= min_real_lifetime + start_granularity) {
|
||||
earliest_start_time = now - 1;
|
||||
} else {
|
||||
earliest_start_time = now + min_real_lifetime + start_granularity
|
||||
- cert_lifetime;
|
||||
}
|
||||
start_time = crypto_rand_time_range(earliest_start_time, now);
|
||||
/* Round the start time back to the start of a day. */
|
||||
start_time -= start_time % start_granularity;
|
||||
|
||||
end_time = start_time + cert_lifetime;
|
||||
|
||||
*start_time_out = start_time;
|
||||
*end_time_out = end_time;
|
||||
}
|
||||
|
||||
/** Return a set of digests for the public key in <b>cert</b>, or NULL if this
|
||||
* cert's public key is not one we know how to take the digest of. */
|
||||
const common_digests_t *
|
||||
tor_x509_cert_get_id_digests(const tor_x509_cert_t *cert)
|
||||
{
|
||||
if (cert->pkey_digests_set)
|
||||
return &cert->pkey_digests;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** Return a set of digests for the public key in <b>cert</b>. */
|
||||
const common_digests_t *
|
||||
tor_x509_cert_get_cert_digests(const tor_x509_cert_t *cert)
|
||||
{
|
||||
return &cert->cert_digests;
|
||||
}
|
||||
|
||||
/** Free all storage held in <b>cert</b> */
|
||||
void
|
||||
tor_x509_cert_free_(tor_x509_cert_t *cert)
|
||||
{
|
||||
if (! cert)
|
||||
return;
|
||||
tor_x509_cert_impl_free(cert->cert);
|
||||
#ifdef ENABLE_OPENSSL
|
||||
tor_free(cert->encoded);
|
||||
#endif
|
||||
memwipe(cert, 0x03, sizeof(*cert));
|
||||
/* LCOV_EXCL_BR_START since cert will never be NULL here */
|
||||
tor_free(cert);
|
||||
/* LCOV_EXCL_BR_STOP */
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a new tor_x509_cert_t to hold the certificate "x509_cert".
|
||||
*
|
||||
* Steals a reference to x509_cert.
|
||||
*/
|
||||
MOCK_IMPL(tor_x509_cert_t *,
|
||||
tor_x509_cert_new,(tor_x509_cert_impl_t *x509_cert))
|
||||
{
|
||||
tor_x509_cert_t *cert;
|
||||
|
||||
if (!x509_cert)
|
||||
return NULL;
|
||||
|
||||
cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
|
||||
cert->cert = x509_cert;
|
||||
|
||||
if (tor_x509_cert_set_cached_der_encoding(cert) < 0)
|
||||
goto err;
|
||||
|
||||
{
|
||||
const uint8_t *encoded=NULL;
|
||||
size_t encoded_len=0;
|
||||
tor_x509_cert_get_der(cert, &encoded, &encoded_len);
|
||||
tor_assert(encoded);
|
||||
crypto_common_digests(&cert->cert_digests, (char *)encoded, encoded_len);
|
||||
}
|
||||
|
||||
{
|
||||
crypto_pk_t *pk = tor_tls_cert_get_key(cert);
|
||||
if (pk) {
|
||||
if (crypto_pk_get_common_digests(pk, &cert->pkey_digests) < 0) {
|
||||
log_warn(LD_CRYPTO, "unable to compute digests of certificate key");
|
||||
crypto_pk_free(pk);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
cert->pkey_digests_set = 1;
|
||||
crypto_pk_free(pk);
|
||||
}
|
||||
|
||||
return cert;
|
||||
err:
|
||||
log_err(LD_CRYPTO, "Couldn't wrap encoded X509 certificate.");
|
||||
tor_x509_cert_free(cert);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** Return a new copy of <b>cert</b>. */
|
||||
tor_x509_cert_t *
|
||||
tor_x509_cert_dup(const tor_x509_cert_t *cert)
|
||||
{
|
||||
tor_assert(cert);
|
||||
tor_assert(cert->cert);
|
||||
return tor_x509_cert_new(tor_x509_cert_impl_dup_(cert->cert));
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/* Copyright (c) 2003, Roger Dingledine
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
#ifndef TOR_X509_H
|
||||
#define TOR_X509_H
|
||||
|
||||
/**
|
||||
* \file x509.h
|
||||
* \brief Headers for tortls.c
|
||||
**/
|
||||
|
||||
#include "lib/crypt_ops/crypto_rsa.h"
|
||||
#include "lib/testsupport/testsupport.h"
|
||||
|
||||
/* Opaque structure to hold an X509 certificate. */
|
||||
typedef struct tor_x509_cert_t tor_x509_cert_t;
|
||||
|
||||
#ifdef ENABLE_NSS
|
||||
typedef struct CERTCertificateStr tor_x509_cert_impl_t;
|
||||
#elif defined(ENABLE_OPENSSL)
|
||||
typedef struct x509_st tor_x509_cert_impl_t;
|
||||
#endif
|
||||
|
||||
#ifdef TOR_X509_PRIVATE
|
||||
/** Structure that we use for a single certificate. */
|
||||
struct tor_x509_cert_t {
|
||||
tor_x509_cert_impl_t *cert;
|
||||
#ifdef ENABLE_OPENSSL
|
||||
uint8_t *encoded;
|
||||
size_t encoded_len;
|
||||
#endif
|
||||
unsigned pkey_digests_set : 1;
|
||||
common_digests_t cert_digests;
|
||||
common_digests_t pkey_digests;
|
||||
};
|
||||
#endif
|
||||
|
||||
void tor_tls_pick_certificate_lifetime(time_t now,
|
||||
unsigned cert_lifetime,
|
||||
time_t *start_time_out,
|
||||
time_t *end_time_out);
|
||||
|
||||
#ifdef TOR_UNIT_TESTS
|
||||
tor_x509_cert_t *tor_x509_cert_replace_expiration(
|
||||
const tor_x509_cert_t *inp,
|
||||
time_t new_expiration_time,
|
||||
crypto_pk_t *signing_key);
|
||||
#endif
|
||||
|
||||
tor_x509_cert_t *tor_x509_cert_dup(const tor_x509_cert_t *cert);
|
||||
|
||||
void tor_x509_cert_free_(tor_x509_cert_t *cert);
|
||||
#define tor_x509_cert_free(c) \
|
||||
FREE_AND_NULL(tor_x509_cert_t, tor_x509_cert_free_, (c))
|
||||
tor_x509_cert_t *tor_x509_cert_decode(const uint8_t *certificate,
|
||||
size_t certificate_len);
|
||||
void tor_x509_cert_get_der(const tor_x509_cert_t *cert,
|
||||
const uint8_t **encoded_out, size_t *size_out);
|
||||
|
||||
const common_digests_t *tor_x509_cert_get_id_digests(
|
||||
const tor_x509_cert_t *cert);
|
||||
const common_digests_t *tor_x509_cert_get_cert_digests(
|
||||
const tor_x509_cert_t *cert);
|
||||
|
||||
crypto_pk_t *tor_tls_cert_get_key(tor_x509_cert_t *cert);
|
||||
|
||||
int tor_tls_cert_is_valid(int severity,
|
||||
const tor_x509_cert_t *cert,
|
||||
const tor_x509_cert_t *signing_cert,
|
||||
time_t now,
|
||||
int check_rsa_1024);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,53 @@
|
||||
/* Copyright (c) 2003, Roger Dingledine
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
#ifndef TOR_X509_INTERNAL_H
|
||||
#define TOR_X509_INTERNAL_H
|
||||
|
||||
/**
|
||||
* \file x509.h
|
||||
* \brief Internal headers for tortls.c
|
||||
**/
|
||||
|
||||
#include "lib/crypt_ops/crypto_rsa.h"
|
||||
#include "lib/testsupport/testsupport.h"
|
||||
|
||||
/**
|
||||
* How skewed do we allow our clock to be with respect to certificates that
|
||||
* seem to be expired? (seconds)
|
||||
*/
|
||||
#define TOR_X509_PAST_SLOP (2*24*60*60)
|
||||
/**
|
||||
* How skewed do we allow our clock to be with respect to certificates that
|
||||
* seem to come from the future? (seconds)
|
||||
*/
|
||||
#define TOR_X509_FUTURE_SLOP (30*24*60*60)
|
||||
|
||||
MOCK_DECL(tor_x509_cert_impl_t *, tor_tls_create_certificate,
|
||||
(crypto_pk_t *rsa,
|
||||
crypto_pk_t *rsa_sign,
|
||||
const char *cname,
|
||||
const char *cname_sign,
|
||||
unsigned int cert_lifetime));
|
||||
MOCK_DECL(tor_x509_cert_t *, tor_x509_cert_new,
|
||||
(tor_x509_cert_impl_t *x509_cert));
|
||||
|
||||
int tor_x509_check_cert_lifetime_internal(int severity,
|
||||
const tor_x509_cert_impl_t *cert,
|
||||
time_t now,
|
||||
int past_tolerance,
|
||||
int future_tolerance);
|
||||
|
||||
void tor_x509_cert_impl_free_(tor_x509_cert_impl_t *cert);
|
||||
#define tor_x509_cert_impl_free(cert) \
|
||||
FREE_AND_NULL(tor_x509_cert_impl_t, tor_x509_cert_impl_free_, (cert))
|
||||
tor_x509_cert_impl_t *tor_x509_cert_impl_dup_(tor_x509_cert_impl_t *cert);
|
||||
#ifdef ENABLE_OPENSSL
|
||||
int tor_x509_cert_set_cached_der_encoding(tor_x509_cert_t *cert);
|
||||
#else
|
||||
#define tor_x509_cert_set_cached_der_encoding(cert) (0)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,450 @@
|
||||
/* Copyright (c) 2003, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file x509_nss.c
|
||||
* \brief Wrapper functions to present a consistent interface to
|
||||
* X.509 functions from NSS.
|
||||
**/
|
||||
|
||||
#define TOR_X509_PRIVATE
|
||||
#include "lib/tls/x509.h"
|
||||
#include "lib/tls/x509_internal.h"
|
||||
#include "lib/tls/tortls.h"
|
||||
#include "lib/crypt_ops/crypto_rand.h"
|
||||
#include "lib/crypt_ops/crypto_util.h"
|
||||
#include "lib/crypt_ops/crypto_nss_mgt.h"
|
||||
#include "lib/log/util_bug.h"
|
||||
#include "lib/encoding/time_fmt.h"
|
||||
#include "lib/string/printf.h"
|
||||
|
||||
#include <pk11pub.h>
|
||||
#include <cryptohi.h>
|
||||
#include <cert.h>
|
||||
#include <keyhi.h>
|
||||
#include <time.h>
|
||||
|
||||
/* Units of PRTime per second.
|
||||
*
|
||||
* (PRTime is based in microseconds since the Unix
|
||||
* epoch.) */
|
||||
#define PRTIME_PER_SEC (1000*1000)
|
||||
|
||||
static tor_x509_cert_impl_t *tor_x509_cert_decode_internal(
|
||||
const uint8_t *certificate, int certificate_len);
|
||||
|
||||
static tor_x509_cert_impl_t *
|
||||
tor_tls_create_certificate_internal(crypto_pk_t *rsa,
|
||||
crypto_pk_t *rsa_sign,
|
||||
CERTName *subject_dn,
|
||||
CERTName *issuer_dn,
|
||||
time_t start_time,
|
||||
time_t end_time)
|
||||
{
|
||||
if (! crypto_pk_key_is_private(rsa_sign)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const SECKEYPublicKey *subject_key = crypto_pk_get_nss_pubkey(rsa);
|
||||
const SECKEYPrivateKey *signing_key = crypto_pk_get_nss_privkey(rsa_sign);
|
||||
SECStatus s;
|
||||
|
||||
CERTSubjectPublicKeyInfo *subject_spki = NULL;
|
||||
CERTCertificateRequest *request = NULL;
|
||||
CERTValidity *validity = NULL;
|
||||
CERTCertificate *cert = NULL;
|
||||
SECItem der = { .data = NULL, .len = 0 };
|
||||
SECItem signed_der = { .data = NULL, .len = 0 };
|
||||
|
||||
CERTCertificate *result_cert = NULL;
|
||||
|
||||
validity = CERT_CreateValidity(((PRTime)start_time) * PRTIME_PER_SEC,
|
||||
((PRTime)end_time) * PRTIME_PER_SEC);
|
||||
if (BUG(! validity)) {
|
||||
/* LCOV_EXCL_START */
|
||||
crypto_nss_log_errors(LOG_WARN, "creating a validity object");
|
||||
goto err;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
unsigned long serial_number;
|
||||
crypto_rand((char*)&serial_number, sizeof(serial_number));
|
||||
|
||||
subject_spki = SECKEY_CreateSubjectPublicKeyInfo(subject_key);
|
||||
if (!subject_spki)
|
||||
goto err;
|
||||
|
||||
/* Make a CSR ... */
|
||||
// XXX do we need to set any attributes?
|
||||
request = CERT_CreateCertificateRequest(subject_dn,
|
||||
subject_spki,
|
||||
NULL /* attributes */);
|
||||
if (!request)
|
||||
goto err;
|
||||
|
||||
/* Put it into a certificate ... */
|
||||
cert = CERT_CreateCertificate(serial_number,
|
||||
issuer_dn,
|
||||
validity,
|
||||
request);
|
||||
if (!cert)
|
||||
goto err;
|
||||
|
||||
/* version 3 cert */
|
||||
*cert->version.data = 2; /* 2 means version 3. */
|
||||
cert->version.len = 1;
|
||||
|
||||
// XXX do we need to set anything else on the cert?
|
||||
|
||||
/* Sign it. */
|
||||
KeyType privkey_type = SECKEY_GetPrivateKeyType(signing_key);
|
||||
SECOidTag oid_tag = SEC_GetSignatureAlgorithmOidTag(privkey_type,
|
||||
SEC_OID_SHA256);
|
||||
if (oid_tag == SEC_OID_UNKNOWN)
|
||||
goto err;
|
||||
s = SECOID_SetAlgorithmID(cert->arena, &cert->signature, oid_tag, NULL);
|
||||
if (s != SECSuccess)
|
||||
goto err;
|
||||
|
||||
void *tmp;
|
||||
tmp = SEC_ASN1EncodeItem(cert->arena, &der, cert,
|
||||
SEC_ASN1_GET(CERT_CertificateTemplate));
|
||||
if (!tmp)
|
||||
goto err;
|
||||
|
||||
s = SEC_DerSignDataWithAlgorithmID(cert->arena,
|
||||
&signed_der,
|
||||
der.data, der.len,
|
||||
(SECKEYPrivateKey *)signing_key,//const
|
||||
&cert->signature);
|
||||
|
||||
if (s != SECSuccess)
|
||||
goto err;
|
||||
|
||||
/* Re-parse it, to make sure all the certificates we actually use
|
||||
* appear via being decoded. */
|
||||
result_cert = tor_x509_cert_decode_internal(signed_der.data, signed_der.len);
|
||||
|
||||
#if 1
|
||||
{
|
||||
// Can we check the cert we just signed?
|
||||
tor_assert(result_cert);
|
||||
SECKEYPublicKey *issuer_pk = (SECKEYPublicKey *)
|
||||
crypto_pk_get_nss_pubkey(rsa_sign);
|
||||
SECStatus cert_ok = CERT_VerifySignedDataWithPublicKey(
|
||||
&result_cert->signatureWrap, issuer_pk, NULL);
|
||||
tor_assert(cert_ok == SECSuccess);
|
||||
}
|
||||
#endif
|
||||
|
||||
err:
|
||||
if (subject_spki)
|
||||
SECKEY_DestroySubjectPublicKeyInfo(subject_spki);
|
||||
if (request)
|
||||
CERT_DestroyCertificateRequest(request);
|
||||
if (validity)
|
||||
CERT_DestroyValidity(validity);
|
||||
|
||||
// unnecessary, since these are allocated in the cert's arena.
|
||||
//SECITEM_FreeItem(&der, PR_FALSE);
|
||||
//SECITEM_FreeItem(&signed_der, PR_FALSE);
|
||||
if (cert)
|
||||
CERT_DestroyCertificate(cert);
|
||||
|
||||
return result_cert;
|
||||
}
|
||||
|
||||
MOCK_IMPL(tor_x509_cert_impl_t *,
|
||||
tor_tls_create_certificate,(crypto_pk_t *rsa,
|
||||
crypto_pk_t *rsa_sign,
|
||||
const char *cname,
|
||||
const char *cname_sign,
|
||||
unsigned int cert_lifetime))
|
||||
{
|
||||
tor_assert(rsa);
|
||||
tor_assert(rsa_sign);
|
||||
tor_assert(cname);
|
||||
tor_assert(cname_sign);
|
||||
|
||||
char *cname_rfc_1485 = NULL, *cname_sign_rfc_1485 = NULL;
|
||||
CERTName *subject_dn = NULL, *issuer_dn = NULL;
|
||||
time_t start_time;
|
||||
time_t end_time;
|
||||
CERTCertificate *result = NULL;
|
||||
|
||||
tor_asprintf(&cname_rfc_1485, "CN=%s", cname);
|
||||
tor_asprintf(&cname_sign_rfc_1485, "CN=%s", cname_sign);
|
||||
|
||||
subject_dn = CERT_AsciiToName(cname_rfc_1485);
|
||||
issuer_dn = CERT_AsciiToName(cname_sign_rfc_1485);
|
||||
if (!subject_dn || !issuer_dn)
|
||||
goto err;
|
||||
|
||||
tor_tls_pick_certificate_lifetime(time(NULL), cert_lifetime,
|
||||
&start_time, &end_time);
|
||||
|
||||
result = tor_tls_create_certificate_internal(rsa,
|
||||
rsa_sign,
|
||||
subject_dn,
|
||||
issuer_dn,
|
||||
start_time,
|
||||
end_time);
|
||||
err:
|
||||
tor_free(cname_rfc_1485);
|
||||
tor_free(cname_sign_rfc_1485);
|
||||
if (subject_dn)
|
||||
CERT_DestroyName(subject_dn);
|
||||
if (issuer_dn)
|
||||
CERT_DestroyName(issuer_dn);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Set *<b>encoded_out</b> and *<b>size_out</b> to <b>cert</b>'s encoded DER
|
||||
* representation and length, respectively. */
|
||||
void
|
||||
tor_x509_cert_get_der(const tor_x509_cert_t *cert,
|
||||
const uint8_t **encoded_out, size_t *size_out)
|
||||
{
|
||||
tor_assert(cert);
|
||||
tor_assert(cert->cert);
|
||||
tor_assert(encoded_out);
|
||||
tor_assert(size_out);
|
||||
|
||||
const SECItem *item = &cert->cert->derCert;
|
||||
*encoded_out = item->data;
|
||||
*size_out = (size_t)item->len;
|
||||
}
|
||||
|
||||
void
|
||||
tor_x509_cert_impl_free_(tor_x509_cert_impl_t *cert)
|
||||
{
|
||||
if (cert)
|
||||
CERT_DestroyCertificate(cert);
|
||||
}
|
||||
|
||||
tor_x509_cert_impl_t *
|
||||
tor_x509_cert_impl_dup_(tor_x509_cert_impl_t *cert)
|
||||
{
|
||||
if (cert)
|
||||
return CERT_DupCertificate(cert);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* As tor_x509_cert_decode, but return the NSS certificate type
|
||||
*/
|
||||
static tor_x509_cert_impl_t *
|
||||
tor_x509_cert_decode_internal(const uint8_t *certificate,
|
||||
int certificate_len)
|
||||
{
|
||||
tor_assert(certificate);
|
||||
if (certificate_len > INT_MAX)
|
||||
return NULL;
|
||||
|
||||
SECItem der = { .type = siBuffer,
|
||||
.data = (unsigned char *)certificate,
|
||||
.len = certificate_len };
|
||||
CERTCertDBHandle *certdb = CERT_GetDefaultCertDB();
|
||||
tor_assert(certdb);
|
||||
return CERT_NewTempCertificate(certdb,
|
||||
&der,
|
||||
NULL /* nickname */,
|
||||
PR_FALSE, /* isPerm */
|
||||
PR_TRUE /* CopyDER */);
|
||||
}
|
||||
|
||||
tor_x509_cert_t *
|
||||
tor_x509_cert_decode(const uint8_t *certificate,
|
||||
size_t certificate_len)
|
||||
{
|
||||
CERTCertificate *cert = tor_x509_cert_decode_internal(certificate,
|
||||
(int)certificate_len);
|
||||
if (! cert) {
|
||||
crypto_nss_log_errors(LOG_INFO, "decoding certificate");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tor_x509_cert_t *newcert = tor_x509_cert_new(cert);
|
||||
|
||||
return newcert;
|
||||
}
|
||||
|
||||
crypto_pk_t *
|
||||
tor_tls_cert_get_key(tor_x509_cert_t *cert)
|
||||
{
|
||||
tor_assert(cert);
|
||||
CERTSubjectPublicKeyInfo *spki = &cert->cert->subjectPublicKeyInfo;
|
||||
SECKEYPublicKey *pub = SECKEY_ExtractPublicKey(spki); // we own this pointer
|
||||
if (pub == NULL)
|
||||
return NULL;
|
||||
|
||||
if (SECKEY_GetPublicKeyType(pub) != rsaKey) {
|
||||
SECKEY_DestroyPublicKey(pub);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return crypto_pk_new_from_nss_pubkey(pub);
|
||||
}
|
||||
|
||||
int
|
||||
tor_tls_cert_is_valid(int severity,
|
||||
const tor_x509_cert_t *cert,
|
||||
const tor_x509_cert_t *signing_cert,
|
||||
time_t now,
|
||||
int check_rsa_1024)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
tor_assert(cert);
|
||||
tor_assert(signing_cert);
|
||||
|
||||
SECKEYPublicKey *pk = CERT_ExtractPublicKey(signing_cert->cert);
|
||||
if (pk == NULL) {
|
||||
log_fn(severity, LD_CRYPTO,
|
||||
"Invalid certificate: could not extract issuer key");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
SECStatus s = CERT_VerifySignedDataWithPublicKey(&cert->cert->signatureWrap,
|
||||
pk, NULL);
|
||||
if (s != SECSuccess) {
|
||||
log_fn(severity, LD_CRYPTO,
|
||||
"Invalid certificate: could not validate signature.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (tor_x509_check_cert_lifetime_internal(severity,
|
||||
cert->cert,
|
||||
now,
|
||||
TOR_X509_PAST_SLOP,
|
||||
TOR_X509_FUTURE_SLOP) < 0)
|
||||
goto fail;
|
||||
|
||||
if (check_rsa_1024) {
|
||||
/* We require that this is a 1024-bit RSA key, for legacy reasons .:p */
|
||||
if (SECKEY_GetPublicKeyType(pk) != rsaKey ||
|
||||
SECKEY_PublicKeyStrengthInBits(pk) != 1024) {
|
||||
log_fn(severity, LD_CRYPTO, "Invalid certificate: Key is not RSA1024.");
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
/* We require that this key is at least minimally strong. */
|
||||
unsigned min_bits = (SECKEY_GetPublicKeyType(pk) == ecKey) ? 128: 1024;
|
||||
if (SECKEY_PublicKeyStrengthInBits(pk) < min_bits) {
|
||||
log_fn(severity, LD_CRYPTO, "Invalid certificate: Key is too weak.");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* The certificate is valid. */
|
||||
result = 1;
|
||||
|
||||
fail:
|
||||
if (pk)
|
||||
SECKEY_DestroyPublicKey(pk);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
log_cert_lifetime(int severity,
|
||||
const char *status,
|
||||
time_t now,
|
||||
PRTime notBefore,
|
||||
PRTime notAfter)
|
||||
{
|
||||
log_fn(severity, LD_GENERAL,
|
||||
"Certificate %s. Either their clock is set wrong, or your clock "
|
||||
"is incorrect.", status);
|
||||
|
||||
char nowbuf[ISO_TIME_LEN+1];
|
||||
char nbbuf[ISO_TIME_LEN+1];
|
||||
char nabuf[ISO_TIME_LEN+1];
|
||||
|
||||
format_iso_time(nowbuf, now);
|
||||
format_iso_time(nbbuf, notBefore / PRTIME_PER_SEC);
|
||||
format_iso_time(nabuf, notAfter / PRTIME_PER_SEC);
|
||||
|
||||
log_fn(severity, LD_GENERAL,
|
||||
"(The certificate is valid from %s until %s. Your time is %s.)",
|
||||
nbbuf, nabuf, nowbuf);
|
||||
}
|
||||
|
||||
int
|
||||
tor_x509_check_cert_lifetime_internal(int severity,
|
||||
const tor_x509_cert_impl_t *cert,
|
||||
time_t now,
|
||||
int past_tolerance,
|
||||
int future_tolerance)
|
||||
{
|
||||
tor_assert(cert);
|
||||
|
||||
PRTime notBefore=0, notAfter=0;
|
||||
int64_t t;
|
||||
SECStatus r = CERT_GetCertTimes(cert, ¬Before, ¬After);
|
||||
if (r != SECSuccess) {
|
||||
log_fn(severity, LD_CRYPTO,
|
||||
"Couldn't get validity times from certificate");
|
||||
return -1;
|
||||
}
|
||||
|
||||
t = ((int64_t)now) + future_tolerance;
|
||||
t *= PRTIME_PER_SEC;
|
||||
if (notBefore > t) {
|
||||
log_cert_lifetime(severity, "not yet valid", now,
|
||||
notBefore, notAfter);
|
||||
return -1;
|
||||
}
|
||||
|
||||
t = ((int64_t)now) - past_tolerance;
|
||||
t *= PRTIME_PER_SEC;
|
||||
if (notAfter < t) {
|
||||
log_cert_lifetime(severity, "already expired", now,
|
||||
notBefore, notAfter);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef TOR_UNIT_TESTS
|
||||
tor_x509_cert_t *
|
||||
tor_x509_cert_replace_expiration(const tor_x509_cert_t *inp,
|
||||
time_t new_expiration_time,
|
||||
crypto_pk_t *signing_key)
|
||||
{
|
||||
tor_assert(inp);
|
||||
tor_assert(signing_key);
|
||||
|
||||
PRTime notBefore=0, notAfter=0;
|
||||
SECStatus r = CERT_GetCertTimes(inp->cert, ¬Before, ¬After);
|
||||
if (r != SECSuccess)
|
||||
return NULL;
|
||||
|
||||
time_t start_time = notBefore / PRTIME_PER_SEC;
|
||||
if (new_expiration_time < start_time) {
|
||||
/* This prevents an NSS error. */
|
||||
start_time = new_expiration_time - 10;
|
||||
}
|
||||
|
||||
crypto_pk_t *subject_key = tor_tls_cert_get_key((tor_x509_cert_t *)inp);
|
||||
if (!subject_key)
|
||||
return NULL;
|
||||
|
||||
CERTCertificate *newcert;
|
||||
|
||||
newcert = tor_tls_create_certificate_internal(subject_key,
|
||||
signing_key,
|
||||
&inp->cert->subject,
|
||||
&inp->cert->issuer,
|
||||
start_time,
|
||||
new_expiration_time);
|
||||
|
||||
crypto_pk_free(subject_key);
|
||||
|
||||
return newcert ? tor_x509_cert_new(newcert) : NULL;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,461 @@
|
||||
/* Copyright (c) 2003, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file x509_openssl.c
|
||||
* \brief Wrapper functions to present a consistent interface to
|
||||
* X.509 functions from OpenSSL.
|
||||
**/
|
||||
|
||||
#define TOR_X509_PRIVATE
|
||||
#include "lib/tls/x509.h"
|
||||
#include "lib/tls/x509_internal.h"
|
||||
#include "lib/tls/tortls.h"
|
||||
#include "lib/crypt_ops/crypto_rand.h"
|
||||
#include "lib/crypt_ops/crypto_util.h"
|
||||
#include "lib/crypt_ops/compat_openssl.h"
|
||||
|
||||
/* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in
|
||||
* srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */
|
||||
DISABLE_GCC_WARNING(redundant-decls)
|
||||
|
||||
#include <openssl/opensslv.h>
|
||||
|
||||
#ifdef OPENSSL_NO_EC
|
||||
#error "We require OpenSSL with ECC support"
|
||||
#endif
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/asn1.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/rsa.h>
|
||||
|
||||
ENABLE_GCC_WARNING(redundant-decls)
|
||||
|
||||
#include "lib/log/log.h"
|
||||
#include "lib/log/util_bug.h"
|
||||
#include "lib/ctime/di_ops.h"
|
||||
#include "lib/encoding/time_fmt.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef OPENSSL_1_1_API
|
||||
#define X509_get_notBefore_const(cert) \
|
||||
X509_get0_notBefore(cert)
|
||||
#define X509_get_notAfter_const(cert) \
|
||||
X509_get0_notAfter(cert)
|
||||
#ifndef X509_get_notBefore
|
||||
#define X509_get_notBefore(cert) \
|
||||
X509_getm_notBefore(cert)
|
||||
#endif
|
||||
#ifndef X509_get_notAfter
|
||||
#define X509_get_notAfter(cert) \
|
||||
X509_getm_notAfter(cert)
|
||||
#endif
|
||||
#else /* ! OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) */
|
||||
#define X509_get_notBefore_const(cert) \
|
||||
((const ASN1_TIME*) X509_get_notBefore((X509 *)cert))
|
||||
#define X509_get_notAfter_const(cert) \
|
||||
((const ASN1_TIME*) X509_get_notAfter((X509 *)cert))
|
||||
#endif
|
||||
|
||||
/** Return a newly allocated X509 name with commonName <b>cname</b>. */
|
||||
static X509_NAME *
|
||||
tor_x509_name_new(const char *cname)
|
||||
{
|
||||
int nid;
|
||||
X509_NAME *name;
|
||||
/* LCOV_EXCL_BR_START : these branches will only fail on OOM errors */
|
||||
if (!(name = X509_NAME_new()))
|
||||
return NULL;
|
||||
if ((nid = OBJ_txt2nid("commonName")) == NID_undef) goto error;
|
||||
if (!(X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC,
|
||||
(unsigned char*)cname, -1, -1, 0)))
|
||||
goto error;
|
||||
/* LCOV_EXCL_BR_STOP */
|
||||
return name;
|
||||
|
||||
/* LCOV_EXCL_START : these lines will only execute on out of memory errors*/
|
||||
error:
|
||||
X509_NAME_free(name);
|
||||
return NULL;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/** Generate and sign an X509 certificate with the public key <b>rsa</b>,
|
||||
* signed by the private key <b>rsa_sign</b>. The commonName of the
|
||||
* certificate will be <b>cname</b>; the commonName of the issuer will be
|
||||
* <b>cname_sign</b>. The cert will be valid for <b>cert_lifetime</b>
|
||||
* seconds, starting from some time in the past.
|
||||
*
|
||||
* Return a certificate on success, NULL on failure.
|
||||
*/
|
||||
MOCK_IMPL(X509 *,
|
||||
tor_tls_create_certificate,(crypto_pk_t *rsa,
|
||||
crypto_pk_t *rsa_sign,
|
||||
const char *cname,
|
||||
const char *cname_sign,
|
||||
unsigned int cert_lifetime))
|
||||
{
|
||||
/* OpenSSL generates self-signed certificates with random 64-bit serial
|
||||
* numbers, so let's do that too. */
|
||||
#define SERIAL_NUMBER_SIZE 8
|
||||
|
||||
time_t start_time, end_time;
|
||||
BIGNUM *serial_number = NULL;
|
||||
unsigned char serial_tmp[SERIAL_NUMBER_SIZE];
|
||||
EVP_PKEY *sign_pkey = NULL, *pkey=NULL;
|
||||
X509 *x509 = NULL;
|
||||
X509_NAME *name = NULL, *name_issuer=NULL;
|
||||
|
||||
tor_tls_init();
|
||||
|
||||
time_t now = time(NULL);
|
||||
|
||||
tor_tls_pick_certificate_lifetime(now, cert_lifetime,
|
||||
&start_time, &end_time);
|
||||
|
||||
tor_assert(rsa);
|
||||
tor_assert(cname);
|
||||
tor_assert(rsa_sign);
|
||||
tor_assert(cname_sign);
|
||||
if (!(sign_pkey = crypto_pk_get_openssl_evp_pkey_(rsa_sign,1)))
|
||||
goto error;
|
||||
if (!(pkey = crypto_pk_get_openssl_evp_pkey_(rsa,0)))
|
||||
goto error;
|
||||
if (!(x509 = X509_new()))
|
||||
goto error;
|
||||
if (!(X509_set_version(x509, 2)))
|
||||
goto error;
|
||||
|
||||
{ /* our serial number is 8 random bytes. */
|
||||
crypto_rand((char *)serial_tmp, sizeof(serial_tmp));
|
||||
if (!(serial_number = BN_bin2bn(serial_tmp, sizeof(serial_tmp), NULL)))
|
||||
goto error;
|
||||
if (!(BN_to_ASN1_INTEGER(serial_number, X509_get_serialNumber(x509))))
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!(name = tor_x509_name_new(cname)))
|
||||
goto error;
|
||||
if (!(X509_set_subject_name(x509, name)))
|
||||
goto error;
|
||||
if (!(name_issuer = tor_x509_name_new(cname_sign)))
|
||||
goto error;
|
||||
if (!(X509_set_issuer_name(x509, name_issuer)))
|
||||
goto error;
|
||||
|
||||
if (!X509_time_adj(X509_get_notBefore(x509),0,&start_time))
|
||||
goto error;
|
||||
if (!X509_time_adj(X509_get_notAfter(x509),0,&end_time))
|
||||
goto error;
|
||||
if (!X509_set_pubkey(x509, pkey))
|
||||
goto error;
|
||||
|
||||
if (!X509_sign(x509, sign_pkey, EVP_sha256()))
|
||||
goto error;
|
||||
|
||||
goto done;
|
||||
error:
|
||||
if (x509) {
|
||||
X509_free(x509);
|
||||
x509 = NULL;
|
||||
}
|
||||
done:
|
||||
tls_log_errors(NULL, LOG_WARN, LD_NET, "generating certificate");
|
||||
if (sign_pkey)
|
||||
EVP_PKEY_free(sign_pkey);
|
||||
if (pkey)
|
||||
EVP_PKEY_free(pkey);
|
||||
if (serial_number)
|
||||
BN_clear_free(serial_number);
|
||||
if (name)
|
||||
X509_NAME_free(name);
|
||||
if (name_issuer)
|
||||
X509_NAME_free(name_issuer);
|
||||
return x509;
|
||||
|
||||
#undef SERIAL_NUMBER_SIZE
|
||||
}
|
||||
|
||||
/** Set the 'encoded' and 'encoded_len' fields of "cert" from cert->cert. */
|
||||
int
|
||||
tor_x509_cert_set_cached_der_encoding(tor_x509_cert_t *cert)
|
||||
{
|
||||
unsigned char *buf = NULL;
|
||||
int length = i2d_X509(cert->cert, &buf);
|
||||
|
||||
if (length <= 0 || buf == NULL) {
|
||||
return -1;
|
||||
}
|
||||
cert->encoded_len = (size_t) length;
|
||||
cert->encoded = tor_malloc(length);
|
||||
memcpy(cert->encoded, buf, length);
|
||||
OPENSSL_free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
tor_x509_cert_impl_free_(tor_x509_cert_impl_t *cert)
|
||||
{
|
||||
if (cert)
|
||||
X509_free(cert);
|
||||
}
|
||||
|
||||
tor_x509_cert_impl_t *
|
||||
tor_x509_cert_impl_dup_(tor_x509_cert_impl_t *cert)
|
||||
{
|
||||
if (cert)
|
||||
return X509_dup(cert);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** Set *<b>encoded_out</b> and *<b>size_out</b> to <b>cert</b>'s encoded DER
|
||||
* representation and length, respectively. */
|
||||
void
|
||||
tor_x509_cert_get_der(const tor_x509_cert_t *cert,
|
||||
const uint8_t **encoded_out, size_t *size_out)
|
||||
{
|
||||
tor_assert(cert);
|
||||
tor_assert(encoded_out);
|
||||
tor_assert(size_out);
|
||||
*encoded_out = cert->encoded;
|
||||
*size_out = cert->encoded_len;
|
||||
}
|
||||
|
||||
/** Read a DER-encoded X509 cert, of length exactly <b>certificate_len</b>,
|
||||
* from a <b>certificate</b>. Return a newly allocated tor_x509_cert_t on
|
||||
* success and NULL on failure. */
|
||||
tor_x509_cert_t *
|
||||
tor_x509_cert_decode(const uint8_t *certificate, size_t certificate_len)
|
||||
{
|
||||
X509 *x509;
|
||||
const unsigned char *cp = (const unsigned char *)certificate;
|
||||
tor_x509_cert_t *newcert;
|
||||
tor_assert(certificate);
|
||||
check_no_tls_errors();
|
||||
|
||||
if (certificate_len > INT_MAX)
|
||||
goto err;
|
||||
|
||||
x509 = d2i_X509(NULL, &cp, (int)certificate_len);
|
||||
|
||||
if (!x509)
|
||||
goto err; /* Couldn't decode */
|
||||
if (cp - certificate != (int)certificate_len) {
|
||||
X509_free(x509);
|
||||
goto err; /* Didn't use all the bytes */
|
||||
}
|
||||
newcert = tor_x509_cert_new(x509);
|
||||
if (!newcert) {
|
||||
goto err;
|
||||
}
|
||||
if (newcert->encoded_len != certificate_len ||
|
||||
fast_memneq(newcert->encoded, certificate, certificate_len)) {
|
||||
/* Cert wasn't in DER */
|
||||
tor_x509_cert_free(newcert);
|
||||
goto err;
|
||||
}
|
||||
return newcert;
|
||||
err:
|
||||
tls_log_errors(NULL, LOG_INFO, LD_CRYPTO, "decoding a certificate");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a newly allocated copy of the public key that a certificate
|
||||
* certifies. Watch out! This returns NULL if the cert's key is not RSA.
|
||||
*/
|
||||
crypto_pk_t *
|
||||
tor_tls_cert_get_key(tor_x509_cert_t *cert)
|
||||
{
|
||||
crypto_pk_t *result = NULL;
|
||||
EVP_PKEY *pkey = X509_get_pubkey(cert->cert);
|
||||
RSA *rsa;
|
||||
if (!pkey)
|
||||
return NULL;
|
||||
rsa = EVP_PKEY_get1_RSA(pkey);
|
||||
if (!rsa) {
|
||||
EVP_PKEY_free(pkey);
|
||||
return NULL;
|
||||
}
|
||||
result = crypto_new_pk_from_openssl_rsa_(rsa);
|
||||
EVP_PKEY_free(pkey);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Check whether <b>cert</b> is well-formed, currently live, and correctly
|
||||
* signed by the public key in <b>signing_cert</b>. If <b>check_rsa_1024</b>,
|
||||
* make sure that it has an RSA key with 1024 bits; otherwise, just check that
|
||||
* the key is long enough. Return 1 if the cert is good, and 0 if it's bad or
|
||||
* we couldn't check it. */
|
||||
int
|
||||
tor_tls_cert_is_valid(int severity,
|
||||
const tor_x509_cert_t *cert,
|
||||
const tor_x509_cert_t *signing_cert,
|
||||
time_t now,
|
||||
int check_rsa_1024)
|
||||
{
|
||||
check_no_tls_errors();
|
||||
EVP_PKEY *cert_key;
|
||||
int r, key_ok = 0;
|
||||
|
||||
if (!signing_cert || !cert)
|
||||
goto bad;
|
||||
|
||||
EVP_PKEY *signing_key = X509_get_pubkey(signing_cert->cert);
|
||||
if (!signing_key)
|
||||
goto bad;
|
||||
r = X509_verify(cert->cert, signing_key);
|
||||
EVP_PKEY_free(signing_key);
|
||||
if (r <= 0)
|
||||
goto bad;
|
||||
|
||||
/* okay, the signature checked out right. Now let's check the check the
|
||||
* lifetime. */
|
||||
if (tor_x509_check_cert_lifetime_internal(severity, cert->cert, now,
|
||||
TOR_X509_PAST_SLOP,
|
||||
TOR_X509_FUTURE_SLOP) < 0)
|
||||
goto bad;
|
||||
|
||||
cert_key = X509_get_pubkey(cert->cert);
|
||||
if (check_rsa_1024 && cert_key) {
|
||||
RSA *rsa = EVP_PKEY_get1_RSA(cert_key);
|
||||
#ifdef OPENSSL_1_1_API
|
||||
if (rsa && RSA_bits(rsa) == 1024) {
|
||||
#else
|
||||
if (rsa && BN_num_bits(rsa->n) == 1024) {
|
||||
#endif
|
||||
key_ok = 1;
|
||||
} else {
|
||||
log_fn(severity, LD_CRYPTO, "Invalid certificate: Key is not RSA1024.");
|
||||
}
|
||||
|
||||
if (rsa)
|
||||
RSA_free(rsa);
|
||||
} else if (cert_key) {
|
||||
int min_bits = 1024;
|
||||
#ifdef EVP_PKEY_EC
|
||||
if (EVP_PKEY_base_id(cert_key) == EVP_PKEY_EC)
|
||||
min_bits = 128;
|
||||
#endif
|
||||
if (EVP_PKEY_bits(cert_key) >= min_bits)
|
||||
key_ok = 1;
|
||||
}
|
||||
EVP_PKEY_free(cert_key);
|
||||
if (!key_ok)
|
||||
goto bad;
|
||||
|
||||
/* XXXX compare DNs or anything? */
|
||||
|
||||
return 1;
|
||||
bad:
|
||||
tls_log_errors(NULL, LOG_INFO, LD_CRYPTO, "checking a certificate");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Warn that a certificate lifetime extends through a certain range. */
|
||||
static void
|
||||
log_cert_lifetime(int severity, const X509 *cert, const char *problem,
|
||||
time_t now)
|
||||
{
|
||||
BIO *bio = NULL;
|
||||
BUF_MEM *buf;
|
||||
char *s1=NULL, *s2=NULL;
|
||||
char mytime[33];
|
||||
struct tm tm;
|
||||
size_t n;
|
||||
|
||||
if (problem)
|
||||
tor_log(severity, LD_GENERAL,
|
||||
"Certificate %s. Either their clock is set wrong, or your clock "
|
||||
"is wrong.",
|
||||
problem);
|
||||
|
||||
if (!(bio = BIO_new(BIO_s_mem()))) {
|
||||
log_warn(LD_GENERAL, "Couldn't allocate BIO!"); goto end;
|
||||
}
|
||||
if (!(ASN1_TIME_print(bio, X509_get_notBefore_const(cert)))) {
|
||||
tls_log_errors(NULL, LOG_WARN, LD_NET, "printing certificate lifetime");
|
||||
goto end;
|
||||
}
|
||||
BIO_get_mem_ptr(bio, &buf);
|
||||
s1 = tor_strndup(buf->data, buf->length);
|
||||
|
||||
(void)BIO_reset(bio);
|
||||
if (!(ASN1_TIME_print(bio, X509_get_notAfter_const(cert)))) {
|
||||
tls_log_errors(NULL, LOG_WARN, LD_NET, "printing certificate lifetime");
|
||||
goto end;
|
||||
}
|
||||
BIO_get_mem_ptr(bio, &buf);
|
||||
s2 = tor_strndup(buf->data, buf->length);
|
||||
|
||||
n = strftime(mytime, 32, "%b %d %H:%M:%S %Y UTC", tor_gmtime_r(&now, &tm));
|
||||
if (n > 0) {
|
||||
tor_log(severity, LD_GENERAL,
|
||||
"(certificate lifetime runs from %s through %s. Your time is %s.)",
|
||||
s1,s2,mytime);
|
||||
} else {
|
||||
tor_log(severity, LD_GENERAL,
|
||||
"(certificate lifetime runs from %s through %s. "
|
||||
"Couldn't get your time.)",
|
||||
s1, s2);
|
||||
}
|
||||
|
||||
end:
|
||||
/* Not expected to get invoked */
|
||||
tls_log_errors(NULL, LOG_WARN, LD_NET, "getting certificate lifetime");
|
||||
if (bio)
|
||||
BIO_free(bio);
|
||||
tor_free(s1);
|
||||
tor_free(s2);
|
||||
}
|
||||
|
||||
/** Helper: check whether <b>cert</b> is expired give or take
|
||||
* <b>past_tolerance</b> seconds, or not-yet-valid give or take
|
||||
* <b>future_tolerance</b> seconds. (Relative to the current time
|
||||
* <b>now</b>.) If it is live, return 0. If it is not live, log a message
|
||||
* and return -1. */
|
||||
int
|
||||
tor_x509_check_cert_lifetime_internal(int severity, const X509 *cert,
|
||||
time_t now,
|
||||
int past_tolerance, int future_tolerance)
|
||||
{
|
||||
time_t t;
|
||||
|
||||
t = now + future_tolerance;
|
||||
if (X509_cmp_time(X509_get_notBefore_const(cert), &t) > 0) {
|
||||
log_cert_lifetime(severity, cert, "not yet valid", now);
|
||||
return -1;
|
||||
}
|
||||
t = now - past_tolerance;
|
||||
if (X509_cmp_time(X509_get_notAfter_const(cert), &t) < 0) {
|
||||
log_cert_lifetime(severity, cert, "already expired", now);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef TOR_UNIT_TESTS
|
||||
/* Testing only: return a new x509 cert with the same contents as <b>inp</b>,
|
||||
but with the expiration time <b>new_expiration_time</b>, signed with
|
||||
<b>signing_key</b>. */
|
||||
STATIC tor_x509_cert_t *
|
||||
tor_x509_cert_replace_expiration(const tor_x509_cert_t *inp,
|
||||
time_t new_expiration_time,
|
||||
crypto_pk_t *signing_key)
|
||||
{
|
||||
X509 *newc = X509_dup(inp->cert);
|
||||
X509_time_adj(X509_get_notAfter(newc), 0, &new_expiration_time);
|
||||
EVP_PKEY *pk = crypto_pk_get_openssl_evp_pkey_(signing_key, 1);
|
||||
tor_assert(X509_sign(newc, pk, EVP_sha256()));
|
||||
EVP_PKEY_free(pk);
|
||||
return tor_x509_cert_new(newc);
|
||||
}
|
||||
#endif /* defined(TOR_UNIT_TESTS) */
|
||||
@@ -172,6 +172,7 @@ pub fn main() {
|
||||
|
||||
cfg.from_cflags("TOR_ZLIB_LIBS");
|
||||
cfg.from_cflags("TOR_LIB_MATH");
|
||||
cfg.from_cflags("NSS_LIBS");
|
||||
cfg.from_cflags("TOR_OPENSSL_LIBS");
|
||||
cfg.from_cflags("TOR_LIBEVENT_LIBS");
|
||||
cfg.from_cflags("TOR_LIB_WS32");
|
||||
|
||||
+11
-3
@@ -13,11 +13,14 @@
|
||||
#include "core/or/or.h"
|
||||
#include "core/crypto/onion_tap.h"
|
||||
#include "core/crypto/relay_crypto.h"
|
||||
|
||||
#ifdef ENABLE_OPENSSL
|
||||
#include <openssl/opensslv.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/ecdh.h>
|
||||
#include <openssl/obj_mac.h>
|
||||
#endif
|
||||
|
||||
#include "core/or/circuitlist.h"
|
||||
#include "app/config/config.h"
|
||||
@@ -33,6 +36,7 @@
|
||||
#include "core/or/or_circuit_st.h"
|
||||
|
||||
#include "lib/crypt_ops/digestset.h"
|
||||
#include "lib/crypt_ops/crypto_init.h"
|
||||
|
||||
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID)
|
||||
static uint64_t nanostart;
|
||||
@@ -579,6 +583,7 @@ bench_dh(void)
|
||||
" %f millisec each.\n", NANOCOUNT(start, end, iters)/1e6);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_OPENSSL
|
||||
static void
|
||||
bench_ecdh_impl(int nid, const char *name)
|
||||
{
|
||||
@@ -628,6 +633,7 @@ bench_ecdh_p224(void)
|
||||
{
|
||||
bench_ecdh_impl(NID_secp224r1, "P-224");
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef void (*bench_fn)(void);
|
||||
|
||||
@@ -651,8 +657,11 @@ static struct benchmark_t benchmarks[] = {
|
||||
ENT(cell_aes),
|
||||
ENT(cell_ops),
|
||||
ENT(dh),
|
||||
|
||||
#ifdef ENABLE_OPENSSL
|
||||
ENT(ecdh_p256),
|
||||
ENT(ecdh_p224),
|
||||
#endif
|
||||
{NULL,NULL,0}
|
||||
};
|
||||
|
||||
@@ -680,9 +689,9 @@ main(int argc, const char **argv)
|
||||
|
||||
tor_threads_init();
|
||||
tor_compress_init();
|
||||
init_logging(1);
|
||||
|
||||
if (argc == 4 && !strcmp(argv[1], "diff")) {
|
||||
init_logging(1);
|
||||
const int N = 200;
|
||||
char *f1 = read_file_to_str(argv[2], RFTS_BIN, NULL);
|
||||
char *f2 = read_file_to_str(argv[3], RFTS_BIN, NULL);
|
||||
@@ -718,13 +727,12 @@ main(int argc, const char **argv)
|
||||
|
||||
reset_perftime();
|
||||
|
||||
if (crypto_seed_rng() < 0) {
|
||||
if (crypto_global_init(0, NULL, NULL) < 0) {
|
||||
printf("Couldn't seed RNG; exiting.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
init_protocol_warning_severity_level();
|
||||
crypto_init_siphash_key();
|
||||
options = options_new();
|
||||
init_logging(1);
|
||||
options->command = CMD_RUN_UNITTESTS;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -5,12 +5,12 @@ FUZZING_CPPFLAGS = \
|
||||
FUZZING_CFLAGS = \
|
||||
$(AM_CFLAGS) $(TEST_CFLAGS)
|
||||
FUZZING_LDFLAG = \
|
||||
@TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@
|
||||
@TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) @TOR_LDFLAGS_libevent@
|
||||
FUZZING_LIBS = \
|
||||
$(TOR_INTERNAL_TESTING_LIBS) \
|
||||
$(rust_ldadd) \
|
||||
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \
|
||||
@TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
|
||||
@TOR_LIBEVENT_LIBS@ $(TOR_LIBS_CRYPTLIB) \
|
||||
@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ @CURVE25519_LIBS@ \
|
||||
@TOR_SYSTEMD_LIBS@ \
|
||||
@TOR_LZMA_LIBS@ \
|
||||
|
||||
+23
-12
@@ -118,7 +118,6 @@ src_test_test_SOURCES += \
|
||||
src/test/test_controller_events.c \
|
||||
src/test/test_crypto.c \
|
||||
src/test/test_crypto_ope.c \
|
||||
src/test/test_crypto_openssl.c \
|
||||
src/test/test_data.c \
|
||||
src/test/test_dir.c \
|
||||
src/test/test_dir_common.c \
|
||||
@@ -151,6 +150,7 @@ src_test_test_SOURCES += \
|
||||
src/test/test_oom.c \
|
||||
src/test/test_oos.c \
|
||||
src/test/test_options.c \
|
||||
src/test/test_pem.c \
|
||||
src/test/test_periodic_event.c \
|
||||
src/test/test_policy.c \
|
||||
src/test/test_procmon.c \
|
||||
@@ -178,11 +178,21 @@ src_test_test_SOURCES += \
|
||||
src/test/test_util_format.c \
|
||||
src/test/test_util_process.c \
|
||||
src/test/test_voting_schedule.c \
|
||||
src/test/test_x509.c \
|
||||
src/test/test_helpers.c \
|
||||
src/test/test_dns.c \
|
||||
src/test/testing_common.c \
|
||||
src/test/testing_rsakeys.c \
|
||||
src/ext/tinytest.c
|
||||
|
||||
if USE_NSS
|
||||
# ...
|
||||
else
|
||||
src_test_test_SOURCES += \
|
||||
src/test/test_crypto_openssl.c \
|
||||
src/test/test_tortls_openssl.c
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
src_test_test_slow_SOURCES =
|
||||
@@ -226,13 +236,13 @@ src_test_test_switch_id_LDADD = \
|
||||
@TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_USERENV@ \
|
||||
@TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@
|
||||
|
||||
src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
|
||||
src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) \
|
||||
@TOR_LDFLAGS_libevent@
|
||||
src_test_test_LDADD = \
|
||||
$(TOR_INTERNAL_TESTING_LIBS) \
|
||||
$(rust_ldadd) \
|
||||
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
|
||||
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
|
||||
$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
|
||||
@CURVE25519_LIBS@ \
|
||||
@TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@
|
||||
|
||||
@@ -249,23 +259,23 @@ src_test_test_memwipe_LDADD = $(src_test_test_LDADD)
|
||||
# successfully with the libraries built with them.
|
||||
src_test_test_memwipe_LDFLAGS = $(src_test_test_LDFLAGS) @CFLAGS_BUGTRAP@
|
||||
|
||||
src_test_bench_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
|
||||
src_test_bench_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) \
|
||||
@TOR_LDFLAGS_libevent@
|
||||
src_test_bench_LDADD = \
|
||||
$(TOR_INTERNAL_LIBS) \
|
||||
$(rust_ldadd) \
|
||||
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
|
||||
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
|
||||
$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
|
||||
@CURVE25519_LIBS@ \
|
||||
@TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@
|
||||
|
||||
src_test_test_workqueue_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
|
||||
src_test_test_workqueue_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) \
|
||||
@TOR_LDFLAGS_libevent@
|
||||
src_test_test_workqueue_LDADD = \
|
||||
$(TOR_INTERNAL_TESTING_LIBS) \
|
||||
$(rust_ldadd) \
|
||||
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
|
||||
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
|
||||
$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
|
||||
@CURVE25519_LIBS@ \
|
||||
@TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@
|
||||
|
||||
@@ -277,7 +287,7 @@ src_test_test_timers_LDADD = \
|
||||
$(TOR_UTIL_TESTING_LIBS) \
|
||||
$(rust_ldadd) \
|
||||
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
|
||||
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
|
||||
$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
|
||||
@CURVE25519_LIBS@ \
|
||||
@TOR_LZMA_LIBS@
|
||||
src_test_test_timers_LDFLAGS = $(src_test_test_LDFLAGS)
|
||||
@@ -291,6 +301,7 @@ noinst_HEADERS+= \
|
||||
src/test/test_helpers.h \
|
||||
src/test/test_dir_common.h \
|
||||
src/test/test_connection.h \
|
||||
src/test/test_tortls.h \
|
||||
src/test/test_descriptors.inc \
|
||||
src/test/example_extrainfo.inc \
|
||||
src/test/failing_routerdescs.inc \
|
||||
@@ -302,22 +313,22 @@ noinst_HEADERS+= \
|
||||
noinst_PROGRAMS+= src/test/test-ntor-cl
|
||||
noinst_PROGRAMS+= src/test/test-hs-ntor-cl
|
||||
src_test_test_ntor_cl_SOURCES = src/test/test_ntor_cl.c
|
||||
src_test_test_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
|
||||
src_test_test_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB)
|
||||
src_test_test_ntor_cl_LDADD = \
|
||||
$(TOR_INTERNAL_LIBS) \
|
||||
$(rust_ldadd) \
|
||||
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \
|
||||
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
|
||||
$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
|
||||
@CURVE25519_LIBS@ @TOR_LZMA_LIBS@
|
||||
src_test_test_ntor_cl_AM_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS)
|
||||
|
||||
src_test_test_hs_ntor_cl_SOURCES = src/test/test_hs_ntor_cl.c
|
||||
src_test_test_hs_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
|
||||
src_test_test_hs_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB)
|
||||
src_test_test_hs_ntor_cl_LDADD = \
|
||||
$(TOR_INTERNAL_LIBS) \
|
||||
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \
|
||||
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
|
||||
$(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
|
||||
src_test_test_hs_ntor_cl_AM_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS)
|
||||
|
||||
|
||||
+19
-18
@@ -33,7 +33,7 @@ void mock_dump_saved_logs(void);
|
||||
#define assert_log_predicate(predicate, failure_msg) \
|
||||
do { \
|
||||
if (!(predicate)) { \
|
||||
tt_fail_msg((failure_msg)); \
|
||||
TT_FAIL(failure_msg); \
|
||||
mock_dump_saved_logs(); \
|
||||
TT_EXIT_TEST_FUNCTION; \
|
||||
} \
|
||||
@@ -41,74 +41,75 @@ void mock_dump_saved_logs(void);
|
||||
|
||||
#define expect_log_msg(str) \
|
||||
assert_log_predicate(mock_saved_log_has_message(str), \
|
||||
"expected log to contain " # str);
|
||||
("expected log to contain \"%s\"", str));
|
||||
|
||||
#define expect_log_msg_containing(str) \
|
||||
assert_log_predicate(mock_saved_log_has_message_containing(str), \
|
||||
"expected log to contain " # str);
|
||||
("expected log to contain \"%s\"", str));
|
||||
|
||||
#define expect_log_msg_not_containing(str) \
|
||||
assert_log_predicate(mock_saved_log_has_message_not_containing(str), \
|
||||
"expected log to not contain " # str);
|
||||
("expected log to not contain \"%s\"", str));
|
||||
|
||||
#define expect_log_msg_containing_either(str1, str2) \
|
||||
assert_log_predicate(mock_saved_log_has_message_containing(str1) || \
|
||||
mock_saved_log_has_message_containing(str2), \
|
||||
"expected log to contain " # str1 " or " # str2);
|
||||
("expected log to contain \"%s\" or \"%s\"", str1, str2));
|
||||
|
||||
#define expect_log_msg_containing_either3(str1, str2, str3) \
|
||||
assert_log_predicate(mock_saved_log_has_message_containing(str1) || \
|
||||
mock_saved_log_has_message_containing(str2) || \
|
||||
mock_saved_log_has_message_containing(str3), \
|
||||
"expected log to contain " # str1 " or " # str2 \
|
||||
" or " # str3);
|
||||
("expected log to contain \"%s\" or \"%s\" or \"%s\"", \
|
||||
str1, str2, str3))
|
||||
|
||||
#define expect_log_msg_containing_either4(str1, str2, str3, str4) \
|
||||
assert_log_predicate(mock_saved_log_has_message_containing(str1) || \
|
||||
mock_saved_log_has_message_containing(str2) || \
|
||||
mock_saved_log_has_message_containing(str3) || \
|
||||
mock_saved_log_has_message_containing(str4), \
|
||||
"expected log to contain " # str1 " or " # str2 \
|
||||
" or " # str3 " or " # str4);
|
||||
("expected log to contain \"%s\" or \"%s\" or \"%s\" or \"%s\"", \
|
||||
str1, str2, str3, str4))
|
||||
|
||||
#define expect_single_log_msg(str) \
|
||||
do { \
|
||||
\
|
||||
assert_log_predicate(mock_saved_log_has_message_containing(str) && \
|
||||
mock_saved_log_n_entries() == 1, \
|
||||
"expected log to contain exactly 1 message " # str); \
|
||||
("expected log to contain exactly 1 message \"%s\"", \
|
||||
str)); \
|
||||
} while (0);
|
||||
|
||||
#define expect_single_log_msg_containing(str) \
|
||||
do { \
|
||||
assert_log_predicate(mock_saved_log_has_message_containing(str)&& \
|
||||
mock_saved_log_n_entries() == 1 , \
|
||||
"expected log to contain 1 message, containing " # str); \
|
||||
("expected log to contain 1 message, containing \"%s\"",\
|
||||
str)); \
|
||||
} while (0);
|
||||
|
||||
#define expect_no_log_msg(str) \
|
||||
assert_log_predicate(!mock_saved_log_has_message(str), \
|
||||
"expected log to not contain " # str);
|
||||
("expected log to not contain \"%s\"",str));
|
||||
|
||||
#define expect_no_log_msg_containing(str) \
|
||||
assert_log_predicate(!mock_saved_log_has_message_containing(str), \
|
||||
"expected log to not contain " # str);
|
||||
("expected log to not contain \"%s\"", str));
|
||||
|
||||
#define expect_log_severity(severity) \
|
||||
assert_log_predicate(mock_saved_log_has_severity(severity), \
|
||||
"expected log to contain severity " # severity);
|
||||
("expected log to contain severity " # severity));
|
||||
|
||||
#define expect_no_log_severity(severity) \
|
||||
assert_log_predicate(!mock_saved_log_has_severity(severity), \
|
||||
"expected log to not contain severity " # severity);
|
||||
("expected log to not contain severity " # severity));
|
||||
|
||||
#define expect_log_entry() \
|
||||
assert_log_predicate(mock_saved_log_has_entry(), \
|
||||
"expected log to contain entries");
|
||||
("expected log to contain entries"));
|
||||
|
||||
#define expect_no_log_entry() \
|
||||
assert_log_predicate(!mock_saved_log_has_entry(), \
|
||||
"expected log to not contain entries");
|
||||
("expected log to not contain entries"));
|
||||
|
||||
#endif /* !defined(TOR_LOG_TEST_HELPERS_H) */
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -860,7 +860,10 @@ struct testgroup_t testgroups[] = {
|
||||
{ "control/event/", controller_event_tests },
|
||||
{ "crypto/", crypto_tests },
|
||||
{ "crypto/ope/", crypto_ope_tests },
|
||||
#ifdef ENABLE_OPENSSL
|
||||
{ "crypto/openssl/", crypto_openssl_tests },
|
||||
#endif
|
||||
{ "crypto/pem/", pem_tests },
|
||||
{ "dir/", dir_tests },
|
||||
{ "dir_handle_get/", dir_handle_get_tests },
|
||||
{ "dir/md/", microdesc_tests },
|
||||
@@ -912,6 +915,10 @@ struct testgroup_t testgroups[] = {
|
||||
{ "status/" , status_tests },
|
||||
{ "storagedir/", storagedir_tests },
|
||||
{ "tortls/", tortls_tests },
|
||||
#ifndef ENABLE_NSS
|
||||
{ "tortls/openssl/", tortls_openssl_tests },
|
||||
#endif
|
||||
{ "tortls/x509/", x509_tests },
|
||||
{ "util/", util_tests },
|
||||
{ "util/format/", util_format_tests },
|
||||
{ "util/logging/", logging_tests },
|
||||
|
||||
@@ -234,6 +234,7 @@ extern struct testcase_t nodelist_tests[];
|
||||
extern struct testcase_t oom_tests[];
|
||||
extern struct testcase_t oos_tests[];
|
||||
extern struct testcase_t options_tests[];
|
||||
extern struct testcase_t pem_tests[];
|
||||
extern struct testcase_t periodic_event_tests[];
|
||||
extern struct testcase_t policy_tests[];
|
||||
extern struct testcase_t procmon_tests[];
|
||||
@@ -256,6 +257,7 @@ extern struct testcase_t socks_tests[];
|
||||
extern struct testcase_t status_tests[];
|
||||
extern struct testcase_t thread_tests[];
|
||||
extern struct testcase_t tortls_tests[];
|
||||
extern struct testcase_t tortls_openssl_tests[];
|
||||
extern struct testcase_t util_tests[];
|
||||
extern struct testcase_t util_format_tests[];
|
||||
extern struct testcase_t util_process_tests[];
|
||||
@@ -263,6 +265,7 @@ extern struct testcase_t voting_schedule_tests[];
|
||||
extern struct testcase_t dns_tests[];
|
||||
extern struct testcase_t handle_tests[];
|
||||
extern struct testcase_t sr_tests[];
|
||||
extern struct testcase_t x509_tests[];
|
||||
|
||||
extern struct testcase_t slow_crypto_tests[];
|
||||
extern struct testcase_t slow_util_tests[];
|
||||
|
||||
@@ -161,7 +161,7 @@ test_add_onion_helper_keyarg_v2(void *arg)
|
||||
/* Test loading a RSA1024 key. */
|
||||
tor_free(err_msg);
|
||||
pk1 = pk_generate(0);
|
||||
tt_int_op(0, OP_EQ, crypto_pk_base64_encode(pk1, &encoded));
|
||||
tt_int_op(0, OP_EQ, crypto_pk_base64_encode_private(pk1, &encoded));
|
||||
tor_asprintf(&arg_str, "RSA1024:%s", encoded);
|
||||
ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
|
||||
&pk, &hs_version, &err_msg);
|
||||
|
||||
+122
-5
@@ -16,7 +16,9 @@
|
||||
#include "lib/crypt_ops/crypto_format.h"
|
||||
#include "lib/crypt_ops/crypto_hkdf.h"
|
||||
#include "lib/crypt_ops/crypto_rand.h"
|
||||
#include "lib/crypt_ops/crypto_init.h"
|
||||
#include "ed25519_vectors.inc"
|
||||
#include "test/log_test_helpers.h"
|
||||
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
@@ -25,6 +27,13 @@
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_OPENSSL)
|
||||
#include "lib/crypt_ops/compat_openssl.h"
|
||||
DISABLE_GCC_WARNING(redundant-decls)
|
||||
#include <openssl/dh.h>
|
||||
ENABLE_GCC_WARNING(redundant-decls)
|
||||
#endif
|
||||
|
||||
/** Run unit tests for Diffie-Hellman functionality. */
|
||||
static void
|
||||
test_crypto_dh(void *arg)
|
||||
@@ -37,6 +46,11 @@ test_crypto_dh(void *arg)
|
||||
char s1[DH1024_KEY_LEN];
|
||||
char s2[DH1024_KEY_LEN];
|
||||
ssize_t s1len, s2len;
|
||||
#ifdef ENABLE_OPENSSL
|
||||
crypto_dh_t *dh3 = NULL;
|
||||
DH *dh4 = NULL;
|
||||
BIGNUM *pubkey_tmp = NULL;
|
||||
#endif
|
||||
|
||||
(void)arg;
|
||||
tt_int_op(crypto_dh_get_bytes(dh1),OP_EQ, DH1024_KEY_LEN);
|
||||
@@ -91,6 +105,10 @@ test_crypto_dh(void *arg)
|
||||
s1len = crypto_dh_compute_secret(LOG_WARN, dh1, "\x02", 1, s1, 50);
|
||||
tt_int_op(50, OP_EQ, s1len);
|
||||
|
||||
/* 2 a second time is still okay, though weird. */
|
||||
s1len = crypto_dh_compute_secret(LOG_WARN, dh1, "\x02", 1, s1, 50);
|
||||
tt_int_op(50, OP_EQ, s1len);
|
||||
|
||||
const char P[] =
|
||||
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
|
||||
"8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
|
||||
@@ -157,16 +175,59 @@ test_crypto_dh(void *arg)
|
||||
tt_int_op(s1len, OP_EQ, -1);
|
||||
}
|
||||
|
||||
#if defined(ENABLE_OPENSSL)
|
||||
{
|
||||
/* Make sure that our crypto library can handshake with openssl. */
|
||||
dh3 = crypto_dh_new(DH_TYPE_TLS);
|
||||
tt_assert(!crypto_dh_get_public(dh3, p1, DH1024_KEY_LEN));
|
||||
|
||||
dh4 = crypto_dh_new_openssl_tls();
|
||||
tt_assert(DH_generate_key(dh4));
|
||||
const BIGNUM *pk=NULL;
|
||||
#ifdef OPENSSL_1_1_API
|
||||
const BIGNUM *sk=NULL;
|
||||
DH_get0_key(dh4, &pk, &sk);
|
||||
#else
|
||||
pk = dh4->pub_key;
|
||||
#endif
|
||||
tt_assert(pk);
|
||||
tt_int_op(BN_num_bytes(pk), OP_LE, DH1024_KEY_LEN);
|
||||
tt_int_op(BN_num_bytes(pk), OP_GT, 0);
|
||||
memset(p2, 0, sizeof(p2));
|
||||
/* right-pad. */
|
||||
BN_bn2bin(pk, (unsigned char *)(p2+DH1024_KEY_LEN-BN_num_bytes(pk)));
|
||||
|
||||
s1len = crypto_dh_handshake(LOG_WARN, dh3, p2, DH1024_KEY_LEN,
|
||||
(unsigned char *)s1, sizeof(s1));
|
||||
pubkey_tmp = BN_bin2bn((unsigned char *)p1, DH1024_KEY_LEN, NULL);
|
||||
s2len = DH_compute_key((unsigned char *)s2, pubkey_tmp, dh4);
|
||||
|
||||
tt_int_op(s1len, OP_EQ, s2len);
|
||||
tt_int_op(s1len, OP_GT, 0);
|
||||
tt_mem_op(s1, OP_EQ, s2, s1len);
|
||||
}
|
||||
#endif
|
||||
|
||||
done:
|
||||
crypto_dh_free(dh1);
|
||||
crypto_dh_free(dh2);
|
||||
crypto_dh_free(dh1_dup);
|
||||
#ifdef ENABLE_OPENSSL
|
||||
crypto_dh_free(dh3);
|
||||
if (dh4)
|
||||
DH_free(dh4);
|
||||
if (pubkey_tmp)
|
||||
BN_free(pubkey_tmp);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
test_crypto_openssl_version(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
#ifdef ENABLE_NSS
|
||||
tt_skip();
|
||||
#else
|
||||
const char *version = crypto_openssl_get_version_str();
|
||||
const char *h_version = crypto_openssl_get_header_version_str();
|
||||
tt_assert(version);
|
||||
@@ -186,6 +247,7 @@ test_crypto_openssl_version(void *arg)
|
||||
tt_int_op(a, OP_GE, 0);
|
||||
tt_int_op(b, OP_GE, 0);
|
||||
tt_int_op(c, OP_GE, 0);
|
||||
#endif
|
||||
|
||||
done:
|
||||
;
|
||||
@@ -1363,22 +1425,22 @@ test_crypto_pk_base64(void *arg)
|
||||
/* Test Base64 encoding a key. */
|
||||
pk1 = pk_generate(0);
|
||||
tt_assert(pk1);
|
||||
tt_int_op(0, OP_EQ, crypto_pk_base64_encode(pk1, &encoded));
|
||||
tt_int_op(0, OP_EQ, crypto_pk_base64_encode_private(pk1, &encoded));
|
||||
tt_assert(encoded);
|
||||
|
||||
/* Test decoding a valid key. */
|
||||
pk2 = crypto_pk_base64_decode(encoded, strlen(encoded));
|
||||
pk2 = crypto_pk_base64_decode_private(encoded, strlen(encoded));
|
||||
tt_assert(pk2);
|
||||
tt_int_op(crypto_pk_cmp_keys(pk1, pk2), OP_EQ, 0);
|
||||
crypto_pk_free(pk2);
|
||||
|
||||
/* Test decoding a invalid key (not Base64). */
|
||||
static const char *invalid_b64 = "The key is in another castle!";
|
||||
pk2 = crypto_pk_base64_decode(invalid_b64, strlen(invalid_b64));
|
||||
pk2 = crypto_pk_base64_decode_private(invalid_b64, strlen(invalid_b64));
|
||||
tt_ptr_op(pk2, OP_EQ, NULL);
|
||||
|
||||
/* Test decoding a truncated Base64 blob. */
|
||||
pk2 = crypto_pk_base64_decode(encoded, strlen(encoded)/2);
|
||||
pk2 = crypto_pk_base64_decode_private(encoded, strlen(encoded)/2);
|
||||
tt_ptr_op(pk2, OP_EQ, NULL);
|
||||
|
||||
done:
|
||||
@@ -1427,6 +1489,58 @@ test_crypto_pk_pem_encrypted(void *arg)
|
||||
done:
|
||||
crypto_pk_free(pk);
|
||||
}
|
||||
|
||||
static void
|
||||
test_crypto_pk_invalid_private_key(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
/* Here is a simple invalid private key: it was produced by making
|
||||
* a regular private key, and then adding 2 to the modulus. */
|
||||
const char pem[] =
|
||||
"-----BEGIN RSA PRIVATE KEY-----\n"
|
||||
"MIIEpQIBAAKCAQEAskRyZrs+YAukvBmZlgo6/rCxyKF2xyUk073ap+2CgRUnSfGG\n"
|
||||
"mflHlzqVq7tpH50DafpS+fFAbaEaNV/ac20QG0rUZi38HTB4qURWOu6n0Bws6E4l\n"
|
||||
"UX/AkvDlWnuYH0pHHi2c3DGNFjwoJpjKuUTk+cRffVR8X3Kjr62SUDUaBNW0Kecz\n"
|
||||
"3SYLbmgmZI16dFZ+g9sNM3znXZbhvb33WwPqpZSSPs37cPgF7eS6mAw/gUMx6zfE\n"
|
||||
"HRmUnOQSzUdS05rvc/hsiCLhiIZ8hgfkD07XnTT1Ds8DwE55k7BUWY2wvwWCNLsH\n"
|
||||
"qtqAxTr615XdkMxVkYgImpqPybarpfNYhFqkOwIDAQABAoIBACPC3VxEdbfYvhxJ\n"
|
||||
"2mih9sG++nswAN7kUaX0cRe86rAwaShJPmJHApiQ1ROVTfpciiHJaLnhLraPWe2Z\n"
|
||||
"I/6Bw3hmI4O399p3Lc1u+wlpdNqnvE6B1rSptx0DHE9xecvVH70rE0uM2Su7t6Y+\n"
|
||||
"gnR2IKUGQs2mlCilm7aTUEWs0WJkkl4CG1dyxItuOSdNBjOEzXimJyiB10jEBFsp\n"
|
||||
"SZeCF2FZ7AJbck5CVC42+oTsiDbZrHTHOn7v26rFGdONeHD1wOI1v7JwHFpCB923\n"
|
||||
"aEHBzsPbMeq7DWG1rjzCYpcXHhTDBDBWSia4SEhyr2Nl7m7qxWWWwR+x4dqAj3rD\n"
|
||||
"HeTmos0CgYEA6uf1CLpjPpOs5IaW1DQI8dJA/xFEAC/6GVgq4nFOGHZrm8G3L5o+\n"
|
||||
"qvtQNMpDs2naWuZpqROFqv24o01DykHygR72GlPIY6uvmmf5tvJLoGnbFUay33L4\n"
|
||||
"7b9dkNhuEIBNPzVDie0pgS77WgaPbYkVv5fnDwgPuVnkqfakEt7Pz2MCgYEAwkZ5\n"
|
||||
"R1wLuTQEA2Poo6Gf4L8Bg6yNYI46LHDqDIs818iYLjtcnEEvbPfaoKNpOn7s7s4O\n"
|
||||
"Pc+4HnT1aIQs0IKVLRTp+5a/9wfOkPZnobWOUHZk9UzBL3Hc1uy/qhp93iE3tSzx\n"
|
||||
"v0O1pvR+hr3guTCZx8wZnDvaMgG3hlyPnVlHdrMCgYEAzQQxGbMC1ySv6quEjCP2\n"
|
||||
"AogMbhE1lixJTUFj/EoDbNo9xKznIkauly/Lqqc1OysRhfA/G2+MY9YZBX1zwtyX\n"
|
||||
"uBW7mPKynDrFgi9pBECnvJNmwET57Ic9ttIj6Tzbos83nAjyrzgr1zGX8dRz7ZeN\n"
|
||||
"QbBj2vygLJbGOYinXkjUeh0CgYEAhN5aF9n2EqZmkEMGWtMxWy6HRJ0A3Cap1rcq\n"
|
||||
"+4VHCXWhzwy+XAeg/e/N0MuyLlWcif7XcqLcE8h+BwtO8xQ8HmcNWApUJAls12wO\n"
|
||||
"mGRpftJaXgIupdpD5aJpu1b++qrRRNIGTH9sf1D8L/8w8LcylZkbcuTkaAsQj45C\n"
|
||||
"kqT64U0CgYEAq47IKS6xc3CDc17BqExR6t+1yRe+4ml+z1zcVbfUKony4pGvl1yo\n"
|
||||
"rk0IYDN5Vd8h5xtXrkPdX9h+ywmohnelDKsayEuE+opyqEpSU4/96Bb22RZUoucb\n"
|
||||
"LWkV5gZx5hFnDFtEd4vadMIiY4jVv/3JqiZDKwMVBJKlHRXJEEmIEBk=\n"
|
||||
"-----END RSA PRIVATE KEY-----\n";
|
||||
|
||||
crypto_pk_t *pk = NULL;
|
||||
|
||||
pk = crypto_pk_new();
|
||||
setup_capture_of_logs(LOG_WARN);
|
||||
tt_int_op(-1, OP_EQ,
|
||||
crypto_pk_read_private_key_from_string(pk, pem, strlen(pem)));
|
||||
#ifdef ENABLE_NSS
|
||||
expect_single_log_msg_containing("received bad data");
|
||||
#else
|
||||
expect_single_log_msg_containing("while checking RSA key");
|
||||
#endif
|
||||
done:
|
||||
teardown_capture_of_logs();
|
||||
crypto_pk_free(pk);
|
||||
}
|
||||
|
||||
#ifdef HAVE_TRUNCATE
|
||||
#define do_truncate truncate
|
||||
#else
|
||||
@@ -1462,7 +1576,8 @@ test_crypto_digests(void *arg)
|
||||
(void)arg;
|
||||
k = crypto_pk_new();
|
||||
tt_assert(k);
|
||||
r = crypto_pk_read_private_key_from_string(k, AUTHORITY_SIGNKEY_3, -1);
|
||||
r = crypto_pk_read_private_key_from_string(k, AUTHORITY_SIGNKEY_3,
|
||||
strlen(AUTHORITY_SIGNKEY_3));
|
||||
tt_assert(!r);
|
||||
|
||||
r = crypto_pk_get_digest(k, digest);
|
||||
@@ -3047,6 +3162,8 @@ struct testcase_t crypto_tests[] = {
|
||||
{ "pk_fingerprints", test_crypto_pk_fingerprints, TT_FORK, NULL, NULL },
|
||||
{ "pk_base64", test_crypto_pk_base64, TT_FORK, NULL, NULL },
|
||||
{ "pk_pem_encrypted", test_crypto_pk_pem_encrypted, TT_FORK, NULL, NULL },
|
||||
{ "pk_invalid_private_key", test_crypto_pk_invalid_private_key, 0,
|
||||
NULL, NULL },
|
||||
CRYPTO_LEGACY(digests),
|
||||
{ "digest_names", test_crypto_digest_names, 0, NULL, NULL },
|
||||
{ "sha3", test_crypto_sha3, TT_FORK, NULL, NULL},
|
||||
|
||||
@@ -7,9 +7,11 @@
|
||||
|
||||
#define CRYPTO_OPE_PRIVATE
|
||||
|
||||
#include "lib/cc/compat_compiler.h"
|
||||
#include "lib/crypt_ops/crypto_ope.h"
|
||||
#include "lib/crypt_ops/crypto.h"
|
||||
#include "lib/crypt_ops/crypto_cipher.h"
|
||||
#include "lib/encoding/binascii.h"
|
||||
#include "lib/malloc/malloc.h"
|
||||
#include "test/test.h"
|
||||
#include "tinytest.h"
|
||||
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
#include <libscrypt.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_OPENSSL
|
||||
#include <openssl/evp.h>
|
||||
#endif
|
||||
|
||||
/** Run unit tests for our secret-to-key passphrase hashing functionality. */
|
||||
static void
|
||||
|
||||
@@ -2102,6 +2102,7 @@ test_dir_handle_get_status_vote_d(void* data)
|
||||
|
||||
clear_dir_servers();
|
||||
dirvote_free_all();
|
||||
routerlist_free_all();
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -2638,4 +2639,3 @@ struct testcase_t dir_handle_get_tests[] = {
|
||||
DIR_HANDLE_CMD(parse_accept_encoding, 0),
|
||||
END_OF_TESTCASES
|
||||
};
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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")) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
#include "core/or/or_handshake_state_st.h"
|
||||
#include "core/or/var_cell_st.h"
|
||||
|
||||
#define TOR_X509_PRIVATE
|
||||
#include "lib/tls/tortls.h"
|
||||
#include "lib/tls/x509.h"
|
||||
|
||||
#include "test/test.h"
|
||||
#include "test/log_test_helpers.h"
|
||||
@@ -793,11 +795,26 @@ CERTS_FAIL(bad_rsa_id_cert, /*ed25519*/
|
||||
{
|
||||
require_failure_message = "legacy RSA ID certificate was not valid";
|
||||
certs_cell_cert_t *cert = certs_cell_get_certs(d->ccell, 1);
|
||||
uint8_t *body = certs_cell_cert_getarray_body(cert);
|
||||
ssize_t body_len = certs_cell_cert_getlen_body(cert);
|
||||
/* Frob a byte in the signature */
|
||||
body[body_len - 13] ^= 7;
|
||||
uint8_t *body;
|
||||
/* Frob a byte in the signature, after making a new cert. (NSS won't let
|
||||
* us just frob the old cert, since it will see that the issuer & serial
|
||||
* number are the same, which will make it fail at an earlier stage than
|
||||
* signature verification.) */
|
||||
const tor_x509_cert_t *idc;
|
||||
tor_x509_cert_t *newc;
|
||||
tor_tls_get_my_certs(1, NULL, &idc);
|
||||
time_t new_end = time(NULL) + 86400 * 10;
|
||||
newc = tor_x509_cert_replace_expiration(idc, new_end, d->key2);
|
||||
const uint8_t *encoded;
|
||||
size_t encoded_len;
|
||||
tor_x509_cert_get_der(newc, &encoded, &encoded_len);
|
||||
certs_cell_cert_setlen_body(cert, encoded_len);
|
||||
certs_cell_cert_set_cert_len(cert, encoded_len);
|
||||
body = certs_cell_cert_getarray_body(cert);
|
||||
memcpy(body, encoded, encoded_len);
|
||||
body[encoded_len - 13] ^= 7;
|
||||
REENCODE();
|
||||
tor_x509_cert_free(newc);
|
||||
})
|
||||
CERTS_FAIL(expired_rsa_id, /* both */
|
||||
{
|
||||
@@ -809,9 +826,12 @@ CERTS_FAIL(expired_rsa_id, /* both */
|
||||
tor_x509_cert_t *newc;
|
||||
time_t new_end = time(NULL) - 86400 * 10;
|
||||
newc = tor_x509_cert_replace_expiration(idc, new_end, d->key2);
|
||||
certs_cell_cert_setlen_body(cert, newc->encoded_len);
|
||||
memcpy(certs_cell_cert_getarray_body(cert),
|
||||
newc->encoded, newc->encoded_len);
|
||||
const uint8_t *encoded;
|
||||
size_t encoded_len;
|
||||
tor_x509_cert_get_der(newc, &encoded, &encoded_len);
|
||||
certs_cell_cert_setlen_body(cert, encoded_len);
|
||||
certs_cell_cert_set_cert_len(cert, encoded_len);
|
||||
memcpy(certs_cell_cert_getarray_body(cert), encoded, encoded_len);
|
||||
REENCODE();
|
||||
tor_x509_cert_free(newc);
|
||||
})
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
#include "orconfig.h"
|
||||
|
||||
#include "lib/encoding/pem.h"
|
||||
#include "lib/cc/compat_compiler.h"
|
||||
#include "lib/malloc/malloc.h"
|
||||
|
||||
#include "test/test.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static const char example_pre[] =
|
||||
"Lest you get the wrong impression, we wombats "
|
||||
"are not in the habit of tunneling madly about, without any supplies "
|
||||
"or even a map."; /* -- Ursula Vernon, _Digger_ */
|
||||
static const char expected[] =
|
||||
"-----BEGIN WOMBAT QUOTE-----\n"
|
||||
"TGVzdCB5b3UgZ2V0IHRoZSB3cm9uZyBpbXByZXNzaW9uLCB3ZSB3b21iYXRzIGFy\n"
|
||||
"ZSBub3QgaW4gdGhlIGhhYml0IG9mIHR1bm5lbGluZyBtYWRseSBhYm91dCwgd2l0\n"
|
||||
"aG91dCBhbnkgc3VwcGxpZXMgb3IgZXZlbiBhIG1hcC4=\n"
|
||||
"-----END WOMBAT QUOTE-----\n";
|
||||
|
||||
static void
|
||||
test_crypto_pem_encode(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
char buf[4096];
|
||||
|
||||
int n = (int) pem_encoded_size(strlen(example_pre), "WOMBAT QUOTE");
|
||||
|
||||
int n2 = pem_encode(buf, sizeof(buf),
|
||||
(const unsigned char *)example_pre, strlen(example_pre),
|
||||
"WOMBAT QUOTE");
|
||||
tt_int_op(strlen(buf)+1, OP_EQ, n);
|
||||
tt_int_op(n2, OP_EQ, 0);
|
||||
tt_str_op(buf, OP_EQ, expected);
|
||||
|
||||
/* Now make sure it succeeds if the buffer is exactly the length we want. */
|
||||
memset(buf, 0, sizeof(buf));
|
||||
n2 = pem_encode(buf, n, (const unsigned char *)example_pre,
|
||||
strlen(example_pre), "WOMBAT QUOTE");
|
||||
tt_int_op(n2, OP_EQ, 0);
|
||||
tt_str_op(buf, OP_EQ, expected);
|
||||
|
||||
/* Make sure it fails if the buffer is too short. */
|
||||
memset(buf, 0, sizeof(buf));
|
||||
n2 = pem_encode(buf, n - 1, (const unsigned char *)example_pre,
|
||||
strlen(example_pre), "WOMBAT QUOTE");
|
||||
tt_int_op(n2, OP_EQ, -1);
|
||||
|
||||
done:
|
||||
;
|
||||
}
|
||||
|
||||
static void
|
||||
test_crypto_pem_decode(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
unsigned char buf[4096];
|
||||
|
||||
/* Try a straightforward decoding. */
|
||||
int n = pem_decode(buf, sizeof(buf),
|
||||
expected, strlen(expected),
|
||||
"WOMBAT QUOTE");
|
||||
tt_int_op(n, OP_EQ, strlen(example_pre));
|
||||
tt_mem_op(buf, OP_EQ, example_pre, n);
|
||||
|
||||
/* Succeed if the buffer is exactly the right size. */
|
||||
memset(buf, 0xff, sizeof(buf));
|
||||
n = pem_decode(buf, strlen(example_pre),
|
||||
expected, strlen(expected),
|
||||
"WOMBAT QUOTE");
|
||||
tt_int_op(n, OP_EQ, strlen(example_pre));
|
||||
tt_mem_op(buf, OP_EQ, example_pre, n);
|
||||
tt_int_op(buf[n], OP_EQ, 0xff);
|
||||
|
||||
/* Verify that it fails if the buffer is too small. */
|
||||
memset(buf, 0xff, sizeof(buf));
|
||||
n = pem_decode(buf, strlen(example_pre) - 1,
|
||||
expected, strlen(expected),
|
||||
"WOMBAT QUOTE");
|
||||
tt_int_op(n, OP_EQ, -1);
|
||||
|
||||
/* Verify that it fails with an incorrect tag. */
|
||||
memset(buf, 0xff, sizeof(buf));
|
||||
n = pem_decode(buf, sizeof(buf),
|
||||
expected, strlen(expected),
|
||||
"QUOKKA VOTE");
|
||||
tt_int_op(n, OP_EQ, -1);
|
||||
|
||||
/* Try truncated buffers of different sizes. */
|
||||
size_t i;
|
||||
for (i = 0; i <= strlen(expected); ++i) {
|
||||
char *truncated = tor_memdup(expected, i);
|
||||
n = pem_decode(buf, sizeof(buf),
|
||||
truncated, i,
|
||||
"WOMBAT QUOTE");
|
||||
tor_free(truncated);
|
||||
if (i < strlen(expected) - 1) {
|
||||
tt_int_op(n, OP_EQ, -1);
|
||||
} else {
|
||||
tt_int_op(n, OP_EQ, strlen(example_pre));
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
;
|
||||
}
|
||||
|
||||
struct testcase_t pem_tests[] = {
|
||||
{ "encode", test_crypto_pem_encode, 0, NULL, NULL },
|
||||
{ "decode", test_crypto_pem_decode, 0, NULL, NULL },
|
||||
END_OF_TESTCASES
|
||||
};
|
||||
@@ -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"
|
||||
|
||||
@@ -56,6 +56,8 @@ NS(router_get_my_routerinfo)(void)
|
||||
&mock_routerinfo->onion_pkey_len);
|
||||
mock_routerinfo->bandwidthrate = 9001;
|
||||
mock_routerinfo->bandwidthburst = 9002;
|
||||
crypto_pk_free(ident_key);
|
||||
crypto_pk_free(tap_key);
|
||||
}
|
||||
|
||||
return mock_routerinfo;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1318,7 +1318,7 @@ test_keep_commit(void *arg)
|
||||
expect_log_msg_containing("doesn't match the commit value.");
|
||||
expect_log_msg_containing("has an invalid reveal value.");
|
||||
assert_log_predicate(mock_saved_log_n_entries() == 2,
|
||||
"expected 2 log entries");
|
||||
("expected 2 log entries"));
|
||||
teardown_capture_of_logs();
|
||||
memcpy(commit->hashed_reveal, place_holder.hashed_reveal,
|
||||
sizeof(commit->hashed_reveal));
|
||||
|
||||
+275
-2592
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,13 @@
|
||||
/* Copyright (c) 2010-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
#ifndef TEST_TORTLS_H
|
||||
#define TEST_TORTLS_H
|
||||
|
||||
tor_x509_cert_impl_t *read_cert_from(const char *str);
|
||||
|
||||
extern const char *notCompletelyValidCertString;
|
||||
extern const char *validCertString;
|
||||
extern const char *caCertString;
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user