source: trunk/libtransmission/crypto.c @ 8072

Last change on this file since 8072 was 8072, checked in by livings124, 13 years ago

#1276 encrypt the password to access web client interface using SHA-2

  • Property svn:keywords set to Date Rev Author Id
File size: 8.5 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 8072 2009-03-17 21:50:20Z livings124 $
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/des.h>
23#include <openssl/dh.h>
24#include <openssl/err.h>
25#include <openssl/rc4.h>
26#include <openssl/sha.h>
27#include <openssl/rand.h>
28
29#include <event.h>
30
31#include "crypto.h"
32#include "utils.h"
33
34#define MY_NAME "tr_crypto"
35
36/**
37***
38**/
39
40void
41tr_sha1( uint8_t *    setme,
42         const void * content1,
43         int          content1_len,
44         ... )
45{
46    va_list vl;
47    SHA_CTX sha;
48
49    SHA1_Init( &sha );
50    SHA1_Update( &sha, content1, content1_len );
51
52    va_start( vl, content1_len );
53    for( ; ; )
54    {
55        const void * content = (const void*) va_arg( vl, const void* );
56        const int    content_len = content ? (int) va_arg( vl, int ) : -1;
57        if( content == NULL || content_len < 1 )
58            break;
59        SHA1_Update( &sha, content, content_len );
60    }
61    va_end( vl );
62    SHA1_Final( setme, &sha );
63}
64
65/**
66***
67**/
68
69#define KEY_LEN 96
70
71#define PRIME_LEN 96
72
73static const uint8_t dh_P[PRIME_LEN] =
74{
75    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
76    0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
77    0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
78    0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
79    0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
80    0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
81    0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
82    0xA6, 0x3A, 0x36, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x63,
83};
84
85static const uint8_t dh_G[] = { 2 };
86
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};
98
99/**
100***
101**/
102
103#define logErrorFromSSL( ... ) \
104    do { \
105        if( tr_msgLoggingIsActive( TR_MSG_ERR ) ) { \
106            char buf[512]; \
107            ERR_error_string_n( ERR_get_error( ), buf, sizeof( buf ) ); \
108            tr_msg( __FILE__, __LINE__, TR_MSG_ERR, MY_NAME, "%s", buf ); \
109        } \
110    } while( 0 )
111
112static DH*
113getSharedDH( void )
114{
115    static DH * dh = NULL;
116
117    if( dh == NULL )
118    {
119        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        if( !DH_generate_key( dh ) )
130            logErrorFromSSL( );
131    }
132
133    return dh;
134}
135
136tr_crypto *
137tr_cryptoNew( const uint8_t * torrentHash,
138              int             isIncoming )
139{
140    int         len, offset;
141    tr_crypto * crypto;
142    DH *        dh = getSharedDH( );
143
144    crypto = tr_new0( tr_crypto, 1 );
145    crypto->isIncoming = isIncoming ? 1 : 0;
146    tr_cryptoSetTorrentHash( crypto, torrentHash );
147
148    /* DH can generate key sizes that are smaller than the size of
149       P with exponentially decreasing probability, in which case
150       the msb's of myPublicKey need to be zeroed appropriately. */
151    len = DH_size( dh );
152    offset = KEY_LEN - len;
153    assert( len <= KEY_LEN );
154    memset( crypto->myPublicKey, 0, offset );
155    BN_bn2bin( dh->pub_key, crypto->myPublicKey + offset );
156
157    return crypto;
158}
159
160void
161tr_cryptoFree( tr_crypto * crypto )
162{
163    tr_free( crypto );
164}
165
166/**
167***
168**/
169
170const uint8_t*
171tr_cryptoComputeSecret( tr_crypto *     crypto,
172                        const uint8_t * peerPublicKey )
173{
174    int      len;
175    uint8_t  secret[KEY_LEN];
176    BIGNUM * bn = BN_bin2bn( peerPublicKey, KEY_LEN, NULL );
177    DH *     dh = getSharedDH( );
178
179    assert( DH_size( dh ) == KEY_LEN );
180
181    len = DH_compute_key( secret, bn, dh );
182    if( len == -1 )
183        logErrorFromSSL( );
184    else {
185        int offset;
186        assert( len <= KEY_LEN );
187        offset = KEY_LEN - len;
188        memset( crypto->mySecret, 0, offset );
189        memcpy( crypto->mySecret + offset, secret, len );
190        crypto->mySecretIsSet = 1;
191    }
192
193    BN_free( bn );
194    return crypto->mySecret;
195}
196
197const uint8_t*
198tr_cryptoGetMyPublicKey( const tr_crypto * crypto,
199                         int *             setme_len )
200{
201    *setme_len = KEY_LEN;
202    return crypto->myPublicKey;
203}
204
205/**
206***
207**/
208
209static void
210initRC4( tr_crypto *  crypto,
211         RC4_KEY *    setme,
212         const char * key )
213{
214    SHA_CTX sha;
215    uint8_t buf[SHA_DIGEST_LENGTH];
216
217    assert( crypto->torrentHashIsSet );
218    assert( crypto->mySecretIsSet );
219
220    if( SHA1_Init( &sha )
221        && SHA1_Update( &sha, key, 4 )
222        && SHA1_Update( &sha, crypto->mySecret, KEY_LEN )
223        && SHA1_Update( &sha, crypto->torrentHash, SHA_DIGEST_LENGTH )
224        && SHA1_Final( buf, &sha ) )
225    {
226        RC4_set_key( setme, SHA_DIGEST_LENGTH, buf );
227    }
228    else
229    {
230        logErrorFromSSL( );
231    }
232}
233
234void
235tr_cryptoDecryptInit( tr_crypto * crypto )
236{
237    unsigned char discard[1024];
238    const char *  txt = crypto->isIncoming ? "keyA" : "keyB";
239
240    initRC4( crypto, &crypto->dec_key, txt );
241    RC4( &crypto->dec_key, sizeof( discard ), discard, discard );
242}
243
244void
245tr_cryptoDecrypt( tr_crypto *  crypto,
246                  size_t       buf_len,
247                  const void * buf_in,
248                  void *       buf_out )
249{
250    RC4( &crypto->dec_key, buf_len,
251         (const unsigned char*)buf_in,
252         (unsigned char*)buf_out );
253}
254
255void
256tr_cryptoEncryptInit( tr_crypto * crypto )
257{
258    unsigned char discard[1024];
259    const char *  txt = crypto->isIncoming ? "keyB" : "keyA";
260
261    initRC4( crypto, &crypto->enc_key, txt );
262    RC4( &crypto->enc_key, sizeof( discard ), discard, discard );
263}
264
265void
266tr_cryptoEncrypt( tr_crypto *  crypto,
267                  size_t       buf_len,
268                  const void * buf_in,
269                  void *       buf_out )
270{
271    RC4( &crypto->enc_key, buf_len,
272         (const unsigned char*)buf_in,
273         (unsigned char*)buf_out );
274}
275
276/**
277***
278**/
279
280void
281tr_cryptoSetTorrentHash( tr_crypto *     crypto,
282                         const uint8_t * hash )
283{
284    crypto->torrentHashIsSet = hash ? 1 : 0;
285
286    if( hash )
287        memcpy( crypto->torrentHash, hash, SHA_DIGEST_LENGTH );
288    else
289        memset( crypto->torrentHash, 0, SHA_DIGEST_LENGTH );
290}
291
292const uint8_t*
293tr_cryptoGetTorrentHash( const tr_crypto * crypto )
294{
295    assert( crypto );
296    assert( crypto->torrentHashIsSet );
297
298    return crypto->torrentHash;
299}
300
301int
302tr_cryptoHasTorrentHash( const tr_crypto * crypto )
303{
304    assert( crypto );
305
306    return crypto->torrentHashIsSet ? 1 : 0;
307}
308
309int
310tr_cryptoRandInt( int upperBound )
311{
312    int noise;
313    int val;
314
315    if( RAND_pseudo_bytes ( (unsigned char *) &noise, sizeof noise ) >= 0 )
316    {
317        val = abs( noise ) % upperBound;
318    }
319    else /* fall back to a weaker implementation... */
320    {
321        val = tr_cryptoWeakRandInt( upperBound );
322    }
323
324    assert( val >= 0 );
325    assert( val < upperBound );
326    return val;
327}
328
329int
330tr_cryptoWeakRandInt( int upperBound )
331{
332    static int init = 0;
333
334    assert( upperBound > 0 );
335
336    if( !init )
337    {
338        srand( tr_date( ) );
339        init = 1;
340    }
341
342    return rand( ) % upperBound;
343}
344
345void
346tr_cryptoRandBuf( unsigned char *buf,
347                  size_t         len )
348{
349    if( RAND_pseudo_bytes ( buf, len ) != 1 )
350        logErrorFromSSL( );
351}
352
353/***
354****
355***/
356
357char*
358tr_crypt( const void * plaintext )
359{
360    static const char * salter = "0123456789"
361                                 "abcdefghijklmnopqrstuvwxyz"
362                                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
363                                 "./";
364    static const size_t salter_len = 64;
365
366    int i;
367    char salt[12];
368
369    memcpy( salt, "$1$", 3 );
370    for( i=0; i<8; ++i )
371        salt[3+i] = salter[ tr_cryptoRandInt( salter_len ) ];
372    salt[11] = '\0';
373
374    return tr_strdup( DES_crypt( plaintext, salt ) );
375}
Note: See TracBrowser for help on using the repository browser.