static async Task <EncryptorResult> DoCheckOutgoingConnectionAsync(IConnection connection, EncryptionTypes encryption, EngineSettings settings, InfoHash infoHash) { var allowedEncryption = settings.AllowedEncryption & encryption; var supportsRC4Header = allowedEncryption.HasFlag(EncryptionTypes.RC4Header); var supportsRC4Full = allowedEncryption.HasFlag(EncryptionTypes.RC4Full); var supportsPlainText = allowedEncryption.HasFlag(EncryptionTypes.PlainText); if ((settings.PreferEncryption || !supportsPlainText) && (supportsRC4Header || supportsRC4Full)) { var encSocket = new PeerAEncryption(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"); } var data = encSocket.InitialData?.Length > 0 ? encSocket.InitialData : null; return(new EncryptorResult(encSocket.Decryptor, encSocket.Encryptor, data)); } else if (supportsPlainText) { return(new EncryptorResult(PlainTextEncryption.Instance, PlainTextEncryption.Instance, null)); } 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 PeerATest(EncryptionTypes encryption, bool addInitial) { rig.Engine.Settings.AllowedEncryption = encryption; await rig.Engine.StartAll(); HandshakeMessage message = new HandshakeMessage(rig.Manager.InfoHash, "ABC123ABC123ABC123AB", VersionInfo.ProtocolStringV100); byte[] buffer = message.Encode(); PeerAEncryption a = new PeerAEncryption(rig.Manager.InfoHash, encryption); if (addInitial) { a.AddPayload(buffer); } rig.AddConnection(conn.Incoming); var result = a.HandshakeAsync(conn.Outgoing); if (!result.Wait(4000)) { Assert.Fail("Handshake timed out"); } if (!addInitial) { a.Encryptor.Encrypt(buffer); await conn.Outgoing.SendAsync(buffer, 0, buffer.Length); } int received = await conn.Outgoing.ReceiveAsync(buffer, 0, buffer.Length); Assert.AreEqual(HandshakeMessage.HandshakeLength, received, "Recived handshake"); 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 ReusableTask <EncryptorResult> DoCheckOutgoingConnectionAsync(IConnection2 connection, EncryptionTypes encryption, EngineSettings settings, InfoHash infoHash, HandshakeMessage handshake) { EncryptionTypes allowedEncryption = settings.AllowedEncryption & encryption; bool supportsRC4Header = allowedEncryption.HasFlag(EncryptionTypes.RC4Header); bool supportsRC4Full = allowedEncryption.HasFlag(EncryptionTypes.RC4Full); bool supportsPlainText = allowedEncryption.HasFlag(EncryptionTypes.PlainText); if ((settings.PreferEncryption || !supportsPlainText) && (supportsRC4Header || supportsRC4Full)) { // First switch to the threadpool as creating encrypted sockets runs expensive computations in the ctor await MainLoop.SwitchToThreadpool(); var encSocket = new PeerAEncryption(infoHash, allowedEncryption, handshake?.Encode()); await encSocket.HandshakeAsync(connection).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"); } return(new EncryptorResult(encSocket.Decryptor, encSocket.Encryptor, null)); } else if (supportsPlainText) { if (handshake != null) { int length = handshake.ByteLength; byte[] buffer = ClientEngine.BufferPool.Rent(length); handshake.Encode(buffer, 0); try { await NetworkIO.SendAsync(connection, buffer, 0, length, null, null, null).ConfigureAwait(false); } finally { ClientEngine.BufferPool.Return(buffer); } } return(new EncryptorResult(PlainTextEncryption.Instance, PlainTextEncryption.Instance, null)); } connection.Dispose(); throw new EncryptionException("Invalid handshake received and no decryption works"); }
static async ReusableTask <EncryptorResult> DoCheckOutgoingConnectionAsync(IConnection2 connection, EncryptionTypes encryption, EngineSettings settings, InfoHash infoHash, HandshakeMessage handshake) { var allowedEncryption = settings.AllowedEncryption & encryption; var supportsRC4Header = allowedEncryption.HasFlag(EncryptionTypes.RC4Header); var supportsRC4Full = allowedEncryption.HasFlag(EncryptionTypes.RC4Full); var supportsPlainText = allowedEncryption.HasFlag(EncryptionTypes.PlainText); if ((settings.PreferEncryption || !supportsPlainText) && (supportsRC4Header || supportsRC4Full)) { var encSocket = new PeerAEncryption(infoHash, allowedEncryption, handshake?.Encode()); 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"); } return(new EncryptorResult(encSocket.Decryptor, encSocket.Encryptor, null)); } else if (supportsPlainText) { if (handshake != null) { var length = handshake.ByteLength; var buffer = ClientEngine.BufferPool.Rent(length); handshake.Encode(buffer, 0); try { await NetworkIO.SendAsync(connection, buffer, 0, length, null, null, null); } finally { ClientEngine.BufferPool.Return(buffer); } } return(new EncryptorResult(PlainTextEncryption.Instance, PlainTextEncryption.Instance, null)); } connection.Dispose(); throw new EncryptionException("Invalid handshake received and no decryption works"); }
static async ReusableTask <EncryptorResult> DoCheckOutgoingConnectionAsync(IConnection connection, IList <EncryptionType> preferredEncryption, InfoHash infoHash, HandshakeMessage handshake) { bool supportsRC4Header = preferredEncryption.Contains(EncryptionType.RC4Header); bool supportsRC4Full = preferredEncryption.Contains(EncryptionType.RC4Full); bool supportsPlainText = preferredEncryption.Contains(EncryptionType.PlainText); // First switch to the threadpool as creating encrypted sockets runs expensive computations in the ctor await MainLoop.SwitchToThreadpool(); if (preferredEncryption[0] != EncryptionType.PlainText) { var encSocket = new PeerAEncryption(infoHash, preferredEncryption, handshake?.Encode()); await encSocket.HandshakeAsync(connection).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"); } return(new EncryptorResult(encSocket.Decryptor, encSocket.Encryptor, null)); } else if (supportsPlainText) { if (handshake != null) { int length = handshake.ByteLength; using (NetworkIO.BufferPool.Rent(length, out ByteBuffer buffer)) { handshake.Encode(buffer.Data, 0); await NetworkIO.SendAsync(connection, buffer, 0, length, null, null, null).ConfigureAwait(false); } } return(new EncryptorResult(PlainTextEncryption.Instance, PlainTextEncryption.Instance, null)); } connection.Dispose(); throw new EncryptionException("Invalid handshake received and no decryption works"); }
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"); }