source: trunk/libtransmission/crypto.c @ 14241

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

Copyedit the license's revised text: (1) remove unnecessary repitition use of the word 'license' from the top of the header and source files (2) add the standard 'we hope it's useful, but no warranty' clause to COPYING (3) make explicit that linking OpenSSL is allowed (see https://people.gnome.org/~markmc/openssl-and-the-gpl.html for background) (4) sync the Qt and GTK+ clients' license popups with COPYING's revised text

  • 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 14241 2014-01-21 03:10:30Z 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  assert (crypto->torrentHashIsSet);
286
287  return crypto->torrentHash;
288}
289
290int
291tr_cryptoHasTorrentHash (const tr_crypto * crypto)
292{
293  assert (crypto);
294
295  return crypto->torrentHashIsSet ? 1 : 0;
296}
297
298int
299tr_cryptoRandInt (int upperBound)
300{
301  int noise;
302  int val;
303
304  assert (upperBound > 0);
305
306  if (RAND_pseudo_bytes ((unsigned char *) &noise, sizeof noise) >= 0)
307    {
308      val = abs (noise) % upperBound;
309    }
310  else /* fall back to a weaker implementation... */
311    {
312      val = tr_cryptoWeakRandInt (upperBound);
313    }
314
315  return val;
316}
317
318int
319tr_cryptoWeakRandInt (int upperBound)
320{
321  static bool init = false;
322
323  assert (upperBound > 0);
324
325  if (!init)
326    {
327      srand (tr_time_msec ());
328      init = true;
329    }
330
331  return rand () % upperBound;
332}
333
334void
335tr_cryptoRandBuf (void * buf, size_t len)
336{
337  if (RAND_pseudo_bytes ((unsigned char*)buf, len) != 1)
338    logErrorFromSSL ();
339}
340
341/***
342****
343***/
344
345char*
346tr_ssha1 (const void * plaintext)
347{
348  enum { saltval_len = 8,
349         salter_len  = 64 };
350  static const char * salter = "0123456789"
351                               "abcdefghijklmnopqrstuvwxyz"
352                               "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
353                               "./";
354
355  size_t i;
356  unsigned char salt[saltval_len];
357  uint8_t sha[SHA_DIGEST_LENGTH];
358  char buf[2*SHA_DIGEST_LENGTH + saltval_len + 2];
359
360  tr_cryptoRandBuf (salt, saltval_len);
361  for (i=0; i<saltval_len; ++i)
362    salt[i] = salter[ salt[i] % salter_len ];
363
364  tr_sha1 (sha, plaintext, strlen (plaintext), salt, saltval_len, NULL);
365  tr_sha1_to_hex (&buf[1], sha);
366  memcpy (&buf[1+2*SHA_DIGEST_LENGTH], &salt, saltval_len);
367  buf[1+2*SHA_DIGEST_LENGTH + saltval_len] = '\0';
368  buf[0] = '{'; /* signal that this is a hash. this makes saving/restoring easier */
369
370  return tr_strdup (&buf);
371}
372
373bool
374tr_ssha1_matches (const char * source, const char * pass)
375{
376  char * salt;
377  size_t saltlen;
378  char * hashed;
379  uint8_t buf[SHA_DIGEST_LENGTH];
380  bool result;
381  const size_t sourcelen = strlen (source);
382
383  /* extract the salt */
384  if (sourcelen < 2*SHA_DIGEST_LENGTH-1)
385    return false;
386  saltlen = sourcelen - 2*SHA_DIGEST_LENGTH-1;
387  salt = tr_malloc (saltlen);
388  memcpy (salt, source + 2*SHA_DIGEST_LENGTH+1, saltlen);
389
390  /* hash pass + salt */
391  hashed = tr_malloc (2*SHA_DIGEST_LENGTH + saltlen + 2);
392  tr_sha1 (buf, pass, strlen (pass), salt, saltlen, NULL);
393  tr_sha1_to_hex (&hashed[1], buf);
394  memcpy (hashed + 1+2*SHA_DIGEST_LENGTH, salt, saltlen);
395  hashed[1+2*SHA_DIGEST_LENGTH + saltlen] = '\0';
396  hashed[0] = '{';
397
398  result = strcmp (source, hashed) == 0 ? true : false;
399
400  tr_free (hashed);
401  tr_free (salt);
402
403  return result;
404}
Note: See TracBrowser for help on using the repository browser.