--- /dev/null
+/* 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;
+}
#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;
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.
*/
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
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.
*/
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) {
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 *
/* 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)
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.");
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);