Browse Source

drop stream cipher cipherUpdate api, drop table cipher due to performance issue

pull/2865/head
Student Main 5 years ago
parent
commit
b332a6bd1c
11 changed files with 103 additions and 240 deletions
  1. +2
    -2
      shadowsocks-csharp/Controller/Service/TCPRelay.cs
  2. +14
    -15
      shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs
  3. +2
    -2
      shadowsocks-csharp/Encryption/EncryptorBase.cs
  4. +2
    -2
      shadowsocks-csharp/Encryption/EncryptorFactory.cs
  5. +4
    -10
      shadowsocks-csharp/Encryption/Stream/StreamAesBouncyCastleEncryptor.cs
  6. +8
    -17
      shadowsocks-csharp/Encryption/Stream/StreamChachaNaClEncryptor.cs
  7. +14
    -17
      shadowsocks-csharp/Encryption/Stream/StreamEncryptor.cs
  8. +44
    -0
      shadowsocks-csharp/Encryption/Stream/StreamPlainNativeEncryptor.cs
  9. +11
    -16
      shadowsocks-csharp/Encryption/Stream/StreamRc4NativeEncryptor.cs
  10. +0
    -152
      shadowsocks-csharp/Encryption/Stream/StreamTableNativeEncryptor.cs
  11. +2
    -7
      test/CryptographyTest.cs

+ 2
- 2
shadowsocks-csharp/Controller/Service/TCPRelay.cs View File

@@ -133,10 +133,10 @@ namespace Shadowsocks.Controller
public const int RecvSize = 2048; public const int RecvSize = 2048;
// overhead of one chunk, reserved for AEAD ciphers // overhead of one chunk, reserved for AEAD ciphers
public const int ChunkOverheadSize = 16 * 2 /* two tags */ + AEADEncryptor.CHUNK_LEN_BYTES;
public const int ChunkOverheadSize = 16 * 2 /* two tags */ + AEADEncryptor.ChunkLengthBytes;
// max chunk size // max chunk size
public const uint MaxChunkSize = AEADEncryptor.CHUNK_LEN_MASK + AEADEncryptor.CHUNK_LEN_BYTES + 16 * 2;
public const uint MaxChunkSize = AEADEncryptor.ChunkLengthMask + AEADEncryptor.ChunkLengthBytes + 16 * 2;
// In general, the ciphertext length, we should take overhead into account // In general, the ciphertext length, we should take overhead into account
public const int BufferSize = RecvSize + (int)MaxChunkSize + 32 /* max salt len */; public const int BufferSize = RecvSize + (int)MaxChunkSize + 32 /* max salt len */;


+ 14
- 15
shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs View File

