From 0497ce88ff1206538472339ac98117a2edc96beb Mon Sep 17 00:00:00 2001 From: Student Main Date: Sun, 22 Mar 2020 14:48:05 +0800 Subject: [PATCH] remove excessive state in Encryptor, as we use 2 encryptor per connection. --- .../AEAD/AEADAesGcmNativeEncryptor.cs | 35 ++------ .../AEAD/AEADBouncyCastleEncryptor.cs | 14 ++- .../Encryption/AEAD/AEADEncryptor.cs | 87 ++++++++----------- .../Encryption/AEAD/AEADNaClEncryptor.cs | 19 ++-- .../Encryption/Stream/StreamEncryptor.cs | 69 +++++++-------- .../Stream/StreamRc4NativeEncryptor.cs | 37 +++++--- .../Stream/StreamTableNativeEncryptor.cs | 32 +++++++ 7 files changed, 139 insertions(+), 154 deletions(-) diff --git a/shadowsocks-csharp/Encryption/AEAD/AEADAesGcmNativeEncryptor.cs b/shadowsocks-csharp/Encryption/AEAD/AEADAesGcmNativeEncryptor.cs index aa122ac9..c8366fe5 100644 --- a/shadowsocks-csharp/Encryption/AEAD/AEADAesGcmNativeEncryptor.cs +++ b/shadowsocks-csharp/Encryption/AEAD/AEADAesGcmNativeEncryptor.cs @@ -30,8 +30,7 @@ namespace Shadowsocks.Encryption.AEAD { base.InitCipher(salt, isEncrypt, isUdp); - DeriveSessionKey(isEncrypt ? encryptSalt : decryptSalt, - _Masterkey, sessionKey); + DeriveSessionKey(salt, masterKey, sessionKey); } public override void cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen) @@ -42,17 +41,8 @@ namespace Shadowsocks.Encryption.AEAD 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); + aes.Decrypt(nonce, 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) @@ -61,21 +51,10 @@ namespace Shadowsocks.Encryption.AEAD { byte[] tag = new byte[tagLen]; byte[] cipherWithoutTag = new byte[clen]; - aes.Encrypt(encNonce, plaintext, cipherWithoutTag, tag); + aes.Encrypt(nonce, 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 override byte[] CipherDecrypt2(byte[] cipher) @@ -84,19 +63,15 @@ namespace Shadowsocks.Encryption.AEAD Span plainMem = new Span(new byte[cipherMem.Length]); using var aes = new AesGcm(sessionKey); - aes.Decrypt(decNonce.AsSpan(), cipherMem.Span, tagMem.Span, plainMem); + aes.Decrypt(nonce.AsSpan(), cipherMem.Span, tagMem.Span, plainMem); return plainMem.ToArray(); } public override byte[] CipherEncrypt2(byte[] plain) { Span d = new Span(new byte[plain.Length + tagLen]); - - //Span cipherMem = new Span(new byte[plain.Length]); - //Span tagMem = new Span(new byte[tagLen]); - using var aes = new AesGcm(sessionKey); - aes.Encrypt(encNonce.AsSpan(), plain.AsSpan(), d.Slice(0, plain.Length), d.Slice(plain.Length)); + aes.Encrypt(nonce.AsSpan(), plain.AsSpan(), d.Slice(0, plain.Length), d.Slice(plain.Length)); return d.ToArray(); } diff --git a/shadowsocks-csharp/Encryption/AEAD/AEADBouncyCastleEncryptor.cs b/shadowsocks-csharp/Encryption/AEAD/AEADBouncyCastleEncryptor.cs index 0bebb026..537b2b1e 100644 --- a/shadowsocks-csharp/Encryption/AEAD/AEADBouncyCastleEncryptor.cs +++ b/shadowsocks-csharp/Encryption/AEAD/AEADBouncyCastleEncryptor.cs @@ -32,27 +32,23 @@ namespace Shadowsocks.Encryption.AEAD { base.InitCipher(salt, isEncrypt, isUdp); - DeriveSessionKey(isEncrypt ? encryptSalt : decryptSalt, - _Masterkey, sessionKey); + DeriveSessionKey(salt, 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, nonce); cipher.Init(false, parameters); - //var plaintextBC = new byte[cipher.GetOutputSize(ciphertext.Length)]; var len = cipher.ProcessBytes(ciphertext, 0, ciphertext.Length, plaintext, 0); cipher.DoFinal(plaintext, len); - //plen = (uint)(plaintext.Length); - //Array.Copy(plaintextBC, 0, plaintext, 0, plaintext.Length); } 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, nonce); cipher.Init(true, parameters); var ciphertextBC = new byte[cipher.GetOutputSize((int)plen)]; @@ -65,7 +61,7 @@ namespace Shadowsocks.Encryption.AEAD public override byte[] CipherDecrypt2(byte[] cipher) { var aes = new GcmBlockCipher(new AesEngine()); - AeadParameters parameters = new AeadParameters(new KeyParameter(sessionKey), tagLen * 8, decNonce); + AeadParameters parameters = new AeadParameters(new KeyParameter(sessionKey), tagLen * 8, nonce); aes.Init(false, parameters); byte[] plain = new byte[aes.GetOutputSize(cipher.Length)]; @@ -78,7 +74,7 @@ namespace Shadowsocks.Encryption.AEAD public override byte[] CipherEncrypt2(byte[] plain) { var aes = new GcmBlockCipher(new AesEngine()); - AeadParameters parameters = new AeadParameters(new KeyParameter(sessionKey), tagLen * 8, encNonce); + AeadParameters parameters = new AeadParameters(new KeyParameter(sessionKey), tagLen * 8, nonce); aes.Init(true, parameters); var cipher = new byte[aes.GetOutputSize(plain.Length)]; diff --git a/shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs b/shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs index 924dfa8e..42eb40a2 100644 --- a/shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs +++ b/shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs @@ -22,8 +22,8 @@ namespace Shadowsocks.Encryption.AEAD protected static byte[] _udpTmpBuf = new byte[65536]; // every connection should create its own buffer - private ByteCircularBuffer _encCircularBuffer = new ByteCircularBuffer(MAX_INPUT_SIZE * 2); - private ByteCircularBuffer _decCircularBuffer = new ByteCircularBuffer(MAX_INPUT_SIZE * 2); + private ByteCircularBuffer buffer = new ByteCircularBuffer(MAX_INPUT_SIZE * 2); + // private ByteCircularBuffer buffer = new ByteCircularBuffer(MAX_INPUT_SIZE * 2); public const int CHUNK_LEN_BYTES = 2; public const uint CHUNK_LEN_MASK = 0x3FFFu; @@ -32,29 +32,24 @@ namespace Shadowsocks.Encryption.AEAD protected string _method; protected CipherFamily _cipher; - // internal name in the crypto library - protected string _innerLibName; protected CipherInfo CipherInfo; - protected static byte[] _Masterkey = null; + protected static byte[] masterKey = null; protected byte[] sessionKey; protected int keyLen; protected int saltLen; protected int tagLen; protected int nonceLen; - protected byte[] encryptSalt; - protected byte[] decryptSalt; + protected byte[] salt; protected object _nonceIncrementLock = new object(); - protected byte[] encNonce; - protected byte[] decNonce; + protected byte[] nonce; // Is first packet - protected bool _decryptSaltReceived; - protected bool _encryptSaltSent; + protected bool saltReady; // Is first chunk(tcp request) - protected bool _tcpRequestSent; + protected bool tcpRequestSent; public AEADEncryptor(string method, string password) : base(method, password) @@ -62,8 +57,8 @@ namespace Shadowsocks.Encryption.AEAD InitEncryptorInfo(method); InitKey(password); // Initialize all-zero nonce for each connection - encNonce = new byte[nonceLen]; - decNonce = new byte[nonceLen]; + nonce = new byte[nonceLen]; + nonce = new byte[nonceLen]; } protected abstract Dictionary getCiphers(); @@ -86,9 +81,9 @@ namespace Shadowsocks.Encryption.AEAD { byte[] passbuf = Encoding.UTF8.GetBytes(password); // init master key - if (_Masterkey == null) _Masterkey = new byte[keyLen]; - if (_Masterkey.Length != keyLen) Array.Resize(ref _Masterkey, keyLen); - DeriveKey(passbuf, _Masterkey, keyLen); + if (masterKey == null) masterKey = new byte[keyLen]; + if (masterKey.Length != keyLen) Array.Resize(ref masterKey, keyLen); + DeriveKey(passbuf, masterKey, keyLen); // init session key if (sessionKey == null) sessionKey = new byte[keyLen]; } @@ -107,22 +102,14 @@ namespace Shadowsocks.Encryption.AEAD { lock (_nonceIncrementLock) { - CryptoUtils.SodiumIncrement(isEncrypt ? encNonce : decNonce); + CryptoUtils.SodiumIncrement(isEncrypt ? nonce : nonce); } } public virtual void InitCipher(byte[] salt, bool isEncrypt, bool isUdp) { - if (isEncrypt) - { - encryptSalt = new byte[saltLen]; - Array.Copy(salt, encryptSalt, saltLen); - } - else - { - decryptSalt = new byte[saltLen]; - Array.Copy(salt, decryptSalt, saltLen); - } + this.salt = new byte[saltLen]; + Array.Copy(salt, this.salt, saltLen); logger.Dump("Salt", salt, saltLen); } @@ -169,14 +156,14 @@ namespace Shadowsocks.Encryption.AEAD public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength) { - Debug.Assert(_encCircularBuffer != null, "_encCircularBuffer != null"); + Debug.Assert(buffer != null, "_encCircularBuffer != null"); - _encCircularBuffer.Put(buf, 0, length); + buffer.Put(buf, 0, length); outlength = 0; logger.Debug("---Start Encryption"); - if (!_encryptSaltSent) + if (!saltReady) { - _encryptSaltSent = true; + saltReady = true; // Generate salt byte[] saltBytes = RNG.GetBytes(saltLen); InitCipher(saltBytes, true, false); @@ -185,13 +172,13 @@ namespace Shadowsocks.Encryption.AEAD logger.Debug($"_encryptSaltSent outlength {outlength}"); } - if (!_tcpRequestSent) + if (!tcpRequestSent) { - _tcpRequestSent = true; + tcpRequestSent = true; // The first TCP request int encAddrBufLength; byte[] encAddrBufBytes = new byte[AddressBufferLength + tagLen * 2 + CHUNK_LEN_BYTES]; - byte[] addrBytes = _encCircularBuffer.Get(AddressBufferLength); + byte[] addrBytes = buffer.Get(AddressBufferLength); ChunkEncrypt(addrBytes, AddressBufferLength, encAddrBufBytes, out encAddrBufLength); Debug.Assert(encAddrBufLength == AddressBufferLength + tagLen * 2 + CHUNK_LEN_BYTES); Array.Copy(encAddrBufBytes, 0, outbuf, outlength, encAddrBufLength); @@ -202,10 +189,10 @@ namespace Shadowsocks.Encryption.AEAD // handle other chunks while (true) { - uint bufSize = (uint)_encCircularBuffer.Size; + uint bufSize = (uint)buffer.Size; if (bufSize <= 0) return; var chunklength = (int)Math.Min(bufSize, CHUNK_LEN_MASK); - byte[] chunkBytes = _encCircularBuffer.Get(chunklength); + byte[] chunkBytes = buffer.Get(chunklength); int encChunkLength; byte[] encChunkBytes = new byte[chunklength + tagLen * 2 + CHUNK_LEN_BYTES]; ChunkEncrypt(chunkBytes, chunklength, encChunkBytes, out encChunkLength); @@ -219,7 +206,7 @@ namespace Shadowsocks.Encryption.AEAD logger.Debug("enc outbuf almost full, giving up"); return; } - bufSize = (uint)_encCircularBuffer.Size; + bufSize = (uint)buffer.Size; if (bufSize <= 0) { logger.Debug("No more data to encrypt, leaving"); @@ -231,24 +218,24 @@ namespace Shadowsocks.Encryption.AEAD public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength) { - Debug.Assert(_decCircularBuffer != null, "_decCircularBuffer != null"); + Debug.Assert(buffer != null, "_decCircularBuffer != null"); int bufSize; outlength = 0; // drop all into buffer - _decCircularBuffer.Put(buf, 0, length); + buffer.Put(buf, 0, length); logger.Debug("---Start Decryption"); - if (!_decryptSaltReceived) + if (!saltReady) { - bufSize = _decCircularBuffer.Size; + bufSize = buffer.Size; // check if we get the leading salt if (bufSize <= saltLen) { // need more return; } - _decryptSaltReceived = true; - byte[] salt = _decCircularBuffer.Get(saltLen); + saltReady = true; + byte[] salt = buffer.Get(saltLen); InitCipher(salt, false, false); logger.Debug("get salt len " + saltLen); } @@ -256,7 +243,7 @@ namespace Shadowsocks.Encryption.AEAD // handle chunks while (true) { - bufSize = _decCircularBuffer.Size; + bufSize = buffer.Size; // check if we have any data if (bufSize <= 0) { @@ -273,7 +260,7 @@ namespace Shadowsocks.Encryption.AEAD #region Chunk Decryption - byte[] encLenBytes = _decCircularBuffer.Peek(CHUNK_LEN_BYTES + tagLen); + byte[] encLenBytes = buffer.Peek(CHUNK_LEN_BYTES + tagLen); //uint decChunkLenLength = 0; //byte[] decChunkLenBytes = new byte[CHUNK_LEN_BYTES]; // try to dec chunk len @@ -291,7 +278,7 @@ namespace Shadowsocks.Encryption.AEAD throw new CryptoErrorException(); } logger.Debug("Get the real chunk len:" + chunkLen); - bufSize = _decCircularBuffer.Size; + bufSize = buffer.Size; if (bufSize < CHUNK_LEN_BYTES + tagLen /* we haven't remove them */+ chunkLen + tagLen) { logger.Debug("No more data to decrypt one chunk"); @@ -301,8 +288,8 @@ namespace Shadowsocks.Encryption.AEAD // we have enough data to decrypt one chunk // drop chunk len and its tag from buffer - _decCircularBuffer.Skip(CHUNK_LEN_BYTES + tagLen); - byte[] encChunkBytes = _decCircularBuffer.Get(chunkLen + tagLen); + buffer.Skip(CHUNK_LEN_BYTES + tagLen); + byte[] encChunkBytes = buffer.Get(chunkLen + tagLen); //byte[] decChunkBytes = new byte[chunkLen]; //uint decChunkLen = 0; //cipherDecrypt(encChunkBytes, chunkLen + (uint)tagLen, decChunkBytes, ref decChunkLen); @@ -322,7 +309,7 @@ namespace Shadowsocks.Encryption.AEAD logger.Debug("dec outbuf almost full, giving up"); return; } - bufSize = _decCircularBuffer.Size; + bufSize = buffer.Size; // check if we already done all of them if (bufSize <= 0) { diff --git a/shadowsocks-csharp/Encryption/AEAD/AEADNaClEncryptor.cs b/shadowsocks-csharp/Encryption/AEAD/AEADNaClEncryptor.cs index 3f0ec5c7..f4e0ad22 100644 --- a/shadowsocks-csharp/Encryption/AEAD/AEADNaClEncryptor.cs +++ b/shadowsocks-csharp/Encryption/AEAD/AEADNaClEncryptor.cs @@ -12,7 +12,6 @@ namespace Shadowsocks.Encryption.AEAD { SnufflePoly1305 enc; - SnufflePoly1305 dec; public AEADNaClEncryptor(string method, string password) : base(method, password) { } @@ -20,34 +19,30 @@ namespace Shadowsocks.Encryption.AEAD public override void InitCipher(byte[] salt, bool isEncrypt, bool isUdp) { base.InitCipher(salt, isEncrypt, isUdp); - DeriveSessionKey(isEncrypt ? encryptSalt : decryptSalt, - _Masterkey, sessionKey); + DeriveSessionKey(salt, masterKey, sessionKey); - SnufflePoly1305 tmp; switch (_cipher) { default: case CipherFamily.Chacha20Poly1305: - tmp = new ChaCha20Poly1305(sessionKey); + enc = new ChaCha20Poly1305(sessionKey); break; case CipherFamily.XChacha20Poly1305: - tmp = new XChaCha20Poly1305(sessionKey); + enc = new XChaCha20Poly1305(sessionKey); break; } - if (isEncrypt) enc = tmp; - else dec = tmp; } public override void cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen) { - var pt = dec.Decrypt(ciphertext, null, decNonce); + var pt = enc.Decrypt(ciphertext, null, nonce); pt.CopyTo(plaintext, 0); plen = (uint)pt.Length; } public override void cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen) { - var ct = enc.Encrypt(plaintext, null, encNonce); + var ct = enc.Encrypt(plaintext, null, nonce); ct.CopyTo(ciphertext, 0); clen = (uint)ct.Length; } @@ -70,12 +65,12 @@ namespace Shadowsocks.Encryption.AEAD public override byte[] CipherEncrypt2(byte[] plain) { - return enc.Encrypt(plain, null, encNonce); + return enc.Encrypt(plain, null, nonce); } public override byte[] CipherDecrypt2(byte[] cipher) { - return dec.Decrypt(cipher, null, decNonce); + return enc.Decrypt(cipher, null, nonce); } } } diff --git a/shadowsocks-csharp/Encryption/Stream/StreamEncryptor.cs b/shadowsocks-csharp/Encryption/Stream/StreamEncryptor.cs index ee854812..18486ca4 100644 --- a/shadowsocks-csharp/Encryption/Stream/StreamEncryptor.cs +++ b/shadowsocks-csharp/Encryption/Stream/StreamEncryptor.cs @@ -13,25 +13,20 @@ namespace Shadowsocks.Encryption.Stream protected static byte[] _udpTmpBuf = new byte[65536]; // every connection should create its own buffer - private ByteCircularBuffer _encCircularBuffer = new ByteCircularBuffer(TCPHandler.BufferSize * 2); - private ByteCircularBuffer _decCircularBuffer = new ByteCircularBuffer(TCPHandler.BufferSize * 2); + private ByteCircularBuffer buffer = new ByteCircularBuffer(TCPHandler.BufferSize * 2); protected Dictionary ciphers; - protected byte[] _encryptIV; - protected byte[] _decryptIV; // Is first packet - protected bool _decryptIVReceived; - protected bool _encryptIVSent; + protected bool ivReady; protected string _method; protected CipherFamily _cipher; - // internal name in the crypto library - protected string _innerLibName; protected CipherInfo CipherInfo; // long-time master key - protected static byte[] _key = null; + protected static byte[] key = null; + protected byte[] iv; protected int keyLen; protected int ivLen; @@ -59,9 +54,9 @@ namespace Shadowsocks.Encryption.Stream private void InitKey(string password) { byte[] passbuf = Encoding.UTF8.GetBytes(password); - if (_key == null) _key = new byte[keyLen]; - if (_key.Length != keyLen) Array.Resize(ref _key, keyLen); - LegacyDeriveKey(passbuf, _key, keyLen); + key ??= new byte[keyLen]; + if (key.Length != keyLen) Array.Resize(ref key, keyLen); + LegacyDeriveKey(passbuf, key, keyLen); } public static void LegacyDeriveKey(byte[] password, byte[] key, int keylen) @@ -88,42 +83,36 @@ namespace Shadowsocks.Encryption.Stream protected virtual void initCipher(byte[] iv, bool isEncrypt) { - if (isEncrypt) - { - _encryptIV = new byte[ivLen]; - Array.Copy(iv, _encryptIV, ivLen); - } - else - { - _decryptIV = new byte[ivLen]; - Array.Copy(iv, _decryptIV, ivLen); - } + this.iv = new byte[ivLen]; + Array.Copy(iv, this.iv, ivLen); } protected abstract void cipherUpdate(bool isEncrypt, int length, byte[] buf, byte[] outbuf); - protected static void randBytes(byte[] buf, int length) { RNG.GetBytes(buf, length); } + protected abstract int CipherEncrypt(Span plain, Span cipher); + protected abstract int CipherDecrypt(Span plain, Span cipher); + + //protected static void randBytes(byte[] buf, int length) { RNG.GetBytes(buf, length); } #region TCP public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength) { int cipherOffset = 0; - Debug.Assert(_encCircularBuffer != null, "_encCircularBuffer != null"); - _encCircularBuffer.Put(buf, 0, length); - if (!_encryptIVSent) + Debug.Assert(buffer != null, "_encCircularBuffer != null"); + buffer.Put(buf, 0, length); + if (!ivReady) { // Generate IV - byte[] ivBytes = new byte[ivLen]; - randBytes(ivBytes, ivLen); + byte[] ivBytes = RNG.GetBytes(ivLen); initCipher(ivBytes, true); Array.Copy(ivBytes, 0, outbuf, 0, ivLen); cipherOffset = ivLen; - _encryptIVSent = true; + ivReady = true; } - int size = _encCircularBuffer.Size; - byte[] plain = _encCircularBuffer.Get(size); + int size = buffer.Size; + byte[] plain = buffer.Get(size); byte[] cipher = new byte[size]; cipherUpdate(true, size, plain, cipher); Buffer.BlockCopy(cipher, 0, outbuf, cipherOffset, size); @@ -132,29 +121,29 @@ namespace Shadowsocks.Encryption.Stream public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength) { - Debug.Assert(_decCircularBuffer != null, "_circularBuffer != null"); - _decCircularBuffer.Put(buf, 0, length); - if (!_decryptIVReceived) + Debug.Assert(buffer != null, "_circularBuffer != null"); + buffer.Put(buf, 0, length); + if (!ivReady) { - if (_decCircularBuffer.Size <= ivLen) + if (buffer.Size <= ivLen) { // we need more data outlength = 0; return; } // start decryption - _decryptIVReceived = true; + ivReady = true; if (ivLen > 0) { - byte[] iv = _decCircularBuffer.Get(ivLen); + byte[] iv = buffer.Get(ivLen); initCipher(iv, false); } else initCipher(Array.Empty(), false); } - byte[] cipher = _decCircularBuffer.ToArray(); + byte[] cipher = buffer.ToArray(); cipherUpdate(false, cipher.Length, cipher, outbuf); // move pointer only - _decCircularBuffer.Skip(_decCircularBuffer.Size); + buffer.Skip(buffer.Size); outlength = cipher.Length; // done the decryption } @@ -166,7 +155,7 @@ namespace Shadowsocks.Encryption.Stream public override void EncryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength) { // Generate IV - randBytes(outbuf, ivLen); + RNG.GetBytes(outbuf, ivLen); initCipher(outbuf, true); lock (_udpTmpBuf) { diff --git a/shadowsocks-csharp/Encryption/Stream/StreamRc4NativeEncryptor.cs b/shadowsocks-csharp/Encryption/Stream/StreamRc4NativeEncryptor.cs index fd0f8081..6ed35812 100644 --- a/shadowsocks-csharp/Encryption/Stream/StreamRc4NativeEncryptor.cs +++ b/shadowsocks-csharp/Encryption/Stream/StreamRc4NativeEncryptor.cs @@ -1,8 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Shadowsocks.Encryption.Stream { @@ -20,22 +17,19 @@ namespace Shadowsocks.Encryption.Stream if (_cipher == CipherFamily.Rc4Md5) { byte[] temp = new byte[keyLen + ivLen]; - Array.Copy(_key, 0, temp, 0, keyLen); + Array.Copy(key, 0, temp, 0, keyLen); Array.Copy(iv, 0, temp, keyLen, ivLen); realkey = CryptoUtils.MD5(temp); } else { - realkey = _key; + 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); @@ -43,6 +37,24 @@ namespace Shadowsocks.Encryption.Stream Array.Copy(t, outbuf, length); } + protected override int CipherEncrypt(Span plain, Span cipher) + { + return DoRC4(plain, cipher); + } + + protected override int CipherDecrypt(Span plain, Span cipher) + { + return DoRC4(cipher, plain); + } + + private int DoRC4(Span i, Span o) + { + i.CopyTo(o); + RC4(ctx, sbox, o, o.Length); + return o.Length; + } + + #region Ciphers private static readonly Dictionary _ciphers = new Dictionary { // original RC4 doesn't use IV @@ -59,6 +71,7 @@ namespace Shadowsocks.Encryption.Stream { return _ciphers; } + #endregion #region RC4 class Context @@ -67,8 +80,7 @@ namespace Shadowsocks.Encryption.Stream public int index2 = 0; } - private Context enc_ctx = new Context(); - private Context dec_ctx = new Context(); + private Context ctx = new Context(); private byte[] SBox(byte[] key) { @@ -89,7 +101,7 @@ namespace Shadowsocks.Encryption.Stream return s; } - private void RC4(Context ctx, byte[] s, byte[] data, int length) + private void RC4(Context ctx, Span s, Span data, int length) { for (int n = 0; n < length; n++) { @@ -99,12 +111,11 @@ namespace Shadowsocks.Encryption.Stream 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) + private static void Swap(Span s, int i, int j) { byte c = s[i]; diff --git a/shadowsocks-csharp/Encryption/Stream/StreamTableNativeEncryptor.cs b/shadowsocks-csharp/Encryption/Stream/StreamTableNativeEncryptor.cs index 097257cf..67fdafff 100644 --- a/shadowsocks-csharp/Encryption/Stream/StreamTableNativeEncryptor.cs +++ b/shadowsocks-csharp/Encryption/Stream/StreamTableNativeEncryptor.cs @@ -52,6 +52,37 @@ namespace Shadowsocks.Encryption.Stream } } + protected override int CipherDecrypt(Span plain, Span cipher) + { + if (_cipher == CipherFamily.Plain) + { + cipher.CopyTo(plain); + return cipher.Length; + } + + for (int i = 0; i < cipher.Length; i++) + { + plain[i] = _decryptTable[cipher[i]]; + } + return cipher.Length; + } + + protected override int CipherEncrypt(Span plain, Span cipher) + { + if (_cipher == CipherFamily.Plain) + { + plain.CopyTo(cipher); + return plain.Length; + } + + for (int i = 0; i < plain.Length; i++) + { + cipher[i] = _decryptTable[plain[i]]; + } + return plain.Length; + } + + #region Cipher Info private static readonly Dictionary _ciphers = new Dictionary { {"plain", new CipherInfo("plain", 0, 0, CipherFamily.Plain) }, @@ -67,6 +98,7 @@ namespace Shadowsocks.Encryption.Stream { return _ciphers; } + #endregion #region Table private byte[] _encryptTable = new byte[256];