Skip to content

Commit

Permalink
Add OpenSSL 1.1.0g support due to hardware acceleration
Browse files Browse the repository at this point in the history
Note: If you want to compile it by yourself, please make sure
do NOT use `no-asm` configure option, since the main point of
this commit is to utilize assembly in openssl

Add OpenSSL test
  • Loading branch information
wongsyrone committed Feb 10, 2018
1 parent d1219bb commit 4d4f175
Show file tree
Hide file tree
Showing 15 changed files with 693 additions and 67 deletions.
15 changes: 15 additions & 0 deletions OPENSSL-GUIDE
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
OpenSSL library guide for VS2017

# Read NOTES.WIN and NOTES.PERL

# use Visual Studio native tools command prompt
# use activeperl, install NASM assembler
ppm install dmake

# Win32 x86
set PATH=D:\NASM-32;%PATH%
perl Configure VC-WIN32 --release --prefix=C:\Users\home\Downloads\openssl-1.1.0g\x86-build --openssldir=C:\Users\home\Downloads\openssl-1.1.0g\x86-install
nmake
nmake test
# to rebuild
nmake distclean
1 change: 1 addition & 0 deletions shadowsocks-csharp/Controller/Service/Listener.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public void Start(Configuration config)

// Start an asynchronous socket to listen for connections.
Logging.Info("Shadowsocks started");
Logging.Info(Encryption.EncryptorFactory.DumpRegisteredEncryptor());
_tcpSocket.BeginAccept(new AsyncCallback(AcceptCallback), _tcpSocket);
UDPState udpState = new UDPState();
udpState.socket = _udpSocket;
Expand Down
Binary file added shadowsocks-csharp/Data/libcrypto-1_1.dll.gz
Binary file not shown.
4 changes: 2 additions & 2 deletions shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,9 @@ public virtual void InitCipher(byte[] salt, bool isEncrypt, bool isUdp)

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 void cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen);

public abstract int cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen);
public abstract void cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen);

#region TCP

Expand Down
16 changes: 9 additions & 7 deletions shadowsocks-csharp/Encryption/AEAD/AEADMbedTLSEncryptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public AEADMbedTLSEncryptor(string method, string password)
{
}

private static Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo>
private static readonly 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)},
Expand Down Expand Up @@ -48,6 +48,7 @@ public override void InitCipher(byte[] salt, bool isEncrypt, bool isUdp)
{
_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");
Expand All @@ -67,7 +68,7 @@ private void CipherSetKey(bool isEncrypt, byte[] key)
if (ret != 0) throw new System.Exception("failed to finish preparation");
}

public override int cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen)
public override void cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen)
{
// buf: all plaintext
// outbuf: ciphertext + tag
Expand All @@ -87,18 +88,18 @@ public override int cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext
/* cipher */
ciphertext, ref olen,
tagbuf, (uint) tagLen);
if (ret != 0) throw new CryptoErrorException();
if (ret != 0) throw new CryptoErrorException(String.Format("ret is {0}", ret));
Debug.Assert(olen == plen);
// attach tag to ciphertext
Array.Copy(tagbuf, 0, ciphertext, (int) plen, tagLen);
clen = olen + (uint) tagLen;
return ret;
break;
default:
throw new System.Exception("not implemented");
}
}

public override int cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen)
public override void cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen)
{
// buf: ciphertext + tag
// outbuf: plaintext
Expand All @@ -116,10 +117,10 @@ public override int cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext
ciphertext, (uint) (clen - tagLen),
plaintext, ref olen,
tagbuf, (uint) tagLen);
if (ret != 0) throw new CryptoErrorException();
if (ret != 0) throw new CryptoErrorException(String.Format("ret is {0}", ret));
Debug.Assert(olen == clen - tagLen);
plen = olen;
return ret;
break;
default:
throw new System.Exception("not implemented");
}
Expand Down Expand Up @@ -163,6 +164,7 @@ protected virtual void Dispose(bool disposing)
Marshal.FreeHGlobal(_encryptCtx);
_encryptCtx = IntPtr.Zero;
}

