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