Browse Source

add AEAD support

tags/4.0
Syrone Wong 8 years ago
parent
commit
3de14098d7
20 changed files with 1354 additions and 589 deletions
  1. +13
    -1
      shadowsocks-csharp/Controller/Logging.cs
  2. +206
    -125
      shadowsocks-csharp/Controller/Service/TCPRelay.cs
  3. +16
    -12
      shadowsocks-csharp/Controller/Service/UDPRelay.cs
  4. +347
    -0
      shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs
  5. +176
    -0
      shadowsocks-csharp/Encryption/AEAD/AEADMbedTLSEncryptor.cs
  6. +108
    -0
      shadowsocks-csharp/Encryption/AEAD/AEADSodiumEncryptor.cs
  7. +50
    -17
      shadowsocks-csharp/Encryption/EncryptorBase.cs
  8. +21
    -11
      shadowsocks-csharp/Encryption/EncryptorFactory.cs
  9. +17
    -0
      shadowsocks-csharp/Encryption/Exception/CryptoException.cs
  10. +4
    -0
      shadowsocks-csharp/Encryption/IEncryptor.cs
  11. +0
    -288
      shadowsocks-csharp/Encryption/IVEncryptor.cs
  12. +105
    -76
      shadowsocks-csharp/Encryption/MbedTLS.cs
  13. +3
    -2
      shadowsocks-csharp/Encryption/RNG.cs
  14. +49
    -13
      shadowsocks-csharp/Encryption/Sodium.cs
  15. +181
    -0
      shadowsocks-csharp/Encryption/Stream/StreamEncryptor.cs
  16. +18
    -17
      shadowsocks-csharp/Encryption/Stream/StreamMbedTLSEncryptor.cs
  17. +15
    -11
      shadowsocks-csharp/Encryption/Stream/StreamSodiumEncryptor.cs
  18. +1
    -0
      shadowsocks-csharp/packages.config
  19. +16
    -9
      shadowsocks-csharp/shadowsocks-csharp.csproj
  20. +8
    -7
      test/UnitTest.cs

+ 13
- 1
shadowsocks-csharp/Controller/Logging.cs View File

