From 5d658fc8415112e4e6f6f57f2bdeca75cbe50cc7 Mon Sep 17 00:00:00 2001 From: Student Main Date: Fri, 14 Feb 2020 20:51:15 +0800 Subject: [PATCH] bring native rc4 back, along with rc4-md5 partially revert 421554f8ecda46026aa0a083eee582cfbdbfb787 --- .../Stream/StreamNativeEncryptor.cs | 111 ++++++++++++++++++ shadowsocks-csharp/shadowsocks-csharp.csproj | 1 + test/CryptographyTest.cs | 50 +++++++- 3 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 shadowsocks-csharp/Encryption/Stream/StreamNativeEncryptor.cs diff --git a/shadowsocks-csharp/Encryption/Stream/StreamNativeEncryptor.cs b/shadowsocks-csharp/Encryption/Stream/StreamNativeEncryptor.cs new file mode 100644 index 00000000..465e316f --- /dev/null +++ b/shadowsocks-csharp/Encryption/Stream/StreamNativeEncryptor.cs @@ -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 getCiphers() + { + return new Dictionary() + { + { "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; + } + } +} diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index bf562337..5cbc4d78 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -129,6 +129,7 @@ + diff --git a/test/CryptographyTest.cs b/test/CryptographyTest.cs index 68c2e3a8..1b2f98ae 100644 --- a/test/CryptographyTest.cs +++ b/test/CryptographyTest.cs @@ -229,5 +229,53 @@ namespace Shadowsocks.Test throw; } } + + [TestMethod] + public void TestNativeEncryption() + { + encryptionFailed = false; + // run it once before the multi-threading test to initialize global tables + RunSingleNativeEncryptionThread(); + List threads = new List(); + 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; + } + } } -} +} \ No newline at end of file