diff --git a/shadowsocks-csharp/Encryption/AEAD/AEADAesGcmNativeEncryptor.cs b/shadowsocks-csharp/Encryption/AEAD/AEADAesGcmNativeEncryptor.cs new file mode 100644 index 00000000..8de7cc7f --- /dev/null +++ b/shadowsocks-csharp/Encryption/AEAD/AEADAesGcmNativeEncryptor.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace Shadowsocks.Encryption.AEAD +{ + public class AEADAesGcmNativeEncryptor : AEADNativeEncryptor + { + + public AEADAesGcmNativeEncryptor(string method, string password) : base(method, password) + { + } + static int CIPHER_AES = 1; // dummy + + private static readonly Dictionary _ciphers = new Dictionary + { + {"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)}, + }; + + protected override Dictionary getCiphers() + { + return _ciphers; + } + + public override void InitCipher(byte[] salt, bool isEncrypt, bool isUdp) + { + base.InitCipher(salt, isEncrypt, isUdp); + + DeriveSessionKey(isEncrypt ? encryptSalt : decryptSalt, + _Masterkey, sessionKey); + } + + public override void cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen) + { + using (var aes = new AesGcm(sessionKey)) + { + byte[] tag = new byte[tagLen]; + byte[] cipherWithoutTag = new byte[clen]; + Array.Copy(ciphertext, 0, cipherWithoutTag, 0, clen - tagLen); + Array.Copy(ciphertext, clen - tagLen, tag, 0, tagLen); + aes.Decrypt(decNonce, ciphertext, cipherWithoutTag, tag); + } + /*var cipher = new GcmBlockCipher(new AesEngine()); + AeadParameters parameters = new AeadParameters(new KeyParameter(_sessionKey), tagLen * 8, _decNonce); + + cipher.Init(false, parameters); + var plaintextBC = new byte[cipher.GetOutputSize((int)clen)]; + var len = cipher.ProcessBytes(ciphertext, 0, (int)clen, plaintextBC, 0); + cipher.DoFinal(plaintextBC, len); + plen = (uint)(plaintextBC.Length); + Array.Copy(plaintextBC, 0, plaintext, 0, plen);*/ + } + + public override void cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen) + { + using (var aes = new AesGcm(sessionKey)) + { + byte[] tag = new byte[tagLen]; + byte[] cipherWithoutTag = new byte[clen]; + aes.Encrypt(encNonce, plaintext, cipherWithoutTag, tag); + cipherWithoutTag.CopyTo(ciphertext, 0); + tag.CopyTo(ciphertext, clen); + } + + /* + var cipher = new GcmBlockCipher(new AesEngine()); + AeadParameters parameters = new AeadParameters(new KeyParameter(_sessionKey), tagLen * 8, _encNonce); + + cipher.Init(true, parameters); + var ciphertextBC = new byte[cipher.GetOutputSize((int)plen)]; + var len = cipher.ProcessBytes(plaintext, 0, (int)plen, ciphertextBC, 0); + cipher.DoFinal(ciphertextBC, len); + clen = (uint)(ciphertextBC.Length); + Array.Copy(ciphertextBC, 0, ciphertext, 0, clen);*/ + } + + public static List SupportedCiphers() + { + return new List(_ciphers.Keys); + } + + public override void Dispose() + { + } + } +} \ No newline at end of file diff --git a/shadowsocks-csharp/Encryption/AEAD/AEADBouncyCastleEncryptor.cs b/shadowsocks-csharp/Encryption/AEAD/AEADBouncyCastleEncryptor.cs index be028808..24bfa4a4 100644 --- a/shadowsocks-csharp/Encryption/AEAD/AEADBouncyCastleEncryptor.cs +++ b/shadowsocks-csharp/Encryption/AEAD/AEADBouncyCastleEncryptor.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; namespace Shadowsocks.Encryption.AEAD { - public class AEADBouncyCastleEncryptor : AEADEncryptor, IDisposable + public class AEADBouncyCastleEncryptor : AEADEncryptor { static int CIPHER_AES = 1; // dummy @@ -35,14 +35,14 @@ namespace Shadowsocks.Encryption.AEAD { base.InitCipher(salt, isEncrypt, isUdp); - DeriveSessionKey(isEncrypt ? _encryptSalt : _decryptSalt, - _Masterkey, _sessionKey); + DeriveSessionKey(isEncrypt ? encryptSalt : decryptSalt, + _Masterkey, sessionKey); } public override void cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen) { var cipher = new GcmBlockCipher(new AesEngine()); - AeadParameters parameters = new AeadParameters(new KeyParameter(_sessionKey), tagLen * 8, _decNonce); + AeadParameters parameters = new AeadParameters(new KeyParameter(sessionKey), tagLen * 8, decNonce); cipher.Init(false, parameters); var plaintextBC = new byte[cipher.GetOutputSize((int)clen)]; @@ -55,7 +55,7 @@ namespace Shadowsocks.Encryption.AEAD public override void cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen) { var cipher = new GcmBlockCipher(new AesEngine()); - AeadParameters parameters = new AeadParameters(new KeyParameter(_sessionKey), tagLen * 8, _encNonce); + AeadParameters parameters = new AeadParameters(new KeyParameter(sessionKey), tagLen * 8, encNonce); cipher.Init(true, parameters); var ciphertextBC = new byte[cipher.GetOutputSize((int)plen)]; diff --git a/shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs b/shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs index d33c3c65..cadad999 100644 --- a/shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs +++ b/shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs @@ -37,18 +37,19 @@ namespace Shadowsocks.Encryption.AEAD protected string _innerLibName; protected EncryptorInfo CipherInfo; protected static byte[] _Masterkey = null; - protected byte[] _sessionKey; + protected byte[] sessionKey; protected int keyLen; protected int saltLen; protected int tagLen; protected int nonceLen; - protected byte[] _encryptSalt; - protected byte[] _decryptSalt; + protected byte[] encryptSalt; + protected byte[] decryptSalt; protected object _nonceIncrementLock = new object(); - protected byte[] _encNonce; - protected byte[] _decNonce; + protected byte[] encNonce; + protected byte[] decNonce; + // Is first packet protected bool _decryptSaltReceived; protected bool _encryptSaltSent; @@ -62,8 +63,8 @@ namespace Shadowsocks.Encryption.AEAD InitEncryptorInfo(method); InitKey(password); // Initialize all-zero nonce for each connection - _encNonce = new byte[nonceLen]; - _decNonce = new byte[nonceLen]; + encNonce = new byte[nonceLen]; + decNonce = new byte[nonceLen]; } protected abstract Dictionary getCiphers(); @@ -93,7 +94,7 @@ namespace Shadowsocks.Encryption.AEAD if (_Masterkey.Length != keyLen) Array.Resize(ref _Masterkey, keyLen); DeriveKey(passbuf, _Masterkey, keyLen); // init session key - if (_sessionKey == null) _sessionKey = new byte[keyLen]; + if (sessionKey == null) sessionKey = new byte[keyLen]; } public void DeriveKey(byte[] password, byte[] key, int keylen) @@ -109,24 +110,22 @@ namespace Shadowsocks.Encryption.AEAD protected void IncrementNonce(bool isEncrypt) { lock (_nonceIncrementLock) { - CryptoUtils.SodiumIncrement(isEncrypt ? _encNonce : _decNonce); + CryptoUtils.SodiumIncrement(isEncrypt ? encNonce : decNonce); } } public virtual void InitCipher(byte[] salt, bool isEncrypt, bool isUdp) { if (isEncrypt) { - _encryptSalt = new byte[saltLen]; - Array.Copy(salt, _encryptSalt, saltLen); + encryptSalt = new byte[saltLen]; + Array.Copy(salt, encryptSalt, saltLen); } else { - _decryptSalt = new byte[saltLen]; - Array.Copy(salt, _decryptSalt, saltLen); + decryptSalt = new byte[saltLen]; + Array.Copy(salt, decryptSalt, saltLen); } logger.Dump("Salt", salt, saltLen); } - public static void randBytes(byte[] buf, int length) { RNG.GetBytes(buf, length); } - public abstract void cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen); public abstract void cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen); @@ -144,7 +143,7 @@ namespace Shadowsocks.Encryption.AEAD _encryptSaltSent = true; // Generate salt byte[] saltBytes = new byte[saltLen]; - randBytes(saltBytes, saltLen); + RNG.GetBytes(saltBytes, saltLen); InitCipher(saltBytes, true, false); Array.Copy(saltBytes, 0, outbuf, 0, saltLen); outlength = saltLen; @@ -289,7 +288,7 @@ namespace Shadowsocks.Encryption.AEAD public override void EncryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength) { // Generate salt - randBytes(outbuf, saltLen); + RNG.GetBytes(outbuf, saltLen); InitCipher(outbuf, true, true); uint olen = 0; lock (_udpTmpBuf) { diff --git a/shadowsocks-csharp/Encryption/EncryptorFactory.cs b/shadowsocks-csharp/Encryption/EncryptorFactory.cs index 05000796..14bad7ee 100644 --- a/shadowsocks-csharp/Encryption/EncryptorFactory.cs +++ b/shadowsocks-csharp/Encryption/EncryptorFactory.cs @@ -15,10 +15,10 @@ namespace Shadowsocks.Encryption static EncryptorFactory() { - foreach (string method in StreamNativeEncryptor.SupportedCiphers()) + foreach (string method in StreamTableNativeEncryptor.SupportedCiphers()) { if (!_registeredEncryptors.ContainsKey(method)) - _registeredEncryptors.Add(method, typeof(StreamNativeEncryptor)); + _registeredEncryptors.Add(method, typeof(StreamTableNativeEncryptor)); } foreach (string method in AEADNativeEncryptor.SupportedCiphers()) diff --git a/shadowsocks-csharp/Encryption/Stream/StreamRc4NativeEncryptor.cs b/shadowsocks-csharp/Encryption/Stream/StreamRc4NativeEncryptor.cs new file mode 100644 index 00000000..4655aba4 --- /dev/null +++ b/shadowsocks-csharp/Encryption/Stream/StreamRc4NativeEncryptor.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Shadowsocks.Encryption.Stream +{ + public class StreamRc4NativeEncryptor : StreamEncryptor + { + const int Rc4 = 0; + const int Rc4Md5 = 1; + + string _password; + + byte[] realkey; + byte[] sbox; + public StreamRc4NativeEncryptor(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 == Rc4Md5) + { + byte[] temp = new byte[keyLen + ivLen]; + Array.Copy(_key, 0, temp, 0, keyLen); + Array.Copy(iv, 0, temp, keyLen, ivLen); + realkey = CryptoUtils.MD5(temp); + } + else + { + realkey = _key; + } + sbox = SBox(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); + + RC4(ctx, sbox, t, length); + Array.Copy(t, outbuf, length); + } + + private static readonly Dictionary _ciphers = new Dictionary + { + // original RC4 doesn't use IV + { "rc4", new EncryptorInfo("RC4", 16, 0, Rc4) }, + { "rc4-md5", new EncryptorInfo("RC4", 16, 16, Rc4Md5) }, + }; + + public static IEnumerable SupportedCiphers() + { + return _ciphers.Keys; + } + + protected override Dictionary getCiphers() + { + return _ciphers; + } + + #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 + } +} diff --git a/shadowsocks-csharp/Encryption/Stream/StreamNativeEncryptor.cs b/shadowsocks-csharp/Encryption/Stream/StreamTableNativeEncryptor.cs similarity index 60% rename from shadowsocks-csharp/Encryption/Stream/StreamNativeEncryptor.cs rename to shadowsocks-csharp/Encryption/Stream/StreamTableNativeEncryptor.cs index 528e99ab..1771cf29 100644 --- a/shadowsocks-csharp/Encryption/Stream/StreamNativeEncryptor.cs +++ b/shadowsocks-csharp/Encryption/Stream/StreamTableNativeEncryptor.cs @@ -6,18 +6,14 @@ using System.Threading.Tasks; namespace Shadowsocks.Encryption.Stream { - public class StreamNativeEncryptor : StreamEncryptor + public class StreamTableNativeEncryptor : 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) + public StreamTableNativeEncryptor(string method, string password) : base(method, password) { _password = password; } @@ -30,22 +26,7 @@ namespace Shadowsocks.Encryption.Stream 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 = CryptoUtils.MD5(temp); - } - else - { - realkey = _key; - } - sbox = SBox(realkey); - } - else if (_cipher == Table) + if (_cipher == Table) { ulong a = BitConverter.ToUInt64(CryptoUtils.MD5(Encoding.UTF8.GetBytes(_password)), 0); for (int i = 0; i < 256; i++) @@ -77,24 +58,12 @@ namespace Shadowsocks.Encryption.Stream { 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 _ciphers = new Dictionary { {"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 SupportedCiphers() @@ -156,58 +125,5 @@ namespace Shadowsocks.Encryption.Stream 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 } } diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index de582723..d234bbb7 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -2,7 +2,7 @@ WinExe - net472;netcoreapp3.1 + netcoreapp3.1 Shadowsocks true clowwindy & community 2020 diff --git a/test/CryptographyTest.cs b/test/CryptographyTest.cs index fd641877..4e9dc046 100644 --- a/test/CryptographyTest.cs +++ b/test/CryptographyTest.cs @@ -111,8 +111,8 @@ namespace Shadowsocks.Test { IEncryptor encryptorN; IEncryptor decryptorN; - encryptorN = new StreamNativeEncryptor("rc4-md5", "barfoo!"); - decryptorN = new StreamNativeEncryptor("rc4-md5", "barfoo!"); + encryptorN = new StreamRc4NativeEncryptor("rc4-md5", "barfoo!"); + decryptorN = new StreamRc4NativeEncryptor("rc4-md5", "barfoo!"); RunEncryptionRound(encryptorN, decryptorN); } } @@ -134,7 +134,7 @@ namespace Shadowsocks.Test IEncryptor decryptor; encryptor = new Encryption.AEAD.AEADBouncyCastleEncryptor("aes-256-gcm", "barfoo!"); encryptor.AddrBufLength = 1 + 4 + 2;// ADDR_ATYP_LEN + 4 + ADDR_PORT_LEN; - decryptor = new Encryption.AEAD.AEADBouncyCastleEncryptor("aes-256-gcm", "barfoo!"); + decryptor = new Encryption.AEAD.AEADAesGcmNativeEncryptor("aes-256-gcm", "barfoo!"); decryptor.AddrBufLength = 1 + 4 + 2;// ADDR_ATYP_LEN + 4 + ADDR_PORT_LEN; RunEncryptionRound(encryptor, decryptor); } diff --git a/test/ShadowsocksTest.csproj b/test/ShadowsocksTest.csproj index f6a923b8..9c3db5f1 100644 --- a/test/ShadowsocksTest.csproj +++ b/test/ShadowsocksTest.csproj @@ -6,6 +6,10 @@ false + + 0 + +