[tor-commits] [tor/master] Implement DH in NSS.

nickm at torproject.org nickm at torproject.org
Wed Sep 5 00:47:13 UTC 2018


commit 17ea931ac70af3cc11c1729b09b8e1ff17a53348
Author: Nick Mathewson <nickm at torproject.org>
Date:   Tue Jul 17 11:27:08 2018 -0400

    Implement DH in NSS.
---
 src/lib/crypt_ops/crypto_dh.c         |  22 ++++
 src/lib/crypt_ops/crypto_dh.h         |  11 +-
 src/lib/crypt_ops/crypto_dh_nss.c     | 207 ++++++++++++++++++++++++++++++++++
 src/lib/crypt_ops/crypto_dh_openssl.c |  90 ++++++---------
 src/lib/crypt_ops/crypto_init.c       |   2 +
 src/lib/crypt_ops/include.am          |   4 +-
 src/test/testing_common.c             |   1 -
 7 files changed, 281 insertions(+), 56 deletions(-)

diff --git a/src/lib/crypt_ops/crypto_dh.c b/src/lib/crypt_ops/crypto_dh.c
index 6f763e37a..673ef311f 100644
--- a/src/lib/crypt_ops/crypto_dh.c
+++ b/src/lib/crypt_ops/crypto_dh.c
@@ -43,6 +43,28 @@ const char OAKLEY_PRIME_2[] =
   "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
   "49286651ECE65381FFFFFFFFFFFFFFFF";
 
+void
+crypto_dh_init(void)
+{
+#ifdef ENABLE_OPENSSL
+  crypto_dh_init_openssl();
+#endif
+#ifdef ENABLE_NSS
+  crypto_dh_init_nss();
+#endif
+}
+
+void
+crypto_dh_free_all(void)
+{
+#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
  * <b>pubkey_len</b>-byte value in <b>pubkey</b>) generate
  * <b>secret_bytes_out</b> bytes of shared key material and write them
diff --git a/src/lib/crypt_ops/crypto_dh.h b/src/lib/crypt_ops/crypto_dh.h
index 953362696..6e79a6404 100644
--- a/src/lib/crypt_ops/crypto_dh.h
+++ b/src/lib/crypt_ops/crypto_dh.h
@@ -27,7 +27,7 @@ extern const char OAKLEY_PRIME_2[];
 #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);
@@ -52,4 +52,13 @@ void crypto_dh_free_all(void);
 struct dh_st;
 struct dh_st *crypto_dh_new_openssl_tls(void);
 
+#ifdef ENABLE_OPENSSL
+void crypto_dh_init_openssl(void);
+void crypto_dh_free_all_openssl(void);
+#endif
+#ifdef ENABLE_OPENSSL
+void crypto_dh_init_nss(void);
+void crypto_dh_free_all_nss(void);
+#endif
+
 #endif /* !defined(TOR_CRYPTO_DH_H) */
diff --git a/src/lib/crypt_ops/crypto_dh_nss.c b/src/lib/crypt_ops/crypto_dh_nss.c
new file mode 100644
index 000000000..647dfb4e5
--- /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 = 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
index d66031afd..54946458d 100644
--- a/src/lib/crypt_ops/crypto_dh_openssl.c
+++ b/src/lib/crypt_ops/crypto_dh_openssl.c
@@ -26,14 +26,17 @@ 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);
-static DH *new_openssl_dh_from_params(BIGNUM *p, BIGNUM *g);
 
 /** 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;
@@ -96,6 +99,22 @@ crypto_validate_dh_params(const BIGNUM *p, const BIGNUM *g)
   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.
  */
@@ -117,62 +136,23 @@ crypto_set_dh_generator(void)
   dh_param_g = generator;
 }
 
-/** Set the global TLS Diffie-Hellman modulus.  Use the Apache mod_ssl DH
- * modulus. */
+/** Initialize our DH parameters. Idempotent. */
 void
