Browse Source

move polarssl context to unmanaged heap; fix #32

tags/2.3
clowwindy 10 years ago
parent
commit
aa8e6558d5
4 changed files with 160 additions and 163 deletions
  1. +13
    -13
      shadowsocks-csharp/Encrypt/PolarSSL.cs
  2. +128
    -139
      shadowsocks-csharp/Encrypt/PolarSSLEncryptor.cs
  3. +16
    -11
      test/UnitTest.cs
  4. +3
    -0
      test/test.csproj

+ 13
- 13
shadowsocks-csharp/Encrypt/PolarSSL.cs View File

@@ -7,38 +7,38 @@ namespace Shadowsocks.Encrypt
{ {
public class PolarSSL public class PolarSSL
{ {
const string DLLNAME = "libpolarssl";
const string DLLNAME = "polarssl";
public const int AES_CTX_SIZE = 8 + 4 * 68; public const int AES_CTX_SIZE = 8 + 4 * 68;
public const int AES_ENCRYPT = 1; public const int AES_ENCRYPT = 1;
public const int AES_DECRYPT = 0; public const int AES_DECRYPT = 0;
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] [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)] [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)] [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)] [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; public const int ARC4_CTX_SIZE = 264;
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] [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)] [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)] [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)] [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; public const int BLOWFISH_CTX_SIZE = 4168;
@@ -46,16 +46,16 @@ namespace Shadowsocks.Encrypt
public const int BLOWFISH_DECRYPT = 0; public const int BLOWFISH_DECRYPT = 0;
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] [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)] [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)] [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)] [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);
} }
} }

+ 128
- 139
shadowsocks-csharp/Encrypt/PolarSSLEncryptor.cs View File

@@ -27,12 +27,12 @@ namespace Shadowsocks.Encrypt
private int _cipher; private int _cipher;
private int[] _cipherInfo; private int[] _cipherInfo;
private byte[] _key; private byte[] _key;
private byte[] _encryptCtx;
private byte[] _decryptCtx;
private IntPtr _encryptCtx = IntPtr.Zero;
private IntPtr _decryptCtx = IntPtr.Zero;
private byte[] _encryptIV; private byte[] _encryptIV;
private byte[] _decryptIV; private byte[] _decryptIV;
private int _encryptIVOffset;
private int _decryptIVOffset;
private byte[] _encryptIVOffset;
private byte[] _decryptIVOffset;
private string _method; private string _method;
private int keyLen; private int keyLen;
private int ivLen; 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 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) public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength)
{ {
if (_encryptCtx == null)
if (_encryptCtx == IntPtr.Zero)
{ {
randBytes(outbuf, ivLen); randBytes(outbuf, ivLen);
InitCipher(ref _encryptCtx, outbuf, true); InitCipher(ref _encryptCtx, outbuf, true);
@@ -173,34 +175,6 @@ namespace Shadowsocks.Encrypt
lock (tempbuf) lock (tempbuf)
{ {
// C# could be multi-threaded // 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) if (_disposed)
{ {
throw new ObjectDisposedException(this.ToString()); throw new ObjectDisposedException(this.ToString());
@@ -208,22 +182,45 @@ namespace Shadowsocks.Encrypt
switch (_cipher) switch (_cipher)
{ {
case CIPHER_AES: 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; break;
case CIPHER_BF: 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; break;
case CIPHER_RC4: case CIPHER_RC4:
PolarSSL.arc4_crypt(_encryptCtx, length, buf, outbuf);
PolarSSL.arc4_crypt(_encryptCtx, length, buf, tempbuf);
break; 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) 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); InitCipher(ref _decryptCtx, buf, false);
outlength = length - ivLen; outlength = length - ivLen;
@@ -231,32 +228,6 @@ namespace Shadowsocks.Encrypt
{ {
// C# could be multi-threaded // C# could be multi-threaded
Buffer.BlockCopy(buf, ivLen, tempbuf, 0, length - ivLen); 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) if (_disposed)
{ {
throw new ObjectDisposedException(this.ToString()); throw new ObjectDisposedException(this.ToString());
@@ -264,17 +235,37 @@ namespace Shadowsocks.Encrypt
switch (_cipher) switch (_cipher)
{ {
case CIPHER_AES: 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; break;
case CIPHER_BF: 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; break;
case CIPHER_RC4: case CIPHER_RC4:
PolarSSL.arc4_crypt(_decryptCtx, length, buf, outbuf);
PolarSSL.arc4_crypt(_decryptCtx, length - ivLen, tempbuf, outbuf);
break; 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 #region IDisposable
@@ -304,41 +295,39 @@ namespace Shadowsocks.Encrypt
if (disposing) 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;
} }
} }
} }


+ 16
- 11
test/UnitTest.cs View File

@@ -27,7 +27,6 @@ namespace test
{ {
// run it once before the multi-threading test to initialize global tables // run it once before the multi-threading test to initialize global tables
RunSingleEncryptionThread(); RunSingleEncryptionThread();
List<Thread> threads = new List<Thread>(); List<Thread> threads = new List<Thread>();
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
{ {
@@ -54,11 +53,8 @@ namespace test
var random = new Random(); var random = new Random();
IEncryptor encryptor; IEncryptor encryptor;
IEncryptor decryptor; 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[] plain = new byte[16384];
byte[] cipher = new byte[plain.Length + 16]; byte[] cipher = new byte[plain.Length + 16];
byte[] plain2 = 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); encryptor.Encrypt(plain, plain.Length, cipher, out outLen);
decryptor.Decrypt(cipher, outLen, plain2, out outLen2); decryptor.Decrypt(cipher, outLen, plain2, out outLen2);
Assert.AreEqual(plain.Length, 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);
decryptor.Decrypt(cipher, outLen, plain2, out outLen2); decryptor.Decrypt(cipher, outLen, plain2, out outLen2);
Assert.AreEqual(1000, 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);
decryptor.Decrypt(cipher, outLen, plain2, out outLen2); 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 catch
{ {
encryptionFailed = true; encryptionFailed = true;
throw;
} }
} }
} }


+ 3
- 0
test/test.csproj View File

@@ -26,6 +26,9 @@
<OutputPath>bin\x86\Release\</OutputPath> <OutputPath>bin\x86\Release\</OutputPath>
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />
</ItemGroup> </ItemGroup>


Loading…
Cancel
Save