source: trunk/libtransmission/crypto.c @ 7870

Last change on this file since 7870 was 7870, checked in by charles, 14 years ago

(trunk libT) #1384: make tr_cryptoRandInt() simpler to read

  • Property svn:keywords set to Date Rev Author Id
File size: 7.9 KB
Line 
1/* * This file Copyright (C) 2007-2009 Charles Kerr <charles@transmissionbt.com>
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 7870 2009-02-10 21:43:08Z charles $
10 */
11
12#include <stdlib.h> /* for abs() */
13#include <limits.h> /* for INT_MAX */
14#include <sys/types.h> /* for event.h, as well as netinet/in.h on some platforms
15                         */
16#include <assert.h>
17#include <inttypes.h> /* uint8_t */
18#include <string.h> /* memcpy */
19#include <stdarg.h>
20
21#include <openssl/bn.h>
22#include <openssl/dh.h>
23#include <openssl/err.h>
24#include <openssl/rc4.h>
25#include <openssl/sha.h>
26#include <openssl/rand.h>
27
28#include <event.h>
29
30#include "crypto.h"
31#include "utils.h"
32
33#define MY_NAME "tr_crypto"
34
35/**
36***
37**/
38
39void
40tr_sha1( uint8_t *    setme,
41         const void * content1,
42         int          content1_len,
43         ... )
44{
45    va_list vl;
46    SHA_CTX sha;
47
48    SHA1_Init( &sha );
49    SHA1_Update( &sha, content1, content1_len );
50
51    va_start( vl, content1_len );
52    for( ; ; )
53    {
54        const void * content = (const void*) va_arg( vl, const void* );
55        const int    content_len = content ? (int) va_arg( vl, int ) : -1;
56        if( content == NULL || content_len < 1 )
57            break;
58        SHA1_Update( &sha, content, content_len );
59    }
60    va_end( vl );
61    SHA1_Final( setme, &sha );
62}
63
64/**
65***
66**/
67
68#define KEY_LEN 96
69
70#define PRIME_LEN 96
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
86struct tr_crypto
87{
88    RC4_KEY         dec_key;
89    RC4_KEY         enc_key;
90    uint8_t         torrentHash[SHA_DIGEST_LENGTH];
91    tr_bool         isIncoming;
92    tr_bool         torrentHashIsSet;
93    tr_bool         mySecretIsSet;
94    uint8_t         myPublicKey[KEY_LEN];
95    uint8_t         mySecret[KEY_LEN];
96};
97
98/**
99***
100**/
101
102#define logErrorFromSSL( ... ) \
103    do { \
104        if( tr_msgLoggingIsActive( TR_MSG_ERR ) ) { \
105            char buf[512]; \
106            ERR_error_string_n( ERR_get_error( ), buf, sizeof( buf ) ); \
107            tr_msg( __FILE__, __LINE__, TR_MSG_ERR, MY_NAME, "%s", buf ); \
108        } \
109    } while( 0 )
110
111static DH*
112getSharedDH( void )
113{
114    static DH * dh = NULL;
115
116    if( dh == NULL )
117    {
118        dh = DH_new( );
119
120        dh->p = BN_bin2bn( dh_P, sizeof( dh_P ), NULL );
121        if( dh->p == NULL )
122            logErrorFromSSL( );
123
124        dh->g = BN_bin2bn( dh_G, sizeof( dh_G ), NULL );
125        if( dh->g == NULL )
126            logErrorFromSSL( );
127
128        if( !DH_generate_key( dh ) )
129            logErrorFromSSL( );
130    }
131
132    return dh;
133}
134
135tr_crypto *
136tr_cryptoNew( const uint8_t * torrentHash,
137              int             isIncoming )
138{
139    int         len, offset;
140    tr_crypto * crypto;
141    DH *        dh = getSharedDH( );
142
143    crypto = tr_new0( tr_crypto, 1 );
144    crypto->isIncoming = isIncoming ? 1 : 0;
145    tr_cryptoSetTorrentHash( crypto, torrentHash );
146
147    /* DH can generate key sizes that are smaller than the size of
148       P with exponentially decreasing probability, in which case
149       the msb's of myPublicKey need to be zeroed appropriately. */
150    len = DH_size( dh );
151    offset = KEY_LEN - len;
152    assert( len <= KEY_LEN );
153    memset( crypto->myPublicKey, 0, offset );
154    BN_bn2bin( dh->pub_key, crypto->myPublicKey + offset );
155
156    return crypto;
157}
158
159void
160tr_cryptoFree( tr_crypto * crypto )
161{
162    tr_free( crypto );
163}
164
165/**
166***
167**/
168
169const uint8_t*
170tr_cryptoComputeSecret( tr_crypto *     crypto,
171                        const uint8_t * peerPublicKey )
172{
173    int      len;
174    uint8_t  secret[KEY_LEN];
175    BIGNUM * bn = BN_bin2bn( peerPublicKey, KEY_LEN, NULL );
176    DH *     dh = getSharedDH( );
177
178    assert( DH_size( dh ) == KEY_LEN );
179
180    len = DH_compute_key( secret, bn, dh );
181    if( len == -1 )
182        logErrorFromSSL( );
183    else {
184        int offset;
185        assert( len <= KEY_LEN );
186        offset = KEY_LEN - len;
187        memset( crypto->mySecret, 0, offset );
188        memcpy( crypto->mySecret + offset, secret, len );
189        crypto->mySecretIsSet = 1;
190    }
191
192    BN_free( bn );
193    return crypto->mySecret;
194}
195
196const uint8_t*
197tr_cryptoGetMyPublicKey( const tr_crypto * crypto,
198                         int *             setme_len )
199{
200    *setme_len = KEY_LEN;
201    return crypto->myPublicKey;
202}
203
204/**
205***
206**/
207
208static void
209initRC4( tr_crypto *  crypto,
210         RC4_KEY *    setme,
211         const char * key )
212{
213    SHA_CTX sha;
214    uint8_t buf[SHA_DIGEST_LENGTH];
215
216    assert( crypto->torrentHashIsSet );
217    assert( crypto->mySecretIsSet );
218
219    if( SHA1_Init( &sha )
220        && SHA1_Update( &sha, key, 4 )
221        && SHA1_Update( &sha, crypto->mySecret, KEY_LEN )
222        && SHA1_Update( &sha, crypto->torrentHash, SHA_DIGEST_LENGTH )
223        && SHA1_Final( buf, &sha ) )
224    {
225        RC4_set_key( setme, SHA_DIGEST_LENGTH, buf );
226    }
227    else
228    {
229        logErrorFromSSL( );
230    }
231}
232
233void
234tr_cryptoDecryptInit( tr_crypto * crypto )
235{
236    unsigned char discard[1024];
237    const char *  txt = crypto->isIncoming ? "keyA" : "keyB";
238
239    initRC4( crypto, &crypto->dec_key, txt );
240    RC4( &crypto->dec_key, sizeof( discard ), discard, discard );
241}
242
243void
244tr_cryptoDecrypt( tr_crypto *  crypto,
245                  size_t       buf_len,
246                  const void * buf_in,
247                  void *       buf_out )
248{
249    RC4( &crypto->dec_key, buf_len,
250         (const unsigned char*)buf_in,
251         (unsigned char*)buf_out );
252}
253
254void
255tr_cryptoEncryptInit( tr_crypto * crypto )
256{
257    unsigned char discard[1024];
258    const char *  txt = crypto->isIncoming ? "keyB" : "keyA";
259
260    initRC4( crypto, &crypto->enc_key, txt );
261    RC4( &crypto->enc_key, sizeof( discard ), discard, discard );
262}
263
264void
265tr_cryptoEncrypt( tr_crypto *  crypto,
266                  size_t       buf_len,
267                  const void * buf_in,
268                  void *       buf_out )
269{
270    RC4( &crypto->enc_key, buf_len,
271         (const unsigned char*)buf_in,
272         (unsigned char*)buf_out );
273}
274
275/**
276***
277**/
278
279void
280tr_cryptoSetTorrentHash( tr_crypto *     crypto,
281                         const uint8_t * hash )
282{
283    crypto->torrentHashIsSet = hash ? 1 : 0;
284
285    if( hash )
286        memcpy( crypto->torrentHash, hash, SHA_DIGEST_LENGTH );
287    else
288        memset( crypto->torrentHash, 0, SHA_DIGEST_LENGTH );
289}
290
291const uint8_t*
292tr_cryptoGetTorrentHash( const tr_crypto * crypto )
293{
294    assert( crypto );
295    assert( crypto->torrentHashIsSet );
296
297    return crypto->torrentHash;
298}
299
300int
301tr_cryptoHasTorrentHash( const tr_crypto * crypto )
302{
303    assert( crypto );
304
305    return crypto->torrentHashIsSet ? 1 : 0;
306}
307
308int
309tr_cryptoRandInt( int upperBound )
310{
311    int noise;
312    int val;
313
314    if( RAND_pseudo_bytes ( (unsigned char *) &noise, sizeof noise ) >= 0 )
315    {
316        val = abs( noise ) % upperBound;
317    }
318    else /* fall back to a weaker implementation... */
319    {
320        val = tr_cryptoWeakRandInt( upperBound );
321    }
322
323    assert( val >= 0 );
324    assert( val < upperBound );
325    return val;
326}
327
328int
329tr_cryptoWeakRandInt( int upperBound )
330{
331    static int init = 0;
332
333    assert( upperBound > 0 );
334
335    if( !init )
336    {
337        srand( tr_date( ) );
338        init = 1;
339    }
340
341    return rand( ) % upperBound;
342}
343
344void
345tr_cryptoRandBuf( unsigned char *buf,
346                  size_t         len )
347{
348    if( RAND_pseudo_bytes ( buf, len ) != 1 )
349        logErrorFromSSL( );
350}
351
Note: See TracBrowser for help on using the repository browser.