using System; using System.Collections.Generic; using System.Text; using System.Threading; namespace Shadowsocks.Encryption { public class SodiumEncryptor : IVEncryptor, IDisposable { const int CIPHER_SALSA20 = 1; const int CIPHER_CHACHA20 = 2; 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; public SodiumEncryptor(string method, string password, bool onetimeauth) : base(method, password, onetimeauth) { InitKey(method, password); } private static Dictionary _ciphers = new Dictionary { {"salsa20", new int[]{32, 8, CIPHER_SALSA20, PolarSSL.AES_CTX_SIZE}}, {"chacha20", new int[]{32, 8, CIPHER_CHACHA20, PolarSSL.AES_CTX_SIZE}}, }; protected override Dictionary getCiphers() { return _ciphers; } public static List SupportedCiphers() { return new List(_ciphers.Keys); } protected override void cipherUpdate(bool isCipher, 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[] 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; iv = _encryptIV; } else { bytesRemaining = _decryptBytesRemaining; ic = _decryptIC; 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; } 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; } } } public override void Dispose() { } } }