1 /* Copyright (c) 2001, Matej Pfajfar.
2 * Copyright (c) 2001-2004, Roger Dingledine.
3 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
4 * Copyright (c) 2007-2021, The Tor Project, Inc. */
5 /* See LICENSE for licensing information */
8 * \file crypto_dh_openssl.c
9 * \brief Implement Tor's Z_p diffie-hellman stuff for OpenSSL.
12 #include "lib/crypt_ops/compat_openssl.h"
13 #include "lib/crypt_ops/crypto_dh.h"
14 #include "lib/crypt_ops/crypto_digest.h"
15 #include "lib/crypt_ops/crypto_hkdf.h"
16 #include "lib/crypt_ops/crypto_util.h"
17 #include "lib/log/log.h"
18 #include "lib/log/util_bug.h"
20 DISABLE_GCC_WARNING("-Wredundant-decls")
22 #include <openssl/dh.h>
24 ENABLE_GCC_WARNING("-Wredundant-decls")
26 #include <openssl/bn.h>
30 static int tor_check_dh_key(int severity
, const BIGNUM
*bn
);
32 /** A structure to hold the first half (x, g^x) of a Diffie-Hellman handshake
33 * while we're waiting for the second.*/
35 DH
*dh
; /**< The openssl DH object */
37 #endif /* !defined(ENABLE_NSS) */
39 static DH
*new_openssl_dh_from_params(BIGNUM
*p
, BIGNUM
*g
);
41 /** Shared P parameter for our circuit-crypto DH key exchanges. */
42 static BIGNUM
*dh_param_p
= NULL
;
43 /** Shared P parameter for our TLS DH key exchanges. */
44 static BIGNUM
*dh_param_p_tls
= NULL
;
45 /** Shared G parameter for our DH key exchanges. */
46 static BIGNUM
*dh_param_g
= NULL
;
48 /* This function is disabled unless we change the DH parameters. */
50 /** Validate a given set of Diffie-Hellman parameters. This is moderately
51 * computationally expensive (milliseconds), so should only be called when
52 * the DH parameters change. Returns 0 on success, * -1 on failure.
55 crypto_validate_dh_params(const BIGNUM
*p
, const BIGNUM
*g
)
60 /* Copy into a temporary DH object, just so that DH_check() can be called. */
63 #ifdef OPENSSL_1_1_API
65 if (!(dh_p
= BN_dup(p
)))
67 if (!(dh_g
= BN_dup(g
)))
69 if (!DH_set0_pqg(dh
, dh_p
, NULL
, dh_g
))
71 #else /* !defined(OPENSSL_1_1_API) */
72 if (!(dh
->p
= BN_dup(p
)))
74 if (!(dh
->g
= BN_dup(g
)))
76 #endif /* defined(OPENSSL_1_1_API) */
78 /* Perform the validation. */
80 if (!DH_check(dh
, &codes
))
82 if (BN_is_word(g
, DH_GENERATOR_2
)) {
83 /* Per https://wiki.openssl.org/index.php/Diffie-Hellman_parameters
85 * OpenSSL checks the prime is congruent to 11 when g = 2; while the
86 * IETF's primes are congruent to 23 when g = 2.
88 BN_ULONG residue
= BN_mod_word(p
, 24);
89 if (residue
== 11 || residue
== 23)
90 codes
&= ~DH_NOT_SUITABLE_GENERATOR
;
92 if (codes
!= 0) /* Specifics on why the params suck is irrelevant. */
95 /* Things are probably not evil. */
106 * Helper: convert <b>hex</b> to a bignum, and return it. Assert that the
107 * operation was successful.
110 bignum_from_hex(const char *hex
)
112 BIGNUM
*result
= BN_new();
115 int r
= BN_hex2bn(&result
, hex
);
121 /** Set the global Diffie-Hellman generator, used for both TLS and internal
125 crypto_set_dh_generator(void)
133 generator
= BN_new();
134 tor_assert(generator
);
136 r
= BN_set_word(generator
, DH_GENERATOR
);
139 dh_param_g
= generator
;
142 /** Initialize our DH parameters. Idempotent. */
144 crypto_dh_init_openssl(void)
146 if (dh_param_p
&& dh_param_g
&& dh_param_p_tls
)
149 tor_assert(dh_param_g
== NULL
);
150 tor_assert(dh_param_p
== NULL
);
151 tor_assert(dh_param_p_tls
== NULL
);
153 crypto_set_dh_generator();
154 dh_param_p
= bignum_from_hex(OAKLEY_PRIME_2
);
155 dh_param_p_tls
= bignum_from_hex(TLS_DH_PRIME
);
157 /* Checks below are disabled unless we change the hardcoded DH parameters. */
159 tor_assert(0 == crypto_validate_dh_params(dh_param_p
, dh_param_g
));
160 tor_assert(0 == crypto_validate_dh_params(dh_param_p_tls
, dh_param_g
));
164 /** Number of bits to use when choosing the x or y value in a Diffie-Hellman
165 * handshake. Since we exponentiate by this value, choosing a smaller one
166 * lets our handshake go faster.
168 #define DH_PRIVATE_KEY_BITS 320
170 /** Used by tortls.c: Get the DH* for use with TLS.
173 crypto_dh_new_openssl_tls(void)
175 return new_openssl_dh_from_params(dh_param_p_tls
, dh_param_g
);
179 /** Allocate and return a new DH object for a key exchange. Returns NULL on
183 crypto_dh_new(int dh_type
)
185 crypto_dh_t
*res
= tor_malloc_zero(sizeof(crypto_dh_t
));
187 tor_assert(dh_type
== DH_TYPE_CIRCUIT
|| dh_type
== DH_TYPE_TLS
||
188 dh_type
== DH_TYPE_REND
);
194 if (dh_type
== DH_TYPE_TLS
) {
195 dh_p
= dh_param_p_tls
;
200 res
->dh
= new_openssl_dh_from_params(dh_p
, dh_param_g
);
202 tor_free(res
); // sets res to NULL.
205 #endif /* !defined(ENABLE_NSS) */
207 /** Create and return a new openssl DH from a given prime and generator. */
209 new_openssl_dh_from_params(BIGNUM
*p
, BIGNUM
*g
)
212 if (!(res_dh
= DH_new()))
215 BIGNUM
*dh_p
= NULL
, *dh_g
= NULL
;
226 #ifdef OPENSSL_1_1_API
228 if (!DH_set0_pqg(res_dh
, dh_p
, NULL
, dh_g
)) {
232 if (!DH_set_length(res_dh
, DH_PRIVATE_KEY_BITS
))
234 #else /* !defined(OPENSSL_1_1_API) */
237 res_dh
->length
= DH_PRIVATE_KEY_BITS
;
238 #endif /* defined(OPENSSL_1_1_API) */
243 * This error condition is only reached when an allocation fails */
245 crypto_openssl_log_errors(LOG_WARN
, "creating DH object");
246 if (res_dh
) DH_free(res_dh
); /* frees p and g too */
252 /** Return a copy of <b>dh</b>, sharing its internal state. */
254 crypto_dh_dup(const crypto_dh_t
*dh
)
256 crypto_dh_t
*dh_new
= tor_malloc_zero(sizeof(crypto_dh_t
));
264 /** Return the length of the DH key in <b>dh</b>, in bytes.
267 crypto_dh_get_bytes(crypto_dh_t
*dh
)
270 return DH_size(dh
->dh
);
273 /** Generate \<x,g^x\> for our part of the key exchange. Return 0 on
274 * success, -1 on failure.
277 crypto_dh_generate_public(crypto_dh_t
*dh
)
279 #ifndef OPENSSL_1_1_API
282 if (!DH_generate_key(dh
->dh
)) {
284 * To test this we would need some way to tell openssl to break DH. */
285 crypto_openssl_log_errors(LOG_WARN
, "generating DH key");
289 #ifdef OPENSSL_1_1_API
290 /* OpenSSL 1.1.x doesn't appear to let you regenerate a DH key, without
291 * recreating the DH object. I have no idea what sort of aliasing madness
292 * can occur here, so do the check, and just bail on failure.
294 const BIGNUM
*pub_key
, *priv_key
;
295 DH_get0_key(dh
->dh
, &pub_key
, &priv_key
);
296 if (tor_check_dh_key(LOG_WARN
, pub_key
)<0) {
297 log_warn(LD_CRYPTO
, "Weird! Our own DH key was invalid. I guess once-in-"
298 "the-universe chances really do happen. Treating as a failure.");
301 #else /* !defined(OPENSSL_1_1_API) */
302 if (tor_check_dh_key(LOG_WARN
, dh
->dh
->pub_key
)<0) {
304 * If this happens, then openssl's DH implementation is busted. */
305 log_warn(LD_CRYPTO
, "Weird! Our own DH key was invalid. I guess once-in-"
306 "the-universe chances really do happen. Trying again.");
307 /* Free and clear the keys, so OpenSSL will actually try again. */
308 BN_clear_free(dh
->dh
->pub_key
);
309 BN_clear_free(dh
->dh
->priv_key
);
310 dh
->dh
->pub_key
= dh
->dh
->priv_key
= NULL
;
314 #endif /* defined(OPENSSL_1_1_API) */
318 /** Generate g^x as necessary, and write the g^x for the key exchange
319 * as a <b>pubkey_len</b>-byte value into <b>pubkey</b>. Return 0 on
320 * success, -1 on failure. <b>pubkey_len</b> must be \>= DH1024_KEY_LEN.
323 crypto_dh_get_public(crypto_dh_t
*dh
, char *pubkey
, size_t pubkey_len
)
328 const BIGNUM
*dh_pub
;
330 #ifdef OPENSSL_1_1_API
331 const BIGNUM
*dh_priv
;
332 DH_get0_key(dh
->dh
, &dh_pub
, &dh_priv
);
334 dh_pub
= dh
->dh
->pub_key
;
335 #endif /* defined(OPENSSL_1_1_API) */
338 if (crypto_dh_generate_public(dh
)<0)
341 #ifdef OPENSSL_1_1_API
342 DH_get0_key(dh
->dh
, &dh_pub
, &dh_priv
);
344 dh_pub
= dh
->dh
->pub_key
;
350 bytes
= BN_num_bytes(dh_pub
);
351 tor_assert(bytes
>= 0);
352 if (pubkey_len
< (size_t)bytes
) {
354 "Weird! pubkey_len (%d) was smaller than DH1024_KEY_LEN (%d)",
355 (int) pubkey_len
, bytes
);
359 memset(pubkey
, 0, pubkey_len
);
360 BN_bn2bin(dh_pub
, (unsigned char*)(pubkey
+(pubkey_len
-bytes
)));
365 /** Check for bad Diffie-Hellman public keys (g^x). Return 0 if the key is
366 * okay (in the subgroup [2,p-2]), or -1 if it's bad.
367 * See http://www.cl.cam.ac.uk/ftp/users/rja14/psandqs.ps.gz for some tips.
370 tor_check_dh_key(int severity
, const BIGNUM
*bn
)
377 if (BUG(!dh_param_p
))
378 crypto_dh_init(); //LCOV_EXCL_LINE we already checked whether we did this.
380 if (BN_cmp(bn
,x
)<=0) {
381 log_fn(severity
, LD_CRYPTO
, "DH key must be at least 2.");
384 BN_copy(x
,dh_param_p
);
386 if (BN_cmp(bn
,x
)>=0) {
387 log_fn(severity
, LD_CRYPTO
, "DH key must be at most p-2.");
395 log_fn(severity
, LD_CRYPTO
, "Rejecting insecure DH key [%s]", s
);
400 /** Given a DH key exchange object, and our peer's value of g^y (as a
401 * <b>pubkey_len</b>-byte value in <b>pubkey</b>) generate
402 * g^xy as a big-endian integer in <b>secret_out</b>.
403 * Return the number of bytes generated on success,
406 * This function MUST validate that g^y is actually in the group.
409 crypto_dh_handshake(int severity
, crypto_dh_t
*dh
,
410 const char *pubkey
, size_t pubkey_len
,
411 unsigned char *secret_out
, size_t secret_bytes_out
)
413 BIGNUM
*pubkey_bn
= NULL
;
418 tor_assert(secret_bytes_out
/DIGEST_LEN
<= 255);
419 tor_assert(pubkey_len
< INT_MAX
);
421 if (BUG(crypto_dh_get_bytes(dh
) > (int)secret_bytes_out
)) {
425 if (!(pubkey_bn
= BN_bin2bn((const unsigned char*)pubkey
,
426 (int)pubkey_len
, NULL
)))
428 if (tor_check_dh_key(severity
, pubkey_bn
)<0) {
429 /* Check for invalid public keys. */
430 log_fn(severity
, LD_CRYPTO
,"Rejected invalid g^x");
433 result
= DH_compute_key(secret_out
, pubkey_bn
, dh
->dh
);
435 log_warn(LD_CRYPTO
,"DH_compute_key() failed.");
444 crypto_openssl_log_errors(LOG_WARN
, "completing DH handshake");
446 BN_clear_free(pubkey_bn
);
453 /** Free a DH key exchange object.
456 crypto_dh_free_(crypto_dh_t
*dh
)
464 #endif /* !defined(ENABLE_NSS) */
467 crypto_dh_free_all_openssl(void)
470 BN_clear_free(dh_param_p
);
472 BN_clear_free(dh_param_p_tls
);
474 BN_clear_free(dh_param_g
);
476 dh_param_p
= dh_param_p_tls
= dh_param_g
= NULL
;