if (_decryptCtx != IntPtr.Zero)
{
MbedTLS.cipher_free(_decryptCtx);
Expand Down
192 changes: 192 additions & 0 deletions shadowsocks-csharp/Encryption/AEAD/AEADOpenSSLEncryptor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
using System;
using System.Collections.Generic;
using Shadowsocks.Encryption.Exception;

namespace Shadowsocks.Encryption.AEAD
{
public class AEADOpenSSLEncryptor
: AEADEncryptor, IDisposable
{
const int CIPHER_AES = 1;
const int CIPHER_CHACHA20IETFPOLY1305 = 2;

private byte[] _opensslEncSubkey;
private byte[] _opensslDecSubkey;

private IntPtr _encryptCtx = IntPtr.Zero;
private IntPtr _decryptCtx = IntPtr.Zero;

private IntPtr _cipherInfoPtr = IntPtr.Zero;

public AEADOpenSSLEncryptor(string method, string password)
: base(method, password)
{
_opensslEncSubkey = new byte[keyLen];
_opensslDecSubkey = new byte[keyLen];
}

private static readonly 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)},
{"chacha20-ietf-poly1305", new EncryptorInfo("chacha20-poly1305", 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);
_cipherInfoPtr = OpenSSL.GetCipherInfo(_innerLibName);
if (_cipherInfoPtr == IntPtr.Zero) throw new System.Exception("openssl: cipher not found");
IntPtr ctx = OpenSSL.EVP_CIPHER_CTX_new();
if (ctx == IntPtr.Zero) throw new System.Exception("openssl: fail to create ctx");

if (isEncrypt)
{
_encryptCtx = ctx;
}
else
{
_decryptCtx = ctx;
}

DeriveSessionKey(isEncrypt ? _encryptSalt : _decryptSalt, _Masterkey,
isEncrypt ? _opensslEncSubkey : _opensslDecSubkey);

var ret = OpenSSL.EVP_CipherInit_ex(ctx, _cipherInfoPtr, IntPtr.Zero, null, null,
isEncrypt ? OpenSSL.OPENSSL_ENCRYPT : OpenSSL.OPENSSL_DECRYPT);
if (ret != 1) throw new System.Exception("openssl: fail to init ctx");

ret = OpenSSL.EVP_CIPHER_CTX_set_key_length(ctx, keyLen);
if (ret != 1) throw new System.Exception("openssl: fail to set key length");

ret = OpenSSL.EVP_CIPHER_CTX_ctrl(ctx, OpenSSL.EVP_CTRL_AEAD_SET_IVLEN,
nonceLen, IntPtr.Zero);
if (ret != 1) throw new System.Exception("openssl: fail to set AEAD nonce length");

ret = OpenSSL.EVP_CipherInit_ex(ctx, IntPtr.Zero, IntPtr.Zero,
isEncrypt ? _opensslEncSubkey : _opensslDecSubkey,
null,
isEncrypt ? OpenSSL.OPENSSL_ENCRYPT : OpenSSL.OPENSSL_DECRYPT);
if (ret != 1) throw new System.Exception("openssl: cannot set key");
OpenSSL.EVP_CIPHER_CTX_set_padding(ctx, 0);
}

public override void cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen)
{
OpenSSL.SetCtxNonce(_encryptCtx, _encNonce, true);
// buf: all plaintext
// outbuf: ciphertext + tag
int ret;
int tmpLen = 0;
clen = 0;
var tagBuf = new byte[tagLen];

ret = OpenSSL.EVP_CipherUpdate(_encryptCtx, ciphertext, out tmpLen,
plaintext, (int) plen);
if (ret != 1) throw new CryptoErrorException("openssl: fail to encrypt AEAD");
clen += (uint) tmpLen;
// For AEAD cipher, it should not output anything
ret = OpenSSL.EVP_CipherFinal_ex(_encryptCtx, ciphertext, ref tmpLen);
if (ret != 1) throw new CryptoErrorException("openssl: fail to finalize AEAD");
if (tmpLen > 0)
{
throw new System.Exception("openssl: fail to finish AEAD");
}

OpenSSL.AEADGetTag(_encryptCtx, tagBuf, tagLen);
Array.Copy(tagBuf, 0, ciphertext, clen, tagLen);
clen += (uint) tagLen;
}

