@@ -0,0 +1,51 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Shadowsocks.Encryption.AEAD | |||
{ | |||
public class AEADNativeEncryptor : AEADEncryptor | |||
{ | |||
public AEADNativeEncryptor(string method, string password) | |||
: base(method, password) | |||
{ | |||
} | |||
public override void cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen) | |||
{ | |||
Array.Copy(ciphertext, plaintext, 0); | |||
plen = clen; | |||
} | |||
public override void cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen) | |||
{ | |||
Array.Copy(plaintext, ciphertext, 0); | |||
clen = plen; | |||
} | |||
public override void Dispose() | |||
{ | |||
return; | |||
} | |||
private static Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo>() | |||
{ | |||
{"plain-aeadfmt",new EncryptorInfo("PLAIN",0,0,0,0,0) } | |||
}; | |||
protected override Dictionary<string, EncryptorInfo> getCiphers() | |||
{ | |||
return _ciphers; | |||
} | |||
public static IEnumerable<string> SupportedCiphers() | |||
{ | |||
return _ciphers.Keys; | |||
} | |||
} | |||
} |
@@ -28,6 +28,12 @@ namespace Shadowsocks.Encryption | |||
} | |||
// XXX: sequence matters, OpenSSL > Sodium > MbedTLS | |||
foreach (string method in StreamNativeEncryptor.SupportedCiphers()) | |||
{ | |||
if (!_registeredEncryptors.ContainsKey(method)) | |||
_registeredEncryptors.Add(method, typeof(StreamNativeEncryptor)); | |||
} | |||
foreach (string method in StreamOpenSSLEncryptor.SupportedCiphers()) | |||
{ | |||
if (!_registeredEncryptors.ContainsKey(method)) | |||
@@ -46,6 +52,11 @@ namespace Shadowsocks.Encryption | |||
_registeredEncryptors.Add(method, typeof(StreamMbedTLSEncryptor)); | |||
} | |||
foreach (string method in AEADNativeEncryptor.SupportedCiphers()) | |||
{ | |||
if (!_registeredEncryptors.ContainsKey(method)) | |||
_registeredEncryptors.Add(method, typeof(AEADNativeEncryptor)); | |||
} | |||
foreach (string method in AEADOpenSSLEncryptor.SupportedCiphers()) | |||
{ | |||
@@ -0,0 +1,213 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Shadowsocks.Encryption.Stream | |||
{ | |||
public class StreamNativeEncryptor : StreamEncryptor | |||
{ | |||
const int Plain = 0; | |||
const int Table = 1; | |||
const int Rc4 = 2; | |||
const int Rc4Md5 = 3; | |||
string _password; | |||
byte[] realkey; | |||
byte[] sbox; | |||
public StreamNativeEncryptor(string method, string password) : base(method, password) | |||
{ | |||
_password = password; | |||
} | |||
public override void Dispose() | |||
{ | |||
return; | |||
} | |||
protected override void initCipher(byte[] iv, bool isEncrypt) | |||
{ | |||
base.initCipher(iv, isEncrypt); | |||
if (_cipher >= Rc4) | |||
{ | |||
if (_cipher == Rc4Md5) | |||
{ | |||
byte[] temp = new byte[keyLen + ivLen]; | |||
Array.Copy(_key, 0, temp, 0, keyLen); | |||
Array.Copy(iv, 0, temp, keyLen, ivLen); | |||
realkey = MbedTLS.MD5(temp); | |||
} | |||
else | |||
{ | |||
realkey = _key; | |||
} | |||
sbox = SBox(realkey); | |||
} | |||
else if (_cipher == Table) | |||
{ | |||
ulong a = BitConverter.ToUInt64(MbedTLS.MD5(Encoding.UTF8.GetBytes(_password)), 0); | |||
for (int i = 0; i < 256; i++) | |||
{ | |||
_encryptTable[i] = (byte)i; | |||
} | |||
for (int i = 1; i < 1024; i++) | |||
{ | |||
_encryptTable = MergeSort(_encryptTable, a, i); | |||
} | |||
for (int i = 0; i < 256; i++) | |||
{ | |||
_decryptTable[_encryptTable[i]] = (byte)i; | |||
} | |||
} | |||
} | |||
protected override void cipherUpdate(bool isEncrypt, int length, byte[] buf, byte[] outbuf) | |||
{ | |||
if (_cipher == Table) | |||
{ | |||
var table = isEncrypt ? _encryptTable : _decryptTable; | |||
for (int i = 0; i < length; i++) | |||
{ | |||
outbuf[i] = table[buf[i]]; | |||
} | |||
} | |||
else if (_cipher == Plain) | |||
{ | |||
Array.Copy(buf, outbuf, length); | |||
} | |||
else | |||
{ | |||
var ctx = isEncrypt ? enc_ctx : dec_ctx; | |||
byte[] t = new byte[length]; | |||
Array.Copy(buf, t, length); | |||
RC4(ctx, sbox, t, length); | |||
Array.Copy(t, outbuf, length); | |||
} | |||
} | |||
private static readonly Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo> | |||
{ | |||
{"plain", new EncryptorInfo("PLAIN", 0, 0, Plain) }, | |||
{"table", new EncryptorInfo("TABLE", 0, 0, Table) }, | |||
{ "rc4", new EncryptorInfo("RC4", 16, 0, Rc4) }, // original RC4 doesn't use IV | |||
{ "rc4-md5", new EncryptorInfo("RC4", 16, 16, Rc4Md5) }, | |||
}; | |||
public static IEnumerable<string> SupportedCiphers() | |||
{ | |||
return _ciphers.Keys; | |||
} | |||
protected override Dictionary<string, EncryptorInfo> getCiphers() | |||
{ | |||
return _ciphers; | |||
} | |||
#region Table | |||
private byte[] _encryptTable = new byte[256]; | |||
private byte[] _decryptTable = new byte[256]; | |||
private static long Compare(byte x, byte y, ulong a, int i) | |||
{ | |||
return (long)(a % (ulong)(x + i)) - (long)(a % (ulong)(y + i)); | |||
} | |||
private byte[] MergeSort(byte[] array, ulong a, int j) | |||
{ | |||
if (array.Length == 1) | |||
{ | |||
return array; | |||
} | |||
int middle = array.Length / 2; | |||
byte[] left = new byte[middle]; | |||
for (int i = 0; i < middle; i++) | |||
{ | |||
left[i] = array[i]; | |||
} | |||
byte[] right = new byte[array.Length - middle]; | |||
for (int i = 0; i < array.Length - middle; i++) | |||
{ | |||
right[i] = array[i + middle]; | |||
} | |||
left = MergeSort(left, a, j); | |||
right = MergeSort(right, a, j); | |||
int leftptr = 0; | |||
int rightptr = 0; | |||
byte[] sorted = new byte[array.Length]; | |||
for (int k = 0; k < array.Length; k++) | |||
{ | |||
if (rightptr == right.Length || ((leftptr < left.Length) && (Compare(left[leftptr], right[rightptr], a, j) <= 0))) | |||
{ | |||
sorted[k] = left[leftptr]; | |||
leftptr++; | |||
} | |||
else if (leftptr == left.Length || ((rightptr < right.Length) && (Compare(right[rightptr], left[leftptr], a, j)) <= 0)) | |||
{ | |||
sorted[k] = right[rightptr]; | |||
rightptr++; | |||
} | |||
} | |||
return sorted; | |||
} | |||
#endregion | |||
#region RC4 | |||
class Context | |||
{ | |||
public int index1 = 0; | |||
public int index2 = 0; | |||
} | |||
private Context enc_ctx = new Context(); | |||
private Context dec_ctx = new Context(); | |||
private byte[] SBox(byte[] key) | |||
{ | |||
byte[] 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 void RC4(Context ctx, byte[] s, byte[] data, int 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); | |||
data[n] = (byte)(b ^ s[(s[ctx.index1] + s[ctx.index2]) & 255]); | |||
} | |||
} | |||
private static void Swap(byte[] s, int i, int j) | |||
{ | |||
byte c = s[i]; | |||
s[i] = s[j]; | |||
s[j] = c; | |||
} | |||
#endregion | |||
} | |||
} |
@@ -31,6 +31,18 @@ namespace Shadowsocks.View | |||
"salsa20", | |||
"chacha20", | |||
"bf-cfb", | |||
"rc4", | |||
"plain", | |||
"table", | |||
}; | |||
private static string[] inuseMethod = new string[] | |||
{ | |||
"aes-256-gcm", | |||
"aes-192-gcm", | |||
"aes-128-gcm", | |||
"chacha20-ietf-poly1305", | |||
"xchacha20-ietf-poly1305", | |||
"chacha20-ietf", | |||
"aes-256-cfb", | |||
"aes-192-cfb", | |||
@@ -42,14 +54,7 @@ namespace Shadowsocks.View | |||
"camellia-192-cfb", | |||
"camellia-128-cfb", | |||
}; | |||
private static string[] inuseMethod = new string[] | |||
{ | |||
"aes-256-gcm", | |||
"aes-192-gcm", | |||
"aes-128-gcm", | |||
"chacha20-ietf-poly1305", | |||
"xchacha20-ietf-poly1305", | |||
}; | |||
public static EncryptionMethod[] AllMethods | |||
{ | |||
get | |||
@@ -120,6 +120,7 @@ | |||
<Compile Include="Encryption\AEAD\AEADBouncyCastleEncryptor.cs" /> | |||
<Compile Include="Encryption\AEAD\AEADEncryptor.cs" /> | |||
<Compile Include="Encryption\AEAD\AEADMbedTLSEncryptor.cs" /> | |||
<Compile Include="Encryption\AEAD\AEADNativeEncryptor.cs" /> | |||
<Compile Include="Encryption\AEAD\AEADOpenSSLEncryptor.cs" /> | |||
<Compile Include="Encryption\AEAD\AEADSodiumEncryptor.cs" /> | |||
<Compile Include="Encryption\CircularBuffer\ByteCircularBuffer.cs" /> | |||
@@ -133,6 +134,7 @@ | |||
<Compile Include="Encryption\Sodium.cs" /> | |||
<Compile Include="Encryption\Stream\StreamEncryptor.cs" /> | |||
<Compile Include="Encryption\Stream\StreamMbedTLSEncryptor.cs" /> | |||
<Compile Include="Encryption\Stream\StreamNativeEncryptor.cs" /> | |||
<Compile Include="Encryption\Stream\StreamOpenSSLEncryptor.cs" /> | |||
<Compile Include="Encryption\Stream\StreamSodiumEncryptor.cs" /> | |||
<Compile Include="Model\HotKeyConfig.cs" /> | |||
@@ -239,7 +239,27 @@ namespace Shadowsocks.Test | |||
List<Thread> threads = new List<Thread>(); | |||
for (int i = 0; i < 10; i++) | |||
{ | |||
Thread t = new Thread(new ThreadStart(RunSingleBouncyCastleAEADEncryptionThread)); | |||
Thread t = new Thread(new ThreadStart(RunSingleBouncyCastleAEADEncryptionThread));threads.Add(t); | |||
t.Start(); | |||
} | |||
foreach (Thread t in threads) | |||
{ | |||
t.Join(); | |||
} | |||
RNG.Close(); | |||
Assert.IsFalse(encryptionFailed); | |||
} | |||
[TestMethod] | |||
public void TestNativeEncryption() | |||
{ | |||
encryptionFailed = false; | |||
// run it once before the multi-threading test to initialize global tables | |||
RunSingleNativeEncryptionThread(); | |||
List<Thread> threads = new List<Thread>(); | |||
for (int i = 0; i < 10; i++) | |||
{ | |||
Thread t = new Thread(new ThreadStart(RunSingleNativeEncryptionThread)); | |||
threads.Add(t); | |||
t.Start(); | |||
} | |||
@@ -251,6 +271,32 @@ namespace Shadowsocks.Test | |||
Assert.IsFalse(encryptionFailed); | |||
} | |||
private void RunSingleNativeEncryptionThread() | |||
{ | |||
try | |||
{ | |||
for (int i = 0; i < 100; i++) | |||
{ | |||
IEncryptor encryptorO, encryptorN, encryptorN2; | |||
IEncryptor decryptorO, decryptorN, decryptorN2; | |||
encryptorO = new StreamOpenSSLEncryptor("rc4-md5", "barfoo!"); | |||
decryptorO = new StreamOpenSSLEncryptor("rc4-md5", "barfoo!"); | |||
encryptorN = new StreamNativeEncryptor("rc4-md5", "barfoo!"); | |||
encryptorN2 = new StreamNativeEncryptor("rc4-md5", "barfoo!"); | |||
decryptorN = new StreamNativeEncryptor("rc4-md5", "barfoo!"); | |||
decryptorN2 = new StreamNativeEncryptor("rc4-md5", "barfoo!"); | |||
RunEncryptionRound(encryptorN, decryptorN); | |||
RunEncryptionRound(encryptorO, decryptorN2); | |||
RunEncryptionRound(encryptorN2, decryptorO); | |||
} | |||
} | |||
catch | |||
{ | |||
encryptionFailed = true; | |||
throw; | |||
} | |||
} | |||
private void RunSingleBouncyCastleAEADEncryptionThread() | |||
{ | |||
try | |||
@@ -362,4 +408,4 @@ namespace Shadowsocks.Test | |||
} | |||
} | |||
} | |||
} | |||
} |