@@ -125,17 +125,18 @@ especially for feature development. | |||||
#### Open Source Components / Libraries | #### Open Source Components / Libraries | ||||
``` | ``` | ||||
Caseless.Fody (MIT) https://github.com/Fody/Caseless | |||||
Costura.Fody (MIT) https://github.com/Fody/Costura | |||||
Fody (MIT) https://github.com/Fody/Fody | |||||
GlobalHotKey (GPLv3) https://github.com/kirmir/GlobalHotKey | |||||
Newtonsoft.Json (MIT) https://www.newtonsoft.com/json | |||||
StringEx.CS () https://github.com/LazyMode/StringEx | |||||
ZXing.Net (Apache 2.0) https://github.com/micjahn/ZXing.Net | |||||
libsscrypto (GPLv2) https://github.com/shadowsocks/libsscrypto | |||||
Privoxy (GPLv2) https://www.privoxy.org | |||||
Sysproxy () https://github.com/Noisyfox/sysproxy | |||||
Caseless.Fody (MIT) https://github.com/Fody/Caseless | |||||
Costura.Fody (MIT) https://github.com/Fody/Costura | |||||
Fody (MIT) https://github.com/Fody/Fody | |||||
GlobalHotKey (GPLv3) https://github.com/kirmir/GlobalHotKey | |||||
Newtonsoft.Json (MIT) https://www.newtonsoft.com/json | |||||
StringEx.CS () https://github.com/LazyMode/StringEx | |||||
ZXing.Net (Apache 2.0) https://github.com/micjahn/ZXing.Net | |||||
BouncyCastle.NetCore (MIT) https://github.com/chrishaly/bc-csharp | |||||
NaCl.Core (MIT) https://github.com/idaviddesmet/NaCl.Core | |||||
Privoxy (GPLv2) https://www.privoxy.org | |||||
Sysproxy () https://github.com/Noisyfox/sysproxy | |||||
``` | ``` | ||||
@@ -1,9 +1,6 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | |||||
using System.Security.Cryptography; | using System.Security.Cryptography; | ||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace Shadowsocks.Encryption.AEAD | namespace Shadowsocks.Encryption.AEAD | ||||
{ | { | ||||
@@ -34,7 +31,7 @@ namespace Shadowsocks.Encryption.AEAD | |||||
public override int CipherEncrypt(Span<byte> plain, Span<byte> cipher) | public override int CipherEncrypt(Span<byte> plain, Span<byte> cipher) | ||||
{ | { | ||||
using var 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.AsSpan(), plain, cipher.Slice(0, plain.Length), cipher.Slice(plain.Length, tagLen)); | ||||
return plain.Length + tagLen; | return plain.Length + tagLen; | ||||
} | } | ||||
@@ -42,9 +39,9 @@ namespace Shadowsocks.Encryption.AEAD | |||||
public override int CipherDecrypt(Span<byte> plain, Span<byte> cipher) | public override int CipherDecrypt(Span<byte> plain, Span<byte> cipher) | ||||
{ | { | ||||
int clen = cipher.Length - tagLen; | int clen = cipher.Length - tagLen; | ||||
using var aes = new AesGcm(sessionKey); | |||||
var ciphertxt = cipher.Slice(0, clen); | |||||
var tag = cipher.Slice(clen); | |||||
using AesGcm aes = new AesGcm(sessionKey); | |||||
Span<byte> ciphertxt = cipher.Slice(0, clen); | |||||
Span<byte> tag = cipher.Slice(clen); | |||||
aes.Decrypt(nonce.AsSpan(), ciphertxt, tag, plain.Slice(0, clen)); | aes.Decrypt(nonce.AsSpan(), ciphertxt, tag, plain.Slice(0, clen)); | ||||
return clen; | return clen; | ||||
} | } | ||||
@@ -1,11 +1,11 @@ | |||||
using NLog; | using NLog; | ||||
using Shadowsocks.Controller; | |||||
using Shadowsocks.Encryption.Exception; | |||||
using Shadowsocks.Encryption.Stream; | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Net; | using System.Net; | ||||
using System.Text; | using System.Text; | ||||
using Shadowsocks.Controller; | |||||
using Shadowsocks.Encryption.Exception; | |||||
using Shadowsocks.Encryption.Stream; | |||||
namespace Shadowsocks.Encryption.AEAD | namespace Shadowsocks.Encryption.AEAD | ||||
{ | { | ||||
@@ -48,7 +48,7 @@ namespace Shadowsocks.Encryption.AEAD | |||||
{ | { | ||||
CipherInfo = getCiphers()[method.ToLower()]; | CipherInfo = getCiphers()[method.ToLower()]; | ||||
cipherFamily = CipherInfo.Type; | cipherFamily = CipherInfo.Type; | ||||
var parameter = (AEADCipherParameter)CipherInfo.CipherParameter; | |||||
AEADCipherParameter parameter = (AEADCipherParameter)CipherInfo.CipherParameter; | |||||
keyLen = parameter.KeySize; | keyLen = parameter.KeySize; | ||||
saltLen = parameter.SaltSize; | saltLen = parameter.SaltSize; | ||||
tagLen = parameter.TagSize; | tagLen = parameter.TagSize; | ||||
@@ -65,8 +65,16 @@ 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); | |||||
if (masterKey == null) | |||||
{ | |||||
masterKey = new byte[keyLen]; | |||||
} | |||||
if (masterKey.Length != keyLen) | |||||
{ | |||||
Array.Resize(ref masterKey, keyLen); | |||||
} | |||||
DeriveKey(passbuf, masterKey, keyLen); | DeriveKey(passbuf, masterKey, keyLen); | ||||
// init session key | // init session key | ||||
sessionKey = new byte[keyLen]; | sessionKey = new byte[keyLen]; | ||||
@@ -142,8 +150,12 @@ namespace Shadowsocks.Encryption.AEAD | |||||
{ | { | ||||
// calculate next chunk size | // calculate next chunk size | ||||
int bufSize = tmp.Length; | int bufSize = tmp.Length; | ||||
if (bufSize <= 0) return; | |||||
var chunklength = (int)Math.Min(bufSize, ChunkLengthMask); | |||||
if (bufSize <= 0) | |||||
{ | |||||
return; | |||||
} | |||||
int chunklength = (int)Math.Min(bufSize, ChunkLengthMask); | |||||
// read next chunk | // read next chunk | ||||
byte[] chunkBytes = tmp.Slice(0, chunklength).ToArray(); | byte[] chunkBytes = tmp.Slice(0, chunklength).ToArray(); | ||||
tmp = tmp.Slice(chunklength); | tmp = tmp.Slice(chunklength); | ||||
@@ -1,10 +1,7 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
using NaCl.Core; | |||||
using NaCl.Core; | |||||
using NaCl.Core.Base; | using NaCl.Core.Base; | ||||
using System; | |||||
using System.Collections.Generic; | |||||
namespace Shadowsocks.Encryption.AEAD | namespace Shadowsocks.Encryption.AEAD | ||||
{ | { | ||||
@@ -48,17 +45,17 @@ namespace Shadowsocks.Encryption.AEAD | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
public override int CipherEncrypt(Span<byte> plain, Span<byte> cipher) | public override int CipherEncrypt(Span<byte> plain, Span<byte> cipher) | ||||
{ | { | ||||
var 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, Span<byte> cipher) | public override int CipherDecrypt(Span<byte> plain, Span<byte> cipher) | ||||
{ | { | ||||
var 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,5 +1,4 @@ | |||||
using Shadowsocks.Controller; | using Shadowsocks.Controller; | ||||
using System; | |||||
namespace Shadowsocks.Encryption | namespace Shadowsocks.Encryption | ||||
{ | { | ||||
@@ -37,7 +36,10 @@ namespace Shadowsocks.Encryption | |||||
public class StreamCipherParameter : CipherParameter | public class StreamCipherParameter : CipherParameter | ||||
{ | { | ||||
public int IvSize; | public int IvSize; | ||||
public override string ToString() => $"stream (key:{KeySize * 8}, iv:{IvSize * 8})"; | |||||
public override string ToString() | |||||
{ | |||||
return $"stream (key:{KeySize * 8}, iv:{IvSize * 8})"; | |||||
} | |||||
} | } | ||||
public class AEADCipherParameter : CipherParameter | public class AEADCipherParameter : CipherParameter | ||||
@@ -45,7 +47,10 @@ namespace Shadowsocks.Encryption | |||||
public int SaltSize; | public int SaltSize; | ||||
public int TagSize; | public int TagSize; | ||||
public int NonceSize; | public int NonceSize; | ||||
public override string ToString() => $"aead (key:{KeySize * 8}, salt:{SaltSize * 8}, tag:{TagSize * 8}, nonce:{NonceSize * 8})"; | |||||
public override string ToString() | |||||
{ | |||||
return $"aead (key:{KeySize * 8}, salt:{SaltSize * 8}, tag:{TagSize * 8}, nonce:{NonceSize * 8})"; | |||||
} | |||||
} | } | ||||
public class CipherInfo | public class CipherInfo | ||||
@@ -95,7 +100,10 @@ namespace Shadowsocks.Encryption | |||||
} | } | ||||
public string ToString(bool verbose) | public string ToString(bool verbose) | ||||
{ | { | ||||
if (!verbose) return ToString(); | |||||
if (!verbose) | |||||
{ | |||||
return ToString(); | |||||
} | |||||
return $"{Name} {StandardState} {CipherParameter}"; | return $"{Name} {StandardState} {CipherParameter}"; | ||||
} | } | ||||
@@ -54,7 +54,10 @@ namespace Shadowsocks.Encryption | |||||
bool o = true; // overflow flag | bool o = true; // overflow flag | ||||
for (int i = 0; i < salt.Length; i++) | for (int i = 0; i < salt.Length; i++) | ||||
{ | { | ||||
if (!o) continue; | |||||
if (!o) | |||||
{ | |||||
continue; | |||||
} | |||||
salt[i]++; | salt[i]++; | ||||
o = salt[i] == 0; | o = salt[i] == 0; | ||||
@@ -66,7 +69,11 @@ namespace Shadowsocks.Encryption | |||||
bool o = true; // overflow flag | bool o = true; // overflow flag | ||||
for (int i = 0; i < salt.Length; i++) | for (int i = 0; i < salt.Length; i++) | ||||
{ | { | ||||
if (!o) continue; | |||||
if (!o) | |||||
{ | |||||
continue; | |||||
} | |||||
salt[i]++; | salt[i]++; | ||||
o = salt[i] == 0; | o = salt[i] == 0; | ||||
} | } | ||||
@@ -2,9 +2,8 @@ | |||||
// changes: 5th parameter for ProcessBlock, to process without change internal state | // changes: 5th parameter for ProcessBlock, to process without change internal state | ||||
using System; | |||||
using Org.BouncyCastle.Crypto.Parameters; | using Org.BouncyCastle.Crypto.Parameters; | ||||
using System; | |||||
namespace Org.BouncyCastle.Crypto.Modes | namespace Org.BouncyCastle.Crypto.Modes | ||||
{ | { | ||||
@@ -34,10 +33,10 @@ namespace Org.BouncyCastle.Crypto.Modes | |||||
int bitBlockSize) | int bitBlockSize) | ||||
{ | { | ||||
this.cipher = cipher; | this.cipher = cipher; | ||||
this.blockSize = bitBlockSize / 8; | |||||
this.IV = new byte[cipher.GetBlockSize()]; | |||||
this.cfbV = new byte[cipher.GetBlockSize()]; | |||||
this.cfbOutV = new byte[cipher.GetBlockSize()]; | |||||
blockSize = bitBlockSize / 8; | |||||
IV = new byte[cipher.GetBlockSize()]; | |||||
cfbV = new byte[cipher.GetBlockSize()]; | |||||
cfbOutV = new byte[cipher.GetBlockSize()]; | |||||
} | } | ||||
/** | /** | ||||
* return the underlying block cipher that we are wrapping. | * return the underlying block cipher that we are wrapping. | ||||
@@ -63,7 +62,7 @@ namespace Org.BouncyCastle.Crypto.Modes | |||||
bool forEncryption, | bool forEncryption, | ||||
ICipherParameters parameters) | ICipherParameters parameters) | ||||
{ | { | ||||
this.encrypting = forEncryption; | |||||
encrypting = forEncryption; | |||||
if (parameters is ParametersWithIV) | if (parameters is ParametersWithIV) | ||||
{ | { | ||||
ParametersWithIV ivParam = (ParametersWithIV)parameters; | ParametersWithIV ivParam = (ParametersWithIV)parameters; | ||||
@@ -89,15 +88,9 @@ namespace Org.BouncyCastle.Crypto.Modes | |||||
* @return the name of the underlying algorithm followed by "/CFB" | * @return the name of the underlying algorithm followed by "/CFB" | ||||
* and the block size in bits. | * and the block size in bits. | ||||
*/ | */ | ||||
public string AlgorithmName | |||||
{ | |||||
get { return cipher.AlgorithmName + "/CFB" + (blockSize * 8); } | |||||
} | |||||
public string AlgorithmName => cipher.AlgorithmName + "/CFB" + (blockSize * 8); | |||||
public bool IsPartialBlockOkay | |||||
{ | |||||
get { return true; } | |||||
} | |||||
public bool IsPartialBlockOkay => true; | |||||
/** | /** | ||||
* return the block size we are operating at. | * return the block size we are operating at. | ||||
@@ -1,8 +1,6 @@ | |||||
using Org.BouncyCastle.Crypto; | |||||
using Org.BouncyCastle.Crypto.Engines; | |||||
using Org.BouncyCastle.Crypto.Engines; | |||||
using Org.BouncyCastle.Crypto.Modes; | using Org.BouncyCastle.Crypto.Modes; | ||||
using Org.BouncyCastle.Crypto.Parameters; | using Org.BouncyCastle.Crypto.Parameters; | ||||
using Org.BouncyCastle.Security; | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
@@ -13,12 +11,10 @@ namespace Shadowsocks.Encryption.Stream | |||||
{ | { | ||||
byte[] cfbBuf = new byte[MaxInputSize + 128]; | byte[] cfbBuf = new byte[MaxInputSize + 128]; | ||||
int ptr = 0; | int ptr = 0; | ||||
//ExtendedCfbBufferedBlockCipher c; | |||||
ExtendedCfbBlockCipher b; | |||||
readonly ExtendedCfbBlockCipher b; | |||||
public StreamAesBouncyCastleEncryptor(string method, string password) : base(method, password) | public StreamAesBouncyCastleEncryptor(string method, string password) : base(method, password) | ||||
{ | { | ||||
b = new ExtendedCfbBlockCipher(new AesEngine(), 128); | b = new ExtendedCfbBlockCipher(new AesEngine(), 128); | ||||
// c = new ExtendedCfbBufferedBlockCipher(b); | |||||
} | } | ||||
protected override void initCipher(byte[] iv, bool isEncrypt) | protected override void initCipher(byte[] iv, bool isEncrypt) | ||||
@@ -59,14 +55,18 @@ namespace Shadowsocks.Encryption.Stream | |||||
tmp.CopyTo(ob.Slice(readPtr)); | tmp.CopyTo(ob.Slice(readPtr)); | ||||
readPtr += blkSize; | readPtr += blkSize; | ||||
} | } | ||||
if (readPtr != blkSize * blkCount) throw new System.Exception(); | |||||
b.ProcessBlock(cfbBuf, readPtr, tmp, 0, false); | |||||
tmp.CopyTo(ob.Slice(readPtr)); | |||||
Array.Copy(cfbBuf, readPtr, cfbBuf, 0, restSize); | |||||
if (restSize != 0) | |||||
{ | |||||
readPtr = blkSize * blkCount; | |||||
// process last (partial) block without update state | |||||
b.ProcessBlock(cfbBuf, readPtr, tmp, 0, false); | |||||
tmp.CopyTo(ob.Slice(readPtr)); | |||||
// write back the partial block block | |||||
Array.Copy(cfbBuf, readPtr, cfbBuf, 0, restSize); | |||||
} | |||||
// cut correct part to output | |||||
ob.Slice(ptr, o.Length).CopyTo(o); | ob.Slice(ptr, o.Length).CopyTo(o); | ||||
ptr = restSize; | ptr = restSize; | ||||
} | } | ||||
#region Cipher Info | #region Cipher Info | ||||
@@ -9,6 +9,9 @@ namespace Shadowsocks.Encryption.Stream | |||||
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 | |||||
// 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... | ||||
@@ -29,7 +29,7 @@ namespace Shadowsocks.Encryption.Stream | |||||
{ | { | ||||
CipherInfo = getCiphers()[method.ToLower()]; | CipherInfo = getCiphers()[method.ToLower()]; | ||||
cipherFamily = CipherInfo.Type; | cipherFamily = CipherInfo.Type; | ||||
var parameter = (StreamCipherParameter)CipherInfo.CipherParameter; | |||||
StreamCipherParameter parameter = (StreamCipherParameter)CipherInfo.CipherParameter; | |||||
keyLen = parameter.KeySize; | keyLen = parameter.KeySize; | ||||
ivLen = parameter.IvSize; | ivLen = parameter.IvSize; | ||||
@@ -44,7 +44,11 @@ namespace Shadowsocks.Encryption.Stream | |||||
{ | { | ||||
byte[] passbuf = Encoding.UTF8.GetBytes(password); | byte[] passbuf = Encoding.UTF8.GetBytes(password); | ||||
key ??= new byte[keyLen]; | key ??= new byte[keyLen]; | ||||
if (key.Length != keyLen) Array.Resize(ref key, keyLen); | |||||
if (key.Length != keyLen) | |||||
{ | |||||
Array.Resize(ref key, keyLen); | |||||
} | |||||
LegacyDeriveKey(passbuf, key, keyLen); | LegacyDeriveKey(passbuf, key, keyLen); | ||||
} | } | ||||
@@ -72,7 +76,11 @@ namespace Shadowsocks.Encryption.Stream | |||||
protected virtual void initCipher(byte[] iv, bool isEncrypt) | protected virtual void initCipher(byte[] iv, bool isEncrypt) | ||||
{ | { | ||||
if (ivLen == 0) return; | |||||
if (ivLen == 0) | |||||
{ | |||||
return; | |||||
} | |||||
this.iv = new byte[ivLen]; | this.iv = new byte[ivLen]; | ||||
Array.Copy(iv, this.iv, ivLen); | Array.Copy(iv, this.iv, ivLen); | ||||
} | } | ||||
@@ -115,7 +123,7 @@ namespace Shadowsocks.Encryption.Stream | |||||
{ | { | ||||
Span<byte> tmp = buf.AsSpan(0, length); | Span<byte> tmp = buf.AsSpan(0, length); | ||||
logger.Trace($"{instanceId} decrypt TCP, read iv: {!ivReady}"); | logger.Trace($"{instanceId} decrypt TCP, read iv: {!ivReady}"); | ||||
// is first packet, need read iv | // is first packet, need read iv | ||||
if (!ivReady) | if (!ivReady) | ||||
{ | { | ||||
@@ -137,7 +145,11 @@ namespace Shadowsocks.Encryption.Stream | |||||
byte[] iv = sharedBuffer.AsSpan(0, ivLen).ToArray(); | byte[] iv = sharedBuffer.AsSpan(0, ivLen).ToArray(); | ||||
initCipher(iv, false); | initCipher(iv, false); | ||||
} | } | ||||
else initCipher(Array.Empty<byte>(), false); | |||||
else | |||||
{ | |||||
initCipher(Array.Empty<byte>(), false); | |||||
} | |||||
tmp = sharedBuffer.AsSpan(ivLen, recieveCtr - ivLen); | tmp = sharedBuffer.AsSpan(ivLen, recieveCtr - ivLen); | ||||
} | } | ||||
@@ -1,7 +1,5 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Runtime.CompilerServices; | |||||
using System.Text; | |||||
namespace Shadowsocks.Encryption.Stream | namespace Shadowsocks.Encryption.Stream | ||||
{ | { | ||||