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