static int readPadD (tr_handshake * handshake, struct evbuffer * inbuf) { const size_t needlen = handshake->pad_d_len; dbgmsg (handshake, "pad d: need %zu, got %zu", needlen, evbuffer_get_length (inbuf)); if (evbuffer_get_length (inbuf) < needlen) return READ_LATER; tr_peerIoDrain (handshake->io, inbuf, needlen); tr_peerIoSetEncryption (handshake->io, handshake->crypto_select); setState (handshake, AWAITING_HANDSHAKE); return READ_NOW; }
tr_handshake* tr_handshakeNew (tr_peerIo * io, tr_encryption_mode encryptionMode, handshakeDoneCB doneCB, void * doneUserData) { tr_handshake * handshake; tr_session * session = tr_peerIoGetSession (io); handshake = tr_new0 (tr_handshake, 1); handshake->io = io; handshake->crypto = tr_peerIoGetCrypto (io); handshake->encryptionMode = encryptionMode; handshake->doneCB = doneCB; handshake->doneUserData = doneUserData; handshake->session = session; handshake->timeout_timer = evtimer_new (session->event_base, handshakeTimeout, handshake); tr_timerAdd (handshake->timeout_timer, HANDSHAKE_TIMEOUT_SEC, 0); tr_peerIoRef (io); /* balanced by the unref in tr_handshakeFree */ tr_peerIoSetIOFuncs (handshake->io, canRead, NULL, gotError, handshake); tr_peerIoSetEncryption (io, PEER_ENCRYPTION_NONE); if (tr_peerIoIsIncoming (handshake->io)) { setReadState (handshake, AWAITING_HANDSHAKE); } else if (encryptionMode != TR_CLEAR_PREFERRED) { sendYa (handshake); } else { uint8_t msg[HANDSHAKE_SIZE]; buildHandshakeMessage (handshake, msg); handshake->haveSentBitTorrentHandshake = 1; setReadState (handshake, AWAITING_HANDSHAKE); tr_peerIoWriteBytes (handshake->io, msg, sizeof (msg), false); } return handshake; }
/* 2 $A \leftarrow B$: Diffie Hellman $Y_B$, PadB */ 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; (...) isEncrypted = memcmp(evbuffer_pullup(inbuf, HANDSHAKE_NAME_LEN), HANDSHAKE_NAME, HANDSHAKE_NAME_LEN); (...) tr_peerIoSetEncryption(handshake->io, isEncrypted ? PEER_ENCRYPTION_RC4 : PEER_ENCRYPTION_NONE); if (!isEncrypted) { setState(handshake, AWAITING_HANDSHAKE); // Handshake não encriptado acaba aqui. return READ_NOW; } (...) /* compute the secret $S$*/ evbuffer_remove(inbuf, yb, KEY_LEN); secret = tr_cryptoComputeSecret(handshake->crypto, yb); memcpy(handshake->mySecret, secret, KEY_LEN);
static int readIA( tr_handshake * handshake, struct evbuffer * inbuf ) { const size_t needlen = handshake->ia_len; struct evbuffer * outbuf; uint32_t crypto_select; dbgmsg( handshake, "reading IA... have %zu, need %zu", evbuffer_get_length( inbuf ), needlen ); if( evbuffer_get_length( inbuf ) < needlen ) return READ_LATER; /** *** B->A: ENCRYPT(VC, crypto_select, len(padD), padD), ENCRYPT2(Payload Stream) **/ tr_cryptoEncryptInit( handshake->crypto ); outbuf = evbuffer_new( ); dbgmsg( handshake, "sending vc" ); /* send VC */ { uint8_t vc[VC_LENGTH]; memset( vc, 0, VC_LENGTH ); evbuffer_add( outbuf, vc, VC_LENGTH ); } /* send crypto_select */ crypto_select = getCryptoSelect( handshake, handshake->crypto_provide ); if( crypto_select ) { dbgmsg( handshake, "selecting crypto mode '%d'", (int)crypto_select ); evbuffer_add_uint32( outbuf, crypto_select ); } else { dbgmsg( handshake, "peer didn't offer an encryption mode we like." ); evbuffer_free( outbuf ); return tr_handshakeDone( handshake, FALSE ); } dbgmsg( handshake, "sending pad d" ); /* ENCRYPT(VC, crypto_provide, len(PadD), PadD * PadD is reserved for future extensions to the handshake... * standard practice at this time is for it to be zero-length */ { const uint16_t len = 0; evbuffer_add_uint16( outbuf, len ); } /* maybe de-encrypt our connection */ if( crypto_select == CRYPTO_PROVIDE_PLAINTEXT ) { tr_peerIoWriteBuf( handshake->io, outbuf, FALSE ); tr_peerIoSetEncryption( handshake->io, PEER_ENCRYPTION_NONE ); } dbgmsg( handshake, "sending handshake" ); /* send our handshake */ { uint8_t msg[HANDSHAKE_SIZE]; buildHandshakeMessage( handshake, msg ); evbuffer_add( outbuf, msg, sizeof( msg ) ); handshake->haveSentBitTorrentHandshake = 1; } /* send it out */ tr_peerIoWriteBuf( handshake->io, outbuf, FALSE ); evbuffer_free( outbuf ); /* now await the handshake */ setState( handshake, AWAITING_PAYLOAD_STREAM ); return READ_NOW; }
static int readHandshake( tr_handshake * handshake, struct evbuffer * inbuf ) { uint8_t pstrlen; uint8_t * pstr; uint8_t reserved[HANDSHAKE_FLAGS_LEN]; uint8_t hash[SHA_DIGEST_LENGTH]; dbgmsg( handshake, "payload: need %d, got %zu", INCOMING_HANDSHAKE_LEN, evbuffer_get_length( inbuf ) ); if( evbuffer_get_length( inbuf ) < INCOMING_HANDSHAKE_LEN ) return READ_LATER; handshake->haveReadAnythingFromPeer = TRUE; pstrlen = evbuffer_pullup( inbuf, 1 )[0]; /* peek, don't read. We may be handing inbuf to AWAITING_YA */ if( pstrlen == 19 ) /* unencrypted */ { tr_peerIoSetEncryption( handshake->io, PEER_ENCRYPTION_NONE ); if( handshake->encryptionMode == TR_ENCRYPTION_REQUIRED ) { dbgmsg( handshake, "peer is unencrypted, and we're disallowing that" ); return tr_handshakeDone( handshake, FALSE ); } } else /* encrypted or corrupt */ { tr_peerIoSetEncryption( handshake->io, PEER_ENCRYPTION_RC4 ); if( tr_peerIoIsIncoming( handshake->io ) ) { dbgmsg( handshake, "I think peer is sending us an encrypted handshake..." ); setState( handshake, AWAITING_YA ); return READ_NOW; } tr_cryptoDecrypt( handshake->crypto, 1, &pstrlen, &pstrlen ); if( pstrlen != 19 ) { dbgmsg( handshake, "I think peer has sent us a corrupt handshake..." ); return tr_handshakeDone( handshake, FALSE ); } } evbuffer_drain( inbuf, 1 ); /* pstr (BitTorrent) */ pstr = tr_new( uint8_t, pstrlen + 1 ); tr_peerIoReadBytes( handshake->io, inbuf, pstr, pstrlen ); pstr[pstrlen] = '\0'; if( strcmp( (char*)pstr, "BitTorrent protocol" ) ) { tr_free( pstr ); return tr_handshakeDone( handshake, FALSE ); } tr_free( pstr ); /* reserved bytes */ tr_peerIoReadBytes( handshake->io, inbuf, reserved, sizeof( reserved ) ); /** *** Extensions **/ tr_peerIoEnableLTEP( handshake->io, HANDSHAKE_HAS_LTEP( reserved ) ); tr_peerIoEnableFEXT( handshake->io, HANDSHAKE_HAS_FASTEXT( reserved ) ); tr_peerIoEnableDHT( handshake->io, HANDSHAKE_HAS_DHT( reserved ) ); /* torrent hash */ tr_peerIoReadBytes( handshake->io, inbuf, hash, sizeof( hash ) ); if( tr_peerIoIsIncoming( handshake->io ) ) { if( !tr_torrentExists( handshake->session, hash ) ) { dbgmsg( handshake, "peer is trying to connect to us for a torrent we don't have." ); return tr_handshakeDone( handshake, FALSE ); } else { assert( !tr_peerIoHasTorrentHash( handshake->io ) ); tr_peerIoSetTorrentHash( handshake->io, hash ); } } else /* outgoing */ { assert( tr_peerIoHasTorrentHash( handshake->io ) ); if( memcmp( hash, tr_peerIoGetTorrentHash( handshake->io ), SHA_DIGEST_LENGTH ) ) { dbgmsg( handshake, "peer returned the wrong hash. wtf?" ); return tr_handshakeDone( handshake, FALSE ); } } /** *** If it's an incoming message, we need to send a response handshake **/ if( !handshake->haveSentBitTorrentHandshake ) { uint8_t msg[HANDSHAKE_SIZE]; buildHandshakeMessage( handshake, msg ); tr_peerIoWriteBytes( handshake->io, msg, sizeof( msg ), FALSE ); handshake->haveSentBitTorrentHandshake = 1; } setReadState( handshake, AWAITING_PEER_ID ); 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; }