@@ -0,0 +1,111 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace Shadowsocks.Encryption.Stream | |||||
{ | |||||
public class StreamNativeEncryptor : StreamEncryptor | |||||
{ | |||||
bool md5; | |||||
byte[] realkey; | |||||
byte[] sbox; | |||||
public StreamNativeEncryptor(string method, string password) : base(method, password) | |||||
{ | |||||
md5 = method.ToLowerInvariant().IndexOf("md5") >= 0; | |||||
} | |||||
public override void Dispose() | |||||
{ | |||||
return; | |||||
} | |||||
protected override void initCipher(byte[] iv, bool isEncrypt) | |||||
{ | |||||
base.initCipher(iv, isEncrypt); | |||||
if (md5) | |||||
{ | |||||
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 = EncryptInitalize(realkey); | |||||
} | |||||
protected override void cipherUpdate(bool isEncrypt, int length, byte[] buf, byte[] outbuf) | |||||
{ | |||||
var ctx = isEncrypt ? enc_ctx : dec_ctx; | |||||
byte[] t = new byte[length]; | |||||
Array.Copy(buf, t, length); | |||||
EncryptOutput(ctx, sbox, t, length); | |||||
Array.Copy(t, outbuf, length); | |||||
} | |||||
protected override Dictionary<string, EncryptorInfo> getCiphers() | |||||
{ | |||||
return new Dictionary<string, EncryptorInfo>() | |||||
{ | |||||
{ "rc4-md5", new EncryptorInfo("rc4", 16, 16, 1) } | |||||
}; | |||||
} | |||||
class Context | |||||
{ | |||||
public int index1 = 0; | |||||
public int index2 = 0; | |||||
} | |||||
private Context enc_ctx = new Context(); | |||||
private Context dec_ctx = new Context(); | |||||
private byte[] EncryptInitalize(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 EncryptOutput(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; | |||||
} | |||||
} | |||||
} |
@@ -129,6 +129,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" /> | ||||
@@ -229,5 +229,53 @@ namespace Shadowsocks.Test | |||||
throw; | throw; | ||||
} | } | ||||
} | } | ||||
[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(); | |||||
} | |||||
foreach (Thread t in threads) | |||||
{ | |||||
t.Join(); | |||||
} | |||||
RNG.Close(); | |||||
Assert.IsFalse(encryptionFailed); | |||||
} | |||||
private void RunSingleNativeEncryptionThread() | |||||
{ | |||||
try | |||||
{ | |||||
for (int i = 0; i < 100; i++) | |||||
{ | |||||
var random = new Random(); | |||||
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; | |||||
} | |||||
} | |||||
} | } | ||||
} | |||||
} |