From bf56f40292f7de3056841e400ef1e5ba7a4d651b Mon Sep 17 00:00:00 2001 From: Student Main Date: Sat, 28 Mar 2020 15:55:19 +0800 Subject: [PATCH] remove loop buffer from aead encryptor --- .../Encryption/AEAD/AEADEncryptor.cs | 86 +-- .../CircularBuffer/ByteCircularBuffer.cs | 510 ------------------ .../Encryption/Stream/StreamEncryptor.cs | 2 - test/CircularBufferTest.cs | 47 -- test/CryptographyTest.cs | 90 ++-- 5 files changed, 104 insertions(+), 631 deletions(-) delete mode 100644 shadowsocks-csharp/Encryption/CircularBuffer/ByteCircularBuffer.cs delete mode 100644 test/CircularBufferTest.cs diff --git a/shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs b/shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs index 78781182..f53f3340 100644 --- a/shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs +++ b/shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs @@ -1,10 +1,8 @@ using NLog; using System; using System.Collections.Generic; -using System.Diagnostics; using System.Net; using System.Text; -using Shadowsocks.Encryption.CircularBuffer; using Shadowsocks.Controller; using Shadowsocks.Encryption.Exception; using Shadowsocks.Encryption.Stream; @@ -19,7 +17,8 @@ namespace Shadowsocks.Encryption.AEAD private static readonly byte[] InfoBytes = Encoding.ASCII.GetBytes(Info); // every connection should create its own buffer - private ByteCircularBuffer buffer = new ByteCircularBuffer(MaxInputSize * 2); + private byte[] sharedBuffer = new byte[65536]; + private int bufPtr = 0; public const int ChunkLengthBytes = 2; public const uint ChunkLengthMask = 0x3FFFu; @@ -104,9 +103,10 @@ namespace Shadowsocks.Encryption.AEAD public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength) { - Debug.Assert(buffer != null, "_encCircularBuffer != null"); + // push data + buf.CopyTo(sharedBuffer, bufPtr); + Span tmp = sharedBuffer.AsSpan(0, length + bufPtr); - buffer.Put(buf, 0, length); outlength = 0; logger.Debug("---Start Encryption"); if (!saltReady) @@ -125,10 +125,13 @@ namespace Shadowsocks.Encryption.AEAD tcpRequestSent = true; // The first TCP request byte[] encAddrBufBytes = new byte[AddressBufferLength + tagLen * 2 + ChunkLengthBytes]; - byte[] addrBytes = buffer.Get(AddressBufferLength); + + // read addr byte to encrypt + byte[] addrBytes = tmp.Slice(0, AddressBufferLength).ToArray(); + tmp = tmp.Slice(AddressBufferLength); + int encAddrBufLength = ChunkEncrypt(addrBytes, encAddrBufBytes); - // ChunkEncrypt(addrBytes, AddressBufferLength, encAddrBufBytes, out encAddrBufLength); - Debug.Assert(encAddrBufLength == AddressBufferLength + tagLen * 2 + ChunkLengthBytes); + Array.Copy(encAddrBufBytes, 0, outbuf, outlength, encAddrBufLength); outlength += encAddrBufLength; logger.Debug($"_tcpRequestSent outlength {outlength}"); @@ -137,24 +140,34 @@ namespace Shadowsocks.Encryption.AEAD // handle other chunks while (true) { - uint bufSize = (uint)buffer.Size; + // calculate next chunk size + int bufSize = tmp.Length; if (bufSize <= 0) return; var chunklength = (int)Math.Min(bufSize, ChunkLengthMask); - byte[] chunkBytes = buffer.Get(chunklength); + // read next chunk + byte[] chunkBytes = tmp.Slice(0, chunklength).ToArray(); + tmp = tmp.Slice(chunklength); + byte[] encChunkBytes = new byte[chunklength + tagLen * 2 + ChunkLengthBytes]; int encChunkLength = ChunkEncrypt(chunkBytes, encChunkBytes); - // ChunkEncrypt(chunkBytes, chunklength, encChunkBytes, out encChunkLength); - Debug.Assert(encChunkLength == chunklength + tagLen * 2 + ChunkLengthBytes); + Buffer.BlockCopy(encChunkBytes, 0, outbuf, outlength, encChunkLength); outlength += encChunkLength; logger.Debug("chunks enc outlength " + outlength); // check if we have enough space for outbuf + // if not, keep buf for next run, at this condition, buffer is not empty if (outlength + TCPHandler.ChunkOverheadSize > TCPHandler.BufferSize) { logger.Debug("enc outbuf almost full, giving up"); + + // write rest data to head of shared buffer + tmp.CopyTo(sharedBuffer); + bufPtr = tmp.Length; + return; } - bufSize = (uint)buffer.Size; + // check if buffer empty + bufSize = tmp.Length; if (bufSize <= 0) { logger.Debug("No more data to encrypt, leaving"); @@ -166,24 +179,29 @@ namespace Shadowsocks.Encryption.AEAD public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength) { - Debug.Assert(buffer != null, "_decCircularBuffer != null"); - int bufSize; outlength = 0; // drop all into buffer - buffer.Put(buf, 0, length); + buf.CopyTo(sharedBuffer, bufPtr); + Span tmp = buf.AsSpan(0, length + bufPtr); + int bufSize = tmp.Length; logger.Debug("---Start Decryption"); if (!saltReady) { - bufSize = buffer.Size; // check if we get the leading salt if (bufSize <= saltLen) { - // need more + // need more, write back cache + tmp.CopyTo(sharedBuffer); + bufPtr = tmp.Length; return; } saltReady = true; - byte[] salt = buffer.Get(saltLen); + + // buffer.Get(saltLen); + byte[] salt = tmp.Slice(0, saltLen).ToArray(); + tmp = tmp.Slice(saltLen); + InitCipher(salt, false, false); logger.Debug("get salt len " + saltLen); } @@ -191,7 +209,7 @@ namespace Shadowsocks.Encryption.AEAD // handle chunks while (true) { - bufSize = buffer.Size; + bufSize = tmp.Length; // check if we have any data if (bufSize <= 0) { @@ -203,12 +221,16 @@ namespace Shadowsocks.Encryption.AEAD if (bufSize <= ChunkLengthBytes + tagLen) { // so we only have chunk length and its tag? + // wait more + tmp.CopyTo(sharedBuffer); + bufPtr = tmp.Length; return; } #region Chunk Decryption - byte[] encLenBytes = buffer.Peek(ChunkLengthBytes + tagLen); + // byte[] encLenBytes = buffer.Peek(ChunkLengthBytes + tagLen); + byte[] encLenBytes = tmp.Slice(0, ChunkLengthBytes + tagLen).ToArray(); // try to dec chunk len byte[] decChunkLenBytes = new byte[ChunkLengthBytes]; @@ -221,36 +243,40 @@ namespace Shadowsocks.Encryption.AEAD throw new CryptoErrorException(); } logger.Debug("Get the real chunk len:" + chunkLen); - bufSize = buffer.Size; + bufSize = tmp.Length; if (bufSize < ChunkLengthBytes + tagLen /* we haven't remove them */+ chunkLen + tagLen) { logger.Debug("No more data to decrypt one chunk"); + // write back length data + tmp.CopyTo(sharedBuffer); + bufPtr = tmp.Length; return; } IncrementNonce(); - // we have enough data to decrypt one chunk // drop chunk len and its tag from buffer - buffer.Skip(ChunkLengthBytes + tagLen); - byte[] encChunkBytes = buffer.Get(chunkLen + tagLen); - // byte[] decChunkBytes = CipherDecrypt2(encChunkBytes); + // buffer.Skip(ChunkLengthBytes + tagLen); + tmp = tmp.Slice(ChunkLengthBytes + tagLen); + // byte[] encChunkBytes = buffer.Get(chunkLen + tagLen); + byte[] encChunkBytes = tmp.Slice(0, chunkLen + tagLen).ToArray(); + tmp = tmp.Slice(chunkLen + tagLen); - int len = CipherDecrypt(outbuf.AsSpan().Slice(outlength), encChunkBytes); + int len = CipherDecrypt(outbuf.AsSpan(outlength), encChunkBytes); IncrementNonce(); #endregion // output to outbuf - // decChunkBytes.CopyTo(outbuf, outlength); - // Buffer.BlockCopy(decChunkBytes, 0, outbuf, outlength, (int)decChunkLen); outlength += len; logger.Debug("aead dec outlength " + outlength); if (outlength + 100 > TCPHandler.BufferSize) { logger.Debug("dec outbuf almost full, giving up"); + tmp.CopyTo(sharedBuffer); + bufPtr = tmp.Length; return; } - bufSize = buffer.Size; + bufSize = tmp.Length; // check if we already done all of them if (bufSize <= 0) { diff --git a/shadowsocks-csharp/Encryption/CircularBuffer/ByteCircularBuffer.cs b/shadowsocks-csharp/Encryption/CircularBuffer/ByteCircularBuffer.cs deleted file mode 100644 index 61ec2dda..00000000 --- a/shadowsocks-csharp/Encryption/CircularBuffer/ByteCircularBuffer.cs +++ /dev/null @@ -1,510 +0,0 @@ -#region Original License - -//New BSD License(BSD) -// -//Copyright(c) 2014-2015 Cyotek Ltd -//Copyright(c) 2012, Alex Regueiro -//All rights reserved. -// -//Redistribution and use in source and binary forms, with or without -//modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of Cyotek nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -//WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -//DISCLAIMED.IN NO EVENT SHALL BE LIABLE FOR ANY -//DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -//(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -//ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -//(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -//SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#endregion - -using System; -using System.Collections.Generic; - -namespace Shadowsocks.Encryption.CircularBuffer -{ - /// - /// Represents a first-in, first-out collection of objects using a fixed buffer. - /// - /// - /// The capacity of a is the number of elements the can hold. - /// ByteCircularBuffer accepts null as a valid value for reference types and allows duplicate elements. - /// The methods will remove the items that are returned from the ByteCircularBuffer. To view the contents of the ByteCircularBuffer without removing items, use the or methods. - /// - public class ByteCircularBuffer - { - // based on http://circularbuffer.codeplex.com/ - // http://en.wikipedia.org/wiki/Circular_buffer - // modified from https://github.com/cyotek/Cyotek.Collections.Generic.CircularBuffer - // some code taken from https://github.com/xorxornop/RingBuffer - // and https://github.com/xorxornop/PerfCopy - - #region Instance Fields - - private byte[] _buffer; - - private int _capacity; - - #endregion - - #region Public Constructors - - /// - /// Initializes a new instance of the class that is empty and has the specified initial capacity. - /// - /// The maximum capcity of the buffer. - /// Thown if the is less than zero. - public ByteCircularBuffer(int capacity) - { - if (capacity < 0) - { - throw new ArgumentException("The buffer capacity must be greater than or equal to zero.", - nameof(capacity)); - } - - _buffer = new byte[capacity]; - this.Capacity = capacity; - this.Size = 0; - this.Head = 0; - this.Tail = 0; - } - - #endregion - - #region Public Properties - - /// - /// Gets or sets the total number of elements the internal data structure can hold. - /// - /// The total number of elements that the can contain. - /// Thrown if the specified new capacity is smaller than the current contents of the buffer. - public int Capacity - { - get { return _capacity; } - set - { - if (value != _capacity) - { - if (value < this.Size) - { - throw new ArgumentOutOfRangeException(nameof(value), value, - "The new capacity must be greater than or equal to the buffer size."); - } - - var newBuffer = new byte[value]; - if (this.Size > 0) - { - this.CopyTo(newBuffer); - } - - _buffer = newBuffer; - - _capacity = value; - } - } - } - - /// - /// Gets the index of the beginning of the buffer data. - /// - /// The index of the first element in the buffer. - public int Head { get; protected set; } - - /// - /// Gets a value indicating whether the buffer is empty. - /// - /// true if buffer is empty; otherwise, false. - public virtual bool IsEmpty => this.Size == 0; - - /// - /// Gets a value indicating whether the buffer is full. - /// - /// true if the buffer is full; otherwise, false. - /// The property always returns false if the property is set to true. - public virtual bool IsFull => this.Size == this.Capacity; - - /// - /// Gets the number of elements contained in the . - /// - /// The number of elements contained in the . - public int Size { get; protected set; } - - /// - /// Gets the index of the end of the buffer data. - /// - /// The index of the last element in the buffer. - public int Tail { get; protected set; } - - #endregion - - #region Public Members - - /// - /// Removes all items from the . - /// - public void Clear() - { - this.Size = 0; - this.Head = 0; - this.Tail = 0; - _buffer = new byte[this.Capacity]; - } - - /// - /// Determines whether the contains a specific value. - /// - /// The object to locate in the . - /// true if is found in the ; otherwise, false. - public bool Contains(byte item) - { - var bufferIndex = this.Head; - var comparer = EqualityComparer.Default; - var result = false; - - for (int i = 0; i < this.Size; i++, bufferIndex++) - { - if (bufferIndex == this.Capacity) - { - bufferIndex = 0; - } - - if (comparer.Equals(_buffer[bufferIndex], item)) - { - result = true; - break; - } - } - - return result; - } - - /// - /// Copies the entire to a compatible one-dimensional array, starting at the beginning of the target array. - /// - /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. - public void CopyTo(byte[] array) - { - this.CopyTo(array, 0); - } - - /// - /// Copies the entire to a compatible one-dimensional array, starting at the specified index of the target array. - /// - /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. - /// The zero-based index in at which copying begins. - public void CopyTo(byte[] array, int arrayIndex) - { - this.CopyTo(this.Head, array, arrayIndex, Math.Min(this.Size, array.Length - arrayIndex)); - } - - /// - /// Copies a range of elements from the to a compatible one-dimensional array, starting at the specified index of the target array. - /// - /// The zero-based index in the source at which copying begins. - /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. - /// The zero-based index in at which copying begins. - /// The number of elements to copy. - public virtual void CopyTo(int index, byte[] array, int arrayIndex, int count) - { - if (count > this.Size) - { - throw new ArgumentOutOfRangeException(nameof(count), count, - "The read count cannot be greater than the buffer size."); - } - - var startAnchor = index; - var dstIndex = arrayIndex; - - while (count > 0) - { - int chunk = Math.Min(Capacity - startAnchor, count); - Buffer.BlockCopy(_buffer, startAnchor, array, dstIndex, chunk); - startAnchor = (startAnchor + chunk == Capacity) ? 0 : startAnchor + chunk; - dstIndex += chunk; - count -= chunk; - } - } - - /// - /// Removes and returns the specified number of objects from the beginning of the . - /// - /// The number of elements to remove and return from the . - /// The objects that are removed from the beginning of the . - public byte[] Get(int count) - { - if (count <= 0) throw new ArgumentOutOfRangeException("should greater than 0"); - var result = new byte[count]; - - this.Get(result); - - return result; - } - - /// - /// Copies and removes the specified number elements from the to a compatible one-dimensional array, starting at the beginning of the target array. - /// - /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. - /// The actual number of elements copied into . - public int Get(byte[] array) - { - if (array.Length <= 0) throw new ArgumentOutOfRangeException("should greater than 0"); - return this.Get(array, 0, array.Length); - } - - /// - /// Copies and removes the specified number elements from the to a compatible one-dimensional array, starting at the specified index of the target array. - /// - /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. - /// The zero-based index in at which copying begins. - /// The number of elements to copy. - /// The actual number of elements copied into . - public virtual int Get(byte[] array, int arrayIndex, int count) - { - if (arrayIndex < 0) - { - throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Negative offset specified. Offsets must be positive."); - } - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count), "Negative count specified. Count must be positive."); - } - if (count > this.Size) - { - throw new ArgumentException("Ringbuffer contents insufficient for take/read operation.", nameof(count)); - } - if (array.Length < arrayIndex + count) - { - throw new ArgumentException("Destination array too small for requested output."); - } - var bytesCopied = 0; - var dstIndex = arrayIndex; - while (count > 0) - { - int chunk = Math.Min(Capacity - this.Head, count); - Buffer.BlockCopy(_buffer, this.Head, array, dstIndex, chunk); - this.Head = (this.Head + chunk == Capacity) ? 0 : this.Head + chunk; - this.Size -= chunk; - dstIndex += chunk; - bytesCopied += chunk; - count -= chunk; - } - return bytesCopied; - } - - /// - /// Removes and returns the object at the beginning of the . - /// - /// The object that is removed from the beginning of the . - /// Thrown if the buffer is empty. - /// This method is similar to the method, but Peek does not modify the . - public virtual byte Get() - { - if (this.IsEmpty) - { - throw new InvalidOperationException("The buffer is empty."); - } - - var item = _buffer[this.Head]; - if (++this.Head == this.Capacity) - { - this.Head = 0; - } - this.Size--; - - return item; - } - - /// - /// Returns the object at the beginning of the without removing it. - /// - /// The object at the beginning of the . - /// Thrown if the buffer is empty. - public virtual byte Peek() - { - if (this.IsEmpty) - { - throw new InvalidOperationException("The buffer is empty."); - } - - var item = _buffer[this.Head]; - - return item; - } - - /// - /// Returns the specified number of objects from the beginning of the . - /// - /// The number of elements to return from the . - /// The objects that from the beginning of the . - /// Thrown if the buffer is empty. - public virtual byte[] Peek(int count) - { - if (this.IsEmpty) - { - throw new InvalidOperationException("The buffer is empty."); - } - - var items = new byte[count]; - this.CopyTo(items); - - return items; - } - - /// - /// Returns the object at the end of the without removing it. - /// - /// The object at the end of the . - /// Thrown if the buffer is empty. - public virtual byte PeekLast() - { - int bufferIndex; - - if (this.IsEmpty) - { - throw new InvalidOperationException("The buffer is empty."); - } - - if (this.Tail == 0) - { - bufferIndex = this.Size - 1; - } - else - { - bufferIndex = this.Tail - 1; - } - - var item = _buffer[bufferIndex]; - - return item; - } - - /// - /// Copies an entire compatible one-dimensional array to the . - /// - /// The one-dimensional that is the source of the elements copied to . The must have zero-based indexing. - /// Thrown if buffer does not have sufficient capacity to put in new items. - /// If plus the size of exceeds the capacity of the and the property is true, the oldest items in the are overwritten with . - public int Put(byte[] array) - { - return this.Put(array, 0, array.Length); - } - - /// - /// Copies a range of elements from a compatible one-dimensional array to the . - /// - /// The one-dimensional that is the source of the elements copied to . The must have zero-based indexing. - /// The zero-based index in at which copying begins. - /// The number of elements to copy. - /// Thrown if buffer does not have sufficient capacity to put in new items. - /// If plus exceeds the capacity of the and the property is true, the oldest items in the are overwritten with . - public virtual int Put(byte[] array, int arrayIndex, int count) - { - if (count <= 0) throw new ArgumentOutOfRangeException(nameof(count), "Count must be positive."); - if (this.Size + count > this.Capacity) - { - throw new InvalidOperationException("The buffer does not have sufficient capacity to put new items."); - } - - if (array.Length < arrayIndex + count) - { - throw new ArgumentException("Source array too small for requested input."); - } - var srcIndex = arrayIndex; - var bytesToProcess = count; - while (bytesToProcess > 0) - { - int chunk = Math.Min(Capacity - Tail, bytesToProcess); - Buffer.BlockCopy(array, srcIndex, _buffer, Tail, chunk); - Tail = (Tail + chunk == Capacity) ? 0 : Tail + chunk; - this.Size += chunk; - srcIndex += chunk; - bytesToProcess -= chunk; - } - - return count; - } - - /// - /// Adds a byte to the end of the . - /// - /// The byte to add to the . - /// Thrown if buffer does not have sufficient capacity to put in new items. - public virtual void Put(byte item) - { - if (IsFull) - { - throw new InvalidOperationException("The buffer does not have sufficient capacity to put new items."); - } - - _buffer[this.Tail] = item; - - this.Tail++; - if (this.Size == this.Capacity) - { - this.Head++; - if (this.Head >= this.Capacity) - { - this.Head -= this.Capacity; - } - } - - if (this.Tail == this.Capacity) - { - this.Tail = 0; - } - - if (this.Size != this.Capacity) - { - this.Size++; - } - } - - /// - /// Increments the starting index of the data buffer in the . - /// - /// The number of elements to increment the data buffer start index by. - public void Skip(int count) - { - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count), "Negative count specified. Count must be positive."); - } - if (count > this.Size) - { - throw new ArgumentException("Ringbuffer contents insufficient for operation.", nameof(count)); - } - - // Modular division gives new offset position - this.Head = (this.Head + count) % Capacity; - this.Size -= count; - } - - /// - /// Copies the elements to a new array. - /// - /// A new array containing elements copied from the . - /// The is not modified. The order of the elements in the new array is the same as the order of the elements from the beginning of the to its end. - public byte[] ToArray() - { - var result = new byte[this.Size]; - - this.CopyTo(result); - - return result; - } - - #endregion - } -} \ No newline at end of file diff --git a/shadowsocks-csharp/Encryption/Stream/StreamEncryptor.cs b/shadowsocks-csharp/Encryption/Stream/StreamEncryptor.cs index 5a812593..e03c5860 100644 --- a/shadowsocks-csharp/Encryption/Stream/StreamEncryptor.cs +++ b/shadowsocks-csharp/Encryption/Stream/StreamEncryptor.cs @@ -1,6 +1,4 @@ using NLog; -using Shadowsocks.Controller; -using Shadowsocks.Encryption.CircularBuffer; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; diff --git a/test/CircularBufferTest.cs b/test/CircularBufferTest.cs deleted file mode 100644 index ef000610..00000000 --- a/test/CircularBufferTest.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Shadowsocks.Encryption.CircularBuffer; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Shadowsocks.Test -{ - [TestClass] - public class CircularBufferTest - { - void ArrayEqual(IEnumerable expected, IEnumerable actual) - { - var l = expected.GetEnumerator(); - var r = actual.GetEnumerator(); - int p = 0; - while (l.MoveNext() && r.MoveNext()) - { - Assert.AreEqual(l.Current, r.Current, $"not equal at {p}"); - p++; - } - if (l.MoveNext()) Assert.Fail("expected longer than actual"); - else if (r.MoveNext()) Assert.Fail("expected shorter than actual"); - } - [TestMethod] - public void GetPut() - { - var c = new ByteCircularBuffer(8); - c.Put(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }); - Assert.AreEqual(c.Head, 0); - Assert.AreEqual(c.Tail, 0); - c.Get(4); - Assert.AreEqual(c.Head, 4); - Assert.AreEqual(c.Tail, 0); - Assert.AreEqual(c.Get(), 5); - c.Put(new byte[] { 1, 2, 3, 4, 5 }); - Assert.AreEqual(c.Head, 5); - Assert.AreEqual(c.Tail, 5); - var content = c.Get(8); - ArrayEqual(content, new byte[] { 6, 7, 8, 1, 2, 3, 4, 5 }); - c.Put(10); - var b = new byte[1]; - c.Get(b); - Assert.AreEqual(b[0], 10); - } - } -} diff --git a/test/CryptographyTest.cs b/test/CryptographyTest.cs index c57623a1..6b8dc48c 100644 --- a/test/CryptographyTest.cs +++ b/test/CryptographyTest.cs @@ -13,34 +13,6 @@ namespace Shadowsocks.Test { Random random = new Random(); - private void ArrayEqual(IEnumerable expected, IEnumerable actual, string msg = "") - { - var e1 = expected.GetEnumerator(); - var e2 = actual.GetEnumerator(); - int ctr = 0; - while (true) - { - var e1next = e1.MoveNext(); - var e2next = e2.MoveNext(); - - if (e1next && e2next) - { - Assert.AreEqual(e1.Current, e2.Current, "at " + ctr); - } - else if (!e1next && !e2next) - { - return; - } - else if (!e1next) - { - Assert.Fail($"actual longer than expected ({ctr}) {msg}"); - } - else - { - Assert.Fail($"actual shorter than expected ({ctr}) {msg}"); - } - } - } [TestMethod] public void TestMD5() @@ -56,6 +28,7 @@ namespace Shadowsocks.Test } } + #region Encryptor test tools private void SingleEncryptionTestCase(IEncryptor encryptor, IEncryptor decryptor, int length) { RNG.Reload(); @@ -70,14 +43,6 @@ namespace Shadowsocks.Test ArrayEqual(plain.AsSpan(0, length).ToArray(), plain2.AsSpan(0, length).ToArray()); } - private void RunEncryptionRound(IEncryptor encryptor, IEncryptor decryptor) - { - SingleEncryptionTestCase(encryptor, decryptor, 16384); - SingleEncryptionTestCase(encryptor, decryptor, 7); - SingleEncryptionTestCase(encryptor, decryptor, 1000); - SingleEncryptionTestCase(encryptor, decryptor, 12333); - } - const string password = "barfoo!"; private void RunSingleEncryptionThread(Type enc, Type dec, string method) @@ -104,6 +69,35 @@ namespace Shadowsocks.Test } } + private void ArrayEqual(IEnumerable expected, IEnumerable actual, string msg = "") + { + var e1 = expected.GetEnumerator(); + var e2 = actual.GetEnumerator(); + int ctr = 0; + while (true) + { + var e1next = e1.MoveNext(); + var e2next = e2.MoveNext(); + + if (e1next && e2next) + { + Assert.AreEqual(e1.Current, e2.Current, "at " + ctr); + } + else if (!e1next && !e2next) + { + return; + } + else if (!e1next) + { + Assert.Fail($"actual longer than expected ({ctr}) {msg}"); + } + else + { + Assert.Fail($"actual shorter than expected ({ctr}) {msg}"); + } + } + } + private static bool encryptionFailed = false; private void TestEncryptionMethod(Type enc, string method) @@ -128,9 +122,19 @@ namespace Shadowsocks.Test } Assert.IsFalse(encryptionFailed); } - + #endregion + + // encryption test cases + private void RunEncryptionRound(IEncryptor encryptor, IEncryptor decryptor) + { + SingleEncryptionTestCase(encryptor, decryptor, 16384); + SingleEncryptionTestCase(encryptor, decryptor, 7); // for not aligned data + SingleEncryptionTestCase(encryptor, decryptor, 1000); + SingleEncryptionTestCase(encryptor, decryptor, 12333); + } + [TestMethod] - public void TestAesGcmNativeAEADEncryption() + public void TestAEADAesGcmNativeEncryption() { TestEncryptionMethod(typeof(AEADAesGcmNativeEncryptor), "aes-128-gcm"); TestEncryptionMethod(typeof(AEADAesGcmNativeEncryptor), "aes-192-gcm"); @@ -138,23 +142,25 @@ namespace Shadowsocks.Test } [TestMethod] - public void TestNaClAEADEncryption() + public void TestAEADNaClEncryption() { TestEncryptionMethod(typeof(AEADNaClEncryptor), "chacha20-ietf-poly1305"); TestEncryptionMethod(typeof(AEADNaClEncryptor), "xchacha20-ietf-poly1305"); } [TestMethod] - public void TestNativeEncryption() + public void TestStreamNativeEncryption() { TestEncryptionMethod(typeof(StreamPlainNativeEncryptor), "plain"); - // TestEncryptionMethod(typeof(StreamRc4NativeEncryptor), "rc4"); + TestEncryptionMethod(typeof(StreamRc4NativeEncryptor), "rc4"); TestEncryptionMethod(typeof(StreamRc4NativeEncryptor), "rc4-md5"); } [TestMethod] - public void TestStreamAesBouncyCastleEncryption() + public void TestStreamAesCfbBouncyCastleEncryption() { + TestEncryptionMethod(typeof(StreamAesBouncyCastleEncryptor), "aes-128-cfb"); + TestEncryptionMethod(typeof(StreamAesBouncyCastleEncryptor), "aes-192-cfb"); TestEncryptionMethod(typeof(StreamAesBouncyCastleEncryptor), "aes-256-cfb"); } [TestMethod]