@@ -6,7 +6,7 @@ | |||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<PackageReference Include="BouncyCastle.NetCore" Version="1.8.8" /> | |||||
<PackageReference Include="CryptoBase" Version="1.1.1" /> | |||||
<PackageReference Include="Pipelines.Sockets.Unofficial" Version="2.2.0" /> | <PackageReference Include="Pipelines.Sockets.Unofficial" Version="2.2.0" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
@@ -1,11 +1,11 @@ | |||||
using Shadowsocks.Protocol.Shadowsocks.Crypto; | using Shadowsocks.Protocol.Shadowsocks.Crypto; | ||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.IO.Pipelines; | using System.IO.Pipelines; | ||||
using System.Net; | using System.Net; | ||||
using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||||
using System.Text; | |||||
using System.Security.Cryptography; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using System.Collections.Generic; | |||||
namespace Shadowsocks.Protocol.Shadowsocks | namespace Shadowsocks.Protocol.Shadowsocks | ||||
{ | { | ||||
@@ -13,7 +13,14 @@ namespace Shadowsocks.Protocol.Shadowsocks | |||||
{ | { | ||||
private CryptoParameter cryptoParameter; | private CryptoParameter cryptoParameter; | ||||
private readonly byte[] mainKey; | private readonly byte[] mainKey; | ||||
private static readonly byte[] _ssSubKeyInfo = Encoding.ASCII.GetBytes("ss-subkey"); | |||||
/// <summary> | |||||
/// ss-subkey | |||||
/// </summary> | |||||
private static ReadOnlySpan<byte> _ssSubKeyInfo => new byte[] | |||||
{ | |||||
0x73, 0x73, 0x2d, 0x73, 0x75, 0x62, 0x6b, 0x65, 0x79 | |||||
}; | |||||
public AeadClient(CryptoParameter parameter, string password) | public AeadClient(CryptoParameter parameter, string password) | ||||
{ | { | ||||
@@ -40,7 +47,8 @@ namespace Shadowsocks.Protocol.Shadowsocks | |||||
var salt = new SaltMessage(16, true); | var salt = new SaltMessage(16, true); | ||||
await pmp.WriteAsync(salt); | 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); | up.Init(key, null); | ||||
Memory<byte> nonce = new byte[cryptoParameter.NonceSize]; | Memory<byte> nonce = new byte[cryptoParameter.NonceSize]; | ||||
nonce.Span.Fill(0); | nonce.Span.Fill(0); | ||||
@@ -74,7 +82,8 @@ namespace Shadowsocks.Protocol.Shadowsocks | |||||
var pmp = new ProtocolMessagePipe(server); | var pmp = new ProtocolMessagePipe(server); | ||||
var salt = await pmp.ReadAsync(new SaltMessage(cryptoParameter.KeySize)); | 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); | down.Init(key, null); | ||||
Memory<byte> nonce = new byte[cryptoParameter.NonceSize]; | Memory<byte> nonce = new byte[cryptoParameter.NonceSize]; | ||||
nonce.Span.Fill(0); | nonce.Span.Fill(0); | ||||
@@ -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; | ||||
using System.Security.Cryptography; | |||||
using System.Buffers; | |||||
using System.Text; | using System.Text; | ||||
using System.Threading; | |||||
namespace Shadowsocks.Protocol.Shadowsocks.Crypto | namespace Shadowsocks.Protocol.Shadowsocks.Crypto | ||||
{ | { | ||||
public static class CryptoUtils | public static class CryptoUtils | ||||
{ | { | ||||
private static readonly ThreadLocal<MD5> Md5Hasher = new ThreadLocal<MD5>(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<byte> MD5(Span<byte> span) | |||||
{ | |||||
Span<byte> 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<byte> HKDF(int keylen, Span<byte> master, Span<byte> salt, Span<byte> 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) | 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 key = new byte[keylen]; | ||||
var pw = Encoding.UTF8.GetBytes(password); | |||||
var result = new byte[password.Length + 16]; | |||||
var i = 0; | |||||
var md5sum = Array.Empty<byte>(); | |||||
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<byte> salt) | |||||
{ | |||||
for (var i = 0; i < salt.Length; ++i) | |||||
var pwBuffer = ArrayPool<byte>.Shared.Rent(pwMaxSize); | |||||
var resultBuffer = ArrayPool<byte>.Shared.Rent(pwMaxSize + md5Length); | |||||
try | |||||
{ | { | ||||
if (++salt[i] != 0) | |||||
var pwLength = Encoding.UTF8.GetBytes(password, pwBuffer); | |||||
var pw = pwBuffer.AsSpan(0, pwLength); | |||||
Span<byte> 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<byte> span) | |||||
{ | |||||
using (var rng = RandomNumberGenerator.Create()) | |||||
finally | |||||
{ | { | ||||
rng.GetBytes(span); | |||||
ArrayPool<byte>.Shared.Return(pwBuffer); | |||||
ArrayPool<byte>.Shared.Return(resultBuffer); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -1,6 +1,7 @@ | |||||
using Shadowsocks.Protocol.Shadowsocks.Crypto; | using Shadowsocks.Protocol.Shadowsocks.Crypto; | ||||
using System; | using System; | ||||
using System.Diagnostics.CodeAnalysis; | using System.Diagnostics.CodeAnalysis; | ||||
using System.Security.Cryptography; | |||||
namespace Shadowsocks.Protocol.Shadowsocks | namespace Shadowsocks.Protocol.Shadowsocks | ||||
{ | { | ||||
@@ -15,7 +16,7 @@ namespace Shadowsocks.Protocol.Shadowsocks | |||||
if (roll) | if (roll) | ||||
{ | { | ||||
Salt = new byte[length]; | Salt = new byte[length]; | ||||
CryptoUtils.RandomSpan(Salt.Span); | |||||
RandomNumberGenerator.Fill(Salt.Span); | |||||
} | } | ||||
} | } | ||||