Browse Source

Merge branch 'net-rc4' into cross-platform

pull/2865/head
Student Main 5 years ago
parent
commit
64c8473d82
6 changed files with 338 additions and 10 deletions
  1. +51
    -0
      shadowsocks-csharp/Encryption/AEAD/AEADNativeEncryptor.cs
  2. +11
    -0
      shadowsocks-csharp/Encryption/EncryptorFactory.cs
  3. +213
    -0
      shadowsocks-csharp/Encryption/Stream/StreamNativeEncryptor.cs
  4. +13
    -8
      shadowsocks-csharp/View/ConfigForm.cs
  5. +2
    -0
      shadowsocks-csharp/shadowsocks-csharp.csproj
  6. +48
    -2
      test/CryptographyTest.cs

+ 51
- 0
shadowsocks-csharp/Encryption/AEAD/AEADNativeEncryptor.cs View File

@@ -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;
}

}
}

+ 11
- 0
shadowsocks-csharp/Encryption/EncryptorFactory.cs View File

@@ -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())
{


+ 213
- 0
shadowsocks-csharp/Encryption/Stream/StreamNativeEncryptor.cs View File

@@ -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
}
}

+ 13
- 8
shadowsocks-csharp/View/ConfigForm.cs View File

@@ -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


+ 2
- 0
shadowsocks-csharp/shadowsocks-csharp.csproj View File

@@ -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" />


+ 48
- 2
test/CryptographyTest.cs View File

@@ -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
}
}
}
}
}

Loading…
Cancel
Save