From f62c0b92bb3af5482b1236d72f9d4395fabf7e6c Mon Sep 17 00:00:00 2001 From: clowwindy Date: Fri, 12 Dec 2014 23:55:24 +0800 Subject: [PATCH] refactor --- shadowsocks-csharp/Encrypt/IVEncryptor.cs | 158 ++++++++++++++ .../Encrypt/PolarSSLEncryptor.cs | 201 ++++-------------- shadowsocks-csharp/Encrypt/Sodium.cs | 39 ++++ shadowsocks-csharp/Encrypt/SodiumEncryptor.cs | 63 ++++++ .../Properties/Resources.Designer.cs | 10 + shadowsocks-csharp/Properties/Resources.resx | 3 + shadowsocks-csharp/shadowsocks-csharp.csproj | 4 + 7 files changed, 317 insertions(+), 161 deletions(-) create mode 100755 shadowsocks-csharp/Encrypt/IVEncryptor.cs create mode 100755 shadowsocks-csharp/Encrypt/Sodium.cs create mode 100755 shadowsocks-csharp/Encrypt/SodiumEncryptor.cs diff --git a/shadowsocks-csharp/Encrypt/IVEncryptor.cs b/shadowsocks-csharp/Encrypt/IVEncryptor.cs new file mode 100755 index 00000000..66883918 --- /dev/null +++ b/shadowsocks-csharp/Encrypt/IVEncryptor.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections.Generic; +using System.Security.Cryptography; +using System.Text; + +namespace Shadowsocks.Encrypt +{ + public abstract class IVEncryptor + : EncryptorBase + { + protected static byte[] tempbuf = new byte[32768]; + + protected Dictionary ciphers; + + private static readonly Dictionary CachedKeys = new Dictionary(); + protected byte[] _encryptIV; + protected byte[] _decryptIV; + protected bool _decryptIVReceived; + protected bool _encryptIVSent; + protected int _encryptIVOffset = 0; + protected int _decryptIVOffset = 0; + protected string _method; + protected int _cipher; + protected int[] _cipherInfo; + protected byte[] _key; + protected int keyLen; + protected int ivLen; + + + public IVEncryptor(string method, string password) + : base(method, password) + { + InitKey(method, password); + } + + protected abstract Dictionary getCiphers(); + + protected void InitKey(string method, string password) + { + method = method.ToLower(); + _method = method; + string k = method + ":" + password; + ciphers = getCiphers(); + _cipherInfo = ciphers[_method]; + _cipher = _cipherInfo[2]; + if (_cipher == 0) + { + throw new Exception("method not found"); + } + keyLen = ciphers[_method][0]; + ivLen = ciphers[_method][1]; + if (CachedKeys.ContainsKey(k)) + { + _key = CachedKeys[k]; + } + else + { + byte[] passbuf = Encoding.UTF8.GetBytes(password); + _key = new byte[32]; + byte[] iv = new byte[16]; + bytesToKey(passbuf, _key); + CachedKeys[k] = _key; + } + } + + protected void bytesToKey(byte[] password, byte[] key) + { + byte[] result = new byte[password.Length + 16]; + int i = 0; + byte[] md5sum = null; + while (i < key.Length) + { + MD5 md5 = MD5.Create(); + if (i == 0) + { + md5sum = md5.ComputeHash(password); + } + else + { + md5sum.CopyTo(result, 0); + password.CopyTo(result, md5sum.Length); + md5sum = md5.ComputeHash(result); + } + md5sum.CopyTo(key, i); + i += md5sum.Length; + } + } + + protected static void randBytes(byte[] buf, int length) + { + byte[] temp = new byte[length]; + new Random().NextBytes(temp); + temp.CopyTo(buf, 0); + } + + protected virtual void initCipher(byte[] iv, bool isCipher) + { + if (ivLen > 0) + { + if (isCipher) + { + _encryptIV = new byte[ivLen]; + Array.Copy(iv, _encryptIV, ivLen); + } + else + { + _decryptIV = new byte[ivLen]; + Array.Copy(iv, _decryptIV, ivLen); + } + } + } + + protected abstract void cipherUpdate(bool isCipher, int length, byte[] buf, byte[] outbuf); + + public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength) + { + if (!_encryptIVSent) + { + _encryptIVSent = true; + randBytes(outbuf, ivLen); + initCipher(outbuf, true); + outlength = length + ivLen; + lock (tempbuf) + { + cipherUpdate(true, length, buf, tempbuf); + outlength = length + ivLen; + Buffer.BlockCopy(tempbuf, 0, outbuf, ivLen, length); + } + } + else + { + outlength = length; + cipherUpdate(true, length, buf, outbuf); + } + } + + public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength) + { + if (!_decryptIVReceived) + { + _decryptIVReceived = true; + initCipher(buf, false); + outlength = length - ivLen; + lock (tempbuf) + { + // C# could be multi-threaded + Buffer.BlockCopy(buf, ivLen, tempbuf, 0, length - ivLen); + cipherUpdate(false, length - ivLen, tempbuf, outbuf); + } + } + else + { + outlength = length; + cipherUpdate(false, length, buf, outbuf); + } + } + } +} diff --git a/shadowsocks-csharp/Encrypt/PolarSSLEncryptor.cs b/shadowsocks-csharp/Encrypt/PolarSSLEncryptor.cs index 959fa350..c9548acb 100755 --- a/shadowsocks-csharp/Encrypt/PolarSSLEncryptor.cs +++ b/shadowsocks-csharp/Encrypt/PolarSSLEncryptor.cs @@ -8,32 +8,13 @@ using System.Threading; namespace Shadowsocks.Encrypt { public class PolarSSLEncryptor - : EncryptorBase, IDisposable + : IVEncryptor, IDisposable { const int CIPHER_AES = 1; const int CIPHER_RC4 = 2; - static Dictionary ciphers = new Dictionary { - {"aes-128-cfb", new int[]{16, 16, CIPHER_AES, PolarSSL.AES_CTX_SIZE}}, - {"aes-192-cfb", new int[]{24, 16, CIPHER_AES, PolarSSL.AES_CTX_SIZE}}, - {"aes-256-cfb", new int[]{32, 16, CIPHER_AES, PolarSSL.AES_CTX_SIZE}}, - {"rc4", new int[]{16, 0, CIPHER_RC4, PolarSSL.ARC4_CTX_SIZE}}, - {"rc4-md5", new int[]{16, 16, CIPHER_RC4, PolarSSL.ARC4_CTX_SIZE}}, - }; - - private static readonly Dictionary CachedKeys = new Dictionary(); - private int _cipher; - private int[] _cipherInfo; - private byte[] _key; private IntPtr _encryptCtx = IntPtr.Zero; private IntPtr _decryptCtx = IntPtr.Zero; - private byte[] _encryptIV; - private byte[] _decryptIV; - private int _encryptIVOffset = 0; - private int _decryptIVOffset = 0; - private string _method; - private int keyLen; - private int ivLen; public PolarSSLEncryptor(string method, string password) : base(method, password) @@ -41,66 +22,31 @@ namespace Shadowsocks.Encrypt InitKey(method, password); } - private static void randBytes(byte[] buf, int length) + protected override Dictionary getCiphers() { - byte[] temp = new byte[length]; - new Random().NextBytes(temp); - temp.CopyTo(buf, 0); + return new Dictionary { + {"aes-128-cfb", new int[]{16, 16, CIPHER_AES, PolarSSL.AES_CTX_SIZE}}, + {"aes-192-cfb", new int[]{24, 16, CIPHER_AES, PolarSSL.AES_CTX_SIZE}}, + {"aes-256-cfb", new int[]{32, 16, CIPHER_AES, PolarSSL.AES_CTX_SIZE}}, + {"rc4", new int[]{16, 0, CIPHER_RC4, PolarSSL.ARC4_CTX_SIZE}}, + {"rc4-md5", new int[]{16, 16, CIPHER_RC4, PolarSSL.ARC4_CTX_SIZE}}, + }; } - private void bytesToKey(byte[] password, byte[] key) + protected override void initCipher(byte[] iv, bool isCipher) { - byte[] result = new byte[password.Length + 16]; - int i = 0; - byte[] md5sum = null; - while (i < key.Length) - { - MD5 md5 = MD5.Create(); - if (i == 0) - { - md5sum = md5.ComputeHash(password); - } - else - { - md5sum.CopyTo(result, 0); - password.CopyTo(result, md5sum.Length); - md5sum = md5.ComputeHash(result); - } - md5sum.CopyTo(key, i); - i += md5sum.Length; - } - } + base.initCipher(iv, isCipher); - private void InitKey(string method, string password) - { - method = method.ToLower(); - _method = method; - string k = method + ":" + password; - _cipherInfo = ciphers[_method]; - _cipher = _cipherInfo[2]; - if (_cipher == 0) - { - throw new Exception("method not found"); - } - keyLen = ciphers[_method][0]; - ivLen = ciphers[_method][1]; - if (CachedKeys.ContainsKey(k)) + IntPtr ctx; + ctx = Marshal.AllocHGlobal(_cipherInfo[3]); + if (isCipher) { - _key = CachedKeys[k]; + _encryptCtx = ctx; } else { - byte[] passbuf = Encoding.UTF8.GetBytes(password); - _key = new byte[32]; - byte[] iv = new byte[16]; - bytesToKey(passbuf, _key); - CachedKeys[k] = _key; + _decryptCtx = ctx; } - } - - private void InitCipher(ref IntPtr ctx, byte[] iv, bool isCipher) - { - ctx = Marshal.AllocHGlobal(_cipherInfo[3]); byte[] realkey; if (_method == "rc4-md5") { @@ -120,16 +66,6 @@ namespace Shadowsocks.Encrypt // PolarSSL takes key length by bit // since we'll use CFB mode, here we both do enc, not dec PolarSSL.aes_setkey_enc(ctx, realkey, keyLen * 8); - if (isCipher) - { - _encryptIV = new byte[ivLen]; - Array.Copy(iv, _encryptIV, ivLen); - } - else - { - _decryptIV = new byte[ivLen]; - Array.Copy(iv, _decryptIV, ivLen); - } } else if (_cipher == CIPHER_RC4) { @@ -139,98 +75,41 @@ namespace Shadowsocks.Encrypt } } - - - static byte[] tempbuf = new byte[32768]; - - public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength) + protected override void cipherUpdate(bool isCipher, int length, byte[] buf, byte[] outbuf) { - if (_encryptCtx == IntPtr.Zero) + // C# could be multi-threaded + if (_disposed) { - randBytes(outbuf, ivLen); - InitCipher(ref _encryptCtx, outbuf, true); - outlength = length + ivLen; - lock (tempbuf) - { - // C# could be multi-threaded - if (_disposed) - { - throw new ObjectDisposedException(this.ToString()); - } - switch (_cipher) - { - case CIPHER_AES: - PolarSSL.aes_crypt_cfb128(_encryptCtx, PolarSSL.AES_ENCRYPT, length, ref _encryptIVOffset, _encryptIV, buf, tempbuf); - break; - case CIPHER_RC4: - PolarSSL.arc4_crypt(_encryptCtx, length, buf, tempbuf); - break; - } - outlength = length + ivLen; - Buffer.BlockCopy(tempbuf, 0, outbuf, ivLen, length); - - } + throw new ObjectDisposedException(this.ToString()); + } + byte[] iv; + int ivOffset; + if (isCipher) + { + iv = _encryptIV; + ivOffset = _encryptIVOffset; } else { - outlength = length; - if (_disposed) - { - throw new ObjectDisposedException(this.ToString()); - } - switch (_cipher) - { - case CIPHER_AES: - PolarSSL.aes_crypt_cfb128(_encryptCtx, PolarSSL.AES_ENCRYPT, length, ref _encryptIVOffset, _encryptIV, buf, outbuf); - break; - case CIPHER_RC4: - PolarSSL.arc4_crypt(_encryptCtx, length, buf, outbuf); - break; - } + iv = _decryptIV; + ivOffset = _decryptIVOffset; } - } - - public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength) - { - if (_decryptCtx == IntPtr.Zero) + switch (_cipher) { - InitCipher(ref _decryptCtx, buf, false); - outlength = length - ivLen; - lock (tempbuf) - { - // C# could be multi-threaded - Buffer.BlockCopy(buf, ivLen, tempbuf, 0, length - ivLen); - if (_disposed) + case CIPHER_AES: + PolarSSL.aes_crypt_cfb128(_encryptCtx, isCipher ? PolarSSL.AES_ENCRYPT : PolarSSL.AES_DECRYPT, length, ref ivOffset, iv, buf, outbuf); + if (isCipher) { - throw new ObjectDisposedException(this.ToString()); + _encryptIVOffset = ivOffset; } - switch (_cipher) + else { - case CIPHER_AES: - PolarSSL.aes_crypt_cfb128(_decryptCtx, PolarSSL.AES_DECRYPT, length - ivLen, ref _decryptIVOffset, _decryptIV, tempbuf, outbuf); - break; - case CIPHER_RC4: - PolarSSL.arc4_crypt(_decryptCtx, length - ivLen, tempbuf, outbuf); - break; + _decryptIVOffset = ivOffset; } - } - } - else - { - outlength = length; - if (_disposed) - { - throw new ObjectDisposedException(this.ToString()); - } - switch (_cipher) - { - case CIPHER_AES: - PolarSSL.aes_crypt_cfb128(_decryptCtx, PolarSSL.AES_DECRYPT, length, ref _decryptIVOffset, _decryptIV, buf, outbuf); - break; - case CIPHER_RC4: - PolarSSL.arc4_crypt(_decryptCtx, length, buf, outbuf); - break; - } + break; + case CIPHER_RC4: + PolarSSL.arc4_crypt(_encryptCtx, length, buf, outbuf); + break; } } diff --git a/shadowsocks-csharp/Encrypt/Sodium.cs b/shadowsocks-csharp/Encrypt/Sodium.cs new file mode 100755 index 00000000..14f75872 --- /dev/null +++ b/shadowsocks-csharp/Encrypt/Sodium.cs @@ -0,0 +1,39 @@ +using Shadowsocks.Controller; +using Shadowsocks.Properties; +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +namespace Shadowsocks.Encrypt +{ + public class Sodium + { + const string DLLNAME = "libsodium"; + + static Sodium() + { + string tempPath = Path.GetTempPath(); + string dllPath = tempPath + "/libsodium.dll"; + try + { + FileManager.UncompressFile(dllPath, Resources.libsodium_dll); + } + catch (IOException e) + { + Console.WriteLine(e.ToString()); + } + LoadLibrary(dllPath); + } + + [DllImport("Kernel32.dll")] + private static extern IntPtr LoadLibrary(string path); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public extern static void crypto_stream_salsa20_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, ulong ic, byte[] k); + + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public extern static void crypto_stream_chacha20_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, ulong ic, byte[] k); + } +} diff --git a/shadowsocks-csharp/Encrypt/SodiumEncryptor.cs b/shadowsocks-csharp/Encrypt/SodiumEncryptor.cs new file mode 100755 index 00000000..d08027d7 --- /dev/null +++ b/shadowsocks-csharp/Encrypt/SodiumEncryptor.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Shadowsocks.Encrypt +{ + public class SodiumEncryptor + : IVEncryptor, IDisposable + { + const int CIPHER_SALSA20 = 1; + const int CIPHER_CHACHA20 = 2; + + protected uint _encryptBytesRemaining; + protected uint _decryptBytesRemaining; + protected ulong _encryptIC; + protected ulong _decryptIC; + + public SodiumEncryptor(string method, string password) + : base(method, password) + { + InitKey(method, password); + } + + protected override Dictionary getCiphers() + { + return new Dictionary { + {"salsa20", new int[]{32, 8, CIPHER_SALSA20, PolarSSL.AES_CTX_SIZE}}, + {"chacha20", new int[]{32, 8, CIPHER_CHACHA20, PolarSSL.AES_CTX_SIZE}}, + }; ; + } + + protected override void cipherUpdate(bool isCipher, int length, byte[] buf, byte[] outbuf) + { + uint bytesRemaining; + ulong ic; + if (isCipher) + { + bytesRemaining = _encryptBytesRemaining; + ic = _encryptIC; + } + else + { + bytesRemaining = _decryptBytesRemaining; + ic = _decryptIC; + } + + if (isCipher) + { + _encryptBytesRemaining = bytesRemaining; + _encryptIC = ic; + } + else + { + _decryptBytesRemaining = bytesRemaining; + _decryptIC = ic; + } + } + + public override void Dispose() + { + } + } +} diff --git a/shadowsocks-csharp/Properties/Resources.Designer.cs b/shadowsocks-csharp/Properties/Resources.Designer.cs index 1af315b5..ffc24b33 100755 --- a/shadowsocks-csharp/Properties/Resources.Designer.cs +++ b/shadowsocks-csharp/Properties/Resources.Designer.cs @@ -60,6 +60,16 @@ namespace Shadowsocks.Properties { } } + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] libsodium_dll { + get { + object obj = ResourceManager.GetObject("libsodium_dll", resourceCulture); + return ((byte[])(obj)); + } + } + /// /// Looks up a localized resource of type System.Byte[]. /// diff --git a/shadowsocks-csharp/Properties/Resources.resx b/shadowsocks-csharp/Properties/Resources.resx index a644b5f9..4b6f8f3e 100755 --- a/shadowsocks-csharp/Properties/Resources.resx +++ b/shadowsocks-csharp/Properties/Resources.resx @@ -118,6 +118,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + ..\data\libsodium.dll.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + ..\Data\polarssl.dll.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index 6221c918..06f5bc3a 100755 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -77,8 +77,11 @@ + + + @@ -125,6 +128,7 @@ Designer +