@@ -4,7 +4,7 @@ using System.IO;
using System.Net.Sockets;
using System.Net;
using System.Diagnostics;
using System.Text;
using Shadowsocks.Util;
namespace Shadowsocks.Controller
@@ -70,6 +70,18 @@ namespace Shadowsocks.Controller
WriteToLogFile("[D] " + o);
}
[Conditional("DEBUG")]
public static void Dump(string tag, byte[] arr, int length)
{
var sb = new StringBuilder($"{Environment.NewLine}{tag}: ");
for (int i = 0; i < length - 1; i++) {
sb.Append($"0x{arr[i]:X2}, ");
}
sb.Append($"0x{arr[length - 1]:X2}");
sb.Append(Environment.NewLine);
Debug(sb.ToString());
}
[Conditional("DEBUG")]
public static void Debug(EndPoint local, EndPoint remote, int len, string header = null, string tailer = null)
{


+ 206
- 125
shadowsocks-csharp/Controller/Service/TCPRelay.cs View File

@@ -4,12 +4,14 @@ using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Timers;
using Shadowsocks.Controller.Strategy;
using Shadowsocks.Encryption;
using Shadowsocks.Encryption.AEAD;
using Shadowsocks.Encryption.Exception;
using Shadowsocks.Model;
using Shadowsocks.Proxy;
using Shadowsocks.Util.Sockets;
using static Shadowsocks.Encryption.EncryptorBase;
namespace Shadowsocks.Controller
{
@@ -74,7 +76,7 @@ namespace Shadowsocks.Controller
{
handlersToClose.AddRange(Handlers);
}
handlersToClose.ForEach(h=>h.Close());
handlersToClose.ForEach(h => h.Close());
}
public void UpdateInboundCounter(Server server, long n)
@@ -93,7 +95,7 @@ namespace Shadowsocks.Controller
}
}
class TCPHandler
internal class TCPHandler
{
class AsyncSession
{
@@ -114,7 +116,7 @@ namespace Shadowsocks.Controller
State = state;
}
public AsyncSession(AsyncSession session, T state): base(session.Remote)
public AsyncSession(AsyncSession session, T state) : base(session.Remote)
{
State = state;
}
@@ -123,46 +125,66 @@ namespace Shadowsocks.Controller
private readonly int _serverTimeout;
private readonly int _proxyTimeout;
// Size of receive buffer.
public static readonly int RecvSize = 8192;
public static readonly int RecvReserveSize = IVEncryptor.ONETIMEAUTH_BYTES + IVEncryptor.AUTH_BYTES; // reserve for one-time auth
public static readonly int BufferSize = RecvSize + RecvReserveSize + 32;
// each recv size.
public const int RecvSize = 2048;
// overhead of one chunk, reserved for AEAD ciphers
public const int ChunkOverheadSize = 16 * 2 /* two tags */ + AEADEncryptor.CHUNK_LEN_BYTES;
// max chunk size
public const uint MaxChunkSize = AEADEncryptor.CHUNK_LEN_MASK + AEADEncryptor.CHUNK_LEN_BYTES + 16 * 2;
// In general, the ciphertext length, we should take overhead into account
public const int BufferSize = RecvSize + (int)MaxChunkSize + 32 /* max salt len */;
public DateTime lastActivity;
private ShadowsocksController _controller;
private Configuration _config;
private TCPRelay _tcprelay;
private Socket _connection;
private ShadowsocksController _controller;
private Configuration _config;
private TCPRelay _tcprelay;
private Socket _connection;
private IEncryptor _encryptor;
private Server _server;
private IEncryptor _encryptor;
private Server _server;
private AsyncSession _currentRemoteSession;
private bool _proxyConnected;
private bool _destConnected;
private bool _proxyConnected;
private bool _destConnected;
private byte _command;
private byte[] _firstPacket;
private int _firstPacketLength;
private byte _command;
private byte[] _firstPacket;
private int _firstPacketLength;
private const int CMD_CONNECT = 0x01;
private const int CMD_UDP_ASSOC = 0x03;
private int _totalRead = 0;
private int _totalWrite = 0;
private int _addrBufLength = -1;
private byte[] _remoteRecvBuffer = new byte[BufferSize];
private byte[] _remoteSendBuffer = new byte[BufferSize];
private byte[] _connetionRecvBuffer = new byte[BufferSize];
private byte[] _connetionSendBuffer = new byte[BufferSize];
private int _totalRead = 0;
private int _totalWrite = 0;
private bool _connectionShutdown = false;
private bool _remoteShutdown = false;
private bool _closed = false;
// remote -> local proxy (ciphertext, before decrypt)
private byte[] _remoteRecvBuffer = new byte[BufferSize];
// client -> local proxy (plaintext, before encrypt)
private byte[] _connetionRecvBuffer = new byte[BufferSize];
// local proxy -> remote (plaintext, after decrypt)
private byte[] _remoteSendBuffer = new byte[BufferSize];
// local proxy -> client (ciphertext, before decrypt)
private byte[] _connetionSendBuffer = new byte[BufferSize];
private bool _connectionShutdown = false;
private bool _remoteShutdown = false;
private bool _closed = false;
// instance-based lock without static
private readonly object _encryptionLock = new object();
private readonly object _decryptionLock = new object();
private readonly object _closeConnLock = new object();
private readonly object _encryptionLock = new object();
private readonly object _decryptionLock = new object();
private readonly object _closeConnLock = new object();
private DateTime _startConnectTime;
private DateTime _startReceivingTime;
@@ -184,17 +206,18 @@ namespace Shadowsocks.Controller
public void CreateRemote()
{
Server server = _controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)_connection.RemoteEndPoint, _destEndPoint);
Server server = _controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)_connection.RemoteEndPoint,
_destEndPoint);
if (server == null || server.server == "")
throw new ArgumentException("No server configured");
lock (_encryptionLock)
{
lock (_decryptionLock)
{
_encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.auth, false);
}
}
_encryptor = EncryptorFactory.GetEncryptor(server.method, server.password);
this._server = server;
/* prepare address buffer length for AEAD */
Logging.Debug($"_addrBufLength={_addrBufLength}");
_encryptor.AddrBufLength = _addrBufLength;
}
public void Start(byte[] firstPacket, int length)
@@ -269,7 +292,8 @@ namespace Shadowsocks.Controller
response = new byte[] { 0, 91 };
Logging.Error("socks 5 protocol error");
}
_connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(HandshakeSendCallback), null);
_connection.BeginSend(response, 0, response.Length, SocketFlags.None,
HandshakeSendCallback, null);
}
else
Close();
@@ -296,8 +320,8 @@ namespace Shadowsocks.Controller
// Skip first 3 bytes, and read 2 more bytes to analysis the address.
// 2 more bytes is designed if address is domain then we don't need to read once more to get the addr length.
// TODO validate
_connection.BeginReceive(_connetionRecvBuffer, 0, 3 + 2, SocketFlags.None,
new AsyncCallback(handshakeReceive2Callback), null);
_connection.BeginReceive(_connetionRecvBuffer, 0, 3 + ADDR_ATYP_LEN + 1, SocketFlags.None,
HandshakeReceive2Callback, null);
}
catch (Exception e)
{
@@ -306,7 +330,7 @@ namespace Shadowsocks.Controller
}
}
private void handshakeReceive2Callback(IAsyncResult ar)
private void HandshakeReceive2Callback(IAsyncResult ar)
{
if (_closed) return;
try
@@ -315,20 +339,20 @@ namespace Shadowsocks.Controller
if (bytesRead >= 5)
{
_command = _connetionRecvBuffer[1];
if (_command != 1 && _command != 3)
if (_command != CMD_CONNECT && _command != CMD_UDP_ASSOC)
{
Logging.Debug("Unsupported CMD=" + _command);
Close();
}
else
{
if (_command == 1)
if (_command == CMD_CONNECT)
{
byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 };
_connection.BeginSend(response, 0, response.Length, SocketFlags.None,
new AsyncCallback(ResponseCallback), null);
ResponseCallback, null);
}
else if (_command == 3)
else if (_command == CMD_UDP_ASSOC)
{
ReadAddress(HandleUDPAssociate);
}
@@ -336,7 +360,8 @@ namespace Shadowsocks.Controller
}
else
{
Logging.Debug("failed to recv data in Shadowsocks.Controller.TCPHandler.handshakeReceive2Callback()");
Logging.Debug(
"failed to recv data in Shadowsocks.Controller.TCPHandler.handshakeReceive2Callback()");
Close();
}
}
@@ -368,15 +393,15 @@ namespace Shadowsocks.Controller
switch (atyp)
{
case 1: // IPv4 address, 4 bytes
ReadAddress(4 + 2 - 1, onSuccess);
case ATYP_IPv4: // IPv4 address, 4 bytes
ReadAddress(4 + ADDR_PORT_LEN - 1, onSuccess);
break;
case 3: // domain name, length + str
case ATYP_DOMAIN: // domain name, length + str
int len = _connetionRecvBuffer[4];
ReadAddress(len + 2, onSuccess);
ReadAddress(len + ADDR_PORT_LEN, onSuccess);
break;
case 4: // IPv6 address, 16 bytes
ReadAddress(16 + 2 - 1, onSuccess);
case ATYP_IPv6: // IPv6 address, 16 bytes
ReadAddress(16 + ADDR_PORT_LEN - 1, onSuccess);
break;
default:
Logging.Debug("Unsupported ATYP=" + atyp);
@@ -387,10 +412,12 @@ namespace Shadowsocks.Controller
private void ReadAddress(int bytesRemain, Action onSuccess)
{
Array.Copy(_connetionRecvBuffer, 3, _connetionRecvBuffer, 0, 2);
// drop [ VER | CMD | RSV ]
Array.Copy(_connetionRecvBuffer, 3, _connetionRecvBuffer, 0, ADDR_ATYP_LEN + 1);
// Read the remain address bytes
_connection.BeginReceive(_connetionRecvBuffer, 2, RecvSize - 2, SocketFlags.None, OnAddressFullyRead, new object[] {bytesRemain, onSuccess});
_connection.BeginReceive(_connetionRecvBuffer, 2, RecvSize - 2, SocketFlags.None, OnAddressFullyRead,
new object[] { bytesRemain, onSuccess });
}
private void OnAddressFullyRead(IAsyncResult ar)
@@ -400,10 +427,10 @@ namespace Shadowsocks.Controller
{
int bytesRead = _connection.EndReceive(ar);
var states = (object[]) ar.AsyncState;
var states = (object[])ar.AsyncState;
int bytesRemain = (int)states[0];
var onSuccess = (Action) states[1];
var onSuccess = (Action)states[1];
if (bytesRead >= bytesRemain)
{
@@ -411,35 +438,39 @@ namespace Shadowsocks.Controller
int atyp = _connetionRecvBuffer[0];
string dst_addr = "Unknown";
int dst_port = -1;
string dstAddr = "Unknown";
int dstPort = -1;
switch (atyp)
{
case 1: // IPv4 address, 4 bytes
dst_addr = new IPAddress(_connetionRecvBuffer.Skip(1).Take(4).ToArray()).ToString();
dst_port = (_connetionRecvBuffer[5] << 8) + _connetionRecvBuffer[6];
case ATYP_IPv4: // IPv4 address, 4 bytes
dstAddr = new IPAddress(_connetionRecvBuffer.Skip(1).Take(4).ToArray()).ToString();
dstPort = (_connetionRecvBuffer[5] << 8) + _connetionRecvBuffer[6];
_addrBufLength = ADDR_ATYP_LEN + 4 + ADDR_PORT_LEN;
break;
case 3: // domain name, length + str
case ATYP_DOMAIN: // domain name, length + str
int len = _connetionRecvBuffer[1];
dst_addr = System.Text.Encoding.UTF8.GetString(_connetionRecvBuffer, 2, len);
dst_port = (_connetionRecvBuffer[len + 2] << 8) + _connetionRecvBuffer[len + 3];
dstAddr = System.Text.Encoding.UTF8.GetString(_connetionRecvBuffer, 2, len);
dstPort = (_connetionRecvBuffer[len + 2] << 8) + _connetionRecvBuffer[len + 3];
_addrBufLength = ADDR_ATYP_LEN + 1 + len + ADDR_PORT_LEN;
break;
case 4: // IPv6 address, 16 bytes
dst_addr = $"[{new IPAddress(_connetionRecvBuffer.Skip(1).Take(16).ToArray())}]";
dst_port = (_connetionRecvBuffer[17] << 8) + _connetionRecvBuffer[18];
case ATYP_IPv6: // IPv6 address, 16 bytes
dstAddr = $"[{new IPAddress(_connetionRecvBuffer.Skip(1).Take(16).ToArray())}]";
dstPort = (_connetionRecvBuffer[17] << 8) + _connetionRecvBuffer[18];
_addrBufLength = ADDR_ATYP_LEN + 16 + ADDR_PORT_LEN;
break;
}
if (_config.isVerboseLogging)
{
Logging.Info($"connect to {dst_addr}:{dst_port}");
Logging.Info($"connect to {dstAddr}:{dstPort}");
}
_destEndPoint = SocketUtil.GetEndPoint(dst_addr, dst_port);
_destEndPoint = SocketUtil.GetEndPoint(dstAddr, dstPort);
onSuccess.Invoke();
onSuccess.Invoke(); /* StartConnect() */
}
else
{
@@ -459,21 +490,21 @@ namespace Shadowsocks.Controller
IPEndPoint endPoint = (IPEndPoint)_connection.LocalEndPoint;
byte[] address = endPoint.Address.GetAddressBytes();
int port = endPoint.Port;
byte[] response = new byte[4 + address.Length + 2];
byte[] response = new byte[4 + address.Length + ADDR_PORT_LEN];
response[0] = 5;
switch (endPoint.AddressFamily)
{
case AddressFamily.InterNetwork:
response[3] = 1;
response[3] = ATYP_IPv4;
break;
case AddressFamily.InterNetworkV6:
response[3] = 4;
response[3] = ATYP_IPv6;
break;
}
address.CopyTo(response, 4);
response[response.Length - 1] = (byte)(port & 0xFF);
response[response.Length - 2] = (byte)((port >> 8) & 0xFF);
_connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ReadAll), true);
_connection.BeginSend(response, 0, response.Length, SocketFlags.None, ReadAll, true);
}
private void ReadAll(IAsyncResult ar)
@@ -484,14 +515,16 @@ namespace Shadowsocks.Controller
if (ar.AsyncState != null)
{
_connection.EndSend(ar);
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null);
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None,
ReadAll, null);
}
else
{
int bytesRead = _connection.EndReceive(ar);
if (bytesRead > 0)
{
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null);
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None,
ReadAll, null);
}
else
Close();
@@ -522,7 +555,10 @@ namespace Shadowsocks.Controller
public AsyncSession Session;
public Server Server;
public ServerTimer(int p) : base(p) { }
public ServerTimer(int p) : base(p)
{
}
}
private void StartConnect()
@@ -533,7 +569,7 @@ namespace Shadowsocks.Controller
// Setting up proxy
IProxy remote;
EndPoint proxyEP;
EndPoint proxyEP = null;
if (_config.proxy.useProxy)
{
switch (_config.proxy.proxyType)
@@ -552,7 +588,6 @@ namespace Shadowsocks.Controller
else
{
remote = new DirectConnect();
proxyEP = null;
}
var session = new AsyncSession(remote);
@@ -567,9 +602,8 @@ namespace Shadowsocks.Controller
_currentRemoteSession = session;
}
ProxyTimer proxyTimer = new ProxyTimer(_proxyTimeout);
proxyTimer.AutoReset = false;
proxyTimer.Elapsed += proxyConnectTimer_Elapsed;
ProxyTimer proxyTimer = new ProxyTimer(_proxyTimeout) { AutoReset = false };
proxyTimer.Elapsed += ProxyConnectTimer_Elapsed;
proxyTimer.Enabled = true;
proxyTimer.Session = session;
@@ -579,7 +613,8 @@ namespace Shadowsocks.Controller
_proxyConnected = false;
// Connect to the proxy server.
remote.BeginConnectProxy(proxyEP, new AsyncCallback(ProxyConnectCallback), new AsyncSession<ProxyTimer>(remote, proxyTimer));
remote.BeginConnectProxy(proxyEP, ProxyConnectCallback,
new AsyncSession<ProxyTimer>(remote, proxyTimer));
}
catch (Exception e)
{
@@ -588,10 +623,10 @@ namespace Shadowsocks.Controller
}
}
private void proxyConnectTimer_Elapsed(object sender, ElapsedEventArgs e)
private void ProxyConnectTimer_Elapsed(object sender, ElapsedEventArgs e)
{
var timer = (ProxyTimer) sender;
timer.Elapsed -= proxyConnectTimer_Elapsed;
var timer = (ProxyTimer)sender;
timer.Elapsed -= ProxyConnectTimer_Elapsed;
timer.Enabled = false;
timer.Dispose();
@@ -609,18 +644,17 @@ namespace Shadowsocks.Controller
private void ProxyConnectCallback(IAsyncResult ar)
{
Server server = null;
if (_closed)
{
return;
}
try
{
var session = (AsyncSession<ProxyTimer>) ar.AsyncState;
var session = (AsyncSession<ProxyTimer>)ar.AsyncState;
ProxyTimer timer = session.State;
var destEndPoint = timer.DestEndPoint;
server = timer.Server;
timer.Elapsed -= proxyConnectTimer_Elapsed;
var server = timer.Server;
timer.Elapsed -= ProxyConnectTimer_Elapsed;
timer.Enabled = false;
timer.Dispose();
@@ -640,16 +674,16 @@ namespace Shadowsocks.Controller
}
_startConnectTime = DateTime.Now;
ServerTimer connectTimer = new ServerTimer(_serverTimeout);
connectTimer.AutoReset = false;
connectTimer.Elapsed += destConnectTimer_Elapsed;
ServerTimer connectTimer = new ServerTimer(_serverTimeout) { AutoReset = false };
connectTimer.Elapsed += DestConnectTimer_Elapsed;
connectTimer.Enabled = true;
connectTimer.Session = session;
connectTimer.Server = server;
_destConnected = false;
// Connect to the remote endpoint.
remote.BeginConnectDest(destEndPoint, new AsyncCallback(ConnectCallback), new AsyncSession<ServerTimer>(session, connectTimer));
remote.BeginConnectDest(destEndPoint, ConnectCallback,
new AsyncSession<ServerTimer>(session, connectTimer));
}
catch (ArgumentException)
{
@@ -661,10 +695,10 @@ namespace Shadowsocks.Controller
}
}
private void destConnectTimer_Elapsed(object sender, ElapsedEventArgs e)
private void DestConnectTimer_Elapsed(object sender, ElapsedEventArgs e)
{
var timer = (ServerTimer)sender;
timer.Elapsed -= destConnectTimer_Elapsed;
timer.Elapsed -= DestConnectTimer_Elapsed;
timer.Enabled = false;
timer.Dispose();
@@ -687,17 +721,17 @@ namespace Shadowsocks.Controller
if (_closed) return;
try
{
var session = (AsyncSession<ServerTimer>) ar.AsyncState;
var session = (AsyncSession<ServerTimer>)ar.AsyncState;
ServerTimer timer = session.State;
_server = timer.Server;
timer.Elapsed -= destConnectTimer_Elapsed;
timer.Elapsed -= DestConnectTimer_Elapsed;
timer.Enabled = false;
timer.Dispose();
var remote = session.Remote;
// Complete the connection.
remote.EndConnectDest(ar);
_destConnected = true;
if (_config.isVerboseLogging)
@@ -727,19 +761,11 @@ namespace Shadowsocks.Controller
}
}
// private static readonly Random Rnd = new Random();
private void TryReadAvailableData()
{
int available = Math.Min(_connection.Available, RecvSize - _firstPacketLength);
if (available > 0)
{
// Pick a random chunk size, or is it truly necessary? Random packet size is some sort of 'characteristic' itself.
//lock (Rnd)
//{
// available = Rnd.Next(available + 1);
//}
var size = _connection.Receive(_connetionRecvBuffer, _firstPacketLength, available,
SocketFlags.None);
@@ -753,7 +779,8 @@ namespace Shadowsocks.Controller
try
{
_startReceivingTime = DateTime.Now;
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session);
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None,
PipeRemoteReceiveCallback, session);
TryReadAvailableData();
Logging.Debug($"_firstPacketLength = {_firstPacketLength}");
@@ -771,19 +798,38 @@ namespace Shadowsocks.Controller
if (_closed) return;
try
{
var session = (AsyncSession) ar.AsyncState;
var session = (AsyncSession)ar.AsyncState;
int bytesRead = session.Remote.EndReceive(ar);
_totalRead += bytesRead;
_tcprelay.UpdateInboundCounter(_server, bytesRead);
if (bytesRead > 0)
{
lastActivity = DateTime.Now;
int bytesToSend;
int bytesToSend = -1;
lock (_decryptionLock)
{
_encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend);
try
{
_encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend);
}
catch (CryptoErrorException)
{
Logging.Error("decryption error");
Close();
return;
}
}
if (bytesToSend == 0)
{
// need more to decrypt
Logging.Debug("Need more to decrypt");
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None,
PipeRemoteReceiveCallback, session);
return;
}
_connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeConnectionSendCallback), session);
Logging.Debug($"start sending {bytesToSend}");
_connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None,
PipeConnectionSendCallback, new object[] { session, bytesToSend });
IStrategy strategy = _controller.GetCurrentStrategy();
strategy?.UpdateLastRead(_server);
}
@@ -808,7 +854,7 @@ namespace Shadowsocks.Controller
{
int bytesRead = _connection.EndReceive(ar);
var session = (AsyncSession) ar.AsyncState;
var session = (AsyncSession)ar.AsyncState;
var remote = session.Remote;
if (bytesRead > 0)
@@ -835,11 +881,21 @@ namespace Shadowsocks.Controller
int bytesToSend;
lock (_encryptionLock)
{
_encryptor.Encrypt(_connetionRecvBuffer, length, _connetionSendBuffer, out bytesToSend);
try
{
_encryptor.Encrypt(_connetionRecvBuffer, length, _connetionSendBuffer, out bytesToSend);
}
catch (CryptoErrorException)
{
Logging.Debug("encryption error");
Close();
return;
}
}
_tcprelay.UpdateOutboundCounter(_server, bytesToSend);
_startSendingTime = DateTime.Now;
session.Remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeRemoteSendCallback), session);
session.Remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None,
PipeRemoteSendCallback, new object[] { session, bytesToSend });
IStrategy strategy = _controller.GetCurrentStrategy();
strategy?.UpdateLastWrite(_server);
}
@@ -849,9 +905,21 @@ namespace Shadowsocks.Controller
if (_closed) return;
try
{
var session = (AsyncSession)ar.AsyncState;
session.Remote.EndSend(ar);
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), session);
var container = (object[])ar.AsyncState;
var session = (AsyncSession)container[0];
var bytesShouldSend = (int)container[1];
int bytesSent = session.Remote.EndSend(ar);
int bytesRemaining = bytesShouldSend - bytesSent;
if (bytesRemaining > 0)
{
Logging.Info("reconstruct _connetionSendBuffer to re-send");
Buffer.BlockCopy(_connetionSendBuffer, bytesSent, _connetionSendBuffer, 0, bytesRemaining);
session.Remote.BeginSend(_connetionSendBuffer, 0, bytesRemaining, SocketFlags.None,
PipeRemoteSendCallback, new object[] { session, bytesRemaining });
return;
}
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None,
PipeConnectionReceiveCallback, session);
}
catch (Exception e)
{
@@ -860,13 +928,26 @@ namespace Shadowsocks.Controller
}
}
// In general, we assume there is no delay between local proxy and client, add this for sanity
private void PipeConnectionSendCallback(IAsyncResult ar)
{
try
{
var session = (AsyncSession)ar.AsyncState;
_connection.EndSend(ar);
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session);
var container = (object[])ar.AsyncState;
var session = (AsyncSession)container[0];
var bytesShouldSend = (int)container[1];
var bytesSent = _connection.EndSend(ar);
var bytesRemaining = bytesShouldSend - bytesSent;
if (bytesRemaining > 0)
{
Logging.Info("reconstruct _remoteSendBuffer to re-send");
Buffer.BlockCopy(_remoteSendBuffer, bytesSent, _remoteSendBuffer, 0, bytesRemaining);
_connection.BeginSend(_remoteSendBuffer, 0, bytesRemaining, SocketFlags.None,
PipeConnectionSendCallback, new object[] { session, bytesRemaining });
return;
}
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None,
PipeRemoteReceiveCallback, session);
}
catch (Exception e)
{
@@ -875,4 +956,4 @@ namespace Shadowsocks.Controller
}
}
}
}
}

