@@ -22,9 +22,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
if (string.IsNullOrEmpty(_cachedPacSecret)) | if (string.IsNullOrEmpty(_cachedPacSecret)) | ||||
{ | { | ||||
var rd = new byte[32]; | |||||
RNG.GetBytes(rd); | |||||
_cachedPacSecret = HttpServerUtilityUrlToken.Encode(rd); | |||||
_cachedPacSecret = HttpServerUtilityUrlToken.Encode(RNG.GetBytes(32)); | |||||
} | } | ||||
return _cachedPacSecret; | return _cachedPacSecret; | ||||
} | } | ||||
@@ -325,7 +325,6 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
SystemProxy.Update(_config, true, null); | SystemProxy.Update(_config, true, null); | ||||
} | } | ||||
Encryption.RNG.Close(); | |||||
} | } | ||||
private void StopPlugins() | private void StopPlugins() | ||||
@@ -178,8 +178,7 @@ namespace Shadowsocks.Encryption.AEAD | |||||
{ | { | ||||
_encryptSaltSent = true; | _encryptSaltSent = true; | ||||
// Generate salt | // Generate salt | ||||
byte[] saltBytes = new byte[saltLen]; | |||||
RNG.GetBytes(saltBytes, saltLen); | |||||
byte[] saltBytes = RNG.GetBytes(saltLen); | |||||
InitCipher(saltBytes, true, false); | InitCipher(saltBytes, true, false); | ||||
Array.Copy(saltBytes, 0, outbuf, 0, saltLen); | Array.Copy(saltBytes, 0, outbuf, 0, saltLen); | ||||
outlength = saltLen; | outlength = saltLen; | ||||
@@ -336,11 +335,19 @@ namespace Shadowsocks.Encryption.AEAD | |||||
#endregion | #endregion | ||||
#region UDP | #region UDP | ||||
/// <summary> | |||||
/// Perform AEAD UDP packet encryption | |||||
/// </summary> | |||||
/// payload => [salt][encrypted payload][tag] | |||||
/// <param name="buf"></param> | |||||
/// <param name="length"></param> | |||||
/// <param name="outbuf"></param> | |||||
/// <param name="outlength"></param> | |||||
public override void EncryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength) | public override void EncryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength) | ||||
{ | { | ||||
// Generate salt | // Generate salt | ||||
RNG.GetBytes(outbuf, saltLen); | |||||
//RNG.GetBytes(outbuf, saltLen); | |||||
RNG.GetSpan(outbuf.AsSpan().Slice(0, saltLen)); | |||||
InitCipher(outbuf, true, true); | InitCipher(outbuf, true, true); | ||||
//uint olen = 0; | //uint olen = 0; | ||||
lock (_udpTmpBuf) | lock (_udpTmpBuf) | ||||
@@ -351,7 +358,7 @@ namespace Shadowsocks.Encryption.AEAD | |||||
//Debug.Assert(olen == length + tagLen); | //Debug.Assert(olen == length + tagLen); | ||||
Buffer.BlockCopy(cipher, 0, outbuf, saltLen, length + tagLen); | Buffer.BlockCopy(cipher, 0, outbuf, saltLen, length + tagLen); | ||||
//Buffer.BlockCopy(_udpTmpBuf, 0, outbuf, saltLen, (int)olen); | //Buffer.BlockCopy(_udpTmpBuf, 0, outbuf, saltLen, (int)olen); | ||||
outlength = (int)(saltLen + cipher.Length); | |||||
outlength = saltLen + cipher.Length; | |||||
} | } | ||||
} | } | ||||
@@ -35,6 +35,7 @@ namespace Shadowsocks.Encryption | |||||
public class StreamCipherParameter : CipherParameter | public class StreamCipherParameter : CipherParameter | ||||
{ | { | ||||
public int IvSize; | public int IvSize; | ||||
public override string ToString() => $"stream (key:{KeySize * 8}, iv:{IvSize * 8})"; | |||||
} | } | ||||
public class AEADCipherParameter : CipherParameter | public class AEADCipherParameter : CipherParameter | ||||
@@ -42,6 +43,7 @@ namespace Shadowsocks.Encryption | |||||
public int SaltSize; | public int SaltSize; | ||||
public int TagSize; | public int TagSize; | ||||
public int NonceSize; | public int NonceSize; | ||||
public override string ToString() => $"aead (key:{KeySize * 8}, salt:{SaltSize * 8}, tag:{TagSize * 8}, nonce:{NonceSize * 8})"; | |||||
} | } | ||||
public class CipherInfo | public class CipherInfo | ||||
@@ -87,5 +89,11 @@ namespace Shadowsocks.Encryption | |||||
{ | { | ||||
return StandardState == CipherStandardState.InUse ? Name : $"{Name} ({I18N.GetString("deprecated")})"; | return StandardState == CipherStandardState.InUse ? Name : $"{Name} ({I18N.GetString("deprecated")})"; | ||||
} | } | ||||
public string ToString(bool verbose) | |||||
{ | |||||
if (!verbose) return ToString(); | |||||
return $"{Name} {StandardState} {CipherParameter}"; | |||||
} | |||||
} | } | ||||
} | } |
@@ -2,6 +2,7 @@ | |||||
using Org.BouncyCastle.Crypto.Digests; | using Org.BouncyCastle.Crypto.Digests; | ||||
using Org.BouncyCastle.Crypto.Generators; | using Org.BouncyCastle.Crypto.Generators; | ||||
using Org.BouncyCastle.Crypto.Parameters; | using Org.BouncyCastle.Crypto.Parameters; | ||||
using System; | |||||
namespace Shadowsocks.Encryption | namespace Shadowsocks.Encryption | ||||
{ | { | ||||
@@ -15,6 +16,16 @@ namespace Shadowsocks.Encryption | |||||
md5.DoFinal(r, 0); | md5.DoFinal(r, 0); | ||||
return r; | return r; | ||||
} | } | ||||
// currently useless, just keep api same | |||||
public static Span<byte> MD5(Span<byte> span) | |||||
{ | |||||
byte[] b = span.ToArray(); | |||||
MD5Digest md5 = new MD5Digest(); | |||||
md5.BlockUpdate(b, 0, b.Length); | |||||
byte[] r = new byte[16]; | |||||
md5.DoFinal(r, 0); | |||||
return r; | |||||
} | |||||
public static byte[] HKDF(int keylen, byte[] master, byte[] salt, byte[] info) | public static byte[] HKDF(int keylen, byte[] master, byte[] salt, byte[] info) | ||||
{ | { | ||||
@@ -26,6 +37,17 @@ namespace Shadowsocks.Encryption | |||||
hkdf.GenerateBytes(ret, 0, keylen); | hkdf.GenerateBytes(ret, 0, keylen); | ||||
return ret; | 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) | |||||
{ | |||||
byte[] ret = new byte[keylen]; | |||||
IDigest degist = new Sha1Digest(); | |||||
HkdfParameters parameters = new HkdfParameters(master.ToArray(), salt.ToArray(), info.ToArray()); | |||||
HkdfBytesGenerator hkdf = new HkdfBytesGenerator(degist); | |||||
hkdf.Init(parameters); | |||||
hkdf.GenerateBytes(ret, 0, keylen); | |||||
return ret.AsSpan(); | |||||
} | |||||
public static void SodiumIncrement(byte[] salt) | public static void SodiumIncrement(byte[] salt) | ||||
{ | { | ||||
@@ -38,5 +60,16 @@ namespace Shadowsocks.Encryption | |||||
o = salt[i] == 0; | o = salt[i] == 0; | ||||
} | } | ||||
} | } | ||||
public static void SodiumIncrement(Span<byte> salt) | |||||
{ | |||||
bool o = true; // overflow flag | |||||
for (int i = 0; i < salt.Length; i++) | |||||
{ | |||||
if (!o) continue; | |||||
salt[i]++; | |||||
o = salt[i] == 0; | |||||
} | |||||
} | |||||
} | } | ||||
} | } |
@@ -9,6 +9,8 @@ namespace Shadowsocks.Encryption | |||||
{ | { | ||||
public static class EncryptorFactory | public static class EncryptorFactory | ||||
{ | { | ||||
public static string DefaultCipher = "chacha20-ietf-poly1305"; | |||||
private static Dictionary<string, Type> _registeredEncryptors = new Dictionary<string, Type>(); | private static Dictionary<string, Type> _registeredEncryptors = new Dictionary<string, Type>(); | ||||
private static Dictionary<string, CipherInfo> ciphers = new Dictionary<string, CipherInfo>(); | private static Dictionary<string, CipherInfo> ciphers = new Dictionary<string, CipherInfo>(); | ||||
private static readonly Type[] ConstructorTypes = { typeof(string), typeof(string) }; | private static readonly Type[] ConstructorTypes = { typeof(string), typeof(string) }; | ||||
@@ -48,15 +50,6 @@ namespace Shadowsocks.Encryption | |||||
_registeredEncryptors.Add(method.Key, typeof(AEADNaClEncryptor)); | _registeredEncryptors.Add(method.Key, typeof(AEADNaClEncryptor)); | ||||
} | } | ||||
} | } | ||||
foreach (var method in StreamRc4NativeEncryptor.SupportedCiphers()) | |||||
{ | |||||
if (!_registeredEncryptors.ContainsKey(method.Key)) | |||||
{ | |||||
ciphers.Add(method.Key, method.Value); | |||||
_registeredEncryptors.Add(method.Key, typeof(StreamRc4NativeEncryptor)); | |||||
} | |||||
} | |||||
} | } | ||||
public static IEncryptor GetEncryptor(string method, string password) | public static IEncryptor GetEncryptor(string method, string password) | ||||
@@ -67,7 +60,11 @@ namespace Shadowsocks.Encryption | |||||
} | } | ||||
method = method.ToLowerInvariant(); | method = method.ToLowerInvariant(); | ||||
Type t = _registeredEncryptors[method]; | |||||
bool ok = _registeredEncryptors.TryGetValue(method, out Type t); | |||||
if (!ok) | |||||
{ | |||||
t = _registeredEncryptors[DefaultCipher]; | |||||
} | |||||
ConstructorInfo c = t.GetConstructor(ConstructorTypes); | ConstructorInfo c = t.GetConstructor(ConstructorTypes); | ||||
if (c == null) throw new System.Exception("Invalid ctor"); | if (c == null) throw new System.Exception("Invalid ctor"); | ||||
@@ -83,7 +80,7 @@ namespace Shadowsocks.Encryption | |||||
sb.AppendLine("Registered Encryptor Info"); | sb.AppendLine("Registered Encryptor Info"); | ||||
foreach (var encryptor in _registeredEncryptors) | foreach (var encryptor in _registeredEncryptors) | ||||
{ | { | ||||
sb.AppendLine(String.Format("{0}=>{1}", encryptor.Key, encryptor.Value.Name)); | |||||
sb.AppendLine($"{ciphers[encryptor.Key].ToString(true)} => {encryptor.Value.Name}"); | |||||
} | } | ||||
// use ----- instead of =======, so when user paste it to Github, it won't became title | // use ----- instead of =======, so when user paste it to Github, it won't became title | ||||
sb.AppendLine("-------------------------"); | sb.AppendLine("-------------------------"); | ||||
@@ -92,6 +89,7 @@ namespace Shadowsocks.Encryption | |||||
public static CipherInfo GetCipherInfo(string name) | public static CipherInfo GetCipherInfo(string name) | ||||
{ | { | ||||
// TODO: Replace cipher when required not exist | |||||
return ciphers[name]; | return ciphers[name]; | ||||
} | } | ||||
@@ -5,33 +5,35 @@ namespace Shadowsocks.Encryption | |||||
{ | { | ||||
public static class RNG | public static class RNG | ||||
{ | { | ||||
private static RNGCryptoServiceProvider _rng = null; | |||||
private static RNGCryptoServiceProvider _rng = new RNGCryptoServiceProvider(); | |||||
public static void Init() | |||||
public static void Reload() | |||||
{ | { | ||||
_rng = _rng ?? new RNGCryptoServiceProvider(); | |||||
_rng.Dispose(); | |||||
_rng = new RNGCryptoServiceProvider(); | |||||
} | } | ||||
public static void Close() | |||||
public static void GetSpan(Span<byte> span) | |||||
{ | { | ||||
_rng?.Dispose(); | |||||
_rng = null; | |||||
_rng.GetBytes(span); | |||||
} | } | ||||
public static void Reload() | |||||
public static Span<byte> GetSpan(int length) | |||||
{ | { | ||||
Close(); | |||||
Init(); | |||||
Span<byte> span = new byte[length]; | |||||
_rng.GetBytes(span); | |||||
return span; | |||||
} | } | ||||
public static void GetBytes(byte[] buf) | |||||
public static byte[] GetBytes(int length) | |||||
{ | { | ||||
GetBytes(buf, buf.Length); | |||||
byte[] buf = new byte[length]; | |||||
_rng.GetBytes(buf); | |||||
return buf; | |||||
} | } | ||||
public static void GetBytes(byte[] buf, int len) | public static void GetBytes(byte[] buf, int len) | ||||
{ | { | ||||
if (_rng == null) Init(); | |||||
try | try | ||||
{ | { | ||||
_rng.GetBytes(buf, 0, len); | _rng.GetBytes(buf, 0, len); | ||||
@@ -107,7 +107,6 @@ namespace Shadowsocks.Test | |||||
{ | { | ||||
t.Join(); | t.Join(); | ||||
} | } | ||||
RNG.Close(); | |||||
Assert.IsFalse(encryptionFailed); | Assert.IsFalse(encryptionFailed); | ||||
} | } | ||||