source: trunk/libtransmission/crypto.c @ 11599

Last change on this file since 11599 was 11599, checked in by charles, 11 years ago

(trunk) Join the 21st century and use only 1 space at the end sentences. This commit is nearly as important as the semi-annual ones that remove trailing spaces from the ends of lines of code... :)

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