@@ -0,0 +1,91 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Security.Cryptography; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Shadowsocks.Encryption.AEAD | |||
{ | |||
public class AEADAesGcmNativeEncryptor : AEADNativeEncryptor | |||
{ | |||
public AEADAesGcmNativeEncryptor(string method, string password) : base(method, password) | |||
{ | |||
} | |||
static int CIPHER_AES = 1; // dummy | |||
private static readonly Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo> | |||
{ | |||
{"aes-128-gcm", new EncryptorInfo("AES-128-GCM", 16, 16, 12, 16, CIPHER_AES)}, | |||
{"aes-192-gcm", new EncryptorInfo("AES-192-GCM", 24, 24, 12, 16, CIPHER_AES)}, | |||
{"aes-256-gcm", new EncryptorInfo("AES-256-GCM", 32, 32, 12, 16, CIPHER_AES)}, | |||
}; | |||
protected override Dictionary<string, EncryptorInfo> getCiphers() | |||
{ | |||
return _ciphers; | |||
} | |||
public override void InitCipher(byte[] salt, bool isEncrypt, bool isUdp) | |||
{ | |||
base.InitCipher(salt, isEncrypt, isUdp); | |||
DeriveSessionKey(isEncrypt ? encryptSalt : decryptSalt, | |||
_Masterkey, sessionKey); | |||
} | |||
public override void cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen) | |||
{ | |||
using (var aes = new AesGcm(sessionKey)) | |||
{ | |||
byte[] tag = new byte[tagLen]; | |||
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); | |||
} | |||
/*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) | |||
{ | |||
using (var aes = new AesGcm(sessionKey)) | |||
{ | |||
byte[] tag = new byte[tagLen]; | |||
byte[] cipherWithoutTag = new byte[clen]; | |||
aes.Encrypt(encNonce, 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 static List<string> SupportedCiphers() | |||
{ | |||
return new List<string>(_ciphers.Keys); | |||
} | |||
public override void Dispose() | |||
{ | |||
} | |||
} | |||
} |
@@ -9,7 +9,7 @@ using System.Threading.Tasks; | |||
namespace Shadowsocks.Encryption.AEAD | |||
{ | |||
public class AEADBouncyCastleEncryptor : AEADEncryptor, IDisposable | |||
public class AEADBouncyCastleEncryptor : AEADEncryptor | |||
{ | |||
static int CIPHER_AES = 1; // dummy | |||
@@ -35,14 +35,14 @@ namespace Shadowsocks.Encryption.AEAD | |||
{ | |||
base.InitCipher(salt, isEncrypt, isUdp); | |||
DeriveSessionKey(isEncrypt ? _encryptSalt : _decryptSalt, | |||
_Masterkey, _sessionKey); | |||
DeriveSessionKey(isEncrypt ? encryptSalt : decryptSalt, | |||
_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, decNonce); | |||
cipher.Init(false, parameters); | |||
var plaintextBC = new byte[cipher.GetOutputSize((int)clen)]; | |||
@@ -55,7 +55,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
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, encNonce); | |||
cipher.Init(true, parameters); | |||
var ciphertextBC = new byte[cipher.GetOutputSize((int)plen)]; | |||
@@ -37,18 +37,19 @@ namespace Shadowsocks.Encryption.AEAD | |||
protected string _innerLibName; | |||
protected EncryptorInfo CipherInfo; | |||
protected static byte[] _Masterkey = null; | |||
protected byte[] _sessionKey; | |||
protected byte[] sessionKey; | |||
protected int keyLen; | |||
protected int saltLen; | |||
protected int tagLen; | |||
protected int nonceLen; | |||
protected byte[] _encryptSalt; | |||
protected byte[] _decryptSalt; | |||
protected byte[] encryptSalt; | |||
protected byte[] decryptSalt; | |||
protected object _nonceIncrementLock = new object(); | |||
protected byte[] _encNonce; | |||
protected byte[] _decNonce; | |||
protected byte[] encNonce; | |||
protected byte[] decNonce; | |||
// Is first packet | |||
protected bool _decryptSaltReceived; | |||
protected bool _encryptSaltSent; | |||
@@ -62,8 +63,8 @@ namespace Shadowsocks.Encryption.AEAD | |||
InitEncryptorInfo(method); | |||
InitKey(password); | |||
// Initialize all-zero nonce for each connection | |||
_encNonce = new byte[nonceLen]; | |||
_decNonce = new byte[nonceLen]; | |||
encNonce = new byte[nonceLen]; | |||
decNonce = new byte[nonceLen]; | |||
} | |||
protected abstract Dictionary<string, EncryptorInfo> getCiphers(); | |||
@@ -93,7 +94,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
if (_Masterkey.Length != keyLen) Array.Resize(ref _Masterkey, keyLen); | |||
DeriveKey(passbuf, _Masterkey, keyLen); | |||
// init session key | |||
if (_sessionKey == null) _sessionKey = new byte[keyLen]; | |||
if (sessionKey == null) sessionKey = new byte[keyLen]; | |||
} | |||
public void DeriveKey(byte[] password, byte[] key, int keylen) | |||
@@ -109,24 +110,22 @@ namespace Shadowsocks.Encryption.AEAD | |||
protected void IncrementNonce(bool isEncrypt) | |||
{ | |||
lock (_nonceIncrementLock) { | |||
CryptoUtils.SodiumIncrement(isEncrypt ? _encNonce : _decNonce); | |||
CryptoUtils.SodiumIncrement(isEncrypt ? encNonce : decNonce); | |||
} | |||
} | |||
public virtual void InitCipher(byte[] salt, bool isEncrypt, bool isUdp) | |||
{ | |||
if (isEncrypt) { | |||
_encryptSalt = new byte[saltLen]; | |||
Array.Copy(salt, _encryptSalt, saltLen); | |||
encryptSalt = new byte[saltLen]; | |||
Array.Copy(salt, encryptSalt, saltLen); | |||
} else { | |||
_decryptSalt = new byte[saltLen]; | |||
Array.Copy(salt, _decryptSalt, saltLen); | |||
decryptSalt = new byte[saltLen]; | |||
Array.Copy(salt, decryptSalt, saltLen); | |||
} | |||
logger.Dump("Salt", salt, saltLen); | |||
} | |||
public static void randBytes(byte[] buf, int length) { RNG.GetBytes(buf, length); } | |||
public abstract void cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen); | |||
public abstract void cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen); | |||
@@ -144,7 +143,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
_encryptSaltSent = true; | |||
// Generate salt | |||
byte[] saltBytes = new byte[saltLen]; | |||
randBytes(saltBytes, saltLen); | |||
RNG.GetBytes(saltBytes, saltLen); | |||
InitCipher(saltBytes, true, false); | |||
Array.Copy(saltBytes, 0, outbuf, 0, saltLen); | |||
outlength = saltLen; | |||
@@ -289,7 +288,7 @@ namespace Shadowsocks.Encryption.AEAD | |||
public override void EncryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength) | |||
{ | |||
// Generate salt | |||
randBytes(outbuf, saltLen); | |||
RNG.GetBytes(outbuf, saltLen); | |||
InitCipher(outbuf, true, true); | |||
uint olen = 0; | |||
lock (_udpTmpBuf) { | |||
@@ -15,10 +15,10 @@ namespace Shadowsocks.Encryption | |||
static EncryptorFactory() | |||
{ | |||
foreach (string method in StreamNativeEncryptor.SupportedCiphers()) | |||
foreach (string method in StreamTableNativeEncryptor.SupportedCiphers()) | |||
{ | |||
if (!_registeredEncryptors.ContainsKey(method)) | |||
_registeredEncryptors.Add(method, typeof(StreamNativeEncryptor)); | |||
_registeredEncryptors.Add(method, typeof(StreamTableNativeEncryptor)); | |||
} | |||
foreach (string method in AEADNativeEncryptor.SupportedCiphers()) | |||
@@ -0,0 +1,127 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Shadowsocks.Encryption.Stream | |||
{ | |||
public class StreamRc4NativeEncryptor : StreamEncryptor | |||
{ | |||
const int Rc4 = 0; | |||
const int Rc4Md5 = 1; | |||
string _password; | |||
byte[] realkey; | |||
byte[] sbox; | |||
public StreamRc4NativeEncryptor(string method, string password) : base(method, password) | |||
{ | |||
_password = password; | |||
} | |||
public override void Dispose() | |||
{ | |||
return; | |||
} | |||
protected override void initCipher(byte[] iv, bool isEncrypt) | |||
{ | |||
base.initCipher(iv, isEncrypt); | |||
if (_cipher == Rc4Md5) | |||
{ | |||
byte[] temp = new byte[keyLen + ivLen]; | |||
Array.Copy(_key, 0, temp, 0, keyLen); | |||
Array.Copy(iv, 0, temp, keyLen, ivLen); | |||
realkey = CryptoUtils.MD5(temp); | |||
} | |||
else | |||
{ | |||
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); | |||
RC4(ctx, sbox, t, length); | |||
Array.Copy(t, outbuf, length); | |||
} | |||
private static readonly Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo> | |||
{ | |||
// original RC4 doesn't use IV | |||
{ "rc4", new EncryptorInfo("RC4", 16, 0, Rc4) }, | |||
{ "rc4-md5", new EncryptorInfo("RC4", 16, 16, Rc4Md5) }, | |||
}; | |||
public static IEnumerable<string> SupportedCiphers() | |||
{ | |||
return _ciphers.Keys; | |||
} | |||
protected override Dictionary<string, EncryptorInfo> getCiphers() | |||
{ | |||
return _ciphers; | |||
} | |||
#region RC4 | |||
class Context | |||
{ | |||
public int index1 = 0; | |||
public int index2 = 0; | |||
} | |||
private Context enc_ctx = new Context(); | |||
private Context dec_ctx = new Context(); | |||
private byte[] SBox(byte[] key) | |||
{ | |||
byte[] s = new byte[256]; | |||
for (int i = 0; i < 256; i++) | |||
{ | |||
s[i] = (byte)i; | |||
} | |||
for (int i = 0, j = 0; i < 256; i++) | |||
{ | |||
j = (j + key[i % key.Length] + s[i]) & 255; | |||
Swap(s, i, j); | |||
} | |||
return s; | |||
} | |||
private void RC4(Context ctx, byte[] s, byte[] data, int length) | |||
{ | |||
for (int n = 0; n < length; n++) | |||
{ | |||
byte b = data[n]; | |||
ctx.index1 = (ctx.index1 + 1) & 255; | |||
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) | |||
{ | |||
byte c = s[i]; | |||
s[i] = s[j]; | |||
s[j] = c; | |||
} | |||
#endregion | |||
} | |||
} |
@@ -6,18 +6,14 @@ using System.Threading.Tasks; | |||
namespace Shadowsocks.Encryption.Stream | |||
{ | |||
public class StreamNativeEncryptor : StreamEncryptor | |||
public class StreamTableNativeEncryptor : StreamEncryptor | |||
{ | |||
const int Plain = 0; | |||
const int Table = 1; | |||
const int Rc4 = 2; | |||
const int Rc4Md5 = 3; | |||
string _password; | |||
byte[] realkey; | |||
byte[] sbox; | |||
public StreamNativeEncryptor(string method, string password) : base(method, password) | |||
public StreamTableNativeEncryptor(string method, string password) : base(method, password) | |||
{ | |||
_password = password; | |||
} | |||
@@ -30,22 +26,7 @@ namespace Shadowsocks.Encryption.Stream | |||
protected override void initCipher(byte[] iv, bool isEncrypt) | |||
{ | |||
base.initCipher(iv, isEncrypt); | |||
if (_cipher >= Rc4) | |||
{ | |||
if (_cipher == Rc4Md5) | |||
{ | |||
byte[] temp = new byte[keyLen + ivLen]; | |||
Array.Copy(_key, 0, temp, 0, keyLen); | |||
Array.Copy(iv, 0, temp, keyLen, ivLen); | |||
realkey = CryptoUtils.MD5(temp); | |||
} | |||
else | |||
{ | |||
realkey = _key; | |||
} | |||
sbox = SBox(realkey); | |||
} | |||
else if (_cipher == Table) | |||
if (_cipher == Table) | |||
{ | |||
ulong a = BitConverter.ToUInt64(CryptoUtils.MD5(Encoding.UTF8.GetBytes(_password)), 0); | |||
for (int i = 0; i < 256; i++) | |||
@@ -77,24 +58,12 @@ namespace Shadowsocks.Encryption.Stream | |||
{ | |||
Array.Copy(buf, outbuf, length); | |||
} | |||
else | |||
{ | |||
var ctx = isEncrypt ? enc_ctx : dec_ctx; | |||
byte[] t = new byte[length]; | |||
Array.Copy(buf, t, length); | |||
RC4(ctx, sbox, t, length); | |||
Array.Copy(t, outbuf, length); | |||
} | |||
} | |||
private static readonly Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo> | |||
{ | |||
{"plain", new EncryptorInfo("PLAIN", 0, 0, Plain) }, | |||
{"table", new EncryptorInfo("TABLE", 0, 0, Table) }, | |||
{ "rc4", new EncryptorInfo("RC4", 16, 0, Rc4) }, // original RC4 doesn't use IV | |||
{ "rc4-md5", new EncryptorInfo("RC4", 16, 16, Rc4Md5) }, | |||
}; | |||
public static IEnumerable<string> SupportedCiphers() | |||
@@ -156,58 +125,5 @@ namespace Shadowsocks.Encryption.Stream | |||
return sorted; | |||
} | |||
#endregion | |||
#region RC4 | |||
class Context | |||
{ | |||
public int index1 = 0; | |||
public int index2 = 0; | |||
} | |||
private Context enc_ctx = new Context(); | |||
private Context dec_ctx = new Context(); | |||
private byte[] SBox(byte[] key) | |||
{ | |||
byte[] s = new byte[256]; | |||
for (int i = 0; i < 256; i++) | |||
{ | |||
s[i] = (byte)i; | |||
} | |||
for (int i = 0, j = 0; i < 256; i++) | |||
{ | |||
j = (j + key[i % key.Length] + s[i]) & 255; | |||
Swap(s, i, j); | |||
} | |||
return s; | |||
} | |||
private void RC4(Context ctx, byte[] s, byte[] data, int length) | |||
{ | |||
for (int n = 0; n < length; n++) | |||
{ | |||
byte b = data[n]; | |||
ctx.index1 = (ctx.index1 + 1) & 255; | |||
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) | |||
{ | |||
byte c = s[i]; | |||
s[i] = s[j]; | |||
s[j] = c; | |||
} | |||
#endregion | |||
} | |||
} |
@@ -2,7 +2,7 @@ | |||
<PropertyGroup> | |||
<OutputType>WinExe</OutputType> | |||
<TargetFrameworks>net472;netcoreapp3.1</TargetFrameworks> | |||
<TargetFrameworks>netcoreapp3.1</TargetFrameworks> | |||
<RootNamespace>Shadowsocks</RootNamespace> | |||
<UseWindowsForms>true</UseWindowsForms> | |||
<Authors>clowwindy & community 2020</Authors> | |||
@@ -111,8 +111,8 @@ namespace Shadowsocks.Test | |||
{ | |||
IEncryptor encryptorN; | |||
IEncryptor decryptorN; | |||
encryptorN = new StreamNativeEncryptor("rc4-md5", "barfoo!"); | |||
decryptorN = new StreamNativeEncryptor("rc4-md5", "barfoo!"); | |||
encryptorN = new StreamRc4NativeEncryptor("rc4-md5", "barfoo!"); | |||
decryptorN = new StreamRc4NativeEncryptor("rc4-md5", "barfoo!"); | |||
RunEncryptionRound(encryptorN, decryptorN); | |||
} | |||
} | |||
@@ -134,7 +134,7 @@ namespace Shadowsocks.Test | |||
IEncryptor decryptor; | |||
encryptor = new Encryption.AEAD.AEADBouncyCastleEncryptor("aes-256-gcm", "barfoo!"); | |||
encryptor.AddrBufLength = 1 + 4 + 2;// ADDR_ATYP_LEN + 4 + ADDR_PORT_LEN; | |||
decryptor = new Encryption.AEAD.AEADBouncyCastleEncryptor("aes-256-gcm", "barfoo!"); | |||
decryptor = new Encryption.AEAD.AEADAesGcmNativeEncryptor("aes-256-gcm", "barfoo!"); | |||
decryptor.AddrBufLength = 1 + 4 + 2;// ADDR_ATYP_LEN + 4 + ADDR_PORT_LEN; | |||
RunEncryptionRound(encryptor, decryptor); | |||
} | |||
@@ -6,6 +6,10 @@ | |||
<IsPackable>false</IsPackable> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> | |||
<WarningLevel>0</WarningLevel> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<PackageReference Include="GlobalHotKeyCore" Version="1.2.0" /> | |||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" /> | |||