@@ -224,8 +224,8 @@ namespace Shadowsocks.Controller | |||||
/* prepare address buffer length for AEAD */ | /* prepare address buffer length for AEAD */ | ||||
Logger.Debug($"_addrBufLength={_addrBufLength}"); | Logger.Debug($"_addrBufLength={_addrBufLength}"); | ||||
encryptor.AddrBufLength = _addrBufLength; | |||||
decryptor.AddrBufLength = _addrBufLength; | |||||
encryptor.AddressBufferLength = _addrBufLength; | |||||
decryptor.AddressBufferLength = _addrBufLength; | |||||
} | } | ||||
public void Start(byte[] firstPacket, int length) | public void Start(byte[] firstPacket, int length) | ||||
@@ -275,15 +275,6 @@ namespace Shadowsocks.Controller | |||||
Logger.LogUsefulException(e); | Logger.LogUsefulException(e); | ||||
} | } | ||||
} | } | ||||
lock (_encryptionLock) | |||||
{ | |||||
lock (_decryptionLock) | |||||
{ | |||||
encryptor?.Dispose(); | |||||
decryptor?.Dispose(); | |||||
} | |||||
} | |||||
} | } | ||||
private void HandshakeReceive() | private void HandshakeReceive() | ||||
@@ -13,16 +13,15 @@ namespace Shadowsocks.Encryption.AEAD | |||||
public AEADAesGcmNativeEncryptor(string method, string password) : base(method, password) | public AEADAesGcmNativeEncryptor(string method, string password) : base(method, password) | ||||
{ | { | ||||
} | } | ||||
static int CIPHER_AES = 1; // dummy | |||||
private static readonly Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo> | |||||
private static readonly Dictionary<string, CipherInfo> _ciphers = new Dictionary<string, CipherInfo> | |||||
{ | { | ||||
{"aes-128-gcm", new EncryptorInfo("AES-128-GCM", 16, 16, 12, 16, CIPHER_AES)}, | |||||
{"aes-192-gcm", new EncryptorInfo("AES-192-GCM", 24, 24, 12, 16, CIPHER_AES)}, | |||||
{"aes-256-gcm", new EncryptorInfo("AES-256-GCM", 32, 32, 12, 16, CIPHER_AES)}, | |||||
{"aes-128-gcm", new CipherInfo("AES-128-GCM", 16, 16, 12, 16, CipherFamily.AesGcm)}, | |||||
{"aes-192-gcm", new CipherInfo("AES-192-GCM", 24, 24, 12, 16, CipherFamily.AesGcm)}, | |||||
{"aes-256-gcm", new CipherInfo("AES-256-GCM", 32, 32, 12, 16, CipherFamily.AesGcm)}, | |||||
}; | }; | ||||
protected override Dictionary<string, EncryptorInfo> getCiphers() | |||||
protected override Dictionary<string, CipherInfo> getCiphers() | |||||
{ | { | ||||
return _ciphers; | return _ciphers; | ||||
} | } | ||||
@@ -102,13 +101,9 @@ namespace Shadowsocks.Encryption.AEAD | |||||
return d.ToArray(); | return d.ToArray(); | ||||
} | } | ||||
public static List<string> SupportedCiphers() | |||||
{ | |||||
return new List<string>(_ciphers.Keys); | |||||
} | |||||
public override void Dispose() | |||||
public static Dictionary<string, CipherInfo> SupportedCiphers() | |||||
{ | { | ||||
return _ciphers; | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -11,22 +11,19 @@ namespace Shadowsocks.Encryption.AEAD | |||||
{ | { | ||||
public class AEADBouncyCastleEncryptor : AEADEncryptor | public class AEADBouncyCastleEncryptor : AEADEncryptor | ||||
{ | { | ||||
static int CIPHER_AES = 1; // dummy | |||||
public AEADBouncyCastleEncryptor(string method, string password) | |||||
: base(method, password) | |||||
public AEADBouncyCastleEncryptor(string method, string password) : base(method, password) | |||||
{ | { | ||||
} | } | ||||
private static readonly Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo> | |||||
private static readonly Dictionary<string, CipherInfo> _ciphers = new Dictionary<string, CipherInfo> | |||||
{ | { | ||||
{"aes-128-gcm", new EncryptorInfo("AES-128-GCM", 16, 16, 12, 16, CIPHER_AES)}, | |||||
{"aes-192-gcm", new EncryptorInfo("AES-192-GCM", 24, 24, 12, 16, CIPHER_AES)}, | |||||
{"aes-256-gcm", new EncryptorInfo("AES-256-GCM", 32, 32, 12, 16, CIPHER_AES)}, | |||||
{"aes-128-gcm", new CipherInfo("aes-128-gcm", 16, 16, 12, 16, CipherFamily.AesGcm)}, | |||||
{"aes-192-gcm", new CipherInfo("aes-192-gcm", 24, 24, 12, 16, CipherFamily.AesGcm)}, | |||||
{"aes-256-gcm", new CipherInfo("aes-256-gcm", 32, 32, 12, 16, CipherFamily.AesGcm)}, | |||||
}; | }; | ||||
protected override Dictionary<string, EncryptorInfo> getCiphers() | |||||
protected override Dictionary<string, CipherInfo> getCiphers() | |||||
{ | { | ||||
return _ciphers; | return _ciphers; | ||||
} | } | ||||
@@ -90,14 +87,10 @@ namespace Shadowsocks.Encryption.AEAD | |||||
aes.DoFinal(cipher, len); | aes.DoFinal(cipher, len); | ||||
return cipher; | return cipher; | ||||
} | } | ||||
public static List<string> SupportedCiphers() | |||||
{ | |||||
return new List<string>(_ciphers.Keys); | |||||
} | |||||
public override void Dispose() | |||||
public static Dictionary<string, CipherInfo> SupportedCiphers() | |||||
{ | { | ||||
return _ciphers; | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -11,8 +11,7 @@ using Shadowsocks.Encryption.Stream; | |||||
namespace Shadowsocks.Encryption.AEAD | namespace Shadowsocks.Encryption.AEAD | ||||
{ | { | ||||
public abstract class AEADEncryptor | |||||
: EncryptorBase | |||||
public abstract class AEADEncryptor : EncryptorBase | |||||
{ | { | ||||
private static Logger logger = LogManager.GetCurrentClassLogger(); | private static Logger logger = LogManager.GetCurrentClassLogger(); | ||||
// We are using the same saltLen and keyLen | // We are using the same saltLen and keyLen | ||||
@@ -29,13 +28,13 @@ namespace Shadowsocks.Encryption.AEAD | |||||
public const int CHUNK_LEN_BYTES = 2; | public const int CHUNK_LEN_BYTES = 2; | ||||
public const uint CHUNK_LEN_MASK = 0x3FFFu; | public const uint CHUNK_LEN_MASK = 0x3FFFu; | ||||
protected Dictionary<string, EncryptorInfo> ciphers; | |||||
protected Dictionary<string, CipherInfo> ciphers; | |||||
protected string _method; | protected string _method; | ||||
protected int _cipher; | |||||
protected CipherFamily _cipher; | |||||
// internal name in the crypto library | // internal name in the crypto library | ||||
protected string _innerLibName; | protected string _innerLibName; | ||||
protected EncryptorInfo CipherInfo; | |||||
protected CipherInfo CipherInfo; | |||||
protected static byte[] _Masterkey = null; | protected static byte[] _Masterkey = null; | ||||
protected byte[] sessionKey; | protected byte[] sessionKey; | ||||
protected int keyLen; | protected int keyLen; | ||||
@@ -67,7 +66,7 @@ namespace Shadowsocks.Encryption.AEAD | |||||
decNonce = new byte[nonceLen]; | decNonce = new byte[nonceLen]; | ||||
} | } | ||||
protected abstract Dictionary<string, EncryptorInfo> getCiphers(); | |||||
protected abstract Dictionary<string, CipherInfo> getCiphers(); | |||||
protected void InitEncryptorInfo(string method) | protected void InitEncryptorInfo(string method) | ||||
{ | { | ||||
@@ -75,16 +74,12 @@ namespace Shadowsocks.Encryption.AEAD | |||||
_method = method; | _method = method; | ||||
ciphers = getCiphers(); | ciphers = getCiphers(); | ||||
CipherInfo = ciphers[_method]; | CipherInfo = ciphers[_method]; | ||||
_innerLibName = CipherInfo.InnerLibName; | |||||
_cipher = CipherInfo.Type; | _cipher = CipherInfo.Type; | ||||
if (_cipher == 0) | |||||
{ | |||||
throw new System.Exception("method not found"); | |||||
} | |||||
keyLen = CipherInfo.KeySize; | |||||
saltLen = CipherInfo.SaltSize; | |||||
tagLen = CipherInfo.TagSize; | |||||
nonceLen = CipherInfo.NonceSize; | |||||
var parameter = (AEADCipherParameter)CipherInfo.CipherParameter; | |||||
keyLen = parameter.KeySize; | |||||
saltLen = parameter.SaltSize; | |||||
tagLen = parameter.TagSize; | |||||
nonceLen = parameter.NonceSize; | |||||
} | } | ||||
protected void InitKey(string password) | protected void InitKey(string password) | ||||
@@ -196,10 +191,10 @@ namespace Shadowsocks.Encryption.AEAD | |||||
_tcpRequestSent = true; | _tcpRequestSent = true; | ||||
// The first TCP request | // The first TCP request | ||||
int encAddrBufLength; | int encAddrBufLength; | ||||
byte[] encAddrBufBytes = new byte[AddrBufLength + tagLen * 2 + CHUNK_LEN_BYTES]; | |||||
byte[] addrBytes = _encCircularBuffer.Get(AddrBufLength); | |||||
ChunkEncrypt(addrBytes, AddrBufLength, encAddrBufBytes, out encAddrBufLength); | |||||
Debug.Assert(encAddrBufLength == AddrBufLength + tagLen * 2 + CHUNK_LEN_BYTES); | |||||
byte[] encAddrBufBytes = new byte[AddressBufferLength + tagLen * 2 + CHUNK_LEN_BYTES]; | |||||
byte[] addrBytes = _encCircularBuffer.Get(AddressBufferLength); | |||||
ChunkEncrypt(addrBytes, AddressBufferLength, encAddrBufBytes, out encAddrBufLength); | |||||
Debug.Assert(encAddrBufLength == AddressBufferLength + tagLen * 2 + CHUNK_LEN_BYTES); | |||||
Array.Copy(encAddrBufBytes, 0, outbuf, outlength, encAddrBufLength); | Array.Copy(encAddrBufBytes, 0, outbuf, outlength, encAddrBufLength); | ||||
outlength += encAddrBufLength; | outlength += encAddrBufLength; | ||||
logger.Debug($"_tcpRequestSent outlength {outlength}"); | logger.Debug($"_tcpRequestSent outlength {outlength}"); | ||||
@@ -15,7 +15,6 @@ namespace Shadowsocks.Encryption.AEAD | |||||
SnufflePoly1305 dec; | SnufflePoly1305 dec; | ||||
public AEADNaClEncryptor(string method, string password) : base(method, password) | public AEADNaClEncryptor(string method, string password) : base(method, password) | ||||
{ | { | ||||
} | } | ||||
public override void InitCipher(byte[] salt, bool isEncrypt, bool isUdp) | public override void InitCipher(byte[] salt, bool isEncrypt, bool isUdp) | ||||
@@ -28,10 +27,10 @@ namespace Shadowsocks.Encryption.AEAD | |||||
switch (_cipher) | switch (_cipher) | ||||
{ | { | ||||
default: | default: | ||||
case CipherChaCha20Poly1305: | |||||
case CipherFamily.Chacha20Poly1305: | |||||
tmp = new ChaCha20Poly1305(sessionKey); | tmp = new ChaCha20Poly1305(sessionKey); | ||||
break; | break; | ||||
case CipherXChaCha20Poly1305: | |||||
case CipherFamily.XChacha20Poly1305: | |||||
tmp = new XChaCha20Poly1305(sessionKey); | tmp = new XChaCha20Poly1305(sessionKey); | ||||
break; | break; | ||||
} | } | ||||
@@ -53,28 +52,25 @@ namespace Shadowsocks.Encryption.AEAD | |||||
clen = (uint)ct.Length; | clen = (uint)ct.Length; | ||||
} | } | ||||
public override void Dispose() | |||||
private static readonly Dictionary<string, CipherInfo> _ciphers = new Dictionary<string, CipherInfo> | |||||
{ | { | ||||
} | |||||
const int CipherChaCha20Poly1305 = 1; | |||||
const int CipherXChaCha20Poly1305 = 2; | |||||
{"chacha20-ietf-poly1305", new CipherInfo("chacha20-ietf-poly1305",32, 32, 12, 16, CipherFamily.Chacha20Poly1305)}, | |||||
{"xchacha20-ietf-poly1305", new CipherInfo("xchacha20-ietf-poly1305",32, 32, 24, 16, CipherFamily.XChacha20Poly1305)}, | |||||
}; | |||||
private static readonly Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo> | |||||
protected override Dictionary<string, CipherInfo> getCiphers() | |||||
{ | { | ||||
{"chacha20-ietf-poly1305", new EncryptorInfo(32, 32, 12, 16, 1)}, | |||||
{"xchacha20-ietf-poly1305", new EncryptorInfo(32, 32, 24, 16, 2)}, | |||||
//{"aes-256-gcm", new EncryptorInfo(32, 32, 12, 16, CIPHER_AES256GCM)}, | |||||
}; | |||||
return _ciphers; | |||||
} | |||||
protected override Dictionary<string, EncryptorInfo> getCiphers() | |||||
public static Dictionary<string, CipherInfo> SupportedCiphers() | |||||
{ | { | ||||
return _ciphers; | return _ciphers; | ||||
} | } | ||||
public override byte[] CipherEncrypt2(byte[] plain) | public override byte[] CipherEncrypt2(byte[] plain) | ||||
{ | { | ||||
return enc.Encrypt(plain, null, encNonce); | |||||
return enc.Encrypt(plain, null, encNonce); | |||||
} | } | ||||
public override byte[] CipherDecrypt2(byte[] cipher) | public override byte[] CipherDecrypt2(byte[] cipher) | ||||
@@ -1,56 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
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-fake-aead",new EncryptorInfo("PLAIN_AEAD",0,0,0,0,0) } | |||||
}; | |||||
protected override Dictionary<string, EncryptorInfo> getCiphers() | |||||
{ | |||||
return _ciphers; | |||||
} | |||||
public static IEnumerable<string> SupportedCiphers() | |||||
{ | |||||
return _ciphers.Keys; | |||||
} | |||||
public override byte[] CipherEncrypt2(byte[] plain) | |||||
{ | |||||
return plain; | |||||
} | |||||
public override byte[] CipherDecrypt2(byte[] cipher) | |||||
{ | |||||
return cipher; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,85 @@ | |||||
using System; | |||||
namespace Shadowsocks.Encryption | |||||
{ | |||||
public enum CipherFamily | |||||
{ | |||||
Plain, | |||||
Table, | |||||
AesGcm, | |||||
AesCfb, | |||||
AesCtr, | |||||
Chacha20, | |||||
Chacha20Poly1305, | |||||
XChacha20Poly1305, | |||||
Rc4, | |||||
Rc4Md5, | |||||
} | |||||
public enum CipherStandardState | |||||
{ | |||||
InUse, | |||||
Deprecated, | |||||
Hidden, | |||||
} | |||||
public class CipherParameter | |||||
{ | |||||
public int KeySize; | |||||
} | |||||
public class StreamCipherParameter : CipherParameter | |||||
{ | |||||
public int IvSize; | |||||
} | |||||
public class AEADCipherParameter : CipherParameter | |||||
{ | |||||
public int SaltSize; | |||||
public int TagSize; | |||||
public int NonceSize; | |||||
} | |||||
public class CipherInfo | |||||
{ | |||||
public string Name; | |||||
public CipherFamily Type; | |||||
public CipherParameter CipherParameter; | |||||
public CipherStandardState StandardState = CipherStandardState.InUse; | |||||
#region Stream ciphers | |||||
public CipherInfo(string name, int keySize, int ivSize, CipherFamily type) | |||||
{ | |||||
Type = type; | |||||
Name = name; | |||||
StandardState = CipherStandardState.Hidden; | |||||
CipherParameter = new StreamCipherParameter | |||||
{ | |||||
KeySize = keySize, | |||||
IvSize = ivSize, | |||||
}; | |||||
} | |||||
#endregion | |||||
#region AEAD ciphers | |||||
public CipherInfo(string name, int keySize, int saltSize, int nonceSize, int tagSize, CipherFamily type) | |||||
{ | |||||
Type = type; | |||||
Name = name; | |||||
CipherParameter = new AEADCipherParameter | |||||
{ | |||||
KeySize = keySize, | |||||
SaltSize = saltSize, | |||||
NonceSize = nonceSize, | |||||
TagSize = tagSize, | |||||
}; | |||||
} | |||||
#endregion | |||||
} | |||||
} |
@@ -1,65 +1,6 @@ | |||||
namespace Shadowsocks.Encryption | namespace Shadowsocks.Encryption | ||||
{ | { | ||||
public class EncryptorInfo | |||||
{ | |||||
public int KeySize; | |||||
public int IvSize; | |||||
public int SaltSize; | |||||
public int TagSize; | |||||
public int NonceSize; | |||||
public int Type; | |||||
public string InnerLibName; | |||||
// For those who make use of internal crypto method name | |||||
// e.g. mbed TLS | |||||
#region Stream ciphers | |||||
public EncryptorInfo(string innerLibName, int keySize, int ivSize, int type) | |||||
{ | |||||
this.KeySize = keySize; | |||||
this.IvSize = ivSize; | |||||
this.Type = type; | |||||
this.InnerLibName = innerLibName; | |||||
} | |||||
public EncryptorInfo(int keySize, int ivSize, int type) | |||||
{ | |||||
this.KeySize = keySize; | |||||
this.IvSize = ivSize; | |||||
this.Type = type; | |||||
this.InnerLibName = string.Empty; | |||||
} | |||||
#endregion | |||||
#region AEAD ciphers | |||||
public EncryptorInfo(string innerLibName, int keySize, int saltSize, int nonceSize, int tagSize, int type) | |||||
{ | |||||
this.KeySize = keySize; | |||||
this.SaltSize = saltSize; | |||||
this.NonceSize = nonceSize; | |||||
this.TagSize = tagSize; | |||||
this.Type = type; | |||||
this.InnerLibName = innerLibName; | |||||
} | |||||
public EncryptorInfo(int keySize, int saltSize, int nonceSize, int tagSize, int type) | |||||
{ | |||||
this.KeySize = keySize; | |||||
this.SaltSize = saltSize; | |||||
this.NonceSize = nonceSize; | |||||
this.TagSize = tagSize; | |||||
this.Type = type; | |||||
this.InnerLibName = string.Empty; | |||||
} | |||||
#endregion | |||||
} | |||||
public abstract class EncryptorBase | |||||
: IEncryptor | |||||
public abstract class EncryptorBase : IEncryptor | |||||
{ | { | ||||
public const int MAX_INPUT_SIZE = 32768; | public const int MAX_INPUT_SIZE = 32768; | ||||
@@ -81,7 +22,7 @@ | |||||
protected string Method; | protected string Method; | ||||
protected string Password; | protected string Password; | ||||
public abstract void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength); | public abstract void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength); | ||||
public abstract void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength); | public abstract void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength); | ||||
@@ -90,8 +31,6 @@ | |||||
public abstract void DecryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength); | public abstract void DecryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength); | ||||
public abstract void Dispose(); | |||||
public int AddrBufLength { get; set; } = - 1; | |||||
public int AddressBufferLength { get; set; } = -1; | |||||
} | } | ||||
} | } |
@@ -10,27 +10,51 @@ namespace Shadowsocks.Encryption | |||||
public static class EncryptorFactory | public static class EncryptorFactory | ||||
{ | { | ||||
private static Dictionary<string, Type> _registeredEncryptors = new Dictionary<string, Type>(); | private static Dictionary<string, Type> _registeredEncryptors = new Dictionary<string, Type>(); | ||||
private static Dictionary<string, CipherInfo> ciphers = new Dictionary<string, CipherInfo>(); | |||||
private static readonly Type[] ConstructorTypes = { typeof(string), typeof(string) }; | private static readonly Type[] ConstructorTypes = { typeof(string), typeof(string) }; | ||||
static EncryptorFactory() | static EncryptorFactory() | ||||
{ | { | ||||
foreach (string method in StreamTableNativeEncryptor.SupportedCiphers()) | |||||
foreach (var method in StreamTableNativeEncryptor.SupportedCiphers()) | |||||
{ | { | ||||
if (!_registeredEncryptors.ContainsKey(method)) | |||||
_registeredEncryptors.Add(method, typeof(StreamTableNativeEncryptor)); | |||||
if (!_registeredEncryptors.ContainsKey(method.Key)) | |||||
{ | |||||
ciphers.Add(method.Key, method.Value); | |||||
_registeredEncryptors.Add(method.Key, typeof(StreamTableNativeEncryptor)); | |||||
} | |||||
} | } | ||||
foreach (string method in AEADNativeEncryptor.SupportedCiphers()) | |||||
foreach (var method in StreamRc4NativeEncryptor.SupportedCiphers()) | |||||
{ | { | ||||
if (!_registeredEncryptors.ContainsKey(method)) | |||||
_registeredEncryptors.Add(method, typeof(AEADNativeEncryptor)); | |||||
if (!_registeredEncryptors.ContainsKey(method.Key)) | |||||
{ | |||||
ciphers.Add(method.Key, method.Value); | |||||
_registeredEncryptors.Add(method.Key, typeof(StreamRc4NativeEncryptor)); | |||||
} | |||||
} | } | ||||
foreach (string method in AEADBouncyCastleEncryptor.SupportedCiphers()) | |||||
foreach (var method in AEADAesGcmNativeEncryptor.SupportedCiphers()) | |||||
{ | |||||
if (!_registeredEncryptors.ContainsKey(method.Key)) | |||||
{ | |||||
ciphers.Add(method.Key, method.Value); | |||||
_registeredEncryptors.Add(method.Key, typeof(AEADAesGcmNativeEncryptor)); | |||||
} | |||||
} | |||||
foreach (var method in AEADNaClEncryptor.SupportedCiphers()) | |||||
{ | |||||
if (!_registeredEncryptors.ContainsKey(method.Key)) | |||||
{ | |||||
ciphers.Add(method.Key, method.Value); | |||||
_registeredEncryptors.Add(method.Key, typeof(AEADNaClEncryptor)); | |||||
} | |||||
} | |||||
foreach (var method in StreamRc4NativeEncryptor.SupportedCiphers()) | |||||
{ | { | ||||
if (!_registeredEncryptors.ContainsKey(method)) | |||||
_registeredEncryptors.Add(method, typeof(AEADBouncyCastleEncryptor)); | |||||
if (!_registeredEncryptors.ContainsKey(method.Key)) | |||||
{ | |||||
ciphers.Add(method.Key, method.Value); | |||||
_registeredEncryptors.Add(method.Key, typeof(StreamRc4NativeEncryptor)); | |||||
} | |||||
} | } | ||||
} | } | ||||
@@ -1,11 +1,12 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
namespace Shadowsocks.Encryption | namespace Shadowsocks.Encryption | ||||
{ | { | ||||
public interface IEncryptor : IDisposable | |||||
public interface IEncryptor | |||||
{ | { | ||||
/* length == -1 means not used */ | /* length == -1 means not used */ | ||||
int AddrBufLength { set; get; } | |||||
int AddressBufferLength { set; get; } | |||||
void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength); | void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength); | ||||
void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength); | void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength); | ||||
void EncryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength); | void EncryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength); | ||||
@@ -7,8 +7,7 @@ using Shadowsocks.Controller; | |||||
namespace Shadowsocks.Encryption.Stream | namespace Shadowsocks.Encryption.Stream | ||||
{ | { | ||||
public abstract class StreamEncryptor | |||||
: EncryptorBase | |||||
public abstract class StreamEncryptor : EncryptorBase | |||||
{ | { | ||||
// for UDP only | // for UDP only | ||||
protected static byte[] _udpTmpBuf = new byte[65536]; | protected static byte[] _udpTmpBuf = new byte[65536]; | ||||
@@ -17,7 +16,7 @@ namespace Shadowsocks.Encryption.Stream | |||||
private ByteCircularBuffer _encCircularBuffer = new ByteCircularBuffer(TCPHandler.BufferSize * 2); | private ByteCircularBuffer _encCircularBuffer = new ByteCircularBuffer(TCPHandler.BufferSize * 2); | ||||
private ByteCircularBuffer _decCircularBuffer = new ByteCircularBuffer(TCPHandler.BufferSize * 2); | private ByteCircularBuffer _decCircularBuffer = new ByteCircularBuffer(TCPHandler.BufferSize * 2); | ||||
protected Dictionary<string, EncryptorInfo> ciphers; | |||||
protected Dictionary<string, CipherInfo> ciphers; | |||||
protected byte[] _encryptIV; | protected byte[] _encryptIV; | ||||
protected byte[] _decryptIV; | protected byte[] _decryptIV; | ||||
@@ -27,10 +26,10 @@ namespace Shadowsocks.Encryption.Stream | |||||
protected bool _encryptIVSent; | protected bool _encryptIVSent; | ||||
protected string _method; | protected string _method; | ||||
protected int _cipher; | |||||
protected CipherFamily _cipher; | |||||
// internal name in the crypto library | // internal name in the crypto library | ||||
protected string _innerLibName; | protected string _innerLibName; | ||||
protected EncryptorInfo CipherInfo; | |||||
protected CipherInfo CipherInfo; | |||||
// long-time master key | // long-time master key | ||||
protected static byte[] _key = null; | protected static byte[] _key = null; | ||||
protected int keyLen; | protected int keyLen; | ||||
@@ -43,7 +42,7 @@ namespace Shadowsocks.Encryption.Stream | |||||
InitKey(password); | InitKey(password); | ||||
} | } | ||||
protected abstract Dictionary<string, EncryptorInfo> getCiphers(); | |||||
protected abstract Dictionary<string, CipherInfo> getCiphers(); | |||||
private void InitEncryptorInfo(string method) | private void InitEncryptorInfo(string method) | ||||
{ | { | ||||
@@ -51,14 +50,10 @@ namespace Shadowsocks.Encryption.Stream | |||||
_method = method; | _method = method; | ||||
ciphers = getCiphers(); | ciphers = getCiphers(); | ||||
CipherInfo = ciphers[_method]; | CipherInfo = ciphers[_method]; | ||||
_innerLibName = CipherInfo.InnerLibName; | |||||
_cipher = CipherInfo.Type; | _cipher = CipherInfo.Type; | ||||
if (_cipher == 0) | |||||
{ | |||||
throw new System.Exception("method not found"); | |||||
} | |||||
keyLen = CipherInfo.KeySize; | |||||
ivLen = CipherInfo.IvSize; | |||||
var parameter = (StreamCipherParameter)CipherInfo.CipherParameter; | |||||
keyLen = parameter.KeySize; | |||||
ivLen = parameter.IvSize; | |||||
} | } | ||||
private void InitKey(string password) | private void InitKey(string password) | ||||
@@ -8,27 +8,16 @@ namespace Shadowsocks.Encryption.Stream | |||||
{ | { | ||||
public class StreamRc4NativeEncryptor : StreamEncryptor | public class StreamRc4NativeEncryptor : StreamEncryptor | ||||
{ | { | ||||
const int Rc4 = 0; | |||||
const int Rc4Md5 = 1; | |||||
string _password; | |||||
byte[] realkey; | |||||
byte[] sbox; | |||||
byte[] realkey = new byte[256]; | |||||
byte[] sbox = new byte[256]; | |||||
public StreamRc4NativeEncryptor(string method, string password) : base(method, password) | public StreamRc4NativeEncryptor(string method, string password) : base(method, password) | ||||
{ | { | ||||
_password = password; | |||||
} | |||||
public override void Dispose() | |||||
{ | |||||
return; | |||||
} | } | ||||
protected override void initCipher(byte[] iv, bool isEncrypt) | protected override void initCipher(byte[] iv, bool isEncrypt) | ||||
{ | { | ||||
base.initCipher(iv, isEncrypt); | base.initCipher(iv, isEncrypt); | ||||
if (_cipher == Rc4Md5) | |||||
if (_cipher == CipherFamily.Rc4Md5) | |||||
{ | { | ||||
byte[] temp = new byte[keyLen + ivLen]; | byte[] temp = new byte[keyLen + ivLen]; | ||||
Array.Copy(_key, 0, temp, 0, keyLen); | Array.Copy(_key, 0, temp, 0, keyLen); | ||||
@@ -54,19 +43,19 @@ namespace Shadowsocks.Encryption.Stream | |||||
Array.Copy(t, outbuf, length); | Array.Copy(t, outbuf, length); | ||||
} | } | ||||
private static readonly Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo> | |||||
private static readonly Dictionary<string, CipherInfo> _ciphers = new Dictionary<string, CipherInfo> | |||||
{ | { | ||||
// original RC4 doesn't use IV | // original RC4 doesn't use IV | ||||
{ "rc4", new EncryptorInfo("RC4", 16, 0, Rc4) }, | |||||
{ "rc4-md5", new EncryptorInfo("RC4", 16, 16, Rc4Md5) }, | |||||
{ "rc4", new CipherInfo("rc4", 16, 0, CipherFamily.Rc4) }, | |||||
{ "rc4-md5", new CipherInfo("rc4-md5", 16, 16, CipherFamily.Rc4Md5) }, | |||||
}; | }; | ||||
public static IEnumerable<string> SupportedCiphers() | |||||
public static Dictionary<string, CipherInfo> SupportedCiphers() | |||||
{ | { | ||||
return _ciphers.Keys; | |||||
return _ciphers; | |||||
} | } | ||||
protected override Dictionary<string, EncryptorInfo> getCiphers() | |||||
protected override Dictionary<string, CipherInfo> getCiphers() | |||||
{ | { | ||||
return _ciphers; | return _ciphers; | ||||
} | } | ||||
@@ -8,9 +8,6 @@ namespace Shadowsocks.Encryption.Stream | |||||
{ | { | ||||
public class StreamTableNativeEncryptor : StreamEncryptor | public class StreamTableNativeEncryptor : StreamEncryptor | ||||
{ | { | ||||
const int Plain = 0; | |||||
const int Table = 1; | |||||
string _password; | string _password; | ||||
public StreamTableNativeEncryptor(string method, string password) : base(method, password) | public StreamTableNativeEncryptor(string method, string password) : base(method, password) | ||||
@@ -18,15 +15,10 @@ namespace Shadowsocks.Encryption.Stream | |||||
_password = password; | _password = password; | ||||
} | } | ||||
public override void Dispose() | |||||
{ | |||||
return; | |||||
} | |||||
protected override void initCipher(byte[] iv, bool isEncrypt) | protected override void initCipher(byte[] iv, bool isEncrypt) | ||||
{ | { | ||||
base.initCipher(iv, isEncrypt); | base.initCipher(iv, isEncrypt); | ||||
if (_cipher == Table) | |||||
if (_cipher == CipherFamily.Table) | |||||
{ | { | ||||
ulong a = BitConverter.ToUInt64(CryptoUtils.MD5(Encoding.UTF8.GetBytes(_password)), 0); | ulong a = BitConverter.ToUInt64(CryptoUtils.MD5(Encoding.UTF8.GetBytes(_password)), 0); | ||||
for (int i = 0; i < 256; i++) | for (int i = 0; i < 256; i++) | ||||
@@ -46,7 +38,7 @@ namespace Shadowsocks.Encryption.Stream | |||||
protected override void cipherUpdate(bool isEncrypt, int length, byte[] buf, byte[] outbuf) | protected override void cipherUpdate(bool isEncrypt, int length, byte[] buf, byte[] outbuf) | ||||
{ | { | ||||
if (_cipher == Table) | |||||
if (_cipher == CipherFamily.Table) | |||||
{ | { | ||||
var table = isEncrypt ? _encryptTable : _decryptTable; | var table = isEncrypt ? _encryptTable : _decryptTable; | ||||
for (int i = 0; i < length; i++) | for (int i = 0; i < length; i++) | ||||
@@ -54,24 +46,24 @@ namespace Shadowsocks.Encryption.Stream | |||||
outbuf[i] = table[buf[i]]; | outbuf[i] = table[buf[i]]; | ||||
} | } | ||||
} | } | ||||
else if (_cipher == Plain) | |||||
else if (_cipher == CipherFamily.Plain) | |||||
{ | { | ||||
Array.Copy(buf, outbuf, length); | Array.Copy(buf, outbuf, length); | ||||
} | } | ||||
} | } | ||||
private static readonly Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo> | |||||
private static readonly Dictionary<string, CipherInfo> _ciphers = new Dictionary<string, CipherInfo> | |||||
{ | { | ||||
{"plain", new EncryptorInfo("PLAIN", 0, 0, Plain) }, | |||||
{"table", new EncryptorInfo("TABLE", 0, 0, Table) }, | |||||
{"plain", new CipherInfo("plain", 0, 0, CipherFamily.Plain) }, | |||||
{"table", new CipherInfo("table", 0, 0, CipherFamily.Table) }, | |||||
}; | }; | ||||
public static IEnumerable<string> SupportedCiphers() | |||||
public static Dictionary<string, CipherInfo> SupportedCiphers() | |||||
{ | { | ||||
return _ciphers.Keys; | |||||
return _ciphers; | |||||
} | } | ||||
protected override Dictionary<string, EncryptorInfo> getCiphers() | |||||
protected override Dictionary<string, CipherInfo> getCiphers() | |||||
{ | { | ||||
return _ciphers; | return _ciphers; | ||||
} | } | ||||
@@ -11,7 +11,12 @@ | |||||
<Version>4.1.9.2</Version> | <Version>4.1.9.2</Version> | ||||
<AssemblyName>Shadowsocks</AssemblyName> | <AssemblyName>Shadowsocks</AssemblyName> | ||||
<ApplicationIcon>shadowsocks.ico</ApplicationIcon> | <ApplicationIcon>shadowsocks.ico</ApplicationIcon> | ||||
<StartupObject /> | |||||
<StartupObject>Shadowsocks.Program</StartupObject> | |||||
<Nullable>enable</Nullable> | |||||
</PropertyGroup> | |||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> | |||||
<DefineConstants>TRACE</DefineConstants> | |||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
@@ -58,26 +63,24 @@ | |||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<PackageReference Include="BouncyCastle.NetCore" Version="1.8.5" /> | <PackageReference Include="BouncyCastle.NetCore" Version="1.8.5" /> | ||||
<PackageReference Include="Caseless.Fody" Version="1.9.0" /> | |||||
<!--PackageReference Include="Caseless.Fody" Version="1.9.0" /> | |||||
<PackageReference Include="Costura.Fody" Version="4.1.0" /> | <PackageReference Include="Costura.Fody" Version="4.1.0" /> | ||||
<PackageReference Include="Fody" Version="6.1.1"> | <PackageReference Include="Fody" Version="6.1.1"> | ||||
<PrivateAssets>all</PrivateAssets> | <PrivateAssets>all</PrivateAssets> | ||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
</PackageReference> | |||||
</PackageReference--> | |||||
<PackageReference Include="Microsoft.VisualBasic" Version="10.3.0" /> | <PackageReference Include="Microsoft.VisualBasic" Version="10.3.0" /> | ||||
<PackageReference Include="NaCl.Core" Version="1.2.0" /> | <PackageReference Include="NaCl.Core" Version="1.2.0" /> | ||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> | <PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> | ||||
<PackageReference Include="NLog" Version="4.6.8" /> | <PackageReference Include="NLog" Version="4.6.8" /> | ||||
<PackageReference Include="ZXing.Net" Version="0.16.5" /> | |||||
<PackageReference Include="StringEx.CS" Version="0.3.1"> | <PackageReference Include="StringEx.CS" Version="0.3.1"> | ||||
<PrivateAssets>all</PrivateAssets> | <PrivateAssets>all</PrivateAssets> | ||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
</PackageReference> | </PackageReference> | ||||
<PackageReference Include="System.Data.SqlClient" Version="4.8.1" /> | |||||
<PackageReference Include="System.Management" Version="4.7.0" /> | <PackageReference Include="System.Management" Version="4.7.0" /> | ||||
<PackageReference Include="ZXing.Net" Version="0.16.5" /> | |||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
@@ -11,7 +11,7 @@ namespace Shadowsocks.Test | |||||
[TestClass] | [TestClass] | ||||
public class CryptographyTest | public class CryptographyTest | ||||
{ | { | ||||
Random random = new Random(); | |||||
[TestMethod] | [TestMethod] | ||||
public void TestMD5() | public void TestMD5() | ||||
{ | { | ||||
@@ -19,7 +19,6 @@ namespace Shadowsocks.Test | |||||
{ | { | ||||
System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create(); | System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create(); | ||||
byte[] bytes = new byte[len]; | byte[] bytes = new byte[len]; | ||||
var random = new Random(); | |||||
random.NextBytes(bytes); | random.NextBytes(bytes); | ||||
string md5str = Convert.ToBase64String(md5.ComputeHash(bytes)); | string md5str = Convert.ToBase64String(md5.ComputeHash(bytes)); | ||||
string md5str2 = Convert.ToBase64String(CryptoUtils.MD5(bytes)); | string md5str2 = Convert.ToBase64String(CryptoUtils.MD5(bytes)); | ||||
@@ -33,12 +32,12 @@ namespace Shadowsocks.Test | |||||
byte[] plain = new byte[16384]; | byte[] plain = new byte[16384]; | ||||
byte[] cipher = new byte[plain.Length + 100];// AEAD with IPv4 address type needs +100 | byte[] cipher = new byte[plain.Length + 100];// AEAD with IPv4 address type needs +100 | ||||
byte[] plain2 = new byte[plain.Length + 16]; | byte[] plain2 = new byte[plain.Length + 16]; | ||||
int outLen = 0; | |||||
int outLen2 = 0; | |||||
var random = new Random(); | |||||
//random = new Random(); | |||||
random.NextBytes(plain); | random.NextBytes(plain); | ||||
encryptor.Encrypt(plain, plain.Length, cipher, out outLen); | |||||
decryptor.Decrypt(cipher, outLen, plain2, out outLen2); | |||||
encryptor.Encrypt(plain, plain.Length, cipher, out int outLen); | |||||
decryptor.Decrypt(cipher, outLen, plain2, out int outLen2); | |||||
Assert.AreEqual(plain.Length, outLen2); | Assert.AreEqual(plain.Length, outLen2); | ||||
for (int j = 0; j < plain.Length; j++) | for (int j = 0; j < plain.Length; j++) | ||||
{ | { | ||||
@@ -66,15 +65,16 @@ namespace Shadowsocks.Test | |||||
{ | { | ||||
var ector = enc.GetConstructor(new Type[] { typeof(string), typeof(string) }); | var ector = enc.GetConstructor(new Type[] { typeof(string), typeof(string) }); | ||||
var dctor = dec.GetConstructor(new Type[] { typeof(string), typeof(string) }); | var dctor = dec.GetConstructor(new Type[] { typeof(string), typeof(string) }); | ||||
try | try | ||||
{ | { | ||||
for (int i = 0; i < 100; i++) | |||||
IEncryptor encryptor = (IEncryptor)ector.Invoke(new object[] { method, password }); | |||||
IEncryptor decryptor = (IEncryptor)dctor.Invoke(new object[] { method, password }); | |||||
encryptor.AddressBufferLength = 1 + 4 + 2;// ADDR_ATYP_LEN + 4 + ADDR_PORT_LEN; | |||||
decryptor.AddressBufferLength = 1 + 4 + 2;// ADDR_ATYP_LEN + 4 + ADDR_PORT_LEN; | |||||
for (int i = 0; i < 16; i++) | |||||
{ | { | ||||
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; | |||||
decryptor.AddrBufLength = 1 + 4 + 2;// ADDR_ATYP_LEN + 4 + ADDR_PORT_LEN; | |||||
RunEncryptionRound(encryptor, decryptor); | RunEncryptionRound(encryptor, decryptor); | ||||
} | } | ||||
} | } | ||||
@@ -98,7 +98,7 @@ namespace Shadowsocks.Test | |||||
// run it once before the multi-threading test to initialize global tables | // run it once before the multi-threading test to initialize global tables | ||||
RunSingleEncryptionThread(enc, dec, method); | 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 < 8; i++) | |||||
{ | { | ||||
Thread t = new Thread(new ThreadStart(() => RunSingleEncryptionThread(enc, dec, method))); threads.Add(t); | Thread t = new Thread(new ThreadStart(() => RunSingleEncryptionThread(enc, dec, method))); threads.Add(t); | ||||
t.Start(); | t.Start(); | ||||
@@ -114,20 +114,41 @@ namespace Shadowsocks.Test | |||||
[TestMethod] | [TestMethod] | ||||
public void TestBouncyCastleAEADEncryption() | public void TestBouncyCastleAEADEncryption() | ||||
{ | { | ||||
TestEncryptionMethod(typeof(AEADBouncyCastleEncryptor), "aes-128-gcm"); | |||||
TestEncryptionMethod(typeof(AEADBouncyCastleEncryptor), "aes-192-gcm"); | |||||
TestEncryptionMethod(typeof(AEADBouncyCastleEncryptor), "aes-256-gcm"); | TestEncryptionMethod(typeof(AEADBouncyCastleEncryptor), "aes-256-gcm"); | ||||
} | } | ||||
[TestMethod] | |||||
public void TestAesGcmNativeAEADEncryption() | |||||
{ | |||||
TestEncryptionMethod(typeof(AEADAesGcmNativeEncryptor), "aes-128-gcm"); | |||||
TestEncryptionMethod(typeof(AEADAesGcmNativeEncryptor), "aes-192-gcm"); | |||||
TestEncryptionMethod(typeof(AEADAesGcmNativeEncryptor), "aes-256-gcm"); | |||||
} | |||||
[TestMethod] | [TestMethod] | ||||
public void TestNaClAEADEncryption() | public void TestNaClAEADEncryption() | ||||
{ | { | ||||
TestEncryptionMethod(typeof(AEADNaClEncryptor), "chacha20-ietf-poly1305"); | TestEncryptionMethod(typeof(AEADNaClEncryptor), "chacha20-ietf-poly1305"); | ||||
TestEncryptionMethod(typeof(AEADNaClEncryptor), "xchacha20-ietf-poly1305"); | |||||
} | } | ||||
[TestMethod] | [TestMethod] | ||||
public void TestNativeEncryption() | public void TestNativeEncryption() | ||||
{ | { | ||||
//TestEncryptionMethod(typeof(StreamTableNativeEncryptor), "table"); | |||||
TestEncryptionMethod(typeof(StreamTableNativeEncryptor), "plain"); | |||||
TestEncryptionMethod(typeof(StreamRc4NativeEncryptor), "rc4"); | |||||
TestEncryptionMethod(typeof(StreamRc4NativeEncryptor), "rc4-md5"); | TestEncryptionMethod(typeof(StreamRc4NativeEncryptor), "rc4-md5"); | ||||
} | } | ||||
[TestMethod] | |||||
public void TestNativeTableEncryption() | |||||
{ | |||||
// Too slow, run once to save CPU | |||||
var enc = new StreamTableNativeEncryptor("table", "barfoo!"); | |||||
var dec = new StreamTableNativeEncryptor("table", "barfoo!"); | |||||
RunEncryptionRound(enc, dec); | |||||
} | |||||
} | } | ||||
} | } |