@@ -18,7 +18,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
{"aes-256-gcm", new CipherInfo("aes-256-gcm", 32, 32, 12, 16, CipherFamily.AesGcm)}, | |||
}; | |||
protected override Dictionary<string, CipherInfo> getCiphers() | |||
protected override Dictionary<string, CipherInfo> GetCiphers() | |||
{ | |||
return _ciphers; | |||
} | |||
@@ -32,7 +32,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
public override int CipherEncrypt(ReadOnlySpan<byte> plain, Span<byte> cipher) | |||
{ | |||
using AesGcm aes = new AesGcm(sessionKey); | |||
aes.Encrypt(nonce.AsSpan(), plain, cipher.Slice(0, plain.Length), cipher.Slice(plain.Length, tagLen)); | |||
aes.Encrypt(nonce, plain, cipher.Slice(0, plain.Length), cipher.Slice(plain.Length, tagLen)); | |||
return plain.Length + tagLen; | |||
} | |||
@@ -42,7 +42,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
using AesGcm aes = new AesGcm(sessionKey); | |||
ReadOnlySpan<byte> ciphertxt = cipher.Slice(0, clen); | |||
ReadOnlySpan<byte> tag = cipher.Slice(clen); | |||
aes.Decrypt(nonce.AsSpan(), ciphertxt, tag, plain.Slice(0, clen)); | |||
aes.Decrypt(nonce, ciphertxt, tag, plain.Slice(0, clen)); | |||
return clen; | |||
} | |||
} |
@@ -5,26 +5,25 @@ using Shadowsocks.Encryption.Stream; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Net; | |||
using System.Runtime.CompilerServices; | |||
using System.Text; | |||
namespace Shadowsocks.Encryption.AEAD | |||
{ | |||
public abstract class AEADEncryptor : EncryptorBase | |||
{ | |||
private static Logger logger = LogManager.GetCurrentClassLogger(); | |||
private static readonly Logger logger = LogManager.GetCurrentClassLogger(); | |||
// We are using the same saltLen and keyLen | |||
private const string Info = "ss-subkey"; | |||
private static readonly byte[] InfoBytes = Encoding.ASCII.GetBytes(Info); | |||
// every connection should create its own buffer | |||
private byte[] sharedBuffer = new byte[65536]; | |||
private readonly byte[] buffer = new byte[65536]; | |||
private int bufPtr = 0; | |||
public const int ChunkLengthBytes = 2; | |||
public const uint ChunkLengthMask = 0x3FFFu; | |||
protected Dictionary<string, CipherInfo> ciphers; | |||
protected CipherFamily cipherFamily; | |||
protected CipherInfo CipherInfo; | |||
protected static byte[] masterKey = Array.Empty<byte>(); | |||
@@ -46,7 +45,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
public AEADEncryptor(string method, string password) | |||
: base(method, password) | |||
{ | |||
CipherInfo = getCiphers()[method.ToLower()]; | |||
CipherInfo = GetCiphers()[method.ToLower()]; | |||
cipherFamily = CipherInfo.Type; | |||
AEADCipherParameter parameter = (AEADCipherParameter)CipherInfo.CipherParameter; | |||
keyLen = parameter.KeySize; | |||
@@ -55,13 +54,15 @@ namespace Shadowsocks.Encryption.AEAD | |||
nonceLen = parameter.NonceSize; | |||
InitKey(password); | |||
salt = new byte[saltLen]; | |||
// Initialize all-zero nonce for each connection | |||
nonce = new byte[nonceLen]; | |||
logger.Dump($"masterkey {instanceId}", masterKey, keyLen); | |||
logger.Dump($"nonce {instanceId}", nonce, keyLen); | |||
} | |||
protected abstract Dictionary<string, CipherInfo> getCiphers(); | |||
protected abstract Dictionary<string, CipherInfo> GetCiphers(); | |||
protected void InitKey(string password) | |||
{ | |||
@@ -77,32 +78,18 @@ namespace Shadowsocks.Encryption.AEAD | |||
Array.Resize(ref masterKey, keyLen); | |||
} | |||
DeriveKey(passbuf, masterKey, keyLen); | |||
StreamEncryptor.LegacyDeriveKey(passbuf, masterKey, keyLen); | |||
// init session key | |||
sessionKey = new byte[keyLen]; | |||
} | |||
public void DeriveKey(byte[] password, byte[] key, int keylen) | |||
{ | |||
StreamEncryptor.LegacyDeriveKey(password, key, keylen); | |||
} | |||
public void DeriveSessionKey(byte[] salt, byte[] masterKey, byte[] sessionKey) | |||
{ | |||
CryptoUtils.HKDF(keyLen, masterKey, salt, InfoBytes).CopyTo(sessionKey, 0); | |||
} | |||
protected void IncrementNonce() | |||
{ | |||
CryptoUtils.SodiumIncrement(nonce); | |||
} | |||
public virtual void InitCipher(byte[] salt, bool isEncrypt, bool isUdp) | |||
public virtual void InitCipher(byte[] salt, bool isEncrypt) | |||
{ | |||
this.salt = new byte[saltLen]; | |||
Array.Copy(salt, this.salt, saltLen); | |||
DeriveSessionKey(salt, masterKey, sessionKey); | |||
CryptoUtils.HKDF(keyLen, masterKey, salt, InfoBytes).CopyTo(sessionKey, 0); | |||
logger.Dump($"salt {instanceId}", salt, saltLen); | |||
logger.Dump($"sessionkey {instanceId}", sessionKey, keyLen); | |||
} | |||
@@ -112,10 +99,11 @@ namespace Shadowsocks.Encryption.AEAD | |||
#region TCP | |||
[MethodImpl(MethodImplOptions.Synchronized | MethodImplOptions.AggressiveOptimization)] | |||
public override int Encrypt(ReadOnlySpan<byte> plain, Span<byte> cipher) | |||
{ | |||
// push data | |||
Span<byte> tmp = sharedBuffer.AsSpan(0, plain.Length + bufPtr); | |||
Span<byte> tmp = buffer.AsSpan(0, plain.Length + bufPtr); | |||
plain.CopyTo(tmp.Slice(bufPtr)); | |||
int outlength = 0; | |||
@@ -124,7 +112,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
saltReady = true; | |||
// Generate salt | |||
byte[] saltBytes = RNG.GetBytes(saltLen); | |||
InitCipher(saltBytes, true, false); | |||
InitCipher(saltBytes, true); | |||
saltBytes.CopyTo(cipher); | |||
outlength = saltLen; | |||
} | |||
@@ -162,7 +150,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
logger.Debug("enc outbuf almost full, giving up"); | |||
// write rest data to head of shared buffer | |||
tmp.CopyTo(sharedBuffer); | |||
tmp.CopyTo(buffer); | |||
bufPtr = tmp.Length; | |||
return outlength; | |||
@@ -177,11 +165,12 @@ namespace Shadowsocks.Encryption.AEAD | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.Synchronized | MethodImplOptions.AggressiveOptimization)] | |||
public override int Decrypt(Span<byte> plain, ReadOnlySpan<byte> cipher) | |||
{ | |||
int outlength = 0; | |||
// drop all into buffer | |||
Span<byte> tmp = sharedBuffer.AsSpan(0, cipher.Length + bufPtr); | |||
Span<byte> tmp = buffer.AsSpan(0, cipher.Length + bufPtr); | |||
cipher.CopyTo(tmp.Slice(bufPtr)); | |||
int bufSize = tmp.Length; | |||
@@ -192,7 +181,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
if (bufSize <= saltLen) | |||
{ | |||
// need more, write back cache | |||
tmp.CopyTo(sharedBuffer); | |||
tmp.CopyTo(buffer); | |||
bufPtr = tmp.Length; | |||
return outlength; | |||
} | |||
@@ -202,7 +191,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
byte[] salt = tmp.Slice(0, saltLen).ToArray(); | |||
tmp = tmp.Slice(saltLen); | |||
InitCipher(salt, false, false); | |||
InitCipher(salt, false); | |||
logger.Debug("get salt len " + saltLen); | |||
} | |||
@@ -222,7 +211,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
{ | |||
// so we only have chunk length and its tag? | |||
// wait more | |||
tmp.CopyTo(sharedBuffer); | |||
tmp.CopyTo(buffer); | |||
bufPtr = tmp.Length; | |||
return outlength; | |||
} | |||
@@ -231,7 +220,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
if (len <= 0) | |||
{ | |||
// no chunk decrypted | |||
tmp.CopyTo(sharedBuffer); | |||
tmp.CopyTo(buffer); | |||
bufPtr = tmp.Length; | |||
return outlength; | |||
} | |||
@@ -243,7 +232,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
if (outlength + 100 > TCPHandler.BufferSize) | |||
{ | |||
logger.Debug("dec outbuf almost full, giving up"); | |||
tmp.CopyTo(sharedBuffer); | |||
tmp.CopyTo(buffer); | |||
bufPtr = tmp.Length; | |||
return outlength; | |||
} | |||
@@ -260,21 +249,24 @@ namespace Shadowsocks.Encryption.AEAD | |||
#endregion | |||
#region UDP | |||
[MethodImpl(MethodImplOptions.Synchronized | MethodImplOptions.AggressiveOptimization)] | |||
public override int EncryptUDP(ReadOnlySpan<byte> plain, Span<byte> cipher) | |||
{ | |||
RNG.GetSpan(cipher.Slice(0, saltLen)); | |||
InitCipher(cipher.Slice(0, saltLen).ToArray(), true, true); | |||
InitCipher(cipher.Slice(0, saltLen).ToArray(), true); | |||
return saltLen + CipherEncrypt(plain, cipher.Slice(saltLen)); | |||
} | |||
[MethodImpl(MethodImplOptions.Synchronized | MethodImplOptions.AggressiveOptimization)] | |||
public override int DecryptUDP(Span<byte> plain, ReadOnlySpan<byte> cipher) | |||
{ | |||
InitCipher(cipher.Slice(0, saltLen).ToArray(), false, true); | |||
InitCipher(cipher.Slice(0, saltLen).ToArray(), false); | |||
return CipherDecrypt(plain, cipher.Slice(saltLen)); | |||
} | |||
#endregion | |||
[MethodImpl(MethodImplOptions.AggressiveOptimization)] | |||
private int ChunkEncrypt(ReadOnlySpan<byte> plain, Span<byte> cipher) | |||
{ | |||
if (plain.Length > ChunkLengthMask) | |||
@@ -285,13 +277,14 @@ namespace Shadowsocks.Encryption.AEAD | |||
byte[] lenbuf = BitConverter.GetBytes((ushort)IPAddress.HostToNetworkOrder((short)plain.Length)); | |||
int cipherLenSize = CipherEncrypt(lenbuf, cipher); | |||
IncrementNonce(); | |||
CryptoUtils.SodiumIncrement(nonce); | |||
int cipherDataSize = CipherEncrypt(plain, cipher.Slice(cipherLenSize)); | |||
IncrementNonce(); | |||
CryptoUtils.SodiumIncrement(nonce); | |||
return cipherLenSize + cipherDataSize; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveOptimization)] | |||
private int ChunkDecrypt(Span<byte> plain, ReadOnlySpan<byte> cipher) | |||
{ | |||
// try to dec chunk len | |||
@@ -311,11 +304,11 @@ namespace Shadowsocks.Encryption.AEAD | |||
logger.Debug("No data to decrypt one chunk"); | |||
return 0; | |||
} | |||
IncrementNonce(); | |||
CryptoUtils.SodiumIncrement(nonce); | |||
// we have enough data to decrypt one chunk | |||
// drop chunk len and its tag from buffer | |||
int len = CipherDecrypt(plain, cipher.Slice(ChunkLengthBytes + tagLen, chunkLength + tagLen)); | |||
IncrementNonce(); | |||
CryptoUtils.SodiumIncrement(nonce); | |||
return len; | |||
} | |||
} |
@@ -8,7 +8,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
public class AEADNaClEncryptor : AEADEncryptor | |||
{ | |||
SnufflePoly1305 enc; | |||
SnufflePoly1305? enc; | |||
public AEADNaClEncryptor(string method, string password) : base(method, password) | |||
{ | |||
} | |||
@@ -20,7 +20,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
{"xchacha20-ietf-poly1305", new CipherInfo("xchacha20-ietf-poly1305",32, 32, 24, 16, CipherFamily.XChacha20Poly1305)}, | |||
}; | |||
protected override Dictionary<string, CipherInfo> getCiphers() | |||
protected override Dictionary<string, CipherInfo> GetCiphers() | |||
{ | |||
return _ciphers; | |||
} | |||
@@ -31,31 +31,26 @@ namespace Shadowsocks.Encryption.AEAD | |||
} | |||
#endregion | |||
public override void InitCipher(byte[] salt, bool isEncrypt, bool isUdp) | |||
public override void InitCipher(byte[] salt, bool isEncrypt) | |||
{ | |||
base.InitCipher(salt, isEncrypt, isUdp); | |||
switch (cipherFamily) | |||
base.InitCipher(salt, isEncrypt); | |||
enc = cipherFamily switch | |||
{ | |||
default: | |||
case CipherFamily.Chacha20Poly1305: | |||
enc = new ChaCha20Poly1305(sessionKey); | |||
break; | |||
case CipherFamily.XChacha20Poly1305: | |||
enc = new XChaCha20Poly1305(sessionKey); | |||
break; | |||
} | |||
CipherFamily.XChacha20Poly1305 => new XChaCha20Poly1305(sessionKey), | |||
_ => new ChaCha20Poly1305(sessionKey), | |||
}; | |||
} | |||
public override int CipherEncrypt(ReadOnlySpan<byte> plain, Span<byte> cipher) | |||
{ | |||
byte[] ct = enc.Encrypt(plain, null, nonce); | |||
byte[] ct = enc!.Encrypt(plain, null, nonce); | |||
ct.CopyTo(cipher); | |||
return ct.Length; | |||
} | |||
public override int CipherDecrypt(Span<byte> plain, ReadOnlySpan<byte> cipher) | |||
{ | |||
byte[] pt = enc.Decrypt(cipher, null, nonce); | |||
byte[] pt = enc!.Decrypt(cipher, null, nonce); | |||
pt.CopyTo(plain); | |||
return pt.Length; | |||
} | |||
@@ -1,4 +1,5 @@ | |||
using System; | |||
using System.Runtime.CompilerServices; | |||
namespace Shadowsocks.Encryption | |||
{ | |||
@@ -11,8 +11,8 @@ namespace Shadowsocks.Encryption | |||
{ | |||
public static string DefaultCipher = "chacha20-ietf-poly1305"; | |||
private static Dictionary<string, Type> _registeredEncryptors = new Dictionary<string, Type>(); | |||
private static Dictionary<string, CipherInfo> ciphers = new Dictionary<string, CipherInfo>(); | |||
private static readonly Dictionary<string, Type> _registeredEncryptors = new Dictionary<string, Type>(); | |||
private static readonly Dictionary<string, CipherInfo> ciphers = new Dictionary<string, CipherInfo>(); | |||
private static readonly Type[] ConstructorTypes = { typeof(string), typeof(string) }; | |||
static EncryptorFactory() | |||
@@ -33,12 +33,12 @@ namespace Shadowsocks.Encryption | |||
_registeredEncryptors.Add(method.Key, typeof(StreamRc4NativeEncryptor)); | |||
} | |||
} | |||
foreach (var method in StreamAesBouncyCastleEncryptor.SupportedCiphers()) | |||
foreach (var method in StreamAesCfbBouncyCastleEncryptor.SupportedCiphers()) | |||
{ | |||
if (!_registeredEncryptors.ContainsKey(method.Key)) | |||
{ | |||
ciphers.Add(method.Key, method.Value); | |||
_registeredEncryptors.Add(method.Key, typeof(StreamAesBouncyCastleEncryptor)); | |||
_registeredEncryptors.Add(method.Key, typeof(StreamAesCfbBouncyCastleEncryptor)); | |||
} | |||
} | |||
foreach (var method in StreamChachaNaClEncryptor.SupportedCiphers()) | |||
@@ -77,7 +77,7 @@ namespace Shadowsocks.Encryption | |||
} | |||
method = method.ToLowerInvariant(); | |||
bool ok = _registeredEncryptors.TryGetValue(method, out Type t); | |||
bool ok = _registeredEncryptors.TryGetValue(method, out Type? t); | |||
if (!ok) | |||
{ | |||
t = _registeredEncryptors[DefaultCipher]; | |||
@@ -13,9 +13,9 @@ namespace Org.BouncyCastle.Crypto.Modes | |||
public class ExtendedCfbBlockCipher | |||
: IBlockCipher | |||
{ | |||
private byte[] IV; | |||
private byte[] cfbV; | |||
private byte[] cfbOutV; | |||
private readonly byte[] IV; | |||
private readonly byte[] cfbV; | |||
private readonly byte[] cfbOutV; | |||
private bool encrypting; | |||
private readonly int blockSize; | |||
@@ -63,9 +63,8 @@ namespace Org.BouncyCastle.Crypto.Modes | |||
ICipherParameters parameters) | |||
{ | |||
encrypting = forEncryption; | |||
if (parameters is ParametersWithIV) | |||
if (parameters is ParametersWithIV ivParam) | |||
{ | |||
ParametersWithIV ivParam = (ParametersWithIV)parameters; | |||
byte[] iv = ivParam.GetIV(); | |||
int diff = IV.Length - iv.Length; | |||
Array.Copy(iv, 0, IV, diff, iv.Length); | |||
@@ -3,23 +3,24 @@ using Org.BouncyCastle.Crypto.Modes; | |||
using Org.BouncyCastle.Crypto.Parameters; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Runtime.CompilerServices; | |||
namespace Shadowsocks.Encryption.Stream | |||
{ | |||
public class StreamAesBouncyCastleEncryptor : StreamEncryptor | |||
public class StreamAesCfbBouncyCastleEncryptor : StreamEncryptor | |||
{ | |||
byte[] cfbBuf = new byte[MaxInputSize + 128]; | |||
readonly byte[] cfbBuf = new byte[MaxInputSize + 128]; | |||
int ptr = 0; | |||
readonly ExtendedCfbBlockCipher b; | |||
public StreamAesBouncyCastleEncryptor(string method, string password) : base(method, password) | |||
public StreamAesCfbBouncyCastleEncryptor(string method, string password) : base(method, password) | |||
{ | |||
b = new ExtendedCfbBlockCipher(new AesEngine(), 128); | |||
} | |||
protected override void initCipher(byte[] iv, bool isEncrypt) | |||
protected override void InitCipher(byte[] iv, bool isEncrypt) | |||
{ | |||
base.initCipher(iv, isEncrypt); | |||
base.InitCipher(iv, isEncrypt); | |||
b.Init(isEncrypt, new ParametersWithIV(new KeyParameter(key), iv)); | |||
} | |||
@@ -35,6 +36,7 @@ namespace Shadowsocks.Encryption.Stream | |||
return cipher.Length; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)] | |||
private void CipherUpdate(ReadOnlySpan<byte> i, Span<byte> o) | |||
{ | |||
Span<byte> ob = new byte[o.Length + 128]; | |||
@@ -82,7 +84,7 @@ namespace Shadowsocks.Encryption.Stream | |||
return _ciphers; | |||
} | |||
protected override Dictionary<string, CipherInfo> getCiphers() | |||
protected override Dictionary<string, CipherInfo> GetCiphers() | |||
{ | |||
return _ciphers; | |||
} | |||
@@ -1,5 +1,6 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Runtime.CompilerServices; | |||
using NaCl.Core; | |||
namespace Shadowsocks.Encryption.Stream | |||
@@ -7,15 +8,16 @@ namespace Shadowsocks.Encryption.Stream | |||
public class StreamChachaNaClEncryptor : StreamEncryptor | |||
{ | |||
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... | |||
byte[] chachaBuf = new byte[MaxInputSize + BlockSize]; | |||
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... | |||
@@ -34,6 +36,7 @@ namespace Shadowsocks.Encryption.Stream | |||
return CipherUpdate(plain, cipher, true); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)] | |||
private int CipherUpdate(ReadOnlySpan<byte> i, Span<byte> o, bool enc) | |||
{ | |||
int len = i.Length; | |||
@@ -58,7 +61,7 @@ namespace Shadowsocks.Encryption.Stream | |||
return _ciphers; | |||
} | |||
protected override Dictionary<string, CipherInfo> getCiphers() | |||
protected override Dictionary<string, CipherInfo> GetCiphers() | |||
{ | |||
return _ciphers; | |||
} | |||
@@ -1,6 +1,7 @@ | |||
using NLog; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Runtime.CompilerServices; | |||
using System.Text; | |||
namespace Shadowsocks.Encryption.Stream | |||
@@ -26,7 +27,7 @@ namespace Shadowsocks.Encryption.Stream | |||
public StreamEncryptor(string method, string password) | |||
: base(method, password) | |||
{ | |||
CipherInfo = getCiphers()[method.ToLower()]; | |||
CipherInfo = GetCiphers()[method.ToLower()]; | |||
cipherFamily = CipherInfo.Type; | |||
StreamCipherParameter parameter = (StreamCipherParameter)CipherInfo.CipherParameter; | |||
keyLen = parameter.KeySize; | |||
@@ -37,7 +38,7 @@ namespace Shadowsocks.Encryption.Stream | |||
logger.Dump($"key {instanceId}", key, keyLen); | |||
} | |||
protected abstract Dictionary<string, CipherInfo> getCiphers(); | |||
protected abstract Dictionary<string, CipherInfo> GetCiphers(); | |||
private void InitKey(string password) | |||
{ | |||
@@ -51,6 +52,7 @@ namespace Shadowsocks.Encryption.Stream | |||
LegacyDeriveKey(passbuf, key, keyLen); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public static void LegacyDeriveKey(byte[] password, byte[] key, int keylen) | |||
{ | |||
byte[] result = new byte[password.Length + MD5Length]; | |||
@@ -73,7 +75,7 @@ namespace Shadowsocks.Encryption.Stream | |||
} | |||
} | |||
protected virtual void initCipher(byte[] iv, bool isEncrypt) | |||
protected virtual void InitCipher(byte[] iv, bool isEncrypt) | |||
{ | |||
if (ivLen == 0) | |||
{ | |||
@@ -88,6 +90,7 @@ namespace Shadowsocks.Encryption.Stream | |||
protected abstract int CipherDecrypt(Span<byte> plain, ReadOnlySpan<byte> cipher); | |||
#region TCP | |||
[MethodImpl(MethodImplOptions.Synchronized | MethodImplOptions.AggressiveOptimization)] | |||
public override int Encrypt(ReadOnlySpan<byte> plain, Span<byte> cipher) | |||
{ | |||
int cipherOffset = 0; | |||
@@ -96,7 +99,7 @@ namespace Shadowsocks.Encryption.Stream | |||
{ | |||
// Generate IV | |||
byte[] ivBytes = RNG.GetBytes(ivLen); | |||
initCipher(ivBytes, true); | |||
InitCipher(ivBytes, true); | |||
ivBytes.CopyTo(cipher); | |||
cipherOffset = ivLen; | |||
cipher = cipher.Slice(cipherOffset); | |||
@@ -111,9 +114,9 @@ namespace Shadowsocks.Encryption.Stream | |||
} | |||
private int recieveCtr = 0; | |||
[MethodImpl(MethodImplOptions.Synchronized | MethodImplOptions.AggressiveOptimization)] | |||
public override int Decrypt(Span<byte> plain, ReadOnlySpan<byte> cipher) | |||
{ | |||
Span<byte> tmp;// = cipher; | |||
logger.Trace($"{instanceId} decrypt TCP, read iv: {!ivReady}"); | |||
int cipherOffset = 0; | |||
@@ -135,14 +138,13 @@ namespace Shadowsocks.Encryption.Stream | |||
{ | |||
// read iv | |||
byte[] iv = sharedBuffer.AsSpan(0, ivLen).ToArray(); | |||
initCipher(iv, false); | |||
InitCipher(iv, false); | |||
} | |||
else | |||
{ | |||
initCipher(Array.Empty<byte>(), false); | |||
InitCipher(Array.Empty<byte>(), false); | |||
} | |||
cipherOffset += ivLen; | |||
tmp = sharedBuffer.AsSpan(ivLen, recieveCtr - ivLen); | |||
} | |||
// read all data from buffer | |||
@@ -156,17 +158,19 @@ namespace Shadowsocks.Encryption.Stream | |||
#endregion | |||
#region UDP | |||
[MethodImpl(MethodImplOptions.Synchronized | MethodImplOptions.AggressiveOptimization)] | |||
public override int EncryptUDP(ReadOnlySpan<byte> plain, Span<byte> cipher) | |||
{ | |||
byte[] iv = RNG.GetBytes(ivLen); | |||
iv.CopyTo(cipher); | |||
initCipher(iv, true); | |||
InitCipher(iv, true); | |||
return ivLen + CipherEncrypt(plain, cipher.Slice(ivLen)); | |||
} | |||
[MethodImpl(MethodImplOptions.Synchronized | MethodImplOptions.AggressiveOptimization)] | |||
public override int DecryptUDP(Span<byte> plain, ReadOnlySpan<byte> cipher) | |||
{ | |||
initCipher(cipher.Slice(0, ivLen).ToArray(), false); | |||
InitCipher(cipher.Slice(0, ivLen).ToArray(), false); | |||
return CipherDecrypt(plain, cipher.Slice(ivLen)); | |||
} | |||
@@ -33,7 +33,7 @@ namespace Shadowsocks.Encryption.Stream | |||
return _ciphers; | |||
} | |||
protected override Dictionary<string, CipherInfo> getCiphers() | |||
protected override Dictionary<string, CipherInfo> GetCiphers() | |||
{ | |||
return _ciphers; | |||
} | |||
@@ -1,5 +1,6 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Runtime.CompilerServices; | |||
namespace Shadowsocks.Encryption.Stream | |||
{ | |||
@@ -11,9 +12,9 @@ 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) | |||
{ | |||
@@ -39,6 +40,7 @@ namespace Shadowsocks.Encryption.Stream | |||
return CipherUpdate(cipher, plain); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
private int CipherUpdate(ReadOnlySpan<byte> i, Span<byte> o) | |||
{ | |||
// don't know why we need third array, but it works... | |||
@@ -62,7 +64,7 @@ namespace Shadowsocks.Encryption.Stream | |||
return _ciphers; | |||
} | |||
protected override Dictionary<string, CipherInfo> getCiphers() | |||
protected override Dictionary<string, CipherInfo> GetCiphers() | |||
{ | |||
return _ciphers; | |||
} | |||
@@ -75,7 +77,7 @@ namespace Shadowsocks.Encryption.Stream | |||
public int index2 = 0; | |||
} | |||
private Context ctx = new Context(); | |||
private readonly Context ctx = new Context(); | |||
private byte[] SBox(byte[] key) | |||
{ | |||
@@ -96,6 +98,7 @@ namespace Shadowsocks.Encryption.Stream | |||
return s; | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveOptimization)] | |||
private void RC4(Context ctx, Span<byte> s, Span<byte> data, int length) | |||
{ | |||
for (int n = 0; n < length; n++) | |||
@@ -110,6 +113,7 @@ namespace Shadowsocks.Encryption.Stream | |||
} | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
private static void Swap(Span<byte> s, int i, int j) | |||
{ | |||
byte c = s[i]; | |||
@@ -11,7 +11,7 @@ namespace Shadowsocks.Test | |||
[TestClass] | |||
public class CryptographyTest | |||
{ | |||
Random random = new Random(); | |||
readonly Random random = new Random(); | |||
[TestMethod] | |||
@@ -161,9 +161,9 @@ namespace Shadowsocks.Test | |||
[TestMethod] | |||
public void TestStreamAesCfbBouncyCastleEncryption() | |||
{ | |||
TestEncryptionMethod(typeof(StreamAesBouncyCastleEncryptor), "aes-128-cfb"); | |||
TestEncryptionMethod(typeof(StreamAesBouncyCastleEncryptor), "aes-192-cfb"); | |||
TestEncryptionMethod(typeof(StreamAesBouncyCastleEncryptor), "aes-256-cfb"); | |||
TestEncryptionMethod(typeof(StreamAesCfbBouncyCastleEncryptor), "aes-128-cfb"); | |||
TestEncryptionMethod(typeof(StreamAesCfbBouncyCastleEncryptor), "aes-192-cfb"); | |||
TestEncryptionMethod(typeof(StreamAesCfbBouncyCastleEncryptor), "aes-256-cfb"); | |||
} | |||
[TestMethod] | |||
public void TestStreamChachaNaClEncryption() | |||