@@ -30,8 +30,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
{ | |||
base.InitCipher(salt, isEncrypt, isUdp); | |||
DeriveSessionKey(isEncrypt ? encryptSalt : decryptSalt, | |||
_Masterkey, sessionKey); | |||
DeriveSessionKey(salt, masterKey, sessionKey); | |||
} | |||
public override void cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen) | |||
@@ -42,17 +41,8 @@ namespace Shadowsocks.Encryption.AEAD | |||
byte[] cipherWithoutTag = new byte[clen]; | |||
Array.Copy(ciphertext, 0, cipherWithoutTag, 0, clen - tagLen); | |||
Array.Copy(ciphertext, clen - tagLen, tag, 0, tagLen); | |||
aes.Decrypt(decNonce, ciphertext, cipherWithoutTag, tag); | |||
aes.Decrypt(nonce, ciphertext, cipherWithoutTag, tag); | |||
} | |||
/*var cipher = new GcmBlockCipher(new AesEngine()); | |||
AeadParameters parameters = new AeadParameters(new KeyParameter(_sessionKey), tagLen * 8, _decNonce); | |||
cipher.Init(false, parameters); | |||
var plaintextBC = new byte[cipher.GetOutputSize((int)clen)]; | |||
var len = cipher.ProcessBytes(ciphertext, 0, (int)clen, plaintextBC, 0); | |||
cipher.DoFinal(plaintextBC, len); | |||
plen = (uint)(plaintextBC.Length); | |||
Array.Copy(plaintextBC, 0, plaintext, 0, plen);*/ | |||
} | |||
public override void cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen) | |||
@@ -61,21 +51,10 @@ namespace Shadowsocks.Encryption.AEAD | |||
{ | |||
byte[] tag = new byte[tagLen]; | |||
byte[] cipherWithoutTag = new byte[clen]; | |||
aes.Encrypt(encNonce, plaintext, cipherWithoutTag, tag); | |||
aes.Encrypt(nonce, plaintext, cipherWithoutTag, tag); | |||
cipherWithoutTag.CopyTo(ciphertext, 0); | |||
tag.CopyTo(ciphertext, clen); | |||
} | |||
/* | |||
var cipher = new GcmBlockCipher(new AesEngine()); | |||
AeadParameters parameters = new AeadParameters(new KeyParameter(_sessionKey), tagLen * 8, _encNonce); | |||
cipher.Init(true, parameters); | |||
var ciphertextBC = new byte[cipher.GetOutputSize((int)plen)]; | |||
var len = cipher.ProcessBytes(plaintext, 0, (int)plen, ciphertextBC, 0); | |||
cipher.DoFinal(ciphertextBC, len); | |||
clen = (uint)(ciphertextBC.Length); | |||
Array.Copy(ciphertextBC, 0, ciphertext, 0, clen);*/ | |||
} | |||
public override byte[] CipherDecrypt2(byte[] cipher) | |||
@@ -84,19 +63,15 @@ namespace Shadowsocks.Encryption.AEAD | |||
Span<byte> plainMem = new Span<byte>(new byte[cipherMem.Length]); | |||
using var aes = new AesGcm(sessionKey); | |||
aes.Decrypt(decNonce.AsSpan(), cipherMem.Span, tagMem.Span, plainMem); | |||
aes.Decrypt(nonce.AsSpan(), cipherMem.Span, tagMem.Span, plainMem); | |||
return plainMem.ToArray(); | |||
} | |||
public override byte[] CipherEncrypt2(byte[] plain) | |||
{ | |||
Span<byte> d = new Span<byte>(new byte[plain.Length + tagLen]); | |||
//Span<byte> cipherMem = new Span<byte>(new byte[plain.Length]); | |||
//Span<byte> tagMem = new Span<byte>(new byte[tagLen]); | |||
using var aes = new AesGcm(sessionKey); | |||
aes.Encrypt(encNonce.AsSpan(), plain.AsSpan(), d.Slice(0, plain.Length), d.Slice(plain.Length)); | |||
aes.Encrypt(nonce.AsSpan(), plain.AsSpan(), d.Slice(0, plain.Length), d.Slice(plain.Length)); | |||
return d.ToArray(); | |||
} | |||
@@ -32,27 +32,23 @@ namespace Shadowsocks.Encryption.AEAD | |||
{ | |||
base.InitCipher(salt, isEncrypt, isUdp); | |||
DeriveSessionKey(isEncrypt ? encryptSalt : decryptSalt, | |||
_Masterkey, sessionKey); | |||
DeriveSessionKey(salt, masterKey, sessionKey); | |||
} | |||
public override void cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen) | |||
{ | |||
var cipher = new GcmBlockCipher(new AesEngine()); | |||
AeadParameters parameters = new AeadParameters(new KeyParameter(sessionKey), tagLen * 8, decNonce); | |||
AeadParameters parameters = new AeadParameters(new KeyParameter(sessionKey), tagLen * 8, nonce); | |||
cipher.Init(false, parameters); | |||
//var plaintextBC = new byte[cipher.GetOutputSize(ciphertext.Length)]; | |||
var len = cipher.ProcessBytes(ciphertext, 0, ciphertext.Length, plaintext, 0); | |||
cipher.DoFinal(plaintext, len); | |||
//plen = (uint)(plaintext.Length); | |||
//Array.Copy(plaintextBC, 0, plaintext, 0, plaintext.Length); | |||
} | |||
public override void cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen) | |||
{ | |||
var cipher = new GcmBlockCipher(new AesEngine()); | |||
AeadParameters parameters = new AeadParameters(new KeyParameter(sessionKey), tagLen * 8, encNonce); | |||
AeadParameters parameters = new AeadParameters(new KeyParameter(sessionKey), tagLen * 8, nonce); | |||
cipher.Init(true, parameters); | |||
var ciphertextBC = new byte[cipher.GetOutputSize((int)plen)]; | |||
@@ -65,7 +61,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
public override byte[] CipherDecrypt2(byte[] cipher) | |||
{ | |||
var aes = new GcmBlockCipher(new AesEngine()); | |||
AeadParameters parameters = new AeadParameters(new KeyParameter(sessionKey), tagLen * 8, decNonce); | |||
AeadParameters parameters = new AeadParameters(new KeyParameter(sessionKey), tagLen * 8, nonce); | |||
aes.Init(false, parameters); | |||
byte[] plain = new byte[aes.GetOutputSize(cipher.Length)]; | |||
@@ -78,7 +74,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
public override byte[] CipherEncrypt2(byte[] plain) | |||
{ | |||
var aes = new GcmBlockCipher(new AesEngine()); | |||
AeadParameters parameters = new AeadParameters(new KeyParameter(sessionKey), tagLen * 8, encNonce); | |||
AeadParameters parameters = new AeadParameters(new KeyParameter(sessionKey), tagLen * 8, nonce); | |||
aes.Init(true, parameters); | |||
var cipher = new byte[aes.GetOutputSize(plain.Length)]; | |||
@@ -22,8 +22,8 @@ namespace Shadowsocks.Encryption.AEAD | |||
protected static byte[] _udpTmpBuf = new byte[65536]; | |||
// every connection should create its own buffer | |||
private ByteCircularBuffer _encCircularBuffer = new ByteCircularBuffer(MAX_INPUT_SIZE * 2); | |||
private ByteCircularBuffer _decCircularBuffer = new ByteCircularBuffer(MAX_INPUT_SIZE * 2); | |||
private ByteCircularBuffer buffer = new ByteCircularBuffer(MAX_INPUT_SIZE * 2); | |||
// private ByteCircularBuffer buffer = new ByteCircularBuffer(MAX_INPUT_SIZE * 2); | |||
public const int CHUNK_LEN_BYTES = 2; | |||
public const uint CHUNK_LEN_MASK = 0x3FFFu; | |||
@@ -32,29 +32,24 @@ namespace Shadowsocks.Encryption.AEAD | |||
protected string _method; | |||
protected CipherFamily _cipher; | |||
// internal name in the crypto library | |||
protected string _innerLibName; | |||
protected CipherInfo CipherInfo; | |||
protected static byte[] _Masterkey = null; | |||
protected static byte[] masterKey = null; | |||
protected byte[] sessionKey; | |||
protected int keyLen; | |||
protected int saltLen; | |||
protected int tagLen; | |||
protected int nonceLen; | |||
protected byte[] encryptSalt; | |||
protected byte[] decryptSalt; | |||
protected byte[] salt; | |||
protected object _nonceIncrementLock = new object(); | |||
protected byte[] encNonce; | |||
protected byte[] decNonce; | |||
protected byte[] nonce; | |||
// Is first packet | |||
protected bool _decryptSaltReceived; | |||
protected bool _encryptSaltSent; | |||
protected bool saltReady; | |||
// Is first chunk(tcp request) | |||
protected bool _tcpRequestSent; | |||
protected bool tcpRequestSent; | |||
public AEADEncryptor(string method, string password) | |||
: base(method, password) | |||
@@ -62,8 +57,8 @@ namespace Shadowsocks.Encryption.AEAD | |||
InitEncryptorInfo(method); | |||
InitKey(password); | |||
// Initialize all-zero nonce for each connection | |||
encNonce = new byte[nonceLen]; | |||
decNonce = new byte[nonceLen]; | |||
nonce = new byte[nonceLen]; | |||
nonce = new byte[nonceLen]; | |||
} | |||
protected abstract Dictionary<string, CipherInfo> getCiphers(); | |||
@@ -86,9 +81,9 @@ namespace Shadowsocks.Encryption.AEAD | |||
{ | |||
byte[] passbuf = Encoding.UTF8.GetBytes(password); | |||
// init master key | |||
if (_Masterkey == null) _Masterkey = new byte[keyLen]; | |||
if (_Masterkey.Length != keyLen) Array.Resize(ref _Masterkey, keyLen); | |||
DeriveKey(passbuf, _Masterkey, keyLen); | |||
if (masterKey == null) masterKey = new byte[keyLen]; | |||
if (masterKey.Length != keyLen) Array.Resize(ref masterKey, keyLen); | |||
DeriveKey(passbuf, masterKey, keyLen); | |||
// init session key | |||
if (sessionKey == null) sessionKey = new byte[keyLen]; | |||
} | |||
@@ -107,22 +102,14 @@ namespace Shadowsocks.Encryption.AEAD | |||
{ | |||
lock (_nonceIncrementLock) | |||
{ | |||
CryptoUtils.SodiumIncrement(isEncrypt ? encNonce : decNonce); | |||
CryptoUtils.SodiumIncrement(isEncrypt ? nonce : nonce); | |||
} | |||
} | |||
public virtual void InitCipher(byte[] salt, bool isEncrypt, bool isUdp) | |||
{ | |||
if (isEncrypt) | |||
{ | |||
encryptSalt = new byte[saltLen]; | |||
Array.Copy(salt, encryptSalt, saltLen); | |||
} | |||
else | |||
{ | |||
decryptSalt = new byte[saltLen]; | |||
Array.Copy(salt, decryptSalt, saltLen); | |||
} | |||
this.salt = new byte[saltLen]; | |||
Array.Copy(salt, this.salt, saltLen); | |||
logger.Dump("Salt", salt, saltLen); | |||
} | |||
@@ -169,14 +156,14 @@ namespace Shadowsocks.Encryption.AEAD | |||
public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength) | |||
{ | |||
Debug.Assert(_encCircularBuffer != null, "_encCircularBuffer != null"); | |||
Debug.Assert(buffer != null, "_encCircularBuffer != null"); | |||
_encCircularBuffer.Put(buf, 0, length); | |||
buffer.Put(buf, 0, length); | |||
outlength = 0; | |||
logger.Debug("---Start Encryption"); | |||
if (!_encryptSaltSent) | |||
if (!saltReady) | |||
{ | |||
_encryptSaltSent = true; | |||
saltReady = true; | |||
// Generate salt | |||
byte[] saltBytes = RNG.GetBytes(saltLen); | |||
InitCipher(saltBytes, true, false); | |||
@@ -185,13 +172,13 @@ namespace Shadowsocks.Encryption.AEAD | |||
logger.Debug($"_encryptSaltSent outlength {outlength}"); | |||
} | |||
if (!_tcpRequestSent) | |||
if (!tcpRequestSent) | |||
{ | |||
_tcpRequestSent = true; | |||
tcpRequestSent = true; | |||
// The first TCP request | |||
int encAddrBufLength; | |||
byte[] encAddrBufBytes = new byte[AddressBufferLength + tagLen * 2 + CHUNK_LEN_BYTES]; | |||
byte[] addrBytes = _encCircularBuffer.Get(AddressBufferLength); | |||
byte[] addrBytes = buffer.Get(AddressBufferLength); | |||
ChunkEncrypt(addrBytes, AddressBufferLength, encAddrBufBytes, out encAddrBufLength); | |||
Debug.Assert(encAddrBufLength == AddressBufferLength + tagLen * 2 + CHUNK_LEN_BYTES); | |||
Array.Copy(encAddrBufBytes, 0, outbuf, outlength, encAddrBufLength); | |||
@@ -202,10 +189,10 @@ namespace Shadowsocks.Encryption.AEAD | |||
// handle other chunks | |||
while (true) | |||
{ | |||
uint bufSize = (uint)_encCircularBuffer.Size; | |||
uint bufSize = (uint)buffer.Size; | |||
if (bufSize <= 0) return; | |||
var chunklength = (int)Math.Min(bufSize, CHUNK_LEN_MASK); | |||
byte[] chunkBytes = _encCircularBuffer.Get(chunklength); | |||
byte[] chunkBytes = buffer.Get(chunklength); | |||
int encChunkLength; | |||
byte[] encChunkBytes = new byte[chunklength + tagLen * 2 + CHUNK_LEN_BYTES]; | |||
ChunkEncrypt(chunkBytes, chunklength, encChunkBytes, out encChunkLength); | |||
@@ -219,7 +206,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
logger.Debug("enc outbuf almost full, giving up"); | |||
return; | |||
} | |||
bufSize = (uint)_encCircularBuffer.Size; | |||
bufSize = (uint)buffer.Size; | |||
if (bufSize <= 0) | |||
{ | |||
logger.Debug("No more data to encrypt, leaving"); | |||
@@ -231,24 +218,24 @@ namespace Shadowsocks.Encryption.AEAD | |||
public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength) | |||
{ | |||
Debug.Assert(_decCircularBuffer != null, "_decCircularBuffer != null"); | |||
Debug.Assert(buffer != null, "_decCircularBuffer != null"); | |||
int bufSize; | |||
outlength = 0; | |||
// drop all into buffer | |||
_decCircularBuffer.Put(buf, 0, length); | |||
buffer.Put(buf, 0, length); | |||
logger.Debug("---Start Decryption"); | |||
if (!_decryptSaltReceived) | |||
if (!saltReady) | |||
{ | |||
bufSize = _decCircularBuffer.Size; | |||
bufSize = buffer.Size; | |||
// check if we get the leading salt | |||
if (bufSize <= saltLen) | |||
{ | |||
// need more | |||
return; | |||
} | |||
_decryptSaltReceived = true; | |||
byte[] salt = _decCircularBuffer.Get(saltLen); | |||
saltReady = true; | |||
byte[] salt = buffer.Get(saltLen); | |||
InitCipher(salt, false, false); | |||
logger.Debug("get salt len " + saltLen); | |||
} | |||
@@ -256,7 +243,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
// handle chunks | |||
while (true) | |||
{ | |||
bufSize = _decCircularBuffer.Size; | |||
bufSize = buffer.Size; | |||
// check if we have any data | |||
if (bufSize <= 0) | |||
{ | |||
@@ -273,7 +260,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
#region Chunk Decryption | |||
byte[] encLenBytes = _decCircularBuffer.Peek(CHUNK_LEN_BYTES + tagLen); | |||
byte[] encLenBytes = buffer.Peek(CHUNK_LEN_BYTES + tagLen); | |||
//uint decChunkLenLength = 0; | |||
//byte[] decChunkLenBytes = new byte[CHUNK_LEN_BYTES]; | |||
// try to dec chunk len | |||
@@ -291,7 +278,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
throw new CryptoErrorException(); | |||
} | |||
logger.Debug("Get the real chunk len:" + chunkLen); | |||
bufSize = _decCircularBuffer.Size; | |||
bufSize = buffer.Size; | |||
if (bufSize < CHUNK_LEN_BYTES + tagLen /* we haven't remove them */+ chunkLen + tagLen) | |||
{ | |||
logger.Debug("No more data to decrypt one chunk"); | |||
@@ -301,8 +288,8 @@ namespace Shadowsocks.Encryption.AEAD | |||
// we have enough data to decrypt one chunk | |||
// drop chunk len and its tag from buffer | |||
_decCircularBuffer.Skip(CHUNK_LEN_BYTES + tagLen); | |||
byte[] encChunkBytes = _decCircularBuffer.Get(chunkLen + tagLen); | |||
buffer.Skip(CHUNK_LEN_BYTES + tagLen); | |||
byte[] encChunkBytes = buffer.Get(chunkLen + tagLen); | |||
//byte[] decChunkBytes = new byte[chunkLen]; | |||
//uint decChunkLen = 0; | |||
//cipherDecrypt(encChunkBytes, chunkLen + (uint)tagLen, decChunkBytes, ref decChunkLen); | |||
@@ -322,7 +309,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
logger.Debug("dec outbuf almost full, giving up"); | |||
return; | |||
} | |||
bufSize = _decCircularBuffer.Size; | |||
bufSize = buffer.Size; | |||
// check if we already done all of them | |||
if (bufSize <= 0) | |||
{ | |||
@@ -12,7 +12,6 @@ namespace Shadowsocks.Encryption.AEAD | |||
{ | |||
SnufflePoly1305 enc; | |||
SnufflePoly1305 dec; | |||
public AEADNaClEncryptor(string method, string password) : base(method, password) | |||
{ | |||
} | |||
@@ -20,34 +19,30 @@ namespace Shadowsocks.Encryption.AEAD | |||
public override void InitCipher(byte[] salt, bool isEncrypt, bool isUdp) | |||
{ | |||
base.InitCipher(salt, isEncrypt, isUdp); | |||
DeriveSessionKey(isEncrypt ? encryptSalt : decryptSalt, | |||
_Masterkey, sessionKey); | |||
DeriveSessionKey(salt, masterKey, sessionKey); | |||
SnufflePoly1305 tmp; | |||
switch (_cipher) | |||
{ | |||
default: | |||
case CipherFamily.Chacha20Poly1305: | |||
tmp = new ChaCha20Poly1305(sessionKey); | |||
enc = new ChaCha20Poly1305(sessionKey); | |||
break; | |||
case CipherFamily.XChacha20Poly1305: | |||
tmp = new XChaCha20Poly1305(sessionKey); | |||
enc = new XChaCha20Poly1305(sessionKey); | |||
break; | |||
} | |||
if (isEncrypt) enc = tmp; | |||
else dec = tmp; | |||
} | |||
public override void cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen) | |||
{ | |||
var pt = dec.Decrypt(ciphertext, null, decNonce); | |||
var pt = enc.Decrypt(ciphertext, null, nonce); | |||
pt.CopyTo(plaintext, 0); | |||
plen = (uint)pt.Length; | |||
} | |||
public override void cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen) | |||
{ | |||
var ct = enc.Encrypt(plaintext, null, encNonce); | |||
var ct = enc.Encrypt(plaintext, null, nonce); | |||
ct.CopyTo(ciphertext, 0); | |||
clen = (uint)ct.Length; | |||
} | |||
@@ -70,12 +65,12 @@ namespace Shadowsocks.Encryption.AEAD | |||
public override byte[] CipherEncrypt2(byte[] plain) | |||
{ | |||
return enc.Encrypt(plain, null, encNonce); | |||
return enc.Encrypt(plain, null, nonce); | |||
} | |||
public override byte[] CipherDecrypt2(byte[] cipher) | |||
{ | |||
return dec.Decrypt(cipher, null, decNonce); | |||
return enc.Decrypt(cipher, null, nonce); | |||
} | |||
} | |||
} |
@@ -13,25 +13,20 @@ namespace Shadowsocks.Encryption.Stream | |||
protected static byte[] _udpTmpBuf = new byte[65536]; | |||
// every connection should create its own buffer | |||
private ByteCircularBuffer _encCircularBuffer = new ByteCircularBuffer(TCPHandler.BufferSize * 2); | |||
private ByteCircularBuffer _decCircularBuffer = new ByteCircularBuffer(TCPHandler.BufferSize * 2); | |||
private ByteCircularBuffer buffer = new ByteCircularBuffer(TCPHandler.BufferSize * 2); | |||
protected Dictionary<string, CipherInfo> ciphers; | |||
protected byte[] _encryptIV; | |||
protected byte[] _decryptIV; | |||
// Is first packet | |||
protected bool _decryptIVReceived; | |||
protected bool _encryptIVSent; | |||
protected bool ivReady; | |||
protected string _method; | |||
protected CipherFamily _cipher; | |||
// internal name in the crypto library | |||
protected string _innerLibName; | |||
protected CipherInfo CipherInfo; | |||
// long-time master key | |||
protected static byte[] _key = null; | |||
protected static byte[] key = null; | |||
protected byte[] iv; | |||
protected int keyLen; | |||
protected int ivLen; | |||
@@ -59,9 +54,9 @@ namespace Shadowsocks.Encryption.Stream | |||
private void InitKey(string password) | |||
{ | |||
byte[] passbuf = Encoding.UTF8.GetBytes(password); | |||
if (_key == null) _key = new byte[keyLen]; | |||
if (_key.Length != keyLen) Array.Resize(ref _key, keyLen); | |||
LegacyDeriveKey(passbuf, _key, keyLen); | |||
key ??= new byte[keyLen]; | |||
if (key.Length != keyLen) Array.Resize(ref key, keyLen); | |||
LegacyDeriveKey(passbuf, key, keyLen); | |||
} | |||
public static void LegacyDeriveKey(byte[] password, byte[] key, int keylen) | |||
@@ -88,42 +83,36 @@ namespace Shadowsocks.Encryption.Stream | |||
protected virtual void initCipher(byte[] iv, bool isEncrypt) | |||
{ | |||
if (isEncrypt) | |||
{ | |||
_encryptIV = new byte[ivLen]; | |||
Array.Copy(iv, _encryptIV, ivLen); | |||
} | |||
else | |||
{ | |||
_decryptIV = new byte[ivLen]; | |||
Array.Copy(iv, _decryptIV, ivLen); | |||
} | |||
this.iv = new byte[ivLen]; | |||
Array.Copy(iv, this.iv, ivLen); | |||
} | |||
protected abstract void cipherUpdate(bool isEncrypt, int length, byte[] buf, byte[] outbuf); | |||
protected static void randBytes(byte[] buf, int length) { RNG.GetBytes(buf, length); } | |||
protected abstract int CipherEncrypt(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 | |||
public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength) | |||
{ | |||
int cipherOffset = 0; | |||
Debug.Assert(_encCircularBuffer != null, "_encCircularBuffer != null"); | |||
_encCircularBuffer.Put(buf, 0, length); | |||
if (!_encryptIVSent) | |||
Debug.Assert(buffer != null, "_encCircularBuffer != null"); | |||
buffer.Put(buf, 0, length); | |||
if (!ivReady) | |||
{ | |||
// Generate IV | |||
byte[] ivBytes = new byte[ivLen]; | |||
randBytes(ivBytes, ivLen); | |||
byte[] ivBytes = RNG.GetBytes(ivLen); | |||
initCipher(ivBytes, true); | |||
Array.Copy(ivBytes, 0, outbuf, 0, ivLen); | |||
cipherOffset = ivLen; | |||
_encryptIVSent = true; | |||
ivReady = true; | |||
} | |||
int size = _encCircularBuffer.Size; | |||
byte[] plain = _encCircularBuffer.Get(size); | |||
int size = buffer.Size; | |||
byte[] plain = buffer.Get(size); | |||
byte[] cipher = new byte[size]; | |||
cipherUpdate(true, size, plain, cipher); | |||
Buffer.BlockCopy(cipher, 0, outbuf, cipherOffset, size); | |||
@@ -132,29 +121,29 @@ namespace Shadowsocks.Encryption.Stream | |||
public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength) | |||
{ | |||
Debug.Assert(_decCircularBuffer != null, "_circularBuffer != null"); | |||
_decCircularBuffer.Put(buf, 0, length); | |||
if (!_decryptIVReceived) | |||
Debug.Assert(buffer != null, "_circularBuffer != null"); | |||
buffer.Put(buf, 0, length); | |||
if (!ivReady) | |||
{ | |||
if (_decCircularBuffer.Size <= ivLen) | |||
if (buffer.Size <= ivLen) | |||
{ | |||
// we need more data | |||
outlength = 0; | |||
return; | |||
} | |||
// start decryption | |||
_decryptIVReceived = true; | |||
ivReady = true; | |||
if (ivLen > 0) | |||
{ | |||
byte[] iv = _decCircularBuffer.Get(ivLen); | |||
byte[] iv = buffer.Get(ivLen); | |||
initCipher(iv, false); | |||
} | |||
else initCipher(Array.Empty<byte>(), false); | |||
} | |||
byte[] cipher = _decCircularBuffer.ToArray(); | |||
byte[] cipher = buffer.ToArray(); | |||
cipherUpdate(false, cipher.Length, cipher, outbuf); | |||
// move pointer only | |||
_decCircularBuffer.Skip(_decCircularBuffer.Size); | |||
buffer.Skip(buffer.Size); | |||
outlength = cipher.Length; | |||
// done the decryption | |||
} | |||
@@ -166,7 +155,7 @@ namespace Shadowsocks.Encryption.Stream | |||
public override void EncryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength) | |||
{ | |||
// Generate IV | |||
randBytes(outbuf, ivLen); | |||
RNG.GetBytes(outbuf, ivLen); | |||
initCipher(outbuf, true); | |||
lock (_udpTmpBuf) | |||
{ | |||
@@ -1,8 +1,5 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Shadowsocks.Encryption.Stream | |||
{ | |||
@@ -20,22 +17,19 @@ namespace Shadowsocks.Encryption.Stream | |||
if (_cipher == CipherFamily.Rc4Md5) | |||
{ | |||
byte[] temp = new byte[keyLen + ivLen]; | |||
Array.Copy(_key, 0, temp, 0, keyLen); | |||
Array.Copy(key, 0, temp, 0, keyLen); | |||
Array.Copy(iv, 0, temp, keyLen, ivLen); | |||
realkey = CryptoUtils.MD5(temp); | |||
} | |||
else | |||
{ | |||
realkey = _key; | |||
realkey = key; | |||
} | |||
sbox = SBox(realkey); | |||
} | |||
protected override void cipherUpdate(bool isEncrypt, int length, byte[] buf, byte[] outbuf) | |||
{ | |||
var ctx = isEncrypt ? enc_ctx : dec_ctx; | |||
byte[] t = new byte[length]; | |||
Array.Copy(buf, t, length); | |||
@@ -43,6 +37,24 @@ namespace Shadowsocks.Encryption.Stream | |||
Array.Copy(t, outbuf, length); | |||
} | |||
protected override int CipherEncrypt(Span<byte> plain, Span<byte> cipher) | |||
{ | |||
return DoRC4(plain, cipher); | |||
} | |||
protected override int CipherDecrypt(Span<byte> plain, Span<byte> cipher) | |||
{ | |||
return DoRC4(cipher, plain); | |||
} | |||
private int DoRC4(Span<byte> i, Span<byte> o) | |||
{ | |||
i.CopyTo(o); | |||
RC4(ctx, sbox, o, o.Length); | |||
return o.Length; | |||
} | |||
#region Ciphers | |||
private static readonly Dictionary<string, CipherInfo> _ciphers = new Dictionary<string, CipherInfo> | |||
{ | |||
// original RC4 doesn't use IV | |||
@@ -59,6 +71,7 @@ namespace Shadowsocks.Encryption.Stream | |||
{ | |||
return _ciphers; | |||
} | |||
#endregion | |||
#region RC4 | |||
class Context | |||
@@ -67,8 +80,7 @@ namespace Shadowsocks.Encryption.Stream | |||
public int index2 = 0; | |||
} | |||
private Context enc_ctx = new Context(); | |||
private Context dec_ctx = new Context(); | |||
private Context ctx = new Context(); | |||
private byte[] SBox(byte[] key) | |||
{ | |||
@@ -89,7 +101,7 @@ namespace Shadowsocks.Encryption.Stream | |||
return s; | |||
} | |||
private void RC4(Context ctx, byte[] s, byte[] data, int length) | |||
private void RC4(Context ctx, Span<byte> s, Span<byte> data, int length) | |||
{ | |||
for (int n = 0; n < length; n++) | |||
{ | |||
@@ -99,12 +111,11 @@ namespace Shadowsocks.Encryption.Stream | |||
ctx.index2 = (ctx.index2 + s[ctx.index1]) & 255; | |||
Swap(s, ctx.index1, ctx.index2); | |||
data[n] = (byte)(b ^ s[(s[ctx.index1] + s[ctx.index2]) & 255]); | |||
} | |||
} | |||
private static void Swap(byte[] s, int i, int j) | |||
private static void Swap(Span<byte> s, int i, int j) | |||
{ | |||
byte c = s[i]; | |||
@@ -52,6 +52,37 @@ namespace Shadowsocks.Encryption.Stream | |||
} | |||
} | |||
protected override int CipherDecrypt(Span<byte> plain, Span<byte> cipher) | |||
{ | |||
if (_cipher == 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 (_cipher == 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) }, | |||
@@ -67,6 +98,7 @@ namespace Shadowsocks.Encryption.Stream | |||
{ | |||
return _ciphers; | |||
} | |||
#endregion | |||
#region Table | |||
private byte[] _encryptTable = new byte[256]; | |||