diff --git a/Shadowsocks.Net/Crypto/CryptoFactory.cs b/Shadowsocks.Net/Crypto/CryptoFactory.cs index 413e5d14..5eb7b492 100644 --- a/Shadowsocks.Net/Crypto/CryptoFactory.cs +++ b/Shadowsocks.Net/Crypto/CryptoFactory.cs @@ -42,12 +42,12 @@ namespace Shadowsocks.Net.Crypto _registeredEncryptors.Add(method.Key, typeof(StreamAesCfbBouncyCastleCrypto)); } } - foreach (var method in StreamChachaNaClCrypto.SupportedCiphers()) + foreach (var method in StreamChachaBouncyCastleCrypto.SupportedCiphers()) { if (!_registeredEncryptors.ContainsKey(method.Key)) { ciphers.Add(method.Key, method.Value); - _registeredEncryptors.Add(method.Key, typeof(StreamChachaNaClCrypto)); + _registeredEncryptors.Add(method.Key, typeof(StreamChachaBouncyCastleCrypto)); } } diff --git a/Shadowsocks.Net/Crypto/Stream/StreamChachaBouncyCastleCrypto.cs b/Shadowsocks.Net/Crypto/Stream/StreamChachaBouncyCastleCrypto.cs new file mode 100644 index 00000000..83a0e650 --- /dev/null +++ b/Shadowsocks.Net/Crypto/Stream/StreamChachaBouncyCastleCrypto.cs @@ -0,0 +1,59 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; +using System; +using System.Collections.Generic; + +namespace Shadowsocks.Net.Crypto.Stream +{ + public class StreamChachaBouncyCastleCrypto : StreamCrypto + { + private readonly BufferedCipherBase _encryptor; + + public StreamChachaBouncyCastleCrypto(string method, string password) : base(method, password) + { + _encryptor = new BufferedStreamCipher(new ChaCha7539Engine()); + } + + protected override void InitCipher(byte[] iv, bool isEncrypt) + { + base.InitCipher(iv, isEncrypt); + _encryptor.Init(isEncrypt, new ParametersWithIV(new KeyParameter(key), iv)); + } + + protected override int CipherEncrypt(ReadOnlySpan plain, Span cipher) + { + return CipherUpdate(plain, cipher); + } + + protected override int CipherDecrypt(Span plain, ReadOnlySpan cipher) + { + return CipherUpdate(cipher, plain); + } + + protected virtual int CipherUpdate(ReadOnlySpan input, Span output) + { + var i = input.ToArray(); + var o = new byte[_encryptor.GetOutputSize(i.Length)]; + var res = _encryptor.ProcessBytes(i, 0, i.Length, o, 0); + o.CopyTo(output); + return res; + } + + #region Cipher Info + private static readonly Dictionary _ciphers = new Dictionary + { + { "chacha20-ietf", new CipherInfo("chacha20-ietf", 32, 12, CipherFamily.Chacha20) }, + }; + public static Dictionary SupportedCiphers() + { + return _ciphers; + } + + protected override Dictionary GetCiphers() + { + return _ciphers; + } + #endregion + } +} diff --git a/Shadowsocks.Net/Crypto/Stream/StreamChachaNaClCrypto.cs b/Shadowsocks.Net/Crypto/Stream/StreamChachaNaClCrypto.cs deleted file mode 100644 index df65762d..00000000 --- a/Shadowsocks.Net/Crypto/Stream/StreamChachaNaClCrypto.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using NaCl.Core; - -namespace Shadowsocks.Net.Crypto.Stream -{ - public class StreamChachaNaClCrypto : StreamCrypto - { - const int BlockSize = 64; - - // tcp is stream, which can split into chunks at unexpected position... - // so we need some special handling, as we can't read all data before encrypt - - // we did it in AEADEncryptor.cs for AEAD, it can operate at block level - // but we need do it ourselves in stream cipher. - - // when new data arrive, put it on correct offset - // and update it, ignore other data, get it in correct offset... - readonly byte[] chachaBuf = new byte[MaxInputSize + BlockSize]; - // the 'correct offset', always in 0~BlockSize range, so input data always fit into buffer - int remain = 0; - // increase counter manually... - int ic = 0; - public StreamChachaNaClCrypto(string method, string password) : base(method, password) - { - } - - protected override int CipherDecrypt(Span plain, ReadOnlySpan cipher) - { - return CipherUpdate(cipher, plain, false); - } - - protected override int CipherEncrypt(ReadOnlySpan plain, Span cipher) - { - return CipherUpdate(plain, cipher, true); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int CipherUpdate(ReadOnlySpan i, Span o, bool enc) - { - // about performance problem: - // as a part of hacking for streaming, we need manually increase IC - // so we need new Chacha20 - // and to get correct position, copy paste array everytime is required - // NaCl.Core has no int Encrypt(ReadOnlySpan,Span)... - - int len = i.Length; - int pad = remain; - i.CopyTo(chachaBuf.AsSpan(pad)); - var chacha = new ChaCha20(key, ic); - var p = enc ? chacha.Encrypt(chachaBuf, iv) : chacha.Decrypt(chachaBuf, iv); - p.AsSpan(pad, len).CopyTo(o); - pad += len; - ic += pad / BlockSize; - remain = pad % BlockSize; - return len; - } - - #region Cipher Info - private static readonly Dictionary _ciphers = new Dictionary - { - { "chacha20-ietf", new CipherInfo("chacha20-ietf", 32, 12, CipherFamily.Chacha20) }, - }; - public static Dictionary SupportedCiphers() - { - return _ciphers; - } - - protected override Dictionary GetCiphers() - { - return _ciphers; - } - #endregion - } -} diff --git a/Shadowsocks.Net/Shadowsocks.Net.csproj b/Shadowsocks.Net/Shadowsocks.Net.csproj index cc75b0b7..dbca59da 100644 --- a/Shadowsocks.Net/Shadowsocks.Net.csproj +++ b/Shadowsocks.Net/Shadowsocks.Net.csproj @@ -6,7 +6,6 @@ -