+ 16
- 12
shadowsocks-csharp/Controller/Service/UDPRelay.cs View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using Shadowsocks.Controller.Strategy;
using Shadowsocks.Encryption;
using Shadowsocks.Model;
@@ -13,7 +12,9 @@ namespace Shadowsocks.Controller
class UDPRelay : Listener.Service
{
private ShadowsocksController _controller;
private LRUCache<IPEndPoint, UDPHandler> _cache;
// TODO: choose a smart number
private LRUCache<IPEndPoint, UDPHandler> _cache = new LRUCache<IPEndPoint, UDPHandler>(512);
public long outbound = 0;
public long inbound = 0;
@@ -21,7 +22,6 @@ namespace Shadowsocks.Controller
public UDPRelay(ShadowsocksController controller)
{
this._controller = controller;
this._cache = new LRUCache<IPEndPoint, UDPHandler>(512); // todo: choose a smart number
}
public override bool Handle(byte[] firstPacket, int length, Socket socket, object state)
@@ -78,12 +78,12 @@ namespace Shadowsocks.Controller
public void Send(byte[] data, int length)
{
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password, _server.auth, true);
byte[] dataIn = new byte[length - 3 + IVEncryptor.ONETIMEAUTH_BYTES];
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password);
byte[] dataIn = new byte[length - 3];
Array.Copy(data, 3, dataIn, 0, length - 3);
byte[] dataOut = new byte[length - 3 + 16 + IVEncryptor.ONETIMEAUTH_BYTES];
byte[] dataOut = new byte[length - 3 + 16];
int outlen;
encryptor.Encrypt(dataIn, length - 3, dataOut, out outlen);
encryptor.EncryptUDP(dataIn, length - 3, dataOut, out outlen);
Logging.Debug(_localEndPoint, _remoteEndPoint, outlen, "UDP Relay");
_remote?.SendTo(dataOut, outlen, SocketFlags.None, _remoteEndPoint);
}
@@ -106,14 +106,15 @@ namespace Shadowsocks.Controller
byte[] dataOut = new byte[bytesRead];
int outlen;
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password, _server.auth, true);
encryptor.Decrypt(_buffer, bytesRead, dataOut, out outlen);
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password);
encryptor.DecryptUDP(_buffer, bytesRead, dataOut, out outlen);
byte[] sendBuf = new byte[outlen + 3];
Array.Copy(dataOut, 0, sendBuf, 3, outlen);
Logging.Debug(_localEndPoint, _remoteEndPoint, outlen, "UDP Relay");
_local?.SendTo(sendBuf, outlen + 3, 0, _localEndPoint);
Receive();
}
catch (ObjectDisposedException)
@@ -126,6 +127,8 @@ namespace Shadowsocks.Controller
}
finally
{
// No matter success or failed, we keep receiving
}
}
@@ -143,13 +146,12 @@ namespace Shadowsocks.Controller
{
// TODO: need more think about handle other Exceptions, or should remove this catch().
}
finally
{
}
}
}
}
#region LRU cache
// cc by-sa 3.0 http://stackoverflow.com/a/3719378/1124054
class LRUCache<K, V> where V : UDPRelay.UDPHandler
{
@@ -212,4 +214,6 @@ namespace Shadowsocks.Controller
public K key;
public V value;
}
#endregion
}

+ 347
- 0
shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs View File

