source: trunk/libtransmission/crypto.c @ 14282

Last change on this file since 14282 was 14282, checked in by jordan, 8 years ago

fix tr_cryptoGetTorrentHash() behavior for crypto-test.c

  • Property svn:keywords set to Date Rev Author Id
File size: 8.9 KB
Line 
1/*
2 * This file Copyright (C) 2007-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: crypto.c 14282 2014-05-18 20:47:58Z jordan $
8 */
9
10#include <assert.h>
11#include <inttypes.h> /* uint8_t */
12#include <stdarg.h>
13#include <stdlib.h> /* abs () */
14#include <string.h> /* memcpy (), memset (), strcmp () */
15
16#include <openssl/bn.h>
17#include <openssl/dh.h>
18#include <openssl/err.h>
19#include <openssl/rc4.h>
20#include <openssl/sha.h>
21#include <openssl/rand.h>
22
23#include "transmission.h"
24#include "crypto.h"
25#include "log.h"
26#include "utils.h"
27
28#define MY_NAME "tr_crypto"
29
30/**
31***
32**/
33
34void
35tr_sha1 (uint8_t * setme, const void * content1, int content1_len, ...)
36{
37  va_list vl;
38  SHA_CTX sha;
39  const void * content;
40
41  SHA1_Init (&sha);
42  SHA1_Update (&sha, content1, content1_len);
43
44  va_start (vl, content1_len);
45  while ((content = va_arg (vl, const void*)))
46    SHA1_Update (&sha, content, va_arg (vl, int));
47  va_end (vl);
48
49  SHA1_Final (setme, &sha);
50}
51
52/**
53***
54**/
55
56#define KEY_LEN 96
57
58#define PRIME_LEN 96
59
60#define DH_PRIVKEY_LEN_MIN 16
61#define DH_PRIVKEY_LEN 20
62
63static const uint8_t dh_P[PRIME_LEN] =
64{
65  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
66  0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
67  0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
68  0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
69  0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
70  0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
71  0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
72  0xA6, 0x3A, 0x36, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x63,
73};
74
75static const uint8_t dh_G[] = { 2 };
76
77/**
78***
79**/
80
81#define logErrorFromSSL(...) \
82  do { \
83    if (tr_logLevelIsActive (TR_LOG_ERROR)) { \
84      char buf[512]; \
85      ERR_error_string_n (ERR_get_error (), buf, sizeof (buf)); \
86      tr_logAddMessage (__FILE__, __LINE__, TR_LOG_ERROR, MY_NAME, "%s", buf); \
87    } \
88  } while (0)
89
90static void
91ensureKeyExists (tr_crypto * crypto)
92{
93  if (crypto->dh == NULL)
94    {
95      int len, offset;
96      DH * dh = DH_new ();
97
98      dh->p = BN_bin2bn (dh_P, sizeof (dh_P), NULL);
99      if (dh->p == NULL)
100        logErrorFromSSL ();
101
102      dh->g = BN_bin2bn (dh_G, sizeof (dh_G), NULL);
103      if (dh->g == NULL)
104        logErrorFromSSL ();
105
106      /* private DH value: strong random BN of DH_PRIVKEY_LEN*8 bits */
107      dh->priv_key = BN_new ();
108      do
109        {
110          if (BN_rand (dh->priv_key, DH_PRIVKEY_LEN * 8, -1, 0) != 1)
111            logErrorFromSSL ();
112        }
113      while (BN_num_bits (dh->priv_key) < DH_PRIVKEY_LEN_MIN * 8);
114
115      if (!DH_generate_key (dh))
116        logErrorFromSSL ();
117
118      /* DH can generate key sizes that are smaller than the size of
119         P with exponentially decreasing probability, in which case
120         the msb's of myPublicKey need to be zeroed appropriately. */
121      len = BN_num_bytes (dh->pub_key);
122      offset = KEY_LEN - len;
123      assert (len <= KEY_LEN);
124      memset (crypto->myPublicKey, 0, offset);
125      BN_bn2bin (dh->pub_key, crypto->myPublicKey + offset);
126
127      crypto->dh = dh;
128    }
129}
130
131void
132tr_cryptoConstruct (tr_crypto * crypto, const uint8_t * torrentHash, bool isIncoming)
133{
134  memset (crypto, 0, sizeof (tr_crypto));
135
136  crypto->dh = NULL;
137  crypto->isIncoming = isIncoming;
138  tr_cryptoSetTorrentHash (crypto, torrentHash);
139}
140
141void
142tr_cryptoDestruct (tr_crypto * crypto)
143{
144  if (crypto->dh != NULL)
145    DH_free (crypto->dh);
146}
147
148/**
149***
150**/
151
152const uint8_t*
153tr_cryptoComputeSecret (tr_crypto *     crypto,
154                        const uint8_t * peerPublicKey)
155{
156  DH * dh;
157  int len;
158  uint8_t secret[KEY_LEN];
159  BIGNUM * bn = BN_bin2bn (peerPublicKey, KEY_LEN, NULL);
160
161  ensureKeyExists (crypto);
162  dh = crypto->dh;
163
164  assert (DH_size (dh) == KEY_LEN);
165
166  len = DH_compute_key (secret, bn, dh);
167  if (len == -1)
168    {
169      logErrorFromSSL ();
170    }
171  else
172    {
173      int offset;
174      assert (len <= KEY_LEN);
175      offset = KEY_LEN - len;
176      memset (crypto->mySecret, 0, offset);
177      memcpy (crypto->mySecret + offset, secret, len);
178      crypto->mySecretIsSet = 1;
179    }
180
181  BN_free (bn);
182  return crypto->mySecret;
183}
184
185const uint8_t*
186tr_cryptoGetMyPublicKey (const tr_crypto * crypto,
187                         int             * setme_len)
188{
189  ensureKeyExists ((tr_crypto *) crypto);
190  *setme_len = KEY_LEN;
191  return crypto->myPublicKey;
192}
193
194/**
195***
196**/
197
198static void
199initRC4 (tr_crypto  * crypto,
200         RC4_KEY    * setme,
201         const char * key)
202{
203  SHA_CTX sha;
204  uint8_t buf[SHA_DIGEST_LENGTH];
205
206  assert (crypto->torrentHashIsSet);
207  assert (crypto->mySecretIsSet);
208
209  if (SHA1_Init (&sha)
210      && SHA1_Update (&sha, key, 4)
211      && SHA1_Update (&sha, crypto->mySecret, KEY_LEN)
212      && SHA1_Update (&sha, crypto->torrentHash, SHA_DIGEST_LENGTH)
213      && SHA1_Final (buf, &sha))
214    {
215      RC4_set_key (setme, SHA_DIGEST_LENGTH, buf);
216    }
217  else
218    {
219      logErrorFromSSL ();
220    }
221}
222
223void
224tr_cryptoDecryptInit (tr_crypto * crypto)
225{
226  unsigned char discard[1024];
227  const char * txt = crypto->isIncoming ? "keyA" : "keyB";
228
229  initRC4 (crypto, &crypto->dec_key, txt);
230  RC4 (&crypto->dec_key, sizeof (discard), discard, discard);
231}
232
233void
234tr_cryptoDecrypt (tr_crypto  * crypto,
235                  size_t       buf_len,
236                  const void * buf_in,
237                  void       * buf_out)
238{
239  RC4 (&crypto->dec_key, buf_len,
240       (const unsigned char*)buf_in,
241       (unsigned char*)buf_out);
242}
243
244void
245tr_cryptoEncryptInit (tr_crypto * crypto)
246{
247  unsigned char discard[1024];
248  const char * txt = crypto->isIncoming ? "keyB" : "keyA";
249
250  initRC4 (crypto, &crypto->enc_key, txt);
251  RC4 (&crypto->enc_key, sizeof (discard), discard, discard);
252}
253
254void
255tr_cryptoEncrypt (tr_crypto  * crypto,
256                  size_t       buf_len,
257                  const void * buf_in,
258                  void       * buf_out)
259{
260  RC4 (&crypto->enc_key, buf_len,
261       (const unsigned char*)buf_in,
262       (unsigned char*)buf_out);
263}
264
265/**
266***
267**/
268
269void
270tr_cryptoSetTorrentHash (tr_crypto     * crypto,
271                         const uint8_t * hash)
272{
273  crypto->torrentHashIsSet = hash ? 1 : 0;
274
275  if (hash)
276    memcpy (crypto->torrentHash, hash, SHA_DIGEST_LENGTH);
277  else
278    memset (crypto->torrentHash, 0, SHA_DIGEST_LENGTH);
279}
280
281const uint8_t*
282tr_cryptoGetTorrentHash (const tr_crypto * crypto)
283{
284  assert (crypto);
285
286  return crypto->torrentHashIsSet ? crypto->torrentHash : NULL;
287}
288
289int
290tr_cryptoHasTorrentHash (const tr_crypto * crypto)
291{
292  assert (crypto);
293
294  return crypto->torrentHashIsSet ? 1 : 0;
295}
296
297int
298tr_cryptoRandInt (int upperBound)
299{
300  int noise;
301  int val;
302
303  assert (upperBound > 0);
304
305  if (RAND_pseudo_bytes ((unsigned char *) &noise, sizeof noise) >= 0)
306    {
307      val = abs (noise) % upperBound;
308    }
309  else /* fall back to a weaker implementation... */
310    {
311      val = tr_cryptoWeakRandInt (upperBound);
312    }
313
314  return val;
315}
316
317int
318tr_cryptoWeakRandInt (int upperBound)
319{
320  static bool init = false;
321
322  assert (upperBound > 0);
323
324  if (!init)
325    {
326      srand (tr_time_msec ());
327      init = true;
328    }
329
330  return rand () % upperBound;
331}
332
333void
334tr_cryptoRandBuf (void * buf, size_t len)
335{
336  if (RAND_pseudo_bytes ((unsigned char*)buf, len) != 1)
337    logErrorFromSSL ();
338}
339
340/***
341****
342***/
343
344char*
345tr_ssha1 (const void * plaintext)
346{
347  enum { saltval_len = 8,
348         salter_len  = 64 };
349  static const char * salter = "0123456789"
350                               "abcdefghijklmnopqrstuvwxyz"
351                               "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
352                               "./";
353
354  size_t i;
355  unsigned char salt[saltval_len];
356  uint8_t sha[SHA_DIGEST_LENGTH];
357  char buf[2*SHA_DIGEST_LENGTH + saltval_len + 2];
358
359  tr_cryptoRandBuf (salt, saltval_len);
360  for (i=0; i<saltval_len; ++i)
361    salt[i] = salter[ salt[i] % salter_len ];
362
363  tr_sha1 (sha, plaintext, strlen (plaintext), salt, saltval_len, NULL);
364  tr_sha1_to_hex (&buf[1], sha);
365  memcpy (&buf[1+2*SHA_DIGEST_LENGTH], &salt, saltval_len);
366  buf[1+2*SHA_DIGEST_LENGTH + saltval_len] = '\0';
367  buf[0] = '{'; /* signal that this is a hash. this makes saving/restoring easier */
368
369  return tr_strdup (&buf);
370}
371
372bool
373tr_ssha1_matches (const char * source, const char * pass)
374{
375  char * salt;
376  size_t saltlen;
377  char * hashed;
378  uint8_t buf[SHA_DIGEST_LENGTH];
379  bool result;
380  const size_t sourcelen = strlen (source);
381
382  /* extract the salt */
383  if (sourcelen < 2*SHA_DIGEST_LENGTH-1)
384    return false;
385  saltlen = sourcelen - 2*SHA_DIGEST_LENGTH-1;
386  salt = tr_malloc (saltlen);
387  memcpy (salt, source + 2*SHA_DIGEST_LENGTH+1, saltlen);
388
389  /* hash pass + salt */
390  hashed = tr_malloc (2*SHA_DIGEST_LENGTH + saltlen + 2);
391  tr_sha1 (buf, pass, strlen (pass), salt, saltlen, NULL);
392  tr_sha1_to_hex (&hashed[1], buf);
393  memcpy (hashed + 1+2*SHA_DIGEST_LENGTH, salt, saltlen);
394  hashed[1+2*SHA_DIGEST_LENGTH + saltlen] = '\0';
395  hashed[0] = '{';
396
397  result = strcmp (source, hashed) == 0 ? true : false;
398
399  tr_free (hashed);
400  tr_free (salt);
401
402  return result;
403}
Note: See TracBrowser for help on using the repository browser.