@@ -22,11 +22,10 @@ namespace Shadowsocks.Encryption.AEAD
protected static byte[] _udpTmpBuf = new byte[65536]; protected static byte[] _udpTmpBuf = new byte[65536];
// every connection should create its own buffer // every connection should create its own buffer
private ByteCircularBuffer buffer = new ByteCircularBuffer(MAX_INPUT_SIZE * 2);
// private ByteCircularBuffer buffer = new ByteCircularBuffer(MAX_INPUT_SIZE * 2);
private ByteCircularBuffer buffer = new ByteCircularBuffer(MaxInputSize * 2);
public const int CHUNK_LEN_BYTES = 2;
public const uint CHUNK_LEN_MASK = 0x3FFFu;
public const int ChunkLengthBytes = 2;
public const uint ChunkLengthMask = 0x3FFFu;
protected Dictionary<string, CipherInfo> ciphers; protected Dictionary<string, CipherInfo> ciphers;
@@ -180,11 +179,11 @@ namespace Shadowsocks.Encryption.AEAD
{ {
tcpRequestSent = true; tcpRequestSent = true;
// The first TCP request // The first TCP request
byte[] encAddrBufBytes = new byte[AddressBufferLength + tagLen * 2 + CHUNK_LEN_BYTES];
byte[] encAddrBufBytes = new byte[AddressBufferLength + tagLen * 2 + ChunkLengthBytes];
byte[] addrBytes = buffer.Get(AddressBufferLength); byte[] addrBytes = buffer.Get(AddressBufferLength);
int encAddrBufLength = ChunkEncrypt(addrBytes, encAddrBufBytes); int encAddrBufLength = ChunkEncrypt(addrBytes, encAddrBufBytes);
// ChunkEncrypt(addrBytes, AddressBufferLength, encAddrBufBytes, out encAddrBufLength); // ChunkEncrypt(addrBytes, AddressBufferLength, encAddrBufBytes, out encAddrBufLength);
Debug.Assert(encAddrBufLength == AddressBufferLength + tagLen * 2 + CHUNK_LEN_BYTES);
Debug.Assert(encAddrBufLength == AddressBufferLength + tagLen * 2 + ChunkLengthBytes);
Array.Copy(encAddrBufBytes, 0, outbuf, outlength, encAddrBufLength); Array.Copy(encAddrBufBytes, 0, outbuf, outlength, encAddrBufLength);
outlength += encAddrBufLength; outlength += encAddrBufLength;
logger.Debug($"_tcpRequestSent outlength {outlength}"); logger.Debug($"_tcpRequestSent outlength {outlength}");
@@ -195,12 +194,12 @@ namespace Shadowsocks.Encryption.AEAD
{ {
uint bufSize = (uint)buffer.Size; uint bufSize = (uint)buffer.Size;
if (bufSize <= 0) return; if (bufSize <= 0) return;
var chunklength = (int)Math.Min(bufSize, CHUNK_LEN_MASK);
var chunklength = (int)Math.Min(bufSize, ChunkLengthMask);
byte[] chunkBytes = buffer.Get(chunklength); byte[] chunkBytes = buffer.Get(chunklength);
byte[] encChunkBytes = new byte[chunklength + tagLen * 2 + CHUNK_LEN_BYTES];
byte[] encChunkBytes = new byte[chunklength + tagLen * 2 + ChunkLengthBytes];
int encChunkLength = ChunkEncrypt(chunkBytes, encChunkBytes); int encChunkLength = ChunkEncrypt(chunkBytes, encChunkBytes);
// ChunkEncrypt(chunkBytes, chunklength, encChunkBytes, out encChunkLength); // ChunkEncrypt(chunkBytes, chunklength, encChunkBytes, out encChunkLength);
Debug.Assert(encChunkLength == chunklength + tagLen * 2 + CHUNK_LEN_BYTES);
Debug.Assert(encChunkLength == chunklength + tagLen * 2 + ChunkLengthBytes);
Buffer.BlockCopy(encChunkBytes, 0, outbuf, outlength, encChunkLength); Buffer.BlockCopy(encChunkBytes, 0, outbuf, outlength, encChunkLength);
outlength += encChunkLength; outlength += encChunkLength;
logger.Debug("chunks enc outlength " + outlength); logger.Debug("chunks enc outlength " + outlength);
@@ -256,7 +255,7 @@ namespace Shadowsocks.Encryption.AEAD
} }
// first get chunk length // first get chunk length
if (bufSize <= CHUNK_LEN_BYTES + tagLen)
if (bufSize <= ChunkLengthBytes + tagLen)
{ {
// so we only have chunk length and its tag? // so we only have chunk length and its tag?
return; return;
@@ -264,12 +263,12 @@ namespace Shadowsocks.Encryption.AEAD
#region Chunk Decryption #region Chunk Decryption
byte[] encLenBytes = buffer.Peek(CHUNK_LEN_BYTES + tagLen);
byte[] encLenBytes = buffer.Peek(ChunkLengthBytes + tagLen);
// try to dec chunk len // try to dec chunk len
byte[] decChunkLenBytes = CipherDecrypt2(encLenBytes); byte[] decChunkLenBytes = CipherDecrypt2(encLenBytes);
ushort chunkLen = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(decChunkLenBytes, 0)); ushort chunkLen = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(decChunkLenBytes, 0));
if (chunkLen > CHUNK_LEN_MASK)
if (chunkLen > ChunkLengthMask)
{ {
// we get invalid chunk // we get invalid chunk
logger.Error($"Invalid chunk length: {chunkLen}"); logger.Error($"Invalid chunk length: {chunkLen}");
@@ -277,7 +276,7 @@ namespace Shadowsocks.Encryption.AEAD
} }
logger.Debug("Get the real chunk len:" + chunkLen); logger.Debug("Get the real chunk len:" + chunkLen);
bufSize = buffer.Size; bufSize = buffer.Size;
if (bufSize < CHUNK_LEN_BYTES + tagLen /* we haven't remove them */+ chunkLen + tagLen)
if (bufSize < ChunkLengthBytes + tagLen /* we haven't remove them */+ chunkLen + tagLen)
{ {
logger.Debug("No more data to decrypt one chunk"); logger.Debug("No more data to decrypt one chunk");
return; return;
@@ -286,7 +285,7 @@ namespace Shadowsocks.Encryption.AEAD
// we have enough data to decrypt one chunk // we have enough data to decrypt one chunk
// drop chunk len and its tag from buffer // drop chunk len and its tag from buffer
buffer.Skip(CHUNK_LEN_BYTES + tagLen);
buffer.Skip(ChunkLengthBytes + tagLen);
byte[] encChunkBytes = buffer.Get(chunkLen + tagLen); byte[] encChunkBytes = buffer.Get(chunkLen + tagLen);
byte[] decChunkBytes = CipherDecrypt2(encChunkBytes); byte[] decChunkBytes = CipherDecrypt2(encChunkBytes);
IncrementNonce(); IncrementNonce();
@@ -363,7 +362,7 @@ namespace Shadowsocks.Encryption.AEAD
private int ChunkEncrypt(Span<byte> plain, Span<byte> cipher) private int ChunkEncrypt(Span<byte> plain, Span<byte> cipher)
{ {
if (plain.Length > CHUNK_LEN_MASK)
if (plain.Length > ChunkLengthMask)
{ {
logger.Error("enc chunk too big"); logger.Error("enc chunk too big");
throw new CryptoErrorException(); throw new CryptoErrorException();


+ 2
- 2
shadowsocks-csharp/Encryption/EncryptorBase.cs View File

@@ -4,7 +4,7 @@
{ {
private static int _currentId = 0; private static int _currentId = 0;
public const int MAX_INPUT_SIZE = 32768;
public const int MaxInputSize = 32768;
public const int MAX_DOMAIN_LEN = 255; public const int MAX_DOMAIN_LEN = 255;
public const int ADDR_PORT_LEN = 2; public const int ADDR_PORT_LEN = 2;
@@ -14,7 +14,7 @@
public const int ATYP_DOMAIN = 0x03; public const int ATYP_DOMAIN = 0x03;
public const int ATYP_IPv6 = 0x04; public const int ATYP_IPv6 = 0x04;
public const int MD5_LEN = 16;
public const int MD5Length = 16;
// for debugging only, give it a number to trace data stream // for debugging only, give it a number to trace data stream
public readonly int instanceId; public readonly int instanceId;


+ 2
- 2
shadowsocks-csharp/Encryption/EncryptorFactory.cs View File

@@ -17,12 +17,12 @@ namespace Shadowsocks.Encryption
static EncryptorFactory() static EncryptorFactory()
{ {
foreach (var method in StreamTableNativeEncryptor.SupportedCiphers())
foreach (var method in StreamPlainNativeEncryptor.SupportedCiphers())
{ {
if (!_registeredEncryptors.ContainsKey(method.Key)) if (!_registeredEncryptors.ContainsKey(method.Key))
{ {
ciphers.Add(method.Key, method.Value); ciphers.Add(method.Key, method.Value);
_registeredEncryptors.Add(method.Key, typeof(StreamTableNativeEncryptor));
_registeredEncryptors.Add(method.Key, typeof(StreamPlainNativeEncryptor));
} }
} }
foreach (var method in StreamRc4NativeEncryptor.SupportedCiphers()) foreach (var method in StreamRc4NativeEncryptor.SupportedCiphers())


+ 4
- 10
shadowsocks-csharp/Encryption/Stream/StreamAesBouncyCastleEncryptor.cs View File

@@ -14,7 +14,6 @@ namespace Shadowsocks.Encryption.Stream
public StreamAesBouncyCastleEncryptor(string method, string password) : base(method, password) public StreamAesBouncyCastleEncryptor(string method, string password) : base(method, password)
{ {
c = new BufferedBlockCipher(new CfbBlockCipher(new AesEngine(), 128)); c = new BufferedBlockCipher(new CfbBlockCipher(new AesEngine(), 128));
// c = CipherUtilities.GetCipher("AES/CFB/NoPadding");
} }


protected override void initCipher(byte[] iv, bool isEncrypt) protected override void initCipher(byte[] iv, bool isEncrypt)
@@ -23,13 +22,6 @@ namespace Shadowsocks.Encryption.Stream
c.Init(isEncrypt, new ParametersWithIV(new KeyParameter(key), iv)); c.Init(isEncrypt, new ParametersWithIV(new KeyParameter(key), iv));
} }


protected override void cipherUpdate(bool isEncrypt, int length, byte[] buf, byte[] outbuf)
{
var i = buf.AsSpan(0, length);
if (isEncrypt) CipherEncrypt(i, outbuf);
else CipherDecrypt(outbuf, i);
}

protected override int CipherEncrypt(Span<byte> plain, Span<byte> cipher) protected override int CipherEncrypt(Span<byte> plain, Span<byte> cipher)
{ {
CipherUpdate(plain, cipher); CipherUpdate(plain, cipher);
@@ -42,9 +34,9 @@ namespace Shadowsocks.Encryption.Stream
return cipher.Length; return cipher.Length;
} }



private void CipherUpdate(Span<byte> i, Span<byte> o) private void CipherUpdate(Span<byte> i, Span<byte> o)
{ {
// there's some secret in OpenSSL's EVP context.
var ob = new byte[o.Length]; var ob = new byte[o.Length];
int blklen = c.ProcessBytes(i.ToArray(), 0, i.Length, ob, 0); int blklen = c.ProcessBytes(i.ToArray(), 0, i.Length, ob, 0);
int restlen = i.Length - blklen; int restlen = i.Length - blklen;
@@ -55,9 +47,11 @@ namespace Shadowsocks.Encryption.Stream
ob.CopyTo(o); ob.CopyTo(o);
} }


#region Ciphers
#region Cipher Info
private static readonly Dictionary<string, CipherInfo> _ciphers = new Dictionary<string, CipherInfo> private static readonly Dictionary<string, CipherInfo> _ciphers = new Dictionary<string, CipherInfo>
{ {
{"aes-128-cfb",new CipherInfo("aes-128-cfb", 16, 16, CipherFamily.AesCfb, CipherStandardState.Unstable)},
{"aes-192-cfb",new CipherInfo("aes-192-cfb", 24, 16, CipherFamily.AesCfb, CipherStandardState.Unstable)},
{"aes-256-cfb",new CipherInfo("aes-256-cfb", 32, 16, CipherFamily.AesCfb, CipherStandardState.Unstable)}, {"aes-256-cfb",new CipherInfo("aes-256-cfb", 32, 16, CipherFamily.AesCfb, CipherStandardState.Unstable)},
}; };




+ 8
- 17
shadowsocks-csharp/Encryption/Stream/StreamChachaNaClEncryptor.cs View File

@@ -7,11 +7,15 @@ namespace Shadowsocks.Encryption.Stream
public class StreamChachaNaClEncryptor : StreamEncryptor public class StreamChachaNaClEncryptor : StreamEncryptor
{ {
const int BlockSize = 64; const int BlockSize = 64;
// when new data arrive, put it in correct offset of chunk
// 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

// when new data arrive, put it on correct offset
// and update it, ignore other data, get it in correct offset... // and update it, ignore other data, get it in correct offset...
byte[] chachaBuf = new byte[32768 + BlockSize];
byte[] chachaBuf = new byte[MaxInputSize + BlockSize];
// the 'correct offset', always in 0~BlockSize range, so input data always fit into buffer
int remain = 0; int remain = 0;
// increase counter only when a chunk fully recieved
// increase counter manually...
int ic = 0; int ic = 0;
public StreamChachaNaClEncryptor(string method, string password) : base(method, password) public StreamChachaNaClEncryptor(string method, string password) : base(method, password)
{ {
@@ -41,20 +45,7 @@ namespace Shadowsocks.Encryption.Stream
return len; return len;
} }


protected override void cipherUpdate(bool isEncrypt, int length, byte[] buf, byte[] outbuf)
{
var i = buf.AsSpan(0, length);
if (isEncrypt)
{
CipherEncrypt(i, outbuf);
}
else
{
CipherDecrypt(outbuf, i);
}
}

#region Ciphers
#region Cipher Info
private static readonly Dictionary<string, CipherInfo> _ciphers = new Dictionary<string, CipherInfo> private static readonly Dictionary<string, CipherInfo> _ciphers = new Dictionary<string, CipherInfo>
{ {
{ "chacha20-ietf", new CipherInfo("chacha20-ietf", 32, 12, CipherFamily.Chacha20) }, { "chacha20-ietf", new CipherInfo("chacha20-ietf", 32, 12, CipherFamily.Chacha20) },


+ 14
- 17
shadowsocks-csharp/Encryption/Stream/StreamEncryptor.cs View File

@@ -1,10 +1,10 @@
using System;
using NLog;
using Shadowsocks.Controller;
using Shadowsocks.Encryption.CircularBuffer;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Text; using System.Text;
using Shadowsocks.Encryption.CircularBuffer;
using Shadowsocks.Controller;
using NLog;
namespace Shadowsocks.Encryption.Stream namespace Shadowsocks.Encryption.Stream
{ {
@@ -54,7 +54,7 @@ namespace Shadowsocks.Encryption.Stream
public static void LegacyDeriveKey(byte[] password, byte[] key, int keylen) public static void LegacyDeriveKey(byte[] password, byte[] key, int keylen)
{ {
byte[] result = new byte[password.Length + MD5_LEN];
byte[] result = new byte[password.Length + MD5Length];
int i = 0; int i = 0;
byte[] md5sum = Array.Empty<byte>(); byte[] md5sum = Array.Empty<byte>();
while (i < keylen) while (i < keylen)
@@ -65,12 +65,12 @@ namespace Shadowsocks.Encryption.Stream
} }
else else
{ {
Array.Copy(md5sum, 0, result, 0, MD5_LEN);
Array.Copy(password, 0, result, MD5_LEN, password.Length);
Array.Copy(md5sum, 0, result, 0, MD5Length);
Array.Copy(password, 0, result, MD5Length, password.Length);
md5sum = CryptoUtils.MD5(result); md5sum = CryptoUtils.MD5(result);
} }
Array.Copy(md5sum, 0, key, i, Math.Min(MD5_LEN, keylen - i));
i += MD5_LEN;
Array.Copy(md5sum, 0, key, i, Math.Min(MD5Length, keylen - i));
i += MD5Length;
} }
} }
@@ -81,13 +81,9 @@ namespace Shadowsocks.Encryption.Stream
Array.Copy(iv, this.iv, ivLen); Array.Copy(iv, this.iv, ivLen);
} }
protected abstract void cipherUpdate(bool isEncrypt, int length, byte[] buf, byte[] outbuf);
protected abstract int CipherEncrypt(Span<byte> plain, Span<byte> cipher); protected abstract int CipherEncrypt(Span<byte> plain, Span<byte> cipher);
protected abstract int CipherDecrypt(Span<byte> plain, Span<byte> cipher); protected abstract int CipherDecrypt(Span<byte> plain, Span<byte> cipher);
//protected static void randBytes(byte[] buf, int length) { RNG.GetBytes(buf, length); }
#region TCP #region TCP
public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength) public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength)
@@ -109,7 +105,8 @@ namespace Shadowsocks.Encryption.Stream
int size = buffer.Size; int size = buffer.Size;
byte[] plain = buffer.Get(size); byte[] plain = buffer.Get(size);
byte[] cipher = new byte[size]; byte[] cipher = new byte[size];
cipherUpdate(true, size, plain, cipher);
CipherEncrypt(plain, cipher);
logger.DumpBase64($"plain {instanceId}", plain, size); logger.DumpBase64($"plain {instanceId}", plain, size);
logger.DumpBase64($"cipher {instanceId}", cipher, cipher.Length); logger.DumpBase64($"cipher {instanceId}", cipher, cipher.Length);
logger.Dump($"iv {instanceId}", iv, ivLen); logger.Dump($"iv {instanceId}", iv, ivLen);
@@ -140,7 +137,7 @@ namespace Shadowsocks.Encryption.Stream
else initCipher(Array.Empty<byte>(), false); else initCipher(Array.Empty<byte>(), false);
} }
byte[] cipher = buffer.ToArray(); byte[] cipher = buffer.ToArray();
cipherUpdate(false, cipher.Length, cipher, outbuf);
CipherDecrypt(outbuf, cipher);
logger.DumpBase64($"cipher {instanceId}", cipher, cipher.Length); logger.DumpBase64($"cipher {instanceId}", cipher, cipher.Length);
logger.DumpBase64($"plain {instanceId}", outbuf, cipher.Length); logger.DumpBase64($"plain {instanceId}", outbuf, cipher.Length);
logger.Dump($"iv {instanceId}", iv, ivLen); logger.Dump($"iv {instanceId}", iv, ivLen);
@@ -162,7 +159,7 @@ namespace Shadowsocks.Encryption.Stream
initCipher(outbuf, true); initCipher(outbuf, true);
lock (udpBuffer) lock (udpBuffer)
{ {
cipherUpdate(true, length, buf, udpBuffer);
CipherEncrypt(buf, udpBuffer);
outlength = length + ivLen; outlength = length + ivLen;
Buffer.BlockCopy(udpBuffer, 0, outbuf, ivLen, length); Buffer.BlockCopy(udpBuffer, 0, outbuf, ivLen, length);
} }
@@ -177,7 +174,7 @@ namespace Shadowsocks.Encryption.Stream
{ {
// C# could be multi-threaded // C# could be multi-threaded
Buffer.BlockCopy(buf, ivLen, udpBuffer, 0, length - ivLen); Buffer.BlockCopy(buf, ivLen, udpBuffer, 0, length - ivLen);
cipherUpdate(false, length - ivLen, udpBuffer, outbuf);
CipherDecrypt(outbuf, udpBuffer);
} }
} }


+ 44
- 0
shadowsocks-csharp/Encryption/Stream/StreamPlainNativeEncryptor.cs View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;

namespace Shadowsocks.Encryption.Stream
{
public class StreamPlainNativeEncryptor : StreamEncryptor
{

public StreamPlainNativeEncryptor(string method, string password) : base(method, password)
{
}

protected override int CipherDecrypt(Span<byte> plain, Span<byte> cipher)
{
cipher.CopyTo(plain);
return cipher.Length;
}

protected override int CipherEncrypt(Span<byte> plain, Span<byte> cipher)
{
plain.CopyTo(cipher);
return plain.Length;
}

#region Cipher Info
private static readonly Dictionary<string, CipherInfo> _ciphers = new Dictionary<string, CipherInfo>
{
{"plain", new CipherInfo("plain", 0, 0, CipherFamily.Plain) },
};

public static Dictionary<string, CipherInfo> SupportedCiphers()
{
return _ciphers;
}

protected override Dictionary<string, CipherInfo> getCiphers()
{
return _ciphers;
}
#endregion
}
}

+ 11
- 16
shadowsocks-csharp/Encryption/Stream/StreamRc4NativeEncryptor.cs View File

@@ -14,6 +14,7 @@ namespace Shadowsocks.Encryption.Stream
protected override void initCipher(byte[] iv, bool isEncrypt) protected override void initCipher(byte[] iv, bool isEncrypt)
{ {
base.initCipher(iv, isEncrypt); base.initCipher(iv, isEncrypt);
// rc4-md5 is rc4 with md5 based session key
if (cipherFamily == CipherFamily.Rc4Md5) if (cipherFamily == CipherFamily.Rc4Md5)
{ {
byte[] temp = new byte[keyLen + ivLen]; byte[] temp = new byte[keyLen + ivLen];
@@ -28,33 +29,27 @@ namespace Shadowsocks.Encryption.Stream
sbox = SBox(realkey); sbox = SBox(realkey);
} }


protected override void cipherUpdate(bool isEncrypt, int length, byte[] buf, byte[] outbuf)
{
byte[] t = new byte[length];
Array.Copy(buf, t, length);

RC4(ctx, sbox, t, length);
Array.Copy(t, outbuf, length);
}

protected override int CipherEncrypt(Span<byte> plain, Span<byte> cipher) protected override int CipherEncrypt(Span<byte> plain, Span<byte> cipher)
{ {
return DoRC4(plain, cipher);
return CipherUpdate(plain, cipher);
} }


protected override int CipherDecrypt(Span<byte> plain, Span<byte> cipher) protected override int CipherDecrypt(Span<byte> plain, Span<byte> cipher)
{ {
return DoRC4(cipher, plain);
return CipherUpdate(cipher, plain);
} }


private int DoRC4(Span<byte> i, Span<byte> o)
private int CipherUpdate(Span<byte> i, Span<byte> o)
{ {
i.CopyTo(o);
RC4(ctx, sbox, o, o.Length);
return o.Length;
// don't know why we need third array, but it works...
Span<byte> t = new byte[i.Length];
i.CopyTo(t);
RC4(ctx, sbox, t, t.Length);
t.CopyTo(o);
return t.Length;
} }


#region Ciphers
#region Cipher Info
private static readonly Dictionary<string, CipherInfo> _ciphers = new Dictionary<string, CipherInfo> private static readonly Dictionary<string, CipherInfo> _ciphers = new Dictionary<string, CipherInfo>
{ {
// original RC4 doesn't use IV // original RC4 doesn't use IV


+ 0
- 152
shadowsocks-csharp/Encryption/Stream/StreamTableNativeEncryptor.cs View File

@@ -1,152 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace Shadowsocks.Encryption.Stream
{
public class StreamTableNativeEncryptor : StreamEncryptor
{
// table mode use special way to generate key
readonly string _password;

public StreamTableNativeEncryptor(string method, string password) : base(method, password)
{
_password = password;
}

protected override void initCipher(byte[] iv, bool isEncrypt)
{
// another cipher is plain, needn't a table
if (cipherFamily != CipherFamily.Table) return;
ulong a = BitConverter.ToUInt64(CryptoUtils.MD5(Encoding.UTF8.GetBytes(_password)), 0);
for (int i = 0; i < 256; i++)
{
_encryptTable[i] = (byte)i;
}
Span<byte> t = _encryptTable;
// copy array 1024 times? excuse me?
for (int i = 1; i < 1024; i++)
{
t = MergeSort(t, a, i);
}
_encryptTable = t.ToArray();
for (int i = 0; i < 256; i++)
{
_decryptTable[_encryptTable[i]] = (byte)i;
}
}

protected override void cipherUpdate(bool isEncrypt, int length, byte[] buf, byte[] outbuf)
{
if (cipherFamily == CipherFamily.Table)
{
var table = isEncrypt ? _encryptTable : _decryptTable;
for (int i = 0; i < length; i++)
{
outbuf[i] = table[buf[i]];
}
}
else if (cipherFamily == CipherFamily.Plain)
{
Array.Copy(buf, outbuf, length);
}
}

protected override int CipherDecrypt(Span<byte> plain, Span<byte> cipher)
{
if (cipherFamily == 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<byte> plain, Span<byte> cipher)
{
if (cipherFamily == 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<string, CipherInfo> _ciphers = new Dictionary<string, CipherInfo>
{
{"plain", new CipherInfo("plain", 0, 0, CipherFamily.Plain) },
{"table", new CipherInfo("table", 0, 0, CipherFamily.Table) },
};

public static Dictionary<string, CipherInfo> SupportedCiphers()
{
return _ciphers;
}

protected override Dictionary<string, CipherInfo> getCiphers()
{
return _ciphers;
}
#endregion

#region Table
private byte[] _encryptTable = new byte[256];
private byte[] _decryptTable = new byte[256];
private byte[] _tmp = new byte[256];

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static long Compare(byte x, byte y, ulong a, int i)
{
return (long)(a % (ulong)(x + i)) - (long)(a % (ulong)(y + i));
}

byte[] buf = new byte[1024];
private Span<byte> MergeSort(Span<byte> array, ulong a, int j)
{
if (array.Length == 1)
{
return array;
}
int middle = array.Length / 2;

Span<byte> left = MergeSort(array.Slice(0, middle), a, j);;
Span<byte> right = MergeSort(array.Slice(middle), a, j);

int leftptr = 0;
int rightptr = 0;

// why a new array?
Span<byte> sorted = new byte[array.Length];// buf.AsSpan().Slice(0,array.Length); // // _tmp;
for (int k = 0; k < array.Length; k++)
{
if (rightptr == right.Length || ((leftptr < left.Length) && (Compare(left[leftptr], right[rightptr], a, j) <= 0)))
{
sorted[k] = left[leftptr];
leftptr++;
}
else if (leftptr == left.Length || ((rightptr < right.Length) && (Compare(right[rightptr], left[leftptr], a, j)) <= 0))
{
sorted[k] = right[rightptr];
rightptr++;
}
}
return sorted;
}
#endregion
}
}

+ 2
- 7
test/CryptographyTest.cs View File

@@ -147,16 +147,11 @@ namespace Shadowsocks.Test
[TestMethod] [TestMethod]
public void TestNativeEncryption() public void TestNativeEncryption()
{ {
TestEncryptionMethod(typeof(StreamTableNativeEncryptor), "plain");
TestEncryptionMethod(typeof(StreamRc4NativeEncryptor), "rc4");
TestEncryptionMethod(typeof(StreamPlainNativeEncryptor), "plain");
// TestEncryptionMethod(typeof(StreamRc4NativeEncryptor), "rc4");
TestEncryptionMethod(typeof(StreamRc4NativeEncryptor), "rc4-md5"); TestEncryptionMethod(typeof(StreamRc4NativeEncryptor), "rc4-md5");
} }
[TestMethod]
public void TestNativeTableEncryption()
{
TestEncryptionMethod(typeof(StreamTableNativeEncryptor), "table");
}
[TestMethod] [TestMethod]
public void TestStreamAesBouncyCastleEncryption() public void TestStreamAesBouncyCastleEncryption()
{ {


Loading…
Cancel
Save