@@ -53,7 +53,8 @@ namespace Shadowsocks.Encryption.Stream | |||||
CipherInfo = ciphers[_method]; | CipherInfo = ciphers[_method]; | ||||
_innerLibName = CipherInfo.InnerLibName; | _innerLibName = CipherInfo.InnerLibName; | ||||
_cipher = CipherInfo.Type; | _cipher = CipherInfo.Type; | ||||
if (_cipher == 0) { | |||||
if (_cipher == 0) | |||||
{ | |||||
throw new System.Exception("method not found"); | throw new System.Exception("method not found"); | ||||
} | } | ||||
keyLen = CipherInfo.KeySize; | keyLen = CipherInfo.KeySize; | ||||
@@ -73,10 +74,14 @@ namespace Shadowsocks.Encryption.Stream | |||||
byte[] result = new byte[password.Length + MD5_LEN]; | byte[] result = new byte[password.Length + MD5_LEN]; | ||||
int i = 0; | int i = 0; | ||||
byte[] md5sum = null; | byte[] md5sum = null; | ||||
while (i < keylen) { | |||||
if (i == 0) { | |||||
while (i < keylen) | |||||
{ | |||||
if (i == 0) | |||||
{ | |||||
md5sum = CryptoUtils.MD5(password); | md5sum = CryptoUtils.MD5(password); | ||||
} else { | |||||
} | |||||
else | |||||
{ | |||||
Array.Copy(md5sum, 0, result, 0, MD5_LEN); | Array.Copy(md5sum, 0, result, 0, MD5_LEN); | ||||
Array.Copy(password, 0, result, MD5_LEN, password.Length); | Array.Copy(password, 0, result, MD5_LEN, password.Length); | ||||
md5sum = CryptoUtils.MD5(result); | md5sum = CryptoUtils.MD5(result); | ||||
@@ -88,10 +93,13 @@ namespace Shadowsocks.Encryption.Stream | |||||
protected virtual void initCipher(byte[] iv, bool isEncrypt) | protected virtual void initCipher(byte[] iv, bool isEncrypt) | ||||
{ | { | ||||
if (isEncrypt) { | |||||
if (isEncrypt) | |||||
{ | |||||
_encryptIV = new byte[ivLen]; | _encryptIV = new byte[ivLen]; | ||||
Array.Copy(iv, _encryptIV, ivLen); | Array.Copy(iv, _encryptIV, ivLen); | ||||
} else { | |||||
} | |||||
else | |||||
{ | |||||
_decryptIV = new byte[ivLen]; | _decryptIV = new byte[ivLen]; | ||||
Array.Copy(iv, _decryptIV, ivLen); | Array.Copy(iv, _decryptIV, ivLen); | ||||
} | } | ||||
@@ -108,12 +116,13 @@ namespace Shadowsocks.Encryption.Stream | |||||
int cipherOffset = 0; | int cipherOffset = 0; | ||||
Debug.Assert(_encCircularBuffer != null, "_encCircularBuffer != null"); | Debug.Assert(_encCircularBuffer != null, "_encCircularBuffer != null"); | ||||
_encCircularBuffer.Put(buf, 0, length); | _encCircularBuffer.Put(buf, 0, length); | ||||
if (! _encryptIVSent) { | |||||
if (!_encryptIVSent) | |||||
{ | |||||
// Generate IV | // Generate IV | ||||
byte[] ivBytes = new byte[ivLen]; | byte[] ivBytes = new byte[ivLen]; | ||||
randBytes(ivBytes, ivLen); | randBytes(ivBytes, ivLen); | ||||
initCipher(ivBytes, true); | initCipher(ivBytes, true); | ||||
Array.Copy(ivBytes, 0, outbuf, 0, ivLen); | Array.Copy(ivBytes, 0, outbuf, 0, ivLen); | ||||
cipherOffset = ivLen; | cipherOffset = ivLen; | ||||
_encryptIVSent = true; | _encryptIVSent = true; | ||||
@@ -130,16 +139,22 @@ namespace Shadowsocks.Encryption.Stream | |||||
{ | { | ||||
Debug.Assert(_decCircularBuffer != null, "_circularBuffer != null"); | Debug.Assert(_decCircularBuffer != null, "_circularBuffer != null"); | ||||
_decCircularBuffer.Put(buf, 0, length); | _decCircularBuffer.Put(buf, 0, length); | ||||
if (! _decryptIVReceived) { | |||||
if (_decCircularBuffer.Size <= ivLen) { | |||||
if (!_decryptIVReceived) | |||||
{ | |||||
if (_decCircularBuffer.Size <= ivLen) | |||||
{ | |||||
// we need more data | // we need more data | ||||
outlength = 0; | outlength = 0; | ||||
return; | return; | ||||
} | } | ||||
// start decryption | // start decryption | ||||
_decryptIVReceived = true; | _decryptIVReceived = true; | ||||
byte[] iv = _decCircularBuffer.Get(ivLen); | |||||
initCipher(iv, false); | |||||
if (ivLen > 0) | |||||
{ | |||||
byte[] iv = _decCircularBuffer.Get(ivLen); | |||||
initCipher(iv, false); | |||||
} | |||||
else initCipher(Array.Empty<byte>(), false); | |||||
} | } | ||||
byte[] cipher = _decCircularBuffer.ToArray(); | byte[] cipher = _decCircularBuffer.ToArray(); | ||||
cipherUpdate(false, cipher.Length, cipher, outbuf); | cipherUpdate(false, cipher.Length, cipher, outbuf); | ||||
@@ -158,7 +173,8 @@ namespace Shadowsocks.Encryption.Stream | |||||
// Generate IV | // Generate IV | ||||
randBytes(outbuf, ivLen); | randBytes(outbuf, ivLen); | ||||
initCipher(outbuf, true); | initCipher(outbuf, true); | ||||
lock (_udpTmpBuf) { | |||||
lock (_udpTmpBuf) | |||||
{ | |||||
cipherUpdate(true, length, buf, _udpTmpBuf); | cipherUpdate(true, length, buf, _udpTmpBuf); | ||||
outlength = length + ivLen; | outlength = length + ivLen; | ||||
Buffer.BlockCopy(_udpTmpBuf, 0, outbuf, ivLen, length); | Buffer.BlockCopy(_udpTmpBuf, 0, outbuf, ivLen, length); | ||||
@@ -170,7 +186,8 @@ namespace Shadowsocks.Encryption.Stream | |||||
// Get IV from first pos | // Get IV from first pos | ||||
initCipher(buf, false); | initCipher(buf, false); | ||||
outlength = length - ivLen; | outlength = length - ivLen; | ||||
lock (_udpTmpBuf) { | |||||
lock (_udpTmpBuf) | |||||
{ | |||||
// C# could be multi-threaded | // C# could be multi-threaded | ||||
Buffer.BlockCopy(buf, ivLen, _udpTmpBuf, 0, length - ivLen); | Buffer.BlockCopy(buf, ivLen, _udpTmpBuf, 0, length - ivLen); | ||||
cipherUpdate(false, length - ivLen, _udpTmpBuf, outbuf); | cipherUpdate(false, length - ivLen, _udpTmpBuf, outbuf); | ||||
@@ -1,6 +1,7 @@ | |||||
using Microsoft.VisualStudio.TestTools.UnitTesting; | using Microsoft.VisualStudio.TestTools.UnitTesting; | ||||
using Shadowsocks.Encryption; | using Shadowsocks.Encryption; | ||||
using Shadowsocks.Encryption.Stream; | using Shadowsocks.Encryption.Stream; | ||||
using Shadowsocks.Encryption.AEAD; | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Threading; | using System.Threading; | ||||
@@ -59,41 +60,20 @@ namespace Shadowsocks.Test | |||||
} | } | ||||
} | } | ||||
private static bool encryptionFailed = false; | |||||
private static object locker = new object(); | |||||
const string password = "barfoo!"; | |||||
[TestMethod] | |||||
public void TestBouncyCastleAEADEncryption() | |||||
private void RunSingleEncryptionThread(Type enc, Type dec, string method) | |||||
{ | { | ||||
encryptionFailed = false; | |||||
// run it once before the multi-threading test to initialize global tables | |||||
RunSingleBouncyCastleAEADEncryptionThread(); | |||||
List<Thread> threads = new List<Thread>(); | |||||
for (int i = 0; i < 10; i++) | |||||
{ | |||||
Thread t = new Thread(new ThreadStart(RunSingleBouncyCastleAEADEncryptionThread)); threads.Add(t); | |||||
t.Start(); | |||||
} | |||||
foreach (Thread t in threads) | |||||
{ | |||||
t.Join(); | |||||
} | |||||
RNG.Close(); | |||||
Assert.IsFalse(encryptionFailed); | |||||
} | |||||
var ector = enc.GetConstructor(new Type[] { typeof(string), typeof(string) }); | |||||
var dctor = dec.GetConstructor(new Type[] { typeof(string), typeof(string) }); | |||||
private void RunSingleBouncyCastleAEADEncryptionThread() | |||||
{ | |||||
try | try | ||||
{ | { | ||||
for (int i = 0; i < 100; i++) | for (int i = 0; i < 100; i++) | ||||
{ | { | ||||
var random = new Random(); | |||||
IEncryptor encryptor; | |||||
IEncryptor decryptor; | |||||
encryptor = new Encryption.AEAD.AEADBouncyCastleEncryptor("aes-256-gcm", "barfoo!"); | |||||
IEncryptor encryptor = (IEncryptor)ector.Invoke(new object[] { method, password }); | |||||
IEncryptor decryptor = (IEncryptor)dctor.Invoke(new object[] { method, password }); | |||||
encryptor.AddrBufLength = 1 + 4 + 2;// ADDR_ATYP_LEN + 4 + ADDR_PORT_LEN; | encryptor.AddrBufLength = 1 + 4 + 2;// ADDR_ATYP_LEN + 4 + ADDR_PORT_LEN; | ||||
decryptor = new Encryption.AEAD.AEADBouncyCastleEncryptor("aes-256-gcm", "barfoo!"); | |||||
decryptor.AddrBufLength = 1 + 4 + 2;// ADDR_ATYP_LEN + 4 + ADDR_PORT_LEN; | decryptor.AddrBufLength = 1 + 4 + 2;// ADDR_ATYP_LEN + 4 + ADDR_PORT_LEN; | ||||
RunEncryptionRound(encryptor, decryptor); | RunEncryptionRound(encryptor, decryptor); | ||||
} | } | ||||
@@ -105,16 +85,22 @@ namespace Shadowsocks.Test | |||||
} | } | ||||
} | } | ||||
[TestMethod] | |||||
public void TesNaClAEADEncryption() | |||||
private static bool encryptionFailed = false; | |||||
private void TestEncryptionMethod(Type enc, string method) | |||||
{ | |||||
TestEncryptionMethod(enc, enc, method); | |||||
} | |||||
private void TestEncryptionMethod(Type enc, Type dec, string method) | |||||
{ | { | ||||
encryptionFailed = false; | encryptionFailed = false; | ||||
// run it once before the multi-threading test to initialize global tables | // run it once before the multi-threading test to initialize global tables | ||||
RunSingleNaClAEADEncryptionThread(); | |||||
RunSingleEncryptionThread(enc, dec, method); | |||||
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(RunSingleNaClAEADEncryptionThread)); threads.Add(t); | |||||
Thread t = new Thread(new ThreadStart(() => RunSingleEncryptionThread(enc, dec, method))); threads.Add(t); | |||||
t.Start(); | t.Start(); | ||||
} | } | ||||
foreach (Thread t in threads) | foreach (Thread t in threads) | ||||
@@ -125,68 +111,23 @@ namespace Shadowsocks.Test | |||||
Assert.IsFalse(encryptionFailed); | Assert.IsFalse(encryptionFailed); | ||||
} | } | ||||
private void RunSingleNaClAEADEncryptionThread() | |||||
[TestMethod] | |||||
public void TestBouncyCastleAEADEncryption() | |||||
{ | { | ||||
try | |||||
{ | |||||
for (int i = 0; i < 100; i++) | |||||
{ | |||||
var random = new Random(); | |||||
IEncryptor encryptor; | |||||
IEncryptor decryptor; | |||||
encryptor = new Encryption.AEAD.AEADNaClEncryptor("chacha20-ietf-poly1305", "barfoo!"); | |||||
encryptor.AddrBufLength = 1 + 4 + 2;// ADDR_ATYP_LEN + 4 + ADDR_PORT_LEN; | |||||
decryptor = new Encryption.AEAD.AEADNaClEncryptor("chacha20-ietf-poly1305", "barfoo!"); | |||||
decryptor.AddrBufLength = 1 + 4 + 2;// ADDR_ATYP_LEN + 4 + ADDR_PORT_LEN; | |||||
RunEncryptionRound(encryptor, decryptor); | |||||
} | |||||
} | |||||
catch | |||||
{ | |||||
encryptionFailed = true; | |||||
throw; | |||||
} | |||||
TestEncryptionMethod(typeof(AEADBouncyCastleEncryptor), "aes-256-gcm"); | |||||
} | } | ||||
[TestMethod] | [TestMethod] | ||||
public void TestNativeEncryption() | |||||
public void TestNaClAEADEncryption() | |||||
{ | { | ||||
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); | |||||
TestEncryptionMethod(typeof(AEADNaClEncryptor), "chacha20-ietf-poly1305"); | |||||
} | } | ||||
private void RunSingleNativeEncryptionThread() | |||||
[TestMethod] | |||||
public void TestNativeEncryption() | |||||
{ | { | ||||
try | |||||
{ | |||||
for (int i = 0; i < 100; i++) | |||||
{ | |||||
IEncryptor encryptorN; | |||||
IEncryptor decryptorN; | |||||
encryptorN = new StreamRc4NativeEncryptor("rc4-md5", "barfoo!"); | |||||
decryptorN = new StreamRc4NativeEncryptor("rc4-md5", "barfoo!"); | |||||
RunEncryptionRound(encryptorN, decryptorN); | |||||
} | |||||
} | |||||
catch | |||||
{ | |||||
encryptionFailed = true; | |||||
throw; | |||||
} | |||||
//TestEncryptionMethod(typeof(StreamTableNativeEncryptor), "table"); | |||||
TestEncryptionMethod(typeof(StreamRc4NativeEncryptor), "rc4-md5"); | |||||
} | } | ||||
} | } | ||||
} | } |