From 77c6cc4e0875b8eea8ea834798cd5a379d9b3919 Mon Sep 17 00:00:00 2001 From: celeron533 Date: Sun, 16 Feb 2020 14:59:07 +0800 Subject: [PATCH] Add BouncyCastle AEAD --- .../AEAD/AEADBouncyCastleEncryptor.cs | 74 +++++++++++++++++++ shadowsocks-csharp/packages.config | 1 + shadowsocks-csharp/shadowsocks-csharp.csproj | 4 + test/CryptographyTest.cs | 44 +++++++++++ 4 files changed, 123 insertions(+) create mode 100644 shadowsocks-csharp/Encryption/AEAD/AEADBouncyCastleEncryptor.cs diff --git a/shadowsocks-csharp/Encryption/AEAD/AEADBouncyCastleEncryptor.cs b/shadowsocks-csharp/Encryption/AEAD/AEADBouncyCastleEncryptor.cs new file mode 100644 index 00000000..2147028c --- /dev/null +++ b/shadowsocks-csharp/Encryption/AEAD/AEADBouncyCastleEncryptor.cs @@ -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 _ciphers = new Dictionary + { + {"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 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 SupportedCiphers() + { + return new List(_ciphers.Keys); + } + } +} diff --git a/shadowsocks-csharp/packages.config b/shadowsocks-csharp/packages.config index 911e1d9a..67621a9d 100644 --- a/shadowsocks-csharp/packages.config +++ b/shadowsocks-csharp/packages.config @@ -1,5 +1,6 @@  + diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index bf562337..31095fd6 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -68,6 +68,9 @@ app.manifest + + ..\packages\BouncyCastle.1.8.5\lib\BouncyCastle.Crypto.dll + ..\packages\Caseless.Fody.1.8.3\lib\net452\Caseless.dll @@ -114,6 +117,7 @@ + diff --git a/test/CryptographyTest.cs b/test/CryptographyTest.cs index a5b78bbd..3fa82d34 100644 --- a/test/CryptographyTest.cs +++ b/test/CryptographyTest.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 threads = new List(); + 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() {