@@ -339,7 +339,7 @@ namespace Shadowsocks.Controller | |||
if (bytesRead >= 5) | |||
{ | |||
_command = _connetionRecvBuffer[1]; | |||
switch(_command) | |||
switch (_command) | |||
{ | |||
case CMD_CONNECT: | |||
@@ -467,7 +467,7 @@ namespace Shadowsocks.Controller | |||
break; | |||
} | |||
Logger.Debug($"connect to {dstAddr}:{dstPort}"); | |||
Logger.Debug($"connect to {dstAddr}:{dstPort}"); | |||
_destEndPoint = SocketUtil.GetEndPoint(dstAddr, dstPort); | |||
@@ -820,7 +820,8 @@ namespace Shadowsocks.Controller | |||
{ | |||
try | |||
{ | |||
decryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend); | |||
bytesToSend = decryptor.Decrypt(_remoteSendBuffer, _remoteRecvBuffer.AsSpan(0, bytesRead)); | |||
// decryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend); | |||
} | |||
catch (CryptoErrorException) | |||
{ | |||
@@ -893,7 +894,8 @@ namespace Shadowsocks.Controller | |||
{ | |||
try | |||
{ | |||
encryptor.Encrypt(_connetionRecvBuffer, length, _connetionSendBuffer, out bytesToSend); | |||
bytesToSend = encryptor.Encrypt(_connetionRecvBuffer.AsSpan(0, length), _connetionSendBuffer); | |||
// encryptor.Encrypt(_connetionRecvBuffer, length, _connetionSendBuffer, out bytesToSend); | |||
} | |||
catch (CryptoErrorException) | |||
{ | |||
@@ -96,11 +96,11 @@ namespace Shadowsocks.Controller | |||
public void Send(byte[] data, int length) | |||
{ | |||
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); | |||
byte[] dataIn = new byte[length - 3]; | |||
Array.Copy(data, 3, dataIn, 0, length - 3); | |||
// byte[] dataIn = new byte[length - 3]; | |||
// Array.Copy(data, 3, dataIn, 0, length - 3); | |||
byte[] dataOut = new byte[65536]; // enough space for AEAD ciphers | |||
int outlen; | |||
encryptor.EncryptUDP(dataIn, length - 3, dataOut, out outlen); | |||
// encryptor.EncryptUDP(dataIn, length - 3, dataOut, out outlen); | |||
int outlen = encryptor.EncryptUDP(data.AsSpan(3), dataOut); | |||
logger.Debug(_localEndPoint, _remoteEndPoint, outlen, "UDP Relay"); | |||
_remote?.SendTo(dataOut, outlen, SocketFlags.None, _remoteEndPoint); | |||
} | |||
@@ -124,8 +124,8 @@ namespace Shadowsocks.Controller | |||
int outlen; | |||
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); | |||
encryptor.DecryptUDP(_buffer, bytesRead, dataOut, out outlen); | |||
// encryptor.DecryptUDP(_buffer, bytesRead, dataOut, out outlen); | |||
outlen = encryptor.DecryptUDP(dataOut, _buffer.AsSpan(0, bytesRead)); | |||
byte[] sendBuf = new byte[outlen + 3]; | |||
Array.Copy(dataOut, 0, sendBuf, 3, outlen); | |||
@@ -57,6 +57,8 @@ namespace Shadowsocks.Encryption.AEAD | |||
InitKey(password); | |||
// Initialize all-zero nonce for each connection | |||
nonce = new byte[nonceLen]; | |||
logger.Dump($"masterkey {instanceId}", masterKey, keyLen); | |||
logger.Dump($"nonce {instanceId}", nonce, keyLen); | |||
} | |||
protected abstract Dictionary<string, CipherInfo> getCiphers(); | |||
@@ -99,9 +101,10 @@ namespace Shadowsocks.Encryption.AEAD | |||
{ | |||
this.salt = new byte[saltLen]; | |||
Array.Copy(salt, this.salt, saltLen); | |||
logger.Dump("Salt", salt, saltLen); | |||
DeriveSessionKey(salt, masterKey, sessionKey); | |||
logger.Dump($"salt {instanceId}", salt, saltLen); | |||
logger.Dump($"sessionkey {instanceId}", sessionKey, keyLen); | |||
} | |||
public abstract int CipherEncrypt(ReadOnlySpan<byte> plain, Span<byte> cipher); | |||
@@ -109,85 +112,6 @@ namespace Shadowsocks.Encryption.AEAD | |||
#region TCP | |||
public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength) | |||
{ | |||
// push data | |||
buf.CopyTo(sharedBuffer, bufPtr); | |||
Span<byte> tmp = sharedBuffer.AsSpan(0, length + bufPtr); | |||
outlength = 0; | |||
logger.Debug("---Start Encryption"); | |||
if (!saltReady) | |||
{ | |||
saltReady = true; | |||
// Generate salt | |||
byte[] saltBytes = RNG.GetBytes(saltLen); | |||
InitCipher(saltBytes, true, false); | |||
Array.Copy(saltBytes, 0, outbuf, 0, saltLen); | |||
outlength = saltLen; | |||
logger.Debug($"_encryptSaltSent outlength {outlength}"); | |||
} | |||
if (!tcpRequestSent) | |||
{ | |||
tcpRequestSent = true; | |||
// The first TCP request | |||
byte[] encAddrBufBytes = new byte[AddressBufferLength + tagLen * 2 + ChunkLengthBytes]; | |||
// read addr byte to encrypt | |||
byte[] addrBytes = tmp.Slice(0, AddressBufferLength).ToArray(); | |||
tmp = tmp.Slice(AddressBufferLength); | |||
int encAddrBufLength = ChunkEncrypt(addrBytes, encAddrBufBytes); | |||
Array.Copy(encAddrBufBytes, 0, outbuf, outlength, encAddrBufLength); | |||
outlength += encAddrBufLength; | |||
logger.Debug($"_tcpRequestSent outlength {outlength}"); | |||
} | |||
// handle other chunks | |||
while (true) | |||
{ | |||
// calculate next chunk size | |||
int bufSize = tmp.Length; | |||
if (bufSize <= 0) | |||
{ | |||
return; | |||
} | |||
int chunklength = (int)Math.Min(bufSize, ChunkLengthMask); | |||
// read next chunk | |||
byte[] chunkBytes = tmp.Slice(0, chunklength).ToArray(); | |||
tmp = tmp.Slice(chunklength); | |||
byte[] encChunkBytes = new byte[chunklength + tagLen * 2 + ChunkLengthBytes]; | |||
int encChunkLength = ChunkEncrypt(chunkBytes, encChunkBytes); | |||
Buffer.BlockCopy(encChunkBytes, 0, outbuf, outlength, encChunkLength); | |||
outlength += encChunkLength; | |||
logger.Debug("chunks enc outlength " + outlength); | |||
// check if we have enough space for outbuf | |||
// if not, keep buf for next run, at this condition, buffer is not empty | |||
if (outlength + TCPHandler.ChunkOverheadSize > TCPHandler.BufferSize) | |||
{ | |||
logger.Debug("enc outbuf almost full, giving up"); | |||
// write rest data to head of shared buffer | |||
tmp.CopyTo(sharedBuffer); | |||
bufPtr = tmp.Length; | |||
return; | |||
} | |||
// check if buffer empty | |||
bufSize = tmp.Length; | |||
if (bufSize <= 0) | |||
{ | |||
logger.Debug("No more data to encrypt, leaving"); | |||
return; | |||
} | |||
} | |||
} | |||
public override int Encrypt(ReadOnlySpan<byte> plain, Span<byte> cipher) | |||
{ | |||
// push data | |||
@@ -253,86 +177,6 @@ namespace Shadowsocks.Encryption.AEAD | |||
} | |||
} | |||
public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength) | |||
{ | |||
outlength = 0; | |||
// drop all into buffer | |||
buf.CopyTo(sharedBuffer, bufPtr); | |||
Span<byte> tmp = buf.AsSpan(0, length + bufPtr); | |||
int bufSize = tmp.Length; | |||
logger.Debug("---Start Decryption"); | |||
if (!saltReady) | |||
{ | |||
// check if we get the leading salt | |||
if (bufSize <= saltLen) | |||
{ | |||
// need more, write back cache | |||
tmp.CopyTo(sharedBuffer); | |||
bufPtr = tmp.Length; | |||
return; | |||
} | |||
saltReady = true; | |||
// buffer.Get(saltLen); | |||
byte[] salt = tmp.Slice(0, saltLen).ToArray(); | |||
tmp = tmp.Slice(saltLen); | |||
InitCipher(salt, false, false); | |||
logger.Debug("get salt len " + saltLen); | |||
} | |||
// handle chunks | |||
while (true) | |||
{ | |||
bufSize = tmp.Length; | |||
// check if we have any data | |||
if (bufSize <= 0) | |||
{ | |||
logger.Debug("No data in _decCircularBuffer"); | |||
return; | |||
} | |||
// first get chunk length | |||
if (bufSize <= ChunkLengthBytes + tagLen) | |||
{ | |||
// so we only have chunk length and its tag? | |||
// wait more | |||
tmp.CopyTo(sharedBuffer); | |||
bufPtr = tmp.Length; | |||
return; | |||
} | |||
int len = ChunkDecrypt(outbuf.AsSpan(outlength), tmp); | |||
if (len <= 0) | |||
{ | |||
tmp.CopyTo(sharedBuffer); | |||
bufPtr = tmp.Length; | |||
return; | |||
} | |||
tmp = tmp.Slice(ChunkLengthBytes + tagLen + len + tagLen); | |||
// output to outbuf | |||
outlength += len; | |||
logger.Debug("aead dec outlength " + outlength); | |||
if (outlength + 100 > TCPHandler.BufferSize) | |||
{ | |||
logger.Debug("dec outbuf almost full, giving up"); | |||
tmp.CopyTo(sharedBuffer); | |||
bufPtr = tmp.Length; | |||
return; | |||
} | |||
bufSize = tmp.Length; | |||
// check if we already done all of them | |||
if (bufSize <= 0) | |||
{ | |||
logger.Debug("No data in _decCircularBuffer, already all done"); | |||
return; | |||
} | |||
} | |||
} | |||
public override int Decrypt(Span<byte> plain, ReadOnlySpan<byte> cipher) | |||
{ | |||
int outlength = 0; | |||
@@ -416,16 +260,6 @@ namespace Shadowsocks.Encryption.AEAD | |||
#endregion | |||
#region UDP | |||
public override void EncryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength) | |||
{ | |||
// Generate salt | |||
RNG.GetSpan(outbuf.AsSpan(0, saltLen)); | |||
InitCipher(outbuf, true, true); | |||
outlength = saltLen + CipherEncrypt( | |||
buf.AsSpan(0, length), | |||
outbuf.AsSpan(saltLen, length + tagLen)); | |||
} | |||
public override int EncryptUDP(ReadOnlySpan<byte> plain, Span<byte> cipher) | |||
{ | |||
RNG.GetSpan(cipher.Slice(0, saltLen)); | |||
@@ -433,12 +267,6 @@ namespace Shadowsocks.Encryption.AEAD | |||
return saltLen + CipherEncrypt(plain, cipher.Slice(saltLen)); | |||
} | |||
public override void DecryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength) | |||
{ | |||
InitCipher(buf, false, true); | |||
outlength = CipherDecrypt(outbuf, buf.AsSpan(saltLen, length - saltLen)); | |||
} | |||
public override int DecryptUDP(Span<byte> plain, ReadOnlySpan<byte> cipher) | |||
{ | |||
InitCipher(cipher.Slice(0, saltLen).ToArray(), false, true); | |||
@@ -33,14 +33,6 @@ namespace Shadowsocks.Encryption | |||
protected string Method; | |||
protected string Password; | |||
public abstract void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength); | |||
public abstract void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength); | |||
public abstract void EncryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength); | |||
public abstract void DecryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength); | |||
public override string ToString() | |||
{ | |||
return $"{instanceId}({Method},{Password})"; | |||
@@ -1,5 +1,4 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
namespace Shadowsocks.Encryption | |||
{ | |||
@@ -7,10 +6,6 @@ namespace Shadowsocks.Encryption | |||
{ | |||
/* length == -1 means not used */ | |||
int AddressBufferLength { set; get; } | |||
void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength); | |||
void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength); | |||
void EncryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength); | |||
void DecryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength); | |||
int Encrypt(ReadOnlySpan<byte> plain, Span<byte> cipher); | |||
int Decrypt(Span<byte> plain, ReadOnlySpan<byte> cipher); | |||
int EncryptUDP(ReadOnlySpan<byte> plain, Span<byte> cipher); | |||
@@ -1,7 +1,6 @@ | |||
using NLog; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Runtime.CompilerServices; | |||
using System.Text; | |||
namespace Shadowsocks.Encryption.Stream | |||
@@ -89,34 +88,6 @@ namespace Shadowsocks.Encryption.Stream | |||
protected abstract int CipherDecrypt(Span<byte> plain, ReadOnlySpan<byte> cipher); | |||
#region TCP | |||
[MethodImpl(MethodImplOptions.Synchronized)] | |||
public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength) | |||
{ | |||
int cipherOffset = 0; | |||
Span<byte> tmp = buf.AsSpan(0, length); | |||
logger.Trace($"{instanceId} encrypt TCP, generate iv: {!ivReady}"); | |||
if (!ivReady) | |||
{ | |||
// Generate IV | |||
byte[] ivBytes = RNG.GetBytes(ivLen); | |||
initCipher(ivBytes, true); | |||
Array.Copy(ivBytes, 0, outbuf, 0, ivLen); | |||
cipherOffset = ivLen; | |||
ivReady = true; | |||
} | |||
int size = tmp.Length; | |||
byte[] cipher = new byte[size]; | |||
CipherEncrypt(tmp, cipher); | |||
logger.DumpBase64($"plain {instanceId}", tmp.ToArray(), size); | |||
logger.DumpBase64($"cipher {instanceId}", cipher, cipher.Length); | |||
logger.Dump($"iv {instanceId}", iv, ivLen); | |||
Buffer.BlockCopy(cipher, 0, outbuf, cipherOffset, size); | |||
outlength = size + cipherOffset; | |||
} | |||
public override int Encrypt(ReadOnlySpan<byte> plain, Span<byte> cipher) | |||
{ | |||
int cipherOffset = 0; | |||
@@ -134,55 +105,12 @@ namespace Shadowsocks.Encryption.Stream | |||
int clen = CipherEncrypt(plain, cipher); | |||
logger.DumpBase64($"plain {instanceId}", plain); | |||
logger.DumpBase64($"cipher {instanceId}", cipher); | |||
logger.DumpBase64($"cipher {instanceId}", cipher.Slice(0, clen)); | |||
logger.Dump($"iv {instanceId}", iv, ivLen); | |||
return clen + cipherOffset; | |||
} | |||
private int recieveCtr = 0; | |||
[MethodImpl(MethodImplOptions.Synchronized)] | |||
public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength) | |||
{ | |||
Span<byte> tmp = buf.AsSpan(0, length); | |||
logger.Trace($"{instanceId} decrypt TCP, read iv: {!ivReady}"); | |||
// is first packet, need read iv | |||
if (!ivReady) | |||
{ | |||
// push to buffer in case of not enough data | |||
tmp.CopyTo(sharedBuffer.AsSpan(recieveCtr)); | |||
recieveCtr += tmp.Length; | |||
// not enough data for read iv, return 0 byte data | |||
if (recieveCtr <= ivLen) | |||
{ | |||
outlength = 0; | |||
return; | |||
} | |||
// start decryption | |||
ivReady = true; | |||
if (ivLen > 0) | |||
{ | |||
// read iv | |||
byte[] iv = sharedBuffer.AsSpan(0, ivLen).ToArray(); | |||
initCipher(iv, false); | |||
} | |||
else | |||
{ | |||
initCipher(Array.Empty<byte>(), false); | |||
} | |||
tmp = sharedBuffer.AsSpan(ivLen, recieveCtr - ivLen); | |||
} | |||
// read all data from buffer | |||
CipherDecrypt(outbuf, tmp); | |||
logger.DumpBase64($"cipher {instanceId}", tmp.ToArray(), tmp.Length); | |||
logger.DumpBase64($"plain {instanceId}", outbuf, tmp.Length); | |||
logger.Dump($"iv {instanceId}", iv, ivLen); | |||
outlength = tmp.Length; | |||
} | |||
public override int Decrypt(Span<byte> plain, ReadOnlySpan<byte> cipher) | |||
{ | |||
Span<byte> tmp;// = cipher; | |||
@@ -220,7 +148,7 @@ namespace Shadowsocks.Encryption.Stream | |||
// read all data from buffer | |||
int len = CipherDecrypt(plain, cipher.Slice(cipherOffset)); | |||
logger.DumpBase64($"cipher {instanceId}", cipher.Slice(cipherOffset)); | |||
logger.DumpBase64($"plain {instanceId}", plain); | |||
logger.DumpBase64($"plain {instanceId}", plain.Slice(0, len)); | |||
logger.Dump($"iv {instanceId}", iv, ivLen); | |||
return len; | |||
} | |||
@@ -228,20 +156,6 @@ namespace Shadowsocks.Encryption.Stream | |||
#endregion | |||
#region UDP | |||
[MethodImpl(MethodImplOptions.Synchronized)] | |||
public override void EncryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength) | |||
{ | |||
// Generate IV | |||
RNG.GetBytes(outbuf, ivLen); | |||
initCipher(outbuf, true); | |||
lock (sharedBuffer) | |||
{ | |||
CipherEncrypt(buf, sharedBuffer); | |||
outlength = length + ivLen; | |||
Buffer.BlockCopy(sharedBuffer, 0, outbuf, ivLen, length); | |||
} | |||
} | |||
public override int EncryptUDP(ReadOnlySpan<byte> plain, Span<byte> cipher) | |||
{ | |||
byte[] iv = RNG.GetBytes(ivLen); | |||
@@ -250,20 +164,6 @@ namespace Shadowsocks.Encryption.Stream | |||
return ivLen + CipherEncrypt(plain, cipher.Slice(ivLen)); | |||
} | |||
[MethodImpl(MethodImplOptions.Synchronized)] | |||
public override void DecryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength) | |||
{ | |||
// Get IV from first pos | |||
initCipher(buf, false); | |||
outlength = length - ivLen; | |||
lock (sharedBuffer) | |||
{ | |||
// C# could be multi-threaded | |||
Buffer.BlockCopy(buf, ivLen, sharedBuffer, 0, length - ivLen); | |||
CipherDecrypt(outbuf, sharedBuffer); | |||
} | |||
} | |||
public override int DecryptUDP(Span<byte> plain, ReadOnlySpan<byte> cipher) | |||
{ | |||
initCipher(cipher.Slice(0, ivLen).ToArray(), false); | |||