Files
shadowsocks-windows/shadowsocks-csharp/Encryption/MbedTLSEncryptor.cs
T
Syrone Wong 46d95e7411 Get rid of lock(this)
- Use readonly object as lock

Info from stackoverflow:

If I want to be sure that it will be locked for all threads inside my
application:
The lock object has to be static, if it locks access to static state.
Otherwise it has to be instance, because there's no need to lock state of
one class instance, and prevent other threads to work with another class
instance at the same time.

everyone says that the object has to be "readonly" I didn't found the
reason
Well, it doesn't have to be. This is just a best practice, which helps you
to avoid errors.

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-09-29 16:39:58 +08:00

154 lines
5.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Shadowsocks.Encryption
{
public class MbedTLSEncryptor
: IVEncryptor, IDisposable
{
const int CIPHER_RC4 = 1;
const int CIPHER_AES = 2;
const int CIPHER_BLOWFISH = 3;
const int CIPHER_CAMELLIA = 4;
private IntPtr _encryptCtx = IntPtr.Zero;
private IntPtr _decryptCtx = IntPtr.Zero;
// instance based lock
private readonly object _Lock = new object();
public MbedTLSEncryptor(string method, string password, bool onetimeauth, bool isudp)
: base(method, password, onetimeauth, isudp)
{
}
private static Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo> {
{ "aes-128-cfb", new EncryptorInfo("AES-128-CFB128", 16, 16, CIPHER_AES) },
{ "aes-192-cfb", new EncryptorInfo("AES-192-CFB128", 24, 16, CIPHER_AES) },
{ "aes-256-cfb", new EncryptorInfo("AES-256-CFB128", 32, 16, CIPHER_AES) },
{ "aes-128-ctr", new EncryptorInfo("AES-128-CTR", 16, 16, CIPHER_AES) },
{ "aes-192-ctr", new EncryptorInfo("AES-192-CTR", 24, 16, CIPHER_AES) },
{ "aes-256-ctr", new EncryptorInfo("AES-256-CTR", 32, 16, CIPHER_AES) },
{ "bf-cfb", new EncryptorInfo("BLOWFISH-CFB64", 16, 8, CIPHER_BLOWFISH) },
{ "camellia-128-cfb", new EncryptorInfo("CAMELLIA-128-CFB128", 16, 16, CIPHER_CAMELLIA) },
{ "camellia-192-cfb", new EncryptorInfo("CAMELLIA-192-CFB128", 24, 16, CIPHER_CAMELLIA) },
{ "camellia-256-cfb", new EncryptorInfo("CAMELLIA-256-CFB128", 32, 16, CIPHER_CAMELLIA) },
{ "rc4-md5", new EncryptorInfo("ARC4-128", 16, 16, CIPHER_RC4) }
};
public static List<string> SupportedCiphers()
{
return new List<string>(_ciphers.Keys);
}
protected override Dictionary<string, EncryptorInfo> getCiphers()
{
return _ciphers;
}
protected override void initCipher(byte[] iv, bool isCipher)
{
base.initCipher(iv, isCipher);
IntPtr ctx = Marshal.AllocHGlobal(MbedTLS.cipher_get_size_ex());
if (isCipher)
{
_encryptCtx = ctx;
}
else
{
_decryptCtx = ctx;
}
byte[] realkey;
if (_method == "rc4-md5")
{
byte[] temp = new byte[keyLen + ivLen];
realkey = new byte[keyLen];
Array.Copy(_key, 0, temp, 0, keyLen);
Array.Copy(iv, 0, temp, keyLen, ivLen);
realkey = MbedTLS.MD5(temp);
}
else
{
realkey = _key;
}
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");
/*
* MbedTLS takes key length by bit
* cipher_setkey() will set the correct key schedule
* and operation
*
* MBEDTLS_AES_{EN,DE}CRYPT
* == MBEDTLS_BLOWFISH_{EN,DE}CRYPT
* == MBEDTLS_CAMELLIA_{EN,DE}CRYPT
* == MBEDTLS_{EN,DE}CRYPT
*
*/
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");
if (MbedTLS.cipher_set_iv(ctx, iv, ivLen) != 0)
throw new Exception("Cannot set mbed TLS cipher IV");
if (MbedTLS.cipher_reset(ctx) != 0)
throw new Exception("Cannot finalize mbed TLS cipher context");
}
protected override void cipherUpdate(bool isCipher, 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,
buf, length, outbuf, ref length) != 0 )
throw new Exception("Cannot update mbed TLS cipher context");
}
#region IDisposable
private bool _disposed;
public override void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~MbedTLSEncryptor()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
lock (_Lock)
{
if (_disposed)
{
return;
}
_disposed = true;
}
if (disposing)
{
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
}
}