public override void cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen)
{
OpenSSL.SetCtxNonce(_decryptCtx, _decNonce, false);
// buf: ciphertext + tag
// outbuf: plaintext
int ret;
int tmpLen = 0;
plen = 0;

// split tag
byte[] tagbuf = new byte[tagLen];
Array.Copy(ciphertext, (int) (clen - tagLen), tagbuf, 0, tagLen);
OpenSSL.AEADSetTag(_decryptCtx, tagbuf, tagLen);

ret = OpenSSL.EVP_CipherUpdate(_decryptCtx,
plaintext, out tmpLen, ciphertext, (int) (clen - tagLen));
if (ret != 1) throw new CryptoErrorException("openssl: fail to decrypt AEAD");
plen += (uint) tmpLen;

// For AEAD cipher, it should not output anything
ret = OpenSSL.EVP_CipherFinal_ex(_decryptCtx, plaintext, ref tmpLen);
if (ret <= 0)
{
// If this is not successful authenticated
throw new CryptoErrorException(String.Format("ret is {0}", ret));
}

if (tmpLen > 0)
{
throw new System.Exception("openssl: fail to finish AEAD");
}
}

#region IDisposable

private bool _disposed;

// instance based lock
private readonly object _lock = new object();

public override void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

~AEADOpenSSLEncryptor()
{
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)
{
OpenSSL.EVP_CIPHER_CTX_free(_encryptCtx);
_encryptCtx = IntPtr.Zero;
}

if (_decryptCtx != IntPtr.Zero)
{
OpenSSL.EVP_CIPHER_CTX_free(_decryptCtx);
_decryptCtx = IntPtr.Zero;
}
}

#endregion
}
}
12 changes: 5 additions & 7 deletions shadowsocks-csharp/Encryption/AEAD/AEADSodiumEncryptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public AEADSodiumEncryptor(string method, string password)
_sodiumDecSubkey = new byte[keyLen];
}

private static Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo>
private static readonly Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo>
{
{"chacha20-ietf-poly1305", new EncryptorInfo(32, 32, 12, 16, CIPHER_CHACHA20IETFPOLY1305)},
{"aes-256-gcm", new EncryptorInfo(32, 32, 12, 16, CIPHER_AES256GCM)},
Expand All @@ -46,7 +46,7 @@ public override void InitCipher(byte[] salt, bool isEncrypt, bool isUdp)
}


public override int cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen)
public override void cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext, ref uint clen)
{
Debug.Assert(_sodiumEncSubkey != null);
// buf: all plaintext
Expand Down Expand Up @@ -75,13 +75,12 @@ public override int cipherEncrypt(byte[] plaintext, uint plen, byte[] ciphertext
default:
throw new System.Exception("not implemented");
}
if (ret != 0) throw new CryptoErrorException();
if (ret != 0) throw new CryptoErrorException(String.Format("ret is {0}", ret));
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)
public override void cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext, ref uint plen)
{
Debug.Assert(_sodiumDecSubkey != null);
// buf: ciphertext + tag
Expand Down Expand Up @@ -111,10 +110,9 @@ public override int cipherDecrypt(byte[] ciphertext, uint clen, byte[] plaintext
throw new System.Exception("not implemented");
}

if (ret != 0) throw new CryptoErrorException();
if (ret != 0) throw new CryptoErrorException(String.Format("ret is {0}", ret));
Logging.Dump("after cipherDecrypt: plain", plaintext, (int) decPlen);
plen = (uint) decPlen;
return ret;
}

public override void Dispose()
Expand Down
Loading

0 comments on commit 4d4f175

Please sign in to comment.