summaryrefslogtreecommitdiff
path: root/src/lib/crypt_ops
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/crypt_ops')
-rw-r--r--src/lib/crypt_ops/aes_nss.c106
-rw-r--r--src/lib/crypt_ops/aes_openssl.c (renamed from src/lib/crypt_ops/aes.c)4
-rw-r--r--src/lib/crypt_ops/compat_openssl.h6
-rw-r--r--src/lib/crypt_ops/crypto.c509
-rw-r--r--src/lib/crypt_ops/crypto_cipher.c190
-rw-r--r--src/lib/crypt_ops/crypto_cipher.h (renamed from src/lib/crypt_ops/crypto.h)25
-rw-r--r--src/lib/crypt_ops/crypto_dh.c484
-rw-r--r--src/lib/crypt_ops/crypto_dh.h23
-rw-r--r--src/lib/crypt_ops/crypto_dh_nss.c207
-rw-r--r--src/lib/crypt_ops/crypto_dh_openssl.c471
-rw-r--r--src/lib/crypt_ops/crypto_digest.c262
-rw-r--r--src/lib/crypt_ops/crypto_digest.h6
-rw-r--r--src/lib/crypt_ops/crypto_ed25519.c1
-rw-r--r--src/lib/crypt_ops/crypto_format.c1
-rw-r--r--src/lib/crypt_ops/crypto_hkdf.c2
-rw-r--r--src/lib/crypt_ops/crypto_init.c193
-rw-r--r--src/lib/crypt_ops/crypto_init.h34
-rw-r--r--src/lib/crypt_ops/crypto_nss_mgt.c132
-rw-r--r--src/lib/crypt_ops/crypto_nss_mgt.h34
-rw-r--r--src/lib/crypt_ops/crypto_ope.c2
-rw-r--r--src/lib/crypt_ops/crypto_openssl_mgt.c228
-rw-r--r--src/lib/crypt_ops/crypto_openssl_mgt.h15
-rw-r--r--src/lib/crypt_ops/crypto_pwbox.c2
-rw-r--r--src/lib/crypt_ops/crypto_rand.c122
-rw-r--r--src/lib/crypt_ops/crypto_rsa.c1001
-rw-r--r--src/lib/crypt_ops/crypto_rsa.h48
-rw-r--r--src/lib/crypt_ops/crypto_rsa_nss.c738
-rw-r--r--src/lib/crypt_ops/crypto_rsa_openssl.c590
-rw-r--r--src/lib/crypt_ops/crypto_s2k.c50
-rw-r--r--src/lib/crypt_ops/crypto_util.c29
-rw-r--r--src/lib/crypt_ops/crypto_util.h9
-rw-r--r--src/lib/crypt_ops/include.am32
32 files changed, 3738 insertions, 1818 deletions
diff --git a/src/lib/crypt_ops/aes_nss.c b/src/lib/crypt_ops/aes_nss.c
new file mode 100644
index 0000000000..272edc5592
--- /dev/null
+++ b/src/lib/crypt_ops/aes_nss.c
@@ -0,0 +1,106 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file aes_nss.c
+ * \brief Use NSS to implement AES_CTR.
+ **/
+
+#include "orconfig.h"
+#include "lib/crypt_ops/aes.h"
+#include "lib/crypt_ops/crypto_nss_mgt.h"
+#include "lib/crypt_ops/crypto_util.h"
+#include "lib/log/util_bug.h"
+
+DISABLE_GCC_WARNING(strict-prototypes)
+#include <pk11pub.h>
+#include <secerr.h>
+ENABLE_GCC_WARNING(strict-prototypes)
+
+aes_cnt_cipher_t *
+aes_new_cipher(const uint8_t *key, const uint8_t *iv,
+ int key_bits)
+{
+ const CK_MECHANISM_TYPE ckm = CKM_AES_CTR;
+ SECItem keyItem = { .type = siBuffer,
+ .data = (unsigned char *)key,
+ .len = (key_bits / 8) };
+ CK_AES_CTR_PARAMS params;
+ params.ulCounterBits = 128;
+ memcpy(params.cb, iv, 16);
+ SECItem ivItem = { .type = siBuffer,
+ .data = (unsigned char *)&params,
+ .len = sizeof(params) };
+ PK11SlotInfo *slot = NULL;
+ PK11SymKey *keyObj = NULL;
+ SECItem *ivObj = NULL;
+ PK11Context *result = NULL;
+
+ slot = PK11_GetBestSlot(ckm, NULL);
+ if (!slot)
+ goto err;
+
+ keyObj = PK11_ImportSymKey(slot, ckm, PK11_OriginUnwrap,
+ CKA_ENCRYPT, &keyItem, NULL);
+ if (!keyObj)
+ goto err;
+
+ ivObj = PK11_ParamFromIV(ckm, &ivItem);
+ if (!ivObj)
+ goto err;
+
+ PORT_SetError(SEC_ERROR_IO);
+ result = PK11_CreateContextBySymKey(ckm, CKA_ENCRYPT, keyObj, ivObj);
+
+ err:
+ memwipe(&params, 0, sizeof(params));
+ if (ivObj)
+ SECITEM_FreeItem(ivObj, PR_TRUE);
+ if (keyObj)
+ PK11_FreeSymKey(keyObj);
+ if (slot)
+ PK11_FreeSlot(slot);
+
+ tor_assert(result);
+ return (aes_cnt_cipher_t *)result;
+}
+
+void
+aes_cipher_free_(aes_cnt_cipher_t *cipher)
+{
+ if (!cipher)
+ return;
+ PK11_DestroyContext((PK11Context*) cipher, PR_TRUE);
+}
+
+void
+aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data_, size_t len_)
+{
+ tor_assert(len_ <= INT_MAX);
+
+ SECStatus s;
+ PK11Context *ctx = (PK11Context*)cipher;
+ unsigned char *data = (unsigned char *)data_;
+ int len = (int) len_;
+ int result_len = 0;
+
+ s = PK11_CipherOp(ctx, data, &result_len, len, data, len);
+ tor_assert(s == SECSuccess);
+ tor_assert(result_len == len);
+}
+
+int
+evaluate_evp_for_aes(int force_value)
+{
+ (void)force_value;
+ return 0;
+}
+
+int
+evaluate_ctr_for_aes(void)
+{
+ return 0;
+}
diff --git a/src/lib/crypt_ops/aes.c b/src/lib/crypt_ops/aes_openssl.c
index ff9d4d855c..387f5d3df0 100644
--- a/src/lib/crypt_ops/aes.c
+++ b/src/lib/crypt_ops/aes_openssl.c
@@ -5,8 +5,8 @@
/* See LICENSE for licensing information */
/**
- * \file aes.c
- * \brief Implements a counter-mode stream cipher on top of AES.
+ * \file aes_openssl.c
+ * \brief Use OpenSSL to implement AES_CTR.
**/
#include "orconfig.h"
diff --git a/src/lib/crypt_ops/compat_openssl.h b/src/lib/crypt_ops/compat_openssl.h
index 317c01134a..f2f632ab40 100644
--- a/src/lib/crypt_ops/compat_openssl.h
+++ b/src/lib/crypt_ops/compat_openssl.h
@@ -7,6 +7,10 @@
#ifndef TOR_COMPAT_OPENSSL_H
#define TOR_COMPAT_OPENSSL_H
+#include "orconfig.h"
+
+#ifdef ENABLE_OPENSSL
+
#include <openssl/opensslv.h>
#include "lib/crypt_ops/crypto_openssl_mgt.h"
@@ -47,5 +51,7 @@
#define CONST_IF_OPENSSL_1_1_API const
#endif /* !defined(OPENSSL_1_1_API) */
+#endif /* defined(ENABLE_OPENSSL) */
+
#endif /* !defined(TOR_COMPAT_OPENSSL_H) */
diff --git a/src/lib/crypt_ops/crypto.c b/src/lib/crypt_ops/crypto.c
deleted file mode 100644
index 5bc2da76ab..0000000000
--- a/src/lib/crypt_ops/crypto.c
+++ /dev/null
@@ -1,509 +0,0 @@
-/* Copyright (c) 2001, Matej Pfajfar.
- * Copyright (c) 2001-2004, Roger Dingledine.
- * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2018, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-/**
- * \file crypto.c
- * \brief Wrapper functions to present a consistent interface to
- * public-key and symmetric cryptography operations from OpenSSL and
- * other places.
- **/
-
-#include "orconfig.h"
-
-#ifdef _WIN32
-#include <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;
-}
-
-/** @} */
diff --git a/src/lib/crypt_ops/crypto_cipher.c b/src/lib/crypt_ops/crypto_cipher.c
new file mode 100644
index 0000000000..6b762e374d
--- /dev/null
+++ b/src/lib/crypt_ops/crypto_cipher.c
@@ -0,0 +1,190 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file crypto_cipher.c
+ * \brief Symmetric cryptography (low-level) with AES.
+ **/
+
+#include "orconfig.h"
+
+#include "lib/crypt_ops/crypto_cipher.h"
+#include "lib/crypt_ops/crypto_rand.h"
+#include "lib/crypt_ops/crypto_util.h"
+
+#include "lib/log/log.h"
+#include "lib/log/util_bug.h"
+#include "lib/cc/torint.h"
+#include "lib/crypt_ops/aes.h"
+
+#include <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);
+}
diff --git a/src/lib/crypt_ops/crypto.h b/src/lib/crypt_ops/crypto_cipher.h
index 3a0b330be6..f9444d03fc 100644
--- a/src/lib/crypt_ops/crypto.h
+++ b/src/lib/crypt_ops/crypto_cipher.h
@@ -5,19 +5,18 @@
/* See LICENSE for licensing information */
/**
- * \file crypto.h
+ * \file crypto_cipher.h
*
- * \brief Headers for crypto.c
+ * \brief Headers for crypto_cipher.c
**/
-#ifndef TOR_CRYPTO_H
-#define TOR_CRYPTO_H
+#ifndef TOR_CRYPTO_CIPHER_H
+#define TOR_CRYPTO_CIPHER_H
#include "orconfig.h"
#include <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) */
diff --git a/src/lib/crypt_ops/crypto_dh.c b/src/lib/crypt_ops/crypto_dh.c
index 3e82996935..673ef311f9 100644
--- a/src/lib/crypt_ops/crypto_dh.c
+++ b/src/lib/crypt_ops/crypto_dh.c
@@ -7,6 +7,8 @@
/**
* \file crypto_dh.c
* \brief Block of functions related with DH utilities and operations.
+ * over Z_p. We aren't using this for any new crypto -- EC is more
+ * efficient.
**/
#include "lib/crypt_ops/compat_openssl.h"
@@ -17,411 +19,50 @@
#include "lib/log/log.h"
#include "lib/log/util_bug.h"
-DISABLE_GCC_WARNING(redundant-decls)
-
-#include <openssl/dh.h>
-
-ENABLE_GCC_WARNING(redundant-decls)
-
-#include <openssl/bn.h>
-#include <string.h>
-
-/** A structure to hold the first half (x, g^x) of a Diffie-Hellman handshake
- * while we're waiting for the second.*/
-struct crypto_dh_t {
- DH *dh; /**< The openssl DH object */
-};
-
-static int tor_check_dh_key(int severity, const BIGNUM *bn);
-
-/** Used by tortls.c: Get the DH* from a crypto_dh_t.
- */
-DH *
-crypto_dh_get_dh_(crypto_dh_t *dh)
-{
- return dh->dh;
-}
-
/** Our DH 'g' parameter */
-#define DH_GENERATOR 2
-
-/** Shared P parameter for our circuit-crypto DH key exchanges. */
-static BIGNUM *dh_param_p = NULL;
-/** Shared P parameter for our TLS DH key exchanges. */
-static BIGNUM *dh_param_p_tls = NULL;
-/** Shared G parameter for our DH key exchanges. */
-static BIGNUM *dh_param_g = NULL;
-
-/** Validate a given set of Diffie-Hellman parameters. This is moderately
- * computationally expensive (milliseconds), so should only be called when
- * the DH parameters change. Returns 0 on success, * -1 on failure.
+const unsigned DH_GENERATOR = 2;
+/** This is the 1024-bit safe prime that Apache uses for its DH stuff; see
+ * modules/ssl/ssl_engine_dh.c; Apache also uses a generator of 2 with this
+ * prime.
*/
-static int
-crypto_validate_dh_params(const BIGNUM *p, const BIGNUM *g)
-{
- DH *dh = NULL;
- int ret = -1;
-
- /* Copy into a temporary DH object, just so that DH_check() can be called. */
- if (!(dh = DH_new()))
- goto out;
-#ifdef OPENSSL_1_1_API
- BIGNUM *dh_p, *dh_g;
- if (!(dh_p = BN_dup(p)))
- goto out;
- if (!(dh_g = BN_dup(g)))
- goto out;
- if (!DH_set0_pqg(dh, dh_p, NULL, dh_g))
- goto out;
-#else /* !(defined(OPENSSL_1_1_API)) */
- if (!(dh->p = BN_dup(p)))
- goto out;
- if (!(dh->g = BN_dup(g)))
- goto out;
-#endif /* defined(OPENSSL_1_1_API) */
-
- /* Perform the validation. */
- int codes = 0;
- if (!DH_check(dh, &codes))
- goto out;
- if (BN_is_word(g, DH_GENERATOR_2)) {
- /* Per https://wiki.openssl.org/index.php/Diffie-Hellman_parameters
- *
- * OpenSSL checks the prime is congruent to 11 when g = 2; while the
- * IETF's primes are congruent to 23 when g = 2.
- */
- BN_ULONG residue = BN_mod_word(p, 24);
- if (residue == 11 || residue == 23)
- codes &= ~DH_NOT_SUITABLE_GENERATOR;
- }
- if (codes != 0) /* Specifics on why the params suck is irrelevant. */
- goto out;
-
- /* Things are probably not evil. */
- ret = 0;
-
- out:
- if (dh)
- DH_free(dh);
- return ret;
-}
-
-/** Set the global Diffie-Hellman generator, used for both TLS and internal
- * DH stuff.
+const char TLS_DH_PRIME[] =
+ "D67DE440CBBBDC1936D693D34AFD0AD50C84D239A45F520BB88174CB98"
+ "BCE951849F912E639C72FB13B4B4D7177E16D55AC179BA420B2A29FE324A"
+ "467A635E81FF5901377BEDDCFD33168A461AAD3B72DAE8860078045B07A7"
+ "DBCA7874087D1510EA9FCC9DDD330507DD62DB88AEAA747DE0F4D6E2BD68"
+ "B0E7393E0F24218EB3";
+/**
+ * This is from rfc2409, section 6.2. It's a safe prime, and
+ * supposedly it equals:
+ * 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }.
*/
-static void
-crypto_set_dh_generator(void)
-{
- BIGNUM *generator;
- int r;
+const char OAKLEY_PRIME_2[] =
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ "49286651ECE65381FFFFFFFFFFFFFFFF";
- if (dh_param_g)
- return;
-
- generator = BN_new();
- tor_assert(generator);
-
- r = BN_set_word(generator, DH_GENERATOR);
- tor_assert(r);
-
- dh_param_g = generator;
-}
-
-/** Set the global TLS Diffie-Hellman modulus. Use the Apache mod_ssl DH
- * modulus. */
void
-crypto_set_tls_dh_prime(void)
-{
- BIGNUM *tls_prime = NULL;
- int r;
-
- /* If the space is occupied, free the previous TLS DH prime */
- if (BUG(dh_param_p_tls)) {
- /* LCOV_EXCL_START
- *
- * We shouldn't be calling this twice.
- */
- BN_clear_free(dh_param_p_tls);
- dh_param_p_tls = NULL;
- /* LCOV_EXCL_STOP */
- }
-
- tls_prime = BN_new();
- tor_assert(tls_prime);
-
- /* This is the 1024-bit safe prime that Apache uses for its DH stuff; see
- * modules/ssl/ssl_engine_dh.c; Apache also uses a generator of 2 with this
- * prime.
- */
- r = BN_hex2bn(&tls_prime,
- "D67DE440CBBBDC1936D693D34AFD0AD50C84D239A45F520BB88174CB98"
- "BCE951849F912E639C72FB13B4B4D7177E16D55AC179BA420B2A29FE324A"
- "467A635E81FF5901377BEDDCFD33168A461AAD3B72DAE8860078045B07A7"
- "DBCA7874087D1510EA9FCC9DDD330507DD62DB88AEAA747DE0F4D6E2BD68"
- "B0E7393E0F24218EB3");
- tor_assert(r);
-
- tor_assert(tls_prime);
-
- dh_param_p_tls = tls_prime;
- crypto_set_dh_generator();
- tor_assert(0 == crypto_validate_dh_params(dh_param_p_tls, dh_param_g));
-}
-
-/** Initialize dh_param_p and dh_param_g if they are not already
- * set. */
-static void
-init_dh_param(void)
-{
- BIGNUM *circuit_dh_prime;
- int r;
- if (BUG(dh_param_p && dh_param_g))
- return; // LCOV_EXCL_LINE This function isn't supposed to be called twice.
-
- circuit_dh_prime = BN_new();
- tor_assert(circuit_dh_prime);
-
- /* This is from rfc2409, section 6.2. It's a safe prime, and
- supposedly it equals:
- 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }.
- */
- r = BN_hex2bn(&circuit_dh_prime,
- "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
- "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
- "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
- "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
- "49286651ECE65381FFFFFFFFFFFFFFFF");
- tor_assert(r);
-
- /* Set the new values as the global DH parameters. */
- dh_param_p = circuit_dh_prime;
- crypto_set_dh_generator();
- tor_assert(0 == crypto_validate_dh_params(dh_param_p, dh_param_g));
-
- if (!dh_param_p_tls) {
- crypto_set_tls_dh_prime();
- }
-}
-
-/** Number of bits to use when choosing the x or y value in a Diffie-Hellman
- * handshake. Since we exponentiate by this value, choosing a smaller one
- * lets our handhake go faster.
- */
-#define DH_PRIVATE_KEY_BITS 320
-
-/** Allocate and return a new DH object for a key exchange. Returns NULL on
- * failure.
- */
-crypto_dh_t *
-crypto_dh_new(int dh_type)
-{
- crypto_dh_t *res = tor_malloc_zero(sizeof(crypto_dh_t));
-
- tor_assert(dh_type == DH_TYPE_CIRCUIT || dh_type == DH_TYPE_TLS ||
- dh_type == DH_TYPE_REND);
-
- if (!dh_param_p)
- init_dh_param();
-
- if (!(res->dh = DH_new()))
- goto err;
-
-#ifdef OPENSSL_1_1_API
- BIGNUM *dh_p = NULL, *dh_g = NULL;
-
- if (dh_type == DH_TYPE_TLS) {
- dh_p = BN_dup(dh_param_p_tls);
- } else {
- dh_p = BN_dup(dh_param_p);
- }
- if (!dh_p)
- goto err;
-
- dh_g = BN_dup(dh_param_g);
- if (!dh_g) {
- BN_free(dh_p);
- goto err;
- }
-
- if (!DH_set0_pqg(res->dh, dh_p, NULL, dh_g)) {
- goto err;
- }
-
- if (!DH_set_length(res->dh, DH_PRIVATE_KEY_BITS))
- goto err;
-#else /* !(defined(OPENSSL_1_1_API)) */
- if (dh_type == DH_TYPE_TLS) {
- if (!(res->dh->p = BN_dup(dh_param_p_tls)))
- goto err;
- } else {
- if (!(res->dh->p = BN_dup(dh_param_p)))
- goto err;
- }
-
- if (!(res->dh->g = BN_dup(dh_param_g)))
- goto err;
-
- res->dh->length = DH_PRIVATE_KEY_BITS;
-#endif /* defined(OPENSSL_1_1_API) */
-
- return res;
-
- /* LCOV_EXCL_START
- * This error condition is only reached when an allocation fails */
- err:
- crypto_log_errors(LOG_WARN, "creating DH object");
- if (res->dh) DH_free(res->dh); /* frees p and g too */
- tor_free(res);
- return NULL;
- /* LCOV_EXCL_STOP */
-}
-
-/** Return a copy of <b>dh</b>, sharing its internal state. */
-crypto_dh_t *
-crypto_dh_dup(const crypto_dh_t *dh)
+crypto_dh_init(void)
{
- crypto_dh_t *dh_new = tor_malloc_zero(sizeof(crypto_dh_t));
- tor_assert(dh);
- tor_assert(dh->dh);
- dh_new->dh = dh->dh;
- DH_up_ref(dh->dh);
- return dh_new;
-}
-
-/** Return the length of the DH key in <b>dh</b>, in bytes.
- */
-int
-crypto_dh_get_bytes(crypto_dh_t *dh)
-{
- tor_assert(dh);
- return DH_size(dh->dh);
-}
-
-/** Generate \<x,g^x\> for our part of the key exchange. Return 0 on
- * success, -1 on failure.
- */
-int
-crypto_dh_generate_public(crypto_dh_t *dh)
-{
-#ifndef OPENSSL_1_1_API
- again:
+#ifdef ENABLE_OPENSSL
+ crypto_dh_init_openssl();
#endif
- if (!DH_generate_key(dh->dh)) {
- /* LCOV_EXCL_START
- * To test this we would need some way to tell openssl to break DH. */
- crypto_log_errors(LOG_WARN, "generating DH key");
- return -1;
- /* LCOV_EXCL_STOP */
- }
-#ifdef OPENSSL_1_1_API
- /* OpenSSL 1.1.x doesn't appear to let you regenerate a DH key, without
- * recreating the DH object. I have no idea what sort of aliasing madness
- * can occur here, so do the check, and just bail on failure.
- */
- const BIGNUM *pub_key, *priv_key;
- DH_get0_key(dh->dh, &pub_key, &priv_key);
- if (tor_check_dh_key(LOG_WARN, pub_key)<0) {
- log_warn(LD_CRYPTO, "Weird! Our own DH key was invalid. I guess once-in-"
- "the-universe chances really do happen. Treating as a failure.");
- return -1;
- }
-#else /* !(defined(OPENSSL_1_1_API)) */
- if (tor_check_dh_key(LOG_WARN, dh->dh->pub_key)<0) {
- /* LCOV_EXCL_START
- * If this happens, then openssl's DH implementation is busted. */
- log_warn(LD_CRYPTO, "Weird! Our own DH key was invalid. I guess once-in-"
- "the-universe chances really do happen. Trying again.");
- /* Free and clear the keys, so OpenSSL will actually try again. */
- BN_clear_free(dh->dh->pub_key);
- BN_clear_free(dh->dh->priv_key);
- dh->dh->pub_key = dh->dh->priv_key = NULL;
- goto again;
- /* LCOV_EXCL_STOP */
- }
-#endif /* defined(OPENSSL_1_1_API) */
- return 0;
-}
-
-/** Generate g^x as necessary, and write the g^x for the key exchange
- * as a <b>pubkey_len</b>-byte value into <b>pubkey</b>. Return 0 on
- * success, -1 on failure. <b>pubkey_len</b> must be \>= DH1024_KEY_LEN.
- */
-int
-crypto_dh_get_public(crypto_dh_t *dh, char *pubkey, size_t pubkey_len)
-{
- int bytes;
- tor_assert(dh);
-
- const BIGNUM *dh_pub;
-
-#ifdef OPENSSL_1_1_API
- const BIGNUM *dh_priv;
- DH_get0_key(dh->dh, &dh_pub, &dh_priv);
-#else
- dh_pub = dh->dh->pub_key;
-#endif /* defined(OPENSSL_1_1_API) */
-
- if (!dh_pub) {
- if (crypto_dh_generate_public(dh)<0)
- return -1;
- else {
-#ifdef OPENSSL_1_1_API
- DH_get0_key(dh->dh, &dh_pub, &dh_priv);
-#else
- dh_pub = dh->dh->pub_key;
+#ifdef ENABLE_NSS
+ crypto_dh_init_nss();
#endif
- }
- }
-
- tor_assert(dh_pub);
- bytes = BN_num_bytes(dh_pub);
- tor_assert(bytes >= 0);
- if (pubkey_len < (size_t)bytes) {
- log_warn(LD_CRYPTO,
- "Weird! pubkey_len (%d) was smaller than DH1024_KEY_LEN (%d)",
- (int) pubkey_len, bytes);
- return -1;
- }
-
- memset(pubkey, 0, pubkey_len);
- BN_bn2bin(dh_pub, (unsigned char*)(pubkey+(pubkey_len-bytes)));
-
- return 0;
}
-/** Check for bad Diffie-Hellman public keys (g^x). Return 0 if the key is
- * okay (in the subgroup [2,p-2]), or -1 if it's bad.
- * See http://www.cl.cam.ac.uk/ftp/users/rja14/psandqs.ps.gz for some tips.
- */
-static int
-tor_check_dh_key(int severity, const BIGNUM *bn)
+void
+crypto_dh_free_all(void)
{
- BIGNUM *x;
- char *s;
- tor_assert(bn);
- x = BN_new();
- tor_assert(x);
- if (BUG(!dh_param_p))
- init_dh_param(); //LCOV_EXCL_LINE we already checked whether we did this.
- BN_set_word(x, 1);
- if (BN_cmp(bn,x)<=0) {
- log_fn(severity, LD_CRYPTO, "DH key must be at least 2.");
- goto err;
- }
- BN_copy(x,dh_param_p);
- BN_sub_word(x, 1);
- if (BN_cmp(bn,x)>=0) {
- log_fn(severity, LD_CRYPTO, "DH key must be at most p-2.");
- goto err;
- }
- BN_clear_free(x);
- return 0;
- err:
- BN_clear_free(x);
- s = BN_bn2hex(bn);
- log_fn(severity, LD_CRYPTO, "Rejecting insecure DH key [%s]", s);
- OPENSSL_free(s);
- return -1;
+#ifdef ENABLE_OPENSSL
+ crypto_dh_free_all_openssl();
+#endif
+#ifdef ENABLE_NSS
+ crypto_dh_free_all_nss();
+#endif
}
/** Given a DH key exchange object, and our peer's value of g^y (as a
@@ -439,31 +80,20 @@ crypto_dh_compute_secret(int severity, crypto_dh_t *dh,
const char *pubkey, size_t pubkey_len,
char *secret_out, size_t secret_bytes_out)
{
- char *secret_tmp = NULL;
- BIGNUM *pubkey_bn = NULL;
- size_t secret_len=0, secret_tmp_len=0;
- int result=0;
- tor_assert(dh);
tor_assert(secret_bytes_out/DIGEST_LEN <= 255);
- tor_assert(pubkey_len < INT_MAX);
- if (!(pubkey_bn = BN_bin2bn((const unsigned char*)pubkey,
- (int)pubkey_len, NULL)))
- goto error;
- if (tor_check_dh_key(severity, pubkey_bn)<0) {
- /* Check for invalid public keys. */
- log_fn(severity, LD_CRYPTO,"Rejected invalid g^x");
- goto error;
- }
+ unsigned char *secret_tmp = NULL;
+ size_t secret_len=0, secret_tmp_len=0;
secret_tmp_len = crypto_dh_get_bytes(dh);
secret_tmp = tor_malloc(secret_tmp_len);
- result = DH_compute_key((unsigned char*)secret_tmp, pubkey_bn, dh->dh);
- if (result < 0) {
- log_warn(LD_CRYPTO,"DH_compute_key() failed.");
+
+ ssize_t result = crypto_dh_handshake(severity, dh, pubkey, pubkey_len,
+ secret_tmp, secret_tmp_len);
+ if (result < 0)
goto error;
- }
+
secret_len = result;
- if (crypto_expand_key_material_TAP((uint8_t*)secret_tmp, secret_len,
+ if (crypto_expand_key_material_TAP(secret_tmp, secret_len,
(uint8_t*)secret_out, secret_bytes_out)<0)
goto error;
secret_len = secret_bytes_out;
@@ -472,9 +102,6 @@ crypto_dh_compute_secret(int severity, crypto_dh_t *dh,
error:
result = -1;
done:
- crypto_log_errors(LOG_WARN, "completing DH handshake");
- if (pubkey_bn)
- BN_clear_free(pubkey_bn);
if (secret_tmp) {
memwipe(secret_tmp, 0, secret_tmp_len);
tor_free(secret_tmp);
@@ -484,28 +111,3 @@ crypto_dh_compute_secret(int severity, crypto_dh_t *dh,
else
return secret_len;
}
-
-/** Free a DH key exchange object.
- */
-void
-crypto_dh_free_(crypto_dh_t *dh)
-{
- if (!dh)
- return;
- tor_assert(dh->dh);
- DH_free(dh->dh);
- tor_free(dh);
-}
-
-void
-crypto_dh_free_all(void)
-{
- if (dh_param_p)
- BN_clear_free(dh_param_p);
- if (dh_param_p_tls)
- BN_clear_free(dh_param_p_tls);
- if (dh_param_g)
- BN_clear_free(dh_param_g);
-
- dh_param_p = dh_param_p_tls = dh_param_g = NULL;
-}
diff --git a/src/lib/crypt_ops/crypto_dh.h b/src/lib/crypt_ops/crypto_dh.h
index 88e8a919a8..3ee343a278 100644
--- a/src/lib/crypt_ops/crypto_dh.h
+++ b/src/lib/crypt_ops/crypto_dh.h
@@ -19,11 +19,15 @@
typedef struct crypto_dh_t crypto_dh_t;
+extern const unsigned DH_GENERATOR;
+extern const char TLS_DH_PRIME[];
+extern const char OAKLEY_PRIME_2[];
+
/* Key negotiation */
#define DH_TYPE_CIRCUIT 1
#define DH_TYPE_REND 2
#define DH_TYPE_TLS 3
-void crypto_set_tls_dh_prime(void);
+void crypto_dh_init(void);
crypto_dh_t *crypto_dh_new(int dh_type);
crypto_dh_t *crypto_dh_dup(const crypto_dh_t *dh);
int crypto_dh_get_bytes(crypto_dh_t *dh);
@@ -36,12 +40,25 @@ ssize_t crypto_dh_compute_secret(int severity, crypto_dh_t *dh,
void crypto_dh_free_(crypto_dh_t *dh);
#define crypto_dh_free(dh) FREE_AND_NULL(crypto_dh_t, crypto_dh_free_, (dh))
-/* Crypto DH free */
+ssize_t crypto_dh_handshake(int severity, crypto_dh_t *dh,
+ const char *pubkey, size_t pubkey_len,
+ unsigned char *secret_out,
+ size_t secret_bytes_out);
+
void crypto_dh_free_all(void);
/* Prototypes for private functions only used by tortls.c, crypto.c, and the
* unit tests. */
struct dh_st;
-struct dh_st *crypto_dh_get_dh_(crypto_dh_t *dh);
+struct dh_st *crypto_dh_new_openssl_tls(void);
+
+#ifdef ENABLE_OPENSSL
+void crypto_dh_init_openssl(void);
+void crypto_dh_free_all_openssl(void);
+#endif
+#ifdef ENABLE_NSS
+void crypto_dh_init_nss(void);
+void crypto_dh_free_all_nss(void);
+#endif
#endif /* !defined(TOR_CRYPTO_DH_H) */
diff --git a/src/lib/crypt_ops/crypto_dh_nss.c b/src/lib/crypt_ops/crypto_dh_nss.c
new file mode 100644
index 0000000000..9a14b809b4
--- /dev/null
+++ b/src/lib/crypt_ops/crypto_dh_nss.c
@@ -0,0 +1,207 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file crypto_dh_nss.h
+ *
+ * \brief NSS implementation of Diffie-Hellman over Z_p.
+ **/
+
+#include "lib/crypt_ops/crypto_dh.h"
+#include "lib/crypt_ops/crypto_nss_mgt.h"
+
+#include "lib/encoding/binascii.h"
+#include "lib/log/util_bug.h"
+#include "lib/malloc/malloc.h"
+
+#include <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;
+}
diff --git a/src/lib/crypt_ops/crypto_dh_openssl.c b/src/lib/crypt_ops/crypto_dh_openssl.c
new file mode 100644
index 0000000000..54946458d5
--- /dev/null
+++ b/src/lib/crypt_ops/crypto_dh_openssl.c
@@ -0,0 +1,471 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file crypto_dh_openssl.c
+ * \brief Implement Tor's Z_p diffie-hellman stuff for OpenSSL.
+ **/
+
+#include "lib/crypt_ops/compat_openssl.h"
+#include "lib/crypt_ops/crypto_dh.h"
+#include "lib/crypt_ops/crypto_digest.h"
+#include "lib/crypt_ops/crypto_hkdf.h"
+#include "lib/crypt_ops/crypto_util.h"
+#include "lib/log/log.h"
+#include "lib/log/util_bug.h"
+
+DISABLE_GCC_WARNING(redundant-decls)
+
+#include <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;
+}
diff --git a/src/lib/crypt_ops/crypto_digest.c b/src/lib/crypt_ops/crypto_digest.c
index ee5c362e38..77cf18dca9 100644
--- a/src/lib/crypt_ops/crypto_digest.c
+++ b/src/lib/crypt_ops/crypto_digest.c
@@ -12,7 +12,6 @@
#include "lib/container/smartlist.h"
#include "lib/crypt_ops/crypto_digest.h"
-#include "lib/crypt_ops/crypto_openssl_mgt.h"
#include "lib/crypt_ops/crypto_util.h"
#include "lib/log/log.h"
#include "lib/log/util_bug.h"
@@ -24,12 +23,92 @@
#include "lib/arch/bytes.h"
+#ifdef ENABLE_NSS
+DISABLE_GCC_WARNING(strict-prototypes)
+#include <pk11pub.h>
+ENABLE_GCC_WARNING(strict-prototypes)
+#else
+
+#include "lib/crypt_ops/crypto_openssl_mgt.h"
+
DISABLE_GCC_WARNING(redundant-decls)
#include <openssl/hmac.h>
#include <openssl/sha.h>
ENABLE_GCC_WARNING(redundant-decls)
+#endif
+
+#ifdef ENABLE_NSS
+/**
+ * Convert a digest_algorithm_t (used by tor) to a HashType (used by NSS).
+ * On failure, return SEC_OID_UNKNOWN. */
+static SECOidTag
+digest_alg_to_nss_oid(digest_algorithm_t alg)
+{
+ switch (alg) {
+ case DIGEST_SHA1: return SEC_OID_SHA1;
+ case DIGEST_SHA256: return SEC_OID_SHA256;
+ case DIGEST_SHA512: return SEC_OID_SHA512;
+ case DIGEST_SHA3_256: /* Fall through */
+ case DIGEST_SHA3_512: /* Fall through */
+ default:
+ return SEC_OID_UNKNOWN;
+ }
+}
+
+/* Helper: get an unkeyed digest via pk11wrap */
+static int
+digest_nss_internal(SECOidTag alg,
+ char *digest, unsigned len_out,
+ const char *msg, size_t msg_len)
+{
+ if (alg == SEC_OID_UNKNOWN)
+ return -1;
+ tor_assert(msg_len <= UINT_MAX);
+
+ int rv = -1;
+ SECStatus s;
+ PK11Context *ctx = PK11_CreateDigestContext(alg);
+ if (!ctx)
+ return -1;
+
+ s = PK11_DigestBegin(ctx);
+ if (s != SECSuccess)
+ goto done;
+
+ s = PK11_DigestOp(ctx, (const unsigned char *)msg, (unsigned int)msg_len);
+ if (s != SECSuccess)
+ goto done;
+
+ unsigned int len = 0;
+ s = PK11_DigestFinal(ctx, (unsigned char *)digest, &len, len_out);
+ if (s != SECSuccess)
+ goto done;
+
+ rv = 0;
+ done:
+ PK11_DestroyContext(ctx, PR_TRUE);
+ return rv;
+}
+
+/** True iff alg is implemented in our crypto library, and we want to use that
+ * implementation */
+static bool
+library_supports_digest(digest_algorithm_t alg)
+{
+ switch (alg) {
+ case DIGEST_SHA1: /* Fall through */
+ case DIGEST_SHA256: /* Fall through */
+ case DIGEST_SHA512: /* Fall through */
+ return true;
+ case DIGEST_SHA3_256: /* Fall through */
+ case DIGEST_SHA3_512: /* Fall through */
+ default:
+ return false;
+ }
+}
+#endif
/* Crypto digest functions */
@@ -37,13 +116,18 @@ ENABLE_GCC_WARNING(redundant-decls)
* <b>m</b>. Write the DIGEST_LEN byte result into <b>digest</b>.
* Return 0 on success, -1 on failure.
*/
-int
-crypto_digest(char *digest, const char *m, size_t len)
+MOCK_IMPL(int,
+crypto_digest,(char *digest, const char *m, size_t len))
{
tor_assert(m);
tor_assert(digest);
- if (SHA1((const unsigned char*)m,len,(unsigned char*)digest) == NULL)
+#ifdef ENABLE_NSS
+ return digest_nss_internal(SEC_OID_SHA1, digest, DIGEST_LEN, m, len);
+#else
+ if (SHA1((const unsigned char*)m,len,(unsigned char*)digest) == NULL) {
return -1;
+ }
+#endif
return 0;
}
@@ -59,11 +143,16 @@ crypto_digest256(char *digest, const char *m, size_t len,
tor_assert(algorithm == DIGEST_SHA256 || algorithm == DIGEST_SHA3_256);
int ret = 0;
- if (algorithm == DIGEST_SHA256)
+ if (algorithm == DIGEST_SHA256) {
+#ifdef ENABLE_NSS
+ return digest_nss_internal(SEC_OID_SHA256, digest, DIGEST256_LEN, m, len);
+#else
ret = (SHA256((const uint8_t*)m,len,(uint8_t*)digest) != NULL);
- else
+#endif
+ } else {
ret = (sha3_256((uint8_t *)digest, DIGEST256_LEN,(const uint8_t *)m, len)
> -1);
+ }
if (!ret)
return -1;
@@ -82,12 +171,17 @@ crypto_digest512(char *digest, const char *m, size_t len,
tor_assert(algorithm == DIGEST_SHA512 || algorithm == DIGEST_SHA3_512);
int ret = 0;
- if (algorithm == DIGEST_SHA512)
+ if (algorithm == DIGEST_SHA512) {
+#ifdef ENABLE_NSS
+ return digest_nss_internal(SEC_OID_SHA512, digest, DIGEST512_LEN, m, len);
+#else
ret = (SHA512((const unsigned char*)m,len,(unsigned char*)digest)
!= NULL);
- else
+#endif
+ } else {
ret = (sha3_512((uint8_t*)digest, DIGEST512_LEN, (const uint8_t*)m, len)
> -1);
+ }
if (!ret)
return -1;
@@ -181,9 +275,13 @@ struct crypto_digest_t {
* that space for other members might not even be allocated!
*/
union {
+#ifdef ENABLE_NSS
+ PK11Context *ctx;
+#else
SHA_CTX sha1; /**< state for SHA1 */
SHA256_CTX sha2; /**< state for SHA256 */
SHA512_CTX sha512; /**< state for SHA512 */
+#endif
keccak_state sha3; /**< state for SHA3-[256,512] */
} d;
};
@@ -214,12 +312,19 @@ crypto_digest_alloc_bytes(digest_algorithm_t alg)
#define END_OF_FIELD(f) (offsetof(crypto_digest_t, f) + \
STRUCT_FIELD_SIZE(crypto_digest_t, f))
switch (alg) {
+#ifdef ENABLE_NSS
+ case DIGEST_SHA1: /* Fall through */
+ case DIGEST_SHA256: /* Fall through */
+ case DIGEST_SHA512:
+ return END_OF_FIELD(d.ctx);
+#else
case DIGEST_SHA1:
return END_OF_FIELD(d.sha1);
case DIGEST_SHA256:
return END_OF_FIELD(d.sha2);
case DIGEST_SHA512:
return END_OF_FIELD(d.sha512);
+#endif
case DIGEST_SHA3_256:
case DIGEST_SHA3_512:
return END_OF_FIELD(d.sha3);
@@ -243,6 +348,21 @@ crypto_digest_new_internal(digest_algorithm_t algorithm)
switch (algorithm)
{
+#ifdef ENABLE_NSS
+ case DIGEST_SHA1: /* fall through */
+ case DIGEST_SHA256: /* fall through */
+ case DIGEST_SHA512:
+ r->d.ctx = PK11_CreateDigestContext(digest_alg_to_nss_oid(algorithm));
+ if (BUG(!r->d.ctx)) {
+ tor_free(r);
+ return NULL;
+ }
+ if (BUG(SECSuccess != PK11_DigestBegin(r->d.ctx))) {
+ crypto_digest_free(r);
+ return NULL;
+ }
+ break;
+#else
case DIGEST_SHA1:
SHA1_Init(&r->d.sha1);
break;
@@ -252,6 +372,7 @@ crypto_digest_new_internal(digest_algorithm_t algorithm)
case DIGEST_SHA512:
SHA512_Init(&r->d.sha512);
break;
+#endif
case DIGEST_SHA3_256:
keccak_digest_init(&r->d.sha3, 256);
break;
@@ -302,6 +423,11 @@ crypto_digest_free_(crypto_digest_t *digest)
{
if (!digest)
return;
+#ifdef ENABLE_NSS
+ if (library_supports_digest(digest->algorithm)) {
+ PK11_DestroyContext(digest->d.ctx, PR_TRUE);
+ }
+#endif
size_t bytes = crypto_digest_alloc_bytes(digest->algorithm);
memwipe(digest, 0, bytes);
tor_free(digest);
@@ -324,6 +450,17 @@ crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
* just doing it ourselves. Hashes are fast.
*/
switch (digest->algorithm) {
+#ifdef ENABLE_NSS
+ case DIGEST_SHA1: /* fall through */
+ case DIGEST_SHA256: /* fall through */
+ case DIGEST_SHA512:
+ tor_assert(len <= UINT_MAX);
+ SECStatus s = PK11_DigestOp(digest->d.ctx,
+ (const unsigned char *)data,
+ (unsigned int)len);
+ tor_assert(s == SECSuccess);
+ break;
+#else
case DIGEST_SHA1:
SHA1_Update(&digest->d.sha1, (void*)data, len);
break;
@@ -333,6 +470,7 @@ crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
case DIGEST_SHA512:
SHA512_Update(&digest->d.sha512, (void*)data, len);
break;
+#endif
case DIGEST_SHA3_256: /* FALLSTHROUGH */
case DIGEST_SHA3_512:
keccak_digest_update(&digest->d.sha3, (const uint8_t *)data, len);
@@ -357,7 +495,6 @@ crypto_digest_get_digest(crypto_digest_t *digest,
char *out, size_t out_len)
{
unsigned char r[DIGEST512_LEN];
- crypto_digest_t tmpenv;
tor_assert(digest);
tor_assert(out);
tor_assert(out_len <= crypto_digest_algorithm_get_length(digest->algorithm));
@@ -370,7 +507,26 @@ crypto_digest_get_digest(crypto_digest_t *digest,
return;
}
+#ifdef ENABLE_NSS
+ /* Copy into a temporary buffer since DigestFinal (alters) the context */
+ unsigned char buf[1024];
+ unsigned int saved_len = 0;
+ unsigned rlen;
+ unsigned char *saved = PK11_SaveContextAlloc(digest->d.ctx,
+ buf, sizeof(buf),
+ &saved_len);
+ tor_assert(saved);
+ SECStatus s = PK11_DigestFinal(digest->d.ctx, r, &rlen, sizeof(r));
+ tor_assert(s == SECSuccess);
+ tor_assert(rlen >= out_len);
+ s = PK11_RestoreContext(digest->d.ctx, saved, saved_len);
+ tor_assert(s == SECSuccess);
+ if (saved != buf) {
+ PORT_ZFree(saved, saved_len);
+ }
+#else
const size_t alloc_bytes = crypto_digest_alloc_bytes(digest->algorithm);
+ crypto_digest_t tmpenv;
/* memcpy into a temporary ctx, since SHA*_Final clears the context */
memcpy(&tmpenv, digest, alloc_bytes);
switch (digest->algorithm) {
@@ -393,6 +549,7 @@ crypto_digest_get_digest(crypto_digest_t *digest,
break;
//LCOV_EXCL_STOP
}
+#endif
memcpy(out, r, out_len);
memwipe(r, 0, sizeof(r));
}
@@ -408,7 +565,13 @@ crypto_digest_dup(const crypto_digest_t *digest)
{
tor_assert(digest);
const size_t alloc_bytes = crypto_digest_alloc_bytes(digest->algorithm);
- return tor_memdup(digest, alloc_bytes);
+ crypto_digest_t *result = tor_memdup(digest, alloc_bytes);
+#ifdef ENABLE_NSS
+ if (library_supports_digest(digest->algorithm)) {
+ result->d.ctx = PK11_CloneContext(digest->d.ctx);
+ }
+#endif
+ return result;
}
/** Temporarily save the state of <b>digest</b> in <b>checkpoint</b>.
@@ -420,6 +583,18 @@ crypto_digest_checkpoint(crypto_digest_checkpoint_t *checkpoint,
{
const size_t bytes = crypto_digest_alloc_bytes(digest->algorithm);
tor_assert(bytes <= sizeof(checkpoint->mem));
+#ifdef ENABLE_NSS
+ if (library_supports_digest(digest->algorithm)) {
+ unsigned char *allocated;
+ allocated = PK11_SaveContextAlloc(digest->d.ctx,
+ (unsigned char *)checkpoint->mem,
+ sizeof(checkpoint->mem),
+ &checkpoint->bytes_used);
+ /* No allocation is allowed here. */
+ tor_assert(allocated == checkpoint->mem);
+ return;
+ }
+#endif
memcpy(checkpoint->mem, digest, bytes);
}
@@ -431,6 +606,15 @@ crypto_digest_restore(crypto_digest_t *digest,
const crypto_digest_checkpoint_t *checkpoint)
{
const size_t bytes = crypto_digest_alloc_bytes(digest->algorithm);
+#ifdef ENABLE_NSS
+ if (library_supports_digest(digest->algorithm)) {
+ SECStatus s = PK11_RestoreContext(digest->d.ctx,
+ (unsigned char *)checkpoint->mem,
+ checkpoint->bytes_used);
+ tor_assert(s == SECSuccess);
+ return;
+ }
+#endif
memcpy(digest, checkpoint->mem, bytes);
}
@@ -446,6 +630,13 @@ crypto_digest_assign(crypto_digest_t *into,
tor_assert(from);
tor_assert(into->algorithm == from->algorithm);
const size_t alloc_bytes = crypto_digest_alloc_bytes(from->algorithm);
+#ifdef ENABLE_NSS
+ if (library_supports_digest(from->algorithm)) {
+ PK11_DestroyContext(into->d.ctx, PR_TRUE);
+ into->d.ctx = PK11_CloneContext(from->d.ctx);
+ return;
+ }
+#endif
memcpy(into,from,alloc_bytes);
}
@@ -496,14 +687,63 @@ crypto_hmac_sha256(char *hmac_out,
const char *key, size_t key_len,
const char *msg, size_t msg_len)
{
- unsigned char *rv = NULL;
/* If we've got OpenSSL >=0.9.8 we can use its hmac implementation. */
tor_assert(key_len < INT_MAX);
tor_assert(msg_len < INT_MAX);
tor_assert(hmac_out);
+#ifdef ENABLE_NSS
+ PK11SlotInfo *slot = NULL;
+ PK11SymKey *symKey = NULL;
+ PK11Context *hmac = NULL;
+
+ int ok = 0;
+ SECStatus s;
+ SECItem keyItem, paramItem;
+ keyItem.data = (unsigned char *)key;
+ keyItem.len = (unsigned)key_len;
+ paramItem.type = siBuffer;
+ paramItem.data = NULL;
+ paramItem.len = 0;
+
+ slot = PK11_GetBestSlot(CKM_SHA256_HMAC, NULL);
+ if (!slot)
+ goto done;
+ symKey = PK11_ImportSymKey(slot, CKM_SHA256_HMAC,
+ PK11_OriginUnwrap, CKA_SIGN, &keyItem, NULL);
+ if (!symKey)
+ goto done;
+
+ hmac = PK11_CreateContextBySymKey(CKM_SHA256_HMAC, CKA_SIGN, symKey,
+ &paramItem);
+ if (!hmac)
+ goto done;
+ s = PK11_DigestBegin(hmac);
+ if (s != SECSuccess)
+ goto done;
+ s = PK11_DigestOp(hmac, (const unsigned char *)msg, (unsigned int)msg_len);
+ if (s != SECSuccess)
+ goto done;
+ unsigned int len=0;
+ s = PK11_DigestFinal(hmac, (unsigned char *)hmac_out, &len, DIGEST256_LEN);
+ if (s != SECSuccess || len != DIGEST256_LEN)
+ goto done;
+ ok = 1;
+
+ done:
+ if (hmac)
+ PK11_DestroyContext(hmac, PR_TRUE);
+ if (symKey)
+ PK11_FreeSymKey(symKey);
+ if (slot)
+ PK11_FreeSlot(slot);
+
+ tor_assert(ok);
+#else
+ unsigned char *rv = NULL;
rv = HMAC(EVP_sha256(), key, (int)key_len, (unsigned char*)msg, (int)msg_len,
(unsigned char*)hmac_out, NULL);
tor_assert(rv);
+#endif
}
/** Compute a MAC using SHA3-256 of <b>msg_len</b> bytes in <b>msg</b> using a
diff --git a/src/lib/crypt_ops/crypto_digest.h b/src/lib/crypt_ops/crypto_digest.h
index 9facf3b981..204f1aaff3 100644
--- a/src/lib/crypt_ops/crypto_digest.h
+++ b/src/lib/crypt_ops/crypto_digest.h
@@ -16,6 +16,7 @@
#include "lib/cc/torint.h"
#include "lib/defs/digest_sizes.h"
#include "lib/malloc/malloc.h"
+#include "lib/testsupport/testsupport.h"
/** Length of a sha1 message digest when encoded in base32 with trailing =
* signs removed. */
@@ -51,6 +52,9 @@ typedef enum {
/** Structure used to temporarily save the a digest object. Only implemented
* for SHA1 digest for now. */
typedef struct crypto_digest_checkpoint_t {
+#ifdef ENABLE_NSS
+ unsigned int bytes_used;
+#endif
uint8_t mem[DIGEST_CHECKPOINT_BYTES];
} crypto_digest_checkpoint_t;
@@ -72,7 +76,7 @@ typedef struct crypto_xof_t crypto_xof_t;
struct smartlist_t;
/* SHA-1 and other digests */
-int crypto_digest(char *digest, const char *m, size_t len);
+MOCK_DECL(int, crypto_digest,(char *digest, const char *m, size_t len));
int crypto_digest256(char *digest, const char *m, size_t len,
digest_algorithm_t algorithm);
int crypto_digest512(char *digest, const char *m, size_t len,
diff --git a/src/lib/crypt_ops/crypto_ed25519.c b/src/lib/crypt_ops/crypto_ed25519.c
index 9d2c9e9fab..11c1f56aef 100644
--- a/src/lib/crypt_ops/crypto_ed25519.c
+++ b/src/lib/crypt_ops/crypto_ed25519.c
@@ -37,6 +37,7 @@
#include "ed25519/donna/ed25519_donna_tor.h"
#include <string.h>
+#include <errno.h>
static void pick_ed25519_impl(void);
diff --git a/src/lib/crypt_ops/crypto_format.c b/src/lib/crypt_ops/crypto_format.c
index 50916a8d68..09ec753a00 100644
--- a/src/lib/crypt_ops/crypto_format.c
+++ b/src/lib/crypt_ops/crypto_format.c
@@ -29,6 +29,7 @@
#include "lib/fs/files.h"
#include <string.h>
+#include <errno.h>
/** Write the <b>datalen</b> bytes from <b>data</b> to the file named
* <b>fname</b> in the tagged-data format. This format contains a
diff --git a/src/lib/crypt_ops/crypto_hkdf.c b/src/lib/crypt_ops/crypto_hkdf.c
index 1873632a9d..a63d9131d9 100644
--- a/src/lib/crypt_ops/crypto_hkdf.c
+++ b/src/lib/crypt_ops/crypto_hkdf.c
@@ -17,12 +17,14 @@
#include "lib/intmath/cmp.h"
#include "lib/log/util_bug.h"
+#ifdef ENABLE_OPENSSL
#include <openssl/opensslv.h>
#if defined(HAVE_ERR_LOAD_KDF_STRINGS)
#include <openssl/kdf.h>
#define HAVE_OPENSSL_HKDF 1
#endif
+#endif
#include <string.h>
diff --git a/src/lib/crypt_ops/crypto_init.c b/src/lib/crypt_ops/crypto_init.c
new file mode 100644
index 0000000000..c731662d49
--- /dev/null
+++ b/src/lib/crypt_ops/crypto_init.c
@@ -0,0 +1,193 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file crypto_init.c
+ *
+ * \brief Initialize and shut down Tor's crypto library and subsystem.
+ **/
+
+#include "orconfig.h"
+
+#include "lib/crypt_ops/crypto_init.h"
+
+#include "lib/crypt_ops/crypto_curve25519.h"
+#include "lib/crypt_ops/crypto_dh.h"
+#include "lib/crypt_ops/crypto_ed25519.h"
+#include "lib/crypt_ops/crypto_openssl_mgt.h"
+#include "lib/crypt_ops/crypto_nss_mgt.h"
+#include "lib/crypt_ops/crypto_rand.h"
+
+#include "siphash.h"
+
+/** Boolean: has our crypto library been initialized? (early phase) */
+static int crypto_early_initialized_ = 0;
+
+/** Boolean: has our crypto library been initialized? (late phase) */
+static int crypto_global_initialized_ = 0;
+
+static int have_seeded_siphash = 0;
+
+/** Set up the siphash key if we haven't already done so. */
+int
+crypto_init_siphash_key(void)
+{
+ struct sipkey key;
+ if (have_seeded_siphash)
+ return 0;
+
+ crypto_rand((char*) &key, sizeof(key));
+ siphash_set_global_key(&key);
+ have_seeded_siphash = 1;
+ return 0;
+}
+
+/** Initialize the crypto library. Return 0 on success, -1 on failure.
+ */
+int
+crypto_early_init(void)
+{
+ if (!crypto_early_initialized_) {
+
+ crypto_early_initialized_ = 1;
+
+#ifdef ENABLE_OPENSSL
+ crypto_openssl_early_init();
+#endif
+#ifdef ENABLE_NSS
+ crypto_nss_early_init(0);
+#endif
+
+ if (crypto_seed_rng() < 0)
+ return -1;
+ if (crypto_init_siphash_key() < 0)
+ return -1;
+
+ curve25519_init();
+ ed25519_init();
+ }
+ return 0;
+}
+
+/** Initialize the crypto library. Return 0 on success, -1 on failure.
+ */
+int
+crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
+{
+ if (!crypto_global_initialized_) {
+ if (crypto_early_init() < 0)
+ return -1;
+
+ crypto_global_initialized_ = 1;
+
+ crypto_dh_init();
+
+#ifdef ENABLE_OPENSSL
+ if (crypto_openssl_late_init(useAccel, accelName, accelDir) < 0)
+ return -1;
+#else
+ (void)useAccel;
+ (void)accelName;
+ (void)accelDir;
+#endif
+#ifdef ENABLE_NSS
+ if (crypto_nss_late_init() < 0)
+ return -1;
+#endif
+ }
+ return 0;
+}
+
+/** Free crypto resources held by this thread. */
+void
+crypto_thread_cleanup(void)
+{
+#ifdef ENABLE_OPENSSL
+ crypto_openssl_thread_cleanup();
+#endif
+}
+
+/**
+ * Uninitialize the crypto library. Return 0 on success. Does not detect
+ * failure.
+ */
+int
+crypto_global_cleanup(void)
+{
+ crypto_dh_free_all();
+
+#ifdef ENABLE_OPENSSL
+ crypto_openssl_global_cleanup();
+#endif
+#ifdef ENABLE_NSS
+ crypto_nss_global_cleanup();
+#endif
+
+ crypto_early_initialized_ = 0;
+ crypto_global_initialized_ = 0;
+ have_seeded_siphash = 0;
+ siphash_unset_global_key();
+
+ return 0;
+}
+
+/** Run operations that the crypto library requires to be happy again
+ * after forking. */
+void
+crypto_prefork(void)
+{
+#ifdef ENABLE_NSS
+ crypto_nss_prefork();
+#endif
+}
+
+/** Run operations that the crypto library requires to be happy again
+ * after forking. */
+void
+crypto_postfork(void)
+{
+#ifdef ENABLE_NSS
+ crypto_nss_postfork();
+#endif
+}
+
+/** Return the name of the crypto library we're using. */
+const char *
+crypto_get_library_name(void)
+{
+#ifdef ENABLE_OPENSSL
+ return "OpenSSL";
+#endif
+#ifdef ENABLE_NSS
+ return "NSS";
+#endif
+}
+
+/** Return the version of the crypto library we are using, as given in the
+ * library. */
+const char *
+crypto_get_library_version_string(void)
+{
+#ifdef ENABLE_OPENSSL
+ return crypto_openssl_get_version_str();
+#endif
+#ifdef ENABLE_NSS
+ return crypto_nss_get_version_str();
+#endif
+}
+
+/** Return the version of the crypto library we're using, as given in the
+ * headers. */
+const char *
+crypto_get_header_version_string(void)
+{
+#ifdef ENABLE_OPENSSL
+ return crypto_openssl_get_header_version_str();
+#endif
+#ifdef ENABLE_NSS
+ return crypto_nss_get_header_version_str();
+#endif
+}
diff --git a/src/lib/crypt_ops/crypto_init.h b/src/lib/crypt_ops/crypto_init.h
new file mode 100644
index 0000000000..5b6d65d48c
--- /dev/null
+++ b/src/lib/crypt_ops/crypto_init.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file crypto_init.h
+ *
+ * \brief Headers for crypto_init.c
+ **/
+
+#ifndef TOR_CRYPTO_INIT_H
+#define TOR_CRYPTO_INIT_H
+
+#include "orconfig.h"
+#include "lib/cc/compat_compiler.h"
+
+int crypto_init_siphash_key(void);
+int crypto_early_init(void) ATTR_WUR;
+int crypto_global_init(int hardwareAccel,
+ const char *accelName,
+ const char *accelPath) ATTR_WUR;
+
+void crypto_thread_cleanup(void);
+int crypto_global_cleanup(void);
+void crypto_prefork(void);
+void crypto_postfork(void);
+
+const char *crypto_get_library_name(void);
+const char *crypto_get_library_version_string(void);
+const char *crypto_get_header_version_string(void);
+
+#endif /* !defined(TOR_CRYPTO_H) */
diff --git a/src/lib/crypt_ops/crypto_nss_mgt.c b/src/lib/crypt_ops/crypto_nss_mgt.c
new file mode 100644
index 0000000000..a1da74aff5
--- /dev/null
+++ b/src/lib/crypt_ops/crypto_nss_mgt.c
@@ -0,0 +1,132 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file crypto_nss_mgt.c
+ *
+ * \brief Manage the NSS library (if used)
+ **/
+
+#include "lib/crypt_ops/crypto_nss_mgt.h"
+
+#include "lib/log/log.h"
+#include "lib/log/util_bug.h"
+#include "lib/string/printf.h"
+
+DISABLE_GCC_WARNING(strict-prototypes)
+#include <nss.h>
+#include <pk11func.h>
+#include <ssl.h>
+
+#include <prerror.h>
+#include <prtypes.h>
+#include <prinit.h>
+ENABLE_GCC_WARNING(strict-prototypes)
+
+const char *
+crypto_nss_get_version_str(void)
+{
+ return NSS_GetVersion();
+}
+const char *
+crypto_nss_get_header_version_str(void)
+{
+ return NSS_VERSION;
+}
+
+/** A password function that always returns NULL. */
+static char *
+nss_password_func_always_fail(PK11SlotInfo *slot,
+ PRBool retry,
+ void *arg)
+{
+ (void) slot;
+ (void) retry;
+ (void) arg;
+ return NULL;
+}
+
+void
+crypto_nss_early_init(int nss_only)
+{
+ if (! nss_only) {
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+ PK11_SetPasswordFunc(nss_password_func_always_fail);
+ }
+
+ /* Eventually we should use NSS_Init() instead -- but that wants a
+ directory. The documentation says that we can't use this if we want
+ to use OpenSSL. */
+ if (NSS_NoDB_Init(NULL) == SECFailure) {
+ log_err(LD_CRYPTO, "Unable to initialize NSS.");
+ crypto_nss_log_errors(LOG_ERR, "initializing NSS");
+ tor_assert_unreached();
+ }
+
+ if (NSS_SetDomesticPolicy() == SECFailure) {
+ log_err(LD_CRYPTO, "Unable to set NSS cipher policy.");
+ crypto_nss_log_errors(LOG_ERR, "setting cipher policy");
+ tor_assert_unreached();
+ }
+
+ /* We need to override the default here, or NSS will reject all the
+ * legacy Tor certificates. */
+ SECStatus rv = NSS_OptionSet(NSS_RSA_MIN_KEY_SIZE, 1024);
+ if (rv != SECSuccess) {
+ log_err(LD_CRYPTO, "Unable to set NSS min RSA key size");
+ crypto_nss_log_errors(LOG_ERR, "setting cipher option.");
+ tor_assert_unreached();
+ }
+}
+
+void
+crypto_nss_log_errors(int severity, const char *doing)
+{
+ PRErrorCode code = PR_GetError();
+ const char *string = PORT_ErrorToString(code);
+ const char *name = PORT_ErrorToName(code);
+ char buf[16];
+ if (!string)
+ string = "<unrecognized>";
+ if (!name) {
+ tor_snprintf(buf, sizeof(buf), "%d", code);
+ name = buf;
+ }
+ if (doing) {
+ tor_log(severity, LD_CRYPTO, "NSS error %s while %s: %s",
+ name, doing, string);
+ } else {
+ tor_log(severity, LD_CRYPTO, "NSS error %s: %s", name, string);
+ }
+}
+
+int
+crypto_nss_late_init(void)
+{
+ /* Possibly, SSL_OptionSetDefault? */
+
+ return 0;
+}
+
+void
+crypto_nss_global_cleanup(void)
+{
+ NSS_Shutdown();
+ PL_ArenaFinish();
+ PR_Cleanup();
+}
+
+void
+crypto_nss_prefork(void)
+{
+ NSS_Shutdown();
+}
+
+void
+crypto_nss_postfork(void)
+{
+ crypto_nss_early_init(1);
+}
diff --git a/src/lib/crypt_ops/crypto_nss_mgt.h b/src/lib/crypt_ops/crypto_nss_mgt.h
new file mode 100644
index 0000000000..27793dcc45
--- /dev/null
+++ b/src/lib/crypt_ops/crypto_nss_mgt.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file crypto_nss_mgt.h
+ *
+ * \brief Headers for crypto_nss_mgt.c
+ **/
+
+#ifndef TOR_CRYPTO_NSS_MGT_H
+#define TOR_CRYPTO_NSS_MGT_H
+
+#include "orconfig.h"
+
+#ifdef ENABLE_NSS
+/* global nss state */
+const char *crypto_nss_get_version_str(void);
+const char *crypto_nss_get_header_version_str(void);
+
+void crypto_nss_log_errors(int severity, const char *doing);
+
+void crypto_nss_early_init(int nss_only);
+int crypto_nss_late_init(void);
+
+void crypto_nss_global_cleanup(void);
+
+void crypto_nss_prefork(void);
+void crypto_nss_postfork(void);
+#endif
+
+#endif /* !defined(TOR_CRYPTO_NSS_H) */
diff --git a/src/lib/crypt_ops/crypto_ope.c b/src/lib/crypt_ops/crypto_ope.c
index ca42ae4341..fd5d5f3770 100644
--- a/src/lib/crypt_ops/crypto_ope.c
+++ b/src/lib/crypt_ops/crypto_ope.c
@@ -18,8 +18,8 @@
#define CRYPTO_OPE_PRIVATE
#include "lib/crypt_ops/crypto_ope.h"
-#include "lib/crypt_ops/crypto.h"
#include "lib/crypt_ops/crypto_util.h"
+#include "lib/crypt_ops/crypto_cipher.h"
#include "lib/log/util_bug.h"
#include "lib/malloc/malloc.h"
#include "lib/arch/bytes.h"
diff --git a/src/lib/crypt_ops/crypto_openssl_mgt.c b/src/lib/crypt_ops/crypto_openssl_mgt.c
index 01de6a9d9e..125da0786b 100644
--- a/src/lib/crypt_ops/crypto_openssl_mgt.c
+++ b/src/lib/crypt_ops/crypto_openssl_mgt.c
@@ -12,8 +12,12 @@
#include "lib/crypt_ops/compat_openssl.h"
#include "lib/crypt_ops/crypto_openssl_mgt.h"
+#include "lib/crypt_ops/crypto_rand.h"
+#include "lib/crypt_ops/aes.h"
#include "lib/string/util_string.h"
#include "lib/lock/compat_mutex.h"
+#include "lib/log/log.h"
+#include "lib/log/util_bug.h"
#include "lib/testsupport/testsupport.h"
#include "lib/thread/threads.h"
@@ -30,6 +34,7 @@ DISABLE_GCC_WARNING(redundant-decls)
#include <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();
+}
diff --git a/src/lib/crypt_ops/crypto_openssl_mgt.h b/src/lib/crypt_ops/crypto_openssl_mgt.h
index a2c53302e1..3b288fb9d8 100644
--- a/src/lib/crypt_ops/crypto_openssl_mgt.h
+++ b/src/lib/crypt_ops/crypto_openssl_mgt.h
@@ -14,6 +14,8 @@
#define TOR_CRYPTO_OPENSSL_H
#include "orconfig.h"
+
+#ifdef ENABLE_OPENSSL
#include <openssl/engine.h>
/*
@@ -69,14 +71,19 @@
#define NEW_THREAD_API
#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) && ... */
+void crypto_openssl_log_errors(int severity, const char *doing);
+
/* global openssl state */
const char * crypto_openssl_get_version_str(void);
const char * crypto_openssl_get_header_version_str(void);
-/* OpenSSL threading setup function */
-int setup_openssl_threading(void);
+void crypto_openssl_early_init(void);
+int crypto_openssl_late_init(int useAccel, const char *accelName,
+ const char *accelDir);
+
+void crypto_openssl_thread_cleanup(void);
+void crypto_openssl_global_cleanup(void);
-/* Tor OpenSSL utility functions */
-void crypto_openssl_free_all(void);
+#endif /* ENABLE_OPENSSL */
#endif /* !defined(TOR_CRYPTO_OPENSSL_H) */
diff --git a/src/lib/crypt_ops/crypto_pwbox.c b/src/lib/crypt_ops/crypto_pwbox.c
index c001e295da..2377f216a0 100644
--- a/src/lib/crypt_ops/crypto_pwbox.c
+++ b/src/lib/crypt_ops/crypto_pwbox.c
@@ -11,7 +11,7 @@
#include <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"
diff --git a/src/lib/crypt_ops/crypto_rand.c b/src/lib/crypt_ops/crypto_rand.c
index fb9d0c2c6c..78471bf398 100644
--- a/src/lib/crypt_ops/crypto_rand.c
+++ b/src/lib/crypt_ops/crypto_rand.c
@@ -35,9 +35,24 @@
#include "lib/testsupport/testsupport.h"
#include "lib/fs/files.h"
+#include "lib/defs/digest_sizes.h"
+#include "lib/crypt_ops/crypto_digest.h"
+
+#ifdef ENABLE_NSS
+#include "lib/crypt_ops/crypto_nss_mgt.h"
+#endif
+
+#ifdef ENABLE_OPENSSL
DISABLE_GCC_WARNING(redundant-decls)
#include <openssl/rand.h>
ENABLE_GCC_WARNING(redundant-decls)
+#endif
+
+#ifdef ENABLE_NSS
+#include <pk11pub.h>
+#include <secerr.h>
+#include <prerror.h>
+#endif
#if __GNUC__ && GCC_VERSION >= 402
#if GCC_VERSION >= 406
@@ -67,6 +82,7 @@ ENABLE_GCC_WARNING(redundant-decls)
#endif
#include <string.h>
+#include <errno.h>
/**
* How many bytes of entropy we add at once.
@@ -322,16 +338,24 @@ crypto_strongest_rand_raw(uint8_t *out, size_t out_len)
void
crypto_strongest_rand(uint8_t *out, size_t out_len)
{
-#define DLEN SHA512_DIGEST_LENGTH
+#define DLEN DIGEST512_LEN
+
/* We're going to hash DLEN bytes from the system RNG together with some
- * bytes from the openssl PRNG, in order to yield DLEN bytes.
+ * bytes from the PRNGs from our crypto librar(y/ies), in order to yield
+ * DLEN bytes.
*/
- uint8_t inp[DLEN*2];
+ uint8_t inp[DLEN*3];
uint8_t tmp[DLEN];
tor_assert(out);
while (out_len) {
- crypto_rand((char*) inp, DLEN);
- if (crypto_strongest_rand_raw(inp+DLEN, DLEN) < 0) {
+ memset(inp, 0, sizeof(inp));
+#ifdef ENABLE_OPENSSL
+ RAND_bytes(inp, DLEN);
+#endif
+#ifdef ENABLE_NSS
+ PK11_GenerateRandom(inp+DLEN, DLEN);
+#endif
+ if (crypto_strongest_rand_raw(inp+DLEN*2, DLEN) < 0) {
// LCOV_EXCL_START
log_err(LD_CRYPTO, "Failed to load strong entropy when generating an "
"important key. Exiting.");
@@ -340,11 +364,11 @@ crypto_strongest_rand(uint8_t *out, size_t out_len)
// LCOV_EXCL_STOP
}
if (out_len >= DLEN) {
- SHA512(inp, sizeof(inp), out);
+ crypto_digest512((char*)out, (char*)inp, sizeof(inp), DIGEST_SHA512);
out += DLEN;
out_len -= DLEN;
} else {
- SHA512(inp, sizeof(inp), tmp);
+ crypto_digest512((char*)tmp, (char*)inp, sizeof(inp), DIGEST_SHA512);
memcpy(out, tmp, out_len);
break;
}
@@ -354,12 +378,13 @@ crypto_strongest_rand(uint8_t *out, size_t out_len)
#undef DLEN
}
+#ifdef ENABLE_OPENSSL
/**
* Seed OpenSSL's random number generator with bytes from the operating
* system. Return 0 on success, -1 on failure.
**/
-int
-crypto_seed_rng(void)
+static int
+crypto_seed_openssl_rng(void)
{
int rand_poll_ok = 0, load_entropy_ok = 0;
uint8_t buf[ADD_ENTROPY];
@@ -383,6 +408,52 @@ crypto_seed_rng(void)
else
return -1;
}
+#endif
+
+#ifdef ENABLE_NSS
+/**
+ * Seed OpenSSL's random number generator with bytes from the operating
+ * system. Return 0 on success, -1 on failure.
+ **/
+static int
+crypto_seed_nss_rng(void)
+{
+ uint8_t buf[ADD_ENTROPY];
+
+ int load_entropy_ok = !crypto_strongest_rand_raw(buf, sizeof(buf));
+ if (load_entropy_ok) {
+ if (PK11_RandomUpdate(buf, sizeof(buf)) != SECSuccess) {
+ load_entropy_ok = 0;
+ }
+ }
+
+ memwipe(buf, 0, sizeof(buf));
+
+ return load_entropy_ok ? 0 : -1;
+}
+#endif
+
+/**
+ * Seed the RNG for any and all crypto libraries that we're using with bytes
+ * from the operating system. Return 0 on success, -1 on failure.
+ */
+int
+crypto_seed_rng(void)
+{
+ int seeded = 0;
+#ifdef ENABLE_NSS
+ if (crypto_seed_nss_rng() < 0)
+ return -1;
+ ++seeded;
+#endif
+#ifdef ENABLE_OPENSSL
+ if (crypto_seed_openssl_rng() < 0)
+ return -1;
+ ++seeded;
+#endif
+ tor_assert(seeded);
+ return 0;
+}
/**
* Write <b>n</b> bytes of strong random data to <b>to</b>. Supports mocking
@@ -407,17 +478,44 @@ crypto_rand, (char *to, size_t n))
void
crypto_rand_unmocked(char *to, size_t n)
{
- int r;
if (n == 0)
return;
tor_assert(n < INT_MAX);
tor_assert(to);
- r = RAND_bytes((unsigned char*)to, (int)n);
+
+#ifdef ENABLE_NSS
+ SECStatus s = PK11_GenerateRandom((unsigned char*)to, (int)n);
+ if (s != SECSuccess) {
+ /* NSS rather sensibly might refuse to generate huge amounts of random
+ * data at once. Unfortunately, our unit test do this in a couple of
+ * places. To solve this issue, we use our XOF to stretch a shorter
+ * output when a longer one is needed.
+ *
+ * Yes, this is secure. */
+
+ /* This is longer than it needs to be; 1600 bits == 200 bytes is the
+ * state-size of SHA3. */
+#define BUFLEN 512
+ tor_assert(PR_GetError() == SEC_ERROR_INVALID_ARGS && n > BUFLEN);
+ unsigned char buf[BUFLEN];
+ s = PK11_GenerateRandom(buf, BUFLEN);
+ tor_assert(s == SECSuccess);
+ crypto_xof_t *xof = crypto_xof_new();
+ crypto_xof_add_bytes(xof, buf, BUFLEN);
+ crypto_xof_squeeze_bytes(xof, (unsigned char *)to, n);
+ crypto_xof_free(xof);
+ memwipe(buf, 0, BUFLEN);
+
+#undef BUFLEN
+ }
+#else
+ int r = RAND_bytes((unsigned char*)to, (int)n);
/* We consider a PRNG failure non-survivable. Let's assert so that we get a
* stack trace about where it happened.
*/
tor_assert(r >= 0);
+#endif
}
/**
@@ -605,6 +703,7 @@ smartlist_shuffle(smartlist_t *sl)
int
crypto_force_rand_ssleay(void)
{
+#ifdef ENABLE_OPENSSL
RAND_METHOD *default_method;
default_method = RAND_OpenSSL();
if (RAND_get_rand_method() != default_method) {
@@ -614,6 +713,7 @@ crypto_force_rand_ssleay(void)
RAND_set_rand_method(default_method);
return 1;
}
+#endif
return 0;
}
diff --git a/src/lib/crypt_ops/crypto_rsa.c b/src/lib/crypt_ops/crypto_rsa.c
index 5ec69d7319..6a9e2948f1 100644
--- a/src/lib/crypt_ops/crypto_rsa.c
+++ b/src/lib/crypt_ops/crypto_rsa.c
@@ -9,7 +9,7 @@
* \brief Block of functions related with RSA utilities and operations.
**/
-#include "lib/crypt_ops/crypto.h"
+#include "lib/crypt_ops/crypto_cipher.h"
#include "lib/crypt_ops/crypto_curve25519.h"
#include "lib/crypt_ops/crypto_digest.h"
#include "lib/crypt_ops/crypto_format.h"
@@ -21,32 +21,14 @@
#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/dh.h>
-#include <openssl/conf.h>
-#include <openssl/hmac.h>
-
-ENABLE_GCC_WARNING(redundant-decls)
-
#include "lib/log/log.h"
#include "lib/encoding/binascii.h"
+#include "lib/encoding/pem.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 */
-};
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
/** Return the number of bytes added by padding method <b>padding</b>.
*/
@@ -55,11 +37,12 @@ crypto_get_rsa_padding_overhead(int padding)
{
switch (padding)
{
- case RSA_PKCS1_OAEP_PADDING: return PKCS1_OAEP_PADDING_OVERHEAD;
+ case PK_PKCS1_OAEP_PADDING: return PKCS1_OAEP_PADDING_OVERHEAD;
default: tor_assert(0); return -1; // LCOV_EXCL_LINE
}
}
+#ifdef ENABLE_OPENSSL
/** Given a padding method <b>padding</b>, return the correct OpenSSL constant.
*/
int
@@ -71,442 +54,7 @@ crypto_get_rsa_padding(int padding)
default: tor_assert(0); return -1; // LCOV_EXCL_LINE
}
}
-
-/** used internally: quicly validate a crypto_pk_t object as a private key.
- * Return 1 iff the public key is valid, 0 if obviously invalid.
- */
-static int
-crypto_pk_private_ok(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. */
-crypto_pk_t *
-crypto_new_pk_from_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 the RSA from a
- * crypto_pk_t. */
-RSA *
-crypto_pk_get_rsa_(crypto_pk_t *env)
-{
- return 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_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_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, 65537))
- 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_log_errors(LOG_WARN, "generating RSA key");
- return -1;
- }
-
- return 0;
-}
-
-/** A PEM callback that always reports a failure to get a password */
-static int
-pem_no_password_cb(char *buf, int size, int rwflag, void *u)
-{
- (void)buf;
- (void)size;
- (void)rwflag;
- (void)u;
- return -1;
-}
-
-/** Read a PEM-encoded private key from the <b>len</b>-byte string <b>s</b>
- * into <b>env</b>. Return 0 on success, -1 on failure. If len is -1,
- * the string is nul-terminated.
- */
-int
-crypto_pk_read_private_key_from_string(crypto_pk_t *env,
- const char *s, ssize_t len)
-{
- BIO *b;
-
- tor_assert(env);
- tor_assert(s);
- tor_assert(len < INT_MAX && len < SSIZE_T_CEILING);
-
- /* Create a read-only memory BIO, backed by the string 's' */
- b = BIO_new_mem_buf((char*)s, (int)len);
- if (!b)
- return -1;
-
- if (env->key)
- RSA_free(env->key);
-
- env->key = PEM_read_bio_RSAPrivateKey(b,NULL,pem_no_password_cb,NULL);
-
- BIO_free(b);
-
- if (!env->key) {
- crypto_log_errors(LOG_WARN, "Error parsing private key");
- return -1;
- }
- return 0;
-}
-
-/** Read a PEM-encoded private key from the file named by
- * <b>keyfile</b> into <b>env</b>. Return 0 on success, -1 on failure.
- */
-int
-crypto_pk_read_private_key_from_filename(crypto_pk_t *env,
- const char *keyfile)
-{
- char *contents;
- int r;
-
- /* Read the file into a string. */
- contents = read_file_to_str(keyfile, 0, NULL);
- if (!contents) {
- log_warn(LD_CRYPTO, "Error reading private key from \"%s\"", keyfile);
- return -1;
- }
-
- /* Try to parse it. */
- r = crypto_pk_read_private_key_from_string(env, contents, -1);
- memwipe(contents, 0, strlen(contents));
- tor_free(contents);
- if (r)
- return -1; /* read_private_key_from_string already warned, so we don't.*/
-
- /* Make sure it's valid. */
- if (crypto_pk_check_key(env) <= 0)
- return -1;
-
- return 0;
-}
-
-/** Helper function to implement crypto_pk_write_*_key_to_string. Return 0 on
- * success, -1 on failure. */
-static int
-crypto_pk_write_key_to_string_impl(crypto_pk_t *env, char **dest,
- size_t *len, int is_public)
-{
- BUF_MEM *buf;
- BIO *b;
- int r;
-
- tor_assert(env);
- tor_assert(env->key);
- tor_assert(dest);
-
- b = BIO_new(BIO_s_mem()); /* Create a memory BIO */
- if (!b)
- return -1;
-
- /* Now you can treat b as if it were a file. Just use the
- * PEM_*_bio_* functions instead of the non-bio variants.
- */
- if (is_public)
- r = PEM_write_bio_RSAPublicKey(b, env->key);
- else
- r = PEM_write_bio_RSAPrivateKey(b, env->key, NULL,NULL,0,NULL,NULL);
-
- if (!r) {
- crypto_log_errors(LOG_WARN, "writing RSA key to string");
- BIO_free(b);
- return -1;
- }
-
- BIO_get_mem_ptr(b, &buf);
-
- *dest = tor_malloc(buf->length+1);
- memcpy(*dest, buf->data, buf->length);
- (*dest)[buf->length] = 0; /* nul terminate it */
- *len = buf->length;
-
- BIO_free(b);
-
- return 0;
-}
-
-/** PEM-encode the public key portion of <b>env</b> and write it to a
- * newly allocated string. On success, set *<b>dest</b> to the new
- * string, *<b>len</b> to the string's length, and return 0. On
- * failure, return -1.
- */
-int
-crypto_pk_write_public_key_to_string(crypto_pk_t *env, char **dest,
- size_t *len)
-{
- return crypto_pk_write_key_to_string_impl(env, dest, len, 1);
-}
-
-/** PEM-encode the private key portion of <b>env</b> and write it to a
- * newly allocated string. On success, set *<b>dest</b> to the new
- * string, *<b>len</b> to the string's length, and return 0. On
- * failure, return -1.
- */
-int
-crypto_pk_write_private_key_to_string(crypto_pk_t *env, char **dest,
- size_t *len)
-{
- return crypto_pk_write_key_to_string_impl(env, dest, len, 0);
-}
-
-/** Read a PEM-encoded public key from the first <b>len</b> characters of
- * <b>src</b>, and store the result in <b>env</b>. Return 0 on success, -1 on
- * failure.
- */
-int
-crypto_pk_read_public_key_from_string(crypto_pk_t *env, const char *src,
- size_t len)
-{
- BIO *b;
-
- tor_assert(env);
- tor_assert(src);
- tor_assert(len<INT_MAX);
-
- b = BIO_new(BIO_s_mem()); /* Create a memory BIO */
- if (!b)
- return -1;
-
- BIO_write(b, src, (int)len);
-
- if (env->key)
- RSA_free(env->key);
- env->key = PEM_read_bio_RSAPublicKey(b, NULL, pem_no_password_cb, NULL);
- BIO_free(b);
- if (!env->key) {
- crypto_log_errors(LOG_WARN, "reading public key from string");
- return -1;
- }
-
- return 0;
-}
-
-/** Write the private key from <b>env</b> into the file named by <b>fname</b>,
- * PEM-encoded. Return 0 on success, -1 on failure.
- */
-int
-crypto_pk_write_private_key_to_filename(crypto_pk_t *env,
- const char *fname)
-{
- BIO *bio;
- char *cp;
- long len;
- char *s;
- int r;
-
- tor_assert(crypto_pk_private_ok(env));
-
- if (!(bio = BIO_new(BIO_s_mem())))
- return -1;
- if (PEM_write_bio_RSAPrivateKey(bio, env->key, NULL,NULL,0,NULL,NULL)
- == 0) {
- crypto_log_errors(LOG_WARN, "writing private key");
- BIO_free(bio);
- return -1;
- }
- len = BIO_get_mem_data(bio, &cp);
- tor_assert(len >= 0);
- s = tor_malloc(len+1);
- memcpy(s, cp, len);
- s[len]='\0';
- r = write_str_to_file(fname, s, 0);
- BIO_free(bio);
- memwipe(s, 0, strlen(s));
- tor_free(s);
- return r;
-}
-
-/** Return true iff <b>env</b> has a valid key.
- */
-int
-crypto_pk_check_key(crypto_pk_t *env)
-{
- int r;
- tor_assert(env);
-
- r = RSA_check_key(env->key);
- if (r <= 0)
- crypto_log_errors(LOG_WARN,"checking RSA key");
- return r;
-}
-
-/** 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)
-{
- tor_assert(key);
- return crypto_pk_private_ok(key);
-}
-
-/** Return true iff <b>env</b> contains a public key whose public exponent
- * equals 65537.
- */
-int
-crypto_pk_public_exponent_ok(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, 65537);
-}
-
-/** 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);
-}
+#endif
/** Compare the public-key components of a and b. Return non-zero iff
* a==b. A NULL key is considered to be distinct from all non-NULL
@@ -520,98 +68,6 @@ crypto_pk_eq_keys(const crypto_pk_t *a, const crypto_pk_t *b)
return (crypto_pk_cmp_keys(a, b) == 0);
}
-/** 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;
-}
-
-#ifdef TOR_UNIT_TESTS
-/** For testing: replace dest with src. (Dest must have a refcount
- * of 1) */
-void
-crypto_pk_assign_(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);
-}
-#endif /* defined(TOR_UNIT_TESTS) */
-
-/** 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_private_ok(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_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_rsa_(new_key);
-}
-
/** Perform a hybrid (public/secret) encryption on <b>fromlen</b>
* bytes of data from <b>from</b>, with padding type 'padding',
* storing the results on <b>to</b>.
@@ -646,7 +102,7 @@ crypto_pk_obsolete_public_hybrid_encrypt(crypto_pk_t *env,
tor_assert(to);
tor_assert(fromlen < SIZE_T_CEILING);
- overhead = crypto_get_rsa_padding_overhead(crypto_get_rsa_padding(padding));
+ overhead = crypto_get_rsa_padding_overhead(padding);
pkeylen = crypto_pk_keysize(env);
if (!force && fromlen+overhead <= pkeylen) {
@@ -754,179 +210,6 @@ crypto_pk_obsolete_private_hybrid_decrypt(crypto_pk_t *env,
return -1;
}
-/** 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_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_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_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_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_log_errors(LOG_WARN,"decoding public key");
- return NULL;
- }
- return crypto_new_pk_from_rsa_(rsa);
-}
-
/** Given a private or public key <b>pk</b>, put a fingerprint of the
* public key into <b>fp_out</b> (must have at least FINGERPRINT_LEN+1 bytes of
* space). Return 0 on success, -1 on failure.
@@ -976,6 +259,26 @@ crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out)
return 0;
}
+/** Copy <b>in</b> to the <b>outlen</b>-byte buffer <b>out</b>, adding spaces
+ * every four characters. */
+void
+crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in)
+{
+ int n = 0;
+ char *end = out+outlen;
+ tor_assert(outlen < SIZE_T_CEILING);
+
+ while (*in && out<end) {
+ *out++ = *in++;
+ if (++n == 4 && *in && out<end) {
+ n = 0;
+ *out++ = ' ';
+ }
+ }
+ tor_assert(out<end);
+ *out = '\0';
+}
+
/** Check a siglen-byte long signature at <b>sig</b> against
* <b>datalen</b> bytes of data at <b>data</b>, using the public key
* in <b>env</b>. Return 0 if <b>sig</b> is a correct signature for
@@ -1092,6 +395,188 @@ crypto_pk_get_common_digests(crypto_pk_t *pk, common_digests_t *digests_out)
return rv;
}
+static const char RSA_PUBLIC_TAG[] = "RSA PUBLIC KEY";
+static const char RSA_PRIVATE_TAG[] = "RSA PRIVATE KEY";
+
+/* These are overestimates for how many extra bytes we might need to encode
+ * a key in DER */
+#define PRIVATE_ASN_MAX_OVERHEAD_FACTOR 16
+#define PUBLIC_ASN_MAX_OVERHEAD_FACTOR 3
+
+/** Helper: PEM-encode <b>env</b> and write it to a newly allocated string.
+ * If <b>private_key</b>, write the private part of <b>env</b>; otherwise
+ * write only the public portion. On success, set *<b>dest</b> to the new
+ * string, *<b>len</b> to the string's length, and return 0. On failure,
+ * return -1.
+ */
+static int
+crypto_pk_write_to_string_generic(crypto_pk_t *env,
+ char **dest, size_t *len,
+ bool private_key)
+{
+ const int factor =
+ private_key ? PRIVATE_ASN_MAX_OVERHEAD_FACTOR
+ : PUBLIC_ASN_MAX_OVERHEAD_FACTOR;
+ size_t buflen = crypto_pk_keysize(env) * factor;
+ const char *tag =
+ private_key ? RSA_PRIVATE_TAG : RSA_PUBLIC_TAG;
+ char *buf = tor_malloc(buflen);
+ char *result = NULL;
+ size_t resultlen = 0;
+ int rv = -1;
+
+ int n = private_key
+ ? crypto_pk_asn1_encode_private(env, buf, buflen)
+ : crypto_pk_asn1_encode(env, buf, buflen);
+ if (n < 0)
+ goto done;
+
+ resultlen = pem_encoded_size(n, tag);
+ result = tor_malloc(resultlen);
+ if (pem_encode(result, resultlen,
+ (const unsigned char *)buf, n, tag) < 0) {
+ goto done;
+ }
+
+ *dest = result;
+ *len = resultlen;
+ rv = 0;
+
+ done:
+ if (rv < 0 && result) {
+ memwipe(result, 0, resultlen);
+ tor_free(result);
+ }
+ memwipe(buf, 0, buflen);
+ tor_free(buf);
+ return rv;
+}
+
+/** PEM-encode the public key portion of <b>env</b> and write it to a
+ * newly allocated string. On success, set *<b>dest</b> to the new
+ * string, *<b>len</b> to the string's length, and return 0. On
+ * failure, return -1.
+ */
+int
+crypto_pk_write_public_key_to_string(crypto_pk_t *env,
+ char **dest, size_t *len)
+{
+ return crypto_pk_write_to_string_generic(env, dest, len, false);
+}
+
+/** PEM-encode the private key portion of <b>env</b> and write it to a
+ * newly allocated string. On success, set *<b>dest</b> to the new
+ * string, *<b>len</b> to the string's length, and return 0. On
+ * failure, return -1.
+ */
+int
+crypto_pk_write_private_key_to_string(crypto_pk_t *env,
+ char **dest, size_t *len)
+{
+ return crypto_pk_write_to_string_generic(env, dest, len, true);
+}
+
+/**
+ * Helper. Read a PEM-encoded RSA from the first <b>len</b> characters of
+ * <b>src</b>, and store the result in <b>env</b>. If <b>private_key</b>,
+ * expect a private key; otherwise expect a public key. Return 0 on success,
+ * -1 on failure. If len is -1, the string is nul-terminated.
+ */
+static int
+crypto_pk_read_from_string_generic(crypto_pk_t *env, const char *src,
+ size_t len, bool private_key)
+{
+ if (len == (size_t)-1) // "-1" indicates "use the length of the string."
+ len = strlen(src);
+
+ const char *tag =
+ private_key ? RSA_PRIVATE_TAG : RSA_PUBLIC_TAG;
+ size_t buflen = len;
+ uint8_t *buf = tor_malloc(buflen);
+ int rv = -1;
+
+ int n = pem_decode(buf, buflen, src, len, tag);
+ if (n < 0)
+ goto done;
+
+ crypto_pk_t *pk = private_key
+ ? crypto_pk_asn1_decode_private((const char*)buf, n)
+ : crypto_pk_asn1_decode((const char*)buf, n);
+ if (! pk)
+ goto done;
+
+ if (private_key)
+ crypto_pk_assign_private(env, pk);
+ else
+ crypto_pk_assign_public(env, pk);
+ crypto_pk_free(pk);
+ rv = 0;
+
+ done:
+ memwipe(buf, 0, buflen);
+ tor_free(buf);
+ return rv;
+}
+
+/** Read a PEM-encoded public key from the first <b>len</b> characters of
+ * <b>src</b>, and store the result in <b>env</b>. Return 0 on success, -1 on
+ * failure. If len is -1, the string is nul-terminated.
+ */
+int
+crypto_pk_read_public_key_from_string(crypto_pk_t *env,
+ const char *src, size_t len)
+{
+ return crypto_pk_read_from_string_generic(env, src, len, false);
+}
+
+/** Read a PEM-encoded private key from the <b>len</b>-byte string <b>src</b>
+ * into <b>env</b>. Return 0 on success, -1 on failure. If len is -1,
+ * the string is nul-terminated.
+ */
+int
+crypto_pk_read_private_key_from_string(crypto_pk_t *env,
+ const char *src, ssize_t len)
+{
+ return crypto_pk_read_from_string_generic(env, src, len, true);
+}
+
+/** Read a PEM-encoded private key from the file named by
+ * <b>keyfile</b> into <b>env</b>. Return 0 on success, -1 on failure.
+ */
+int
+crypto_pk_read_private_key_from_filename(crypto_pk_t *env,
+ const char *keyfile)
+{
+ struct stat st;
+ char *buf = read_file_to_str(keyfile, 0, &st);
+ if (!buf)
+ return -1;
+
+ int rv = crypto_pk_read_private_key_from_string(env, buf, st.st_size);
+ memwipe(buf, 0, st.st_size);
+ tor_free(buf);
+ return rv;
+}
+
+/** Write the private key from <b>env</b> into the file named by <b>fname</b>,
+ * PEM-encoded. Return 0 on success, -1 on failure.
+ */
+int
+crypto_pk_write_private_key_to_filename(crypto_pk_t *env,
+ const char *fname)
+{
+ char *s = NULL;
+ size_t n = 0;
+
+ if (crypto_pk_write_private_key_to_string(env, &s, &n) < 0)
+ return -1;
+
+ int rv = write_bytes_to_file(fname, s, n, 0);
+ memwipe(s, 0, n);
+ tor_free(s);
+ return rv;
+}
+
/** Given a crypto_pk_t <b>pk</b>, allocate a new buffer containing the
* Base64 encoding of the DER representation of the private key as a NUL
* terminated string, and return it via <b>priv_out</b>. Return 0 on
@@ -1100,30 +585,35 @@ crypto_pk_get_common_digests(crypto_pk_t *pk, common_digests_t *digests_out)
* It is the caller's responsibility to sanitize and free the resulting buffer.
*/
int
-crypto_pk_base64_encode(const crypto_pk_t *pk, char **priv_out)
+crypto_pk_base64_encode_private(const crypto_pk_t *pk, char **priv_out)
{
- unsigned char *der = NULL;
- int der_len;
- int ret = -1;
+ size_t buflen = crypto_pk_keysize(pk)*16;
+ char *buf = tor_malloc(buflen);
+ char *result = NULL;
+ size_t reslen = 0;
+ bool ok = false;
- *priv_out = NULL;
+ int n = crypto_pk_asn1_encode_private(pk, buf, buflen);
- der_len = i2d_RSAPrivateKey(pk->key, &der);
- if (der_len < 0 || der == NULL)
- return ret;
+ if (n < 0)
+ goto done;
- size_t priv_len = base64_encode_size(der_len, 0) + 1;
- char *priv = tor_malloc_zero(priv_len);
- if (base64_encode(priv, priv_len, (char *)der, der_len, 0) >= 0) {
- *priv_out = priv;
- ret = 0;
- } else {
- tor_free(priv);
- }
+ reslen = base64_encode_size(n, 0)+1;
+ result = tor_malloc(reslen);
+ if (base64_encode(result, reslen, buf, n, 0) < 0)
+ goto done;
- memwipe(der, 0, der_len);
- OPENSSL_free(der);
- return ret;
+ ok = true;
+
+ done:
+ memwipe(buf, 0, buflen);
+ tor_free(buf);
+ if (result && ! ok) {
+ memwipe(result, 0, reslen);
+ tor_free(result);
+ }
+ *priv_out = result;
+ return ok ? 0 : -1;
}
/** Given a string containing the Base64 encoded DER representation of the
@@ -1131,7 +621,7 @@ crypto_pk_base64_encode(const crypto_pk_t *pk, char **priv_out)
* on failure.
*/
crypto_pk_t *
-crypto_pk_base64_decode(const char *str, size_t len)
+crypto_pk_base64_decode_private(const char *str, size_t len)
{
crypto_pk_t *pk = NULL;
@@ -1142,24 +632,11 @@ crypto_pk_base64_decode(const char *str, size_t len)
goto out;
}
- const unsigned char *dp = (unsigned char*)der; /* Shut the compiler up. */
- RSA *rsa = d2i_RSAPrivateKey(NULL, &dp, der_len);
- if (!rsa) {
- crypto_log_errors(LOG_WARN, "decoding private key");
- goto out;
- }
-
- pk = crypto_new_pk_from_rsa_(rsa);
-
- /* Make sure it's valid. */
- if (crypto_pk_check_key(pk) <= 0) {
- crypto_pk_free(pk);
- pk = NULL;
- goto out;
- }
+ pk = crypto_pk_asn1_decode_private(der, der_len);
out:
- memwipe(der, 0, len + 1);
+ memwipe(der, 0, len+1);
tor_free(der);
+
return pk;
}
diff --git a/src/lib/crypt_ops/crypto_rsa.h b/src/lib/crypt_ops/crypto_rsa.h
index 51fc974821..007964b268 100644
--- a/src/lib/crypt_ops/crypto_rsa.h
+++ b/src/lib/crypt_ops/crypto_rsa.h
@@ -29,6 +29,13 @@
/** Number of bytes added for PKCS1-OAEP padding. */
#define PKCS1_OAEP_PADDING_OVERHEAD 42
+/** Length of encoded public key fingerprints, including space; but not
+ * including terminating NUL. */
+#define FINGERPRINT_LEN 49
+
+/** Value of 'e' to use in our public keys */
+#define TOR_RSA_EXPONENT 65537
+
/** A public key, or a public/private key-pair. */
typedef struct crypto_pk_t crypto_pk_t;
@@ -57,7 +64,7 @@ int crypto_pk_read_private_key_from_string(crypto_pk_t *env,
int crypto_pk_write_private_key_to_filename(crypto_pk_t *env,
const char *fname);
-int crypto_pk_check_key(crypto_pk_t *env);
+int crypto_pk_is_valid_private_key(const crypto_pk_t *env);
int crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b);
int crypto_pk_eq_keys(const crypto_pk_t *a, const crypto_pk_t *b);
size_t crypto_pk_keysize(const crypto_pk_t *env);
@@ -65,7 +72,7 @@ int crypto_pk_num_bits(crypto_pk_t *env);
crypto_pk_t *crypto_pk_dup_key(crypto_pk_t *orig);
crypto_pk_t *crypto_pk_copy_full(crypto_pk_t *orig);
int crypto_pk_key_is_private(const crypto_pk_t *key);
-int crypto_pk_public_exponent_ok(crypto_pk_t *env);
+int crypto_pk_public_exponent_ok(const crypto_pk_t *env);
int crypto_pk_obsolete_public_hybrid_encrypt(crypto_pk_t *env, char *to,
size_t tolen,
const char *from, size_t fromlen,
@@ -86,8 +93,12 @@ int crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen,
const char *from, size_t fromlen);
int crypto_pk_asn1_encode(const crypto_pk_t *pk, char *dest, size_t dest_len);
crypto_pk_t *crypto_pk_asn1_decode(const char *str, size_t len);
+int crypto_pk_asn1_encode_private(const crypto_pk_t *pk,
+ char *dest, size_t dest_len);
+crypto_pk_t *crypto_pk_asn1_decode_private(const char *str, size_t len);
int crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out,int add_space);
int crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out);
+void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in);
MOCK_DECL(int, crypto_pk_public_checksig_digest,(crypto_pk_t *env,
const char *data, size_t datalen, const char *sig, size_t siglen));
@@ -96,20 +107,39 @@ int crypto_pk_private_sign_digest(crypto_pk_t *env, char *to, size_t tolen,
int crypto_pk_get_digest(const crypto_pk_t *pk, char *digest_out);
int crypto_pk_get_common_digests(crypto_pk_t *pk,
common_digests_t *digests_out);
-int crypto_pk_base64_encode(const crypto_pk_t *pk, char **priv_out);
-crypto_pk_t *crypto_pk_base64_decode(const char *str, size_t len);
+int crypto_pk_base64_encode_private(const crypto_pk_t *pk, char **priv_out);
+crypto_pk_t *crypto_pk_base64_decode_private(const char *str, size_t len);
+#ifdef ENABLE_OPENSSL
/* Prototypes for private functions only used by tortls.c, crypto.c, and the
* unit tests. */
struct rsa_st;
-struct rsa_st *crypto_pk_get_rsa_(crypto_pk_t *env);
-crypto_pk_t *crypto_new_pk_from_rsa_(struct rsa_st *rsa);
-MOCK_DECL(struct evp_pkey_st *, crypto_pk_get_evp_pkey_,(crypto_pk_t *env,
- int private));
struct evp_pkey_st;
+struct rsa_st *crypto_pk_get_openssl_rsa_(crypto_pk_t *env);
+crypto_pk_t *crypto_new_pk_from_openssl_rsa_(struct rsa_st *rsa);
+MOCK_DECL(struct evp_pkey_st *, crypto_pk_get_openssl_evp_pkey_,(
+ crypto_pk_t *env,int private));
+#endif
+
+#ifdef ENABLE_NSS
+struct SECKEYPublicKeyStr;
+struct SECKEYPrivateKeyStr;
+crypto_pk_t *crypto_pk_new_from_nss_pubkey(struct SECKEYPublicKeyStr *pub);
+const struct SECKEYPublicKeyStr *crypto_pk_get_nss_pubkey(
+ const crypto_pk_t *key);
+const struct SECKEYPrivateKeyStr *crypto_pk_get_nss_privkey(
+ const crypto_pk_t *key);
+#endif
+
+void crypto_pk_assign_public(crypto_pk_t *dest, const crypto_pk_t *src);
+void crypto_pk_assign_private(crypto_pk_t *dest, const crypto_pk_t *src);
#ifdef TOR_UNIT_TESTS
-void crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src);
+#ifdef ENABLE_NSS
+struct SECItemStr;
+STATIC int secitem_uint_cmp(const struct SECItemStr *a,
+ const struct SECItemStr *b);
+#endif
#endif
#endif
diff --git a/src/lib/crypt_ops/crypto_rsa_nss.c b/src/lib/crypt_ops/crypto_rsa_nss.c
new file mode 100644
index 0000000000..dc282d7c9d
--- /dev/null
+++ b/src/lib/crypt_ops/crypto_rsa_nss.c
@@ -0,0 +1,738 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file crypto_rsa.c
+ * \brief NSS implementations of our RSA code.
+ **/
+
+#include "lib/crypt_ops/crypto_rsa.h"
+
+#include "lib/crypt_ops/crypto_nss_mgt.h"
+#include "lib/crypt_ops/crypto_util.h"
+#include "lib/ctime/di_ops.h"
+#include "lib/encoding/binascii.h"
+#include "lib/fs/files.h"
+#include "lib/intmath/cmp.h"
+#include "lib/intmath/muldiv.h"
+#include "lib/log/log.h"
+#include "lib/log/util_bug.h"
+
+#include <string.h>
+
+#include <keyhi.h>
+#include <pk11pub.h>
+#include <secder.h>
+
+#ifdef ENABLE_OPENSSL
+#include <openssl/rsa.h>
+#include <openssl/evp.h>
+#endif
+
+/** Declaration for crypto_pk_t structure. */
+struct crypto_pk_t
+{
+ SECKEYPrivateKey *seckey;
+ SECKEYPublicKey *pubkey;
+};
+
+/** Return true iff <b>key</b> contains the private-key portion of the RSA
+ * key. */
+int
+crypto_pk_key_is_private(const crypto_pk_t *key)
+{
+ return key && key->seckey;
+}
+
+/** used by tortls.c: wrap a SecKEYPublicKey in a crypto_pk_t. Take ownership
+ * of the RSA object. */
+crypto_pk_t *
+crypto_pk_new_from_nss_pubkey(struct SECKEYPublicKeyStr *pub)
+{
+ crypto_pk_t *result = tor_malloc_zero(sizeof(crypto_pk_t));
+ result->pubkey = pub;
+ return result;
+}
+
+/** Return the SECKEYPublicKey for the provided crypto_pk_t. */
+const SECKEYPublicKey *
+crypto_pk_get_nss_pubkey(const crypto_pk_t *key)
+{
+ tor_assert(key);
+ return key->pubkey;
+}
+
+/** Return the SECKEYPrivateKey for the provided crypto_pk_t, or NULL if it
+ * does not exist. */
+const SECKEYPrivateKey *
+crypto_pk_get_nss_privkey(const crypto_pk_t *key)
+{
+ tor_assert(key);
+ return key->seckey;
+}
+
+#ifdef ENABLE_OPENSSL
+/** used by tortls.c: wrap an RSA* in a crypto_pk_t. Take ownership of the
+ * RSA object. */
+crypto_pk_t *
+crypto_new_pk_from_openssl_rsa_(RSA *rsa)
+{
+ crypto_pk_t *pk = NULL;
+ unsigned char *buf = NULL;
+ int len = i2d_RSAPublicKey(rsa, &buf);
+ RSA_free(rsa);
+
+ if (len < 0 || buf == NULL)
+ goto end;
+
+ pk = crypto_pk_asn1_decode((const char *)buf, len);
+
+ end:
+ if (buf)
+ OPENSSL_free(buf);
+ return pk;
+}
+
+/** Helper, used by tor-gencert.c. Return the RSA from a
+ * crypto_pk_t. */
+struct rsa_st *
+crypto_pk_get_openssl_rsa_(crypto_pk_t *pk)
+{
+ size_t buflen = crypto_pk_keysize(pk)*16;
+ unsigned char *buf = tor_malloc_zero(buflen);
+ const unsigned char *cp = buf;
+ RSA *rsa = NULL;
+
+ int used = crypto_pk_asn1_encode_private(pk, (char*)buf, buflen);
+ if (used < 0)
+ goto end;
+ rsa = d2i_RSAPrivateKey(NULL, &cp, used);
+
+ end:
+ memwipe(buf, 0, buflen);
+ tor_free(buf);
+ return rsa;
+}
+
+/** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_t. Iff
+ * private is set, include the private-key portion of the key. Return a valid
+ * pointer on success, and NULL on failure. */
+MOCK_IMPL(struct evp_pkey_st *,
+crypto_pk_get_openssl_evp_pkey_,(crypto_pk_t *pk, int private))
+{
+ size_t buflen = crypto_pk_keysize(pk)*16;
+ unsigned char *buf = tor_malloc_zero(buflen);
+ const unsigned char *cp = buf;
+ RSA *rsa = NULL;
+ EVP_PKEY *result = NULL;
+
+ if (private) {
+ int len = crypto_pk_asn1_encode_private(pk, (char*)buf, buflen);
+ if (len < 0)
+ goto end;
+ rsa = d2i_RSAPrivateKey(NULL, &cp, len);
+ } else {
+ int len = crypto_pk_asn1_encode(pk, (char*)buf, buflen);
+ if (len < 0)
+ goto end;
+ rsa = d2i_RSAPublicKey(NULL, &cp, len);
+ }
+ if (!rsa)
+ goto end;
+
+ if (!(result = EVP_PKEY_new()))
+ goto end;
+ if (!(EVP_PKEY_assign_RSA(result, rsa))) {
+ EVP_PKEY_free(result);
+ RSA_free(rsa);
+ result = NULL;
+ }
+
+ end:
+ memwipe(buf, 0, buflen);
+ tor_free(buf);
+ return result;
+}
+#endif
+
+/** Allocate and return storage for a public key. The key itself will not yet
+ * be set.
+ */
+MOCK_IMPL(crypto_pk_t *,
+crypto_pk_new,(void))
+{
+ crypto_pk_t *result = tor_malloc_zero(sizeof(crypto_pk_t));
+ return result;
+}
+
+/** Release the NSS objects held in <b>key</b> */
+static void
+crypto_pk_clear(crypto_pk_t *key)
+{
+ if (key->pubkey)
+ SECKEY_DestroyPublicKey(key->pubkey);
+ if (key->seckey)
+ SECKEY_DestroyPrivateKey(key->seckey);
+ memset(key, 0, sizeof(crypto_pk_t));
+}
+
+/** Release a reference to an asymmetric key; when all the references
+ * are released, free the key.
+ */
+void
+crypto_pk_free_(crypto_pk_t *key)
+{
+ if (!key)
+ return;
+
+ crypto_pk_clear(key);
+
+ tor_free(key);
+}
+
+/** Generate a <b>bits</b>-bit new public/private keypair in <b>env</b>.
+ * Return 0 on success, -1 on failure.
+ */
+MOCK_IMPL(int,
+crypto_pk_generate_key_with_bits,(crypto_pk_t *key, int bits))
+{
+ tor_assert(key);
+
+ PK11RSAGenParams params = {
+ .keySizeInBits = bits,
+ .pe = TOR_RSA_EXPONENT
+ };
+
+ int result = -1;
+ PK11SlotInfo *slot = PK11_GetBestSlot(CKM_RSA_PKCS_KEY_PAIR_GEN, NULL);
+ SECKEYPrivateKey *seckey = NULL;
+ SECKEYPublicKey *pubkey = NULL;
+
+ if (!slot) {
+ crypto_nss_log_errors(LOG_WARN, "getting slot for RSA keygen");
+ goto done;
+ }
+
+ seckey = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, &params,
+ &pubkey,
+ PR_FALSE /*isPerm */,
+ PR_FALSE /*isSensitive*/,
+ NULL);
+ if (seckey == NULL || pubkey == NULL) {
+ crypto_nss_log_errors(LOG_WARN, "generating an RSA key");
+ goto done;
+ }
+
+ crypto_pk_clear(key);
+ key->seckey = seckey;
+ key->pubkey = pubkey;
+ seckey = NULL;
+ pubkey = NULL;
+
+ result = 0;
+ done:
+ if (slot)
+ PK11_FreeSlot(slot);
+ if (pubkey)
+ SECKEY_DestroyPublicKey(pubkey);
+ if (seckey)
+ SECKEY_DestroyPrivateKey(seckey);
+
+ return result;
+}
+
+/** Return true iff <b>env</b> is a valid private key.
+ */
+int
+crypto_pk_is_valid_private_key(const crypto_pk_t *key)
+{
+ /* We don't need to do validation here, since unlike OpenSSL, NSS won't let
+ * us load private keys without validating them. */
+ return key && key->seckey;
+}
+
+/** Return true iff <b>env</b> contains a public key whose public exponent
+ * equals 65537.
+ */
+int
+crypto_pk_public_exponent_ok(const crypto_pk_t *key)
+{
+ return key &&
+ key->pubkey &&
+ key->pubkey->keyType == rsaKey &&
+ DER_GetUInteger(&key->pubkey->u.rsa.publicExponent) == TOR_RSA_EXPONENT;
+}
+
+/** Compare two big-endian integers stored in a and b; return a tristate.
+ */
+STATIC int
+secitem_uint_cmp(const SECItem *a, const SECItem *b)
+{
+ const unsigned abits = SECKEY_BigIntegerBitLength(a);
+ const unsigned bbits = SECKEY_BigIntegerBitLength(b);
+
+ if (abits < bbits)
+ return -1;
+ else if (abits > bbits)
+ return 1;
+
+ /* okay, they have the same number of bits set. Get a pair of aligned
+ * pointers to their bytes that are set... */
+ const unsigned nbytes = CEIL_DIV(abits, 8);
+ tor_assert(nbytes <= a->len);
+ tor_assert(nbytes <= b->len);
+
+ const unsigned char *aptr = a->data + (a->len - nbytes);
+ const unsigned char *bptr = b->data + (b->len - nbytes);
+
+ /* And compare them. */
+ return fast_memcmp(aptr, bptr, nbytes);
+}
+
+/** Compare the public-key components of a and b. Return less than 0
+ * if a\<b, 0 if a==b, and greater than 0 if a\>b. A NULL key is
+ * considered to be less than all non-NULL keys, and equal to itself.
+ *
+ * Note that this may leak information about the keys through timing.
+ */
+int
+crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b)
+{
+ int result;
+ char a_is_non_null = (a != NULL) && (a->pubkey != NULL);
+ char b_is_non_null = (b != NULL) && (b->pubkey != NULL);
+ char an_argument_is_null = !a_is_non_null | !b_is_non_null;
+
+ result = tor_memcmp(&a_is_non_null, &b_is_non_null, sizeof(a_is_non_null));
+ if (an_argument_is_null)
+ return result;
+
+ // This is all Tor uses with this structure.
+ tor_assert(a->pubkey->keyType == rsaKey);
+ tor_assert(b->pubkey->keyType == rsaKey);
+
+ const SECItem *a_n, *a_e, *b_n, *b_e;
+ a_n = &a->pubkey->u.rsa.modulus;
+ b_n = &b->pubkey->u.rsa.modulus;
+ a_e = &a->pubkey->u.rsa.publicExponent;
+ b_e = &b->pubkey->u.rsa.publicExponent;
+
+ result = secitem_uint_cmp(a_n, b_n);
+ if (result)
+ return result;
+ return secitem_uint_cmp(a_e, b_e);
+}
+
+/** Return the size of the public key modulus in <b>env</b>, in bytes. */
+size_t
+crypto_pk_keysize(const crypto_pk_t *key)
+{
+ tor_assert(key);
+ tor_assert(key->pubkey);
+ return SECKEY_PublicKeyStrength(key->pubkey);
+}
+
+/** Return the size of the public key modulus of <b>env</b>, in bits. */
+int
+crypto_pk_num_bits(crypto_pk_t *key)
+{
+ tor_assert(key);
+ tor_assert(key->pubkey);
+ return SECKEY_PublicKeyStrengthInBits(key->pubkey);
+}
+
+/**
+ * Make a copy of <b>key</b> and return it.
+ */
+crypto_pk_t *
+crypto_pk_dup_key(crypto_pk_t *key)
+{
+ crypto_pk_t *result = crypto_pk_new();
+ if (key->pubkey)
+ result->pubkey = SECKEY_CopyPublicKey(key->pubkey);
+ if (key->seckey)
+ result->seckey = SECKEY_CopyPrivateKey(key->seckey);
+ return result;
+}
+
+/** For testing: replace dest with src. (Dest must have a refcount
+ * of 1) */
+void
+crypto_pk_assign_public(crypto_pk_t *dest, const crypto_pk_t *src)
+{
+ crypto_pk_clear(dest);
+ if (src->pubkey)
+ dest->pubkey = SECKEY_CopyPublicKey(src->pubkey);
+}
+
+/** For testing: replace dest with src. (Dest must have a refcount
+ * of 1) */
+void
+crypto_pk_assign_private(crypto_pk_t *dest, const crypto_pk_t *src)
+{
+ crypto_pk_clear(dest);
+ if (src->pubkey)
+ dest->pubkey = SECKEY_CopyPublicKey(src->pubkey);
+ if (src->seckey)
+ dest->seckey = SECKEY_CopyPrivateKey(src->seckey);
+}
+
+/** Make a real honest-to-goodness copy of <b>env</b>, and return it.
+ * Returns NULL on failure. */
+crypto_pk_t *
+crypto_pk_copy_full(crypto_pk_t *key)
+{
+ // These aren't reference-counted is nss, so it's fine to just
+ // use the same function.
+ return crypto_pk_dup_key(key);
+}
+
+static const CK_RSA_PKCS_OAEP_PARAMS oaep_params = {
+ .hashAlg = CKM_SHA_1,
+ .mgf = CKG_MGF1_SHA1,
+ .source = CKZ_DATA_SPECIFIED,
+ .pSourceData = NULL,
+ .ulSourceDataLen = 0
+};
+static const SECItem oaep_item = {
+ .type = siBuffer,
+ .data = (unsigned char *) &oaep_params,
+ .len = sizeof(oaep_params)
+};
+
+/** Return the mechanism code and parameters for a given padding method when
+ * used with RSA */
+static CK_MECHANISM_TYPE
+padding_to_mechanism(int padding, SECItem **item_out)
+{
+ switch (padding) {
+ case PK_PKCS1_OAEP_PADDING:
+ *item_out = (SECItem *)&oaep_item;
+ return CKM_RSA_PKCS_OAEP;
+ default:
+ tor_assert_unreached();
+ *item_out = NULL;
+ return CKM_INVALID_MECHANISM;
+ }
+}
+
+/** Encrypt <b>fromlen</b> bytes from <b>from</b> with the public key
+ * in <b>env</b>, using the padding method <b>padding</b>. On success,
+ * write the result to <b>to</b>, and return the number of bytes
+ * written. On failure, return -1.
+ *
+ * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
+ * at least the length of the modulus of <b>env</b>.
+ */
+int
+crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen,
+ const char *from, size_t fromlen, int padding)
+{
+ tor_assert(env);
+ tor_assert(to);
+ tor_assert(from);
+ tor_assert(tolen < INT_MAX);
+ tor_assert(fromlen < INT_MAX);
+
+ if (BUG(! env->pubkey))
+ return -1;
+
+ unsigned int result_len = 0;
+ SECItem *item = NULL;
+ CK_MECHANISM_TYPE m = padding_to_mechanism(padding, &item);
+
+ SECStatus s = PK11_PubEncrypt(env->pubkey, m, item,
+ (unsigned char *)to, &result_len,
+ (unsigned int)tolen,
+ (const unsigned char *)from,
+ (unsigned int)fromlen,
+ NULL);
+ if (s != SECSuccess) {
+ crypto_nss_log_errors(LOG_WARN, "encrypting to an RSA key");
+ return -1;
+ }
+
+ return (int)result_len;
+}
+
+/** Decrypt <b>fromlen</b> bytes from <b>from</b> with the private key
+ * in <b>env</b>, using the padding method <b>padding</b>. On success,
+ * write the result to <b>to</b>, and return the number of bytes
+ * written. On failure, return -1.
+ *
+ * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
+ * at least the length of the modulus of <b>key</b>.
+ */
+int
+crypto_pk_private_decrypt(crypto_pk_t *key, char *to,
+ size_t tolen,
+ const char *from, size_t fromlen,
+ int padding, int warnOnFailure)
+{
+ tor_assert(key);
+ tor_assert(to);
+ tor_assert(from);
+ tor_assert(tolen < INT_MAX);
+ tor_assert(fromlen < INT_MAX);
+
+ if (!crypto_pk_key_is_private(key))
+ return -1; /* Not a private key. */
+
+ unsigned int result_len = 0;
+ SECItem *item = NULL;
+ CK_MECHANISM_TYPE m = padding_to_mechanism(padding, &item);
+ SECStatus s = PK11_PrivDecrypt(key->seckey, m, item,
+ (unsigned char *)to, &result_len,
+ (unsigned int)tolen,
+ (const unsigned char *)from,
+ (unsigned int)fromlen);
+
+ if (s != SECSuccess) {
+ const int severity = warnOnFailure ? LOG_WARN : LOG_INFO;
+ crypto_nss_log_errors(severity, "decrypting with an RSA key");
+ return -1;
+ }
+
+ return (int)result_len;
+}
+
+/** Check the signature in <b>from</b> (<b>fromlen</b> bytes long) with the
+ * public key in <b>key</b>, using PKCS1 padding. On success, write the
+ * signed data to <b>to</b>, and return the number of bytes written.
+ * On failure, return -1.
+ *
+ * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
+ * at least the length of the modulus of <b>key</b>.
+ */
+MOCK_IMPL(int,
+crypto_pk_public_checksig,(const crypto_pk_t *key, char *to,
+ size_t tolen,
+ const char *from, size_t fromlen))
+{
+ tor_assert(key);
+ tor_assert(to);
+ tor_assert(from);
+ tor_assert(tolen < INT_MAX);
+ tor_assert(fromlen < INT_MAX);
+ tor_assert(key->pubkey);
+
+ SECItem sig = {
+ .type = siBuffer,
+ .data = (unsigned char *) from,
+ .len = (unsigned int) fromlen,
+ };
+ SECItem dsig = {
+ .type = siBuffer,
+ .data = (unsigned char *) to,
+ .len = (unsigned int) tolen
+ };
+ SECStatus s;
+ s = PK11_VerifyRecover(key->pubkey, &sig, &dsig, NULL);
+ if (s != SECSuccess)
+ return -1;
+
+ return (int)dsig.len;
+}
+
+/** Sign <b>fromlen</b> bytes of data from <b>from</b> with the private key in
+ * <b>env</b>, using PKCS1 padding. On success, write the signature to
+ * <b>to</b>, and return the number of bytes written. On failure, return
+ * -1.
+ *
+ * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
+ * at least the length of the modulus of <b>env</b>.
+ */
+int
+crypto_pk_private_sign(const crypto_pk_t *key, char *to, size_t tolen,
+ const char *from, size_t fromlen)
+{
+ tor_assert(key);
+ tor_assert(to);
+ tor_assert(from);
+ tor_assert(tolen < INT_MAX);
+ tor_assert(fromlen < INT_MAX);
+
+ if (BUG(!crypto_pk_key_is_private(key)))
+ return -1;
+
+ SECItem sig = {
+ .type = siBuffer,
+ .data = (unsigned char *)to,
+ .len = (unsigned int) tolen
+ };
+ SECItem hash = {
+ .type = siBuffer,
+ .data = (unsigned char *)from,
+ .len = (unsigned int) fromlen
+ };
+ CK_MECHANISM_TYPE m = CKM_RSA_PKCS;
+ SECStatus s = PK11_SignWithMechanism(key->seckey, m, NULL,
+ &sig, &hash);
+
+ if (s != SECSuccess) {
+ crypto_nss_log_errors(LOG_WARN, "signing with an RSA key");
+ return -1;
+ }
+
+ return (int)sig.len;
+}
+
+/* "This has lead to people trading hard-to-find object identifiers and ASN.1
+ * definitions like baseball cards" - Peter Gutmann, "X.509 Style Guide". */
+static const unsigned char RSA_OID[] = {
+ /* RSADSI */ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ /* PKCS1 */ 0x01, 0x01,
+ /* RSA */ 0x01
+};
+
+/** ASN.1-encode the public portion of <b>pk</b> into <b>dest</b>.
+ * Return -1 on error, or the number of characters used on success.
+ */
+int
+crypto_pk_asn1_encode(const crypto_pk_t *pk, char *dest, size_t dest_len)
+{
+ tor_assert(pk);
+ if (pk->pubkey == NULL)
+ return -1;
+
+ CERTSubjectPublicKeyInfo *info;
+ info = SECKEY_CreateSubjectPublicKeyInfo(pk->pubkey);
+ if (! info)
+ return -1;
+
+ const SECItem *item = &info->subjectPublicKey;
+ size_t actual_len = (item->len) >> 3; /* bits to bytes */
+ size_t n_used = MIN(actual_len, dest_len);
+ memcpy(dest, item->data, n_used);
+
+ SECKEY_DestroySubjectPublicKeyInfo(info);
+ return (int) n_used;
+}
+
+/** Decode an ASN.1-encoded public key from <b>str</b>; return the result on
+ * success and NULL on failure.
+ */
+crypto_pk_t *
+crypto_pk_asn1_decode(const char *str, size_t len)
+{
+ tor_assert(str);
+ if (len >= INT_MAX)
+ return NULL;
+ CERTSubjectPublicKeyInfo info = {
+ .algorithm = {
+ .algorithm = {
+ .type = siDEROID,
+ .data = (unsigned char *)RSA_OID,
+ .len = sizeof(RSA_OID)
+ }
+ },
+ .subjectPublicKey = {
+ .type = siBuffer,
+ .data = (unsigned char *)str,
+ .len = (unsigned int)(len << 3) /* bytes to bits */
+ }
+ };
+
+ SECKEYPublicKey *pub = SECKEY_ExtractPublicKey(&info);
+ if (pub == NULL)
+ return NULL;
+
+ crypto_pk_t *result = crypto_pk_new();
+ result->pubkey = pub;
+ return result;
+}
+
+DISABLE_GCC_WARNING(unused-parameter)
+
+/** Given a crypto_pk_t <b>pk</b>, allocate a new buffer containing the Base64
+ * encoding of the DER representation of the private key into the
+ * <b>dest_len</b>-byte buffer in <b>dest</b>.
+ * Return the number of bytes written on success, -1 on failure.
+ */
+int
+crypto_pk_asn1_encode_private(const crypto_pk_t *pk,
+ char *dest, size_t destlen)
+{
+ tor_assert(destlen <= INT_MAX);
+ if (!crypto_pk_key_is_private(pk))
+ return -1;
+
+ SECKEYPrivateKeyInfo *info = PK11_ExportPrivKeyInfo(pk->seckey, NULL);
+ if (!info)
+ return -1;
+ SECItem *item = &info->privateKey;
+
+ if (destlen < item->len) {
+ SECKEY_DestroyPrivateKeyInfo(info, PR_TRUE);
+ return -1;
+ }
+ int result = (int)item->len;
+ memcpy(dest, item->data, item->len);
+ SECKEY_DestroyPrivateKeyInfo(info, PR_TRUE);
+
+ return result;
+}
+
+/** Given a buffer containing the DER representation of the
+ * private key <b>str</b>, decode and return the result on success, or NULL
+ * on failure.
+ */
+crypto_pk_t *
+crypto_pk_asn1_decode_private(const char *str, size_t len)
+{
+ tor_assert(str);
+ tor_assert(len < INT_MAX);
+ PK11SlotInfo *slot = PK11_GetBestSlot(CKM_RSA_PKCS, NULL);
+ if (!slot)
+ return NULL;
+
+ SECKEYPrivateKeyInfo info = {
+ .algorithm = {
+ .algorithm = {
+ .type = siBuffer,
+ .data = (unsigned char *)RSA_OID,
+ .len = sizeof(RSA_OID)
+ }
+ },
+ .privateKey = {
+ .type = siBuffer,
+ .data = (unsigned char *)str,
+ .len = (int)len,
+ }
+ };
+
+ SECStatus s;
+ SECKEYPrivateKey *seckey = NULL;
+
+ s = PK11_ImportPrivateKeyInfoAndReturnKey(slot, &info,
+ NULL /* nickname */,
+ NULL /* publicValue */,
+ PR_FALSE /* isPerm */,
+ PR_FALSE /* isPrivate */,
+ KU_ALL /* keyUsage */,
+ &seckey, NULL);
+
+ crypto_pk_t *output = NULL;
+
+ if (s == SECSuccess && seckey) {
+ output = crypto_pk_new();
+ output->seckey = seckey;
+ output->pubkey = SECKEY_ConvertToPublicKey(seckey);
+ tor_assert(output->pubkey);
+ } else {
+ crypto_nss_log_errors(LOG_WARN, "decoding an RSA private key");
+ }
+
+ if (! crypto_pk_is_valid_private_key(output)) {
+ crypto_pk_free(output);
+ output = NULL;
+ }
+
+ if (slot)
+ PK11_FreeSlot(slot);
+
+ return output;
+}
diff --git a/src/lib/crypt_ops/crypto_rsa_openssl.c b/src/lib/crypt_ops/crypto_rsa_openssl.c
new file mode 100644
index 0000000000..df81c963eb
--- /dev/null
+++ b/src/lib/crypt_ops/crypto_rsa_openssl.c
@@ -0,0 +1,590 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file crypto_rsa.c
+ * \brief OpenSSL implementations of our RSA code.
+ **/
+
+#include "lib/crypt_ops/compat_openssl.h"
+#include "lib/crypt_ops/crypto_rsa.h"
+#include "lib/crypt_ops/crypto_util.h"
+#include "lib/ctime/di_ops.h"
+#include "lib/log/util_bug.h"
+#include "lib/fs/files.h"
+
+DISABLE_GCC_WARNING(redundant-decls)
+
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+#include <openssl/engine.h>
+#include <openssl/rand.h>
+#include <openssl/bn.h>
+#include <openssl/conf.h>
+
+ENABLE_GCC_WARNING(redundant-decls)
+
+#include "lib/log/log.h"
+#include "lib/encoding/binascii.h"
+
+#include <string.h>
+
+/** Declaration for crypto_pk_t structure. */
+struct crypto_pk_t
+{
+ int refs; /**< reference count, so we don't have to copy keys */
+ RSA *key; /**< The key itself */
+};
+
+/** Return true iff <b>key</b> contains the private-key portion of the RSA
+ * key. */
+int
+crypto_pk_key_is_private(const crypto_pk_t *k)
+{
+#ifdef OPENSSL_1_1_API
+ if (!k || !k->key)
+ return 0;
+
+ const BIGNUM *p, *q;
+ RSA_get0_factors(k->key, &p, &q);
+ return p != NULL; /* XXX/yawning: Should we check q? */
+#else /* !(defined(OPENSSL_1_1_API)) */
+ return k && k->key && k->key->p;
+#endif /* defined(OPENSSL_1_1_API) */
+}
+
+/** used by tortls.c: wrap an RSA* in a crypto_pk_t. Takes ownership of
+ * its argument. */
+crypto_pk_t *
+crypto_new_pk_from_openssl_rsa_(RSA *rsa)
+{
+ crypto_pk_t *env;
+ tor_assert(rsa);
+ env = tor_malloc(sizeof(crypto_pk_t));
+ env->refs = 1;
+ env->key = rsa;
+ return env;
+}
+
+/** Helper, used by tor-gencert.c. Return a copy of the private RSA from a
+ * crypto_pk_t. */
+RSA *
+crypto_pk_get_openssl_rsa_(crypto_pk_t *env)
+{
+ return RSAPrivateKey_dup(env->key);
+}
+
+/** used by tortls.c: get an equivalent EVP_PKEY* for a crypto_pk_t. Iff
+ * private is set, include the private-key portion of the key. Return a valid
+ * pointer on success, and NULL on failure. */
+MOCK_IMPL(EVP_PKEY *,
+crypto_pk_get_openssl_evp_pkey_,(crypto_pk_t *env, int private))
+{
+ RSA *key = NULL;
+ EVP_PKEY *pkey = NULL;
+ tor_assert(env->key);
+ if (private) {
+ if (!(key = RSAPrivateKey_dup(env->key)))
+ goto error;
+ } else {
+ if (!(key = RSAPublicKey_dup(env->key)))
+ goto error;
+ }
+ if (!(pkey = EVP_PKEY_new()))
+ goto error;
+ if (!(EVP_PKEY_assign_RSA(pkey, key)))
+ goto error;
+ return pkey;
+ error:
+ if (pkey)
+ EVP_PKEY_free(pkey);
+ if (key)
+ RSA_free(key);
+ return NULL;
+}
+
+/** Allocate and return storage for a public key. The key itself will not yet
+ * be set.
+ */
+MOCK_IMPL(crypto_pk_t *,
+crypto_pk_new,(void))
+{
+ RSA *rsa;
+
+ rsa = RSA_new();
+ tor_assert(rsa);
+ return crypto_new_pk_from_openssl_rsa_(rsa);
+}
+
+/** Release a reference to an asymmetric key; when all the references
+ * are released, free the key.
+ */
+void
+crypto_pk_free_(crypto_pk_t *env)
+{
+ if (!env)
+ return;
+
+ if (--env->refs > 0)
+ return;
+ tor_assert(env->refs == 0);
+
+ if (env->key)
+ RSA_free(env->key);
+
+ tor_free(env);
+}
+
+/** Generate a <b>bits</b>-bit new public/private keypair in <b>env</b>.
+ * Return 0 on success, -1 on failure.
+ */
+MOCK_IMPL(int,
+crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits))
+{
+ tor_assert(env);
+
+ if (env->key) {
+ RSA_free(env->key);
+ env->key = NULL;
+ }
+
+ {
+ BIGNUM *e = BN_new();
+ RSA *r = NULL;
+ if (!e)
+ goto done;
+ if (! BN_set_word(e, TOR_RSA_EXPONENT))
+ goto done;
+ r = RSA_new();
+ if (!r)
+ goto done;
+ if (RSA_generate_key_ex(r, bits, e, NULL) == -1)
+ goto done;
+
+ env->key = r;
+ r = NULL;
+ done:
+ if (e)
+ BN_clear_free(e);
+ if (r)
+ RSA_free(r);
+ }
+
+ if (!env->key) {
+ crypto_openssl_log_errors(LOG_WARN, "generating RSA key");
+ return -1;
+ }
+
+ return 0;
+}
+
+/** Return true if <b>env</b> has a valid key; false otherwise.
+ */
+int
+crypto_pk_is_valid_private_key(const crypto_pk_t *env)
+{
+ int r;
+ tor_assert(env);
+
+ r = RSA_check_key(env->key);
+ if (r <= 0) {
+ crypto_openssl_log_errors(LOG_WARN,"checking RSA key");
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+/** Return true iff <b>env</b> contains a public key whose public exponent
+ * equals TOR_RSA_EXPONENT.
+ */
+int
+crypto_pk_public_exponent_ok(const crypto_pk_t *env)
+{
+ tor_assert(env);
+ tor_assert(env->key);
+
+ const BIGNUM *e;
+
+#ifdef OPENSSL_1_1_API
+ const BIGNUM *n, *d;
+ RSA_get0_key(env->key, &n, &e, &d);
+#else
+ e = env->key->e;
+#endif /* defined(OPENSSL_1_1_API) */
+ return BN_is_word(e, TOR_RSA_EXPONENT);
+}
+
+/** Compare the public-key components of a and b. Return less than 0
+ * if a\<b, 0 if a==b, and greater than 0 if a\>b. A NULL key is
+ * considered to be less than all non-NULL keys, and equal to itself.
+ *
+ * Note that this may leak information about the keys through timing.
+ */
+int
+crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b)
+{
+ int result;
+ char a_is_non_null = (a != NULL) && (a->key != NULL);
+ char b_is_non_null = (b != NULL) && (b->key != NULL);
+ char an_argument_is_null = !a_is_non_null | !b_is_non_null;
+
+ result = tor_memcmp(&a_is_non_null, &b_is_non_null, sizeof(a_is_non_null));
+ if (an_argument_is_null)
+ return result;
+
+ const BIGNUM *a_n, *a_e;
+ const BIGNUM *b_n, *b_e;
+
+#ifdef OPENSSL_1_1_API
+ const BIGNUM *a_d, *b_d;
+ RSA_get0_key(a->key, &a_n, &a_e, &a_d);
+ RSA_get0_key(b->key, &b_n, &b_e, &b_d);
+#else
+ a_n = a->key->n;
+ a_e = a->key->e;
+ b_n = b->key->n;
+ b_e = b->key->e;
+#endif /* defined(OPENSSL_1_1_API) */
+
+ tor_assert(a_n != NULL && a_e != NULL);
+ tor_assert(b_n != NULL && b_e != NULL);
+
+ result = BN_cmp(a_n, b_n);
+ if (result)
+ return result;
+ return BN_cmp(a_e, b_e);
+}
+
+/** Return the size of the public key modulus in <b>env</b>, in bytes. */
+size_t
+crypto_pk_keysize(const crypto_pk_t *env)
+{
+ tor_assert(env);
+ tor_assert(env->key);
+
+ return (size_t) RSA_size((RSA*)env->key);
+}
+
+/** Return the size of the public key modulus of <b>env</b>, in bits. */
+int
+crypto_pk_num_bits(crypto_pk_t *env)
+{
+ tor_assert(env);
+ tor_assert(env->key);
+
+#ifdef OPENSSL_1_1_API
+ /* It's so stupid that there's no other way to check that n is valid
+ * before calling RSA_bits().
+ */
+ const BIGNUM *n, *e, *d;
+ RSA_get0_key(env->key, &n, &e, &d);
+ tor_assert(n != NULL);
+
+ return RSA_bits(env->key);
+#else /* !(defined(OPENSSL_1_1_API)) */
+ tor_assert(env->key->n);
+ return BN_num_bits(env->key->n);
+#endif /* defined(OPENSSL_1_1_API) */
+}
+
+/** Increase the reference count of <b>env</b>, and return it.
+ */
+crypto_pk_t *
+crypto_pk_dup_key(crypto_pk_t *env)
+{
+ tor_assert(env);
+ tor_assert(env->key);
+
+ env->refs++;
+ return env;
+}
+
+/** Replace dest with src (private key only). (Dest must have a refcount
+ * of 1)
+ */
+void
+crypto_pk_assign_private(crypto_pk_t *dest, const crypto_pk_t *src)
+{
+ tor_assert(dest);
+ tor_assert(dest->refs == 1);
+ tor_assert(src);
+ RSA_free(dest->key);
+ dest->key = RSAPrivateKey_dup(src->key);
+}
+
+/** Replace dest with src (public key only). (Dest must have a refcount
+ * of 1)
+ */
+void
+crypto_pk_assign_public(crypto_pk_t *dest, const crypto_pk_t *src)
+{
+ tor_assert(dest);
+ tor_assert(dest->refs == 1);
+ tor_assert(src);
+ RSA_free(dest->key);
+ dest->key = RSAPublicKey_dup(src->key);
+}
+
+/** Make a real honest-to-goodness copy of <b>env</b>, and return it.
+ * Returns NULL on failure. */
+crypto_pk_t *
+crypto_pk_copy_full(crypto_pk_t *env)
+{
+ RSA *new_key;
+ int privatekey = 0;
+ tor_assert(env);
+ tor_assert(env->key);
+
+ if (crypto_pk_key_is_private(env)) {
+ new_key = RSAPrivateKey_dup(env->key);
+ privatekey = 1;
+ } else {
+ new_key = RSAPublicKey_dup(env->key);
+ }
+ if (!new_key) {
+ /* LCOV_EXCL_START
+ *
+ * We can't cause RSA*Key_dup() to fail, so we can't really test this.
+ */
+ log_err(LD_CRYPTO, "Unable to duplicate a %s key: openssl failed.",
+ privatekey?"private":"public");
+ crypto_openssl_log_errors(LOG_ERR,
+ privatekey ? "Duplicating a private key" :
+ "Duplicating a public key");
+ tor_fragile_assert();
+ return NULL;
+ /* LCOV_EXCL_STOP */
+ }
+
+ return crypto_new_pk_from_openssl_rsa_(new_key);
+}
+
+/** Encrypt <b>fromlen</b> bytes from <b>from</b> with the public key
+ * in <b>env</b>, using the padding method <b>padding</b>. On success,
+ * write the result to <b>to</b>, and return the number of bytes
+ * written. On failure, return -1.
+ *
+ * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
+ * at least the length of the modulus of <b>env</b>.
+ */
+int
+crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen,
+ const char *from, size_t fromlen, int padding)
+{
+ int r;
+ tor_assert(env);
+ tor_assert(from);
+ tor_assert(to);
+ tor_assert(fromlen<INT_MAX);
+ tor_assert(tolen >= crypto_pk_keysize(env));
+
+ r = RSA_public_encrypt((int)fromlen,
+ (unsigned char*)from, (unsigned char*)to,
+ env->key, crypto_get_rsa_padding(padding));
+ if (r<0) {
+ crypto_openssl_log_errors(LOG_WARN, "performing RSA encryption");
+ return -1;
+ }
+ return r;
+}
+
+/** Decrypt <b>fromlen</b> bytes from <b>from</b> with the private key
+ * in <b>env</b>, using the padding method <b>padding</b>. On success,
+ * write the result to <b>to</b>, and return the number of bytes
+ * written. On failure, return -1.
+ *
+ * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
+ * at least the length of the modulus of <b>env</b>.
+ */
+int
+crypto_pk_private_decrypt(crypto_pk_t *env, char *to,
+ size_t tolen,
+ const char *from, size_t fromlen,
+ int padding, int warnOnFailure)
+{
+ int r;
+ tor_assert(env);
+ tor_assert(from);
+ tor_assert(to);
+ tor_assert(env->key);
+ tor_assert(fromlen<INT_MAX);
+ tor_assert(tolen >= crypto_pk_keysize(env));
+ if (!crypto_pk_key_is_private(env))
+ /* Not a private key */
+ return -1;
+
+ r = RSA_private_decrypt((int)fromlen,
+ (unsigned char*)from, (unsigned char*)to,
+ env->key, crypto_get_rsa_padding(padding));
+
+ if (r<0) {
+ crypto_openssl_log_errors(warnOnFailure?LOG_WARN:LOG_DEBUG,
+ "performing RSA decryption");
+ return -1;
+ }
+ return r;
+}
+
+/** Check the signature in <b>from</b> (<b>fromlen</b> bytes long) with the
+ * public key in <b>env</b>, using PKCS1 padding. On success, write the
+ * signed data to <b>to</b>, and return the number of bytes written.
+ * On failure, return -1.
+ *
+ * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
+ * at least the length of the modulus of <b>env</b>.
+ */
+MOCK_IMPL(int,
+crypto_pk_public_checksig,(const crypto_pk_t *env, char *to,
+ size_t tolen,
+ const char *from, size_t fromlen))
+{
+ int r;
+ tor_assert(env);
+ tor_assert(from);
+ tor_assert(to);
+ tor_assert(fromlen < INT_MAX);
+ tor_assert(tolen >= crypto_pk_keysize(env));
+ r = RSA_public_decrypt((int)fromlen,
+ (unsigned char*)from, (unsigned char*)to,
+ env->key, RSA_PKCS1_PADDING);
+
+ if (r<0) {
+ crypto_openssl_log_errors(LOG_INFO, "checking RSA signature");
+ return -1;
+ }
+ return r;
+}
+
+/** Sign <b>fromlen</b> bytes of data from <b>from</b> with the private key in
+ * <b>env</b>, using PKCS1 padding. On success, write the signature to
+ * <b>to</b>, and return the number of bytes written. On failure, return
+ * -1.
+ *
+ * <b>tolen</b> is the number of writable bytes in <b>to</b>, and must be
+ * at least the length of the modulus of <b>env</b>.
+ */
+int
+crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen,
+ const char *from, size_t fromlen)
+{
+ int r;
+ tor_assert(env);
+ tor_assert(from);
+ tor_assert(to);
+ tor_assert(fromlen < INT_MAX);
+ tor_assert(tolen >= crypto_pk_keysize(env));
+ if (!crypto_pk_key_is_private(env))
+ /* Not a private key */
+ return -1;
+
+ r = RSA_private_encrypt((int)fromlen,
+ (unsigned char*)from, (unsigned char*)to,
+ (RSA*)env->key, RSA_PKCS1_PADDING);
+ if (r<0) {
+ crypto_openssl_log_errors(LOG_WARN, "generating RSA signature");
+ return -1;
+ }
+ return r;
+}
+
+/** ASN.1-encode the public portion of <b>pk</b> into <b>dest</b>.
+ * Return -1 on error, or the number of characters used on success.
+ */
+int
+crypto_pk_asn1_encode(const crypto_pk_t *pk, char *dest, size_t dest_len)
+{
+ int len;
+ unsigned char *buf = NULL;
+
+ len = i2d_RSAPublicKey(pk->key, &buf);
+ if (len < 0 || buf == NULL)
+ return -1;
+
+ if ((size_t)len > dest_len || dest_len > SIZE_T_CEILING) {
+ OPENSSL_free(buf);
+ return -1;
+ }
+ /* We don't encode directly into 'dest', because that would be illegal
+ * type-punning. (C99 is smarter than me, C99 is smarter than me...)
+ */
+ memcpy(dest,buf,len);
+ OPENSSL_free(buf);
+ return len;
+}
+
+/** Decode an ASN.1-encoded public key from <b>str</b>; return the result on
+ * success and NULL on failure.
+ */
+crypto_pk_t *
+crypto_pk_asn1_decode(const char *str, size_t len)
+{
+ RSA *rsa;
+ unsigned char *buf;
+ const unsigned char *cp;
+ cp = buf = tor_malloc(len);
+ memcpy(buf,str,len);
+ rsa = d2i_RSAPublicKey(NULL, &cp, len);
+ tor_free(buf);
+ if (!rsa) {
+ crypto_openssl_log_errors(LOG_WARN,"decoding public key");
+ return NULL;
+ }
+ return crypto_new_pk_from_openssl_rsa_(rsa);
+}
+
+/** ASN.1-encode the private portion of <b>pk</b> into <b>dest</b>.
+ * Return -1 on error, or the number of characters used on success.
+ */
+int
+crypto_pk_asn1_encode_private(const crypto_pk_t *pk, char *dest,
+ size_t dest_len)
+{
+ int len;
+ unsigned char *buf = NULL;
+
+ len = i2d_RSAPrivateKey(pk->key, &buf);
+ if (len < 0 || buf == NULL)
+ return -1;
+
+ if ((size_t)len > dest_len || dest_len > SIZE_T_CEILING) {
+ OPENSSL_free(buf);
+ return -1;
+ }
+ /* We don't encode directly into 'dest', because that would be illegal
+ * type-punning. (C99 is smarter than me, C99 is smarter than me...)
+ */
+ memcpy(dest,buf,len);
+ OPENSSL_free(buf);
+ return len;
+}
+
+/** Decode an ASN.1-encoded private key from <b>str</b>; return the result on
+ * success and NULL on failure.
+ */
+crypto_pk_t *
+crypto_pk_asn1_decode_private(const char *str, size_t len)
+{
+ RSA *rsa;
+ unsigned char *buf;
+ const unsigned char *cp;
+ cp = buf = tor_malloc(len);
+ memcpy(buf,str,len);
+ rsa = d2i_RSAPrivateKey(NULL, &cp, len);
+ tor_free(buf);
+ if (!rsa) {
+ crypto_openssl_log_errors(LOG_WARN,"decoding public key");
+ return NULL;
+ }
+ crypto_pk_t *result = crypto_new_pk_from_openssl_rsa_(rsa);
+ if (! crypto_pk_is_valid_private_key(result)) {
+ crypto_pk_free(result);
+ return NULL;
+ }
+ return result;
+}
diff --git a/src/lib/crypt_ops/crypto_s2k.c b/src/lib/crypt_ops/crypto_s2k.c
index ab91d92f0e..e0b2f40bb3 100644
--- a/src/lib/crypt_ops/crypto_s2k.c
+++ b/src/lib/crypt_ops/crypto_s2k.c
@@ -12,7 +12,7 @@
#define CRYPTO_S2K_PRIVATE
-#include "lib/crypt_ops/crypto.h"
+#include "lib/crypt_ops/crypto_cipher.h"
#include "lib/crypt_ops/crypto_digest.h"
#include "lib/crypt_ops/crypto_hkdf.h"
#include "lib/crypt_ops/crypto_rand.h"
@@ -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: {
diff --git a/src/lib/crypt_ops/crypto_util.c b/src/lib/crypt_ops/crypto_util.c
index 79988c6a91..7af80291ef 100644
--- a/src/lib/crypt_ops/crypto_util.c
+++ b/src/lib/crypt_ops/crypto_util.c
@@ -10,7 +10,6 @@
* \brief Common cryptographic utilities.
**/
-#ifndef CRYPTO_UTIL_PRIVATE
#define CRYPTO_UTIL_PRIVATE
#include "lib/crypt_ops/crypto_util.h"
@@ -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) */
diff --git a/src/lib/crypt_ops/crypto_util.h b/src/lib/crypt_ops/crypto_util.h
index 3ce34e6f23..e032263225 100644
--- a/src/lib/crypt_ops/crypto_util.h
+++ b/src/lib/crypt_ops/crypto_util.h
@@ -18,13 +18,4 @@
/** OpenSSL-based utility functions. */
void memwipe(void *mem, uint8_t byte, size_t sz);
-/** Log utility function */
-void crypto_log_errors(int severity, const char *doing);
-
-#ifdef CRYPTO_UTIL_PRIVATE
-#ifdef TOR_UNIT_TESTS
-#endif /* defined(TOR_UNIT_TESTS) */
-#endif /* defined(CRYPTO_UTIL_PRIVATE) */
-
#endif /* !defined(TOR_CRYPTO_UTIL_H) */
-
diff --git a/src/lib/crypt_ops/include.am b/src/lib/crypt_ops/include.am
index 017d7956d0..1022096fdc 100644
--- a/src/lib/crypt_ops/include.am
+++ b/src/lib/crypt_ops/include.am
@@ -6,16 +6,15 @@ noinst_LIBRARIES += src/lib/libtor-crypt-ops-testing.a
endif
src_lib_libtor_crypt_ops_a_SOURCES = \
- src/lib/crypt_ops/aes.c \
- src/lib/crypt_ops/crypto.c \
+ src/lib/crypt_ops/crypto_cipher.c \
src/lib/crypt_ops/crypto_curve25519.c \
src/lib/crypt_ops/crypto_dh.c \
src/lib/crypt_ops/crypto_digest.c \
src/lib/crypt_ops/crypto_ed25519.c \
src/lib/crypt_ops/crypto_format.c \
src/lib/crypt_ops/crypto_hkdf.c \
+ src/lib/crypt_ops/crypto_init.c \
src/lib/crypt_ops/crypto_ope.c \
- src/lib/crypt_ops/crypto_openssl_mgt.c \
src/lib/crypt_ops/crypto_pwbox.c \
src/lib/crypt_ops/crypto_rand.c \
src/lib/crypt_ops/crypto_rsa.c \
@@ -23,10 +22,31 @@ src_lib_libtor_crypt_ops_a_SOURCES = \
src/lib/crypt_ops/crypto_util.c \
src/lib/crypt_ops/digestset.c
+if USE_NSS
+src_lib_libtor_crypt_ops_a_SOURCES += \
+ src/lib/crypt_ops/aes_nss.c \
+ src/lib/crypt_ops/crypto_dh_nss.c \
+ src/lib/crypt_ops/crypto_nss_mgt.c \
+ src/lib/crypt_ops/crypto_rsa_nss.c
+else
+src_lib_libtor_crypt_ops_a_SOURCES += \
+ src/lib/crypt_ops/aes_openssl.c \
+ src/lib/crypt_ops/crypto_rsa_openssl.c
+endif
+
+if USE_OPENSSL
+src_lib_libtor_crypt_ops_a_SOURCES += \
+ src/lib/crypt_ops/crypto_dh_openssl.c \
+ src/lib/crypt_ops/crypto_openssl_mgt.c
+endif
+
+src_lib_libtor_crypt_ops_a_CFLAGS = $(AM_CFLAGS) $(TOR_CFLAGS_CRYPTLIB)
+
src_lib_libtor_crypt_ops_testing_a_SOURCES = \
$(src_lib_libtor_crypt_ops_a_SOURCES)
src_lib_libtor_crypt_ops_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
-src_lib_libtor_crypt_ops_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+src_lib_libtor_crypt_ops_testing_a_CFLAGS = \
+ $(AM_CFLAGS) $(TOR_CFLAGS_CRYPTLIB) $(TEST_CFLAGS)
noinst_HEADERS += \
src/lib/crypt_ops/aes.h \
@@ -36,8 +56,10 @@ noinst_HEADERS += \
src/lib/crypt_ops/crypto_digest.h \
src/lib/crypt_ops/crypto_ed25519.h \
src/lib/crypt_ops/crypto_format.h \
- src/lib/crypt_ops/crypto.h \
+ src/lib/crypt_ops/crypto_cipher.h \
src/lib/crypt_ops/crypto_hkdf.h \
+ src/lib/crypt_ops/crypto_init.h \
+ src/lib/crypt_ops/crypto_nss_mgt.h \
src/lib/crypt_ops/crypto_openssl_mgt.h \
src/lib/crypt_ops/crypto_ope.h \
src/lib/crypt_ops/crypto_pwbox.h \