static void buildHandshakeMessage( tr_handshake * handshake, uint8_t * buf ) { uint8_t * walk = buf; const uint8_t * torrentHash = tr_cryptoGetTorrentHash( handshake->crypto ); const tr_torrent * tor = tr_torrentFindFromHash( handshake->session, torrentHash ); const uint8_t * peer_id = tor && tor->peer_id ? tor->peer_id : tr_getPeerId( ); memcpy( walk, HANDSHAKE_NAME, HANDSHAKE_NAME_LEN ); walk += HANDSHAKE_NAME_LEN; memset( walk, 0, HANDSHAKE_FLAGS_LEN ); HANDSHAKE_SET_LTEP( walk ); HANDSHAKE_SET_FASTEXT( walk ); /* Note that this doesn't depend on whether the torrent is private. * We don't accept DHT peers for a private torrent, * but we participate in the DHT regardless. */ if( tr_dhtEnabled( handshake->session ) ) HANDSHAKE_SET_DHT( walk ); walk += HANDSHAKE_FLAGS_LEN; memcpy( walk, torrentHash, SHA_DIGEST_LENGTH ); walk += SHA_DIGEST_LENGTH; memcpy( walk, peer_id, PEER_ID_LEN ); walk += PEER_ID_LEN; assert( strlen( ( const char* )peer_id ) == PEER_ID_LEN ); assert( walk - buf == HANDSHAKE_SIZE ); }
const uint8_t* tr_peerIoGetTorrentHash( tr_peerIo * io ) { assert( tr_isPeerIo( io ) ); assert( io->crypto ); return tr_cryptoGetTorrentHash( io->crypto ); }
static bool buildHandshakeMessage (tr_handshake * handshake, uint8_t * buf) { const unsigned char * peer_id = NULL; const uint8_t * torrentHash; tr_torrent * tor; bool success; if ((torrentHash = tr_cryptoGetTorrentHash (handshake->crypto))) if ((tor = tr_torrentFindFromHash (handshake->session, torrentHash))) peer_id = tr_torrentGetPeerId (tor); if (peer_id == NULL) { success = false; } else { uint8_t * walk = buf; memcpy (walk, HANDSHAKE_NAME, HANDSHAKE_NAME_LEN); walk += HANDSHAKE_NAME_LEN; memset (walk, 0, HANDSHAKE_FLAGS_LEN); HANDSHAKE_SET_LTEP (walk); HANDSHAKE_SET_FASTEXT (walk); /* Note that this doesn't depend on whether the torrent is private. * We don't accept DHT peers for a private torrent, * but we participate in the DHT regardless. */ if (tr_dhtEnabled (handshake->session)) HANDSHAKE_SET_DHT (walk); walk += HANDSHAKE_FLAGS_LEN; memcpy (walk, torrentHash, SHA_DIGEST_LENGTH); walk += SHA_DIGEST_LENGTH; memcpy (walk, peer_id, PEER_ID_LEN); walk += PEER_ID_LEN; assert (walk - buf == HANDSHAKE_SIZE); success = true; } return success; }
static int test_torrent_hash (void) { tr_crypto a; uint8_t hash[SHA_DIGEST_LENGTH]; int i; for (i = 0; i < SHA_DIGEST_LENGTH; ++i) hash[i] = i; tr_cryptoConstruct (&a, NULL, true); check (!tr_cryptoHasTorrentHash (&a)); check (tr_cryptoGetTorrentHash (&a) == NULL); tr_cryptoSetTorrentHash (&a, hash); check (tr_cryptoHasTorrentHash (&a)); check (tr_cryptoGetTorrentHash (&a) != NULL); check (memcmp (tr_cryptoGetTorrentHash (&a), hash, SHA_DIGEST_LENGTH) == 0); tr_cryptoDestruct (&a); for (i = 0; i < SHA_DIGEST_LENGTH; ++i) hash[i] = i + 1; tr_cryptoConstruct (&a, hash, false); check (tr_cryptoHasTorrentHash (&a)); check (tr_cryptoGetTorrentHash (&a) != NULL); check (memcmp (tr_cryptoGetTorrentHash (&a), hash, SHA_DIGEST_LENGTH) == 0); tr_cryptoSetTorrentHash (&a, NULL); check (!tr_cryptoHasTorrentHash (&a)); check (tr_cryptoGetTorrentHash (&a) == NULL); tr_cryptoDestruct (&a); return 0; }
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; }