using System; using System.IO; using System.Runtime.InteropServices; using System.Security; using System.Text; using Shadowsocks.Controller; using Shadowsocks.Encryption.Exception; using Shadowsocks.Properties; using Shadowsocks.Util; namespace Shadowsocks.Encryption { // XXX: only for OpenSSL 1.1.0 and higher public static class OpenSSL { private const string DLLNAME = "libsscrypto.dll"; public const int OPENSSL_ENCRYPT = 1; public const int OPENSSL_DECRYPT = 0; public const int EVP_CTRL_AEAD_SET_IVLEN = 0x9; public const int EVP_CTRL_AEAD_GET_TAG = 0x10; public const int EVP_CTRL_AEAD_SET_TAG = 0x11; static OpenSSL() { 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 IntPtr GetCipherInfo(string cipherName) { var name = Encoding.ASCII.GetBytes(cipherName); Array.Resize(ref name, name.Length + 1); return EVP_get_cipherbyname(name); } /// /// Need init cipher context after EVP_CipherFinal_ex to reuse context /// /// /// /// public static void SetCtxNonce(IntPtr ctx, byte[] nonce, bool isEncrypt) { var ret = EVP_CipherInit_ex(ctx, IntPtr.Zero, IntPtr.Zero, null, nonce, isEncrypt ? OPENSSL_ENCRYPT : OPENSSL_DECRYPT); if (ret != 1) throw new System.Exception("openssl: fail to set AEAD nonce"); } public static void AEADGetTag(IntPtr ctx, byte[] tagbuf, int taglen) { IntPtr tagBufIntPtr = IntPtr.Zero; try { tagBufIntPtr = Marshal.AllocHGlobal(taglen); var ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, taglen, tagBufIntPtr); if (ret != 1) throw new CryptoErrorException("openssl: fail to get AEAD tag"); // take tag from unmanaged memory Marshal.Copy(tagBufIntPtr, tagbuf, 0, taglen); } finally { if (tagBufIntPtr != IntPtr.Zero) { Marshal.FreeHGlobal(tagBufIntPtr); } } } public static void AEADSetTag(IntPtr ctx, byte[] tagbuf, int taglen) { IntPtr tagBufIntPtr = IntPtr.Zero; try { // allocate unmanaged memory for tag tagBufIntPtr = Marshal.AllocHGlobal(taglen); // copy tag to unmanaged memory Marshal.Copy(tagbuf, 0, tagBufIntPtr, taglen); var ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, taglen, tagBufIntPtr); if (ret != 1) throw new CryptoErrorException("openssl: fail to set AEAD tag"); } finally { if (tagBufIntPtr != IntPtr.Zero) { Marshal.FreeHGlobal(tagBufIntPtr); } } } [DllImport("Kernel32.dll")] private static extern IntPtr LoadLibrary(string path); [SuppressUnmanagedCodeSecurity] [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr EVP_CIPHER_CTX_new(); [SuppressUnmanagedCodeSecurity] [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] public static extern void EVP_CIPHER_CTX_free(IntPtr ctx); [SuppressUnmanagedCodeSecurity] [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] public static extern int EVP_CIPHER_CTX_reset(IntPtr ctx); [SuppressUnmanagedCodeSecurity] [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] public static extern int EVP_CipherInit_ex(IntPtr ctx, IntPtr type, IntPtr impl, byte[] key, byte[] iv, int enc); [SuppressUnmanagedCodeSecurity] [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] public static extern int EVP_CipherUpdate(IntPtr ctx, byte[] outb, out int outl, byte[] inb, int inl); [SuppressUnmanagedCodeSecurity] [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] public static extern int EVP_CipherFinal_ex(IntPtr ctx, byte[] outm, ref int outl); [SuppressUnmanagedCodeSecurity] [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] public static extern int EVP_CIPHER_CTX_set_padding(IntPtr x, int padding); [SuppressUnmanagedCodeSecurity] [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] public static extern int EVP_CIPHER_CTX_set_key_length(IntPtr x, int keylen); [SuppressUnmanagedCodeSecurity] [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] public static extern int EVP_CIPHER_CTX_ctrl(IntPtr ctx, int type, int arg, IntPtr ptr); /// /// simulate NUL-terminated string /// /// /// [SuppressUnmanagedCodeSecurity] [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr EVP_get_cipherbyname(byte[] name); } }