/// <summary> /// Send Y to the remote client, with a random padding that is 0 to 512 bytes long /// (Either "1 A->B: Diffie Hellman Ya, PadA" or "2 B->A: Diffie Hellman Yb, PadB") /// </summary> protected async Task SendY() { byte[] toSend = new byte[96 + RandomNumber(512)]; random.GetBytes(toSend); Buffer.BlockCopy(Y, 0, toSend, 0, 96); await NetworkIO.SendAsync(socket, toSend, 0, toSend.Length, null, null, null).ConfigureAwait(false); }
protected override async ReusableTask doneReceiveY() { CreateCryptors(KeyABytes, KeyBBytes); // 3 A->B: HASH('req1', S) byte[] req1 = Hash(Req1Bytes, S); // ... HASH('req2', SKEY) byte[] req2 = Hash(Req2Bytes, SKEY.Hash); // ... HASH('req3', S) byte[] req3 = Hash(Req3Bytes, S); // HASH('req2', SKEY) xor HASH('req3', S) for (int i = 0; i < req2.Length; i++) { req2[i] ^= req3[i]; } byte[] padC = GeneratePad(); // 3 A->B: HASH('req1', S), HASH('req2', SKEY) xor HASH('req3', S), ENCRYPT(VC, crypto_provide, len(PadC), ... int bufferLength = req1.Length + req2.Length + VerificationConstant.Length + CryptoProvide.Length + 2 + padC.Length + 2 + InitialPayload.Length; byte[] buffer = ClientEngine.BufferPool.Rent(bufferLength); int offset = 0; offset += Message.Write(buffer, offset, req1); offset += Message.Write(buffer, offset, req2); offset += Message.Write(buffer, offset, VerificationConstant); DoEncrypt(buffer, offset - VerificationConstant.Length, VerificationConstant.Length); offset += Message.Write(buffer, offset, DoEncrypt(CryptoProvide)); offset += Message.Write(buffer, offset, (short)padC.Length); DoEncrypt(buffer, offset - 2, 2); offset += Message.Write(buffer, offset, DoEncrypt(padC)); // ... PadC, len(IA)), ENCRYPT(IA) offset += Message.Write(buffer, offset, (short)InitialPayload.Length); DoEncrypt(buffer, offset - 2, 2); offset += Message.Write(buffer, offset, DoEncrypt(InitialPayload)); // Send the entire message in one go try { await NetworkIO.SendAsync(socket, buffer, 0, bufferLength).ConfigureAwait(false); } finally { ClientEngine.BufferPool.Return(buffer); } DoDecrypt(VerificationConstant, 0, VerificationConstant.Length); await Synchronize(VerificationConstant, 616).ConfigureAwait(false); // 4 B->A: ENCRYPT(VC) }
/// <summary> /// Send Y to the remote client, with a random padding that is 0 to 512 bytes long /// (Either "1 A->B: Diffie Hellman Ya, PadA" or "2 B->A: Diffie Hellman Yb, PadB") /// </summary> async ReusableTask SendYAsync() { int length = 96 + RandomNumber(512); using (NetworkIO.BufferPool.Rent(length, out ByteBuffer toSend)) { Buffer.BlockCopy(Y, 0, toSend.Data, 0, 96); random.GetBytes(toSend.Data, 96, length - 96); await NetworkIO.SendAsync(socket, toSend, 0, length, null, null, null).ConfigureAwait(false); } }
/// <summary> /// Send Y to the remote client, with a random padding that is 0 to 512 bytes long /// (Either "1 A->B: Diffie Hellman Ya, PadA" or "2 B->A: Diffie Hellman Yb, PadB") /// </summary> async ReusableTask SendYAsync() { int length = 96 + RandomNumber(512); using (NetworkIO.BufferPool.Rent(length, out SocketMemory toSend)) { Y.AsSpan(0, 96).CopyTo(toSend.AsSpan()); random.GetBytes(toSend.Span.Slice(96)); await NetworkIO.SendAsync(socket, toSend, null, null, null).ConfigureAwait(false); } }
/// <summary> /// Send Y to the remote client, with a random padding that is 0 to 512 bytes long /// (Either "1 A->B: Diffie Hellman Ya, PadA" or "2 B->A: Diffie Hellman Yb, PadB") /// </summary> protected async ReusableTask SendY() { int length = 96 + RandomNumber(512); byte[] toSend = ClientEngine.BufferPool.Rent(length); Buffer.BlockCopy(Y, 0, toSend, 0, 96); random.GetBytes(toSend, 96, length - 96); try { await NetworkIO.SendAsync(socket, toSend, 0, length, null, null, null).ConfigureAwait(false); } finally { ClientEngine.BufferPool.Return(toSend); } }
/// <summary> /// Send Y to the remote client, with a random padding that is 0 to 512 bytes long /// (Either "1 A->B: Diffie Hellman Ya, PadA" or "2 B->A: Diffie Hellman Yb, PadB") /// </summary> protected async ReusableTask SendY() { var length = 96 + RandomNumber(512); var toSend = ClientEngine.BufferManager.GetBuffer(length); Buffer.BlockCopy(Y, 0, toSend, 0, 96); random.GetBytes(toSend, 96, length - 96); try { await NetworkIO.SendAsync(socket, toSend, 0, length, null, null, null).ConfigureAwait(false); } finally { ClientEngine.BufferManager.FreeBuffer(toSend); } }
private async Task StepThree() { CreateCryptors("keyA", "keyB"); // 3 A->B: HASH('req1', S) byte[] req1 = Hash(Encoding.ASCII.GetBytes("req1"), S); // ... HASH('req2', SKEY) byte[] req2 = Hash(Encoding.ASCII.GetBytes("req2"), SKEY.Hash); // ... HASH('req3', S) byte[] req3 = Hash(Encoding.ASCII.GetBytes("req3"), S); // HASH('req2', SKEY) xor HASH('req3', S) for (int i = 0; i < req2.Length; i++) { req2[i] ^= req3[i]; } byte[] padC = GeneratePad(); // 3 A->B: HASH('req1', S), HASH('req2', SKEY) xor HASH('req3', S), ENCRYPT(VC, crypto_provide, len(PadC), ... byte[] buffer = new byte[req1.Length + req2.Length + VerificationConstant.Length + CryptoProvide.Length + 2 + padC.Length + 2 + InitialPayload.Length]; int offset = 0; offset += Message.Write(buffer, offset, req1); offset += Message.Write(buffer, offset, req2); offset += Message.Write(buffer, offset, DoEncrypt(VerificationConstant)); offset += Message.Write(buffer, offset, DoEncrypt(CryptoProvide)); offset += Message.Write(buffer, offset, DoEncrypt(Len(padC))); offset += Message.Write(buffer, offset, DoEncrypt(padC)); // ... PadC, len(IA)), ENCRYPT(IA) offset += Message.Write(buffer, offset, DoEncrypt(Len(InitialPayload))); offset += Message.Write(buffer, offset, DoEncrypt(InitialPayload)); // Send the entire message in one go await NetworkIO.SendAsync(socket, buffer, 0, buffer.Length, null, null, null).ConfigureAwait(false); InitialPayload = BufferManager.EmptyBuffer; await Synchronize(DoDecrypt(VerificationConstant), 616); // 4 B->A: ENCRYPT(VC) }
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"); }
protected override async ReusableTask DoneReceiveY() { CreateCryptors(KeyABytes, KeyBBytes); // 3 A->B: HASH('req1', S) byte[] req1 = Hash(Req1Bytes, S); // ... HASH('req2', SKEY) byte[] req2 = Hash(Req2Bytes, SKEY.Span.ToArray()); // ... HASH('req3', S) byte[] req3 = Hash(Req3Bytes, S); // HASH('req2', SKEY) xor HASH('req3', S) for (int i = 0; i < req2.Length; i++) { req2[i] ^= req3[i]; } using var releaser = MemoryPool.Default.Rent(RandomNumber(512), out Memory <byte> padC); // 3 A->B: HASH('req1', S), HASH('req2', SKEY) xor HASH('req3', S), ENCRYPT(VC, crypto_provide, len(PadC), ... int bufferLength = req1.Length + req2.Length + VerificationConstant.Length + CryptoProvide.Length + 2 + padC.Length + 2 + InitialPayload.Length; using (NetworkIO.BufferPool.Rent(bufferLength, out SocketMemory buffer)) { var position = buffer.Memory; Message.Write(ref position, req1); Message.Write(ref position, req2); var before = position; Message.Write(ref position, VerificationConstant); Message.Write(ref position, CryptoProvide); Message.Write(ref position, (short)padC.Length); Message.Write(ref position, padC.Span); Message.Write(ref position, (short)InitialPayload.Length); Message.Write(ref position, InitialPayload); DoEncrypt(before.Span); await NetworkIO.SendAsync(socket, buffer).ConfigureAwait(false); } DoDecrypt(VerificationConstant); await SynchronizeAsync(VerificationConstant, 616).ConfigureAwait(false); // 4 B->A: ENCRYPT(VC) }
private async Task StepFour() { byte[] padD = GeneratePad(); SelectCrypto(b, false); // 4 B->A: ENCRYPT(VC, crypto_select, len(padD), padD) byte[] buffer = new byte[VerificationConstant.Length + CryptoSelect.Length + 2 + padD.Length]; int offset = 0; offset += Message.Write(buffer, offset, VerificationConstant); offset += Message.Write(buffer, offset, CryptoSelect); offset += Message.Write(buffer, offset, Len(padD)); offset += Message.Write(buffer, offset, padD); DoEncrypt(buffer, 0, buffer.Length); await NetworkIO.SendAsync(socket, buffer, 0, buffer.Length, null, null, null).ConfigureAwait(false); SelectCrypto(b, true); }
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(IPeerConnection connection, IList <EncryptionType> preferredEncryption, InfoHash infoHash, HandshakeMessage?handshake, Factories factories) { 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(); Memory <byte> handshakeBuffer = default; using var releaser = handshake == null ? default : NetworkIO.BufferPool.Rent(handshake.ByteLength, out handshakeBuffer); handshake?.Encode(handshakeBuffer.Span); if (preferredEncryption[0] != EncryptionType.PlainText) { using var encSocket = new PeerAEncryption(factories, infoHash, preferredEncryption, handshakeBuffer); 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 (handshakeBuffer.Length > 0) { await NetworkIO.SendAsync(connection, handshakeBuffer, 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 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"); }
async ReusableTask GotVerification(byte[] verifyBytes) { byte[] torrentHash = new byte[20]; byte[] myCP = new byte[4]; Array.Copy(verifyBytes, 0, torrentHash, 0, torrentHash.Length); // HASH('req2', SKEY) xor HASH('req3', S) if (!MatchSKEY(torrentHash)) { throw new EncryptionException("No valid SKey found"); } CreateCryptors(KeyBBytes, KeyABytes); DoDecrypt(verifyBytes, 20, 14); // ENCRYPT(VC, ... if (!Toolbox.ByteMatch(verifyBytes, 20, VerificationConstant, 0, VerificationConstant.Length)) { throw new EncryptionException("Verification constant was invalid"); } Array.Copy(verifyBytes, 28, myCP, 0, myCP.Length); // ...crypto_provide ... // We need to select the crypto *after* we send our response, otherwise the wrong // encryption will be used on the response int lenInitialPayload; int lenPadC = Message.ReadShort(verifyBytes, 32) + 2; using (NetworkIO.BufferPool.Rent(lenPadC, out ByteBuffer padC)) { await ReceiveMessageAsync(padC, lenPadC).ConfigureAwait(false); // padC DoDecrypt(padC.Data, 0, lenPadC); lenInitialPayload = Message.ReadShort(padC.Data, lenPadC - 2); } InitialData = new byte[lenInitialPayload]; // ... ENCRYPT(IA) using (NetworkIO.BufferPool.Rent(InitialData.Length, out ByteBuffer receiveBuffer)) { await ReceiveMessageAsync(receiveBuffer, InitialData.Length).ConfigureAwait(false); Buffer.BlockCopy(receiveBuffer.Data, 0, InitialData, 0, lenInitialPayload); DoDecrypt(InitialData, 0, InitialData.Length); // ... ENCRYPT(IA) } // Step Four byte[] padD = GeneratePad(); SelectCrypto(myCP, false); // 4 B->A: ENCRYPT(VC, crypto_select, len(padD), padD) int finalBufferLength = VerificationConstant.Length + CryptoSelect.Length + 2 + padD.Length; using (NetworkIO.BufferPool.Rent(finalBufferLength, out ByteBuffer buffer)) { int offset = 0; offset += Message.Write(buffer.Data, offset, VerificationConstant); offset += Message.Write(buffer.Data, offset, CryptoSelect); offset += Message.Write(buffer.Data, offset, Len(padD)); offset += Message.Write(buffer.Data, offset, padD); DoEncrypt(buffer.Data, 0, finalBufferLength); await NetworkIO.SendAsync(socket, buffer, 0, finalBufferLength).ConfigureAwait(false); } SelectCrypto(myCP, true); }
async ReusableTask GotVerification(Memory <byte> verifyBytes) { var infoHash = verifyBytes.Slice(0, 20); var verificationConstant = verifyBytes.Slice(20, 8); var myCP = verifyBytes.Slice(28, 4); var padCSpan = verifyBytes.Slice(32, 2); if (!MatchSKEY(infoHash.Span)) { throw new EncryptionException("No valid SKey found"); } // Create the encryptor/decryptors CreateCryptors(KeyBBytes, KeyABytes); // Decrypt everything after the infohash. DoDecrypt(verifyBytes.Slice(infoHash.Length).Span); if (!verificationConstant.Span.SequenceEqual(VerificationConstant)) { throw new EncryptionException("Verification constant was invalid"); } // We need to select the crypto *after* we send our response, otherwise the wrong // encryption will be used on the response int lenInitialPayload; int lenPadC = Message.ReadShort(padCSpan.Span) + 2; using (NetworkIO.BufferPool.Rent(lenPadC, out SocketMemory padC)) { await ReceiveMessageAsync(padC).ConfigureAwait(false); // padC DoDecrypt(padC.AsSpan()); lenInitialPayload = Message.ReadShort(padC.AsSpan(lenPadC - 2, 2)); } InitialData = new byte[lenInitialPayload]; // ... ENCRYPT(IA) using (NetworkIO.BufferPool.Rent(InitialData.Length, out SocketMemory receiveBuffer)) { await ReceiveMessageAsync(receiveBuffer).ConfigureAwait(false); receiveBuffer.Memory.CopyTo(InitialData); DoDecrypt(InitialData); // ... ENCRYPT(IA) } // Step Four using var releaser = MemoryPool.Default.Rent(RandomNumber(512), out Memory <byte> padD); SelectCrypto(myCP.Span, false); // 4 B->A: ENCRYPT(VC, crypto_select, len(padD), padD) int finalBufferLength = VerificationConstant.Length + CryptoSelect.Length + 2 + padD.Length; using (NetworkIO.BufferPool.Rent(finalBufferLength, out SocketMemory buffer)) { var position = buffer.Memory; Message.Write(ref position, VerificationConstant); Message.Write(ref position, CryptoSelect); Message.Write(ref position, (short)padD.Length); Message.Write(ref position, padD.Span); DoEncrypt(buffer.Span); await NetworkIO.SendAsync(socket, buffer).ConfigureAwait(false); } SelectCrypto(myCP.Span, true); }
private async Task gotVerification(byte[] verifyBytes) { byte[] torrentHash = new byte[20]; byte[] myVC = new byte[8]; byte[] myCP = new byte[4]; byte[] lenPadC = new byte[2]; Array.Copy(verifyBytes, 0, torrentHash, 0, torrentHash.Length); // HASH('req2', SKEY) xor HASH('req3', S) if (!MatchSKEY(torrentHash)) { throw new EncryptionException("No valid SKey found"); } CreateCryptors("keyB", "keyA"); DoDecrypt(verifyBytes, 20, 14); // ENCRYPT(VC, ... Array.Copy(verifyBytes, 20, myVC, 0, myVC.Length); if (!Toolbox.ByteMatch(myVC, VerificationConstant)) { throw new EncryptionException("Verification constant was invalid"); } Array.Copy(verifyBytes, 28, myCP, 0, myCP.Length); // ...crypto_provide ... // We need to select the crypto *after* we send our response, otherwise the wrong // encryption will be used on the response Array.Copy(verifyBytes, 32, lenPadC, 0, lenPadC.Length); // ... len(padC) ... var padC = new byte[DeLen(lenPadC) + 2]; await ReceiveMessage(padC, padC.Length); // padC DoDecrypt(padC, 0, padC.Length); byte[] lenInitialPayload = new byte[2]; // ... len(IA)) Array.Copy(padC, padC.Length - 2, lenInitialPayload, 0, 2); InitialData = new byte[DeLen(lenInitialPayload)]; // ... ENCRYPT(IA) await ReceiveMessage(InitialData, InitialData.Length); DoDecrypt(InitialData, 0, InitialData.Length); // ... ENCRYPT(IA) // Step Four byte[] padD = GeneratePad(); SelectCrypto(myCP, false); // 4 B->A: ENCRYPT(VC, crypto_select, len(padD), padD) byte[] buffer = new byte[VerificationConstant.Length + CryptoSelect.Length + 2 + padD.Length]; int offset = 0; offset += Message.Write(buffer, offset, VerificationConstant); offset += Message.Write(buffer, offset, CryptoSelect); offset += Message.Write(buffer, offset, Len(padD)); offset += Message.Write(buffer, offset, padD); DoEncrypt(buffer, 0, buffer.Length); await NetworkIO.SendAsync(socket, buffer, 0, buffer.Length, null, null, null).ConfigureAwait(false); SelectCrypto(myCP, true); }