Changeset 14358


Ignore:
Timestamp:
Dec 4, 2014, 7:18:08 PM (8 years ago)
Author:
mikedld
Message:

#4400, #5462: Move DH helpers to crypto-utils

On a way to factoring out OpenSSL support to a standalone file to ease
addition of other crypto libraries support in the future, move helpers
providing DH key exchange to crypto-utils.{c,h}. OpenSSL-related
functionality (DH context management) is moved to crypto-utils-openssl.c.
Since we know in advance that DH secret key management code will be the
same for most of backends, implement common functionality in separate
crypto-utils-fallback.c.

Add new tr_dh_ctx_t and tr_dh_secret_t types and functions to be
implemented by crypto backends:

  • tr_dh_new - allocate DH context,
  • tr_dh_free - free the context,
  • tr_dh_make_key - generate private/public keypair,
  • tr_dh_agree - perform DH key exchange and generate secret key,
  • tr_dh_secret_derive - calculate secret key hash,
  • tr_dh_secret_free - free the secret key,
  • tr_dh_align_key - align some DH key in the buffer allocated for it.

Make DH secret key not accessible in plain form outside the crypto
backend. This allows for implementations where the key is managed by
the underlying library and is not even exposed to our backend.

Location:
trunk/libtransmission
Files:
1 added
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/libtransmission/CMakeLists.txt

    r14355 r14358  
    1616    crypto.c
    1717    crypto-utils.c
     18    crypto-utils-fallback.c
    1819    crypto-utils-openssl.c
    1920    error.c
  • trunk/libtransmission/Makefile.am

    r14354 r14358  
    3030  crypto.c \
    3131  crypto-utils.c \
     32  crypto-utils-fallback.c \
    3233  crypto-utils-openssl.c \
    3334  error.c \
  • trunk/libtransmission/crypto-test.c

    r14355 r14358  
    7474  tr_cryptoConstruct (&a, hash, false);
    7575  tr_cryptoConstruct (&b, hash, true);
    76   tr_cryptoComputeSecret (&a, tr_cryptoGetMyPublicKey (&b, &i));
    77   tr_cryptoComputeSecret (&b, tr_cryptoGetMyPublicKey (&a, &i));
     76  check (tr_cryptoComputeSecret (&a, tr_cryptoGetMyPublicKey (&b, &i)));
     77  check (tr_cryptoComputeSecret (&b, tr_cryptoGetMyPublicKey (&a, &i)));
    7878
    7979  tr_cryptoEncryptInit (&a);
  • trunk/libtransmission/crypto-utils-openssl.c

    r14357 r14358  
    1010#include <assert.h>
    1111
     12#include <openssl/bn.h>
     13#include <openssl/dh.h>
    1214#include <openssl/err.h>
    1315#include <openssl/evp.h>
     
    1820#include "log.h"
    1921#include "utils.h"
     22
     23#define TR_CRYPTO_DH_SECRET_FALLBACK
     24#include "crypto-utils-fallback.c"
    2025
    2126/***
     
    194199***/
    195200
     201tr_dh_ctx_t
     202tr_dh_new (const uint8_t * prime_num,
     203           size_t          prime_num_length,
     204           const uint8_t * generator_num,
     205           size_t          generator_num_length)
     206{
     207  DH * handle = DH_new ();
     208
     209  assert (prime_num != NULL);
     210  assert (generator_num != NULL);
     211
     212  if (!check_pointer (handle->p = BN_bin2bn (prime_num, prime_num_length, NULL)) ||
     213      !check_pointer (handle->g = BN_bin2bn (generator_num, generator_num_length, NULL)))
     214    {
     215      DH_free (handle);
     216      handle = NULL;
     217    }
     218
     219  return handle;
     220}
     221
     222void
     223tr_dh_free (tr_dh_ctx_t handle)
     224{
     225  if (handle == NULL)
     226    return;
     227
     228  DH_free (handle);
     229}
     230
     231bool
     232tr_dh_make_key (tr_dh_ctx_t   raw_handle,
     233                size_t        private_key_length,
     234                uint8_t     * public_key,
     235                size_t      * public_key_length)
     236{
     237  DH * handle = raw_handle;
     238  int dh_size, my_public_key_length;
     239
     240  assert (handle != NULL);
     241  assert (public_key != NULL);
     242
     243  handle->length = private_key_length * 8;
     244
     245  if (!check_result (DH_generate_key (handle)))
     246    return false;
     247
     248  my_public_key_length = BN_bn2bin (handle->pub_key, public_key);
     249  dh_size = DH_size (handle);
     250
     251  tr_dh_align_key (public_key, my_public_key_length, dh_size);
     252
     253  if (public_key_length != NULL)
     254    *public_key_length = dh_size;
     255
     256  return true;
     257}
     258
     259tr_dh_secret_t
     260tr_dh_agree (tr_dh_ctx_t     handle,
     261             const uint8_t * other_public_key,
     262             size_t          other_public_key_length)
     263{
     264  struct tr_dh_secret * ret;
     265  int dh_size, secret_key_length;
     266  BIGNUM * other_key;
     267
     268  assert (handle != NULL);
     269  assert (other_public_key != NULL);
     270
     271  if (!check_pointer (other_key = BN_bin2bn (other_public_key, other_public_key_length, NULL)))
     272    return NULL;
     273
     274  dh_size = DH_size (handle);
     275  ret = tr_dh_secret_new (dh_size);
     276
     277  secret_key_length = DH_compute_key (ret->key, other_key, handle);
     278  if (check_result_neq (secret_key_length, -1))
     279    {
     280      tr_dh_secret_align (ret, secret_key_length);
     281    }
     282  else
     283    {
     284      tr_dh_secret_free (ret);
     285      ret = NULL;
     286    }
     287
     288  BN_free (other_key);
     289  return ret;
     290}
     291
     292/***
     293****
     294***/
     295
    196296bool
    197297tr_rand_buffer (void   * buffer,
  • trunk/libtransmission/crypto-utils.c

    r14355 r14358  
    1111#include <stdarg.h>
    1212#include <stdlib.h> /* abs (), srand (), rand () */
     13#include <string.h> /* memmove (), memset () */
    1314
    1415#include "transmission.h"
    1516#include "crypto-utils.h"
    1617#include "utils.h"
     18
     19/***
     20****
     21***/
     22
     23void
     24tr_dh_align_key (uint8_t * key_buffer,
     25                 size_t    key_size,
     26                 size_t    buffer_size)
     27{
     28  assert (key_size <= buffer_size);
     29
     30  /* DH can generate key sizes that are smaller than the size of
     31     key buffer with exponentially decreasing probability, in which case
     32     the msb's of key buffer need to be zeroed appropriately. */
     33  if (key_size < buffer_size)
     34    {
     35      const size_t offset = buffer_size - key_size;
     36      memmove (key_buffer + offset, key_buffer, key_size);
     37      memset (key_buffer, 0, offset);
     38    }
     39}
    1740
    1841/***
  • trunk/libtransmission/crypto-utils.h

    r14356 r14358  
    2525 /** @brief Opaque RC4 context type. */
    2626typedef void * tr_rc4_ctx_t;
     27 /** @brief Opaque DH context type. */
     28typedef void * tr_dh_ctx_t;
     29 /** @brief Opaque DH secret key type. */
     30typedef void * tr_dh_secret_t;
    2731
    2832/**
     
    7882
    7983/**
     84 * @brief Allocate and initialize new Diffie-Hellman (DH) key exchange context.
     85 */
     86tr_dh_ctx_t      tr_dh_new             (const uint8_t  * prime_num,
     87                                        size_t           prime_num_length,
     88                                        const uint8_t  * generator_num,
     89                                        size_t           generator_num_length);
     90
     91/**
     92 * @brief Free DH key exchange context.
     93 */
     94void             tr_dh_free            (tr_dh_ctx_t      handle);
     95
     96/**
     97 * @brief Generate private and public DH keys, export public key.
     98 */
     99bool             tr_dh_make_key        (tr_dh_ctx_t      handle,
     100                                        size_t           private_key_length,
     101                                        uint8_t        * public_key,
     102                                        size_t         * public_key_length);
     103
     104/**
     105 * @brief Perform DH key exchange, generate secret key.
     106 */
     107tr_dh_secret_t   tr_dh_agree           (tr_dh_ctx_t      handle,
     108                                        const uint8_t  * other_public_key,
     109                                        size_t           other_public_key_length);
     110
     111/**
     112 * @brief Calculate SHA1 hash of DH secret key, prepending and/or appending
     113 *        given data to the key during calculation.
     114 */
     115bool             tr_dh_secret_derive   (tr_dh_secret_t   handle,
     116                                        const void     * prepend_data,
     117                                        size_t           prepend_data_size,
     118                                        const void     * append_data,
     119                                        size_t           append_data_size,
     120                                        uint8_t        * hash);
     121
     122/**
     123 * @brief Free DH secret key returned by @ref tr_dh_agree.
     124 */
     125void             tr_dh_secret_free     (tr_dh_secret_t   handle);
     126
     127/**
     128 * @brief Align DH key (big-endian number) to required length (internal, do not use).
     129 */
     130void             tr_dh_align_key       (uint8_t        * key_buffer,
     131                                        size_t           key_size,
     132                                        size_t           buffer_size);
     133
     134/**
    80135 * @brief Returns a random number in the range of [0...upper_bound).
    81136 */
  • trunk/libtransmission/crypto.c

    r14356 r14358  
    99
    1010#include <assert.h>
    11 #include <stdarg.h>
    1211#include <string.h> /* memcpy (), memmove (), memset (), strcmp () */
    13 
    14 #include <openssl/bn.h>
    15 #include <openssl/dh.h>
    16 #include <openssl/err.h>
    1712
    1813#include "transmission.h"
    1914#include "crypto.h"
    2015#include "crypto-utils.h"
    21 #include "log.h"
    2216#include "utils.h"
    2317
    24 #define MY_NAME "tr_crypto"
    25 
    26 /**
    27 ***
    28 **/
    29 
    30 #define KEY_LEN 96
     18/**
     19***
     20**/
    3121
    3222#define PRIME_LEN 96
    33 
    34 #define DH_PRIVKEY_LEN_MIN 16
    3523#define DH_PRIVKEY_LEN 20
    3624
     
    5341**/
    5442
    55 #define logErrorFromSSL(...) \
    56   do { \
    57     if (tr_logLevelIsActive (TR_LOG_ERROR)) { \
    58       char buf[512]; \
    59       ERR_error_string_n (ERR_get_error (), buf, sizeof (buf)); \
    60       tr_logAddMessage (__FILE__, __LINE__, TR_LOG_ERROR, MY_NAME, "%s", buf); \
    61     } \
    62   } while (0)
    63 
    6443static void
    6544ensureKeyExists (tr_crypto * crypto)
     
    6746  if (crypto->dh == NULL)
    6847    {
    69       int len, offset;
    70       DH * dh = DH_new ();
    71 
    72       dh->p = BN_bin2bn (dh_P, sizeof (dh_P), NULL);
    73       if (dh->p == NULL)
    74         logErrorFromSSL ();
    75 
    76       dh->g = BN_bin2bn (dh_G, sizeof (dh_G), NULL);
    77       if (dh->g == NULL)
    78         logErrorFromSSL ();
    79 
    80       /* private DH value: strong random BN of DH_PRIVKEY_LEN*8 bits */
    81       dh->priv_key = BN_new ();
    82       do
    83         {
    84           if (BN_rand (dh->priv_key, DH_PRIVKEY_LEN * 8, -1, 0) != 1)
    85             logErrorFromSSL ();
    86         }
    87       while (BN_num_bits (dh->priv_key) < DH_PRIVKEY_LEN_MIN * 8);
    88 
    89       if (!DH_generate_key (dh))
    90         logErrorFromSSL ();
    91 
    92       /* DH can generate key sizes that are smaller than the size of
    93          P with exponentially decreasing probability, in which case
    94          the msb's of myPublicKey need to be zeroed appropriately. */
    95       len = BN_num_bytes (dh->pub_key);
    96       offset = KEY_LEN - len;
    97       assert (len <= KEY_LEN);
    98       memset (crypto->myPublicKey, 0, offset);
    99       BN_bn2bin (dh->pub_key, crypto->myPublicKey + offset);
    100 
    101       crypto->dh = dh;
     48      size_t public_key_length;
     49
     50      crypto->dh = tr_dh_new (dh_P, sizeof (dh_P), dh_G, sizeof (dh_G));
     51      tr_dh_make_key (crypto->dh, DH_PRIVKEY_LEN, crypto->myPublicKey, &public_key_length);
     52
     53      assert (public_key_length == KEY_LEN);
    10254    }
    10355}
     
    10860  memset (crypto, 0, sizeof (tr_crypto));
    10961
    110   crypto->dh = NULL;
    11162  crypto->isIncoming = isIncoming;
    11263  tr_cryptoSetTorrentHash (crypto, torrentHash);
     
    11667tr_cryptoDestruct (tr_crypto * crypto)
    11768{
    118   if (crypto->dh != NULL)
    119     DH_free (crypto->dh);
     69  tr_dh_secret_free (crypto->mySecret);
     70  tr_dh_free (crypto->dh);
    12071  tr_rc4_free (crypto->enc_key);
    12172  tr_rc4_free (crypto->dec_key);
     
    12677**/
    12778
    128 const uint8_t*
     79bool
    12980tr_cryptoComputeSecret (tr_crypto *     crypto,
    13081                        const uint8_t * peerPublicKey)
    13182{
    132   DH * dh;
    133   int len;
    134   uint8_t secret[KEY_LEN];
    135   BIGNUM * bn = BN_bin2bn (peerPublicKey, KEY_LEN, NULL);
    136 
    13783  ensureKeyExists (crypto);
    138   dh = crypto->dh;
    139 
    140   assert (DH_size (dh) == KEY_LEN);
    141 
    142   len = DH_compute_key (secret, bn, dh);
    143   if (len == -1)
    144     {
    145       logErrorFromSSL ();
    146     }
    147   else
    148     {
    149       int offset;
    150       assert (len <= KEY_LEN);
    151       offset = KEY_LEN - len;
    152       memset (crypto->mySecret, 0, offset);
    153       memcpy (crypto->mySecret + offset, secret, len);
    154       crypto->mySecretIsSet = true;
    155     }
    156 
    157   BN_free (bn);
    158   return crypto->mySecret;
     84  crypto->mySecret = tr_dh_agree (crypto->dh, peerPublicKey, KEY_LEN);
     85  return crypto->mySecret != NULL;
    15986}
    16087
     
    180107
    181108  assert (crypto->torrentHashIsSet);
    182   assert (crypto->mySecretIsSet);
    183109
    184110  if (*setme == NULL)
    185111    *setme = tr_rc4_new ();
    186112
    187   if (tr_sha1 (buf,
    188                key, 4,
    189                crypto->mySecret, KEY_LEN,
    190                crypto->torrentHash, SHA_DIGEST_LENGTH,
    191                NULL))
     113  if (tr_cryptoSecretKeySha1 (crypto,
     114                              key, 4,
     115                              crypto->torrentHash, SHA_DIGEST_LENGTH,
     116                              buf))
    192117    tr_rc4_set_key (*setme, buf, SHA_DIGEST_LENGTH);
    193118}
     
    247172}
    248173
     174bool
     175tr_cryptoSecretKeySha1 (const tr_crypto * crypto,
     176                        const void      * prepend_data,
     177                        size_t            prepend_data_size,
     178                        const void      * append_data,
     179                        size_t            append_data_size,
     180                        uint8_t         * hash)
     181{
     182  assert (crypto != NULL);
     183  assert (crypto->mySecret != NULL);
     184
     185  return tr_dh_secret_derive (crypto->mySecret,
     186                              prepend_data, prepend_data_size,
     187                              append_data, append_data_size,
     188                              hash);
     189}
     190
    249191/**
    250192***
  • trunk/libtransmission/crypto.h

    r14356 r14358  
    2525**/
    2626
    27 #include <openssl/dh.h> /* DH */
    28 
    2927enum
    3028{
    31     KEY_LEN = 96
     29  KEY_LEN = 96
    3230};
    3331
     
    3735    tr_rc4_ctx_t    dec_key;
    3836    tr_rc4_ctx_t    enc_key;
    39     DH *            dh;
     37    tr_dh_ctx_t     dh;
    4038    uint8_t         myPublicKey[KEY_LEN];
    41     uint8_t         mySecret[KEY_LEN];
     39    tr_dh_secret_t  mySecret;
    4240    uint8_t         torrentHash[SHA_DIGEST_LENGTH];
    4341    bool            isIncoming;
    4442    bool            torrentHashIsSet;
    45     bool            mySecretIsSet;
    4643}
    4744tr_crypto;
     
    6057bool           tr_cryptoHasTorrentHash (const tr_crypto * crypto);
    6158
    62 const uint8_t* tr_cryptoComputeSecret (tr_crypto *     crypto,
     59bool          tr_cryptoComputeSecret (tr_crypto *     crypto,
    6360                                       const uint8_t * peerPublicKey);
    6461
     
    8077                                 void *       buf_out);
    8178
     79bool           tr_cryptoSecretKeySha1 (const tr_crypto * crypto,
     80                                       const void      * prepend_data,
     81                                       size_t            prepend_data_size,
     82                                       const void      * append_data,
     83                                       size_t            append_data_size,
     84                                       uint8_t         * hash);
     85
    8286/* @} */
    8387
  • trunk/libtransmission/handshake.c

    r14354 r14358  
    102102  tr_crypto *           crypto;
    103103  tr_session *          session;
    104   uint8_t               mySecret[KEY_LEN];
    105104  handshake_state_t     state;
    106105  tr_encryption_mode    encryptionMode;
     
    386385}
    387386
     387static void
     388computeRequestHash (const tr_handshake * handshake,
     389                    const char         * name,
     390                    uint8_t            * hash)
     391{
     392  tr_cryptoSecretKeySha1 (handshake->crypto, name, 4, NULL, 0, hash);
     393}
     394
    388395static int
    389396readYb (tr_handshake * handshake, struct evbuffer * inbuf)
    390397{
    391398  int isEncrypted;
    392   const uint8_t * secret;
    393399  uint8_t yb[KEY_LEN];
    394400  struct evbuffer * outbuf;
     
    421427  /* compute the secret */
    422428  evbuffer_remove (inbuf, yb, KEY_LEN);
    423   secret = tr_cryptoComputeSecret (handshake->crypto, yb);
    424   memcpy (handshake->mySecret, secret, KEY_LEN);
     429  tr_cryptoComputeSecret (handshake->crypto, yb);
    425430
    426431  /* now send these: HASH ('req1', S), HASH ('req2', SKEY) xor HASH ('req3', S),
     
    431436  {
    432437    uint8_t req1[SHA_DIGEST_LENGTH];
    433     tr_sha1 (req1, "req1", 4, secret, KEY_LEN, NULL);
     438    computeRequestHash (handshake, "req1", req1);
    434439    evbuffer_add (outbuf, req1, SHA_DIGEST_LENGTH);
    435440  }
     
    443448
    444449    tr_sha1 (req2, "req2", 4, tr_cryptoGetTorrentHash (handshake->crypto), SHA_DIGEST_LENGTH, NULL);
    445     tr_sha1 (req3, "req3", 4, secret, KEY_LEN, NULL);
     450    computeRequestHash (handshake, "req3", req3);
    446451
    447452    for (i=0; i<SHA_DIGEST_LENGTH; ++i)
     
    729734  uint8_t * walk, outbuf[KEY_LEN + PadB_MAXLEN];
    730735  const uint8_t * myKey;
    731   const uint8_t * secret;
    732736  int len;
    733737
     
    739743  /* read the incoming peer's public key */
    740744  evbuffer_remove (inbuf, ya, KEY_LEN);
    741   secret = tr_cryptoComputeSecret (handshake->crypto, ya);
    742   memcpy (handshake->mySecret, secret, KEY_LEN);
    743   tr_sha1 (handshake->myReq1, "req1", 4, secret, KEY_LEN, NULL);
     745  tr_cryptoComputeSecret (handshake->crypto, ya);
     746  computeRequestHash (handshake, "req1", handshake->myReq1);
    744747
    745748  /* send our public key to the peer */
     
    811814  dbgmsg (handshake, "reading obfuscated torrent hash...");
    812815  evbuffer_remove (inbuf, req2, SHA_DIGEST_LENGTH);
    813   tr_sha1 (req3, "req3", 4, handshake->mySecret, KEY_LEN, NULL);
     816  computeRequestHash (handshake, "req3", req3);
    814817  for (i=0; i<SHA_DIGEST_LENGTH; ++i)
    815818    obfuscatedTorrentHash[i] = req2[i] ^ req3[i];
Note: See TracChangeset for help on using the changeset viewer.