@@ -0,0 +1,347 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Text;
using Cyotek.Collections.Generic;
using Shadowsocks.Controller;
using Shadowsocks.Encryption.Exception;
using Shadowsocks.Encryption.Stream;
namespace Shadowsocks.Encryption.AEAD
{
public abstract class AEADEncryptor
: EncryptorBase
{
// We are using the same saltLen and keyLen
private const string Info = "ss-subkey";
private static readonly byte[] InfoBytes = Encoding.ASCII.GetBytes(Info);
// for UDP only
protected static byte[] _udpTmpBuf = new byte[MAX_INPUT_SIZE];
// every connection should create its own buffer
private CircularBuffer<byte> _encCircularBuffer = new CircularBuffer<byte>(MAX_INPUT_SIZE * 2, false);
private CircularBuffer<byte> _decCircularBuffer = new CircularBuffer<byte>(MAX_INPUT_SIZE * 2, false);
public const int CHUNK_LEN_BYTES = 2;
public const uint CHUNK_LEN_MASK = 0x3FFFu;
protected Dictionary<string, EncryptorInfo> ciphers;
protected string _method;
protected int _cipher;
// internal name in the crypto library
protected string _innerLibName;
protected EncryptorInfo CipherInfo;
protected static byte[] _Masterkey = null;
protected byte[] _sessionKey;
protected int keyLen;
protected int saltLen;
protected int tagLen;
protected int nonceLen;
protected byte[] _encryptSalt;
protected byte[] _decryptSalt;
protected object _nonceIncrementLock = new object();
protected byte[] _encNonce;
protected byte[] _decNonce;
// Is first packet
protected bool _decryptSaltReceived;
protected bool _encryptSaltSent;
// Is first chunk(tcp request)
protected bool _tcpRequestSent;
public AEADEncryptor(string method, string password)
: base(method, password)
{
InitEncryptorInfo(method);
InitKey(password);
// Initialize all-zero nonce for each connection
_encNonce = new byte[nonceLen];
_decNonce = new byte[nonceLen];
}
protected abstract Dictionary<string, EncryptorInfo> getCiphers();
protected void InitEncryptorInfo(string method)
{
method = method.ToLower();
_method = method;
ciphers = getCiphers();
CipherInfo = ciphers[_method];
_innerLibName = CipherInfo.InnerLibName;
_cipher = CipherInfo.Type;
if (_cipher == 0) {
throw new System.Exception("method not found");
}
keyLen = CipherInfo.KeySize;
saltLen = CipherInfo.SaltSize;
tagLen = CipherInfo.TagSize;
nonceLen = CipherInfo.NonceSize;
}
protected void InitKey(string password)
{
byte[] passbuf = Encoding.UTF8.GetBytes(password);
// init master key
if (_Masterkey == null) _Masterkey = new byte[keyLen];
if (_Masterkey.Length < keyLen) Array.Resize(ref _Masterkey, keyLen);
DeriveKey(passbuf, _Masterkey);
// init session key
if (_sessionKey == null) _sessionKey = new byte[keyLen];
}
public void DeriveKey(byte[] password, byte[] key)
{
StreamEncryptor.LegacyDeriveKey(password, key);
}
public void DeriveSessionKey(byte[] salt, byte[] masterKey, byte[] sessionKey)
{
int ret = MbedTLS.hkdf(salt, saltLen, masterKey, keyLen, InfoBytes, InfoBytes.Length, sessionKey,
keyLen);
if (ret != 0) throw new System.Exception("failed to generate session key");
}
protected void IncrementNonce(bool isEncrypt)
{
lock (_nonceIncrementLock) {
Sodium.sodium_increment(isEncrypt ? _encNonce : _decNonce, nonceLen);
}
}
public virtual void InitCipher(byte[] salt, bool isEncrypt, bool isUdp)
{
if (isEncrypt) {
_encryptSalt = new byte[saltLen];
Array.Copy(salt, _encryptSalt, saltLen);
} else {
_decryptSalt = new byte[saltLen];
Array.Copy(salt, _decryptSalt, saltLen);
}
Logging.Dump("Salt", salt, saltLen);
}
public static void randBytes(byte[] buf, int length) { RNG.GetBytes(buf, length); }
public abstract int cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen);
public abstract int cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen);
#region TCP
public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength)
{
Debug.Assert(_encCircularBuffer != null, "_encCircularBuffer != null");
_encCircularBuffer.Put(buf, 0, length);
outlength = 0;
Logging.Debug("---Start Encryption");
if (! _encryptSaltSent) {
_encryptSaltSent = true;
// Generate salt
byte[] saltBytes = new byte[saltLen];
randBytes(saltBytes, saltLen);
InitCipher(saltBytes, true, false);
Array.Copy(saltBytes, 0, outbuf, 0, saltLen);
outlength = saltLen;
Logging.Debug($"_encryptSaltSent outlength {outlength}");
}
if (! _tcpRequestSent) {
_tcpRequestSent = true;
// The first TCP request
int encAddrBufLength;
byte[] encAddrBufBytes = new byte[AddrBufLength + tagLen * 2 + CHUNK_LEN_BYTES];
byte[] addrBytes = _encCircularBuffer.Get(AddrBufLength);
ChunkEncrypt(addrBytes, AddrBufLength, encAddrBufBytes, out encAddrBufLength);
Debug.Assert(encAddrBufLength == AddrBufLength + tagLen * 2 + CHUNK_LEN_BYTES);
Array.Copy(encAddrBufBytes, 0, outbuf, outlength, encAddrBufLength);
outlength += encAddrBufLength;
Logging.Debug($"_tcpRequestSent outlength {outlength}");
}
// handle other chunks
while (true) {
uint bufSize = (uint)_encCircularBuffer.Size;
if (bufSize <= 0) return;
var chunklength = (int)Math.Min(bufSize, CHUNK_LEN_MASK);
byte[] chunkBytes = _encCircularBuffer.Get(chunklength);
int encChunkLength;
byte[] encChunkBytes = new byte[chunklength + tagLen * 2 + CHUNK_LEN_BYTES];
ChunkEncrypt(chunkBytes, chunklength, encChunkBytes, out encChunkLength);
Debug.Assert(encChunkLength == chunklength + tagLen * 2 + CHUNK_LEN_BYTES);
Buffer.BlockCopy(encChunkBytes, 0, outbuf, outlength, encChunkLength);
outlength += encChunkLength;
Logging.Debug("chunks enc outlength " + outlength);
// check if we have enough space for outbuf
if (outlength + TCPHandler.ChunkOverheadSize > TCPHandler.BufferSize) {
Logging.Debug("enc outbuf almost full, giving up");
return;
}
bufSize = (uint)_encCircularBuffer.Size;
if (bufSize <= 0) {
Logging.Debug("No more data to encrypt, leaving");
return;
}
}
}
public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength)
{
Debug.Assert(_decCircularBuffer != null, "_decCircularBuffer != null");
int bufSize;
outlength = 0;
// drop all into buffer
_decCircularBuffer.Put(buf, 0, length);
Logging.Debug("---Start Decryption");
if (! _decryptSaltReceived) {
bufSize = _decCircularBuffer.Size;
// check if we get the leading salt
if (bufSize <= saltLen) {
// need more
return;
}
_decryptSaltReceived = true;
byte[] salt = _decCircularBuffer.Get(saltLen);
InitCipher(salt, false, false);
Logging.Debug("get salt len " + saltLen);
}
// handle chunks
while (true) {
bufSize = _decCircularBuffer.Size;
// check if we have any data
if (bufSize <= 0) {
Logging.Debug("No data in _decCircularBuffer");
return;
}
// first get chunk length
if (bufSize <= CHUNK_LEN_BYTES + tagLen) {
// so we only have chunk length and its tag?
return;
}
#region Chunk Decryption
byte[] encLenBytes = _decCircularBuffer.Peek(CHUNK_LEN_BYTES + tagLen);
uint decChunkLenLength = 0;
byte[] decChunkLenBytes = new byte[CHUNK_LEN_BYTES];
// try to dec chunk len
cipherDecrypt(encLenBytes, CHUNK_LEN_BYTES + (uint)tagLen, decChunkLenBytes, ref decChunkLenLength);
Debug.Assert(decChunkLenLength == CHUNK_LEN_BYTES);
// finally we get the real chunk len
ushort chunkLen = (ushort) IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(decChunkLenBytes, 0));
if (chunkLen > CHUNK_LEN_MASK)
{
// we get invalid chunk
Logging.Error($"Invalid chunk length: {chunkLen}");
throw new CryptoErrorException();
}
Logging.Debug("Get the real chunk len:" + chunkLen);
bufSize = _decCircularBuffer.Size;
if (bufSize < CHUNK_LEN_BYTES + tagLen /* we haven't remove them */+ chunkLen + tagLen) {
Logging.Debug("No more data to decrypt one chunk");
return;
}
IncrementNonce(false);
// we have enough data to decrypt one chunk
// drop chunk len and its tag from buffer
_decCircularBuffer.Get(CHUNK_LEN_BYTES + tagLen);
byte[] encChunkBytes = _decCircularBuffer.Get(chunkLen + tagLen);
byte[] decChunkBytes = new byte[chunkLen];
uint decChunkLen = 0;
cipherDecrypt(encChunkBytes, chunkLen + (uint)tagLen, decChunkBytes, ref decChunkLen);
Debug.Assert(decChunkLen == chunkLen);
IncrementNonce(false);
#endregion
// output to outbuf
Buffer.BlockCopy(decChunkBytes, 0, outbuf, outlength, (int) decChunkLen);
outlength += (int)decChunkLen;
Logging.Debug("aead dec outlength " + outlength);
if (outlength + 100 > TCPHandler.BufferSize)
{
Logging.Debug("dec outbuf almost full, giving up");
return;
}
bufSize = _decCircularBuffer.Size;
// check if we already done all of them
if (bufSize <= 0) {
Logging.Debug("No data in _decCircularBuffer, already all done");
return;
}
}
}
#endregion
#region UDP
public override void EncryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength)
{
// Generate salt
randBytes(outbuf, saltLen);
InitCipher(outbuf, true, true);
uint olen = 0;
lock (_udpTmpBuf) {
cipherEncrypt(buf, (uint) length, _udpTmpBuf, ref olen);
Debug.Assert(olen == length + tagLen);
Buffer.BlockCopy(_udpTmpBuf, 0, outbuf, saltLen, (int) olen);
outlength = (int) (saltLen + olen);
}
}
public override void DecryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength)
{
InitCipher(buf, false, true);
uint olen = 0;
lock (_udpTmpBuf) {
// copy remaining data to first pos
Buffer.BlockCopy(buf, saltLen, buf, 0, length - saltLen);
cipherDecrypt(buf, (uint) (length - saltLen), _udpTmpBuf, ref olen);
Buffer.BlockCopy(_udpTmpBuf, 0, outbuf, 0, (int) olen);
outlength = (int) olen;
}
}
#endregion
// we know the plaintext length before encryption, so we can do it in one operation
private void ChunkEncrypt(byte[] plaintext, int plainLen, byte[] ciphertext, out int cipherLen)
{
if (plainLen > CHUNK_LEN_MASK) {
Logging.Error("enc chunk too big");
throw new CryptoErrorException();
}
// encrypt len
byte[] encLenBytes = new byte[CHUNK_LEN_BYTES + tagLen];
uint encChunkLenLength = 0;
byte[] lenbuf = BitConverter.GetBytes((ushort) IPAddress.HostToNetworkOrder((short)plainLen));
cipherEncrypt(lenbuf, CHUNK_LEN_BYTES, encLenBytes, ref encChunkLenLength);
Debug.Assert(encChunkLenLength == CHUNK_LEN_BYTES + tagLen);
IncrementNonce(true);
// encrypt corresponding data
byte[] encBytes = new byte[plainLen + tagLen];
uint encBufLength = 0;
cipherEncrypt(plaintext, (uint) plainLen, encBytes, ref encBufLength);
Debug.Assert(encBufLength == plainLen + tagLen);
IncrementNonce(true);
// construct outbuf
Array.Copy(encLenBytes, 0, ciphertext, 0, (int) encChunkLenLength);
Buffer.BlockCopy(encBytes, 0, ciphertext, (int) encChunkLenLength, (int) encBufLength);
cipherLen = (int) (encChunkLenLength + encBufLength);
}
}
}

+ 176
- 0
shadowsocks-csharp/Encryption/AEAD/AEADMbedTLSEncryptor.cs View File

@@ -0,0 +1,176 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Shadowsocks.Encryption.Exception;
namespace Shadowsocks.Encryption.AEAD
{
public class AEADMbedTLSEncryptor
: AEADEncryptor, IDisposable
{
const int CIPHER_AES = 1;
private IntPtr _encryptCtx = IntPtr.Zero;
private IntPtr _decryptCtx = IntPtr.Zero;
public AEADMbedTLSEncryptor(string method, string password)
: base(method, password)
{
}
private static Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo>
{
{"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)},
};
public static List<string> SupportedCiphers()
{
return new List<string>(_ciphers.Keys);
}
protected override Dictionary<string, EncryptorInfo> getCiphers()
{
return _ciphers;
}
public override void InitCipher(byte[] salt, bool isEncrypt, bool isUdp)
{
base.InitCipher(salt, isEncrypt, isUdp);
IntPtr ctx = Marshal.AllocHGlobal(MbedTLS.cipher_get_size_ex());
if (isEncrypt)
{
_encryptCtx = ctx;
}
else
{
_decryptCtx = ctx;
}
MbedTLS.cipher_init(ctx);
if (MbedTLS.cipher_setup(ctx, MbedTLS.cipher_info_from_string(_innerLibName)) != 0)
throw new System.Exception("Cannot initialize mbed TLS cipher context");
DeriveSessionKey(isEncrypt ? _encryptSalt : _decryptSalt,
_Masterkey, _sessionKey);
CipherSetKey(isEncrypt, _sessionKey);
}
private void CipherSetKey(bool isEncrypt, byte[] key)
{
IntPtr ctx = isEncrypt ? _encryptCtx : _decryptCtx;
int ret = MbedTLS.cipher_setkey(ctx, key, keyLen * 8,
isEncrypt ? MbedTLS.MBEDTLS_ENCRYPT : MbedTLS.MBEDTLS_DECRYPT);
if (ret != 0) throw new System.Exception("failed to set key");
ret = MbedTLS.cipher_reset(ctx);
if (ret != 0) throw new System.Exception("failed to finish preparation");
}
public override int cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen)
{
// buf: all plaintext
// outbuf: ciphertext + tag
int ret;
byte[] tagbuf = new byte[tagLen];
uint olen = 0;
switch (_cipher)
{
case CIPHER_AES:
ret = MbedTLS.cipher_auth_encrypt(_encryptCtx,
/* nonce */
_encNonce, (uint) nonceLen,
/* AD */
IntPtr.Zero, 0,
/* plain */
plaintext, plen,
/* cipher */
ciphertext, ref olen,
tagbuf, (uint) tagLen);
if (ret != 0) throw new CryptoErrorException();
Debug.Assert(olen == plen);
// attach tag to ciphertext
Array.Copy(tagbuf, 0, ciphertext, (int) plen, tagLen);
clen = olen + (uint) tagLen;
return ret;
default:
throw new System.Exception("not implemented");
}
}
public override int cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen)
{
// buf: ciphertext + tag
// outbuf: plaintext
int ret;
uint olen = 0;
// split tag
byte[] tagbuf = new byte[tagLen];
Array.Copy(ciphertext, (int) (clen - tagLen), tagbuf, 0, tagLen);
switch (_cipher)
{
case CIPHER_AES:
ret = MbedTLS.cipher_auth_decrypt(_decryptCtx,
_decNonce, (uint) nonceLen,
IntPtr.Zero, 0,
ciphertext, (uint) (clen - tagLen),
plaintext, ref olen,
tagbuf, (uint) tagLen);
if (ret != 0) throw new CryptoErrorException();
Debug.Assert(olen == clen - tagLen);
plen = olen;
return ret;
default:
throw new System.Exception("not implemented");
}
}
#region IDisposable
private bool _disposed;
// instance based lock
private readonly object _lock = new object();
public override void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~AEADMbedTLSEncryptor()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
lock (_lock)
{
if (_disposed) return;
_disposed = true;
}
if (disposing)
{
// free managed objects
}
// free unmanaged objects
if (_encryptCtx != IntPtr.Zero)
{
MbedTLS.cipher_free(_encryptCtx);
Marshal.FreeHGlobal(_encryptCtx);
_encryptCtx = IntPtr.Zero;
}
if (_decryptCtx != IntPtr.Zero)
{
MbedTLS.cipher_free(_decryptCtx);
Marshal.FreeHGlobal(_decryptCtx);
_decryptCtx = IntPtr.Zero;
}
}
#endregion
}
}

