From aa8e6558d5781ad392bd52abd61f11268cecbf0a Mon Sep 17 00:00:00 2001 From: clowwindy Date: Wed, 12 Nov 2014 01:51:11 +0800 Subject: [PATCH] move polarssl context to unmanaged heap; fix #32 --- shadowsocks-csharp/Encrypt/PolarSSL.cs | 26 +- .../Encrypt/PolarSSLEncryptor.cs | 267 +++++++++--------- test/UnitTest.cs | 27 +- test/test.csproj | 3 + 4 files changed, 160 insertions(+), 163 deletions(-) diff --git a/shadowsocks-csharp/Encrypt/PolarSSL.cs b/shadowsocks-csharp/Encrypt/PolarSSL.cs index 1ce44439..2ccf35ac 100755 --- a/shadowsocks-csharp/Encrypt/PolarSSL.cs +++ b/shadowsocks-csharp/Encrypt/PolarSSL.cs @@ -7,38 +7,38 @@ namespace Shadowsocks.Encrypt { public class PolarSSL { - const string DLLNAME = "libpolarssl"; + const string DLLNAME = "polarssl"; public const int AES_CTX_SIZE = 8 + 4 * 68; public const int AES_ENCRYPT = 1; public const int AES_DECRYPT = 0; [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] - public extern static void aes_init(byte[] ctx); + public extern static void aes_init(IntPtr ctx); [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] - public extern static void aes_free(byte[] ctx); + public extern static void aes_free(IntPtr ctx); [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] - public extern static int aes_setkey_enc(byte[] ctx, byte[] key, int keysize); + public extern static int aes_setkey_enc(IntPtr ctx, byte[] key, int keysize); [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] - public extern static int aes_crypt_cfb128(byte[] ctx, int mode, int length, ref int iv_off, byte[] iv, byte[] input, byte[] output); + public extern static int aes_crypt_cfb128(IntPtr ctx, int mode, int length, byte[] iv_off, byte[] iv, byte[] input, byte[] output); public const int ARC4_CTX_SIZE = 264; [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] - public extern static void arc4_init(byte[] ctx); + public extern static void arc4_init(IntPtr ctx); [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] - public extern static void arc4_free(byte[] ctx); + public extern static void arc4_free(IntPtr ctx); [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] - public extern static void arc4_setup(byte[] ctx, byte[] key, int keysize); + public extern static void arc4_setup(IntPtr ctx, byte[] key, int keysize); [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] - public extern static int arc4_crypt(byte[] ctx, int length, byte[] input, byte[] output); + public extern static int arc4_crypt(IntPtr ctx, int length, byte[] input, byte[] output); public const int BLOWFISH_CTX_SIZE = 4168; @@ -46,16 +46,16 @@ namespace Shadowsocks.Encrypt public const int BLOWFISH_DECRYPT = 0; [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] - public extern static void blowfish_init(byte[] ctx); + public extern static void blowfish_init(IntPtr ctx); [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] - public extern static void blowfish_free(byte[] ctx); + public extern static void blowfish_free(IntPtr ctx); [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] - public extern static int blowfish_setkey(byte[] ctx, byte[] key, int keysize); + public extern static int blowfish_setkey(IntPtr ctx, byte[] key, int keysize); [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] - public extern static int blowfish_crypt_cfb64(byte[] ctx, int mode, int length, ref int iv_off, byte[] iv, byte[] input, byte[] output); + public extern static int blowfish_crypt_cfb64(IntPtr ctx, int mode, int length, byte[] iv_off, byte[] iv, byte[] input, byte[] output); } } diff --git a/shadowsocks-csharp/Encrypt/PolarSSLEncryptor.cs b/shadowsocks-csharp/Encrypt/PolarSSLEncryptor.cs index b959565e..c10ae6d4 100755 --- a/shadowsocks-csharp/Encrypt/PolarSSLEncryptor.cs +++ b/shadowsocks-csharp/Encrypt/PolarSSLEncryptor.cs @@ -27,12 +27,12 @@ namespace Shadowsocks.Encrypt private int _cipher; private int[] _cipherInfo; private byte[] _key; - private byte[] _encryptCtx; - private byte[] _decryptCtx; + private IntPtr _encryptCtx = IntPtr.Zero; + private IntPtr _decryptCtx = IntPtr.Zero; private byte[] _encryptIV; private byte[] _decryptIV; - private int _encryptIVOffset; - private int _decryptIVOffset; + private byte[] _encryptIVOffset; + private byte[] _decryptIVOffset; private string _method; private int keyLen; private int ivLen; @@ -100,63 +100,65 @@ namespace Shadowsocks.Encrypt } } - private void InitCipher(ref byte[] ctx, byte[] iv, bool isCipher) + private void InitCipher(ref IntPtr ctx, byte[] iv, bool isCipher) { - ctx = new byte[_cipherInfo[3]]; - lock (ctx) + ctx = Marshal.AllocHGlobal(_cipherInfo[3]); + byte[] realkey; + if (_method == "rc4-md5") { - byte[] realkey; - if (_method == "rc4-md5") + byte[] temp = new byte[keyLen + ivLen]; + realkey = new byte[keyLen]; + Array.Copy(_key, 0, temp, 0, keyLen); + Array.Copy(iv, 0, temp, keyLen, ivLen); + realkey = MD5.Create().ComputeHash(temp); + } + else + { + realkey = _key; + } + if (_cipher == CIPHER_AES) + { + PolarSSL.aes_init(ctx); + // 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) { - byte[] temp = new byte[keyLen + ivLen]; - realkey = new byte[keyLen]; - Array.Copy(_key, 0, temp, 0, keyLen); - Array.Copy(iv, 0, temp, keyLen, ivLen); - realkey = MD5.Create().ComputeHash(temp); + _encryptIV = new byte[ivLen]; + _encryptIVOffset = new byte[8]; + Array.Copy(iv, _encryptIV, ivLen); } else { - realkey = _key; - } - if (_cipher == CIPHER_AES) - { - PolarSSL.aes_init(ctx); - // 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); - } + _decryptIV = new byte[ivLen]; + _decryptIVOffset = new byte[8]; + Array.Copy(iv, _decryptIV, ivLen); } - else if (_cipher == CIPHER_BF) + } + else if (_cipher == CIPHER_BF) + { + PolarSSL.blowfish_init(ctx); + // PolarSSL takes key length by bit + PolarSSL.blowfish_setkey(ctx, realkey, keyLen * 8); + if (isCipher) { - PolarSSL.blowfish_init(ctx); - // PolarSSL takes key length by bit - PolarSSL.blowfish_setkey(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); - } + _encryptIV = new byte[ivLen]; + _encryptIVOffset = new byte[8]; + Array.Copy(iv, _encryptIV, ivLen); } - else if (_cipher == CIPHER_RC4) + else { - PolarSSL.arc4_init(ctx); - PolarSSL.arc4_setup(ctx, realkey, keyLen); + _decryptIV = new byte[ivLen]; + _decryptIVOffset = new byte[8]; + Array.Copy(iv, _decryptIV, ivLen); } } + else if (_cipher == CIPHER_RC4) + { + PolarSSL.arc4_init(ctx); + // PolarSSL RC4 takes key length by byte + PolarSSL.arc4_setup(ctx, realkey, keyLen); + } } @@ -165,7 +167,7 @@ namespace Shadowsocks.Encrypt public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength) { - if (_encryptCtx == null) + if (_encryptCtx == IntPtr.Zero) { randBytes(outbuf, ivLen); InitCipher(ref _encryptCtx, outbuf, true); @@ -173,34 +175,6 @@ namespace Shadowsocks.Encrypt lock (tempbuf) { // C# could be multi-threaded - lock (_encryptCtx) - { - 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_BF: - PolarSSL.blowfish_crypt_cfb64(_encryptCtx, PolarSSL.BLOWFISH_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); - } - } - } - else - { - outlength = length; - lock (_encryptCtx) - { if (_disposed) { throw new ObjectDisposedException(this.ToString()); @@ -208,22 +182,45 @@ namespace Shadowsocks.Encrypt switch (_cipher) { case CIPHER_AES: - PolarSSL.aes_crypt_cfb128(_encryptCtx, PolarSSL.AES_ENCRYPT, length, ref _encryptIVOffset, _encryptIV, buf, outbuf); + PolarSSL.aes_crypt_cfb128(_encryptCtx, PolarSSL.AES_ENCRYPT, length, _encryptIVOffset, _encryptIV, buf, tempbuf); break; case CIPHER_BF: - PolarSSL.blowfish_crypt_cfb64(_encryptCtx, PolarSSL.BLOWFISH_ENCRYPT, length, ref _encryptIVOffset, _encryptIV, buf, outbuf); + PolarSSL.blowfish_crypt_cfb64(_encryptCtx, PolarSSL.BLOWFISH_ENCRYPT, length, _encryptIVOffset, _encryptIV, buf, tempbuf); break; case CIPHER_RC4: - PolarSSL.arc4_crypt(_encryptCtx, length, buf, outbuf); + PolarSSL.arc4_crypt(_encryptCtx, length, buf, tempbuf); break; } + outlength = length + ivLen; + Buffer.BlockCopy(tempbuf, 0, outbuf, ivLen, length); + + } + } + else + { + outlength = length; + if (_disposed) + { + throw new ObjectDisposedException(this.ToString()); + } + switch (_cipher) + { + case CIPHER_AES: + PolarSSL.aes_crypt_cfb128(_encryptCtx, PolarSSL.AES_ENCRYPT, length, _encryptIVOffset, _encryptIV, buf, outbuf); + break; + case CIPHER_BF: + PolarSSL.blowfish_crypt_cfb64(_encryptCtx, PolarSSL.BLOWFISH_ENCRYPT, length, _encryptIVOffset, _encryptIV, buf, outbuf); + break; + case CIPHER_RC4: + PolarSSL.arc4_crypt(_encryptCtx, length, buf, outbuf); + break; } } } public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength) { - if (_decryptCtx == null) + if (_decryptCtx == IntPtr.Zero) { InitCipher(ref _decryptCtx, buf, false); outlength = length - ivLen; @@ -231,32 +228,6 @@ namespace Shadowsocks.Encrypt { // C# could be multi-threaded Buffer.BlockCopy(buf, ivLen, tempbuf, 0, length - ivLen); - lock (_decryptCtx) - { - if (_disposed) - { - throw new ObjectDisposedException(this.ToString()); - } - switch (_cipher) - { - case CIPHER_AES: - PolarSSL.aes_crypt_cfb128(_decryptCtx, PolarSSL.AES_DECRYPT, length - ivLen, ref _decryptIVOffset, _decryptIV, tempbuf, outbuf); - break; - case CIPHER_BF: - PolarSSL.blowfish_crypt_cfb64(_decryptCtx, PolarSSL.BLOWFISH_DECRYPT, length - ivLen, ref _decryptIVOffset, _decryptIV, tempbuf, outbuf); - break; - case CIPHER_RC4: - PolarSSL.arc4_crypt(_decryptCtx, length - ivLen, tempbuf, outbuf); - break; - } - } - } - } - else - { - outlength = length; - lock (_decryptCtx) - { if (_disposed) { throw new ObjectDisposedException(this.ToString()); @@ -264,17 +235,37 @@ namespace Shadowsocks.Encrypt switch (_cipher) { case CIPHER_AES: - PolarSSL.aes_crypt_cfb128(_decryptCtx, PolarSSL.AES_DECRYPT, length, ref _decryptIVOffset, _decryptIV, buf, outbuf); + PolarSSL.aes_crypt_cfb128(_decryptCtx, PolarSSL.AES_DECRYPT, length - ivLen, _decryptIVOffset, _decryptIV, tempbuf, outbuf); break; case CIPHER_BF: - PolarSSL.blowfish_crypt_cfb64(_decryptCtx, PolarSSL.BLOWFISH_DECRYPT, length, ref _decryptIVOffset, _decryptIV, buf, outbuf); + PolarSSL.blowfish_crypt_cfb64(_decryptCtx, PolarSSL.BLOWFISH_DECRYPT, length - ivLen, _decryptIVOffset, _decryptIV, tempbuf, outbuf); break; case CIPHER_RC4: - PolarSSL.arc4_crypt(_decryptCtx, length, buf, outbuf); + PolarSSL.arc4_crypt(_decryptCtx, length - ivLen, tempbuf, outbuf); break; } } } + else + { + outlength = length; + if (_disposed) + { + throw new ObjectDisposedException(this.ToString()); + } + switch (_cipher) + { + case CIPHER_AES: + PolarSSL.aes_crypt_cfb128(_decryptCtx, PolarSSL.AES_DECRYPT, length, _decryptIVOffset, _decryptIV, buf, outbuf); + break; + case CIPHER_BF: + PolarSSL.blowfish_crypt_cfb64(_decryptCtx, PolarSSL.BLOWFISH_DECRYPT, length, _decryptIVOffset, _decryptIV, buf, outbuf); + break; + case CIPHER_RC4: + PolarSSL.arc4_crypt(_decryptCtx, length, buf, outbuf); + break; + } + } } #region IDisposable @@ -304,41 +295,39 @@ namespace Shadowsocks.Encrypt if (disposing) { - if (_encryptCtx != null) + if (_encryptCtx != IntPtr.Zero) { - lock (_encryptCtx) + switch (_cipher) { - switch (_cipher) - { - case CIPHER_AES: - PolarSSL.aes_free(_encryptCtx); - break; - case CIPHER_BF: - PolarSSL.blowfish_free(_encryptCtx); - break; - case CIPHER_RC4: - PolarSSL.arc4_free(_encryptCtx); - break; - } + case CIPHER_AES: + PolarSSL.aes_free(_encryptCtx); + break; + case CIPHER_BF: + PolarSSL.blowfish_free(_encryptCtx); + break; + case CIPHER_RC4: + PolarSSL.arc4_free(_encryptCtx); + break; } + Marshal.FreeHGlobal(_encryptCtx); + _encryptCtx = IntPtr.Zero; } - if (_decryptCtx != null) + if (_decryptCtx != IntPtr.Zero) { - lock (_decryptCtx) + switch (_cipher) { - switch (_cipher) - { - case CIPHER_AES: - PolarSSL.aes_free(_decryptCtx); - break; - case CIPHER_BF: - PolarSSL.blowfish_free(_decryptCtx); - break; - case CIPHER_RC4: - PolarSSL.arc4_free(_decryptCtx); - break; - } + case CIPHER_AES: + PolarSSL.aes_free(_decryptCtx); + break; + case CIPHER_BF: + PolarSSL.blowfish_free(_decryptCtx); + break; + case CIPHER_RC4: + PolarSSL.arc4_free(_decryptCtx); + break; } + Marshal.FreeHGlobal(_decryptCtx); + _decryptCtx = IntPtr.Zero; } } } diff --git a/test/UnitTest.cs b/test/UnitTest.cs index 46f9b662..166620f5 100755 --- a/test/UnitTest.cs +++ b/test/UnitTest.cs @@ -27,7 +27,6 @@ namespace test { // run it once before the multi-threading test to initialize global tables RunSingleEncryptionThread(); - List threads = new List(); for (int i = 0; i < 10; i++) { @@ -54,11 +53,8 @@ namespace test var random = new Random(); IEncryptor encryptor; IEncryptor decryptor; - lock (locker) - { - encryptor = new PolarSSLEncryptor("aes-256-cfb", "barfoo!"); - decryptor = new PolarSSLEncryptor("aes-256-cfb", "barfoo!"); - } + encryptor = new PolarSSLEncryptor("aes-256-cfb", "barfoo!"); + decryptor = new PolarSSLEncryptor("aes-256-cfb", "barfoo!"); byte[] plain = new byte[16384]; byte[] cipher = new byte[plain.Length + 16]; byte[] plain2 = new byte[plain.Length + 16]; @@ -70,22 +66,31 @@ namespace test encryptor.Encrypt(plain, plain.Length, cipher, out outLen); 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); 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); decryptor.Decrypt(cipher, outLen, plain2, out outLen2); + Assert.AreEqual(12333, outLen2); + for (int j = 0; j < outLen2; j++) + { + Assert.AreEqual(plain[j], plain2[j]); + } //} - Assert.AreEqual(12333, outLen2); - for (int j = 0; j < plain.Length; j++) - { - Assert.AreEqual(plain[j], plain2[j]); - } } } catch { encryptionFailed = true; + throw; } } } diff --git a/test/test.csproj b/test/test.csproj index 235846d0..d7420145 100755 --- a/test/test.csproj +++ b/test/test.csproj @@ -26,6 +26,9 @@ bin\x86\Release\ x86 + + +