@@ -42,12 +42,12 @@ namespace Shadowsocks.Net.Crypto | |||||
_registeredEncryptors.Add(method.Key, typeof(StreamAesCfbBouncyCastleCrypto)); | _registeredEncryptors.Add(method.Key, typeof(StreamAesCfbBouncyCastleCrypto)); | ||||
} | } | ||||
} | } | ||||
foreach (var method in StreamChachaNaClCrypto.SupportedCiphers()) | |||||
foreach (var method in StreamChachaBouncyCastleCrypto.SupportedCiphers()) | |||||
{ | { | ||||
if (!_registeredEncryptors.ContainsKey(method.Key)) | if (!_registeredEncryptors.ContainsKey(method.Key)) | ||||
{ | { | ||||
ciphers.Add(method.Key, method.Value); | ciphers.Add(method.Key, method.Value); | ||||
_registeredEncryptors.Add(method.Key, typeof(StreamChachaNaClCrypto)); | |||||
_registeredEncryptors.Add(method.Key, typeof(StreamChachaBouncyCastleCrypto)); | |||||
} | } | ||||
} | } | ||||
@@ -0,0 +1,59 @@ | |||||
using Org.BouncyCastle.Crypto; | |||||
using Org.BouncyCastle.Crypto.Engines; | |||||
using Org.BouncyCastle.Crypto.Parameters; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
namespace Shadowsocks.Net.Crypto.Stream | |||||
{ | |||||
public class StreamChachaBouncyCastleCrypto : StreamCrypto | |||||
{ | |||||
private readonly BufferedCipherBase _encryptor; | |||||
public StreamChachaBouncyCastleCrypto(string method, string password) : base(method, password) | |||||
{ | |||||
_encryptor = new BufferedStreamCipher(new ChaCha7539Engine()); | |||||
} | |||||
protected override void InitCipher(byte[] iv, bool isEncrypt) | |||||
{ | |||||
base.InitCipher(iv, isEncrypt); | |||||
_encryptor.Init(isEncrypt, new ParametersWithIV(new KeyParameter(key), iv)); | |||||
} | |||||
protected override int CipherEncrypt(ReadOnlySpan<byte> plain, Span<byte> cipher) | |||||
{ | |||||
return CipherUpdate(plain, cipher); | |||||
} | |||||
protected override int CipherDecrypt(Span<byte> plain, ReadOnlySpan<byte> cipher) | |||||
{ | |||||
return CipherUpdate(cipher, plain); | |||||
} | |||||
protected virtual int CipherUpdate(ReadOnlySpan<byte> input, Span<byte> output) | |||||
{ | |||||
var i = input.ToArray(); | |||||
var o = new byte[_encryptor.GetOutputSize(i.Length)]; | |||||
var res = _encryptor.ProcessBytes(i, 0, i.Length, o, 0); | |||||
o.CopyTo(output); | |||||
return res; | |||||
} | |||||
#region Cipher Info | |||||
private static readonly Dictionary<string, CipherInfo> _ciphers = new Dictionary<string, CipherInfo> | |||||
{ | |||||
{ "chacha20-ietf", new CipherInfo("chacha20-ietf", 32, 12, CipherFamily.Chacha20) }, | |||||
}; | |||||
public static Dictionary<string, CipherInfo> SupportedCiphers() | |||||
{ | |||||
return _ciphers; | |||||
} | |||||
protected override Dictionary<string, CipherInfo> GetCiphers() | |||||
{ | |||||
return _ciphers; | |||||
} | |||||
#endregion | |||||
} | |||||
} |
@@ -1,76 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Runtime.CompilerServices; | |||||
using NaCl.Core; | |||||
namespace Shadowsocks.Net.Crypto.Stream | |||||
{ | |||||
public class StreamChachaNaClCrypto : StreamCrypto | |||||
{ | |||||
const int BlockSize = 64; | |||||
// tcp is stream, which can split into chunks at unexpected position... | |||||
// so we need some special handling, as we can't read all data before encrypt | |||||
// we did it in AEADEncryptor.cs for AEAD, it can operate at block level | |||||
// but we need do it ourselves in stream cipher. | |||||
// when new data arrive, put it on correct offset | |||||
// and update it, ignore other data, get it in correct offset... | |||||
readonly byte[] chachaBuf = new byte[MaxInputSize + BlockSize]; | |||||
// the 'correct offset', always in 0~BlockSize range, so input data always fit into buffer | |||||
int remain = 0; | |||||
// increase counter manually... | |||||
int ic = 0; | |||||
public StreamChachaNaClCrypto(string method, string password) : base(method, password) | |||||
{ | |||||
} | |||||
protected override int CipherDecrypt(Span<byte> plain, ReadOnlySpan<byte> cipher) | |||||
{ | |||||
return CipherUpdate(cipher, plain, false); | |||||
} | |||||
protected override int CipherEncrypt(ReadOnlySpan<byte> plain, Span<byte> cipher) | |||||
{ | |||||
return CipherUpdate(plain, cipher, true); | |||||
} | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
private int CipherUpdate(ReadOnlySpan<byte> i, Span<byte> o, bool enc) | |||||
{ | |||||
// about performance problem: | |||||
// as a part of hacking for streaming, we need manually increase IC | |||||
// so we need new Chacha20 | |||||
// and to get correct position, copy paste array everytime is required | |||||
// NaCl.Core has no int Encrypt(ReadOnlySpan<byte>,Span<byte>)... | |||||
int len = i.Length; | |||||
int pad = remain; | |||||
i.CopyTo(chachaBuf.AsSpan(pad)); | |||||
var chacha = new ChaCha20(key, ic); | |||||
var p = enc ? chacha.Encrypt(chachaBuf, iv) : chacha.Decrypt(chachaBuf, iv); | |||||
p.AsSpan(pad, len).CopyTo(o); | |||||
pad += len; | |||||
ic += pad / BlockSize; | |||||
remain = pad % BlockSize; | |||||
return len; | |||||
} | |||||
#region Cipher Info | |||||
private static readonly Dictionary<string, CipherInfo> _ciphers = new Dictionary<string, CipherInfo> | |||||
{ | |||||
{ "chacha20-ietf", new CipherInfo("chacha20-ietf", 32, 12, CipherFamily.Chacha20) }, | |||||
}; | |||||
public static Dictionary<string, CipherInfo> SupportedCiphers() | |||||
{ | |||||
return _ciphers; | |||||
} | |||||
protected override Dictionary<string, CipherInfo> GetCiphers() | |||||
{ | |||||
return _ciphers; | |||||
} | |||||
#endregion | |||||
} | |||||
} |
@@ -6,7 +6,6 @@ | |||||
<ItemGroup> | <ItemGroup> | ||||
<PackageReference Include="BouncyCastle.NetCore" Version="1.8.6" /> | <PackageReference Include="BouncyCastle.NetCore" Version="1.8.6" /> | ||||
<PackageReference Include="NaCl.Core" Version="2.0.0" /> | |||||
<PackageReference Include="Splat" Version="9.6.1" /> | <PackageReference Include="Splat" Version="9.6.1" /> | ||||
</ItemGroup> | </ItemGroup> | ||||