LCOV - code coverage report
Current view: top level - lib/crypt_ops - crypto_dh_openssl.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 142 154 92.2 %
Date: 2021-11-24 03:28:48 Functions: 14 14 100.0 %

          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 : }

Generated by: LCOV version 1.14