+ 108
- 0
shadowsocks-csharp/Encryption/AEAD/AEADSodiumEncryptor.cs View File

@@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Shadowsocks.Controller;
using Shadowsocks.Encryption.Exception;
namespace Shadowsocks.Encryption.AEAD
{
public class AEADSodiumEncryptor
: AEADEncryptor, IDisposable
{
private const int CIPHER_CHACHA20IETFPOLY1305 = 1;
private byte[] _sodiumEncSubkey;
private byte[] _sodiumDecSubkey;
public AEADSodiumEncryptor(string method, string password)
: base(method, password)
{
_sodiumEncSubkey = new byte[keyLen];
_sodiumDecSubkey = new byte[keyLen];
}
private static Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo>
{
{"chacha20-ietf-poly1305", new EncryptorInfo(32, 32, 12, 16, CIPHER_CHACHA20IETFPOLY1305)},
};
public static List<string> SupportedCiphers()
{
return new List<string>(_ciphers.Keys);
}
protected override Dictionary<string, EncryptorInfo> getCiphers()
{
return _ciphers;
}
public override void InitCipher(byte[] salt, bool isEncrypt, bool isUdp)
{
base.InitCipher(salt, isEncrypt, isUdp);
DeriveSessionKey(isEncrypt ? _encryptSalt : _decryptSalt, _Masterkey,
isEncrypt ? _sodiumEncSubkey : _sodiumDecSubkey);
}
public override int cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen)
{
Debug.Assert(_sodiumEncSubkey != null);
// buf: all plaintext
// outbuf: ciphertext + tag
int ret;
ulong encClen = 0;
Logging.Dump("_encNonce before enc", _encNonce, nonceLen);
Logging.Dump("_sodiumEncSubkey", _sodiumEncSubkey, keyLen);
Logging.Dump("before cipherEncrypt: plain", plaintext, (int) plen);
switch (_cipher)
{
case CIPHER_CHACHA20IETFPOLY1305:
ret = Sodium.crypto_aead_chacha20poly1305_ietf_encrypt(ciphertext, ref encClen,
plaintext, (ulong) plen,
null, 0,
null, _encNonce,
_sodiumEncSubkey);
break;
default:
throw new System.Exception("not implemented");
}
if (ret != 0) throw new CryptoErrorException();
Logging.Dump("after cipherEncrypt: cipher", ciphertext, (int) encClen);
clen = (uint) encClen;
return ret;
}
public override int cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen)
{
Debug.Assert(_sodiumDecSubkey != null);
// buf: ciphertext + tag
// outbuf: plaintext
int ret;
ulong decPlen = 0;
Logging.Dump("_decNonce before dec", _decNonce, nonceLen);
Logging.Dump("_sodiumDecSubkey", _sodiumDecSubkey, keyLen);
Logging.Dump("before cipherDecrypt: cipher", ciphertext, (int) clen);
switch (_cipher)
{
case CIPHER_CHACHA20IETFPOLY1305:
ret = Sodium.crypto_aead_chacha20poly1305_ietf_decrypt(plaintext, ref decPlen,
null,
ciphertext, (ulong) clen,
null, 0,
_decNonce, _sodiumDecSubkey);
break;
default:
throw new System.Exception("not implemented");
}
if (ret != 0) throw new CryptoErrorException();
Logging.Dump("after cipherDecrypt: plain", plaintext, (int) decPlen);
plen = (uint) decPlen;
return ret;
}
public override void Dispose()
{
}
}
}

+ 50
- 17
shadowsocks-csharp/Encryption/EncryptorBase.cs View File

@@ -1,16 +1,20 @@
using System.Text;
namespace Shadowsocks.Encryption
namespace Shadowsocks.Encryption
{
public struct EncryptorInfo
public class EncryptorInfo
{
public int KeySize;
public int IvSize;
public int SaltSize;
public int TagSize;
public int NonceSize;
public int Type;
public string InnerLibName;
// For those who make use of internal crypto method name
// e.g. mbed TLS
#region Stream ciphers
public EncryptorInfo(string innerLibName, int keySize, int ivSize, int type)
{
this.KeySize = keySize;
@@ -26,6 +30,32 @@ namespace Shadowsocks.Encryption
this.Type = type;
this.InnerLibName = string.Empty;
}
#endregion
#region AEAD ciphers
public EncryptorInfo(string innerLibName, int keySize, int saltSize, int nonceSize, int tagSize, int type)
{
this.KeySize = keySize;
this.SaltSize = saltSize;
this.NonceSize = nonceSize;
this.TagSize = tagSize;
this.Type = type;
this.InnerLibName = innerLibName;
}
public EncryptorInfo(int keySize, int saltSize, int nonceSize, int tagSize, int type)
{
this.KeySize = keySize;
this.SaltSize = saltSize;
this.NonceSize = nonceSize;
this.TagSize = tagSize;
this.Type = type;
this.InnerLibName = string.Empty;
}
#endregion
}
public abstract class EncryptorBase
@@ -33,30 +63,33 @@ namespace Shadowsocks.Encryption
{
public const int MAX_INPUT_SIZE = 32768;
protected EncryptorBase(string method, string password, bool onetimeauth, bool isudp)
public const int MAX_DOMAIN_LEN = 255;
public const int ADDR_PORT_LEN = 2;
public const int ADDR_ATYP_LEN = 1;
public const int ATYP_IPv4 = 0x01;
public const int ATYP_DOMAIN = 0x03;
public const int ATYP_IPv6 = 0x04;
protected EncryptorBase(string method, string password)
{
Method = method;
Password = password;
OnetimeAuth = onetimeauth;
IsUDP = isudp;
}
protected string Method;
protected string Password;
protected bool OnetimeAuth;
protected bool IsUDP;
protected byte[] GetPasswordHash()
{
byte[] inputBytes = Encoding.UTF8.GetBytes(Password);
byte[] hash = MbedTLS.MD5(inputBytes);
return hash;
}
public abstract void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength);
public abstract void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength);
public abstract void EncryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength);
public abstract void DecryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength);
public abstract void Dispose();
public int AddrBufLength { get; set; } = - 1;
}
}
}

+ 21
- 11
shadowsocks-csharp/Encryption/EncryptorFactory.cs View File

@@ -1,29 +1,38 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Shadowsocks.Encryption.AEAD;
using Shadowsocks.Encryption.Stream;
namespace Shadowsocks.Encryption
{
public static class EncryptorFactory
{
private static Dictionary<string, Type> _registeredEncryptors;
private static Dictionary<string, Type> _registeredEncryptors = new Dictionary<string, Type>();
private static Type[] _constructorTypes = new Type[] { typeof(string), typeof(string), typeof(bool), typeof(bool) };
private static readonly Type[] ConstructorTypes = {typeof(string), typeof(string)};
static EncryptorFactory()
{
_registeredEncryptors = new Dictionary<string, Type>();
foreach (string method in MbedTLSEncryptor.SupportedCiphers())
foreach (string method in StreamMbedTLSEncryptor.SupportedCiphers())
{
_registeredEncryptors.Add(method, typeof(MbedTLSEncryptor));
_registeredEncryptors.Add(method, typeof(StreamMbedTLSEncryptor));
}
foreach (string method in SodiumEncryptor.SupportedCiphers())
foreach (string method in StreamSodiumEncryptor.SupportedCiphers())
{
_registeredEncryptors.Add(method, typeof(SodiumEncryptor));
_registeredEncryptors.Add(method, typeof(StreamSodiumEncryptor));
}
foreach (string method in AEADMbedTLSEncryptor.SupportedCiphers())
{
_registeredEncryptors.Add(method, typeof(AEADMbedTLSEncryptor));
}
foreach (string method in AEADSodiumEncryptor.SupportedCiphers())
{
_registeredEncryptors.Add(method, typeof(AEADSodiumEncryptor));
}
}
public static IEncryptor GetEncryptor(string method, string password, bool onetimeauth, bool isudp)
public static IEncryptor GetEncryptor(string method, string password)
{
if (method.IsNullOrEmpty())
{
@@ -31,9 +40,10 @@ namespace Shadowsocks.Encryption
}
method = method.ToLowerInvariant();
Type t = _registeredEncryptors[method];
ConstructorInfo c = t.GetConstructor(_constructorTypes);
IEncryptor result = (IEncryptor)c.Invoke(new object[] { method, password, onetimeauth, isudp });
ConstructorInfo c = t.GetConstructor(ConstructorTypes);
if (c == null) throw new System.Exception("Invalid ctor");
IEncryptor result = (IEncryptor) c.Invoke(new object[] {method, password});
return result;
}
}
}
}

+ 17
- 0
shadowsocks-csharp/Encryption/Exception/CryptoException.cs View File

@@ -0,0 +1,17 @@
namespace Shadowsocks.Encryption.Exception
{
public class CryptoErrorException : System.Exception
{
public CryptoErrorException()
{
}
public CryptoErrorException(string msg) : base(msg)
{
}
public CryptoErrorException(string message, System.Exception innerException) : base(message, innerException)
{
}
}
}

+ 4
- 0
shadowsocks-csharp/Encryption/IEncryptor.cs View File

@@ -4,7 +4,11 @@ namespace Shadowsocks.Encryption
{
public interface IEncryptor : IDisposable
{
/* length == -1 means not used */
int AddrBufLength { set; get; }
void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength);
void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength);
void EncryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength);
void DecryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength);
}
}

+ 0
- 288
shadowsocks-csharp/Encryption/IVEncryptor.cs View File