-crypto_set_tls_dh_prime(void)
+crypto_dh_init_openssl(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);
-
-  r = BN_hex2bn(&tls_prime, TLS_DH_PRIME);
-  tor_assert(r);
+  if (dh_param_p && dh_param_g && dh_param_p_tls)
+    return;
 
-  tor_assert(tls_prime);
+  tor_assert(dh_param_g == NULL);
+  tor_assert(dh_param_p == NULL);
+  tor_assert(dh_param_p_tls == NULL);
 
-  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.
+  dh_param_p = bignum_from_hex(OAKLEY_PRIME_2);
+  dh_param_p_tls = bignum_from_hex(TLS_DH_PRIME);
 
-  circuit_dh_prime = BN_new();
-  tor_assert(circuit_dh_prime);
-
-  r = BN_hex2bn(&circuit_dh_prime, OAKLEY_PRIME_2);
-  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();
-  }
+  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
@@ -189,6 +169,7 @@ 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.
  */
@@ -201,7 +182,7 @@ crypto_dh_new(int dh_type)
              dh_type == DH_TYPE_REND);
 
   if (!dh_param_p)
-    init_dh_param();
+    crypto_dh_init();
 
   BIGNUM *dh_p = NULL;
   if (dh_type == DH_TYPE_TLS) {
@@ -215,6 +196,7 @@ crypto_dh_new(int dh_type)
     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 *
@@ -260,6 +242,7 @@ new_openssl_dh_from_params(BIGNUM *p, BIGNUM *g)
   /* 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)
@@ -386,7 +369,7 @@ tor_check_dh_key(int severity, const BIGNUM *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.
+    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.");
@@ -472,9 +455,10 @@ crypto_dh_free_(crypto_dh_t *dh)
   DH_free(dh->dh);
   tor_free(dh);
 }
+#endif
 
 void
-crypto_dh_free_all(void)
+crypto_dh_free_all_openssl(void)
 {
   if (dh_param_p)
     BN_clear_free(dh_param_p);
diff --git a/src/lib/crypt_ops/crypto_init.c b/src/lib/crypt_ops/crypto_init.c
index b651474cf..620fe8e1b 100644
--- a/src/lib/crypt_ops/crypto_init.c
+++ b/src/lib/crypt_ops/crypto_init.c
@@ -83,6 +83,8 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
 
     crypto_global_initialized_ = 1;
 
+    crypto_dh_init();
+
 #ifdef ENABLE_OPENSSL
     if (crypto_openssl_late_init(useAccel, accelName, accelDir) < 0)
       return -1;
diff --git a/src/lib/crypt_ops/include.am b/src/lib/crypt_ops/include.am
index a5541d49e..05451cc0e 100644
--- a/src/lib/crypt_ops/include.am
+++ b/src/lib/crypt_ops/include.am
@@ -25,10 +25,12 @@ src_lib_libtor_crypt_ops_a_SOURCES =			\
 if USE_NSS
 src_lib_libtor_crypt_ops_a_SOURCES +=			\
 	src/lib/crypt_ops/aes_nss.c			\
+	src/lib/crypt_ops/crypto_dh_nss.c		\
 	src/lib/crypt_ops/crypto_nss_mgt.c
 else
 src_lib_libtor_crypt_ops_a_SOURCES +=			\
-	src/lib/crypt_ops/aes_openssl.c
+	src/lib/crypt_ops/aes_openssl.c			\
+	src/lib/crypt_ops/crypto_dh_openssl.c
 endif
 
 if USE_OPENSSL
diff --git a/src/test/testing_common.c b/src/test/testing_common.c
index 1611a54b6..6957342c6 100644
--- a/src/test/testing_common.c
+++ b/src/test/testing_common.c
@@ -292,7 +292,6 @@ main(int c, const char **v)
     printf("Can't initialize crypto subsystem; exiting.\n");
     return 1;
   }
-  crypto_set_tls_dh_prime();
   if (crypto_seed_rng() < 0) {
     printf("Couldn't seed RNG; exiting.\n");
     return 1;





More information about the tor-commits mailing list