diff --git a/shadowsocks-csharp/Encryption/Stream/StreamChachaNaClEncryptor.cs b/shadowsocks-csharp/Encryption/Stream/StreamChachaNaClEncryptor.cs index da6969b7..ad78627b 100644 --- a/shadowsocks-csharp/Encryption/Stream/StreamChachaNaClEncryptor.cs +++ b/shadowsocks-csharp/Encryption/Stream/StreamChachaNaClEncryptor.cs @@ -6,30 +6,38 @@ namespace Shadowsocks.Encryption.Stream { public class StreamChachaNaClEncryptor : StreamEncryptor { - // yes, they update over all saved data everytime... - byte[] chachaBuf = new byte[65536]; - int ptr = 0; + const int BlockSize = 64; + // when new data arrive, put it in correct offset of chunk + // and update it, ignore other data, get it in correct offset... + byte[] chachaBuf = new byte[32768 + BlockSize]; + int remain = 0; + // increase counter only when a chunk fully recieved + int ic = 0; public StreamChachaNaClEncryptor(string method, string password) : base(method, password) { } protected override int CipherDecrypt(Span plain, Span cipher) { - int len = cipher.Length; - cipher.CopyTo(chachaBuf.AsSpan(ptr)); - var p = new ChaCha20(key, 0).Decrypt(chachaBuf, iv); - p.AsSpan(ptr, len).CopyTo(plain); - ptr += len; - return len; + return CipherUpdate(cipher, plain, false); } protected override int CipherEncrypt(Span plain, Span cipher) { - int len = plain.Length; - plain.CopyTo(chachaBuf.AsSpan(ptr)); - var p = new ChaCha20(key, 0).Encrypt(chachaBuf, iv); - p.AsSpan(ptr, len).CopyTo(cipher); - ptr += len; + return CipherUpdate(plain, cipher, true); + } + + private int CipherUpdate(Span i, Span o, bool enc) + { + 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; }