source: trunk/libtransmission/crypto.c @ 3105

Last change on this file since 3105 was 3105, checked in by livings124, 15 years ago

merge encryption branch to trunk (xcode project is still out of date)

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