mirror of
https://github.com/bolucat/Archive.git
synced 2025-11-01 20:33:10 +08:00
192 lines
6.7 KiB
C#
192 lines
6.7 KiB
C#
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
|
|
}
|
|
} |