source: trunk/libtransmission/crypto.c @ 6425

Last change on this file since 6425 was 6425, checked in by charles, 13 years ago

minor text cleanup

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