Line data Source code
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 */
6 :
7 : /**
8 : * \file crypto_dh_openssl.c
9 : * \brief Implement Tor's Z_p diffie-hellman stuff for OpenSSL.
10 : **/
11 :
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"
19 :
20 : DISABLE_GCC_WARNING("-Wredundant-decls")
21 :
22 : #include <openssl/dh.h>
23 :
24 : ENABLE_GCC_WARNING("-Wredundant-decls")
25 :
26 : #include <openssl/bn.h>
27 : #include <string.h>
28 :
29 : #ifndef ENABLE_NSS
30 : static int tor_check_dh_key(int severity, const BIGNUM *bn);
31 :
32 : /** A structure to hold the first half (x, g^x) of a Diffie-Hellman handshake
33 : * while we're waiting for the second.*/
34 : struct crypto_dh_t {
35 : DH *dh; /**< The openssl DH object */
36 : };
37 : #endif /* !defined(ENABLE_NSS) */
38 :
39 : static DH *new_openssl_dh_from_params(BIGNUM *p, BIGNUM *g);
40 :
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;
47 :
48 : /* This function is disabled unless we change the DH parameters. */
49 : #if 0
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.
53 : */
54 : static int
55 : crypto_validate_dh_params(const BIGNUM *p, const BIGNUM *g)
56 : {
57 : DH *dh = NULL;
58 : int ret = -1;
59 :
60 : /* Copy into a temporary DH object, just so that DH_check() can be called. */
61 : if (!(dh = DH_new()))
62 : goto out;
63 : #ifdef OPENSSL_1_1_API
64 : BIGNUM *dh_p, *dh_g;
65 : if (!(dh_p = BN_dup(p)))
66 : goto out;
67 : if (!(dh_g = BN_dup(g)))
68 : goto out;
69 : if (!DH_set0_pqg(dh, dh_p, NULL, dh_g))
70 : goto out;
71 : #else /* !defined(OPENSSL_1_1_API) */
72 : if (!(dh->p = BN_dup(p)))
73 : goto out;
74 : if (!(dh->g = BN_dup(g)))
75 : goto out;
76 : #endif /* defined(OPENSSL_1_1_API) */
77 :
78 : /* Perform the validation. */
79 : int codes = 0;
80 : if (!DH_check(dh, &codes))
81 : goto out;
82 : if (BN_is_word(g, DH_GENERATOR_2)) {
83 : /* Per https://wiki.openssl.org/index.php/Diffie-Hellman_parameters
84 : *
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.
87 : */
88 : BN_ULONG residue = BN_mod_word(p, 24);
89 : if (residue == 11 || residue == 23)
90 : codes &= ~DH_NOT_SUITABLE_GENERATOR;
91 : }
92 : if (codes != 0) /* Specifics on why the params suck is irrelevant. */
93 : goto out;
94 :
95 : /* Things are probably not evil. */
96 : ret = 0;
97 :
98 : out:
99 : if (dh)
100 : DH_free(dh);
101 : return ret;
102 : }
103 : #endif /* 0 */
104 :
105 : /**
106 : * Helper: convert <b>hex</b> to a bignum, and return it. Assert that the
107 : * operation was successful.
108 : */
109 : static BIGNUM *
110 11122 : bignum_from_hex(const char *hex)
111 : {
112 11122 : BIGNUM *result = BN_new();
113 11122 : tor_assert(result);
114 :
115 11122 : int r = BN_hex2bn(&result, hex);
116 11122 : tor_assert(r);
117 11122 : tor_assert(result);
118 11122 : return result;
119 : }
120 :
121 : /** Set the global Diffie-Hellman generator, used for both TLS and internal
122 : * DH stuff.
123 : */
124 : static void
125 5561 : crypto_set_dh_generator(void)
126 : {
127 5561 : BIGNUM *generator;
128 5561 : int r;
129 :
130 5561 : if (dh_param_g)
131 : return;
132 :
133 5561 : generator = BN_new();
134 5561 : tor_assert(generator);
135 :
136 5561 : r = BN_set_word(generator, DH_GENERATOR);
137 5561 : tor_assert(r);
138 :
139 5561 : dh_param_g = generator;
140 : }
141 :
142 : /** Initialize our DH parameters. Idempotent. */
143 : void
144 11081 : crypto_dh_init_openssl(void)
145 : {
146 11081 : if (dh_param_p && dh_param_g && dh_param_p_tls)
147 : return;
148 :
149 5561 : tor_assert(dh_param_g == NULL);
150 5561 : tor_assert(dh_param_p == NULL);
151 5561 : tor_assert(dh_param_p_tls == NULL);
152 :
153 5561 : crypto_set_dh_generator();
154 5561 : dh_param_p = bignum_from_hex(OAKLEY_PRIME_2);
155 5561 : dh_param_p_tls = bignum_from_hex(TLS_DH_PRIME);
156 :
157 : /* Checks below are disabled unless we change the hardcoded DH parameters. */
158 : #if 0
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));
161 : #endif
162 : }
163 :
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.
167 : */
168 : #define DH_PRIVATE_KEY_BITS 320
169 :
170 : /** Used by tortls.c: Get the DH* for use with TLS.
171 : */
172 : DH *
173 100 : crypto_dh_new_openssl_tls(void)
174 : {
175 100 : return new_openssl_dh_from_params(dh_param_p_tls, dh_param_g);
176 : }
177 :
178 : #ifndef ENABLE_NSS
179 : /** Allocate and return a new DH object for a key exchange. Returns NULL on
180 : * failure.
181 : */
182 : crypto_dh_t *
183 11 : crypto_dh_new(int dh_type)
184 : {
185 11 : crypto_dh_t *res = tor_malloc_zero(sizeof(crypto_dh_t));
186 :
187 11 : tor_assert(dh_type == DH_TYPE_CIRCUIT || dh_type == DH_TYPE_TLS ||
188 : dh_type == DH_TYPE_REND);
189 :
190 11 : if (!dh_param_p)
191 0 : crypto_dh_init();
192 :
193 11 : BIGNUM *dh_p = NULL;
194 11 : if (dh_type == DH_TYPE_TLS) {
195 1 : dh_p = dh_param_p_tls;
196 : } else {
197 10 : dh_p = dh_param_p;
198 : }
199 :
200 11 : res->dh = new_openssl_dh_from_params(dh_p, dh_param_g);
201 11 : if (res->dh == NULL)
202 0 : tor_free(res); // sets res to NULL.
203 11 : return res;
204 : }
205 : #endif /* !defined(ENABLE_NSS) */
206 :
207 : /** Create and return a new openssl DH from a given prime and generator. */
208 : static DH *
209 111 : new_openssl_dh_from_params(BIGNUM *p, BIGNUM *g)
210 : {
211 111 : DH *res_dh;
212 111 : if (!(res_dh = DH_new()))
213 0 : goto err;
214 :
215 111 : BIGNUM *dh_p = NULL, *dh_g = NULL;
216 111 : dh_p = BN_dup(p);
217 111 : if (!dh_p)
218 0 : goto err;
219 :
220 111 : dh_g = BN_dup(g);
221 111 : if (!dh_g) {
222 0 : BN_free(dh_p);
223 0 : goto err;
224 : }
225 :
226 : #ifdef OPENSSL_1_1_API
227 :
228 111 : if (!DH_set0_pqg(res_dh, dh_p, NULL, dh_g)) {
229 0 : goto err;
230 : }
231 :
232 111 : if (!DH_set_length(res_dh, DH_PRIVATE_KEY_BITS))
233 0 : goto err;
234 : #else /* !defined(OPENSSL_1_1_API) */
235 : res_dh->p = dh_p;
236 : res_dh->g = dh_g;
237 : res_dh->length = DH_PRIVATE_KEY_BITS;
238 : #endif /* defined(OPENSSL_1_1_API) */
239 :
240 : return res_dh;
241 :
242 : /* LCOV_EXCL_START
243 : * This error condition is only reached when an allocation fails */
244 : err:
245 : crypto_openssl_log_errors(LOG_WARN, "creating DH object");
246 : if (res_dh) DH_free(res_dh); /* frees p and g too */
247 : return NULL;
248 : /* LCOV_EXCL_STOP */
249 : }
250 :
251 : #ifndef ENABLE_NSS
252 : /** Return a copy of <b>dh</b>, sharing its internal state. */
253 : crypto_dh_t *
254 1 : crypto_dh_dup(const crypto_dh_t *dh)
255 : {
256 1 : crypto_dh_t *dh_new = tor_malloc_zero(sizeof(crypto_dh_t));
257 1 : tor_assert(dh);
258 1 : tor_assert(dh->dh);
259 1 : dh_new->dh = dh->dh;
260 1 : DH_up_ref(dh->dh);
261 1 : return dh_new;
262 : }
263 :
264 : /** Return the length of the DH key in <b>dh</b>, in bytes.
265 : */
266 : int
267 67 : crypto_dh_get_bytes(crypto_dh_t *dh)
268 : {
269 67 : tor_assert(dh);
270 67 : return DH_size(dh->dh);
271 : }
272 :
273 : /** Generate \<x,g^x\> for our part of the key exchange. Return 0 on
274 : * success, -1 on failure.
275 : */
276 : int
277 10 : crypto_dh_generate_public(crypto_dh_t *dh)
278 : {
279 : #ifndef OPENSSL_1_1_API
280 : again:
281 : #endif
282 10 : if (!DH_generate_key(dh->dh)) {
283 : /* LCOV_EXCL_START
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");
286 : return -1;
287 : /* LCOV_EXCL_STOP */
288 : }
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.
293 : */
294 10 : const BIGNUM *pub_key, *priv_key;
295 10 : DH_get0_key(dh->dh, &pub_key, &priv_key);
296 10 : if (tor_check_dh_key(LOG_WARN, pub_key)<0) {
297 0 : 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.");
299 0 : return -1;
300 : }
301 : #else /* !defined(OPENSSL_1_1_API) */
302 : if (tor_check_dh_key(LOG_WARN, dh->dh->pub_key)<0) {
303 : /* LCOV_EXCL_START
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;
311 : goto again;
312 : /* LCOV_EXCL_STOP */
313 : }
314 : #endif /* defined(OPENSSL_1_1_API) */
315 : return 0;
316 : }
317 :
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.
321 : */
322 : int
323 12 : crypto_dh_get_public(crypto_dh_t *dh, char *pubkey, size_t pubkey_len)
324 : {
325 12 : int bytes;
326 12 : tor_assert(dh);
327 :
328 12 : const BIGNUM *dh_pub;
329 :
330 : #ifdef OPENSSL_1_1_API
331 12 : const BIGNUM *dh_priv;
332 12 : DH_get0_key(dh->dh, &dh_pub, &dh_priv);
333 : #else
334 : dh_pub = dh->dh->pub_key;
335 : #endif /* defined(OPENSSL_1_1_API) */
336 :
337 12 : if (!dh_pub) {
338 10 : if (crypto_dh_generate_public(dh)<0)
339 : return -1;
340 : else {
341 : #ifdef OPENSSL_1_1_API
342 10 : DH_get0_key(dh->dh, &dh_pub, &dh_priv);
343 : #else
344 : dh_pub = dh->dh->pub_key;
345 : #endif
346 : }
347 : }
348 :
349 12 : tor_assert(dh_pub);
350 12 : bytes = BN_num_bytes(dh_pub);
351 12 : tor_assert(bytes >= 0);
352 12 : if (pubkey_len < (size_t)bytes) {
353 1 : log_warn(LD_CRYPTO,
354 : "Weird! pubkey_len (%d) was smaller than DH1024_KEY_LEN (%d)",
355 : (int) pubkey_len, bytes);
356 1 : return -1;
357 : }
358 :
359 11 : memset(pubkey, 0, pubkey_len);
360 11 : BN_bn2bin(dh_pub, (unsigned char*)(pubkey+(pubkey_len-bytes)));
361 :
362 11 : return 0;
363 : }
364 :
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.
368 : */
369 : static int
370 39 : tor_check_dh_key(int severity, const BIGNUM *bn)
371 : {
372 39 : BIGNUM *x;
373 39 : char *s;
374 39 : tor_assert(bn);
375 39 : x = BN_new();
376 39 : tor_assert(x);
377 39 : if (BUG(!dh_param_p))
378 : crypto_dh_init(); //LCOV_EXCL_LINE we already checked whether we did this.
379 39 : BN_set_word(x, 1);
380 39 : if (BN_cmp(bn,x)<=0) {
381 6 : log_fn(severity, LD_CRYPTO, "DH key must be at least 2.");
382 6 : goto err;
383 : }
384 33 : BN_copy(x,dh_param_p);
385 33 : BN_sub_word(x, 1);
386 33 : if (BN_cmp(bn,x)>=0) {
387 6 : log_fn(severity, LD_CRYPTO, "DH key must be at most p-2.");
388 6 : goto err;
389 : }
390 27 : BN_clear_free(x);
391 27 : return 0;
392 12 : err:
393 12 : BN_clear_free(x);
394 12 : s = BN_bn2hex(bn);
395 12 : log_fn(severity, LD_CRYPTO, "Rejecting insecure DH key [%s]", s);
396 12 : OPENSSL_free(s);
397 12 : return -1;
398 : }
399 :
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,
404 : * or -1 on failure.
405 : *
406 : * This function MUST validate that g^y is actually in the group.
407 : */
408 : ssize_t
409 29 : 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)
412 : {
413 29 : BIGNUM *pubkey_bn = NULL;
414 29 : size_t secret_len=0;
415 29 : int result=0;
416 :
417 29 : tor_assert(dh);
418 29 : tor_assert(secret_bytes_out/DIGEST_LEN <= 255);
419 29 : tor_assert(pubkey_len < INT_MAX);
420 :
421 29 : if (BUG(crypto_dh_get_bytes(dh) > (int)secret_bytes_out)) {
422 0 : goto error;
423 : }
424 :
425 29 : if (!(pubkey_bn = BN_bin2bn((const unsigned char*)pubkey,
426 : (int)pubkey_len, NULL)))
427 0 : goto error;
428 29 : if (tor_check_dh_key(severity, pubkey_bn)<0) {
429 : /* Check for invalid public keys. */
430 12 : log_fn(severity, LD_CRYPTO,"Rejected invalid g^x");
431 12 : goto error;
432 : }
433 17 : result = DH_compute_key(secret_out, pubkey_bn, dh->dh);
434 17 : if (result < 0) {
435 1 : log_warn(LD_CRYPTO,"DH_compute_key() failed.");
436 1 : goto error;
437 : }
438 16 : secret_len = result;
439 :
440 16 : goto done;
441 : error:
442 : result = -1;
443 29 : done:
444 29 : crypto_openssl_log_errors(LOG_WARN, "completing DH handshake");
445 29 : if (pubkey_bn)
446 29 : BN_clear_free(pubkey_bn);
447 29 : if (result < 0)
448 : return result;
449 : else
450 16 : return secret_len;
451 : }
452 :
453 : /** Free a DH key exchange object.
454 : */
455 : void
456 166 : crypto_dh_free_(crypto_dh_t *dh)
457 : {
458 166 : if (!dh)
459 : return;
460 12 : tor_assert(dh->dh);
461 12 : DH_free(dh->dh);
462 12 : tor_free(dh);
463 : }
464 : #endif /* !defined(ENABLE_NSS) */
465 :
466 : void
467 235 : crypto_dh_free_all_openssl(void)
468 : {
469 235 : if (dh_param_p)
470 235 : BN_clear_free(dh_param_p);
471 235 : if (dh_param_p_tls)
472 235 : BN_clear_free(dh_param_p_tls);
473 235 : if (dh_param_g)
474 235 : BN_clear_free(dh_param_g);
475 :
476 235 : dh_param_p = dh_param_p_tls = dh_param_g = NULL;
477 235 : }
|