Browse Source

Native encryption

pull/2865/head
Student Main 5 years ago
parent
commit
7df8272f0a
9 changed files with 252 additions and 115 deletions
  1. +91
    -0
      shadowsocks-csharp/Encryption/AEAD/AEADAesGcmNativeEncryptor.cs
  2. +5
    -5
      shadowsocks-csharp/Encryption/AEAD/AEADBouncyCastleEncryptor.cs
  3. +16
    -17
      shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs
  4. +2
    -2
      shadowsocks-csharp/Encryption/EncryptorFactory.cs
  5. +127
    -0
      shadowsocks-csharp/Encryption/Stream/StreamRc4NativeEncryptor.cs
  6. +3
    -87
      shadowsocks-csharp/Encryption/Stream/StreamTableNativeEncryptor.cs
  7. +1
    -1
      shadowsocks-csharp/shadowsocks-csharp.csproj
  8. +3
    -3
      test/CryptographyTest.cs
  9. +4
    -0
      test/ShadowsocksTest.csproj

+ 91
- 0
shadowsocks-csharp/Encryption/AEAD/AEADAesGcmNativeEncryptor.cs View File

@@ -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()
{
}
}
}

+ 5
- 5
shadowsocks-csharp/Encryption/AEAD/AEADBouncyCastleEncryptor.cs View File

