@@ -339,7 +339,7 @@ namespace Shadowsocks.Controller | |||||
if (bytesRead >= 5) | if (bytesRead >= 5) | ||||
{ | { | ||||
_command = _connetionRecvBuffer[1]; | _command = _connetionRecvBuffer[1]; | ||||
switch(_command) | |||||
switch (_command) | |||||
{ | { | ||||
case CMD_CONNECT: | case CMD_CONNECT: | ||||
@@ -467,7 +467,7 @@ namespace Shadowsocks.Controller | |||||
break; | break; | ||||
} | } | ||||
Logger.Debug($"connect to {dstAddr}:{dstPort}"); | |||||
Logger.Debug($"connect to {dstAddr}:{dstPort}"); | |||||
_destEndPoint = SocketUtil.GetEndPoint(dstAddr, dstPort); | _destEndPoint = SocketUtil.GetEndPoint(dstAddr, dstPort); | ||||
@@ -820,7 +820,8 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
decryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend); | |||||
bytesToSend = decryptor.Decrypt(_remoteSendBuffer, _remoteRecvBuffer.AsSpan(0, bytesRead)); | |||||
// decryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend); | |||||
} | } | ||||
catch (CryptoErrorException) | catch (CryptoErrorException) | ||||
{ | { | ||||
@@ -893,7 +894,8 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
encryptor.Encrypt(_connetionRecvBuffer, length, _connetionSendBuffer, out bytesToSend); | |||||
bytesToSend = encryptor.Encrypt(_connetionRecvBuffer.AsSpan(0, length), _connetionSendBuffer); | |||||
// encryptor.Encrypt(_connetionRecvBuffer, length, _connetionSendBuffer, out bytesToSend); | |||||
} | } | ||||
catch (CryptoErrorException) | catch (CryptoErrorException) | ||||
{ | { | ||||
@@ -96,11 +96,11 @@ namespace Shadowsocks.Controller | |||||
public void Send(byte[] data, int length) | public void Send(byte[] data, int length) | ||||
{ | { | ||||
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); | IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); | ||||
byte[] dataIn = new byte[length - 3]; | |||||
Array.Copy(data, 3, dataIn, 0, length - 3); | |||||
// byte[] dataIn = new byte[length - 3]; | |||||
// Array.Copy(data, 3, dataIn, 0, length - 3); | |||||
byte[] dataOut = new byte[65536]; // enough space for AEAD ciphers | byte[] dataOut = new byte[65536]; // enough space for AEAD ciphers | ||||
int outlen; | |||||
encryptor.EncryptUDP(dataIn, length - 3, dataOut, out outlen); | |||||
// encryptor.EncryptUDP(dataIn, length - 3, dataOut, out outlen); | |||||
int outlen = encryptor.EncryptUDP(data.AsSpan(3), dataOut); | |||||
logger.Debug(_localEndPoint, _remoteEndPoint, outlen, "UDP Relay"); | logger.Debug(_localEndPoint, _remoteEndPoint, outlen, "UDP Relay"); | ||||
_remote?.SendTo(dataOut, outlen, SocketFlags.None, _remoteEndPoint); | _remote?.SendTo(dataOut, outlen, SocketFlags.None, _remoteEndPoint); | ||||
} | } | ||||
@@ -124,8 +124,8 @@ namespace Shadowsocks.Controller | |||||
int outlen; | int outlen; | ||||
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); | IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); | ||||
encryptor.DecryptUDP(_buffer, bytesRead, dataOut, out outlen); | |||||
// encryptor.DecryptUDP(_buffer, bytesRead, dataOut, out outlen); | |||||
outlen = encryptor.DecryptUDP(dataOut, _buffer.AsSpan(0, bytesRead)); | |||||
byte[] sendBuf = new byte[outlen + 3]; | byte[] sendBuf = new byte[outlen + 3]; | ||||
Array.Copy(dataOut, 0, sendBuf, 3, outlen); | Array.Copy(dataOut, 0, sendBuf, 3, outlen); | ||||
@@ -57,6 +57,8 @@ namespace Shadowsocks.Encryption.AEAD | |||||
InitKey(password); | InitKey(password); | ||||
// Initialize all-zero nonce for each connection | // Initialize all-zero nonce for each connection | ||||
nonce = new byte[nonceLen]; | nonce = new byte[nonceLen]; | ||||
logger.Dump($"masterkey {instanceId}", masterKey, keyLen); | |||||
logger.Dump($"nonce {instanceId}", nonce, keyLen); | |||||
} | } | ||||
protected abstract Dictionary<string, CipherInfo> getCiphers(); | protected abstract Dictionary<string, CipherInfo> getCiphers(); | ||||
@@ -99,9 +101,10 @@ namespace Shadowsocks.Encryption.AEAD | |||||
{ | { | ||||
this.salt = new byte[saltLen]; | this.salt = new byte[saltLen]; | ||||
Array.Copy(salt, this.salt, saltLen); | Array.Copy(salt, this.salt, saltLen); | ||||
logger.Dump("Salt", salt, saltLen); | |||||
DeriveSessionKey(salt, masterKey, sessionKey); | DeriveSessionKey(salt, masterKey, sessionKey); | ||||
logger.Dump($"salt {instanceId}", salt, saltLen); | |||||
logger.Dump($"sessionkey {instanceId}", sessionKey, keyLen); | |||||
} | } | ||||
public abstract int CipherEncrypt(ReadOnlySpan<byte> plain, Span<byte> cipher); | public abstract int CipherEncrypt(ReadOnlySpan<byte> plain, Span<byte> cipher); | ||||
@@ -109,85 +112,6 @@ namespace Shadowsocks.Encryption.AEAD | |||||
#region TCP | #region TCP | ||||
public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength) | |||||
{ | |||||
// push data | |||||
buf.CopyTo(sharedBuffer, bufPtr); | |||||
Span<byte> tmp = sharedBuffer.AsSpan(0, length + bufPtr); | |||||
outlength = 0; | |||||
logger.Debug("---Start Encryption"); | |||||
if (!saltReady) | |||||
{ | |||||
saltReady = true; | |||||
// Generate salt | |||||
byte[] saltBytes = RNG.GetBytes(saltLen); | |||||
InitCipher(saltBytes, true, false); | |||||
Array.Copy(saltBytes, 0, outbuf, 0, saltLen); | |||||
outlength = saltLen; | |||||
logger.Debug($"_encryptSaltSent outlength {outlength}"); | |||||
} | |||||
if (!tcpRequestSent) | |||||
{ | |||||
tcpRequestSent = true; | |||||
// The first TCP request | |||||
byte[] encAddrBufBytes = new byte[AddressBufferLength + tagLen * 2 + ChunkLengthBytes]; | |||||
// read addr byte to encrypt | |||||
byte[] addrBytes = tmp.Slice(0, AddressBufferLength).ToArray(); | |||||
tmp = tmp.Slice(AddressBufferLength); | |||||
int encAddrBufLength = ChunkEncrypt(addrBytes, encAddrBufBytes); | |||||
Array.Copy(encAddrBufBytes, 0, outbuf, outlength, encAddrBufLength); | |||||
outlength += encAddrBufLength; | |||||
logger.Debug($"_tcpRequestSent outlength {outlength}"); | |||||
} | |||||
// handle other chunks | |||||
while (true) | |||||
{ | |||||
// calculate next chunk size | |||||
int bufSize = tmp.Length; | |||||
if (bufSize <= 0) | |||||
{ | |||||
return; | |||||
} | |||||
int chunklength = (int)Math.Min(bufSize, ChunkLengthMask); | |||||
// read next chunk | |||||
byte[] chunkBytes = tmp.Slice(0, chunklength).ToArray(); | |||||
tmp = tmp.Slice(chunklength); | |||||
byte[] encChunkBytes = new byte[chunklength + tagLen * 2 + ChunkLengthBytes]; | |||||
int encChunkLength = ChunkEncrypt(chunkBytes, encChunkBytes); | |||||
Buffer.BlockCopy(encChunkBytes, 0, outbuf, outlength, encChunkLength); | |||||
outlength += encChunkLength; | |||||
logger.Debug("chunks enc outlength " + outlength); | |||||
// check if we have enough space for outbuf | |||||
// if not, keep buf for next run, at this condition, buffer is not empty | |||||
if (outlength + TCPHandler.ChunkOverheadSize > TCPHandler.BufferSize) | |||||
{ | |||||
logger.Debug("enc outbuf almost full, giving up"); | |||||
// write rest data to head of shared buffer | |||||
tmp.CopyTo(sharedBuffer); | |||||
bufPtr = tmp.Length; | |||||
return; | |||||
} | |||||
// check if buffer empty | |||||
bufSize = tmp.Length; | |||||
if (bufSize <= 0) | |||||
{ | |||||
logger.Debug("No more data to encrypt, leaving"); | |||||
return; | |||||
} | |||||
} | |||||
} | |||||
public override int Encrypt(ReadOnlySpan<byte> plain, Span<byte> cipher) | public override int Encrypt(ReadOnlySpan<byte> plain, Span<byte> cipher) | ||||
{ | { | ||||
// push data | // push data | ||||
@@ -253,86 +177,6 @@ namespace Shadowsocks.Encryption.AEAD | |||||
} | } | ||||
} | } | ||||
public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength) | |||||
{ | |||||
outlength = 0; | |||||
// drop all into buffer | |||||
buf.CopyTo(sharedBuffer, bufPtr); | |||||
Span<byte> tmp = buf.AsSpan(0, length + bufPtr); | |||||
int bufSize = tmp.Length; | |||||
logger.Debug("---Start Decryption"); | |||||
if (!saltReady) | |||||
{ | |||||
// check if we get the leading salt | |||||
if (bufSize <= saltLen) | |||||
{ | |||||
// need more, write back cache | |||||
tmp.CopyTo(sharedBuffer); | |||||
bufPtr = tmp.Length; | |||||
return; | |||||
} | |||||
saltReady = true; | |||||
// buffer.Get(saltLen); | |||||
byte[] salt = tmp.Slice(0, saltLen).ToArray(); | |||||
tmp = tmp.Slice(saltLen); | |||||
InitCipher(salt, false, false); | |||||
logger.Debug("get salt len " + saltLen); | |||||
} | |||||
// handle chunks | |||||
while (true) | |||||
{ | |||||
bufSize = tmp.Length; | |||||
// check if we have any data | |||||
if (bufSize <= 0) | |||||
{ | |||||
logger.Debug("No data in _decCircularBuffer"); | |||||
return; | |||||
} | |||||
// first get chunk length | |||||
if (bufSize <= ChunkLengthBytes + tagLen) | |||||
{ | |||||
// so we only have chunk length and its tag? | |||||
// wait more | |||||
tmp.CopyTo(sharedBuffer); | |||||
bufPtr = tmp.Length; | |||||
return; | |||||
} | |||||
int len = ChunkDecrypt(outbuf.AsSpan(outlength), tmp); | |||||
if (len <= 0) | |||||
{ | |||||
tmp.CopyTo(sharedBuffer); | |||||
bufPtr = tmp.Length; | |||||
return; | |||||
} | |||||
tmp = tmp.Slice(ChunkLengthBytes + tagLen + len + tagLen); | |||||
// output to outbuf | |||||
outlength += len; | |||||
logger.Debug("aead dec outlength " + outlength); | |||||
if (outlength + 100 > TCPHandler.BufferSize) | |||||
{ | |||||
logger.Debug("dec outbuf almost full, giving up"); | |||||
tmp.CopyTo(sharedBuffer); | |||||
bufPtr = tmp.Length; | |||||
return; | |||||
} | |||||
bufSize = tmp.Length; | |||||
// check if we already done all of them | |||||
if (bufSize <= 0) | |||||
{ | |||||
logger.Debug("No data in _decCircularBuffer, already all done"); | |||||
return; | |||||
} | |||||
} | |||||
} | |||||
public override int Decrypt(Span<byte> plain, ReadOnlySpan<byte> cipher) | public override int Decrypt(Span<byte> plain, ReadOnlySpan<byte> cipher) | ||||
{ | { | ||||
int outlength = 0; | int outlength = 0; | ||||
@@ -416,16 +260,6 @@ namespace Shadowsocks.Encryption.AEAD | |||||
#endregion | #endregion | ||||
#region UDP | #region UDP | ||||
public override void EncryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength) | |||||
{ | |||||
// Generate salt | |||||
RNG.GetSpan(outbuf.AsSpan(0, saltLen)); | |||||
InitCipher(outbuf, true, true); | |||||
outlength = saltLen + CipherEncrypt( | |||||
buf.AsSpan(0, length), | |||||
outbuf.AsSpan(saltLen, length + tagLen)); | |||||
} | |||||
public override int EncryptUDP(ReadOnlySpan<byte> plain, Span<byte> cipher) | public override int EncryptUDP(ReadOnlySpan<byte> plain, Span<byte> cipher) | ||||
{ | { | ||||
RNG.GetSpan(cipher.Slice(0, saltLen)); | RNG.GetSpan(cipher.Slice(0, saltLen)); | ||||
@@ -433,12 +267,6 @@ namespace Shadowsocks.Encryption.AEAD | |||||
return saltLen + CipherEncrypt(plain, cipher.Slice(saltLen)); | return saltLen + CipherEncrypt(plain, cipher.Slice(saltLen)); | ||||
} | } | ||||
public override void DecryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength) | |||||
{ | |||||
InitCipher(buf, false, true); | |||||
outlength = CipherDecrypt(outbuf, buf.AsSpan(saltLen, length - saltLen)); | |||||
} | |||||
public override int DecryptUDP(Span<byte> plain, ReadOnlySpan<byte> cipher) | public override int DecryptUDP(Span<byte> plain, ReadOnlySpan<byte> cipher) | ||||
{ | { | ||||
InitCipher(cipher.Slice(0, saltLen).ToArray(), false, true); | InitCipher(cipher.Slice(0, saltLen).ToArray(), false, true); | ||||
@@ -33,14 +33,6 @@ namespace Shadowsocks.Encryption | |||||
protected string Method; | protected string Method; | ||||
protected string Password; | protected string Password; | ||||
public abstract void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength); | |||||
public abstract void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength); | |||||
public abstract void EncryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength); | |||||
public abstract void DecryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength); | |||||
public override string ToString() | public override string ToString() | ||||
{ | { | ||||
return $"{instanceId}({Method},{Password})"; | return $"{instanceId}({Method},{Password})"; | ||||
@@ -1,5 +1,4 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
namespace Shadowsocks.Encryption | namespace Shadowsocks.Encryption | ||||
{ | { | ||||
@@ -7,10 +6,6 @@ namespace Shadowsocks.Encryption | |||||
{ | { | ||||
/* length == -1 means not used */ | /* length == -1 means not used */ | ||||
int AddressBufferLength { set; get; } | int AddressBufferLength { set; get; } | ||||
void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength); | |||||
void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength); | |||||
void EncryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength); | |||||
void DecryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength); | |||||
int Encrypt(ReadOnlySpan<byte> plain, Span<byte> cipher); | int Encrypt(ReadOnlySpan<byte> plain, Span<byte> cipher); | ||||
int Decrypt(Span<byte> plain, ReadOnlySpan<byte> cipher); | int Decrypt(Span<byte> plain, ReadOnlySpan<byte> cipher); | ||||
int EncryptUDP(ReadOnlySpan<byte> plain, Span<byte> cipher); | int EncryptUDP(ReadOnlySpan<byte> plain, Span<byte> cipher); | ||||
@@ -1,7 +1,6 @@ | |||||
using NLog; | using NLog; | ||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Runtime.CompilerServices; | |||||
using System.Text; | using System.Text; | ||||
namespace Shadowsocks.Encryption.Stream | namespace Shadowsocks.Encryption.Stream | ||||
@@ -89,34 +88,6 @@ namespace Shadowsocks.Encryption.Stream | |||||
protected abstract int CipherDecrypt(Span<byte> plain, ReadOnlySpan<byte> cipher); | protected abstract int CipherDecrypt(Span<byte> plain, ReadOnlySpan<byte> cipher); | ||||
#region TCP | #region TCP | ||||
[MethodImpl(MethodImplOptions.Synchronized)] | |||||
public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength) | |||||
{ | |||||
int cipherOffset = 0; | |||||
Span<byte> tmp = buf.AsSpan(0, length); | |||||
logger.Trace($"{instanceId} encrypt TCP, generate iv: {!ivReady}"); | |||||
if (!ivReady) | |||||
{ | |||||
// Generate IV | |||||
byte[] ivBytes = RNG.GetBytes(ivLen); | |||||
initCipher(ivBytes, true); | |||||
Array.Copy(ivBytes, 0, outbuf, 0, ivLen); | |||||
cipherOffset = ivLen; | |||||
ivReady = true; | |||||
} | |||||
int size = tmp.Length; | |||||
byte[] cipher = new byte[size]; | |||||
CipherEncrypt(tmp, cipher); | |||||
logger.DumpBase64($"plain {instanceId}", tmp.ToArray(), size); | |||||
logger.DumpBase64($"cipher {instanceId}", cipher, cipher.Length); | |||||
logger.Dump($"iv {instanceId}", iv, ivLen); | |||||
Buffer.BlockCopy(cipher, 0, outbuf, cipherOffset, size); | |||||
outlength = size + cipherOffset; | |||||
} | |||||
public override int Encrypt(ReadOnlySpan<byte> plain, Span<byte> cipher) | public override int Encrypt(ReadOnlySpan<byte> plain, Span<byte> cipher) | ||||
{ | { | ||||
int cipherOffset = 0; | int cipherOffset = 0; | ||||
@@ -134,55 +105,12 @@ namespace Shadowsocks.Encryption.Stream | |||||
int clen = CipherEncrypt(plain, cipher); | int clen = CipherEncrypt(plain, cipher); | ||||
logger.DumpBase64($"plain {instanceId}", plain); | logger.DumpBase64($"plain {instanceId}", plain); | ||||
logger.DumpBase64($"cipher {instanceId}", cipher); | |||||
logger.DumpBase64($"cipher {instanceId}", cipher.Slice(0, clen)); | |||||
logger.Dump($"iv {instanceId}", iv, ivLen); | logger.Dump($"iv {instanceId}", iv, ivLen); | ||||
return clen + cipherOffset; | return clen + cipherOffset; | ||||
} | } | ||||
private int recieveCtr = 0; | private int recieveCtr = 0; | ||||
[MethodImpl(MethodImplOptions.Synchronized)] | |||||
public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength) | |||||
{ | |||||
Span<byte> tmp = buf.AsSpan(0, length); | |||||
logger.Trace($"{instanceId} decrypt TCP, read iv: {!ivReady}"); | |||||
// is first packet, need read iv | |||||
if (!ivReady) | |||||
{ | |||||
// push to buffer in case of not enough data | |||||
tmp.CopyTo(sharedBuffer.AsSpan(recieveCtr)); | |||||
recieveCtr += tmp.Length; | |||||
// not enough data for read iv, return 0 byte data | |||||
if (recieveCtr <= ivLen) | |||||
{ | |||||
outlength = 0; | |||||
return; | |||||
} | |||||
// start decryption | |||||
ivReady = true; | |||||
if (ivLen > 0) | |||||
{ | |||||
// read iv | |||||
byte[] iv = sharedBuffer.AsSpan(0, ivLen).ToArray(); | |||||
initCipher(iv, false); | |||||
} | |||||
else | |||||
{ | |||||
initCipher(Array.Empty<byte>(), false); | |||||
} | |||||
tmp = sharedBuffer.AsSpan(ivLen, recieveCtr - ivLen); | |||||
} | |||||
// read all data from buffer | |||||
CipherDecrypt(outbuf, tmp); | |||||
logger.DumpBase64($"cipher {instanceId}", tmp.ToArray(), tmp.Length); | |||||
logger.DumpBase64($"plain {instanceId}", outbuf, tmp.Length); | |||||
logger.Dump($"iv {instanceId}", iv, ivLen); | |||||
outlength = tmp.Length; | |||||
} | |||||
public override int Decrypt(Span<byte> plain, ReadOnlySpan<byte> cipher) | public override int Decrypt(Span<byte> plain, ReadOnlySpan<byte> cipher) | ||||
{ | { | ||||
Span<byte> tmp;// = cipher; | Span<byte> tmp;// = cipher; | ||||
@@ -220,7 +148,7 @@ namespace Shadowsocks.Encryption.Stream | |||||
// read all data from buffer | // read all data from buffer | ||||
int len = CipherDecrypt(plain, cipher.Slice(cipherOffset)); | int len = CipherDecrypt(plain, cipher.Slice(cipherOffset)); | ||||
logger.DumpBase64($"cipher {instanceId}", cipher.Slice(cipherOffset)); | logger.DumpBase64($"cipher {instanceId}", cipher.Slice(cipherOffset)); | ||||
logger.DumpBase64($"plain {instanceId}", plain); | |||||
logger.DumpBase64($"plain {instanceId}", plain.Slice(0, len)); | |||||
logger.Dump($"iv {instanceId}", iv, ivLen); | logger.Dump($"iv {instanceId}", iv, ivLen); | ||||
return len; | return len; | ||||
} | } | ||||
@@ -228,20 +156,6 @@ namespace Shadowsocks.Encryption.Stream | |||||
#endregion | #endregion | ||||
#region UDP | #region UDP | ||||
[MethodImpl(MethodImplOptions.Synchronized)] | |||||
public override void EncryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength) | |||||
{ | |||||
// Generate IV | |||||
RNG.GetBytes(outbuf, ivLen); | |||||
initCipher(outbuf, true); | |||||
lock (sharedBuffer) | |||||
{ | |||||
CipherEncrypt(buf, sharedBuffer); | |||||
outlength = length + ivLen; | |||||
Buffer.BlockCopy(sharedBuffer, 0, outbuf, ivLen, length); | |||||
} | |||||
} | |||||
public override int EncryptUDP(ReadOnlySpan<byte> plain, Span<byte> cipher) | public override int EncryptUDP(ReadOnlySpan<byte> plain, Span<byte> cipher) | ||||
{ | { | ||||
byte[] iv = RNG.GetBytes(ivLen); | byte[] iv = RNG.GetBytes(ivLen); | ||||
@@ -250,20 +164,6 @@ namespace Shadowsocks.Encryption.Stream | |||||
return ivLen + CipherEncrypt(plain, cipher.Slice(ivLen)); | return ivLen + CipherEncrypt(plain, cipher.Slice(ivLen)); | ||||
} | } | ||||
[MethodImpl(MethodImplOptions.Synchronized)] | |||||
public override void DecryptUDP(byte[] buf, int length, byte[] outbuf, out int outlength) | |||||
{ | |||||
// Get IV from first pos | |||||
initCipher(buf, false); | |||||
outlength = length - ivLen; | |||||
lock (sharedBuffer) | |||||
{ | |||||
// C# could be multi-threaded | |||||
Buffer.BlockCopy(buf, ivLen, sharedBuffer, 0, length - ivLen); | |||||
CipherDecrypt(outbuf, sharedBuffer); | |||||
} | |||||
} | |||||
public override int DecryptUDP(Span<byte> plain, ReadOnlySpan<byte> cipher) | public override int DecryptUDP(Span<byte> plain, ReadOnlySpan<byte> cipher) | ||||
{ | { | ||||
initCipher(cipher.Slice(0, ivLen).ToArray(), false); | initCipher(cipher.Slice(0, ivLen).ToArray(), false); | ||||