static int readVC( tr_handshake * handshake, struct evbuffer * inbuf ) { const uint8_t key[VC_LENGTH] = { 0, 0, 0, 0, 0, 0, 0, 0 }; const int key_len = VC_LENGTH; uint8_t tmp[VC_LENGTH]; /* note: this works w/o having to `unwind' the buffer if * we read too much, but it is pretty brute-force. * it would be nice to make this cleaner. */ for( ; ; ) { if( evbuffer_get_length( inbuf ) < VC_LENGTH ) { dbgmsg( handshake, "not enough bytes... returning read_more" ); return READ_LATER; } memcpy( tmp, evbuffer_pullup( inbuf, key_len ), key_len ); tr_cryptoDecryptInit( handshake->crypto ); tr_cryptoDecrypt( handshake->crypto, key_len, tmp, tmp ); if( !memcmp( tmp, key, key_len ) ) break; evbuffer_drain( inbuf, 1 ); } dbgmsg( handshake, "got it!" ); evbuffer_drain( inbuf, key_len ); setState( handshake, AWAITING_CRYPTO_SELECT ); return READ_NOW; }
static int test_encrypt_decrypt (void) { tr_crypto a, b; uint8_t hash[SHA_DIGEST_LENGTH]; const char test1[] = { "test1" }; char buf11[sizeof (test1)], buf12[sizeof (test1)]; const char test2[] = { "@#)C$@)#(*%bvkdjfhwbc039bc4603756VB3)" }; char buf21[sizeof (test2)], buf22[sizeof (test2)]; int i; for (i = 0; i < SHA_DIGEST_LENGTH; ++i) hash[i] = i; tr_cryptoConstruct (&a, hash, false); tr_cryptoConstruct (&b, hash, true); tr_cryptoComputeSecret (&a, tr_cryptoGetMyPublicKey (&b, &i)); tr_cryptoComputeSecret (&b, tr_cryptoGetMyPublicKey (&a, &i)); tr_cryptoEncryptInit (&a); tr_cryptoEncrypt (&a, sizeof (test1), test1, buf11); tr_cryptoDecryptInit (&b); tr_cryptoDecrypt (&b, sizeof (test1), buf11, buf12); check_streq (test1, buf12); tr_cryptoEncryptInit (&b); tr_cryptoEncrypt (&b, sizeof (test2), test2, buf21); tr_cryptoDecryptInit (&a); tr_cryptoDecrypt (&a, sizeof (test2), buf21, buf22); check_streq (test2, buf22); tr_cryptoDestruct (&b); tr_cryptoDestruct (&a); return 0; }
static int readCryptoProvide( tr_handshake * handshake, struct evbuffer * inbuf ) { /* HASH('req2', SKEY) xor HASH('req3', S), ENCRYPT(VC, crypto_provide, len(PadC)) */ int i; uint8_t vc_in[VC_LENGTH]; uint8_t req2[SHA_DIGEST_LENGTH]; uint8_t req3[SHA_DIGEST_LENGTH]; uint8_t obfuscatedTorrentHash[SHA_DIGEST_LENGTH]; uint16_t padc_len = 0; uint32_t crypto_provide = 0; const size_t needlen = SHA_DIGEST_LENGTH /* HASH('req1',s) */ + SHA_DIGEST_LENGTH /* HASH('req2', SKEY) xor HASH('req3', S) */ + VC_LENGTH + sizeof( crypto_provide ) + sizeof( padc_len ); tr_torrent * tor = NULL; if( evbuffer_get_length( inbuf ) < needlen ) return READ_LATER; /* TODO: confirm they sent HASH('req1',S) here? */ evbuffer_drain( inbuf, SHA_DIGEST_LENGTH ); /* This next piece is HASH('req2', SKEY) xor HASH('req3', S) ... * we can get the first half of that (the obufscatedTorrentHash) * by building the latter and xor'ing it with what the peer sent us */ dbgmsg( handshake, "reading obfuscated torrent hash..." ); evbuffer_remove( inbuf, req2, SHA_DIGEST_LENGTH ); tr_sha1( req3, "req3", 4, handshake->mySecret, KEY_LEN, NULL ); for( i = 0; i < SHA_DIGEST_LENGTH; ++i ) obfuscatedTorrentHash[i] = req2[i] ^ req3[i]; if(( tor = tr_torrentFindFromObfuscatedHash( handshake->session, obfuscatedTorrentHash ))) { const tr_bool clientIsSeed = tr_torrentIsSeed( tor ); const tr_bool peerIsSeed = tr_peerMgrPeerIsSeed( tor, tr_peerIoGetAddress( handshake->io, NULL ) ); dbgmsg( handshake, "got INCOMING connection's encrypted handshake for torrent [%s]", tr_torrentName( tor ) ); tr_peerIoSetTorrentHash( handshake->io, tor->info.hash ); if( clientIsSeed && peerIsSeed ) { dbgmsg( handshake, "another seed tried to reconnect to us!" ); return tr_handshakeDone( handshake, FALSE ); } } else { dbgmsg( handshake, "can't find that torrent..." ); return tr_handshakeDone( handshake, FALSE ); } /* next part: ENCRYPT(VC, crypto_provide, len(PadC), */ tr_cryptoDecryptInit( handshake->crypto ); tr_peerIoReadBytes( handshake->io, inbuf, vc_in, VC_LENGTH ); tr_peerIoReadUint32( handshake->io, inbuf, &crypto_provide ); handshake->crypto_provide = crypto_provide; dbgmsg( handshake, "crypto_provide is %d", (int)crypto_provide ); tr_peerIoReadUint16( handshake->io, inbuf, &padc_len ); dbgmsg( handshake, "padc is %d", (int)padc_len ); handshake->pad_c_len = padc_len; setState( handshake, AWAITING_PAD_C ); return READ_NOW; }
static int readYb( tr_handshake * handshake, struct evbuffer * inbuf ) { int isEncrypted; const uint8_t * secret; uint8_t yb[KEY_LEN]; struct evbuffer * outbuf; size_t needlen = HANDSHAKE_NAME_LEN; if( evbuffer_get_length( inbuf ) < needlen ) return READ_LATER; isEncrypted = memcmp( evbuffer_pullup( inbuf, HANDSHAKE_NAME_LEN ), HANDSHAKE_NAME, HANDSHAKE_NAME_LEN ); if( isEncrypted ) { needlen = KEY_LEN; if( evbuffer_get_length( inbuf ) < needlen ) return READ_LATER; } dbgmsg( handshake, "got a %s handshake", ( isEncrypted ? "encrypted" : "plaintext" ) ); tr_peerIoSetEncryption( handshake->io, isEncrypted ? PEER_ENCRYPTION_RC4 : PEER_ENCRYPTION_NONE ); if( !isEncrypted ) { setState( handshake, AWAITING_HANDSHAKE ); return READ_NOW; } handshake->haveReadAnythingFromPeer = TRUE; /* compute the secret */ evbuffer_remove( inbuf, yb, KEY_LEN ); secret = tr_cryptoComputeSecret( handshake->crypto, yb ); memcpy( handshake->mySecret, secret, KEY_LEN ); /* now send these: HASH('req1', S), HASH('req2', SKEY) xor HASH('req3', S), * ENCRYPT(VC, crypto_provide, len(PadC), PadC, len(IA)), ENCRYPT(IA) */ outbuf = evbuffer_new( ); /* HASH('req1', S) */ { uint8_t req1[SHA_DIGEST_LENGTH]; tr_sha1( req1, "req1", 4, secret, KEY_LEN, NULL ); evbuffer_add( outbuf, req1, SHA_DIGEST_LENGTH ); } /* HASH('req2', SKEY) xor HASH('req3', S) */ { int i; uint8_t req2[SHA_DIGEST_LENGTH]; uint8_t req3[SHA_DIGEST_LENGTH]; uint8_t buf[SHA_DIGEST_LENGTH]; tr_sha1( req2, "req2", 4, tr_cryptoGetTorrentHash( handshake->crypto ), SHA_DIGEST_LENGTH, NULL ); tr_sha1( req3, "req3", 4, secret, KEY_LEN, NULL ); for( i = 0; i < SHA_DIGEST_LENGTH; ++i ) buf[i] = req2[i] ^ req3[i]; evbuffer_add( outbuf, buf, SHA_DIGEST_LENGTH ); } /* ENCRYPT(VC, crypto_provide, len(PadC), PadC * PadC is reserved for future extensions to the handshake... * standard practice at this time is for it to be zero-length */ { uint8_t vc[VC_LENGTH] = { 0, 0, 0, 0, 0, 0, 0, 0 }; tr_peerIoWriteBuf( handshake->io, outbuf, FALSE ); tr_cryptoEncryptInit( handshake->crypto ); tr_peerIoSetEncryption( handshake->io, PEER_ENCRYPTION_RC4 ); evbuffer_add ( outbuf, vc, VC_LENGTH ); evbuffer_add_uint32 ( outbuf, getCryptoProvide( handshake ) ); evbuffer_add_uint16 ( outbuf, 0 ); } /* ENCRYPT len(IA)), ENCRYPT(IA) */ { uint8_t msg[HANDSHAKE_SIZE]; buildHandshakeMessage( handshake, msg ); evbuffer_add_uint16 ( outbuf, sizeof( msg ) ); evbuffer_add ( outbuf, msg, sizeof( msg ) ); handshake->haveSentBitTorrentHandshake = 1; } /* send it */ tr_cryptoDecryptInit( handshake->crypto ); setReadState( handshake, AWAITING_VC ); tr_peerIoWriteBuf( handshake->io, outbuf, FALSE ); /* cleanup */ evbuffer_free( outbuf ); return READ_LATER; }