@@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace Shadowsocks.Encryption.AEAD namespace Shadowsocks.Encryption.AEAD
{ {
public class AEADBouncyCastleEncryptor : AEADEncryptor, IDisposable
public class AEADBouncyCastleEncryptor : AEADEncryptor
{ {
static int CIPHER_AES = 1; // dummy static int CIPHER_AES = 1; // dummy
@@ -35,14 +35,14 @@ namespace Shadowsocks.Encryption.AEAD
{ {
base.InitCipher(salt, isEncrypt, isUdp); 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) 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, decNonce);
cipher.Init(false, parameters); cipher.Init(false, parameters);
var plaintextBC = new byte[cipher.GetOutputSize((int)clen)]; 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) 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, encNonce);
cipher.Init(true, parameters); cipher.Init(true, parameters);
var ciphertextBC = new byte[cipher.GetOutputSize((int)plen)]; var ciphertextBC = new byte[cipher.GetOutputSize((int)plen)];


+ 16
- 17
shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs View File

@@ -37,18 +37,19 @@ namespace Shadowsocks.Encryption.AEAD
protected string _innerLibName; protected string _innerLibName;
protected EncryptorInfo CipherInfo; protected EncryptorInfo 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[] encryptSalt;
protected byte[] decryptSalt;
protected object _nonceIncrementLock = new object(); protected object _nonceIncrementLock = new object();
protected byte[] _encNonce;
protected byte[] _decNonce;
protected byte[] encNonce;
protected byte[] decNonce;
// Is first packet // Is first packet
protected bool _decryptSaltReceived; protected bool _decryptSaltReceived;
protected bool _encryptSaltSent; protected bool _encryptSaltSent;
@@ -62,8 +63,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];
encNonce = new byte[nonceLen];
decNonce = new byte[nonceLen];
} }
protected abstract Dictionary<string, EncryptorInfo> getCiphers(); protected abstract Dictionary<string, EncryptorInfo> getCiphers();
@@ -93,7 +94,7 @@ namespace Shadowsocks.Encryption.AEAD
if (_Masterkey.Length != keyLen) Array.Resize(ref _Masterkey, keyLen); if (_Masterkey.Length != keyLen) Array.Resize(ref _Masterkey, keyLen);
DeriveKey(passbuf, _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];
} }
public void DeriveKey(byte[] password, byte[] key, int keylen) public void DeriveKey(byte[] password, byte[] key, int keylen)
@@ -109,24 +110,22 @@ namespace Shadowsocks.Encryption.AEAD
protected void IncrementNonce(bool isEncrypt) protected void IncrementNonce(bool isEncrypt)
{ {
lock (_nonceIncrementLock) { lock (_nonceIncrementLock) {
CryptoUtils.SodiumIncrement(isEncrypt ? _encNonce : _decNonce);
CryptoUtils.SodiumIncrement(isEncrypt ? encNonce : decNonce);
} }
} }
public virtual void InitCipher(byte[] salt, bool isEncrypt, bool isUdp) public virtual void InitCipher(byte[] salt, bool isEncrypt, bool isUdp)
{ {
if (isEncrypt) { if (isEncrypt) {
_encryptSalt = new byte[saltLen];
Array.Copy(salt, _encryptSalt, saltLen);
encryptSalt = new byte[saltLen];
Array.Copy(salt, encryptSalt, saltLen);
} else { } else {
_decryptSalt = new byte[saltLen];
Array.Copy(salt, _decryptSalt, saltLen);
decryptSalt = new byte[saltLen];
Array.Copy(salt, decryptSalt, saltLen);
} }
logger.Dump("Salt", salt, 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 cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen);
public abstract void cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen); public abstract void cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen);
@@ -144,7 +143,7 @@ namespace Shadowsocks.Encryption.AEAD
_encryptSaltSent = true; _encryptSaltSent = true;
// Generate salt // Generate salt
byte[] saltBytes = new byte[saltLen]; byte[] saltBytes = new byte[saltLen];
randBytes(saltBytes, saltLen);
RNG.GetBytes(saltBytes, saltLen);
InitCipher(saltBytes, true, false); InitCipher(saltBytes, true, false);
Array.Copy(saltBytes, 0, outbuf, 0, saltLen); Array.Copy(saltBytes, 0, outbuf, 0, saltLen);
outlength = saltLen; outlength = saltLen;
@@ -289,7 +288,7 @@ namespace Shadowsocks.Encryption.AEAD
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 salt // Generate salt
randBytes(outbuf, saltLen);
RNG.GetBytes(outbuf, saltLen);
InitCipher(outbuf, true, true); InitCipher(outbuf, true, true);
uint olen = 0; uint olen = 0;
lock (_udpTmpBuf) { lock (_udpTmpBuf) {


+ 2
- 2
shadowsocks-csharp/Encryption/EncryptorFactory.cs View File

@@ -15,10 +15,10 @@ namespace Shadowsocks.Encryption
static EncryptorFactory() static EncryptorFactory()
{ {
foreach (string method in StreamNativeEncryptor.SupportedCiphers())
foreach (string method in StreamTableNativeEncryptor.SupportedCiphers())
{ {
if (!_registeredEncryptors.ContainsKey(method)) if (!_registeredEncryptors.ContainsKey(method))
_registeredEncryptors.Add(method, typeof(StreamNativeEncryptor));
_registeredEncryptors.Add(method, typeof(StreamTableNativeEncryptor));
} }
foreach (string method in AEADNativeEncryptor.SupportedCiphers()) foreach (string method in AEADNativeEncryptor.SupportedCiphers())


+ 127
- 0
shadowsocks-csharp/Encryption/Stream/StreamRc4NativeEncryptor.cs View File

@@ -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
}
}

shadowsocks-csharp/Encryption/Stream/StreamNativeEncryptor.cs → shadowsocks-csharp/Encryption/Stream/StreamTableNativeEncryptor.cs View File

@@ -6,18 +6,14 @@ using System.Threading.Tasks;


namespace Shadowsocks.Encryption.Stream namespace Shadowsocks.Encryption.Stream
{ {
public class StreamNativeEncryptor : StreamEncryptor
public class StreamTableNativeEncryptor : StreamEncryptor
{ {
const int Plain = 0; const int Plain = 0;
const int Table = 1; const int Table = 1;
const int Rc4 = 2;
const int Rc4Md5 = 3;


string _password; 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; _password = password;
} }
@@ -30,22 +26,7 @@ namespace Shadowsocks.Encryption.Stream
protected override void initCipher(byte[] iv, bool isEncrypt) protected override void initCipher(byte[] iv, bool isEncrypt)
{ {
base.initCipher(iv, isEncrypt); base.initCipher(iv, isEncrypt);
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); ulong a = BitConverter.ToUInt64(CryptoUtils.MD5(Encoding.UTF8.GetBytes(_password)), 0);
for (int i = 0; i < 256; i++) for (int i = 0; i < 256; i++)
@@ -77,24 +58,12 @@ namespace Shadowsocks.Encryption.Stream
{ {
Array.Copy(buf, outbuf, length); 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> private static readonly Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo>
{ {
{"plain", new EncryptorInfo("PLAIN", 0, 0, Plain) }, {"plain", new EncryptorInfo("PLAIN", 0, 0, Plain) },
{"table", new EncryptorInfo("TABLE", 0, 0, Table) }, {"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() public static IEnumerable<string> SupportedCiphers()
@@ -156,58 +125,5 @@ namespace Shadowsocks.Encryption.Stream
return sorted; return sorted;
} }
#endregion #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
} }
} }

+ 1
- 1
shadowsocks-csharp/shadowsocks-csharp.csproj View File

@@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFrameworks>net472;netcoreapp3.1</TargetFrameworks>
<TargetFrameworks>netcoreapp3.1</TargetFrameworks>
<RootNamespace>Shadowsocks</RootNamespace> <RootNamespace>Shadowsocks</RootNamespace>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<Authors>clowwindy &amp; community 2020</Authors> <Authors>clowwindy &amp; community 2020</Authors>


+ 3
- 3
test/CryptographyTest.cs View File

@@ -111,8 +111,8 @@ namespace Shadowsocks.Test
{ {
IEncryptor encryptorN; IEncryptor encryptorN;
IEncryptor decryptorN; 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); RunEncryptionRound(encryptorN, decryptorN);
} }
} }
@@ -134,7 +134,7 @@ namespace Shadowsocks.Test
IEncryptor decryptor; IEncryptor decryptor;
encryptor = new Encryption.AEAD.AEADBouncyCastleEncryptor("aes-256-gcm", "barfoo!"); encryptor = new Encryption.AEAD.AEADBouncyCastleEncryptor("aes-256-gcm", "barfoo!");
encryptor.AddrBufLength = 1 + 4 + 2;// ADDR_ATYP_LEN + 4 + ADDR_PORT_LEN; 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; decryptor.AddrBufLength = 1 + 4 + 2;// ADDR_ATYP_LEN + 4 + ADDR_PORT_LEN;
RunEncryptionRound(encryptor, decryptor); RunEncryptionRound(encryptor, decryptor);
} }


+ 4
- 0
test/ShadowsocksTest.csproj View File

@@ -6,6 +6,10 @@
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<WarningLevel>0</WarningLevel>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="GlobalHotKeyCore" Version="1.2.0" /> <PackageReference Include="GlobalHotKeyCore" Version="1.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />


Loading…
Cancel
Save