diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index a6b20cb0..eb676ad2 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -586,7 +586,7 @@ namespace Shadowsocks.Controller { return; } - encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend); + encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend, false); } remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null); diff --git a/shadowsocks-csharp/Controller/Service/UDPRelay.cs b/shadowsocks-csharp/Controller/Service/UDPRelay.cs index a0662dd2..9d3d3dcf 100644 --- a/shadowsocks-csharp/Controller/Service/UDPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/UDPRelay.cs @@ -74,13 +74,13 @@ namespace Shadowsocks.Controller } public void Send(byte[] data, int length) { - IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); - byte[] dataIn = new byte[length - 3]; + IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password, _server.one_time_auth); + byte[] dataIn = new byte[length - 3 + IVEncryptor.ONETIMEAUTH_BYTES]; Array.Copy(data, 3, dataIn, 0, length - 3); - byte[] dataOut = new byte[length - 3 + 16]; + byte[] dataOut = new byte[length - 3 + 16 + IVEncryptor.ONETIMEAUTH_BYTES]; int outlen; - encryptor.Encrypt(dataIn, dataIn.Length, dataOut, out outlen); - _remote.SendTo(dataOut, _remoteEndPoint); + encryptor.Encrypt(dataIn, length - 3, dataOut, out outlen, true); + _remote.SendTo(dataOut, outlen, SocketFlags.None, _remoteEndPoint); } public void Receive() { @@ -97,7 +97,7 @@ namespace Shadowsocks.Controller byte[] dataOut = new byte[bytesRead]; int outlen; - IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); + IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password, _server.one_time_auth); encryptor.Decrypt(_buffer, bytesRead, dataOut, out outlen); byte[] sendBuf = new byte[outlen + 3]; diff --git a/shadowsocks-csharp/Data/libsscrypto.dll.gz b/shadowsocks-csharp/Data/libsscrypto.dll.gz index 5efa6661..b0429860 100755 Binary files a/shadowsocks-csharp/Data/libsscrypto.dll.gz and b/shadowsocks-csharp/Data/libsscrypto.dll.gz differ diff --git a/shadowsocks-csharp/Encryption/EncryptorBase.cs b/shadowsocks-csharp/Encryption/EncryptorBase.cs index b92233ec..594e67bf 100644 --- a/shadowsocks-csharp/Encryption/EncryptorBase.cs +++ b/shadowsocks-csharp/Encryption/EncryptorBase.cs @@ -26,7 +26,7 @@ namespace Shadowsocks.Encryption return hash; } - public abstract void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength); + public abstract void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength, bool udp); public abstract void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength); diff --git a/shadowsocks-csharp/Encryption/EncryptorFactory.cs b/shadowsocks-csharp/Encryption/EncryptorFactory.cs index 120bd53c..d6c89b9f 100644 --- a/shadowsocks-csharp/Encryption/EncryptorFactory.cs +++ b/shadowsocks-csharp/Encryption/EncryptorFactory.cs @@ -27,7 +27,7 @@ namespace Shadowsocks.Encryption } } - public static IEncryptor GetEncryptor(string method, string password, bool onetimeauth = false) + public static IEncryptor GetEncryptor(string method, string password, bool onetimeauth) { if (string.IsNullOrEmpty(method)) { diff --git a/shadowsocks-csharp/Encryption/IEncryptor.cs b/shadowsocks-csharp/Encryption/IEncryptor.cs index b45c62c7..33ce0816 100644 --- a/shadowsocks-csharp/Encryption/IEncryptor.cs +++ b/shadowsocks-csharp/Encryption/IEncryptor.cs @@ -6,7 +6,7 @@ namespace Shadowsocks.Encryption { public interface IEncryptor : IDisposable { - void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength); + void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength, bool udp); void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength); } } diff --git a/shadowsocks-csharp/Encryption/IVEncryptor.cs b/shadowsocks-csharp/Encryption/IVEncryptor.cs index a4b66ce4..8af5b9e5 100755 --- a/shadowsocks-csharp/Encryption/IVEncryptor.cs +++ b/shadowsocks-csharp/Encryption/IVEncryptor.cs @@ -15,12 +15,10 @@ namespace Shadowsocks.Encryption public const int ONETIMEAUTH_FLAG = 0x10; public const int ADDRTYPE_MASK = 0xF; - public const int ONETIMEAUTH_BYTES = 16; - public const int ONETIMEAUTH_KEYBYTES = 32; + public const int ONETIMEAUTH_BYTES = 10; - public const int HASH_BYTES = 4; public const int CLEN_BYTES = 2; - public const int AUTH_BYTES = HASH_BYTES + CLEN_BYTES; + public const int AUTH_BYTES = ONETIMEAUTH_BYTES + CLEN_BYTES; protected static byte[] tempbuf = new byte[MAX_INPUT_SIZE]; @@ -128,7 +126,7 @@ namespace Shadowsocks.Encryption protected abstract void cipherUpdate(bool isCipher, int length, byte[] buf, byte[] outbuf); - protected int ss_headlen(byte[] buf, int length) + protected int getHeadLen(byte[] buf, int length) { int len = 0; int atyp = length > 0 ? (buf[0] & ADDRTYPE_MASK) : 0; @@ -150,22 +148,21 @@ namespace Shadowsocks.Encryption return len; } - protected int ss_onetimeauth(byte[] auth, byte[] msg, int msg_len) + protected byte[] genOnetimeAuthHash(byte[] msg, int msg_len) { - byte[] auth_key = new byte[ONETIMEAUTH_KEYBYTES]; - byte[] auth_bytes = new byte[MAX_IV_LENGTH + MAX_KEY_LENGTH]; - Buffer.BlockCopy(_encryptIV, 0, auth_bytes, 0, ivLen); - Buffer.BlockCopy(_key, 0, auth_bytes, ivLen, keyLen); - Sodium.crypto_generichash(auth_key, ONETIMEAUTH_KEYBYTES, auth_bytes, (ulong)(ivLen + keyLen), null, 0); - return Sodium.crypto_onetimeauth(auth, msg, (ulong)msg_len, auth_key); + byte[] auth = new byte[ONETIMEAUTH_BYTES]; + byte[] hash = new byte[20]; + byte[] auth_key = new byte[MAX_IV_LENGTH + MAX_KEY_LENGTH]; + Buffer.BlockCopy(_encryptIV, 0, auth_key, 0, ivLen); + Buffer.BlockCopy(_key, 0, auth_key, ivLen, keyLen); + Sodium.ss_sha1_hmac_ex(auth_key, (uint)(ivLen + keyLen), + msg, 0, (uint)msg_len, hash); + Buffer.BlockCopy(hash, 0, auth, 0, ONETIMEAUTH_BYTES); + return auth; } - protected void ss_gen_hash(byte[] buf, ref int offset, ref int len, int buf_size) + protected void updateKeyBuffer() { - int size = len + AUTH_BYTES; - if (buf_size < (size + offset)) - throw new Exception("failed to generate hash: buffer size insufficient"); - if (_keyBuffer == null) { _keyBuffer = new byte[MAX_IV_LENGTH + 4]; @@ -174,23 +171,19 @@ namespace Shadowsocks.Encryption byte[] counter_bytes = BitConverter.GetBytes((uint)IPAddress.HostToNetworkOrder((int)counter)); Buffer.BlockCopy(counter_bytes, 0, _keyBuffer, ivLen, 4); - - byte[] hash = new byte[HASH_BYTES]; - byte[] tmp = new byte[len]; - Buffer.BlockCopy(buf, offset, tmp, 0, len); - Sodium.crypto_generichash(hash, HASH_BYTES, tmp, (ulong)len, _keyBuffer, (uint)_keyBuffer.Length); - - Buffer.BlockCopy(buf, offset, buf, offset + AUTH_BYTES, len); - Buffer.BlockCopy(hash, 0, buf, offset + CLEN_BYTES, HASH_BYTES); - byte[] clen = BitConverter.GetBytes((ushort)IPAddress.HostToNetworkOrder((short)len)); - Buffer.BlockCopy(clen, 0, buf, offset, CLEN_BYTES); - counter++; - len += AUTH_BYTES; - offset += len; } - public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength) + protected byte[] genHash(byte[] buf, int offset, int len) + { + byte[] hash = new byte[20]; + updateKeyBuffer(); + Sodium.ss_sha1_hmac_ex(_keyBuffer, (uint)_keyBuffer.Length, + buf, offset, (uint)len, hash); + return hash; + } + + public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength, bool udp) { if (!_encryptIVSent) { @@ -198,21 +191,32 @@ namespace Shadowsocks.Encryption randBytes(outbuf, ivLen); initCipher(outbuf, true); outlength = length + ivLen; - lock (tempbuf) + if (OnetimeAuth && ivLen > 0) { - if (OnetimeAuth) + if(!udp) + { + int headLen = getHeadLen(buf, length); + int dataLen = length - headLen; + buf[0] |= ONETIMEAUTH_FLAG; + byte[] hash = genOnetimeAuthHash(buf, headLen); + Buffer.BlockCopy(buf, headLen, buf, headLen + ONETIMEAUTH_BYTES + AUTH_BYTES, dataLen); + Buffer.BlockCopy(hash, 0, buf, headLen, ONETIMEAUTH_BYTES); + hash = genHash(buf, headLen + ONETIMEAUTH_BYTES + AUTH_BYTES, dataLen); + Buffer.BlockCopy(hash, 0, buf, headLen + ONETIMEAUTH_BYTES + CLEN_BYTES, ONETIMEAUTH_BYTES); + byte[] lenBytes = BitConverter.GetBytes((ushort)IPAddress.HostToNetworkOrder((short)dataLen)); + Buffer.BlockCopy(lenBytes, 0, buf, headLen + ONETIMEAUTH_BYTES, CLEN_BYTES); + length = headLen + ONETIMEAUTH_BYTES + AUTH_BYTES + dataLen; + } + else { - int headLen = ss_headlen(buf, length); - int len = length - headLen; - Buffer.BlockCopy(buf, headLen, buf, headLen + ONETIMEAUTH_BYTES, len); buf[0] |= ONETIMEAUTH_FLAG; - byte[] auth = new byte[ONETIMEAUTH_BYTES]; - ss_onetimeauth(auth, buf, headLen); - Buffer.BlockCopy(auth, 0, buf, headLen, ONETIMEAUTH_BYTES); - int offset = headLen + ONETIMEAUTH_BYTES; - ss_gen_hash(buf, ref offset, ref len, buf.Length); - length = headLen + ONETIMEAUTH_BYTES + len; + byte[] hash = genOnetimeAuthHash(buf, length); + Buffer.BlockCopy(hash, 0, buf, length, ONETIMEAUTH_BYTES); + length += ONETIMEAUTH_BYTES; } + } + lock (tempbuf) + { cipherUpdate(true, length, buf, tempbuf); outlength = length + ivLen; Buffer.BlockCopy(tempbuf, 0, outbuf, ivLen, length); @@ -220,10 +224,14 @@ namespace Shadowsocks.Encryption } else { - if (OnetimeAuth) + if (OnetimeAuth && ivLen > 0) { - int offset = 0; - ss_gen_hash(buf, ref offset, ref length, buf.Length); + byte[] hash = genHash(buf, 0, length); + Buffer.BlockCopy(buf, 0, buf, AUTH_BYTES, length); + byte[] lenBytes = BitConverter.GetBytes((ushort)IPAddress.HostToNetworkOrder((short)length)); + Buffer.BlockCopy(lenBytes, 0, buf, 0, CLEN_BYTES); + Buffer.BlockCopy(hash, 0, buf, CLEN_BYTES, ONETIMEAUTH_BYTES); + length += AUTH_BYTES; } outlength = length; cipherUpdate(true, length, buf, outbuf); diff --git a/shadowsocks-csharp/Encryption/Sodium.cs b/shadowsocks-csharp/Encryption/Sodium.cs index a6bc909e..8d690dd2 100755 --- a/shadowsocks-csharp/Encryption/Sodium.cs +++ b/shadowsocks-csharp/Encryption/Sodium.cs @@ -41,15 +41,10 @@ namespace Shadowsocks.Encryption public extern static int crypto_stream_chacha20_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, ulong ic, byte[] k); [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] - public extern static int crypto_generichash(byte[] outbuf, uint outlen, - byte[] inbuf, ulong inlen, - byte[] key, uint keylen); + public extern static void ss_sha1_hmac_ex(byte[] key, uint keylen, + byte[] input, int ioff, uint ilen, + byte[] output); - [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] - public extern static int crypto_onetimeauth(byte[] outbuf, byte[] inbuf, ulong inlen, byte[] k); - - [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] - public extern static int crypto_onetimeauth_verify(byte[] h, byte[] inbuf, ulong inlen, byte[] k); } } diff --git a/shadowsocks-csharp/Encryption/TableEncryptor.cs b/shadowsocks-csharp/Encryption/TableEncryptor.cs index 3de625a6..3f3c5f8c 100644 --- a/shadowsocks-csharp/Encryption/TableEncryptor.cs +++ b/shadowsocks-csharp/Encryption/TableEncryptor.cs @@ -31,7 +31,7 @@ namespace Shadowsocks.Encryption return new List(new string[]{"table"}); } - public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength) + public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength, bool udp) { byte[] result = new byte[length]; for (int i = 0; i < length; i++) diff --git a/test/UnitTest.cs b/test/UnitTest.cs index 6003770e..70c001f3 100755 --- a/test/UnitTest.cs +++ b/test/UnitTest.cs @@ -25,27 +25,27 @@ namespace test private void RunEncryptionRound(IEncryptor encryptor, IEncryptor decryptor) { byte[] plain = new byte[16384]; - byte[] cipher = new byte[plain.Length + 16]; + byte[] cipher = new byte[plain.Length + 16 + IVEncryptor.ONETIMEAUTH_BYTES + IVEncryptor.AUTH_BYTES]; byte[] plain2 = new byte[plain.Length + 16]; int outLen = 0; int outLen2 = 0; var random = new Random(); random.NextBytes(plain); - encryptor.Encrypt(plain, plain.Length, cipher, out outLen); + encryptor.Encrypt(plain, plain.Length, cipher, out outLen, false); decryptor.Decrypt(cipher, outLen, plain2, out outLen2); Assert.AreEqual(plain.Length, outLen2); for (int j = 0; j < plain.Length; j++) { Assert.AreEqual(plain[j], plain2[j]); } - encryptor.Encrypt(plain, 1000, cipher, out outLen); + encryptor.Encrypt(plain, 1000, cipher, out outLen, false); decryptor.Decrypt(cipher, outLen, plain2, out outLen2); Assert.AreEqual(1000, outLen2); for (int j = 0; j < outLen2; j++) { Assert.AreEqual(plain[j], plain2[j]); } - encryptor.Encrypt(plain, 12333, cipher, out outLen); + encryptor.Encrypt(plain, 12333, cipher, out outLen, false); decryptor.Decrypt(cipher, outLen, plain2, out outLen2); Assert.AreEqual(12333, outLen2); for (int j = 0; j < outLen2; j++)