From 7f14d04c8a7e3055d5ea2a3dfa54401841ce008f Mon Sep 17 00:00:00 2001 From: Bruce Wayne Date: Fri, 26 Feb 2021 13:39:05 +0800 Subject: [PATCH] Replace BouncyCastle with CryptoBase Shadowsocks.Protocol --- .../Shadowsocks.Protocol.csproj | 2 +- .../Shadowsocks/AeadClient.cs | 19 +++- .../Shadowsocks/Crypto/CryptoUtils.cs | 107 ++++++------------ .../Shadowsocks/SaltMessage.cs | 3 +- 4 files changed, 49 insertions(+), 82 deletions(-) diff --git a/Shadowsocks.Protocol/Shadowsocks.Protocol.csproj b/Shadowsocks.Protocol/Shadowsocks.Protocol.csproj index ea0f2c32..7351ea35 100644 --- a/Shadowsocks.Protocol/Shadowsocks.Protocol.csproj +++ b/Shadowsocks.Protocol/Shadowsocks.Protocol.csproj @@ -6,7 +6,7 @@ - + diff --git a/Shadowsocks.Protocol/Shadowsocks/AeadClient.cs b/Shadowsocks.Protocol/Shadowsocks/AeadClient.cs index 97d6eaea..a6b72215 100644 --- a/Shadowsocks.Protocol/Shadowsocks/AeadClient.cs +++ b/Shadowsocks.Protocol/Shadowsocks/AeadClient.cs @@ -1,11 +1,11 @@ using Shadowsocks.Protocol.Shadowsocks.Crypto; using System; +using System.Collections.Generic; using System.IO.Pipelines; using System.Net; using System.Runtime.InteropServices; -using System.Text; +using System.Security.Cryptography; using System.Threading.Tasks; -using System.Collections.Generic; namespace Shadowsocks.Protocol.Shadowsocks { @@ -13,7 +13,14 @@ namespace Shadowsocks.Protocol.Shadowsocks { private CryptoParameter cryptoParameter; private readonly byte[] mainKey; - private static readonly byte[] _ssSubKeyInfo = Encoding.ASCII.GetBytes("ss-subkey"); + + /// + /// ss-subkey + /// + private static ReadOnlySpan _ssSubKeyInfo => new byte[] + { + 0x73, 0x73, 0x2d, 0x73, 0x75, 0x62, 0x6b, 0x65, 0x79 + }; public AeadClient(CryptoParameter parameter, string password) { @@ -40,7 +47,8 @@ namespace Shadowsocks.Protocol.Shadowsocks var salt = new SaltMessage(16, true); await pmp.WriteAsync(salt); - var key = CryptoUtils.HKDF(cryptoParameter.KeySize, mainKey, salt.Salt.ToArray(), _ssSubKeyInfo); + var key = new byte[cryptoParameter.KeySize]; + HKDF.DeriveKey(HashAlgorithmName.SHA1, mainKey, key, salt.Salt.Span, _ssSubKeyInfo); up.Init(key, null); Memory nonce = new byte[cryptoParameter.NonceSize]; nonce.Span.Fill(0); @@ -74,7 +82,8 @@ namespace Shadowsocks.Protocol.Shadowsocks var pmp = new ProtocolMessagePipe(server); var salt = await pmp.ReadAsync(new SaltMessage(cryptoParameter.KeySize)); - var key = CryptoUtils.HKDF(cryptoParameter.KeySize, mainKey, salt.Salt.ToArray(), _ssSubKeyInfo); + var key = new byte[cryptoParameter.KeySize]; + HKDF.DeriveKey(HashAlgorithmName.SHA1, mainKey, key, salt.Salt.Span, _ssSubKeyInfo); down.Init(key, null); Memory nonce = new byte[cryptoParameter.NonceSize]; nonce.Span.Fill(0); diff --git a/Shadowsocks.Protocol/Shadowsocks/Crypto/CryptoUtils.cs b/Shadowsocks.Protocol/Shadowsocks/Crypto/CryptoUtils.cs index d7fae32e..4df67a0d 100644 --- a/Shadowsocks.Protocol/Shadowsocks/Crypto/CryptoUtils.cs +++ b/Shadowsocks.Protocol/Shadowsocks/Crypto/CryptoUtils.cs @@ -1,94 +1,51 @@ -using Org.BouncyCastle.Crypto.Digests; -using Org.BouncyCastle.Crypto.Generators; -using Org.BouncyCastle.Crypto.Parameters; +using CryptoBase.Digests.MD5; using System; -using System.Security.Cryptography; +using System.Buffers; using System.Text; -using System.Threading; namespace Shadowsocks.Protocol.Shadowsocks.Crypto { public static class CryptoUtils { - private static readonly ThreadLocal Md5Hasher = new ThreadLocal(System.Security.Cryptography.MD5.Create); - - public static byte[] MD5(byte[] b) - { - var hash = new byte[16]; - Md5Hasher.Value?.TryComputeHash(b, hash, out _); - return hash; - } - // currently useless, just keep api same - public static Span MD5(Span span) - { - Span hash = new byte[16]; - Md5Hasher.Value?.TryComputeHash(span, hash, out _); - return hash; - } - - public static byte[] HKDF(int keylen, byte[] master, byte[] salt, byte[] info) - { - var ret = new byte[keylen]; - var degist = new Sha1Digest(); - var parameters = new HkdfParameters(master, salt, info); - var hkdf = new HkdfBytesGenerator(degist); - hkdf.Init(parameters); - hkdf.GenerateBytes(ret, 0, keylen); - return ret; - } - // currently useless, just keep api same, again - public static Span HKDF(int keylen, Span master, Span salt, Span info) - { - var ret = new byte[keylen]; - var degist = new Sha1Digest(); - var parameters = new HkdfParameters(master.ToArray(), salt.ToArray(), info.ToArray()); - var hkdf = new HkdfBytesGenerator(degist); - hkdf.Init(parameters); - hkdf.GenerateBytes(ret, 0, keylen); - return ret.AsSpan(); - } - public static byte[] SSKDF(string password, int keylen) { + const int md5Length = 16; + var pwMaxSize = Encoding.UTF8.GetMaxByteCount(password.Length); var key = new byte[keylen]; - var pw = Encoding.UTF8.GetBytes(password); - var result = new byte[password.Length + 16]; - var i = 0; - var md5sum = Array.Empty(); - while (i < keylen) - { - if (i == 0) - { - md5sum = MD5(pw); - } - else - { - Array.Copy(md5sum, 0, result, 0, 16); - Array.Copy(pw, 0, result, 16, password.Length); - md5sum = MD5(result); - } - Array.Copy(md5sum, 0, key, i, Math.Min(16, keylen - i)); - i += 16; - } - return key; - } - public static void SodiumIncrement(Span salt) - { - for (var i = 0; i < salt.Length; ++i) + var pwBuffer = ArrayPool.Shared.Rent(pwMaxSize); + var resultBuffer = ArrayPool.Shared.Rent(pwMaxSize + md5Length); + try { - if (++salt[i] != 0) + var pwLength = Encoding.UTF8.GetBytes(password, pwBuffer); + var pw = pwBuffer.AsSpan(0, pwLength); + Span md5Sum = stackalloc byte[md5Length]; + var result = resultBuffer.AsSpan(0, pwLength + md5Length); + var i = 0; + while (i < keylen) { - break; + if (i == 0) + { + MD5Utils.Default(pw, md5Sum); + } + else + { + md5Sum.CopyTo(result); + pw.CopyTo(result.Slice(md5Length)); + MD5Utils.Default(result, md5Sum); + } + + var length = Math.Min(16, keylen - i); + md5Sum.Slice(0, length).CopyTo(key.AsSpan(i, length)); + + i += md5Length; } + return key; } - } - - public static void RandomSpan(Span span) - { - using (var rng = RandomNumberGenerator.Create()) + finally { - rng.GetBytes(span); + ArrayPool.Shared.Return(pwBuffer); + ArrayPool.Shared.Return(resultBuffer); } } } diff --git a/Shadowsocks.Protocol/Shadowsocks/SaltMessage.cs b/Shadowsocks.Protocol/Shadowsocks/SaltMessage.cs index 4e3ffe97..75236238 100644 --- a/Shadowsocks.Protocol/Shadowsocks/SaltMessage.cs +++ b/Shadowsocks.Protocol/Shadowsocks/SaltMessage.cs @@ -1,6 +1,7 @@ using Shadowsocks.Protocol.Shadowsocks.Crypto; using System; using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography; namespace Shadowsocks.Protocol.Shadowsocks { @@ -15,7 +16,7 @@ namespace Shadowsocks.Protocol.Shadowsocks if (roll) { Salt = new byte[length]; - CryptoUtils.RandomSpan(Salt.Span); + RandomNumberGenerator.Fill(Salt.Span); } }