source: trunk/libtransmission/crypto.c @ 11716

Last change on this file since 11716 was 11716, checked in by jordan, 11 years ago

tr_sha1(): remove unnecessary casts from va_arg

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