use OpenSSL directly

This commit is contained in:
clowwindy
2014-11-02 17:55:51 +08:00
parent 0cf6b422c0
commit 421554f8ec
14 changed files with 110 additions and 5386 deletions
@@ -10,11 +10,6 @@ namespace shadowsocks_csharp.Encrypt
return new TableEncryptor(method, password);
}
if (method.ToLowerInvariant() == "rc4")
{
return new Rc4Encryptor(method, password);
}
return new OpensslEncryptor(method, password);
}
}
+44
View File
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace shadowsocks_csharp.Encrypt
{
public class OpenSSL
{
const string DLLNAME = "libeay32";
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public extern static void OpenSSL_add_all_ciphers();
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public extern static IntPtr EVP_md5();
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public extern static int EVP_BytesToKey(IntPtr type, IntPtr md, IntPtr salt, byte[] data, int datal, int count, byte[] key, byte[] iv);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public extern static int RAND_bytes(byte[] buf, int num);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public extern static IntPtr EVP_get_cipherbyname(byte[] name);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public extern static IntPtr EVP_CIPHER_CTX_new();
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public extern static int EVP_CipherInit_ex(IntPtr ctx, IntPtr type, IntPtr impl, byte[] key, byte[] iv, int enc);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public extern static int EVP_CIPHER_CTX_cleanup(IntPtr a);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public extern static int EVP_CIPHER_CTX_free(IntPtr a);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public extern static int EVP_CipherUpdate(IntPtr ctx, byte[] outb, out int outl, byte[] inb, int inl);
}
}
+63 -59
View File
@@ -1,6 +1,4 @@
using OpenSSL.Core;
using OpenSSL.Crypto;
using System;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
@@ -9,7 +7,14 @@ namespace shadowsocks_csharp.Encrypt
{
public class OpensslEncryptor
: EncryptorBase, IDisposable
{
{
static Dictionary<string, int[]> ciphers = new Dictionary<string, int[]> {
{"aes-128-cfb", new int[]{16, 16}},
{"aes-192-cfb", new int[]{24, 16}},
{"aes-256-cfb", new int[]{32, 16}},
{"bf-cfb", new int[]{16, 8}},
{"rc4", new int[]{16, 0}},
};
public OpensslEncryptor(string method, string password)
: base(method, password)
{
@@ -20,23 +25,22 @@ namespace shadowsocks_csharp.Encrypt
{
if (_encryptCtx == IntPtr.Zero)
{
int ivLen = _cipher.IVLength;
byte[] iv = new byte[ivLen];
Native.RAND_bytes(iv, iv.Length);
OpenSSL.RAND_bytes(iv, iv.Length);
InitCipher(ref _encryptCtx, iv, true);
int outLen = length + _cipher.BlockSize;
byte[] cipherText = new byte[outLen];
Native.EVP_CipherUpdate(_encryptCtx, cipherText, out outLen, buf, length);
int outLen = length + ivLen;
byte[] cipherText = new byte[outLen];
OpenSSL.EVP_CipherUpdate(_encryptCtx, cipherText, out outLen, buf, length);
byte[] result = new byte[outLen + ivLen];
Buffer.BlockCopy(iv, 0, result, 0, ivLen);
Buffer.BlockCopy(cipherText, 0, result, ivLen, outLen);
return result;
}
else
{
int outLen = length + _cipher.BlockSize;
byte[] cipherText = new byte[outLen];
Native.EVP_CipherUpdate(_encryptCtx, cipherText, out outLen, buf, length);
{
int outLen = length + ivLen;
byte[] cipherText = new byte[outLen];
OpenSSL.EVP_CipherUpdate(_encryptCtx, cipherText, out outLen, buf, length);
byte[] result = new byte[outLen];
Buffer.BlockCopy(cipherText, 0, result, 0, outLen);
return result;
@@ -47,25 +51,24 @@ namespace shadowsocks_csharp.Encrypt
{
if (_decryptCtx == IntPtr.Zero)
{
int ivLen = _cipher.IVLength;
byte[] iv = new byte[ivLen];
Buffer.BlockCopy(buf, 0, iv, 0, ivLen);
InitCipher(ref _decryptCtx, iv, false);
int outLen = length + _cipher.BlockSize;
InitCipher(ref _decryptCtx, iv, false);
int outLen = length + ivLen;
outLen -= ivLen;
byte[] cipherText = new byte[outLen];
byte[] subset = new byte[length - ivLen];
Buffer.BlockCopy(buf, ivLen, subset, 0, length - ivLen);
Native.EVP_CipherUpdate(_decryptCtx, cipherText, out outLen, subset, length - ivLen);
Buffer.BlockCopy(buf, ivLen, subset, 0, length - ivLen);
OpenSSL.EVP_CipherUpdate(_decryptCtx, cipherText, out outLen, subset, length - ivLen);
byte[] result = new byte[outLen];
Buffer.BlockCopy(cipherText, 0, result, 0, outLen);
return result;
}
else
{
int outLen = length + _cipher.BlockSize;
byte[] cipherText = new byte[outLen];
Native.EVP_CipherUpdate(_decryptCtx, cipherText, out outLen, buf, length);
{
int outLen = length + ivLen;
byte[] cipherText = new byte[outLen];
OpenSSL.EVP_CipherUpdate(_decryptCtx, cipherText, out outLen, buf, length);
byte[] result = new byte[outLen];
Buffer.BlockCopy(cipherText, 0, result, 0, outLen);
return result;
@@ -73,43 +76,44 @@ namespace shadowsocks_csharp.Encrypt
}
private static readonly Dictionary<string, byte[]> CachedKeys = new Dictionary<string, byte[]>();
private static readonly Dictionary<string, Cipher> CachedCiphers = new Dictionary<string, Cipher>();
private byte[] _key;
private Cipher _cipher;
private IntPtr _encryptCtx;
private IntPtr _decryptCtx;
private IntPtr _encryptCtx;
private IntPtr _decryptCtx;
private IntPtr _cipher;
private int keyLen;
private int ivLen;
private void InitKey(string method, string password)
{
string k = method + ":" + password;
if (CachedKeys.ContainsKey(k))
{
_key = CachedKeys[k];
_cipher = CachedCiphers[k];
return;
private void InitKey(string method, string password)
{
OpenSSL.OpenSSL_add_all_ciphers();
method = method.ToLower();
string k = method + ":" + password;
_cipher = OpenSSL.EVP_get_cipherbyname(System.Text.Encoding.UTF8.GetBytes(method));
if (_cipher == null)
{
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];
OpenSSL.EVP_BytesToKey(_cipher, OpenSSL.EVP_md5(), IntPtr.Zero, passbuf, passbuf.Length, 1, _key, iv);
CachedKeys[k] = _key;
}
_cipher = Cipher.CreateByName(method);
if (_cipher == null)
{
throw new NullReferenceException();
}
byte[] passbuf = Encoding.UTF8.GetBytes(password);
_key = new byte[_cipher.KeyLength];
byte[] iv = new byte[_cipher.IVLength];
Native.EVP_BytesToKey(_cipher.Handle, MessageDigest.MD5.Handle, null, passbuf, passbuf.Length, 1, _key, iv);
CachedKeys[k] = _key;
CachedCiphers[k] = _cipher;
}
private void InitCipher(ref IntPtr ctx, byte[] iv, bool isCipher)
{
ctx = Native.OPENSSL_malloc(Marshal.SizeOf(typeof(CipherContext.EVP_CIPHER_CTX)));
int enc = isCipher ? 1 : 0;
Native.EVP_CIPHER_CTX_init(ctx);
Native.ExpectSuccess(Native.EVP_CipherInit_ex(ctx, _cipher.Handle, IntPtr.Zero, null, null, enc));
Native.ExpectSuccess(Native.EVP_CIPHER_CTX_set_key_length(ctx, _key.Length));
Native.ExpectSuccess(Native.EVP_CIPHER_CTX_set_padding(ctx, 1));
Native.ExpectSuccess(Native.EVP_CipherInit_ex(ctx, _cipher.Handle, IntPtr.Zero, _key, iv, enc));
{
ctx = OpenSSL.EVP_CIPHER_CTX_new();
int enc = isCipher ? 1 : 0;
OpenSSL.EVP_CipherInit_ex(ctx, _cipher, IntPtr.Zero, _key, iv, enc);
}
#region IDisposable
@@ -136,15 +140,15 @@ namespace shadowsocks_csharp.Encrypt
}
if (_encryptCtx != IntPtr.Zero)
{
Native.EVP_CIPHER_CTX_cleanup(_encryptCtx);
Native.OPENSSL_free(_encryptCtx);
{
OpenSSL.EVP_CIPHER_CTX_cleanup(_encryptCtx);
OpenSSL.EVP_CIPHER_CTX_free(_encryptCtx);
_encryptCtx = IntPtr.Zero;
}
if (_decryptCtx != IntPtr.Zero)
{
Native.EVP_CIPHER_CTX_cleanup(_decryptCtx);
Native.OPENSSL_free(_decryptCtx);
{
OpenSSL.EVP_CIPHER_CTX_cleanup(_decryptCtx);
OpenSSL.EVP_CIPHER_CTX_free(_decryptCtx);
_decryptCtx = IntPtr.Zero;
}
@@ -1,81 +0,0 @@
namespace shadowsocks_csharp.Encrypt
{
public class Rc4Encryptor
: EncryptorBase
{
public Rc4Encryptor(string method, string password)
: base(method, password)
{
byte[] hash = GetPasswordHash();
_encryptTable = EncryptInitalize(hash);
_decryptTable = EncryptInitalize(hash);
}
public override byte[] Encrypt(byte[] buf, int length)
{
return EncryptOutput(enc_ctx, _encryptTable, buf, length);
}
public override byte[] Decrypt(byte[] buf, int length)
{
return EncryptOutput(dec_ctx, _decryptTable, buf, length);
}
private readonly byte[] _encryptTable = new byte[256];
private readonly byte[] _decryptTable = new byte[256];
private Context enc_ctx = new Context();
private Context dec_ctx = new Context();
private byte[] EncryptOutput(Context ctx, byte[] s, byte[] data, int length)
{
byte[] result = new byte[length];
for (int n = 0; n < length; n++)
{
byte b = data[n];
ctx.Index1 = (ctx.Index1 + 1) & 255;
ctx.Index2 = (ctx.Index2 + s[ctx.Index1]) & 255;
Swap(s, ctx.Index1, ctx.Index2);
result[n] = (byte)(b ^ s[(s[ctx.Index1] + s[ctx.Index2]) & 255]);
}
return result;
}
private byte[] EncryptInitalize(byte[] key)
{
var s = new byte[256];
for (int i = 0; i < 256; i++)
{
s[i] = (byte)i;
}
for (int i = 0, j = 0; i < 256; i++)
{
j = (j + key[i % key.Length] + s[i]) & 255;
Swap(s, i, j);
}
return s;
}
private static void Swap(byte[] s, int i, int j)
{
byte c = s[i];
s[i] = s[j];
s[j] = c;
}
class Context
{
public int Index1;
public int Index2;
}
}
}