Files
2015-01-01 22:05:16 +08:00

186 lines
5.8 KiB
C#
Executable File

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
namespace Shadowsocks.Encryption
{
public class PolarSSLEncryptor
: IVEncryptor, IDisposable
{
const int CIPHER_AES = 1;
const int CIPHER_RC4 = 2;
private IntPtr _encryptCtx = IntPtr.Zero;
private IntPtr _decryptCtx = IntPtr.Zero;
public PolarSSLEncryptor(string method, string password)
: base(method, password)
{
InitKey(method, password);
}
private static Dictionary<string, int[]> _ciphers = new Dictionary<string, int[]> {
{"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}},
};
public static List<string> SupportedCiphers()
{
return new List<string>(_ciphers.Keys);
}
protected override Dictionary<string, int[]> getCiphers()
{
return _ciphers;
}
protected override void initCipher(byte[] iv, bool isCipher)
{
base.initCipher(iv, isCipher);
IntPtr ctx;
ctx = Marshal.AllocHGlobal(_cipherInfo[3]);
if (isCipher)
{
_encryptCtx = ctx;
}
else
{
_decryptCtx = ctx;
}
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);
}
else if (_cipher == CIPHER_RC4)
{
PolarSSL.arc4_init(ctx);
// PolarSSL RC4 takes key length by byte
PolarSSL.arc4_setup(ctx, realkey, keyLen);
}
}
protected override void cipherUpdate(bool isCipher, int length, byte[] buf, byte[] outbuf)
{
// C# could be multi-threaded
if (_disposed)
{
throw new ObjectDisposedException(this.ToString());
}
byte[] iv;
int ivOffset;
IntPtr ctx;
if (isCipher)
{
iv = _encryptIV;
ivOffset = _encryptIVOffset;
ctx = _encryptCtx;
}
else
{
iv = _decryptIV;
ivOffset = _decryptIVOffset;
ctx = _decryptCtx;
}
switch (_cipher)
{
case CIPHER_AES:
PolarSSL.aes_crypt_cfb128(ctx, isCipher ? PolarSSL.AES_ENCRYPT : PolarSSL.AES_DECRYPT, length, ref ivOffset, iv, buf, outbuf);
if (isCipher)
{
_encryptIVOffset = ivOffset;
}
else
{
_decryptIVOffset = ivOffset;
}
break;
case CIPHER_RC4:
PolarSSL.arc4_crypt(ctx, length, buf, outbuf);
break;
}
}
#region IDisposable
private bool _disposed;
public override void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~PolarSSLEncryptor()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
lock (this)
{
if (_disposed)
{
return;
}
_disposed = true;
}
if (disposing)
{
if (_encryptCtx != IntPtr.Zero)
{
switch (_cipher)
{
case CIPHER_AES:
PolarSSL.aes_free(_encryptCtx);
break;
case CIPHER_RC4:
PolarSSL.arc4_free(_encryptCtx);
break;
}
Marshal.FreeHGlobal(_encryptCtx);
_encryptCtx = IntPtr.Zero;
}
if (_decryptCtx != IntPtr.Zero)
{
switch (_cipher)
{
case CIPHER_AES:
PolarSSL.aes_free(_decryptCtx);
break;
case CIPHER_RC4:
PolarSSL.arc4_free(_decryptCtx);
break;
}
Marshal.FreeHGlobal(_decryptCtx);
_decryptCtx = IntPtr.Zero;
}
}
}
#endregion
}
}