@@ -1,288 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Text;
using System.Net;
namespace Shadowsocks.Encryption
{
public abstract class IVEncryptor
: EncryptorBase
{
public const int MAX_KEY_LENGTH = 64;
public const int MAX_IV_LENGTH = 16;
protected static byte[] tempbuf = new byte[MAX_INPUT_SIZE];
protected Dictionary<string, EncryptorInfo> ciphers;
private static readonly ConcurrentDictionary<string, byte[]> CachedKeys = new ConcurrentDictionary<string, byte[]>();
protected byte[] _encryptIV;
protected byte[] _decryptIV;
protected bool _decryptIVReceived;
protected bool _encryptIVSent;
protected string _method;
protected int _cipher;
// internal name in the crypto library
protected string _innerLibName;
protected EncryptorInfo CipherInfo;
protected byte[] _key;
protected int keyLen;
protected int ivLen;
public IVEncryptor(string method, string password, bool onetimeauth, bool isudp)
: base(method, password, onetimeauth, isudp)
{
InitKey(method, password);
}
protected abstract Dictionary<string, EncryptorInfo> getCiphers();
private void InitKey(string method, string password)
{
method = method.ToLower();
_method = method;
string k = method + ":" + password;
ciphers = getCiphers();
CipherInfo = ciphers[_method];
_innerLibName = CipherInfo.InnerLibName;
_cipher = CipherInfo.Type;
if (_cipher == 0)
{
throw new Exception("method not found");
}
keyLen = CipherInfo.KeySize;
ivLen = CipherInfo.IvSize;
_key = CachedKeys.GetOrAdd(k, (nk) =>
{
byte[] passbuf = Encoding.UTF8.GetBytes(password);
byte[] key = new byte[32];
bytesToKey(passbuf, key);
return key;
});
}
protected void bytesToKey(byte[] password, byte[] key)
{
byte[] result = new byte[password.Length + 16];
int i = 0;
byte[] md5sum = null;
while (i < key.Length)
{
if (i == 0)
{
md5sum = MbedTLS.MD5(password);
}
else
{
md5sum.CopyTo(result, 0);
password.CopyTo(result, md5sum.Length);
md5sum = MbedTLS.MD5(result);
}
md5sum.CopyTo(key, i);
i += md5sum.Length;
}
}
protected virtual void initCipher(byte[] iv, bool isCipher)
{
if (ivLen > 0)
{
if (isCipher)
{
_encryptIV = new byte[ivLen];
Array.Copy(iv, _encryptIV, ivLen);
}
else
{
_decryptIV = new byte[ivLen];
Array.Copy(iv, _decryptIV, ivLen);
}
}
}
protected abstract void cipherUpdate(bool isCipher, int length, byte[] buf, byte[] outbuf);
#region OneTimeAuth
public const int ONETIMEAUTH_FLAG = 0x10;
public const int ADDRTYPE_MASK = 0xEF;
public const int ONETIMEAUTH_BYTES = 10;
public const int CLEN_BYTES = 2;
public const int AUTH_BYTES = ONETIMEAUTH_BYTES + CLEN_BYTES;
private uint _otaChunkCounter;
private byte[] _otaChunkKeyBuffer;
protected int OtaGetHeadLen(byte[] buf, int length)
{
int len = 0;
int atyp = length > 0 ? (buf[0] & ADDRTYPE_MASK) : 0;
if (atyp == 1)
{
len = 7; // atyp (1 bytes) + ipv4 (4 bytes) + port (2 bytes)
}
else if (atyp == 3 && length > 1)
{
int nameLen = buf[1];
len = 4 + nameLen; // atyp (1 bytes) + name length (1 bytes) + name (n bytes) + port (2 bytes)
}
else if (atyp == 4)
{
len = 19; // atyp (1 bytes) + ipv6 (16 bytes) + port (2 bytes)
}
if (len == 0 || len > length)
throw new Exception($"invalid header with addr type {atyp}");
return len;
}
private byte[] OtaGenHash(byte[] msg, int msg_len)
{
byte[] auth = new byte[ONETIMEAUTH_BYTES];
byte[] hash = new byte[20];
byte[] auth_key = new byte[MAX_IV_LENGTH + MAX_KEY_LENGTH];
Buffer.BlockCopy(_encryptIV, 0, auth_key, 0, ivLen);
Buffer.BlockCopy(_key, 0, auth_key, ivLen, keyLen);
Sodium.ss_sha1_hmac_ex(auth_key, (uint)(ivLen + keyLen),
msg, 0, (uint)msg_len, hash);
Buffer.BlockCopy(hash, 0, auth, 0, ONETIMEAUTH_BYTES);
return auth;
}
private void OtaUpdateKeyBuffer()
{
if (_otaChunkKeyBuffer == null)
{
_otaChunkKeyBuffer = new byte[MAX_IV_LENGTH + 4];
Buffer.BlockCopy(_encryptIV, 0, _otaChunkKeyBuffer, 0, ivLen);
}
byte[] counter_bytes = BitConverter.GetBytes((uint)IPAddress.HostToNetworkOrder((int)_otaChunkCounter));
Buffer.BlockCopy(counter_bytes, 0, _otaChunkKeyBuffer, ivLen, 4);
_otaChunkCounter++;
}
private byte[] OtaGenChunkHash(byte[] buf, int offset, int len)
{
byte[] hash = new byte[20];
OtaUpdateKeyBuffer();
Sodium.ss_sha1_hmac_ex(_otaChunkKeyBuffer, (uint)_otaChunkKeyBuffer.Length,
buf, offset, (uint)len, hash);
return hash;
}
private void OtaAuthBuffer4Tcp(byte[] buf, ref int length)
{
if (!_encryptIVSent)
{
int headLen = OtaGetHeadLen(buf, length);
int dataLen = length - headLen;
buf[0] |= ONETIMEAUTH_FLAG;
byte[] hash = OtaGenHash(buf, headLen);
Buffer.BlockCopy(buf, headLen, buf, headLen + ONETIMEAUTH_BYTES + AUTH_BYTES, dataLen);
Buffer.BlockCopy(hash, 0, buf, headLen, ONETIMEAUTH_BYTES);
if (dataLen == 0)
{
length = headLen + ONETIMEAUTH_BYTES;
}
else
{
hash = OtaGenChunkHash(buf, headLen + ONETIMEAUTH_BYTES + AUTH_BYTES, dataLen);
Buffer.BlockCopy(hash, 0, buf, headLen + ONETIMEAUTH_BYTES + CLEN_BYTES, ONETIMEAUTH_BYTES);
byte[] lenBytes = BitConverter.GetBytes((ushort) IPAddress.HostToNetworkOrder((short) dataLen));
Buffer.BlockCopy(lenBytes, 0, buf, headLen + ONETIMEAUTH_BYTES, CLEN_BYTES);
length = headLen + ONETIMEAUTH_BYTES + AUTH_BYTES + dataLen;
}
}
else
{
byte[] hash = OtaGenChunkHash(buf, 0, length);
Buffer.BlockCopy(buf, 0, buf, AUTH_BYTES, length);
byte[] lenBytes = BitConverter.GetBytes((ushort)IPAddress.HostToNetworkOrder((short)length));
Buffer.BlockCopy(lenBytes, 0, buf, 0, CLEN_BYTES);
Buffer.BlockCopy(hash, 0, buf, CLEN_BYTES, ONETIMEAUTH_BYTES);
length += AUTH_BYTES;
}
}
private void OtaAuthBuffer4Udp(byte[] buf, ref int length)
{
buf[0] |= ONETIMEAUTH_FLAG;
byte[] hash = OtaGenHash(buf, length);
Buffer.BlockCopy(hash, 0, buf, length, ONETIMEAUTH_BYTES);
length += ONETIMEAUTH_BYTES;
}
private void OtaAuthBuffer(byte[] buf, ref int length)
{
if (OnetimeAuth && ivLen > 0)
{
if (!IsUDP)
{
OtaAuthBuffer4Tcp(buf, ref length);
}
else
{
OtaAuthBuffer4Udp(buf, ref length);
}
}
}
#endregion
protected static void randBytes(byte[] buf, int length)
{
RNG.GetBytes(buf, length);
}
public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength)
{
if (!_encryptIVSent)
{
// Generate IV
randBytes(outbuf, ivLen);
initCipher(outbuf, true);
outlength = length + ivLen;
OtaAuthBuffer(buf, ref length);
_encryptIVSent = true;
lock (tempbuf)
{
cipherUpdate(true, length, buf, tempbuf);
outlength = length + ivLen;
Buffer.BlockCopy(tempbuf, 0, outbuf, ivLen, length);
}
}
else
{
OtaAuthBuffer(buf, ref length);
outlength = length;
cipherUpdate(true, length, buf, outbuf);
}
}
public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength)
{
if (!_decryptIVReceived)
{
_decryptIVReceived = true;
initCipher(buf, false);
outlength = length - ivLen;
lock (tempbuf)
{
// C# could be multi-threaded
Buffer.BlockCopy(buf, ivLen, tempbuf, 0, length - ivLen);
cipherUpdate(false, length - ivLen, tempbuf, outbuf);
}
}
else
{
outlength = length;
cipherUpdate(false, length, buf, outbuf);
}
}
}
}

+ 105
- 76
shadowsocks-csharp/Encryption/MbedTLS.cs View File

@@ -1,76 +1,105 @@
using System;
using System.IO;
using System.Runtime.InteropServices;

using Shadowsocks.Controller;
using Shadowsocks.Properties;
using Shadowsocks.Util;

