@@ -0,0 +1,74 @@ | |||
using Org.BouncyCastle.Crypto.Engines; | |||
using Org.BouncyCastle.Crypto.Modes; | |||
using Org.BouncyCastle.Crypto.Parameters; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Shadowsocks.Encryption.AEAD | |||
{ | |||
public class AEADBouncyCastleEncryptor : AEADEncryptor//, IDisposable | |||
{ | |||
public AEADBouncyCastleEncryptor(string method, string password) | |||
: base(method, password) | |||
{ | |||
} | |||
static int CIPHER_AES=1; | |||
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 cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen) | |||
{ | |||
GcmBlockCipher cipher = new GcmBlockCipher(new AesEngine()); | |||
AeadParameters parameters = new AeadParameters(new KeyParameter(_sessionKey), tagLen * 8, _decNonce); | |||
cipher.Init(false, parameters); | |||
plaintext = new byte[cipher.GetOutputSize((int)clen)]; | |||
var len = cipher.ProcessBytes(ciphertext, 0, (int)clen, plaintext, 0); | |||
cipher.DoFinal(plaintext, len); | |||
plen = (uint)(plaintext.Length); | |||
} | |||
public override void cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen) | |||
{ | |||
GcmBlockCipher cipher = new GcmBlockCipher(new AesEngine()); | |||
AeadParameters parameters = new AeadParameters(new KeyParameter(_sessionKey), tagLen * 8, _encNonce); | |||
cipher.Init(true, parameters); | |||
ciphertext = new byte[cipher.GetOutputSize((int)plen)]; | |||
var len = cipher.ProcessBytes(plaintext, 0, (int)plen, ciphertext, 0); | |||
cipher.DoFinal(ciphertext, len); | |||
clen = (uint)(ciphertext.Length); | |||
} | |||
public override void Dispose() | |||
{ | |||
//throw new NotImplementedException(); | |||
} | |||
public override void InitCipher(byte[] salt, bool isEncrypt, bool isUdp) | |||
{ | |||
base.InitCipher(salt, isEncrypt, isUdp); | |||
DeriveSessionKey(isEncrypt ? _encryptSalt : _decryptSalt, | |||
_Masterkey, _sessionKey); | |||
} | |||
public static List<string> SupportedCiphers() | |||
{ | |||
return new List<string>(_ciphers.Keys); | |||
} | |||
} | |||
} |
@@ -1,5 +1,6 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<packages> | |||
<package id="BouncyCastle" version="1.8.5" targetFramework="net472" /> | |||
<package id="Caseless.Fody" version="1.8.3" targetFramework="net472" /> | |||
<package id="Costura.Fody" version="3.3.3" targetFramework="net472" /> | |||
<package id="Fody" version="4.2.1" targetFramework="net472" developmentDependency="true" /> | |||
@@ -68,6 +68,9 @@ | |||
<ApplicationManifest>app.manifest</ApplicationManifest> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Reference Include="BouncyCastle.Crypto, Version=1.8.5.0, Culture=neutral, PublicKeyToken=0e99375e54769942"> | |||
<HintPath>..\packages\BouncyCastle.1.8.5\lib\BouncyCastle.Crypto.dll</HintPath> | |||
</Reference> | |||
<Reference Include="Caseless, Version=1.8.3.0, Culture=neutral, PublicKeyToken=409b3227471b0f0d, processorArchitecture=MSIL"> | |||
<HintPath>..\packages\Caseless.Fody.1.8.3\lib\net452\Caseless.dll</HintPath> | |||
</Reference> | |||
@@ -114,6 +117,7 @@ | |||
<Compile Include="Controller\LoggerExtension.cs" /> | |||
<Compile Include="Controller\Service\PACDaemon.cs" /> | |||
<Compile Include="Controller\System\Hotkeys\HotkeyCallbacks.cs" /> | |||
<Compile Include="Encryption\AEAD\AEADBouncyCastleEncryptor.cs" /> | |||
<Compile Include="Encryption\AEAD\AEADEncryptor.cs" /> | |||
<Compile Include="Encryption\AEAD\AEADMbedTLSEncryptor.cs" /> | |||
<Compile Include="Encryption\AEAD\AEADOpenSSLEncryptor.cs" /> | |||
@@ -230,6 +230,50 @@ namespace Shadowsocks.Test | |||
} | |||
} | |||
[TestMethod] | |||
public void TestBouncyCastleEncryption() | |||
{ | |||
encryptionFailed = false; | |||
// run it once before the multi-threading test to initialize global tables | |||
RunSingleBouncyCastleEncryptionThread(); | |||
List<Thread> threads = new List<Thread>(); | |||
for (int i = 0; i < 10; i++) | |||
{ | |||
Thread t = new Thread(new ThreadStart(RunSingleBouncyCastleEncryptionThread)); | |||
threads.Add(t); | |||
t.Start(); | |||
} | |||
foreach (Thread t in threads) | |||
{ | |||
t.Join(); | |||
} | |||
RNG.Close(); | |||
Assert.IsFalse(encryptionFailed); | |||
} | |||
private void RunSingleBouncyCastleEncryptionThread() | |||
{ | |||
try | |||
{ | |||
for (int i = 0; i < 10; i++) | |||
{ | |||
var random = new Random(); | |||
IEncryptor encryptor; | |||
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.AddrBufLength = 1 + 4 + 2;// ADDR_ATYP_LEN + 4 + ADDR_PORT_LEN; | |||
RunEncryptionRound(encryptor, decryptor); | |||
} | |||
} | |||
catch | |||
{ | |||
encryptionFailed = true; | |||
throw; | |||
} | |||
} | |||
[TestMethod] | |||
public void TestOpenSSLAEADEncryption() | |||
{ | |||