@@ -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 | // 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()) | foreach (string method in StreamOpenSSLEncryptor.SupportedCiphers()) | ||||
{ | { | ||||
if (!_registeredEncryptors.ContainsKey(method)) | if (!_registeredEncryptors.ContainsKey(method)) | ||||
@@ -46,6 +52,11 @@ namespace Shadowsocks.Encryption | |||||
_registeredEncryptors.Add(method, typeof(StreamMbedTLSEncryptor)); | _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()) | 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", | "salsa20", | ||||
"chacha20", | "chacha20", | ||||
"bf-cfb", | "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", | "chacha20-ietf", | ||||
"aes-256-cfb", | "aes-256-cfb", | ||||
"aes-192-cfb", | "aes-192-cfb", | ||||
@@ -42,14 +54,7 @@ namespace Shadowsocks.View | |||||
"camellia-192-cfb", | "camellia-192-cfb", | ||||
"camellia-128-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 | public static EncryptionMethod[] AllMethods | ||||
{ | { | ||||
get | get | ||||
@@ -120,6 +120,7 @@ | |||||
<Compile Include="Encryption\AEAD\AEADBouncyCastleEncryptor.cs" /> | <Compile Include="Encryption\AEAD\AEADBouncyCastleEncryptor.cs" /> | ||||
<Compile Include="Encryption\AEAD\AEADEncryptor.cs" /> | <Compile Include="Encryption\AEAD\AEADEncryptor.cs" /> | ||||
<Compile Include="Encryption\AEAD\AEADMbedTLSEncryptor.cs" /> | <Compile Include="Encryption\AEAD\AEADMbedTLSEncryptor.cs" /> | ||||
<Compile Include="Encryption\AEAD\AEADNativeEncryptor.cs" /> | |||||
<Compile Include="Encryption\AEAD\AEADOpenSSLEncryptor.cs" /> | <Compile Include="Encryption\AEAD\AEADOpenSSLEncryptor.cs" /> | ||||
<Compile Include="Encryption\AEAD\AEADSodiumEncryptor.cs" /> | <Compile Include="Encryption\AEAD\AEADSodiumEncryptor.cs" /> | ||||
<Compile Include="Encryption\CircularBuffer\ByteCircularBuffer.cs" /> | <Compile Include="Encryption\CircularBuffer\ByteCircularBuffer.cs" /> | ||||
@@ -133,6 +134,7 @@ | |||||
<Compile Include="Encryption\Sodium.cs" /> | <Compile Include="Encryption\Sodium.cs" /> | ||||
<Compile Include="Encryption\Stream\StreamEncryptor.cs" /> | <Compile Include="Encryption\Stream\StreamEncryptor.cs" /> | ||||
<Compile Include="Encryption\Stream\StreamMbedTLSEncryptor.cs" /> | <Compile Include="Encryption\Stream\StreamMbedTLSEncryptor.cs" /> | ||||
<Compile Include="Encryption\Stream\StreamNativeEncryptor.cs" /> | |||||
<Compile Include="Encryption\Stream\StreamOpenSSLEncryptor.cs" /> | <Compile Include="Encryption\Stream\StreamOpenSSLEncryptor.cs" /> | ||||
<Compile Include="Encryption\Stream\StreamSodiumEncryptor.cs" /> | <Compile Include="Encryption\Stream\StreamSodiumEncryptor.cs" /> | ||||
<Compile Include="Model\HotKeyConfig.cs" /> | <Compile Include="Model\HotKeyConfig.cs" /> | ||||
@@ -239,7 +239,27 @@ namespace Shadowsocks.Test | |||||
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++) | ||||
{ | { | ||||
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); | threads.Add(t); | ||||
t.Start(); | t.Start(); | ||||
} | } | ||||
@@ -251,6 +271,32 @@ namespace Shadowsocks.Test | |||||
Assert.IsFalse(encryptionFailed); | 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() | private void RunSingleBouncyCastleAEADEncryptionThread() | ||||
{ | { | ||||
try | try | ||||
@@ -362,4 +408,4 @@ namespace Shadowsocks.Test | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} | |||||
} |