namespace Shadowsocks.Encryption
{
public class MbedTLS
{
const string DLLNAME = "libsscrypto";

public const int MBEDTLS_ENCRYPT = 1;
public const int MBEDTLS_DECRYPT = 0;

static MbedTLS()
{
string dllPath = Utils.GetTempPath("libsscrypto.dll");
try
{
FileManager.UncompressFile(dllPath, Resources.libsscrypto_dll);
}
catch (IOException)
{
}
catch (Exception e)
{
Logging.LogUsefulException(e);
}
LoadLibrary(dllPath);
}

public static byte[] MD5(byte[] input)
{
byte[] output = new byte[16];
md5(input, (uint)input.Length, output);
return output;
}

[DllImport("Kernel32.dll")]
private static extern IntPtr LoadLibrary(string path);

[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr cipher_info_from_string(string cipher_name);

[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern void cipher_init(IntPtr ctx);

[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int cipher_setup(IntPtr ctx, IntPtr cipher_info);

// XXX: Check operation before using it
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int cipher_setkey(IntPtr ctx, byte[] key, int key_bitlen, int operation);

[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int cipher_set_iv(IntPtr ctx, byte[] iv, int iv_len);

[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int cipher_reset(IntPtr ctx);

[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int cipher_update(IntPtr ctx, byte[] input, int ilen, byte[] output, ref int olen);

[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern void cipher_free(IntPtr ctx);

[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern void md5(byte[] input, uint ilen, byte[] output);

[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int cipher_get_size_ex();
}
}
using System;
using System.IO;
using System.Runtime.InteropServices;
using Shadowsocks.Controller;
using Shadowsocks.Properties;
using Shadowsocks.Util;
namespace Shadowsocks.Encryption
{
public static class MbedTLS
{
private const string DLLNAME = "libsscrypto.dll";
public const int MBEDTLS_ENCRYPT = 1;
public const int MBEDTLS_DECRYPT = 0;
static MbedTLS()
{
string dllPath = Utils.GetTempPath(DLLNAME);
try
{
FileManager.UncompressFile(dllPath, Resources.libsscrypto_dll);
}
catch (IOException)
{
}
catch (System.Exception e)
{
Logging.LogUsefulException(e);
}
LoadLibrary(dllPath);
}
public static byte[] MD5(byte[] input)
{
byte[] output = new byte[16];
md5(input, (uint) input.Length, output);
return output;
}
[DllImport("Kernel32.dll")]
private static extern IntPtr LoadLibrary(string path);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern void md5(byte[] input, uint ilen, byte[] output);
/// <summary>
/// Get cipher ctx size for unmanaged memory allocation
/// </summary>
/// <returns></returns>
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int cipher_get_size_ex();
#region Cipher layer wrappers
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr cipher_info_from_string(string cipher_name);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern void cipher_init(IntPtr ctx);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int cipher_setup(IntPtr ctx, IntPtr cipher_info);
// XXX: Check operation before using it
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int cipher_setkey(IntPtr ctx, byte[] key, int key_bitlen, int operation);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int cipher_set_iv(IntPtr ctx, byte[] iv, int iv_len);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int cipher_reset(IntPtr ctx);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int cipher_update(IntPtr ctx, byte[] input, int ilen, byte[] output, ref int olen);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern void cipher_free(IntPtr ctx);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int cipher_auth_encrypt(IntPtr ctx,
byte[] iv, uint iv_len,
IntPtr ad, uint ad_len,
byte[] input, uint ilen,
byte[] output, ref uint olen,
byte[] tag, uint tag_len);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int cipher_auth_decrypt(IntPtr ctx,
byte[] iv, uint iv_len,
IntPtr ad, uint ad_len,
byte[] input, uint ilen,
byte[] output, ref uint olen,
byte[] tag, uint tag_len);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int hkdf(byte[] salt,
int salt_len, byte[] ikm, int ikm_len,
byte[] info, int info_len, byte[] okm,
int okm_len);
#endregion
}
}

+ 3
- 2
shadowsocks-csharp/Encryption/RNG.cs View File

@@ -28,16 +28,17 @@ namespace Shadowsocks.Encryption
public static void GetBytes(byte[] buf)
{
_rng.GetBytes(buf);
GetBytes(buf, buf.Length);
}
public static void GetBytes(byte[] buf, int len)
{
if (_rng == null) Reload();
try
{
_rng.GetBytes(buf, 0, len);
}
catch (Exception)
catch (System.Exception)
{
// the backup way
byte[] tmp = new byte[len];


+ 49
- 13
shadowsocks-csharp/Encryption/Sodium.cs View File

@@ -1,20 +1,22 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using Shadowsocks.Controller;
using Shadowsocks.Properties;
using Shadowsocks.Util;
namespace Shadowsocks.Encryption
{
public class Sodium
public static class Sodium
{
const string DLLNAME = "libsscrypto";
private const string DLLNAME = "libsscrypto.dll";
private static bool _initialized = false;
private static readonly object _initLock = new object();
static Sodium()
{
string dllPath = Utils.GetTempPath("libsscrypto.dll");
string dllPath = Utils.GetTempPath(DLLNAME);
try
{
FileManager.UncompressFile(dllPath, Resources.libsscrypto_dll);
@@ -22,29 +24,63 @@ namespace Shadowsocks.Encryption
catch (IOException)
{
}
catch (Exception e)
catch (System.Exception e)
{
Logging.LogUsefulException(e);
}
LoadLibrary(dllPath);
lock (_initLock)
{
if (!_initialized)
{
if (sodium_init() == -1)
{
throw new System.Exception("Failed to initialize sodium");
}
else /* 1 means already initialized; 0 means success */
{
_initialized = true;
}
}
}
}
[DllImport("Kernel32.dll")]
private static extern IntPtr LoadLibrary(string path);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int crypto_stream_salsa20_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, ulong ic, byte[] k);
private static extern int sodium_init();
#region AEAD
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int crypto_stream_chacha20_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, ulong ic, byte[] k);
public static extern int sodium_increment(byte[] n, int nlen);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int crypto_stream_chacha20_ietf_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, uint ic, byte[] k);
public static extern int crypto_aead_chacha20poly1305_ietf_encrypt(byte[] c, ref ulong clen_p, byte[] m,
ulong mlen, byte[] ad, ulong adlen, byte[] nsec, byte[] npub, byte[] k);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern void ss_sha1_hmac_ex(byte[] key, uint keylen,
byte[] input, int ioff, uint ilen,
byte[] output);
}
}
public static extern int crypto_aead_chacha20poly1305_ietf_decrypt(byte[] m, ref ulong mlen_p,
byte[] nsec, byte[] c, ulong clen, byte[] ad, ulong adlen, byte[] npub, byte[] k);
#endregion
#region Stream
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int crypto_stream_salsa20_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, ulong ic,
byte[] k);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int crypto_stream_chacha20_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, ulong ic,
byte[] k);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public static extern int crypto_stream_chacha20_ietf_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, uint ic,
byte[] k);
#endregion
}
}

+ 181
- 0
shadowsocks-csharp/Encryption/Stream/StreamEncryptor.cs View File

@@ -0,0 +1,181 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Cyotek.Collections.Generic;
using Shadowsocks.Controller;
namespace Shadowsocks.Encryption.Stream
{
public abstract class StreamEncryptor
: EncryptorBase
{
// for UDP only
protected static byte[] _udpTmpBuf = new byte[MAX_INPUT_SIZE];
// every connection should create its own buffer
private CircularBuffer<byte> _encCircularBuffer = new CircularBuffer<byte>(TCPHandler.BufferSize * 2, false);
private CircularBuffer<byte> _decCircularBuffer = new CircularBuffer<byte>(TCPHandler.BufferSize * 2, false);
protected Dictionary<string, EncryptorInfo> ciphers;
protected byte[] _encryptIV;
protected byte[] _decryptIV;
// Is first packet
protected bool _decryptIVReceived;
protected bool _encryptIVSent;
protected string _method;
protected int _cipher;
// internal name in the crypto library
protected string _innerLibName;
protected EncryptorInfo CipherInfo;
// long-time master key
protected static byte[] _key = null;
protected int keyLen;
protected int ivLen;
public StreamEncryptor(string method, string password)
: base(method, password)
{
InitEncryptorInfo(method);
InitKey(password);
}
protected abstract Dictionary<string, EncryptorInfo> getCiphers();
private void InitEncryptorInfo(string method)
{
method = method.ToLower();
_method = method;
ciphers = getCiphers();
CipherInfo = ciphers[_method];
_innerLibName = CipherInfo.InnerLibName;
_cipher = CipherInfo.Type;
if (_cipher == 0) {
throw new System.Exception("method not found");
}
keyLen = CipherInfo.KeySize;
ivLen = CipherInfo.IvSize;
}
private void InitKey(string password)
{
byte[] passbuf = Encoding.UTF8.GetBytes(password);
if (_key == null) _key = new byte[keyLen];
if (_key.Length < keyLen) Array.Resize(ref _key, keyLen);
LegacyDeriveKey(passbuf, _key);
}
public static void LegacyDeriveKey(byte[] password, byte[] key)
{
byte[] result = new byte[password.Length + 16];
int i = 0;
byte[] md5sum = null;
while (i < key.Length) {
if (i == 0) {
md5sum = MbedTLS.MD5(password);
} else {
md5sum.CopyTo(result, 0);
password.CopyTo(result, md5sum.Length);
md5sum = MbedTLS.MD5(result);
}
md5sum.CopyTo(key, i);
i += md5sum.Length;
}
}
protected virtual void initCipher(byte[] iv, bool isEncrypt)
{
if (isEncrypt) {
_encryptIV = new byte[ivLen];
Array.Copy(iv, _encryptIV, ivLen);
} else {
_decryptIV = new byte[ivLen];
Array.Copy(iv, _decryptIV, ivLen);
}
}
protected abstract void cipherUpdate(bool isEncrypt, int length, byte[] buf, byte[] outbuf);
protected static void randBytes(byte[] buf, int length) { RNG.GetBytes(buf, length); }
#region TCP
public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength)
{
int cipherOffset = 0;
Debug.Assert(_encCircularBuffer != null, "_encCircularBuffer != null");
_encCircularBuffer.Put(buf, 0, length);
if (! _encryptIVSent) {
// Generate IV
byte[] ivBytes = new byte[ivLen];
randBytes(ivBytes, ivLen);
initCipher(ivBytes, true);
Array.Copy(ivBytes, 0, outbuf, 0, ivLen);
cipherOffset = ivLen;
_encryptIVSent = true;
}
int size = _encCircularBuffer.Size;
byte[] plain = _encCircularBuffer.Get(size);
byte[] cipher = new byte[size];
cipherUpdate(true, size, plain, cipher);
Buffer.BlockCopy(cipher, 0, outbuf, cipherOffset, size);
outlength = size + cipherOffset;
}
public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength)
{
Debug.Assert(_decCircularBuffer != null, "_circularBuffer != null");
_decCircularBuffer.Put(buf, 0, length);
if (! _decryptIVReceived) {
if (_decCircularBuffer.Size <= ivLen) {
// we need more data
outlength = 0;
return;
}
// start decryption
_decryptIVReceived = true;
byte[] iv = _decCircularBuffer.Get(ivLen);
initCipher(iv, false);
}
byte[] cipher = _decCircularBuffer.ToArray();
cipherUpdate(false, cipher.Length, cipher, outbuf);
_decCircularBuffer.Clear();
outlength = cipher.Length;
// done the decryption
}
#endregion
#region UDP
public override void EncryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength)
{
// Generate IV
randBytes(outbuf, ivLen);
initCipher(outbuf, true);
lock (_udpTmpBuf) {
cipherUpdate(true, length, buf, _udpTmpBuf);
outlength = length + ivLen;
Buffer.BlockCopy(_udpTmpBuf, 0, outbuf, ivLen, length);
}
}
public override void DecryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength)
{
// Get IV from first pos
initCipher(buf, false);
outlength = length - ivLen;
lock (_udpTmpBuf) {
// C# could be multi-threaded
Buffer.BlockCopy(buf, ivLen, _udpTmpBuf, 0, length - ivLen);
cipherUpdate(false, length - ivLen, _udpTmpBuf, outbuf);
}
}
#endregion
}
}

shadowsocks-csharp/Encryption/MbedTLSEncryptor.cs → shadowsocks-csharp/Encryption/Stream/StreamMbedTLSEncryptor.cs View File

