|
|
@@ -12,16 +12,18 @@ namespace Shadowsocks.Encryption |
|
|
|
|
|
|
|
const int SODIUM_BLOCK_SIZE = 64;
|
|
|
|
|
|
|
|
static byte[] sodiumBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE];
|
|
|
|
|
|
|
|
protected int _encryptBytesRemaining;
|
|
|
|
protected int _decryptBytesRemaining;
|
|
|
|
protected ulong _encryptIC;
|
|
|
|
protected ulong _decryptIC;
|
|
|
|
protected byte[] _encryptBuf;
|
|
|
|
protected byte[] _decryptBuf;
|
|
|
|
|
|
|
|
public SodiumEncryptor(string method, string password, bool onetimeauth, bool isudp)
|
|
|
|
: base(method, password, onetimeauth, isudp)
|
|
|
|
{
|
|
|
|
_encryptBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE];
|
|
|
|
_decryptBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE];
|
|
|
|
}
|
|
|
|
|
|
|
|
private static Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo> {
|
|
|
@@ -45,54 +47,52 @@ namespace Shadowsocks.Encryption |
|
|
|
// TODO write a unidirection cipher so we don't have to if if if
|
|
|
|
int bytesRemaining;
|
|
|
|
ulong ic;
|
|
|
|
byte[] sodiumBuf;
|
|
|
|
byte[] iv;
|
|
|
|
|
|
|
|
// I'm tired. just add a big lock
|
|
|
|
// let's optimize for RAM instead of CPU
|
|
|
|
lock(sodiumBuf)
|
|
|
|
if (isCipher)
|
|
|
|
{
|
|
|
|
bytesRemaining = _encryptBytesRemaining;
|
|
|
|
ic = _encryptIC;
|
|
|
|
sodiumBuf = _encryptBuf;
|
|
|
|
iv = _encryptIV;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (isCipher)
|
|
|
|
{
|
|
|
|
bytesRemaining = _encryptBytesRemaining;
|
|
|
|
ic = _encryptIC;
|
|
|
|
iv = _encryptIV;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bytesRemaining = _decryptBytesRemaining;
|
|
|
|
ic = _decryptIC;
|
|
|
|
iv = _decryptIV;
|
|
|
|
}
|
|
|
|
int padding = bytesRemaining;
|
|
|
|
Buffer.BlockCopy(buf, 0, sodiumBuf, padding, length);
|
|
|
|
bytesRemaining = _decryptBytesRemaining;
|
|
|
|
ic = _decryptIC;
|
|
|
|
sodiumBuf = _decryptBuf;
|
|
|
|
iv = _decryptIV;
|
|
|
|
}
|
|
|
|
int padding = bytesRemaining;
|
|
|
|
Buffer.BlockCopy(buf, 0, sodiumBuf, padding, length);
|
|
|
|
|
|
|
|
switch (_cipher)
|
|
|
|
{
|
|
|
|
case CIPHER_SALSA20:
|
|
|
|
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);
|
|
|
|
break;
|
|
|
|
case CIPHER_CHACHA20_IETF:
|
|
|
|
Sodium.crypto_stream_chacha20_ietf_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, (uint)ic, _key);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Buffer.BlockCopy(sodiumBuf, padding, outbuf, 0, length);
|
|
|
|
padding += length;
|
|
|
|
ic += (ulong)padding / SODIUM_BLOCK_SIZE;
|
|
|
|
bytesRemaining = padding % SODIUM_BLOCK_SIZE;
|
|
|
|
switch (_cipher)
|
|
|
|
{
|
|
|
|
case CIPHER_SALSA20:
|
|
|
|
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);
|
|
|
|
break;
|
|
|
|
case CIPHER_CHACHA20_IETF:
|
|
|
|
Sodium.crypto_stream_chacha20_ietf_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, (uint)ic, _key);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Buffer.BlockCopy(sodiumBuf, padding, outbuf, 0, length);
|
|
|
|
padding += length;
|
|
|
|
ic += (ulong)padding / SODIUM_BLOCK_SIZE;
|
|
|
|
bytesRemaining = padding % SODIUM_BLOCK_SIZE;
|
|
|
|
|
|
|
|
if (isCipher)
|
|
|
|
{
|
|
|
|
_encryptBytesRemaining = bytesRemaining;
|
|
|
|
_encryptIC = ic;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_decryptBytesRemaining = bytesRemaining;
|
|
|
|
_decryptIC = ic;
|
|
|
|
}
|
|
|
|
if (isCipher)
|
|
|
|
{
|
|
|
|
_encryptBytesRemaining = bytesRemaining;
|
|
|
|
_encryptIC = ic;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_decryptBytesRemaining = bytesRemaining;
|
|
|
|
_decryptIC = ic;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|