static async Task <EncryptorResult> DoCheckIncomingConnectionAsync(IConnection connection, EncryptionTypes encryption, EngineSettings settings, InfoHash[] sKeys) { var allowedEncryption = (settings?.AllowedEncryption ?? EncryptionTypes.All) & encryption; var supportsRC4Header = allowedEncryption.HasFlag(EncryptionTypes.RC4Header); var supportsRC4Full = allowedEncryption.HasFlag(EncryptionTypes.RC4Full); var supportsPlainText = allowedEncryption.HasFlag(EncryptionTypes.PlainText); // If the connection is incoming, receive the handshake before // trying to decide what encryption to use var buffer = new byte[HandshakeMessage.HandshakeLength]; await NetworkIO.ReceiveAsync(connection, buffer, 0, buffer.Length, null, null, null).ConfigureAwait(false); HandshakeMessage message = new HandshakeMessage(); message.Decode(buffer, 0, buffer.Length); if (message.ProtocolString == VersionInfo.ProtocolStringV100) { if (supportsPlainText) { return(new EncryptorResult(PlainTextEncryption.Instance, PlainTextEncryption.Instance, message)); } } else if (supportsRC4Header || supportsRC4Full) { // The data we just received was part of an encrypted handshake and was *not* the BitTorrent handshake var encSocket = new PeerBEncryption(sKeys, EncryptionTypes.All); await encSocket.HandshakeAsync(connection, buffer, 0, buffer.Length); if (encSocket.Decryptor is RC4Header && !supportsRC4Header) { throw new EncryptionException("Decryptor was RC4Header but that is not allowed"); } if (encSocket.Decryptor is RC4 && !supportsRC4Full) { throw new EncryptionException("Decryptor was RC4Full but that is not allowed"); } // As the connection was encrypted, the data we got from the initial Receive call will have // been consumed during the crypto handshake process. Now that the encrypted handshake has // been established, we should ensure we read the data again. var data = encSocket.InitialData?.Length > 0 ? encSocket.InitialData : null; if (data == null) { data = buffer; await NetworkIO.ReceiveAsync(connection, data, 0, data.Length, null, null, null); encSocket.Decryptor.Decrypt(data); } message.Decode(data, 0, data.Length); if (message.ProtocolString == VersionInfo.ProtocolStringV100) { return(new EncryptorResult(encSocket.Decryptor, encSocket.Encryptor, message)); } } connection.Dispose(); throw new EncryptionException("Invalid handshake received and no decryption works"); }
private void Handshake(EncryptionTypes encryptionA, EncryptionTypes encryptionB, bool addInitial) { HandshakeMessage m = new HandshakeMessage(rig.Torrent.InfoHash, "12345123451234512345", VersionInfo.ProtocolStringV100); byte[] handshake = m.Encode(); PeerAEncryption a = new PeerAEncryption(rig.Torrent.InfoHash, encryptionA); if (addInitial) { a.AddPayload(handshake); } PeerBEncryption b = new PeerBEncryption(new InfoHash[] { rig.Torrent.InfoHash }, encryptionB); var resultA = a.HandshakeAsync(conn.Outgoing); var resultB = b.HandshakeAsync(conn.Incoming); if (!Task.WhenAll(resultA, resultB).Wait(5000)) { Assert.Fail("Could not handshake"); } HandshakeMessage d = new HandshakeMessage(); if (!addInitial) { a.Encrypt(handshake, 0, handshake.Length); b.Decrypt(handshake, 0, handshake.Length); d.Decode(handshake, 0, handshake.Length); } else { d.Decode(b.InitialData, 0, b.InitialData.Length); } Assert.AreEqual(m, d); if (encryptionA == EncryptionTypes.RC4Full || encryptionB == EncryptionTypes.RC4Full) { Assert.IsTrue(a.Encryptor is RC4); Assert.IsTrue(b.Encryptor is RC4); } else if (encryptionA == EncryptionTypes.RC4Header || encryptionB == EncryptionTypes.RC4Header) { Assert.IsTrue(a.Encryptor is RC4Header); Assert.IsTrue(b.Encryptor is RC4Header); } else if (encryptionA == EncryptionTypes.PlainText || encryptionB == EncryptionTypes.PlainText) { Assert.IsTrue(a.Encryptor is PlainTextEncryption); Assert.IsTrue(b.Encryptor is PlainTextEncryption); } }
async Task PeerBTest(EncryptionTypes encryption) { rig.Engine.Settings.AllowedEncryption = encryption; await rig.Engine.StartAll(); rig.AddConnection(conn.Outgoing); PeerBEncryption a = new PeerBEncryption(new InfoHash[] { rig.Manager.InfoHash }, EncryptionTypes.All); var result = a.HandshakeAsync(conn.Incoming); if (!result.Wait(4000)) { Assert.Fail("Handshake timed out"); } HandshakeMessage message = new HandshakeMessage(); byte[] buffer = new byte[HandshakeMessage.HandshakeLength]; await conn.Incoming.ReceiveAsync(buffer, 0, buffer.Length); a.Decryptor.Decrypt(buffer); message.Decode(buffer, 0, buffer.Length); Assert.AreEqual(VersionInfo.ProtocolStringV100, message.ProtocolString); if (encryption == EncryptionTypes.RC4Full) { Assert.IsTrue(a.Encryptor is RC4); } else if (encryption == EncryptionTypes.RC4Header) { Assert.IsTrue(a.Encryptor is RC4Header); } else if (encryption == EncryptionTypes.PlainText) { Assert.IsTrue(a.Encryptor is RC4Header); } }
static async Task <byte[]> CheckEncryptionAsync(PeerId id, int bytesToReceive, InfoHash[] sKeys, CancellationToken token) { IConnection connection = id.Connection; var allowedEncryption = (id.Engine?.Settings.AllowedEncryption ?? EncryptionTypes.All) & id.Peer.Encryption; var supportsRC4Header = allowedEncryption.HasFlag(EncryptionTypes.RC4Header); var supportsRC4Full = allowedEncryption.HasFlag(EncryptionTypes.RC4Full); var supportsPlainText = allowedEncryption.HasFlag(EncryptionTypes.PlainText); // If the connection is incoming, receive the handshake before // trying to decide what encryption to use if (connection.IsIncoming) { var buffer = new byte[bytesToReceive]; await NetworkIO.ReceiveAsync(connection, buffer, 0, bytesToReceive, null, null, null).ConfigureAwait(false); HandshakeMessage message = new HandshakeMessage(); message.Decode(buffer, 0, buffer.Length); if (message.ProtocolString == VersionInfo.ProtocolStringV100) { if (supportsPlainText) { id.Encryptor = id.Decryptor = PlainTextEncryption.Instance; return(buffer); } } else if (supportsRC4Header || supportsRC4Full) { // The data we just received was part of an encrypted handshake and was *not* the BitTorrent handshake var encSocket = new PeerBEncryption(sKeys, EncryptionTypes.All); await encSocket.HandshakeAsync(connection, buffer, 0, buffer.Length); if (encSocket.Decryptor is RC4Header && !supportsRC4Header) { throw new EncryptionException("Decryptor was RC4Header but that is not allowed"); } if (encSocket.Decryptor is RC4 && !supportsRC4Full) { throw new EncryptionException("Decryptor was RC4Full but that is not allowed"); } id.Decryptor = encSocket.Decryptor; id.Encryptor = encSocket.Encryptor; return(encSocket.InitialData?.Length > 0 ? encSocket.InitialData : null); } } else { if ((id.Engine.Settings.PreferEncryption || !supportsPlainText) && (supportsRC4Header || supportsRC4Full)) { var encSocket = new PeerAEncryption(id.TorrentManager.InfoHash, allowedEncryption); await encSocket.HandshakeAsync(connection); if (encSocket.Decryptor is RC4Header && !supportsRC4Header) { throw new EncryptionException("Decryptor was RC4Header but that is not allowed"); } if (encSocket.Decryptor is RC4 && !supportsRC4Full) { throw new EncryptionException("Decryptor was RC4Full but that is not allowed"); } id.Decryptor = encSocket.Decryptor; id.Encryptor = encSocket.Encryptor; return(encSocket.InitialData?.Length > 0 ? encSocket.InitialData : null); } else if (supportsPlainText) { id.Encryptor = id.Decryptor = PlainTextEncryption.Instance; return(null); } } throw new EncryptionException("Invalid handshake received and no decryption works"); }
static async ReusableTask <EncryptorResult> DoCheckIncomingConnectionAsync(IConnection connection, IList <EncryptionType> preferredEncryption, InfoHash[] sKeys) { bool supportsRC4Header = preferredEncryption.Contains(EncryptionType.RC4Header); bool supportsRC4Full = preferredEncryption.Contains(EncryptionType.RC4Full); bool supportsPlainText = preferredEncryption.Contains(EncryptionType.PlainText); // If the connection is incoming, receive the handshake before // trying to decide what encryption to use var message = new HandshakeMessage(); using (NetworkIO.BufferPool.Rent(HandshakeMessage.HandshakeLength, out ByteBuffer buffer)) { await NetworkIO.ReceiveAsync(connection, buffer, 0, HandshakeMessage.HandshakeLength, null, null, null).ConfigureAwait(false); message.Decode(buffer.Data, 0, HandshakeMessage.HandshakeLength); if (message.ProtocolString == VersionInfo.ProtocolStringV100) { if (supportsPlainText) { return(new EncryptorResult(PlainTextEncryption.Instance, PlainTextEncryption.Instance, message)); } } else if (supportsRC4Header || supportsRC4Full) { // The data we just received was part of an encrypted handshake and was *not* the BitTorrent handshake // First switch to the threadpool as creating encrypted sockets runs expensive computations in the ctor await MainLoop.SwitchToThreadpool(); var encSocket = new PeerBEncryption(sKeys, preferredEncryption); await encSocket.HandshakeAsync(connection, buffer.Data, 0, HandshakeMessage.HandshakeLength).ConfigureAwait(false); if (encSocket.Decryptor is RC4Header && !supportsRC4Header) { throw new EncryptionException("Decryptor was RC4Header but that is not allowed"); } if (encSocket.Decryptor is RC4 && !supportsRC4Full) { throw new EncryptionException("Decryptor was RC4Full but that is not allowed"); } // As the connection was encrypted, the data we got from the initial Receive call will have // been consumed during the crypto handshake process. Now that the encrypted handshake has // been established, we should ensure we read the data again. byte[] data = encSocket.InitialData?.Length > 0 ? encSocket.InitialData : null; if (data == null) { await NetworkIO.ReceiveAsync(connection, buffer, 0, HandshakeMessage.HandshakeLength, null, null, null).ConfigureAwait(false); data = buffer.Data; encSocket.Decryptor.Decrypt(data, 0, HandshakeMessage.HandshakeLength); } message.Decode(data, 0, HandshakeMessage.HandshakeLength); if (message.ProtocolString == VersionInfo.ProtocolStringV100) { return(new EncryptorResult(encSocket.Decryptor, encSocket.Encryptor, message)); } } } connection.Dispose(); throw new EncryptionException("Invalid handshake received and no decryption works"); }