@@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Shadowsocks.Encryption.Exception;
namespace Shadowsocks.Encryption
namespace Shadowsocks.Encryption.Stream
{
public class MbedTLSEncryptor
: IVEncryptor, IDisposable
public class StreamMbedTLSEncryptor
: StreamEncryptor, IDisposable
{
const int CIPHER_RC4 = 1;
const int CIPHER_AES = 2;
@@ -15,8 +16,8 @@ namespace Shadowsocks.Encryption
private IntPtr _encryptCtx = IntPtr.Zero;
private IntPtr _decryptCtx = IntPtr.Zero;
public MbedTLSEncryptor(string method, string password, bool onetimeauth, bool isudp)
: base(method, password, onetimeauth, isudp)
public StreamMbedTLSEncryptor(string method, string password)
: base(method, password)
{
}
@@ -44,11 +45,11 @@ namespace Shadowsocks.Encryption
return _ciphers;
}
protected override void initCipher(byte[] iv, bool isCipher)
protected override void initCipher(byte[] iv, bool isEncrypt)
{
base.initCipher(iv, isCipher);
base.initCipher(iv, isEncrypt);
IntPtr ctx = Marshal.AllocHGlobal(MbedTLS.cipher_get_size_ex());
if (isCipher)
if (isEncrypt)
{
_encryptCtx = ctx;
}
@@ -71,7 +72,7 @@ namespace Shadowsocks.Encryption
}
MbedTLS.cipher_init(ctx);
if (MbedTLS.cipher_setup( ctx, MbedTLS.cipher_info_from_string( _innerLibName ) ) != 0 )
throw new Exception("Cannot initialize mbed TLS cipher context");
throw new System.Exception("Cannot initialize mbed TLS cipher context");
/*
* MbedTLS takes key length by bit
* cipher_setkey() will set the correct key schedule
@@ -84,24 +85,24 @@ namespace Shadowsocks.Encryption
*
*/
if (MbedTLS.cipher_setkey(ctx, realkey, keyLen * 8,
isCipher ? MbedTLS.MBEDTLS_ENCRYPT : MbedTLS.MBEDTLS_DECRYPT) != 0 )
throw new Exception("Cannot set mbed TLS cipher key");
isEncrypt ? MbedTLS.MBEDTLS_ENCRYPT : MbedTLS.MBEDTLS_DECRYPT) != 0 )
throw new System.Exception("Cannot set mbed TLS cipher key");
if (MbedTLS.cipher_set_iv(ctx, iv, ivLen) != 0)
throw new Exception("Cannot set mbed TLS cipher IV");
throw new System.Exception("Cannot set mbed TLS cipher IV");
if (MbedTLS.cipher_reset(ctx) != 0)
throw new Exception("Cannot finalize mbed TLS cipher context");
throw new System.Exception("Cannot finalize mbed TLS cipher context");
}
protected override void cipherUpdate(bool isCipher, int length, byte[] buf, byte[] outbuf)
protected override void cipherUpdate(bool isEncrypt, int length, byte[] buf, byte[] outbuf)
{
// C# could be multi-threaded
if (_disposed)
{
throw new ObjectDisposedException(this.ToString());
}
if (MbedTLS.cipher_update(isCipher ? _encryptCtx : _decryptCtx,
if (MbedTLS.cipher_update(isEncrypt ? _encryptCtx : _decryptCtx,
buf, length, outbuf, ref length) != 0 )
throw new Exception("Cannot update mbed TLS cipher context");
throw new CryptoErrorException();
}
#region IDisposable
@@ -117,7 +118,7 @@ namespace Shadowsocks.Encryption
GC.SuppressFinalize(this);
}
~MbedTLSEncryptor()
~StreamMbedTLSEncryptor()
{
Dispose(false);
}

shadowsocks-csharp/Encryption/SodiumEncryptor.cs → shadowsocks-csharp/Encryption/Stream/StreamSodiumEncryptor.cs View File

@@ -1,10 +1,11 @@
using System;
using System.Collections.Generic;
using Shadowsocks.Encryption.Exception;
namespace Shadowsocks.Encryption
namespace Shadowsocks.Encryption.Stream
{
public class SodiumEncryptor
: IVEncryptor, IDisposable
public class StreamSodiumEncryptor
: StreamEncryptor, IDisposable
{
const int CIPHER_SALSA20 = 1;
const int CIPHER_CHACHA20 = 2;
@@ -19,8 +20,8 @@ namespace Shadowsocks.Encryption
protected byte[] _encryptBuf;
protected byte[] _decryptBuf;
public SodiumEncryptor(string method, string password, bool onetimeauth, bool isudp)
: base(method, password, onetimeauth, isudp)
public StreamSodiumEncryptor(string method, string password)
: base(method, password)
{
_encryptBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE];
_decryptBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE];
@@ -42,15 +43,16 @@ namespace Shadowsocks.Encryption
return new List<string>(_ciphers.Keys);
}
protected override void cipherUpdate(bool isCipher, int length, byte[] buf, byte[] outbuf)
protected override void cipherUpdate(bool isEncrypt, int length, byte[] buf, byte[] outbuf)
{
// TODO write a unidirection cipher so we don't have to if if if
int bytesRemaining;
ulong ic;
byte[] sodiumBuf;
byte[] iv;
int ret = -1;
if (isCipher)
if (isEncrypt)
{
bytesRemaining = _encryptBytesRemaining;
ic = _encryptIC;
@@ -70,21 +72,23 @@ namespace Shadowsocks.Encryption
switch (_cipher)
{
case CIPHER_SALSA20:
Sodium.crypto_stream_salsa20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key);
ret = Sodium.crypto_stream_salsa20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key);
break;
case CIPHER_CHACHA20:
Sodium.crypto_stream_chacha20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key);
ret = Sodium.crypto_stream_chacha20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key);
break;
case CIPHER_CHACHA20_IETF:
Sodium.crypto_stream_chacha20_ietf_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, (uint)ic, _key);
ret = Sodium.crypto_stream_chacha20_ietf_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, (uint)ic, _key);
break;
}
if (ret != 0) throw new CryptoErrorException();
Buffer.BlockCopy(sodiumBuf, padding, outbuf, 0, length);
padding += length;
ic += (ulong)padding / SODIUM_BLOCK_SIZE;
bytesRemaining = padding % SODIUM_BLOCK_SIZE;
if (isCipher)
if (isEncrypt)
{
_encryptBytesRemaining = bytesRemaining;
_encryptIC = ic;

+ 1
- 0
shadowsocks-csharp/packages.config View File

@@ -2,6 +2,7 @@
<packages>
<package id="Caseless.Fody" version="1.4.2" targetFramework="net462" developmentDependency="true" />
<package id="Costura.Fody" version="1.3.3.0" targetFramework="net462" developmentDependency="true" />
<package id="Cyotek.CircularBuffer" version="1.0.2" targetFramework="net462" />
<package id="Fody" version="1.29.4" targetFramework="net462" developmentDependency="true" />
<package id="GlobalHotKey" version="1.1.0" targetFramework="net462" />
<package id="Newtonsoft.Json" version="10.0.1" targetFramework="net462" />


+ 16
- 9
shadowsocks-csharp/shadowsocks-csharp.csproj View File

@@ -66,6 +66,9 @@
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
<Reference Include="Cyotek.Collections.Generic.CircularBuffer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=58daa28b0b2de221, processorArchitecture=MSIL">
<HintPath>3rd\Cyotek.CircularBuffer.1.0.2\lib\net20\Cyotek.Collections.Generic.CircularBuffer.dll</HintPath>
</Reference>
<Reference Include="GlobalHotKey, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>3rd\GlobalHotKey.1.1.0\lib\GlobalHotKey.dll</HintPath>
<Private>True</Private>
@@ -94,6 +97,19 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Controller\System\Hotkeys\HotkeyCallbacks.cs" />
<Compile Include="Encryption\AEAD\AEADEncryptor.cs" />
<Compile Include="Encryption\AEAD\AEADMbedTLSEncryptor.cs" />
<Compile Include="Encryption\AEAD\AEADSodiumEncryptor.cs" />
<Compile Include="Encryption\EncryptorBase.cs" />
<Compile Include="Encryption\EncryptorFactory.cs" />
<Compile Include="Encryption\Exception\CryptoException.cs" />
<Compile Include="Encryption\IEncryptor.cs" />
<Compile Include="Encryption\MbedTLS.cs" />
<Compile Include="Encryption\RNG.cs" />
<Compile Include="Encryption\Sodium.cs" />
<Compile Include="Encryption\Stream\StreamEncryptor.cs" />
<Compile Include="Encryption\Stream\StreamMbedTLSEncryptor.cs" />
<Compile Include="Encryption\Stream\StreamSodiumEncryptor.cs" />
<Compile Include="Model\HotKeyConfig.cs" />
<Compile Include="Model\ProxyConfig.cs" />
<Compile Include="Properties\Settings.Designer.cs">
@@ -116,14 +132,6 @@
<Compile Include="Controller\Service\PortForwarder.cs" />
<Compile Include="Controller\Service\UDPRelay.cs" />
<Compile Include="Controller\Service\UpdateChecker.cs" />
<Compile Include="Encryption\EncryptorBase.cs" />
<Compile Include="Encryption\EncryptorFactory.cs" />
<Compile Include="Encryption\IVEncryptor.cs" />
<Compile Include="Encryption\MbedTLS.cs" />
<Compile Include="Encryption\MbedTLSEncryptor.cs" />
<Compile Include="Encryption\Sodium.cs" />
<Compile Include="Encryption\SodiumEncryptor.cs" />
<Compile Include="Encryption\IEncryptor.cs" />
<Compile Include="Controller\Service\PACServer.cs" />
<Compile Include="Model\LogViewerConfig.cs" />
<Compile Include="Model\Server.cs" />
@@ -144,7 +152,6 @@
<Compile Include="Controller\System\Hotkeys\Hotkeys.cs" />
<Compile Include="Util\ProcessManagement\Job.cs" />
<Compile Include="Util\ProcessManagement\ThreadUtil.cs" />
<Compile Include="Encryption\RNG.cs" />
<Compile Include="Util\Sockets\LineReader.cs" />
<Compile Include="Util\Sockets\SocketUtil.cs" />
<Compile Include="Util\Sockets\WrappedSocket.cs" />


+ 8
- 7
test/UnitTest.cs View File

@@ -7,6 +7,7 @@ using System.Windows.Input;
using System.Threading;
using System.Collections.Generic;
using Shadowsocks.Controller.Hotkeys;
using Shadowsocks.Encryption.Stream;
namespace test
{
@@ -70,7 +71,7 @@ namespace test
{
RNG.Reload();
byte[] plain = new byte[16384];
byte[] cipher = new byte[plain.Length + 16 + IVEncryptor.ONETIMEAUTH_BYTES + IVEncryptor.AUTH_BYTES];
byte[] cipher = new byte[plain.Length + 16];
byte[] plain2 = new byte[plain.Length + 16];
int outLen = 0;
int outLen2 = 0;
@@ -130,8 +131,8 @@ namespace test
{
IEncryptor encryptor;
IEncryptor decryptor;
encryptor = new MbedTLSEncryptor("aes-256-cfb", "barfoo!", false, false);
decryptor = new MbedTLSEncryptor("aes-256-cfb", "barfoo!", false, false);
encryptor = new StreamMbedTLSEncryptor("aes-256-cfb", "barfoo!");
decryptor = new StreamMbedTLSEncryptor("aes-256-cfb", "barfoo!");
RunEncryptionRound(encryptor, decryptor);
}
}
@@ -171,8 +172,8 @@ namespace test
var random = new Random();
IEncryptor encryptor;
IEncryptor decryptor;
encryptor = new MbedTLSEncryptor("rc4-md5", "barfoo!", false, false);
decryptor = new MbedTLSEncryptor("rc4-md5", "barfoo!", false, false);
encryptor = new StreamMbedTLSEncryptor("rc4-md5", "barfoo!");
decryptor = new StreamMbedTLSEncryptor("rc4-md5", "barfoo!");
RunEncryptionRound(encryptor, decryptor);
}
}
@@ -212,8 +213,8 @@ namespace test
var random = new Random();
IEncryptor encryptor;
IEncryptor decryptor;
encryptor = new SodiumEncryptor("salsa20", "barfoo!", false, false);
decryptor = new SodiumEncryptor("salsa20", "barfoo!", false, false);
encryptor = new StreamSodiumEncryptor("salsa20", "barfoo!");
decryptor = new StreamSodiumEncryptor("salsa20", "barfoo!");
RunEncryptionRound(encryptor, decryptor);
}
}


Loading…
Cancel
Save