source: trunk/libtransmission/crypto.c @ 6517

Last change on this file since 6517 was 6517, checked in by muks, 13 years ago

Replace random number generation code

  • Property svn:keywords set to Date Rev Author Id
File size: 6.6 KB
Line 
1/*
2 * This file Copyright (C) 2007-2008 Charles Kerr <charles@rebelbase.com>
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 6517 2008-08-14 11:11:25Z muks $
11 */
12
13#include <stdlib.h> /* for abs() */
14#include <values.h> /* for INT_MAX */
15#include <sys/types.h> /* for event.h, as well as netinet/in.h on some platforms */
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/rc4.h>
24#include <openssl/sha.h>
25#include <openssl/rand.h>
26
27#include <event.h>
28
29#include "crypto.h"
30#include "utils.h"
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        const void * content = (const void*) va_arg( vl, const void* );
51        const int content_len = content ? (int) va_arg( vl, int ) : -1;
52        if( content==NULL || content_len<1 )
53            break;
54        SHA1_Update( &sha, content, content_len );
55    }
56    va_end( vl );
57    SHA1_Final( setme, &sha );
58}
59
60/**
61***
62**/
63
64#define KEY_LEN 96
65
66#define PRIME_LEN 96
67
68static const uint8_t dh_P[PRIME_LEN] =
69{
70    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
71    0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
72    0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
73    0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
74    0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
75    0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
76    0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
77    0xA6, 0x3A, 0x36, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x63,
78};
79
80static const uint8_t dh_G[] = { 2 };
81
82struct tr_crypto
83{
84    RC4_KEY dec_key;
85    RC4_KEY enc_key;
86    uint8_t torrentHash[SHA_DIGEST_LENGTH];
87    unsigned int isIncoming       : 1;
88    unsigned int torrentHashIsSet : 1;
89    unsigned int mySecretIsSet    : 1;
90    uint8_t myPublicKey[KEY_LEN];
91    uint8_t mySecret[KEY_LEN];
92};
93
94/**
95***
96**/
97
98static DH*
99getSharedDH( void )
100{
101    static DH * dh = NULL;
102
103    if( dh == NULL )
104    {
105        dh = DH_new( );
106        dh->p = BN_bin2bn( dh_P, sizeof(dh_P), NULL );
107        dh->g = BN_bin2bn( dh_G, sizeof(dh_G), NULL );
108        DH_generate_key( dh );
109    }
110
111    return dh;
112}
113
114tr_crypto * 
115tr_cryptoNew( const uint8_t * torrentHash,
116              int             isIncoming )
117{
118    int len, offset;
119    tr_crypto * crypto;
120    DH * dh = getSharedDH( );
121
122    crypto = tr_new0( tr_crypto, 1 );
123    crypto->isIncoming = isIncoming ? 1 : 0;
124    tr_cryptoSetTorrentHash( crypto, torrentHash );
125
126    /* DH can generate key sizes that are smaller than the size of
127       P with exponentially decreasing probability, in which case
128       the msb's of myPublicKey need to be zeroed appropriately. */
129    len = DH_size( dh );
130    offset = KEY_LEN - len;
131    assert( len <= KEY_LEN );
132    memset( crypto->myPublicKey, 0, offset );
133    BN_bn2bin( dh->pub_key, crypto->myPublicKey + offset );
134
135    return crypto;
136}
137
138void
139tr_cryptoFree( tr_crypto * crypto )
140{
141    tr_free( crypto );
142}
143
144/**
145***
146**/
147
148const uint8_t*
149tr_cryptoComputeSecret( tr_crypto      * crypto,
150                        const uint8_t  * peerPublicKey )
151{
152    int len, offset;
153    uint8_t secret[KEY_LEN];
154    BIGNUM * bn = BN_bin2bn( peerPublicKey, KEY_LEN, NULL );
155    DH * dh = getSharedDH( );
156    assert( DH_size(dh) == KEY_LEN );
157
158    len = DH_compute_key( secret, bn, dh );
159    assert( len <= KEY_LEN );
160    offset = KEY_LEN - len;
161    memset( crypto->mySecret, 0, offset );
162    memcpy( crypto->mySecret + offset, secret, len );
163    crypto->mySecretIsSet = 1;
164   
165    BN_free( bn );
166
167    return crypto->mySecret;
168}
169
170const uint8_t*
171tr_cryptoGetMyPublicKey( const tr_crypto * crypto, int * setme_len )
172{
173    *setme_len = KEY_LEN;
174    return crypto->myPublicKey;
175}
176
177/**
178***
179**/
180
181static void
182initRC4( tr_crypto * crypto, RC4_KEY * setme, const char * key )
183{
184    SHA_CTX sha;
185    uint8_t buf[SHA_DIGEST_LENGTH];
186
187    assert( crypto->torrentHashIsSet );
188    assert( crypto->mySecretIsSet );
189
190    SHA1_Init( &sha );
191    SHA1_Update( &sha, key, 4 );
192    SHA1_Update( &sha, crypto->mySecret, KEY_LEN );
193    SHA1_Update( &sha, crypto->torrentHash, SHA_DIGEST_LENGTH );
194    SHA1_Final( buf, &sha );
195    RC4_set_key( setme, SHA_DIGEST_LENGTH, buf );
196}
197
198void
199tr_cryptoDecryptInit( tr_crypto * crypto )
200{
201    unsigned char discard[1024];
202    const char * txt = crypto->isIncoming ? "keyA" : "keyB";
203    initRC4( crypto, &crypto->dec_key, txt );
204    RC4( &crypto->dec_key, sizeof(discard), discard, discard );
205}
206
207void
208tr_cryptoDecrypt( tr_crypto  * crypto,
209                  size_t       buf_len,
210                  const void * buf_in,
211                  void       * buf_out )
212{
213    RC4( &crypto->dec_key, buf_len,
214         (const unsigned char*)buf_in,
215         (unsigned char*)buf_out );
216}
217
218void
219tr_cryptoEncryptInit( tr_crypto * crypto )
220{
221    unsigned char discard[1024];
222    const char * txt = crypto->isIncoming ? "keyB" : "keyA";
223    initRC4( crypto, &crypto->enc_key, txt );
224    RC4( &crypto->enc_key, sizeof(discard), discard, discard );
225}
226
227void
228tr_cryptoEncrypt( tr_crypto  * crypto,
229                  size_t       buf_len,
230                  const void * buf_in,
231                  void       * buf_out )
232{
233    RC4( &crypto->enc_key, buf_len,
234         (const unsigned char*)buf_in,
235         (unsigned char*)buf_out );
236}
237
238/**
239***
240**/
241
242void
243tr_cryptoSetTorrentHash( tr_crypto     * crypto,
244                         const uint8_t * hash )
245{
246    crypto->torrentHashIsSet = hash ? 1 : 0;
247
248    if( hash )
249        memcpy( crypto->torrentHash, hash, SHA_DIGEST_LENGTH );
250    else
251        memset( crypto->torrentHash, 0, SHA_DIGEST_LENGTH );
252}
253
254const uint8_t*
255tr_cryptoGetTorrentHash( const tr_crypto * crypto )
256{
257    assert( crypto );
258    assert( crypto->torrentHashIsSet );
259
260    return crypto->torrentHash;
261}
262
263int
264tr_cryptoHasTorrentHash( const tr_crypto * crypto )
265{
266    assert( crypto );
267
268    return crypto->torrentHashIsSet ? 1 : 0;
269}
270
271int tr_cryptoRandInt( int sup )
272{
273    int r;
274
275    RAND_pseudo_bytes ((unsigned char *) &r, sizeof r);
276
277    return ((int) (sup * (abs(r) / (INT_MAX + 1.0))));
278}
279
280void tr_cryptoRandBuf ( unsigned char *buf, size_t len )
281{
282    RAND_pseudo_bytes ( buf, len );
283}
284
Note: See TracBrowser for help on using the repository browser.