Home > Specs > BitTorrent > Peer Connections
Peer Connection Encryption
This connection encryption method is in common use. It uses an initial Diffie-Hellman shared secret generation, followed by RC4 stream encryption keyed on the shared secret.
It should be noted that this scheme does not include a message authentication code, and therefore is not secure against tampering.
First, the local peer initiating the outgoing connection generates a 96-byte random string that is used for this connection attempt only. This will be the local private key.
We must now calculate the corresponding public key using modular exponentiation.
The base is the number 2. The exponent is our private key, taken as a big-endian number.
The modulus is the following big-endian number:
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2,0x21,0x68,0xC2,0x34,
0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1,0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,
0x02,0x0B,0xBE,0xA6,0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD,
0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D,0xF2,0x5F,0x14,0x37,
0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45,0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,
0xF4,0x4C,0x42,0xE9,0xA6,0x3A,0x36,0x21,0x00,0x00,0x00,0x00,0x00,0x09,0x05,0x63
The remainder is taken as our public key, which is always stored in big-endian format.
The local peer after making an outgoing connection will send their public key (96 bytes) appended with a random byte string of random length (0 to 511 bytes).
The remote peer will receive the incoming connection and wait for either a conventional unencrypted peer handshake (which is easily recognizable by the first 20 bytes) or assume that a crypto handshake is being received.
After receiving the key (first 96 bytes only), the remote will generate it's own public/private key pair using the key setup procedure outlined above, and send it's own public key (96 bytes) appended with a random byte string of random length (0 to 511 bytes).
Shared Secret Calculation
Once a peer has read the opposite peer's public key, the shared secret can be calculated.
Using the other peer's public key as the base (instead of 2), calculate the modular exponent the same way as in the initial key setup, with the same private key and same modulus. The remainder is the same for both peers, and is used in big-endian form as the shared secret key (96 bytes).
The random padding after the public key is of unknown length to the opposite peer. Therefore after the 96-byte key and random-length padding is sent, both peers must transmit a synchronization sequence, and also scan the incoming byte stream for a synchronization sequence. These sequences are based upon the shared secret key.
Outgoing
The local peer (that made the outgoing connection) makes the following calculations:
hash_a = SHA1Hash("req1"+shared_secret_key)
hash_b = SHA1Hash("req2"+torrent_info_hash)
hash_c = SHA1Hash("req3"+shared_secret_key)
sync_out = hash_a + XOR(hash_b,hash_c)
key_in = SHA1Hash("keyB"+shared_secret_key+torrent_info_hash)
key_out = SHA1Hash("keyA"+shared_secret_key+torrent_info_hash)
after_sync_out = 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x02
last_pad_len = 10+(rand()%450)
The RC4 instance used for sending is initialized with key_out, and the one used for receiving is initialized with key_in. The key is advanced 1024 bits (this is generally an RC4 implementation detail). Also note that because RC4 is an XOR-based cipher, decryption on a given instance is the same operation as encryption.
The local peer sends sync_out (40 bytes, not encrypted) appended by after_sync_out (12 bytes, encrypted) then the big-endian last_pad_len (2 bytes, encrypted) then a random string (last_pad_len bytes, encrypted) and then finally 2 zero bytes, encrypted. All bytes sent after sync_out until the end of the connection are encrypted using the sending RC4 instance.
An eight-byte series of zeros is decrypted using the receiving RC4 instance. The result is the synchronization sequence. Read the stream, unencrypted, until after these bytes are read, discarding up to 512 padding bytes. All bytes coming in after the end of the sync sequence until the end of the connection must be decrypted using the receiving RC4 instance.
After finding the sync, and starting to apply decryption, the next 4 bytes are a set of flags. If the second lowest bit of the last byte is not set, discard the connection. The two bytes after that are the big-endian representation of the final pad length. Read (and decrypt) the pad bytes and discard them.
Incoming
The remote peer that received the incoming connection makes the following calculation:
hash_a = SHA1Hash("req1"+shared_secret_key)
hash_b = SHA1Hash("req3"+shared_secret_key)
For each torrent that the remote peer can accept connections on, make (and cache) the following calculation:
hash_c = SHA1Hash("req2"+torrent_info_hash)
The remote peer reads up to 512 bytes searching for hash_a (20 bytes). The connection is dropped if it's not found.
The remote peer then reads the next 20 bytes after hash_a, XORs them with hash_b, and then tries to find a torrent with a hash_c that matches the result. The remote peer now knows the torrent_info_hash if it found a match, otherwise it drops the connection.
The remote peer can now make the following calculations:
key_out = SHA1Hash("keyB"+shared_secret_key+torrent_info_hash)
key_in = SHA1Hash("keyA"+shared_secret_key+torrent_info_hash)
after_sync_out = 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x02
last_pad_len = 10+(rand()%450)
The remote peer initializes an incoming and outgoing RC4 instance with key_in and key_out. Every byte read from this point until the end of the connection is decrypted. Every byte sent from this point until the end of the connection is encrypted.
The remote peer reads 8 bytes. If they are not all zero (remember, this is after decryption now) then the connection is discarded.
The remote peer reads 4 bytes. These are the encryption flags. If the last byte does not have the second lowest bit set, the connection is discarded.
The remote peer reads 2 bytes. This is the big-endian last pad length. Read this many bytes and discard them.
The remote peer reads 2 bytes. These are not used and are discarded.
The remote peer now sends after_sync_out (12 bytes) followed by the big-endian last_pad_len (2 bytes) followed by a random string that is last_pad_len bytes long.
After the connections have completed the above steps, they proceed in the same manner as any other non-encrypted connection. The local peer that made the outgoing connection now transmits the conventional peer handshake. The remote peer that received the connection now waits for the conventional peer handshake, and checks to make sure the torrent hash ID matches the one from the crypto negotiation, and then transmits it's own handshake and proceeds with normal operation.
Implementations will need to make the connection shared secret available to the upper protocol layers in peer objects. It is used in the
v3.1 protocol to prevent leakage of the torrent info map to anyone without the original SHA2 or SHA3 info hash from a magnet link via a man in the middle attack.