This commit is contained in:
2026-06-15 18:18:16 +08:00
parent 97c9fba14e
commit 2b9f134e5f
4164 changed files with 386922 additions and 79 deletions

View File

@@ -0,0 +1,221 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes
{
/**
* implements Cipher-Block-Chaining (CBC) mode on top of a simple cipher.
*/
public sealed class CbcBlockCipher
: IBlockCipherMode
{
private byte[] IV, cbcV, cbcNextV;
private int blockSize;
private IBlockCipher cipher;
private bool encrypting;
/**
* Basic constructor.
*
* @param cipher the block cipher to be used as the basis of chaining.
*/
public CbcBlockCipher(
IBlockCipher cipher)
{
this.cipher = cipher;
this.blockSize = cipher.GetBlockSize();
this.IV = new byte[blockSize];
this.cbcV = new byte[blockSize];
this.cbcNextV = new byte[blockSize];
}
/**
* return the underlying block cipher that we are wrapping.
*
* @return the underlying block cipher that we are wrapping.
*/
public IBlockCipher UnderlyingCipher => cipher;
/**
* Initialise the cipher and, possibly, the initialisation vector (IV).
* If an IV isn't passed as part of the parameter, the IV will be all zeros.
*
* @param forEncryption if true the cipher is initialised for
* encryption, if false for decryption.
* @param param the key and other data required by the cipher.
* @exception ArgumentException if the parameters argument is
* inappropriate.
*/
public void Init(bool forEncryption, ICipherParameters parameters)
{
bool oldEncrypting = this.encrypting;
this.encrypting = forEncryption;
if (parameters is ParametersWithIV ivParam)
{
byte[] iv = ivParam.GetIV();
if (iv.Length != blockSize)
throw new ArgumentException("initialisation vector must be the same length as block size");
Array.Copy(iv, 0, IV, 0, iv.Length);
parameters = ivParam.Parameters;
}
Reset();
// if null it's an IV changed only.
if (parameters != null)
{
cipher.Init(encrypting, parameters);
}
else if (oldEncrypting != encrypting)
{
throw new ArgumentException("cannot change encrypting state without providing key.");
}
}
/**
* return the algorithm name and mode.
*
* @return the name of the underlying algorithm followed by "/CBC".
*/
public string AlgorithmName
{
get { return cipher.AlgorithmName + "/CBC"; }
}
public bool IsPartialBlockOkay
{
get { return false; }
}
/**
* return the block size of the underlying cipher.
*
* @return the block size of the underlying cipher.
*/
public int GetBlockSize()
{
return cipher.GetBlockSize();
}
public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
{
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
return encrypting
? EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff))
: DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
#else
return encrypting
? EncryptBlock(input, inOff, output, outOff)
: DecryptBlock(input, inOff, output, outOff);
#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
{
return encrypting
? EncryptBlock(input, output)
: DecryptBlock(input, output);
}
#endif
/**
* reset the chaining vector back to the IV and reset the underlying
* cipher.
*/
public void Reset()
{
Array.Copy(IV, 0, cbcV, 0, IV.Length);
Array.Clear(cbcNextV, 0, cbcNextV.Length);
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
{
Check.DataLength(input, blockSize, "input buffer too short");
Check.OutputLength(output, blockSize, "output buffer too short");
for (int i = 0; i < blockSize; i++)
{
cbcV[i] ^= input[i];
}
int length = cipher.ProcessBlock(cbcV, output);
output[..blockSize].CopyTo(cbcV);
return length;
}
private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
{
Check.DataLength(input, blockSize, "input buffer too short");
Check.OutputLength(output, blockSize, "output buffer too short");
input[..blockSize].CopyTo(cbcNextV);
int length = cipher.ProcessBlock(input, output);
for (int i = 0; i < blockSize; i++)
{
output[i] ^= cbcV[i];
}
byte[] tmp = cbcV;
cbcV = cbcNextV;
cbcNextV = tmp;
return length;
}
#else
private int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
{
Check.DataLength(input, inOff, blockSize, "input buffer too short");
Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
for (int i = 0; i < blockSize; i++)
{
cbcV[i] ^= input[inOff + i];
}
int length = cipher.ProcessBlock(cbcV, 0, outBytes, outOff);
Array.Copy(outBytes, outOff, cbcV, 0, cbcV.Length);
return length;
}
private int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
{
Check.DataLength(input, inOff, blockSize, "input buffer too short");
Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
Array.Copy(input, inOff, cbcNextV, 0, blockSize);
int length = cipher.ProcessBlock(input, inOff, outBytes, outOff);
for (int i = 0; i < blockSize; i++)
{
outBytes[outOff + i] ^= cbcV[i];
}
byte[] tmp = cbcV;
cbcV = cbcNextV;
cbcNextV = tmp;
return length;
}
#endif
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 81829f4c01d07fb4e9361e5200b2ff07
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,659 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using System.IO;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Macs;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes
{
/**
* Implements the Counter with Cipher Block Chaining mode (CCM) detailed in
* NIST Special Publication 800-38C.
* <p>
* <b>Note</b>: this mode is a packet mode - it needs all the data up front.
* </p>
*/
public class CcmBlockCipher
: IAeadBlockCipher
{
private static readonly int BlockSize = 16;
private readonly IBlockCipher cipher;
private readonly byte[] macBlock;
private bool forEncryption;
private byte[] nonce;
private byte[] initialAssociatedText;
private int macSize;
private ICipherParameters keyParam;
private readonly MemoryStream associatedText = new MemoryStream();
private readonly MemoryStream data = new MemoryStream();
/**
* Basic constructor.
*
* @param cipher the block cipher to be used.
*/
public CcmBlockCipher(
IBlockCipher cipher)
{
this.cipher = cipher;
this.macBlock = new byte[BlockSize];
if (cipher.GetBlockSize() != BlockSize)
throw new ArgumentException("cipher required with a block size of " + BlockSize + ".");
}
/**
* return the underlying block cipher that we are wrapping.
*
* @return the underlying block cipher that we are wrapping.
*/
public virtual IBlockCipher UnderlyingCipher => cipher;
public virtual void Init(bool forEncryption, ICipherParameters parameters)
{
this.forEncryption = forEncryption;
ICipherParameters cipherParameters;
if (parameters is AeadParameters aeadParameters)
{
nonce = aeadParameters.GetNonce();
initialAssociatedText = aeadParameters.GetAssociatedText();
macSize = GetMacSize(forEncryption, aeadParameters.MacSize);
cipherParameters = aeadParameters.Key;
}
else if (parameters is ParametersWithIV parametersWithIV)
{
nonce = parametersWithIV.GetIV();
initialAssociatedText = null;
macSize = GetMacSize(forEncryption, 64);
cipherParameters = parametersWithIV.Parameters;
}
else
{
throw new ArgumentException("invalid parameters passed to CCM");
}
// NOTE: Very basic support for key re-use, but no performance gain from it
if (cipherParameters != null)
{
keyParam = cipherParameters;
}
if (nonce == null || nonce.Length < 7 || nonce.Length > 13)
throw new ArgumentException("nonce must have length from 7 to 13 octets");
Reset();
}
public virtual string AlgorithmName => cipher.AlgorithmName + "/CCM";
public virtual int GetBlockSize()
{
return cipher.GetBlockSize();
}
public virtual void ProcessAadByte(byte input)
{
associatedText.WriteByte(input);
}
public virtual void ProcessAadBytes(byte[] inBytes, int inOff, int len)
{
// TODO: Process AAD online
associatedText.Write(inBytes, inOff, len);
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public virtual void ProcessAadBytes(ReadOnlySpan<byte> input)
{
// TODO: Process AAD online
associatedText.Write(input);
}
#endif
public virtual int ProcessByte(byte input, byte[] outBytes, int outOff)
{
data.WriteByte(input);
return 0;
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public virtual int ProcessByte(byte input, Span<byte> output)
{
data.WriteByte(input);
return 0;
}
#endif
public virtual int ProcessBytes(byte[] inBytes, int inOff, int inLen, byte[] outBytes, int outOff)
{
Check.DataLength(inBytes, inOff, inLen, "input buffer too short");
data.Write(inBytes, inOff, inLen);
return 0;
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public virtual int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output)
{
data.Write(input);
return 0;
}
#endif
public virtual int DoFinal(byte[] outBytes, int outOff)
{
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
return DoFinal(outBytes.AsSpan(outOff));
#else
byte[] input = data.GetBuffer();
int inLen = Convert.ToInt32(data.Length);
int len = ProcessPacket(input, 0, inLen, outBytes, outOff);
Reset();
return len;
#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public virtual int DoFinal(Span<byte> output)
{
byte[] input = data.GetBuffer();
int inLen = Convert.ToInt32(data.Length);
int len = ProcessPacket(input.AsSpan(0, inLen), output);
Reset();
return len;
}
#endif
public virtual void Reset()
{
associatedText.SetLength(0);
data.SetLength(0);
}
/**
* Returns a byte array containing the mac calculated as part of the
* last encrypt or decrypt operation.
*
* @return the last mac calculated.
*/
public virtual byte[] GetMac()
{
return Arrays.CopyOfRange(macBlock, 0, macSize);
}
public virtual int GetUpdateOutputSize(int len)
{
return 0;
}
public virtual int GetOutputSize(int len)
{
int totalData = Convert.ToInt32(data.Length) + len;
if (forEncryption)
{
return totalData + macSize;
}
return totalData < macSize ? 0 : totalData - macSize;
}
/**
* Process a packet of data for either CCM decryption or encryption.
*
* @param in data for processing.
* @param inOff offset at which data starts in the input array.
* @param inLen length of the data in the input array.
* @return a byte array containing the processed input..
* @throws IllegalStateException if the cipher is not appropriately set up.
* @throws InvalidCipherTextException if the input data is truncated or the mac check fails.
*/
public virtual byte[] ProcessPacket(byte[] input, int inOff, int inLen)
{
byte[] output;
if (forEncryption)
{
output = new byte[inLen + macSize];
}
else
{
if (inLen < macSize)
throw new InvalidCipherTextException("data too short");
output = new byte[inLen - macSize];
}
ProcessPacket(input, inOff, inLen, output, 0);
return output;
}
/**
* Process a packet of data for either CCM decryption or encryption.
*
* @param in data for processing.
* @param inOff offset at which data starts in the input array.
* @param inLen length of the data in the input array.
* @param output output array.
* @param outOff offset into output array to start putting processed bytes.
* @return the number of bytes added to output.
* @throws IllegalStateException if the cipher is not appropriately set up.
* @throws InvalidCipherTextException if the input data is truncated or the mac check fails.
* @throws DataLengthException if output buffer too short.
*/
public virtual int ProcessPacket(byte[] input, int inOff, int inLen, byte[] output, int outOff)
{
// TODO: handle null keyParam (e.g. via RepeatedKeySpec)
// Need to keep the CTR and CBC Mac parts around and reset
if (keyParam == null)
throw new InvalidOperationException("CCM cipher unitialized.");
int n = nonce.Length;
int q = 15 - n;
if (q < 4)
{
int limitLen = 1 << (8 * q);
if (inLen >= limitLen)
throw new InvalidOperationException("CCM packet too large for choice of q.");
}
byte[] iv = new byte[BlockSize];
iv[0] = (byte)((q - 1) & 0x7);
nonce.CopyTo(iv, 1);
IBlockCipher ctrCipher = new SicBlockCipher(cipher);
ctrCipher.Init(forEncryption, new ParametersWithIV(keyParam, iv));
int outputLen;
int inIndex = inOff;
int outIndex = outOff;
if (forEncryption)
{
outputLen = inLen + macSize;
Check.OutputLength(output, outOff, outputLen, "Output buffer too short.");
CalculateMac(input, inOff, inLen, macBlock);
byte[] encMac = new byte[BlockSize];
ctrCipher.ProcessBlock(macBlock, 0, encMac, 0); // S0
while (inIndex < (inOff + inLen - BlockSize)) // S1...
{
ctrCipher.ProcessBlock(input, inIndex, output, outIndex);
outIndex += BlockSize;
inIndex += BlockSize;
}
byte[] block = new byte[BlockSize];
Array.Copy(input, inIndex, block, 0, inLen + inOff - inIndex);
ctrCipher.ProcessBlock(block, 0, block, 0);
Array.Copy(block, 0, output, outIndex, inLen + inOff - inIndex);
Array.Copy(encMac, 0, output, outOff + inLen, macSize);
}
else
{
if (inLen < macSize)
throw new InvalidCipherTextException("data too short");
outputLen = inLen - macSize;
Check.OutputLength(output, outOff, outputLen, "Output buffer too short.");
Array.Copy(input, inOff + outputLen, macBlock, 0, macSize);
ctrCipher.ProcessBlock(macBlock, 0, macBlock, 0);
for (int i = macSize; i != macBlock.Length; i++)
{
macBlock[i] = 0;
}
while (inIndex < (inOff + outputLen - BlockSize))
{
ctrCipher.ProcessBlock(input, inIndex, output, outIndex);
outIndex += BlockSize;
inIndex += BlockSize;
}
byte[] block = new byte[BlockSize];
Array.Copy(input, inIndex, block, 0, outputLen - (inIndex - inOff));
ctrCipher.ProcessBlock(block, 0, block, 0);
Array.Copy(block, 0, output, outIndex, outputLen - (inIndex - inOff));
byte[] calculatedMacBlock = new byte[BlockSize];
CalculateMac(output, outOff, outputLen, calculatedMacBlock);
if (!Arrays.ConstantTimeAreEqual(macBlock, calculatedMacBlock))
throw new InvalidCipherTextException("mac check in CCM failed");
}
return outputLen;
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public virtual int ProcessPacket(ReadOnlySpan<byte> input, Span<byte> output)
{
int inLen = input.Length;
// TODO: handle null keyParam (e.g. via RepeatedKeySpec)
// Need to keep the CTR and CBC Mac parts around and reset
if (keyParam == null)
throw new InvalidOperationException("CCM cipher unitialized.");
int n = nonce.Length;
int q = 15 - n;
if (q < 4)
{
int limitLen = 1 << (8 * q);
if (inLen >= limitLen)
throw new InvalidOperationException("CCM packet too large for choice of q.");
}
byte[] iv = new byte[BlockSize];
iv[0] = (byte)((q - 1) & 0x7);
nonce.CopyTo(iv, 1);
IBlockCipher ctrCipher = new SicBlockCipher(cipher);
ctrCipher.Init(forEncryption, new ParametersWithIV(keyParam, iv));
int outputLen;
int index = 0;
Span<byte> block = stackalloc byte[BlockSize];
if (forEncryption)
{
outputLen = inLen + macSize;
Check.OutputLength(output, outputLen, "output buffer too short");
CalculateMac(input, macBlock);
byte[] encMac = new byte[BlockSize];
ctrCipher.ProcessBlock(macBlock, encMac); // S0
while (index < (inLen - BlockSize)) // S1...
{
ctrCipher.ProcessBlock(input[index..], output[index..]);
index += BlockSize;
}
input[index..].CopyTo(block);
ctrCipher.ProcessBlock(block, block);
block[..(inLen - index)].CopyTo(output[index..]);
encMac.AsSpan(0, macSize).CopyTo(output[inLen..]);
}
else
{
if (inLen < macSize)
throw new InvalidCipherTextException("data too short");
outputLen = inLen - macSize;
Check.OutputLength(output, outputLen, "output buffer too short");
input[outputLen..].CopyTo(macBlock);
ctrCipher.ProcessBlock(macBlock, macBlock);
for (int i = macSize; i != macBlock.Length; i++)
{
macBlock[i] = 0;
}
while (index < (outputLen - BlockSize))
{
ctrCipher.ProcessBlock(input[index..], output[index..]);
index += BlockSize;
}
input[index..outputLen].CopyTo(block);
ctrCipher.ProcessBlock(block, block);
block[..(outputLen - index)].CopyTo(output[index..]);
Span<byte> calculatedMacBlock = stackalloc byte[BlockSize];
CalculateMac(output[..outputLen], calculatedMacBlock);
if (!Arrays.ConstantTimeAreEqual(macBlock, calculatedMacBlock))
throw new InvalidCipherTextException("mac check in CCM failed");
}
return outputLen;
}
#endif
private int CalculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock)
{
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
return CalculateMac(data.AsSpan(dataOff, dataLen), macBlock);
#else
IMac cMac = new CbcBlockCipherMac(cipher, macSize * 8);
cMac.Init(keyParam);
//
// build b0
//
byte[] b0 = new byte[16];
if (HasAssociatedText())
{
b0[0] |= 0x40;
}
b0[0] |= (byte)((((cMac.GetMacSize() - 2) / 2) & 0x7) << 3);
b0[0] |= (byte)(((15 - nonce.Length) - 1) & 0x7);
Array.Copy(nonce, 0, b0, 1, nonce.Length);
int q = dataLen;
int count = 1;
while (q > 0)
{
b0[b0.Length - count] = (byte)(q & 0xff);
q >>= 8;
count++;
}
cMac.BlockUpdate(b0, 0, b0.Length);
//
// process associated text
//
if (HasAssociatedText())
{
int extra;
int textLength = GetAssociatedTextLength();
if (textLength < ((1 << 16) - (1 << 8)))
{
cMac.Update((byte)(textLength >> 8));
cMac.Update((byte)textLength);
extra = 2;
}
else // can't go any higher than 2^32
{
cMac.Update((byte)0xff);
cMac.Update((byte)0xfe);
cMac.Update((byte)(textLength >> 24));
cMac.Update((byte)(textLength >> 16));
cMac.Update((byte)(textLength >> 8));
cMac.Update((byte)textLength);
extra = 6;
}
if (initialAssociatedText != null)
{
cMac.BlockUpdate(initialAssociatedText, 0, initialAssociatedText.Length);
}
if (associatedText.Length > 0)
{
byte[] input = associatedText.GetBuffer();
int len = Convert.ToInt32(associatedText.Length);
cMac.BlockUpdate(input, 0, len);
}
extra = (extra + textLength) % 16;
if (extra != 0)
{
for (int i = extra; i < 16; ++i)
{
cMac.Update((byte)0x00);
}
}
}
//
// add the text
//
cMac.BlockUpdate(data, dataOff, dataLen);
return cMac.DoFinal(macBlock, 0);
#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
private int CalculateMac(ReadOnlySpan<byte> data, Span<byte> macBlock)
{
IMac cMac = new CbcBlockCipherMac(cipher, macSize * 8);
cMac.Init(keyParam);
//
// build b0
//
byte[] b0 = new byte[16];
if (HasAssociatedText())
{
b0[0] |= 0x40;
}
b0[0] |= (byte)((((cMac.GetMacSize() - 2) / 2) & 0x7) << 3);
b0[0] |= (byte)(((15 - nonce.Length) - 1) & 0x7);
Array.Copy(nonce, 0, b0, 1, nonce.Length);
int q = data.Length;
int count = 1;
while (q > 0)
{
b0[b0.Length - count] = (byte)(q & 0xff);
q >>= 8;
count++;
}
cMac.BlockUpdate(b0, 0, b0.Length);
//
// process associated text
//
if (HasAssociatedText())
{
int extra;
int textLength = GetAssociatedTextLength();
if (textLength < ((1 << 16) - (1 << 8)))
{
cMac.Update((byte)(textLength >> 8));
cMac.Update((byte)textLength);
extra = 2;
}
else // can't go any higher than 2^32
{
cMac.Update((byte)0xff);
cMac.Update((byte)0xfe);
cMac.Update((byte)(textLength >> 24));
cMac.Update((byte)(textLength >> 16));
cMac.Update((byte)(textLength >> 8));
cMac.Update((byte)textLength);
extra = 6;
}
if (initialAssociatedText != null)
{
cMac.BlockUpdate(initialAssociatedText, 0, initialAssociatedText.Length);
}
if (associatedText.Length > 0)
{
byte[] input = associatedText.GetBuffer();
int len = Convert.ToInt32(associatedText.Length);
cMac.BlockUpdate(input, 0, len);
}
extra = (extra + textLength) % 16;
if (extra != 0)
{
for (int i = extra; i < 16; ++i)
{
cMac.Update((byte)0x00);
}
}
}
//
// add the text
//
cMac.BlockUpdate(data);
return cMac.DoFinal(macBlock);
}
#endif
private int GetMacSize(bool forEncryption, int requestedMacBits)
{
if (forEncryption && (requestedMacBits < 32 || requestedMacBits > 128 || 0 != (requestedMacBits & 15)))
throw new ArgumentException("tag length in octets must be one of {4,6,8,10,12,14,16}");
return requestedMacBits >> 3;
}
private int GetAssociatedTextLength()
{
return Convert.ToInt32(associatedText.Length) +
(initialAssociatedText == null ? 0 : initialAssociatedText.Length);
}
private bool HasAssociatedText()
{
return GetAssociatedTextLength() > 0;
}
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8187e045871283546af80d98e405ac4a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,230 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes
{
/**
* implements a Cipher-FeedBack (CFB) mode on top of a simple cipher.
*/
public class CfbBlockCipher
: IBlockCipherMode
{
private byte[] IV;
private byte[] cfbV;
private byte[] cfbOutV;
private bool encrypting;
private readonly int blockSize;
private readonly IBlockCipher cipher;
/**
* Basic constructor.
*
* @param cipher the block cipher to be used as the basis of the
* feedback mode.
* @param blockSize the block size in bits (note: a multiple of 8)
*/
public CfbBlockCipher(
IBlockCipher cipher,
int bitBlockSize)
{
if (bitBlockSize < 8 || (bitBlockSize & 7) != 0)
throw new ArgumentException("CFB" + bitBlockSize + " not supported", "bitBlockSize");
this.cipher = cipher;
this.blockSize = bitBlockSize / 8;
this.IV = new byte[cipher.GetBlockSize()];
this.cfbV = new byte[cipher.GetBlockSize()];
this.cfbOutV = new byte[cipher.GetBlockSize()];
}
/**
* return the underlying block cipher that we are wrapping.
*
* @return the underlying block cipher that we are wrapping.
*/
public IBlockCipher UnderlyingCipher => cipher;
/**
* Initialise the cipher and, possibly, the initialisation vector (IV).
* If an IV isn't passed as part of the parameter, the IV will be all zeros.
* An IV which is too short is handled in FIPS compliant fashion.
*
* @param forEncryption if true the cipher is initialised for
* encryption, if false for decryption.
* @param param the key and other data required by the cipher.
* @exception ArgumentException if the parameters argument is
* inappropriate.
*/
public void Init(
bool forEncryption,
ICipherParameters parameters)
{
this.encrypting = forEncryption;
if (parameters is ParametersWithIV)
{
ParametersWithIV ivParam = (ParametersWithIV) parameters;
byte[] iv = ivParam.GetIV();
int diff = IV.Length - iv.Length;
Array.Copy(iv, 0, IV, diff, iv.Length);
Array.Clear(IV, 0, diff);
parameters = ivParam.Parameters;
}
Reset();
// if it's null, key is to be reused.
if (parameters != null)
{
cipher.Init(true, parameters);
}
}
/**
* return the algorithm name and mode.
*
* @return the name of the underlying algorithm followed by "/CFB"
* and the block size in bits.
*/
public string AlgorithmName
{
get { return cipher.AlgorithmName + "/CFB" + (blockSize * 8); }
}
public bool IsPartialBlockOkay
{
get { return true; }
}
/**
* return the block size we are operating at.
*
* @return the block size we are operating at (in bytes).
*/
public int GetBlockSize()
{
return blockSize;
}
public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
{
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
return encrypting
? EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff))
: DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
#else
return encrypting
? EncryptBlock(input, inOff, output, outOff)
: DecryptBlock(input, inOff, output, outOff);
#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
{
return encrypting
? EncryptBlock(input, output)
: DecryptBlock(input, output);
}
#endif
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
{
Check.DataLength(input, blockSize, "input buffer too short");
Check.OutputLength(output, blockSize, "output buffer too short");
cipher.ProcessBlock(cfbV, cfbOutV);
//
// XOR the cfbV with the plaintext producing the ciphertext
//
for (int i = 0; i < blockSize; i++)
{
output[i] = (byte)(cfbOutV[i] ^ input[i]);
}
//
// change over the input block.
//
Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize);
output[..blockSize].CopyTo(cfbV.AsSpan(cfbV.Length - blockSize));
return blockSize;
}
private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
{
Check.DataLength(input, blockSize, "input buffer too short");
Check.OutputLength(output, blockSize, "output buffer too short");
cipher.ProcessBlock(cfbV, 0, cfbOutV, 0);
//
// change over the input block.
//
Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize);
input[..blockSize].CopyTo(cfbV.AsSpan(cfbV.Length - blockSize));
//
// XOR the cfbV with the ciphertext producing the plaintext
//
for (int i = 0; i < blockSize; i++)
{
output[i] = (byte)(cfbOutV[i] ^ input[i]);
}
return blockSize;
}
#else
private int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
{
Check.DataLength(input, inOff, blockSize, "input buffer too short");
Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
cipher.ProcessBlock(cfbV, 0, cfbOutV, 0);
//
// XOR the cfbV with the plaintext producing the ciphertext
//
for (int i = 0; i < blockSize; i++)
{
outBytes[outOff + i] = (byte)(cfbOutV[i] ^ input[inOff + i]);
}
//
// change over the input block.
//
Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize);
Array.Copy(outBytes, outOff, cfbV, cfbV.Length - blockSize, blockSize);
return blockSize;
}
private int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
{
Check.DataLength(input, inOff, blockSize, "input buffer too short");
Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
cipher.ProcessBlock(cfbV, 0, cfbOutV, 0);
//
// change over the input block.
//
Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize);
Array.Copy(input, inOff, cfbV, cfbV.Length - blockSize, blockSize);
//
// XOR the cfbV with the ciphertext producing the plaintext
//
for (int i = 0; i < blockSize; i++)
{
outBytes[outOff + i] = (byte)(cfbOutV[i] ^ input[inOff + i]);
}
return blockSize;
}
#endif
/**
* reset the chaining vector back to the IV and reset the underlying
* cipher.
*/
public void Reset()
{
Array.Copy(IV, 0, cfbV, 0, IV.Length);
}
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8bbdd28dbb8de4d458aa27f9def26c73
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,905 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Engines;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Macs;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes
{
public class ChaCha20Poly1305
: IAeadCipher
{
private enum State
{
Uninitialized = 0,
EncInit = 1,
EncAad = 2,
EncData = 3,
EncFinal = 4,
DecInit = 5,
DecAad = 6,
DecData = 7,
DecFinal = 8,
}
private const int BufSize = 64;
private const int KeySize = 32;
private const int NonceSize = 12;
private const int MacSize = 16;
private static readonly byte[] Zeroes = new byte[MacSize - 1];
private const ulong AadLimit = ulong.MaxValue;
private const ulong DataLimit = ((1UL << 32) - 1) * 64;
private readonly ChaCha7539Engine mChacha20;
private readonly IMac mPoly1305;
private readonly byte[] mKey = new byte[KeySize];
private readonly byte[] mNonce = new byte[NonceSize];
private readonly byte[] mBuf = new byte[BufSize + MacSize];
private readonly byte[] mMac = new byte[MacSize];
private byte[] mInitialAad;
private ulong mAadCount;
private ulong mDataCount;
private State mState = State.Uninitialized;
private int mBufPos;
public ChaCha20Poly1305()
: this(new Poly1305())
{
}
public ChaCha20Poly1305(IMac poly1305)
{
if (null == poly1305)
throw new ArgumentNullException("poly1305");
if (MacSize != poly1305.GetMacSize())
throw new ArgumentException("must be a 128-bit MAC", "poly1305");
this.mChacha20 = new ChaCha7539Engine();
this.mPoly1305 = poly1305;
}
public virtual string AlgorithmName
{
get { return "ChaCha20Poly1305"; }
}
public virtual void Init(bool forEncryption, ICipherParameters parameters)
{
KeyParameter initKeyParam;
byte[] initNonce;
ICipherParameters chacha20Params;
if (parameters is AeadParameters)
{
AeadParameters aeadParams = (AeadParameters)parameters;
int macSizeBits = aeadParams.MacSize;
if ((MacSize * 8) != macSizeBits)
throw new ArgumentException("Invalid value for MAC size: " + macSizeBits);
initKeyParam = aeadParams.Key;
initNonce = aeadParams.GetNonce();
chacha20Params = new ParametersWithIV(initKeyParam, initNonce);
this.mInitialAad = aeadParams.GetAssociatedText();
}
else if (parameters is ParametersWithIV)
{
ParametersWithIV ivParams = (ParametersWithIV)parameters;
initKeyParam = (KeyParameter)ivParams.Parameters;
initNonce = ivParams.GetIV();
chacha20Params = ivParams;
this.mInitialAad = null;
}
else
{
throw new ArgumentException("invalid parameters passed to ChaCha20Poly1305", "parameters");
}
// Validate key
if (null == initKeyParam)
{
if (State.Uninitialized == mState)
throw new ArgumentException("Key must be specified in initial init");
}
else
{
if (KeySize != initKeyParam.GetKey().Length)
throw new ArgumentException("Key must be 256 bits");
}
// Validate nonce
if (null == initNonce || NonceSize != initNonce.Length)
throw new ArgumentException("Nonce must be 96 bits");
// Check for encryption with reused nonce
if (State.Uninitialized != mState && forEncryption && Arrays.AreEqual(mNonce, initNonce))
{
if (null == initKeyParam || Arrays.AreEqual(mKey, initKeyParam.GetKey()))
throw new ArgumentException("cannot reuse nonce for ChaCha20Poly1305 encryption");
}
if (null != initKeyParam)
{
Array.Copy(initKeyParam.GetKey(), 0, mKey, 0, KeySize);
}
Array.Copy(initNonce, 0, mNonce, 0, NonceSize);
mChacha20.Init(true, chacha20Params);
this.mState = forEncryption ? State.EncInit : State.DecInit;
Reset(true, false);
}
public virtual int GetOutputSize(int len)
{
int total = System.Math.Max(0, len) + mBufPos;
switch (mState)
{
case State.DecInit:
case State.DecAad:
case State.DecData:
return System.Math.Max(0, total - MacSize);
case State.EncInit:
case State.EncAad:
case State.EncData:
return total + MacSize;
default:
throw new InvalidOperationException();
}
}
public virtual int GetUpdateOutputSize(int len)
{
int total = System.Math.Max(0, len) + mBufPos;
switch (mState)
{
case State.DecInit:
case State.DecAad:
case State.DecData:
total = System.Math.Max(0, total - MacSize);
break;
case State.EncInit:
case State.EncAad:
case State.EncData:
break;
default:
throw new InvalidOperationException();
}
return total - (total % BufSize);
}
public virtual void ProcessAadByte(byte input)
{
CheckAad();
this.mAadCount = IncrementCount(mAadCount, 1, AadLimit);
mPoly1305.Update(input);
}
public virtual void ProcessAadBytes(byte[] inBytes, int inOff, int len)
{
if (null == inBytes)
throw new ArgumentNullException("inBytes");
if (inOff < 0)
throw new ArgumentException("cannot be negative", "inOff");
if (len < 0)
throw new ArgumentException("cannot be negative", "len");
Check.DataLength(inBytes, inOff, len, "input buffer too short");
CheckAad();
if (len > 0)
{
this.mAadCount = IncrementCount(mAadCount, (uint)len, AadLimit);
mPoly1305.BlockUpdate(inBytes, inOff, len);
}
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public virtual void ProcessAadBytes(ReadOnlySpan<byte> input)
{
CheckAad();
if (!input.IsEmpty)
{
this.mAadCount = IncrementCount(mAadCount, (uint)input.Length, AadLimit);
mPoly1305.BlockUpdate(input);
}
}
#endif
public virtual int ProcessByte(byte input, byte[] outBytes, int outOff)
{
CheckData();
switch (mState)
{
case State.DecData:
{
mBuf[mBufPos] = input;
if (++mBufPos == mBuf.Length)
{
mPoly1305.BlockUpdate(mBuf, 0, BufSize);
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
ProcessBlock(mBuf, outBytes.AsSpan(outOff));
#else
ProcessBlock(mBuf, 0, outBytes, outOff);
#endif
Array.Copy(mBuf, BufSize, mBuf, 0, MacSize);
this.mBufPos = MacSize;
return BufSize;
}
return 0;
}
case State.EncData:
{
mBuf[mBufPos] = input;
if (++mBufPos == BufSize)
{
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
ProcessBlock(mBuf, outBytes.AsSpan(outOff));
#else
ProcessBlock(mBuf, 0, outBytes, outOff);
#endif
mPoly1305.BlockUpdate(outBytes, outOff, BufSize);
this.mBufPos = 0;
return BufSize;
}
return 0;
}
default:
throw new InvalidOperationException();
}
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public virtual int ProcessByte(byte input, Span<byte> output)
{
CheckData();
switch (mState)
{
case State.DecData:
{
mBuf[mBufPos] = input;
if (++mBufPos == mBuf.Length)
{
mPoly1305.BlockUpdate(mBuf.AsSpan(0, BufSize));
ProcessBlock(mBuf, output);
Array.Copy(mBuf, BufSize, mBuf, 0, MacSize);
this.mBufPos = MacSize;
return BufSize;
}
return 0;
}
case State.EncData:
{
mBuf[mBufPos] = input;
if (++mBufPos == BufSize)
{
ProcessBlock(mBuf, output);
mPoly1305.BlockUpdate(output[..BufSize]);
this.mBufPos = 0;
return BufSize;
}
return 0;
}
default:
throw new InvalidOperationException();
}
}
#endif
public virtual int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff)
{
if (null == inBytes)
throw new ArgumentNullException("inBytes");
/*
* Following bc-java, we allow null when no output is expected (e.g. based on a
* GetUpdateOutputSize call).
*/
if (null == outBytes)
{
//throw new ArgumentNullException("outBytes");
}
if (inOff < 0)
throw new ArgumentException("cannot be negative", "inOff");
if (len < 0)
throw new ArgumentException("cannot be negative", "len");
Check.DataLength(inBytes, inOff, len, "input buffer too short");
if (outOff < 0)
throw new ArgumentException("cannot be negative", "outOff");
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
return ProcessBytes(inBytes.AsSpan(inOff, len), Spans.FromNullable(outBytes, outOff));
#else
CheckData();
int resultLen = 0;
switch (mState)
{
case State.DecData:
{
int available = mBuf.Length - mBufPos;
if (len < available)
{
Array.Copy(inBytes, inOff, mBuf, mBufPos, len);
mBufPos += len;
break;
}
if (mBufPos >= BufSize)
{
mPoly1305.BlockUpdate(mBuf, 0, BufSize);
ProcessBlock(mBuf, 0, outBytes, outOff);
Array.Copy(mBuf, BufSize, mBuf, 0, mBufPos -= BufSize);
resultLen = BufSize;
available += BufSize;
if (len < available)
{
Array.Copy(inBytes, inOff, mBuf, mBufPos, len);
mBufPos += len;
break;
}
}
int inLimit1 = inOff + len - mBuf.Length;
int inLimit2 = inLimit1 - BufSize;
available = BufSize - mBufPos;
Array.Copy(inBytes, inOff, mBuf, mBufPos, available);
mPoly1305.BlockUpdate(mBuf, 0, BufSize);
ProcessBlock(mBuf, 0, outBytes, outOff + resultLen);
inOff += available;
resultLen += BufSize;
while (inOff <= inLimit2)
{
mPoly1305.BlockUpdate(inBytes, inOff, BufSize * 2);
ProcessBlocks2(inBytes, inOff, outBytes, outOff + resultLen);
inOff += BufSize * 2;
resultLen += BufSize * 2;
}
if (inOff <= inLimit1)
{
mPoly1305.BlockUpdate(inBytes, inOff, BufSize);
ProcessBlock(inBytes, inOff, outBytes, outOff + resultLen);
inOff += BufSize;
resultLen += BufSize;
}
mBufPos = mBuf.Length + inLimit1 - inOff;
Array.Copy(inBytes, inOff, mBuf, 0, mBufPos);
break;
}
case State.EncData:
{
int available = BufSize - mBufPos;
if (len < available)
{
Array.Copy(inBytes, inOff, mBuf, mBufPos, len);
mBufPos += len;
break;
}
int inLimit1 = inOff + len - BufSize;
int inLimit2 = inLimit1 - BufSize;
if (mBufPos > 0)
{
Array.Copy(inBytes, inOff, mBuf, mBufPos, available);
ProcessBlock(mBuf, 0, outBytes, outOff);
inOff += available;
resultLen = BufSize;
}
while (inOff <= inLimit2)
{
ProcessBlocks2(inBytes, inOff, outBytes, outOff + resultLen);
inOff += BufSize * 2;
resultLen += BufSize * 2;
}
if (inOff <= inLimit1)
{
ProcessBlock(inBytes, inOff, outBytes, outOff + resultLen);
inOff += BufSize;
resultLen += BufSize;
}
mPoly1305.BlockUpdate(outBytes, outOff, resultLen);
mBufPos = BufSize + inLimit1 - inOff;
Array.Copy(inBytes, inOff, mBuf, 0, mBufPos);
break;
}
default:
throw new InvalidOperationException();
}
return resultLen;
#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public virtual int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output)
{
CheckData();
int resultLen = 0;
switch (mState)
{
case State.DecData:
{
int available = mBuf.Length - mBufPos;
if (input.Length < available)
{
input.CopyTo(mBuf.AsSpan(mBufPos));
mBufPos += input.Length;
break;
}
if (mBufPos >= BufSize)
{
mPoly1305.BlockUpdate(mBuf.AsSpan(0, BufSize));
ProcessBlock(mBuf, output);
Array.Copy(mBuf, BufSize, mBuf, 0, mBufPos -= BufSize);
resultLen = BufSize;
available += BufSize;
if (input.Length < available)
{
input.CopyTo(mBuf.AsSpan(mBufPos));
mBufPos += input.Length;
break;
}
}
int inLimit1 = mBuf.Length;
int inLimit2 = inLimit1 + BufSize;
available = BufSize - mBufPos;
input[..available].CopyTo(mBuf.AsSpan(mBufPos));
mPoly1305.BlockUpdate(mBuf.AsSpan(0, BufSize));
ProcessBlock(mBuf, output[resultLen..]);
input = input[available..];
resultLen += BufSize;
while (input.Length >= inLimit2)
{
mPoly1305.BlockUpdate(input[..(BufSize * 2)]);
ProcessBlocks2(input, output[resultLen..]);
input = input[(BufSize * 2)..];
resultLen += BufSize * 2;
}
if (input.Length >= inLimit1)
{
mPoly1305.BlockUpdate(input[..BufSize]);
ProcessBlock(input, output[resultLen..]);
input = input[BufSize..];
resultLen += BufSize;
}
mBufPos = input.Length;
input.CopyTo(mBuf);
break;
}
case State.EncData:
{
int available = BufSize - mBufPos;
if (input.Length < available)
{
input.CopyTo(mBuf.AsSpan(mBufPos));
mBufPos += input.Length;
break;
}
if (mBufPos > 0)
{
input[..available].CopyTo(mBuf.AsSpan(mBufPos));
ProcessBlock(mBuf, output);
input = input[available..];
resultLen = BufSize;
}
while (input.Length >= BufSize * 2)
{
ProcessBlocks2(input, output[resultLen..]);
input = input[(BufSize * 2)..];
resultLen += BufSize * 2;
}
if (input.Length >= BufSize)
{
ProcessBlock(input, output[resultLen..]);
input = input[BufSize..];
resultLen += BufSize;
}
mPoly1305.BlockUpdate(output[..resultLen]);
mBufPos = input.Length;
input.CopyTo(mBuf);
break;
}
default:
throw new InvalidOperationException();
}
return resultLen;
}
#endif
public virtual int DoFinal(byte[] outBytes, int outOff)
{
if (null == outBytes)
throw new ArgumentNullException("outBytes");
if (outOff < 0)
throw new ArgumentException("cannot be negative", "outOff");
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
return DoFinal(outBytes.AsSpan(outOff));
#else
CheckData();
Array.Clear(mMac, 0, MacSize);
int resultLen = 0;
switch (mState)
{
case State.DecData:
{
if (mBufPos < MacSize)
throw new InvalidCipherTextException("data too short");
resultLen = mBufPos - MacSize;
Check.OutputLength(outBytes, outOff, resultLen, "output buffer too short");
if (resultLen > 0)
{
mPoly1305.BlockUpdate(mBuf, 0, resultLen);
ProcessData(mBuf, 0, resultLen, outBytes, outOff);
}
FinishData(State.DecFinal);
if (!Arrays.ConstantTimeAreEqual(MacSize, mMac, 0, mBuf, resultLen))
throw new InvalidCipherTextException("mac check in ChaCha20Poly1305 failed");
break;
}
case State.EncData:
{
resultLen = mBufPos + MacSize;
Check.OutputLength(outBytes, outOff, resultLen, "output buffer too short");
if (mBufPos > 0)
{
ProcessData(mBuf, 0, mBufPos, outBytes, outOff);
mPoly1305.BlockUpdate(outBytes, outOff, mBufPos);
}
FinishData(State.EncFinal);
Array.Copy(mMac, 0, outBytes, outOff + mBufPos, MacSize);
break;
}
default:
throw new InvalidOperationException();
}
Reset(false, true);
return resultLen;
#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public virtual int DoFinal(Span<byte> output)
{
CheckData();
Array.Clear(mMac, 0, MacSize);
int resultLen = 0;
switch (mState)
{
case State.DecData:
{
if (mBufPos < MacSize)
throw new InvalidCipherTextException("data too short");
resultLen = mBufPos - MacSize;
Check.OutputLength(output, resultLen, "output buffer too short");
if (resultLen > 0)
{
mPoly1305.BlockUpdate(mBuf, 0, resultLen);
ProcessData(mBuf.AsSpan(0, resultLen), output);
}
FinishData(State.DecFinal);
if (!Arrays.ConstantTimeAreEqual(MacSize, mMac, 0, mBuf, resultLen))
throw new InvalidCipherTextException("mac check in ChaCha20Poly1305 failed");
break;
}
case State.EncData:
{
resultLen = mBufPos + MacSize;
Check.OutputLength(output, resultLen, "output buffer too short");
if (mBufPos > 0)
{
ProcessData(mBuf.AsSpan(0, mBufPos), output);
mPoly1305.BlockUpdate(output[..mBufPos]);
}
FinishData(State.EncFinal);
mMac.AsSpan(0, MacSize).CopyTo(output[mBufPos..]);
break;
}
default:
throw new InvalidOperationException();
}
Reset(false, true);
return resultLen;
}
#endif
public virtual byte[] GetMac()
{
return Arrays.Clone(mMac);
}
public virtual void Reset()
{
Reset(true, true);
}
private void CheckAad()
{
switch (mState)
{
case State.DecInit:
this.mState = State.DecAad;
break;
case State.EncInit:
this.mState = State.EncAad;
break;
case State.DecAad:
case State.EncAad:
break;
case State.EncFinal:
throw new InvalidOperationException("ChaCha20Poly1305 cannot be reused for encryption");
default:
throw new InvalidOperationException();
}
}
private void CheckData()
{
switch (mState)
{
case State.DecInit:
case State.DecAad:
FinishAad(State.DecData);
break;
case State.EncInit:
case State.EncAad:
FinishAad(State.EncData);
break;
case State.DecData:
case State.EncData:
break;
case State.EncFinal:
throw new InvalidOperationException("ChaCha20Poly1305 cannot be reused for encryption");
default:
throw new InvalidOperationException();
}
}
private void FinishAad(State nextState)
{
PadMac(mAadCount);
this.mState = nextState;
}
private void FinishData(State nextState)
{
PadMac(mDataCount);
byte[] lengths = new byte[16];
Pack.UInt64_To_LE(mAadCount, lengths, 0);
Pack.UInt64_To_LE(mDataCount, lengths, 8);
mPoly1305.BlockUpdate(lengths, 0, 16);
mPoly1305.DoFinal(mMac, 0);
this.mState = nextState;
}
private ulong IncrementCount(ulong count, uint increment, ulong limit)
{
if (count > (limit - increment))
throw new InvalidOperationException ("Limit exceeded");
return count + increment;
}
private void InitMac()
{
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
Span<byte> firstBlock = stackalloc byte[64];
try
{
mChacha20.ProcessBytes(firstBlock, firstBlock);
mPoly1305.Init(new KeyParameter(firstBlock[..32]));
}
finally
{
firstBlock.Fill(0x00);
}
#else
byte[] firstBlock = new byte[64];
try
{
mChacha20.ProcessBytes(firstBlock, 0, 64, firstBlock, 0);
mPoly1305.Init(new KeyParameter(firstBlock, 0, 32));
}
finally
{
Array.Clear(firstBlock, 0, 64);
}
#endif
}
private void PadMac(ulong count)
{
int partial = (int)count & (MacSize - 1);
if (0 != partial)
{
mPoly1305.BlockUpdate(Zeroes, 0, MacSize - partial);
}
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
private void ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
{
Check.OutputLength(output, 64, "output buffer too short");
mChacha20.ProcessBlock(input, output);
this.mDataCount = IncrementCount(mDataCount, 64U, DataLimit);
}
private void ProcessBlocks2(ReadOnlySpan<byte> input, Span<byte> output)
{
Check.OutputLength(output, 128, "output buffer too short");
mChacha20.ProcessBlocks2(input, output);
this.mDataCount = IncrementCount(mDataCount, 128U, DataLimit);
}
private void ProcessData(ReadOnlySpan<byte> input, Span<byte> output)
{
Check.OutputLength(output, input.Length, "output buffer too short");
mChacha20.ProcessBytes(input, output);
this.mDataCount = IncrementCount(mDataCount, (uint)input.Length, DataLimit);
}
#else
private void ProcessBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff)
{
Check.OutputLength(outBytes, outOff, 64, "output buffer too short");
mChacha20.ProcessBlock(inBytes, inOff, outBytes, outOff);
this.mDataCount = IncrementCount(mDataCount, 64U, DataLimit);
}
private void ProcessBlocks2(byte[] inBytes, int inOff, byte[] outBytes, int outOff)
{
Check.OutputLength(outBytes, outOff, 128, "output buffer too short");
mChacha20.ProcessBlocks2(inBytes, inOff, outBytes, outOff);
this.mDataCount = IncrementCount(mDataCount, 128U, DataLimit);
}
private void ProcessData(byte[] inBytes, int inOff, int inLen, byte[] outBytes, int outOff)
{
Check.OutputLength(outBytes, outOff, inLen, "output buffer too short");
mChacha20.ProcessBytes(inBytes, inOff, inLen, outBytes, outOff);
this.mDataCount = IncrementCount(mDataCount, (uint)inLen, DataLimit);
}
#endif
private void Reset(bool clearMac, bool resetCipher)
{
Array.Clear(mBuf, 0, mBuf.Length);
if (clearMac)
{
Array.Clear(mMac, 0, mMac.Length);
}
this.mAadCount = 0UL;
this.mDataCount = 0UL;
this.mBufPos = 0;
switch (mState)
{
case State.DecInit:
case State.EncInit:
break;
case State.DecAad:
case State.DecData:
case State.DecFinal:
this.mState = State.DecInit;
break;
case State.EncAad:
case State.EncData:
case State.EncFinal:
this.mState = State.EncFinal;
return;
default:
throw new InvalidOperationException();
}
if (resetCipher)
{
mChacha20.Reset();
}
InitMac();
if (null != mInitialAad)
{
ProcessAadBytes(mInitialAad, 0, mInitialAad.Length);
}
}
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2cd8d80304a90214292a351a3fe3e2b2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,353 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using System.Diagnostics;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes
{
/**
* A Cipher Text Stealing (CTS) mode cipher. CTS allows block ciphers to
* be used to produce cipher text which is the same outLength as the plain text.
*/
public class CtsBlockCipher
: BufferedBlockCipher
{
private readonly int blockSize;
public CtsBlockCipher(IBlockCipher cipher)
: this(EcbBlockCipher.GetBlockCipherMode(cipher))
{
}
/**
* Create a buffered block cipher that uses Cipher Text Stealing
*
* @param cipher the underlying block cipher this buffering object wraps.
*/
public CtsBlockCipher(IBlockCipherMode cipherMode)
{
if (!(cipherMode is CbcBlockCipher || cipherMode is EcbBlockCipher))
throw new ArgumentException("CtsBlockCipher can only accept ECB, or CBC ciphers");
m_cipherMode = cipherMode;
blockSize = cipherMode.GetBlockSize();
buf = new byte[blockSize * 2];
bufOff = 0;
}
/**
* return the size of the output buffer required for an update of 'length' bytes.
*
* @param length the outLength of the input.
* @return the space required to accommodate a call to update
* with length bytes of input.
*/
public override int GetUpdateOutputSize(
int length)
{
int total = length + bufOff;
int leftOver = total % buf.Length;
if (leftOver == 0)
{
return total - buf.Length;
}
return total - leftOver;
}
/**
* return the size of the output buffer required for an update plus a
* doFinal with an input of length bytes.
*
* @param length the outLength of the input.
* @return the space required to accommodate a call to update and doFinal
* with length bytes of input.
*/
public override int GetOutputSize(
int length)
{
return length + bufOff;
}
/**
* process a single byte, producing an output block if necessary.
*
* @param in the input byte.
* @param out the space for any output that might be produced.
* @param outOff the offset from which the output will be copied.
* @return the number of output bytes copied to out.
* @exception DataLengthException if there isn't enough space in out.
* @exception InvalidOperationException if the cipher isn't initialised.
*/
public override int ProcessByte(byte input, byte[] output, int outOff)
{
int resultLen = 0;
if (bufOff == buf.Length)
{
resultLen = m_cipherMode.ProcessBlock(buf, 0, output, outOff);
Debug.Assert(resultLen == blockSize);
Array.Copy(buf, blockSize, buf, 0, blockSize);
bufOff = blockSize;
}
buf[bufOff++] = input;
return resultLen;
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public override int ProcessByte(byte input, Span<byte> output)
{
int resultLen = 0;
if (bufOff == buf.Length)
{
resultLen = m_cipherMode.ProcessBlock(buf, output);
Debug.Assert(resultLen == blockSize);
Array.Copy(buf, blockSize, buf, 0, blockSize);
bufOff = blockSize;
}
buf[bufOff++] = input;
return resultLen;
}
#endif
/**
* process an array of bytes, producing output if necessary.
*
* @param in the input byte array.
* @param inOff the offset at which the input data starts.
* @param length the number of bytes to be copied out of the input array.
* @param out the space for any output that might be produced.
* @param outOff the offset from which the output will be copied.
* @return the number of output bytes copied to out.
* @exception DataLengthException if there isn't enough space in out.
* @exception InvalidOperationException if the cipher isn't initialised.
*/
public override int ProcessBytes(byte[] input, int inOff, int length, byte[] output, int outOff)
{
if (length < 0)
throw new ArgumentException("Can't have a negative input length!");
int blockSize = GetBlockSize();
int outLength = GetUpdateOutputSize(length);
if (outLength > 0)
{
Check.OutputLength(output, outOff, outLength, "output buffer too short");
}
int resultLen = 0;
int gapLen = buf.Length - bufOff;
if (length > gapLen)
{
Array.Copy(input, inOff, buf, bufOff, gapLen);
resultLen = m_cipherMode.ProcessBlock(buf, 0, output, outOff);
Array.Copy(buf, blockSize, buf, 0, blockSize);
bufOff = blockSize;
length -= gapLen;
inOff += gapLen;
while (length > blockSize)
{
Array.Copy(input, inOff, buf, bufOff, blockSize);
resultLen += m_cipherMode.ProcessBlock(buf, 0, output, outOff + resultLen);
Array.Copy(buf, blockSize, buf, 0, blockSize);
length -= blockSize;
inOff += blockSize;
}
}
Array.Copy(input, inOff, buf, bufOff, length);
bufOff += length;
return resultLen;
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public override int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output)
{
int blockSize = GetBlockSize();
int outLength = GetUpdateOutputSize(input.Length);
if (outLength > 0)
{
Check.OutputLength(output, outLength, "output buffer too short");
}
int resultLen = 0;
int gapLen = buf.Length - bufOff;
if (input.Length > gapLen)
{
input[..gapLen].CopyTo(buf.AsSpan(bufOff));
resultLen = m_cipherMode.ProcessBlock(buf, output);
Array.Copy(buf, blockSize, buf, 0, blockSize);
bufOff = blockSize;
input = input[gapLen..];
while (input.Length > blockSize)
{
input[..blockSize].CopyTo(buf.AsSpan(bufOff));
resultLen += m_cipherMode.ProcessBlock(buf, output[resultLen..]);
Array.Copy(buf, blockSize, buf, 0, blockSize);
input = input[blockSize..];
}
}
input.CopyTo(buf.AsSpan(bufOff));
bufOff += input.Length;
return resultLen;
}
#endif
/**
* Process the last block in the buffer.
*
* @param out the array the block currently being held is copied into.
* @param outOff the offset at which the copying starts.
* @return the number of output bytes copied to out.
* @exception DataLengthException if there is insufficient space in out for
* the output.
* @exception InvalidOperationException if the underlying cipher is not
* initialised.
* @exception InvalidCipherTextException if cipher text decrypts wrongly (in
* case the exception will never Get thrown).
*/
public override int DoFinal(byte[] output, int outOff)
{
if (bufOff + outOff > output.Length)
throw new DataLengthException("output buffer too small in DoFinal");
int blockSize = m_cipherMode.GetBlockSize();
int length = bufOff - blockSize;
byte[] block = new byte[blockSize];
if (forEncryption)
{
m_cipherMode.ProcessBlock(buf, 0, block, 0);
if (bufOff < blockSize)
throw new DataLengthException("need at least one block of input for CTS");
for (int i = bufOff; i != buf.Length; i++)
{
buf[i] = block[i - blockSize];
}
for (int i = blockSize; i != bufOff; i++)
{
buf[i] ^= block[i - blockSize];
}
m_cipherMode.UnderlyingCipher.ProcessBlock(buf, blockSize, output, outOff);
Array.Copy(block, 0, output, outOff + blockSize, length);
}
else
{
byte[] lastBlock = new byte[blockSize];
m_cipherMode.UnderlyingCipher.ProcessBlock(buf, 0, block, 0);
for (int i = blockSize; i != bufOff; i++)
{
lastBlock[i - blockSize] = (byte)(block[i - blockSize] ^ buf[i]);
}
Array.Copy(buf, blockSize, block, 0, length);
m_cipherMode.ProcessBlock(block, 0, output, outOff);
Array.Copy(lastBlock, 0, output, outOff + blockSize, length);
}
int offset = bufOff;
Reset();
return offset;
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public override int DoFinal(Span<byte> output)
{
if (bufOff > output.Length)
throw new DataLengthException("output buffer too small in DoFinal");
int blockSize = m_cipherMode.GetBlockSize();
int length = bufOff - blockSize;
Span<byte> block = blockSize <= 64
? stackalloc byte[blockSize]
: new byte[blockSize];
if (forEncryption)
{
m_cipherMode.ProcessBlock(buf, block);
if (bufOff < blockSize)
throw new DataLengthException("need at least one block of input for CTS");
for (int i = bufOff; i != buf.Length; i++)
{
buf[i] = block[i - blockSize];
}
for (int i = blockSize; i != bufOff; i++)
{
buf[i] ^= block[i - blockSize];
}
m_cipherMode.UnderlyingCipher.ProcessBlock(buf.AsSpan(blockSize), output);
block[..length].CopyTo(output[blockSize..]);
}
else
{
Span<byte> lastBlock = blockSize <= 64
? stackalloc byte[blockSize]
: new byte[blockSize];
m_cipherMode.UnderlyingCipher.ProcessBlock(buf, block);
for (int i = blockSize; i != bufOff; i++)
{
lastBlock[i - blockSize] = (byte)(block[i - blockSize] ^ buf[i]);
}
buf.AsSpan(blockSize, length).CopyTo(block);
m_cipherMode.ProcessBlock(block, output);
lastBlock[..length].CopyTo(output[blockSize..]);
}
int offset = bufOff;
Reset();
return offset;
}
#endif
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e40dd04cc8a1228489faa2810ffae5c3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,506 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Macs;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes
{
/**
* A Two-Pass Authenticated-Encryption Scheme Optimized for Simplicity and
* Efficiency - by M. Bellare, P. Rogaway, D. Wagner.
*
* http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf
*
* EAX is an AEAD scheme based on CTR and OMAC1/CMAC, that uses a single block
* cipher to encrypt and authenticate data. It's on-line (the length of a
* message isn't needed to begin processing it), has good performances, it's
* simple and provably secure (provided the underlying block cipher is secure).
*
* Of course, this implementations is NOT thread-safe.
*/
public class EaxBlockCipher
: IAeadBlockCipher
{
private enum Tag : byte { N, H, C };
private SicBlockCipher cipher;
private bool forEncryption;
private int blockSize;
private IMac mac;
private byte[] nonceMac;
private byte[] associatedTextMac;
private byte[] macBlock;
private int macSize;
private byte[] bufBlock;
private int bufOff;
private bool cipherInitialized;
private byte[] initialAssociatedText;
/**
* Constructor that accepts an instance of a block cipher engine.
*
* @param cipher the engine to use
*/
public EaxBlockCipher(
IBlockCipher cipher)
{
blockSize = cipher.GetBlockSize();
mac = new CMac(cipher);
macBlock = new byte[blockSize];
associatedTextMac = new byte[mac.GetMacSize()];
nonceMac = new byte[mac.GetMacSize()];
this.cipher = new SicBlockCipher(cipher);
}
public virtual string AlgorithmName => cipher.UnderlyingCipher.AlgorithmName + "/EAX";
public virtual IBlockCipher UnderlyingCipher => cipher;
public virtual int GetBlockSize()
{
return cipher.GetBlockSize();
}
public virtual void Init(bool forEncryption, ICipherParameters parameters)
{
this.forEncryption = forEncryption;
byte[] nonce;
ICipherParameters keyParam;
if (parameters is AeadParameters aeadParameters)
{
nonce = aeadParameters.GetNonce();
initialAssociatedText = aeadParameters.GetAssociatedText();
macSize = aeadParameters.MacSize / 8;
keyParam = aeadParameters.Key;
}
else if (parameters is ParametersWithIV parametersWithIV)
{
nonce = parametersWithIV.GetIV();
initialAssociatedText = null;
macSize = mac.GetMacSize() / 2;
keyParam = parametersWithIV.Parameters;
}
else
{
throw new ArgumentException("invalid parameters passed to EAX");
}
bufBlock = new byte[forEncryption ? blockSize : (blockSize + macSize)];
byte[] tag = new byte[blockSize];
// Key reuse implemented in CBC mode of underlying CMac
mac.Init(keyParam);
tag[blockSize - 1] = (byte)Tag.N;
mac.BlockUpdate(tag, 0, blockSize);
mac.BlockUpdate(nonce, 0, nonce.Length);
mac.DoFinal(nonceMac, 0);
// Same BlockCipher underlies this and the mac, so reuse last key on cipher
cipher.Init(true, new ParametersWithIV(null, nonceMac));
Reset();
}
private void InitCipher()
{
if (cipherInitialized)
{
return;
}
cipherInitialized = true;
mac.DoFinal(associatedTextMac, 0);
byte[] tag = new byte[blockSize];
tag[blockSize - 1] = (byte)Tag.C;
mac.BlockUpdate(tag, 0, blockSize);
}
private void CalculateMac()
{
byte[] outC = new byte[blockSize];
mac.DoFinal(outC, 0);
for (int i = 0; i < macBlock.Length; i++)
{
macBlock[i] = (byte)(nonceMac[i] ^ associatedTextMac[i] ^ outC[i]);
}
}
public virtual void Reset()
{
Reset(true);
}
private void Reset(
bool clearMac)
{
cipher.Reset(); // TODO Redundant since the mac will reset it?
mac.Reset();
bufOff = 0;
Array.Clear(bufBlock, 0, bufBlock.Length);
if (clearMac)
{
Array.Clear(macBlock, 0, macBlock.Length);
}
byte[] tag = new byte[blockSize];
tag[blockSize - 1] = (byte)Tag.H;
mac.BlockUpdate(tag, 0, blockSize);
cipherInitialized = false;
if (initialAssociatedText != null)
{
ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
}
}
public virtual void ProcessAadByte(byte input)
{
if (cipherInitialized)
{
throw new InvalidOperationException("AAD data cannot be added after encryption/decryption processing has begun.");
}
mac.Update(input);
}
public virtual void ProcessAadBytes(byte[] inBytes, int inOff, int len)
{
if (cipherInitialized)
throw new InvalidOperationException("AAD data cannot be added after encryption/decryption processing has begun.");
mac.BlockUpdate(inBytes, inOff, len);
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public virtual void ProcessAadBytes(ReadOnlySpan<byte> input)
{
if (cipherInitialized)
throw new InvalidOperationException("AAD data cannot be added after encryption/decryption processing has begun.");
mac.BlockUpdate(input);
}
#endif
public virtual int ProcessByte(byte input, byte[] outBytes, int outOff)
{
InitCipher();
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
return Process(input, Spans.FromNullable(outBytes, outOff));
#else
return Process(input, outBytes, outOff);
#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public virtual int ProcessByte(byte input, Span<byte> output)
{
InitCipher();
return Process(input, output);
}
#endif
public virtual int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff)
{
InitCipher();
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
return ProcessBytes(inBytes.AsSpan(inOff, len), Spans.FromNullable(outBytes, outOff));
#else
int resultLen = 0;
for (int i = 0; i != len; i++)
{
resultLen += Process(inBytes[inOff + i], outBytes, outOff + resultLen);
}
return resultLen;
#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public virtual int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output)
{
InitCipher();
int len = input.Length;
int resultLen = 0;
for (int i = 0; i != len; i++)
{
resultLen += Process(input[i], output[resultLen..]);
}
return resultLen;
}
#endif
public virtual int DoFinal(byte[] outBytes, int outOff)
{
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
return DoFinal(outBytes.AsSpan(outOff));
#else
InitCipher();
int extra = bufOff;
byte[] tmp = new byte[bufBlock.Length];
bufOff = 0;
if (forEncryption)
{
Check.OutputLength(outBytes, outOff, extra + macSize, "output buffer too short");
cipher.ProcessBlock(bufBlock, 0, tmp, 0);
Array.Copy(tmp, 0, outBytes, outOff, extra);
mac.BlockUpdate(tmp, 0, extra);
CalculateMac();
Array.Copy(macBlock, 0, outBytes, outOff + extra, macSize);
Reset(false);
return extra + macSize;
}
else
{
if (extra < macSize)
throw new InvalidCipherTextException("data too short");
Check.OutputLength(outBytes, outOff, extra - macSize, "output buffer too short");
if (extra > macSize)
{
mac.BlockUpdate(bufBlock, 0, extra - macSize);
cipher.ProcessBlock(bufBlock, 0, tmp, 0);
Array.Copy(tmp, 0, outBytes, outOff, extra - macSize);
}
CalculateMac();
if (!VerifyMac(bufBlock, extra - macSize))
throw new InvalidCipherTextException("mac check in EAX failed");
Reset(false);
return extra - macSize;
}
#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public virtual int DoFinal(Span<byte> output)
{
InitCipher();
int extra = bufOff;
int tmpLength = bufBlock.Length;
Span<byte> tmp = tmpLength <= 128
? stackalloc byte[tmpLength]
: new byte[tmpLength];
bufOff = 0;
if (forEncryption)
{
Check.OutputLength(output, extra + macSize, "output buffer too short");
cipher.ProcessBlock(bufBlock, tmp);
tmp[..extra].CopyTo(output);
mac.BlockUpdate(tmp[..extra]);
CalculateMac();
macBlock.AsSpan(0, macSize).CopyTo(output[extra..]);
Reset(false);
return extra + macSize;
}
else
{
if (extra < macSize)
throw new InvalidCipherTextException("data too short");
Check.OutputLength(output, extra - macSize, "output buffer too short");
if (extra > macSize)
{
mac.BlockUpdate(bufBlock.AsSpan(0, extra - macSize));
cipher.ProcessBlock(bufBlock, tmp);
tmp[..(extra - macSize)].CopyTo(output);
}
CalculateMac();
if (!VerifyMac(bufBlock, extra - macSize))
throw new InvalidCipherTextException("mac check in EAX failed");
Reset(false);
return extra - macSize;
}
}
#endif
public virtual byte[] GetMac()
{
byte[] mac = new byte[macSize];
Array.Copy(macBlock, 0, mac, 0, macSize);
return mac;
}
public virtual int GetUpdateOutputSize(
int len)
{
int totalData = len + bufOff;
if (!forEncryption)
{
if (totalData < macSize)
{
return 0;
}
totalData -= macSize;
}
return totalData - totalData % blockSize;
}
public virtual int GetOutputSize(
int len)
{
int totalData = len + bufOff;
if (forEncryption)
{
return totalData + macSize;
}
return totalData < macSize ? 0 : totalData - macSize;
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
private int Process(byte b, Span<byte> output)
{
bufBlock[bufOff++] = b;
if (bufOff == bufBlock.Length)
{
Check.OutputLength(output, blockSize, "output buffer too short");
// TODO Could move the ProcessByte(s) calls to here
//InitCipher();
int size;
if (forEncryption)
{
size = cipher.ProcessBlock(bufBlock, output);
mac.BlockUpdate(output[..blockSize]);
}
else
{
mac.BlockUpdate(bufBlock.AsSpan(0, blockSize));
size = cipher.ProcessBlock(bufBlock, output);
}
bufOff = 0;
if (!forEncryption)
{
Array.Copy(bufBlock, blockSize, bufBlock, 0, macSize);
bufOff = macSize;
}
return size;
}
return 0;
}
#else
private int Process(byte b, byte[] outBytes, int outOff)
{
bufBlock[bufOff++] = b;
if (bufOff == bufBlock.Length)
{
Check.OutputLength(outBytes, outOff, blockSize, "Output buffer is too short");
// TODO Could move the ProcessByte(s) calls to here
// InitCipher();
int size;
if (forEncryption)
{
size = cipher.ProcessBlock(bufBlock, 0, outBytes, outOff);
mac.BlockUpdate(outBytes, outOff, blockSize);
}
else
{
mac.BlockUpdate(bufBlock, 0, blockSize);
size = cipher.ProcessBlock(bufBlock, 0, outBytes, outOff);
}
bufOff = 0;
if (!forEncryption)
{
Array.Copy(bufBlock, blockSize, bufBlock, 0, macSize);
bufOff = macSize;
}
return size;
}
return 0;
}
#endif
private bool VerifyMac(byte[] mac, int off)
{
int nonEqual = 0;
for (int i = 0; i < macSize; i++)
{
nonEqual |= (macBlock[i] ^ mac[off + i]);
}
return nonEqual == 0;
}
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 611d91847a15dff4ebc2fe9f3326559f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,62 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes
{
public class EcbBlockCipher
: IBlockCipherMode
{
internal static IBlockCipherMode GetBlockCipherMode(IBlockCipher blockCipher)
{
if (blockCipher is IBlockCipherMode blockCipherMode)
return blockCipherMode;
return new EcbBlockCipher(blockCipher);
}
private readonly IBlockCipher m_cipher;
public EcbBlockCipher(IBlockCipher cipher)
{
if (cipher == null)
throw new ArgumentNullException(nameof(cipher));
m_cipher = cipher;
}
public bool IsPartialBlockOkay => false;
public string AlgorithmName => m_cipher.AlgorithmName + "/ECB";
public int GetBlockSize()
{
return m_cipher.GetBlockSize();
}
public IBlockCipher UnderlyingCipher => m_cipher;
public void Init(bool forEncryption, ICipherParameters parameters)
{
m_cipher.Init(forEncryption, parameters);
}
public int ProcessBlock(byte[] inBuf, int inOff, byte[] outBuf, int outOff)
{
return m_cipher.ProcessBlock(inBuf, inOff, outBuf, outOff);
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
{
return m_cipher.ProcessBlock(input, output);
}
#endif
public void Reset()
{
}
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 50e8f0b9b9ecb034bb7aad3b1fb61e75
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1ab9d7d1ff514d9479d1a3bca307ff13
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,234 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes
{
/**
* implements the GOST 28147 OFB counter mode (GCTR).
*/
public class GOfbBlockCipher
: IBlockCipherMode
{
private byte[] IV;
private byte[] ofbV;
private byte[] ofbOutV;
private readonly int blockSize;
private readonly IBlockCipher cipher;
bool firstStep = true;
int N3;
int N4;
const int C1 = 16843012; //00000001000000010000000100000100
const int C2 = 16843009; //00000001000000010000000100000001
/**
* Basic constructor.
*
* @param cipher the block cipher to be used as the basis of the
* counter mode (must have a 64 bit block size).
*/
public GOfbBlockCipher(
IBlockCipher cipher)
{
this.cipher = cipher;
this.blockSize = cipher.GetBlockSize();
if (blockSize != 8)
{
throw new ArgumentException("GCTR only for 64 bit block ciphers");
}
this.IV = new byte[cipher.GetBlockSize()];
this.ofbV = new byte[cipher.GetBlockSize()];
this.ofbOutV = new byte[cipher.GetBlockSize()];
}
/**
* return the underlying block cipher that we are wrapping.
*
* @return the underlying block cipher that we are wrapping.
*/
public IBlockCipher UnderlyingCipher => cipher;
/**
* Initialise the cipher and, possibly, the initialisation vector (IV).
* If an IV isn't passed as part of the parameter, the IV will be all zeros.
* An IV which is too short is handled in FIPS compliant fashion.
*
* @param encrypting if true the cipher is initialised for
* encryption, if false for decryption.
* @param parameters the key and other data required by the cipher.
* @exception ArgumentException if the parameters argument is inappropriate.
*/
public void Init(
bool forEncryption, //ignored by this CTR mode
ICipherParameters parameters)
{
firstStep = true;
N3 = 0;
N4 = 0;
if (parameters is ParametersWithIV)
{
ParametersWithIV ivParam = (ParametersWithIV)parameters;
byte[] iv = ivParam.GetIV();
if (iv.Length < IV.Length)
{
// prepend the supplied IV with zeros (per FIPS PUB 81)
Array.Copy(iv, 0, IV, IV.Length - iv.Length, iv.Length);
for (int i = 0; i < IV.Length - iv.Length; i++)
{
IV[i] = 0;
}
}
else
{
Array.Copy(iv, 0, IV, 0, IV.Length);
}
parameters = ivParam.Parameters;
}
Reset();
// if it's null, key is to be reused.
if (parameters != null)
{
cipher.Init(true, parameters);
}
}
/**
* return the algorithm name and mode.
*
* @return the name of the underlying algorithm followed by "/GCTR"
* and the block size in bits
*/
public string AlgorithmName
{
get { return cipher.AlgorithmName + "/GCTR"; }
}
public bool IsPartialBlockOkay
{
get { return true; }
}
/**
* return the block size we are operating at (in bytes).
*
* @return the block size we are operating at (in bytes).
*/
public int GetBlockSize()
{
return blockSize;
}
public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
{
Check.DataLength(input, inOff, blockSize, "input buffer too short");
Check.OutputLength(output, outOff, blockSize, "output buffer too short");
if (firstStep)
{
firstStep = false;
cipher.ProcessBlock(ofbV, 0, ofbOutV, 0);
N3 = (int)Pack.LE_To_UInt32(ofbOutV, 0);
N4 = (int)Pack.LE_To_UInt32(ofbOutV, 4);
}
N3 += C2;
N4 += C1;
if (N4 < C1) // addition is mod (2**32 - 1)
{
if (N4 > 0)
{
N4++;
}
}
Pack.UInt32_To_LE((uint)N3, ofbV, 0);
Pack.UInt32_To_LE((uint)N4, ofbV, 4);
cipher.ProcessBlock(ofbV, 0, ofbOutV, 0);
//
// XOR the ofbV with the plaintext producing the cipher text (and
// the next input block).
//
for (int i = 0; i < blockSize; i++)
{
output[outOff + i] = (byte)(ofbOutV[i] ^ input[inOff + i]);
}
//
// change over the input block.
//
Array.Copy(ofbV, blockSize, ofbV, 0, ofbV.Length - blockSize);
Array.Copy(ofbOutV, 0, ofbV, ofbV.Length - blockSize, blockSize);
return blockSize;
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
{
Check.DataLength(input, blockSize, "input buffer too short");
Check.OutputLength(output, blockSize, "output buffer too short");
if (firstStep)
{
firstStep = false;
cipher.ProcessBlock(ofbV, ofbOutV);
N3 = (int)Pack.LE_To_UInt32(ofbOutV, 0);
N4 = (int)Pack.LE_To_UInt32(ofbOutV, 4);
}
N3 += C2;
N4 += C1;
if (N4 < C1) // addition is mod (2**32 - 1)
{
if (N4 > 0)
{
N4++;
}
}
Pack.UInt32_To_LE((uint)N3, ofbV, 0);
Pack.UInt32_To_LE((uint)N4, ofbV, 4);
cipher.ProcessBlock(ofbV, ofbOutV);
//
// XOR the ofbV with the plaintext producing the cipher text (and
// the next input block).
//
for (int i = 0; i < blockSize; i++)
{
output[i] = (byte)(ofbOutV[i] ^ input[i]);
}
//
// change over the input block.
//
Array.Copy(ofbV, blockSize, ofbV, 0, ofbV.Length - blockSize);
Array.Copy(ofbOutV, 0, ofbV, ofbV.Length - blockSize, blockSize);
return blockSize;
}
#endif
/**
* reset the feedback vector back to the IV and reset the underlying
* cipher.
*/
public void Reset()
{
Array.Copy(IV, 0, ofbV, 0, IV.Length);
}
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bba6a24ff3c75d94eabc433ea5f09f6d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bde795fc81d90214ca49145d8d4266c4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,19 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes
{
/// <summary>An IAeadCipher based on an IBlockCipher.</summary>
public interface IAeadBlockCipher
: IAeadCipher
{
/// <returns>The block size for this cipher, in bytes.</returns>
int GetBlockSize();
/// <summary>The block cipher underlying this algorithm.</summary>
IBlockCipher UnderlyingCipher { get; }
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 721d46b2e82841d418e36512a8e3c356
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,134 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes
{
/// <summary>
/// A cipher mode that includes authenticated encryption with a streaming mode and optional
/// associated data.
/// </summary>
/// <remarks>
/// Implementations of this interface may operate in a packet mode (where all input data is
/// buffered and processed during the call to DoFinal, or in a streaming mode (where output
/// data is incrementally produced with each call to ProcessByte or ProcessBytes. This is
/// important to consider during decryption: in a streaming mode, unauthenticated plaintext
/// data may be output prior to the call to DoFinal that results in an authentication failure.
/// The higher level protocol utilising this cipher must ensure the plaintext data is handled
/// appropriately until the end of data is reached and the entire ciphertext is authenticated.
/// </remarks>
/// <see cref="AeadParameters"/>
public interface IAeadCipher
{
/// <summary>The name of the algorithm this cipher implements.</summary>
string AlgorithmName { get; }
/// <summary>Initialise the cipher.</summary>
/// <remarks>Parameter can either be an AeadParameters or a ParametersWithIV object.</remarks>
/// <param name="forEncryption">Initialise for encryption if true, for decryption if false.</param>
/// <param name="parameters">The key or other data required by the cipher.</param>
void Init(bool forEncryption, ICipherParameters parameters);
/// <summary>Add a single byte to the associated data check.</summary>
/// <remarks>If the implementation supports it, this will be an online operation and will not retain the associated data.</remarks>
/// <param name="input">The byte to be processed.</param>
void ProcessAadByte(byte input);
/// <summary>Add a sequence of bytes to the associated data check.</summary>
/// <remarks>If the implementation supports it, this will be an online operation and will not retain the associated data.</remarks>
/// <param name="inBytes">The input byte array.</param>
/// <param name="inOff">The offset into the input array where the data to be processed starts.</param>
/// <param name="len">The number of bytes to be processed.</param>
void ProcessAadBytes(byte[] inBytes, int inOff, int len);
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
/// <summary>Add a span of bytes to the associated data check.</summary>
/// <remarks>If the implementation supports it, this will be an online operation and will not retain the associated data.</remarks>
/// <param name="input">the span containing the data.</param>
void ProcessAadBytes(ReadOnlySpan<byte> input);
#endif
/**
* Encrypt/decrypt a single byte.
*
* @param input the byte to be processed.
* @param outBytes the output buffer the processed byte goes into.
* @param outOff the offset into the output byte array the processed data starts at.
* @return the number of bytes written to out.
* @exception DataLengthException if the output buffer is too small.
*/
int ProcessByte(byte input, byte[] outBytes, int outOff);
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
int ProcessByte(byte input, Span<byte> output);
#endif
/**
* Process a block of bytes from in putting the result into out.
*
* @param inBytes the input byte array.
* @param inOff the offset into the in array where the data to be processed starts.
* @param len the number of bytes to be processed.
* @param outBytes the output buffer the processed bytes go into.
* @param outOff the offset into the output byte array the processed data starts at.
* @return the number of bytes written to out.
* @exception DataLengthException if the output buffer is too small.
*/
int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff);
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output);
#endif
/**
* Finish the operation either appending or verifying the MAC at the end of the data.
*
* @param outBytes space for any resulting output data.
* @param outOff offset into out to start copying the data at.
* @return number of bytes written into out.
* @throws InvalidOperationException if the cipher is in an inappropriate state.
* @throws InvalidCipherTextException if the MAC fails to match.
*/
int DoFinal(byte[] outBytes, int outOff);
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
int DoFinal(Span<byte> output);
#endif
/**
* Return the value of the MAC associated with the last stream processed.
*
* @return MAC for plaintext data.
*/
byte[] GetMac();
/**
* Return the size of the output buffer required for a ProcessBytes
* an input of len bytes.
*
* @param len the length of the input.
* @return the space required to accommodate a call to ProcessBytes
* with len bytes of input.
*/
int GetUpdateOutputSize(int len);
/**
* Return the size of the output buffer required for a ProcessBytes plus a
* DoFinal with an input of len bytes.
*
* @param len the length of the input.
* @return the space required to accommodate a call to ProcessBytes and DoFinal
* with len bytes of input.
*/
int GetOutputSize(int len);
/// <summary>
/// Reset the cipher to the same state as it was after the last init (if there was one).
/// </summary>
void Reset();
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a148e28c828dee1469495b92e57c9700
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,23 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes
{
public interface IBlockCipherMode
: IBlockCipher
{
/// <summary>Return the <code cref="IBlockCipher"/> underlying this cipher mode.</summary>
IBlockCipher UnderlyingCipher { get; }
/// <summary>Indicates whether this cipher mode can handle partial blocks.</summary>
bool IsPartialBlockOkay { get; }
/// <summary>
/// Reset the cipher mode to the same state as it was after the last init (if there was one).
/// </summary>
void Reset();
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c1ac4125e3c2b46449b20e6f96891d36
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,668 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using System.IO;
using System.Text;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes
{
public class KCcmBlockCipher
: IAeadBlockCipher
{
private static readonly int BYTES_IN_INT = 4;
private static readonly int BITS_IN_BYTE = 8;
private static readonly int MAX_MAC_BIT_LENGTH = 512;
private static readonly int MIN_MAC_BIT_LENGTH = 64;
private IBlockCipher engine;
private int macSize;
private bool forEncryption;
private byte[] initialAssociatedText;
private byte[] mac;
private byte[] macBlock;
private byte[] nonce;
private byte[] G1;
private byte[] buffer;
private byte[] s;
private byte[] counter;
private readonly MemoryStream associatedText = new MemoryStream();
private readonly MemoryStream data = new MemoryStream();
/*
*
*
*/
private int Nb_ = 4;
private void setNb(int Nb)
{
if (Nb == 4 || Nb == 6 || Nb == 8)
{
Nb_ = Nb;
}
else
{
throw new ArgumentException("Nb = 4 is recommended by DSTU7624 but can be changed to only 6 or 8 in this implementation");
}
}
/// <summary>
/// Base constructor. Nb value is set to 4.
/// </summary>
/// <param name="engine">base cipher to use under CCM.</param>
public KCcmBlockCipher(IBlockCipher engine): this(engine, 4)
{
}
/// <summary>
/// Constructor allowing Nb configuration.
///
/// Nb is a parameter specified in CCM mode of DSTU7624 standard.
/// This parameter specifies maximum possible length of input.It should
/// be calculated as follows: Nb = 1 / 8 * (-3 + log[2]Nmax) + 1,
/// where Nmax - length of input message in bits.For practical reasons
/// Nmax usually less than 4Gb, e.g. for Nmax = 2^32 - 1, Nb = 4.
/// </summary>
/// <param name="engine">base cipher to use under CCM.</param>
/// <param name="Nb">Nb value to use.</param>
public KCcmBlockCipher(IBlockCipher engine, int Nb)
{
this.engine = engine;
this.macSize = engine.GetBlockSize();
this.nonce = new byte[engine.GetBlockSize()];
this.initialAssociatedText = new byte[engine.GetBlockSize()];
this.mac = new byte[engine.GetBlockSize()];
this.macBlock = new byte[engine.GetBlockSize()];
this.G1 = new byte[engine.GetBlockSize()];
this.buffer = new byte[engine.GetBlockSize()];
this.s = new byte[engine.GetBlockSize()];
this.counter = new byte[engine.GetBlockSize()];
setNb(Nb);
}
public virtual void Init(bool forEncryption, ICipherParameters parameters)
{
ICipherParameters cipherParameters;
if (parameters is AeadParameters)
{
AeadParameters param = (AeadParameters)parameters;
if (param.MacSize > MAX_MAC_BIT_LENGTH || param.MacSize < MIN_MAC_BIT_LENGTH || param.MacSize % 8 != 0)
{
throw new ArgumentException("Invalid mac size specified");
}
nonce = param.GetNonce();
macSize = param.MacSize / BITS_IN_BYTE;
initialAssociatedText = param.GetAssociatedText();
cipherParameters = param.Key;
}
else if (parameters is ParametersWithIV)
{
nonce = ((ParametersWithIV)parameters).GetIV();
macSize = engine.GetBlockSize(); // use default blockSize for MAC if it is not specified
initialAssociatedText = null;
cipherParameters = ((ParametersWithIV)parameters).Parameters;
}
else
{
throw new ArgumentException("Invalid parameters specified");
}
this.mac = new byte[macSize];
this.forEncryption = forEncryption;
engine.Init(true, cipherParameters);
counter[0] = 0x01; // defined in standard
if (initialAssociatedText != null)
{
ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
}
}
public virtual string AlgorithmName => engine.AlgorithmName + "/KCCM";
public virtual int GetBlockSize()
{
return engine.GetBlockSize();
}
public virtual IBlockCipher UnderlyingCipher => engine;
public virtual void ProcessAadByte(byte input)
{
associatedText.WriteByte(input);
}
public virtual void ProcessAadBytes(byte[] input, int inOff, int len)
{
associatedText.Write(input, inOff, len);
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public virtual void ProcessAadBytes(ReadOnlySpan<byte> input)
{
associatedText.Write(input);
}
#endif
private void ProcessAAD(byte[] assocText, int assocOff, int assocLen, int dataLen)
{
if (assocLen - assocOff < engine.GetBlockSize())
{
throw new ArgumentException("authText buffer too short");
}
if (assocLen % engine.GetBlockSize() != 0)
{
throw new ArgumentException("padding not supported");
}
Array.Copy(nonce, 0, G1, 0, nonce.Length - Nb_ - 1);
intToBytes(dataLen, buffer, 0); // for G1
Array.Copy(buffer, 0, G1, nonce.Length - Nb_ - 1, BYTES_IN_INT);
G1[G1.Length - 1] = getFlag(true, macSize);
engine.ProcessBlock(G1, 0, macBlock, 0);
intToBytes(assocLen, buffer, 0); // for G2
if (assocLen <= engine.GetBlockSize() - Nb_)
{
for (int byteIndex = 0; byteIndex < assocLen; byteIndex++)
{
buffer[byteIndex + Nb_] ^= assocText[assocOff + byteIndex];
}
for (int byteIndex = 0; byteIndex < engine.GetBlockSize(); byteIndex++)
{
macBlock[byteIndex] ^= buffer[byteIndex];
}
engine.ProcessBlock(macBlock, 0, macBlock, 0);
return;
}
for (int byteIndex = 0; byteIndex < engine.GetBlockSize(); byteIndex++)
{
macBlock[byteIndex] ^= buffer[byteIndex];
}
engine.ProcessBlock(macBlock, 0, macBlock, 0);
int authLen = assocLen;
while (authLen != 0)
{
for (int byteIndex = 0; byteIndex < engine.GetBlockSize(); byteIndex++)
{
macBlock[byteIndex] ^= assocText[byteIndex + assocOff];
}
engine.ProcessBlock(macBlock, 0, macBlock, 0);
assocOff += engine.GetBlockSize();
authLen -= engine.GetBlockSize();
}
}
public virtual int ProcessByte(byte input, byte[] output, int outOff)
{
data.WriteByte(input);
return 0;
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public virtual int ProcessByte(byte input, Span<byte> output)
{
data.WriteByte(input);
return 0;
}
#endif
public virtual int ProcessBytes(byte[] input, int inOff, int inLen, byte[] output, int outOff)
{
Check.DataLength(input, inOff, inLen, "input buffer too short");
data.Write(input, inOff, inLen);
return 0;
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public virtual int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output)
{
data.Write(input);
return 0;
}
#endif
public int ProcessPacket(byte[] input, int inOff, int len, byte[] output, int outOff)
{
Check.DataLength(input, inOff, len, "input buffer too short");
Check.OutputLength(output, outOff, len, "output buffer too short");
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
return ProcessPacket(input.AsSpan(inOff, len), output.AsSpan(outOff));
#else
if (associatedText.Length > 0)
{
byte[] aad = associatedText.GetBuffer();
int aadLen = Convert.ToInt32(associatedText.Length);
int dataLen = Convert.ToInt32(data.Length) - (forEncryption ? 0 : macSize);
ProcessAAD(aad, 0, aadLen, dataLen);
}
if (forEncryption)
{
Check.DataLength(len % engine.GetBlockSize() != 0, "partial blocks not supported");
CalculateMac(input, inOff, len);
engine.ProcessBlock(nonce, 0, s, 0);
int totalLength = len;
while (totalLength > 0)
{
ProcessBlock(input, inOff, output, outOff);
totalLength -= engine.GetBlockSize();
inOff += engine.GetBlockSize();
outOff += engine.GetBlockSize();
}
for (int byteIndex = 0; byteIndex<counter.Length; byteIndex++)
{
s[byteIndex] += counter[byteIndex];
}
engine.ProcessBlock(s, 0, buffer, 0);
for (int byteIndex = 0; byteIndex<macSize; byteIndex++)
{
output[outOff + byteIndex] = (byte)(buffer[byteIndex] ^ macBlock[byteIndex]);
}
Array.Copy(macBlock, 0, mac, 0, macSize);
Reset();
return len + macSize;
}
else
{
Check.DataLength((len - macSize) % engine.GetBlockSize() != 0, "partial blocks not supported");
engine.ProcessBlock(nonce, 0, s, 0);
int blocks = len / engine.GetBlockSize();
for (int blockNum = 0; blockNum<blocks; blockNum++)
{
ProcessBlock(input, inOff, output, outOff);
inOff += engine.GetBlockSize();
outOff += engine.GetBlockSize();
}
if (len > inOff)
{
for (int byteIndex = 0; byteIndex<counter.Length; byteIndex++)
{
s[byteIndex] += counter[byteIndex];
}
engine.ProcessBlock(s, 0, buffer, 0);
for (int byteIndex = 0; byteIndex<macSize; byteIndex++)
{
output[outOff + byteIndex] = (byte)(buffer[byteIndex] ^ input[inOff + byteIndex]);
}
outOff += macSize;
}
for (int byteIndex = 0; byteIndex<counter.Length; byteIndex++)
{
s[byteIndex] += counter[byteIndex];
}
engine.ProcessBlock(s, 0, buffer, 0);
Array.Copy(output, outOff - macSize, buffer, 0, macSize);
CalculateMac(output, 0, outOff - macSize);
Array.Copy(macBlock, 0, mac, 0, macSize);
byte[] calculatedMac = new byte[macSize];
Array.Copy(buffer, 0, calculatedMac, 0, macSize);
if (!Arrays.ConstantTimeAreEqual(mac, calculatedMac))
{
throw new InvalidCipherTextException("mac check failed");
}
Reset();
return len - macSize;
}
#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public int ProcessPacket(ReadOnlySpan<byte> input, Span<byte> output)
{
int len = input.Length;
Check.OutputLength(output, len, "output buffer too short");
if (associatedText.Length > 0)
{
byte[] aad = associatedText.GetBuffer();
int aadLen = Convert.ToInt32(associatedText.Length);
int dataLen = Convert.ToInt32(data.Length) - (forEncryption ? 0 : macSize);
ProcessAAD(aad, 0, aadLen, dataLen);
}
int blockSize = engine.GetBlockSize(), index = 0;
if (forEncryption)
{
Check.DataLength(len % blockSize != 0, "partial blocks not supported");
CalculateMac(input);
engine.ProcessBlock(nonce, s);
int totalLength = len;
while (totalLength > 0)
{
ProcessBlock(input[index..], output[index..]);
totalLength -= blockSize;
index += blockSize;
}
for (int byteIndex = 0; byteIndex < counter.Length; byteIndex++)
{
s[byteIndex] += counter[byteIndex];
}
engine.ProcessBlock(s, buffer);
for (int byteIndex = 0; byteIndex < macSize; byteIndex++)
{
output[index + byteIndex] = (byte)(buffer[byteIndex] ^ macBlock[byteIndex]);
}
Array.Copy(macBlock, 0, mac, 0, macSize);
Reset();
return len + macSize;
}
else
{
Check.DataLength((len - macSize) % blockSize != 0, "partial blocks not supported");
engine.ProcessBlock(nonce, 0, s, 0);
int blocks = len / engine.GetBlockSize();
for (int blockNum = 0; blockNum < blocks; blockNum++)
{
ProcessBlock(input[index..], output[index..]);
index += blockSize;
}
if (len > index)
{
for (int byteIndex = 0; byteIndex < counter.Length; byteIndex++)
{
s[byteIndex] += counter[byteIndex];
}
engine.ProcessBlock(s, buffer);
for (int byteIndex = 0; byteIndex < macSize; byteIndex++)
{
output[index + byteIndex] = (byte)(buffer[byteIndex] ^ input[index + byteIndex]);
}
index += macSize;
}
for (int byteIndex = 0; byteIndex < counter.Length; byteIndex++)
{
s[byteIndex] += counter[byteIndex];
}
engine.ProcessBlock(s, buffer);
output[(index - macSize)..index].CopyTo(buffer);
CalculateMac(output[..(index - macSize)]);
Array.Copy(macBlock, 0, mac, 0, macSize);
Span<byte> calculatedMac = macSize <= 64
? stackalloc byte[macSize]
: new byte[macSize];
calculatedMac.CopyFrom(buffer);
if (!Arrays.ConstantTimeAreEqual(mac.AsSpan(0, macSize), calculatedMac))
throw new InvalidCipherTextException("mac check failed");
Reset();
return len - macSize;
}
}
#endif
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
private void CalculateMac(ReadOnlySpan<byte> authText)
{
int blockSize = engine.GetBlockSize();
while (!authText.IsEmpty)
{
for (int byteIndex = 0; byteIndex < blockSize; byteIndex++)
{
macBlock[byteIndex] ^= authText[byteIndex];
}
engine.ProcessBlock(macBlock, macBlock);
authText = authText[blockSize..];
}
}
private void ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
{
for (int byteIndex = 0; byteIndex < counter.Length; byteIndex++)
{
s[byteIndex] += counter[byteIndex];
}
engine.ProcessBlock(s, buffer);
int blockSize = engine.GetBlockSize();
for (int byteIndex = 0; byteIndex < blockSize; byteIndex++)
{
output[byteIndex] = (byte)(buffer[byteIndex] ^ input[byteIndex]);
}
}
#else
private void CalculateMac(byte[] authText, int authOff, int len)
{
int blockSize = engine.GetBlockSize();
int totalLen = len;
while (totalLen > 0)
{
for (int byteIndex = 0; byteIndex < blockSize; byteIndex++)
{
macBlock[byteIndex] ^= authText[authOff + byteIndex];
}
engine.ProcessBlock(macBlock, 0, macBlock, 0);
totalLen -= blockSize;
authOff += blockSize;
}
}
private void ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
{
for (int byteIndex = 0; byteIndex < counter.Length; byteIndex++)
{
s[byteIndex] += counter[byteIndex];
}
engine.ProcessBlock(s, 0, buffer, 0);
for (int byteIndex = 0; byteIndex < engine.GetBlockSize(); byteIndex++)
{
output[outOff + byteIndex] = (byte)(buffer[byteIndex] ^ input[inOff + byteIndex]);
}
}
#endif
public virtual int DoFinal(byte[] output, int outOff)
{
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
return DoFinal(output.AsSpan(outOff));
#else
byte[] buf = data.GetBuffer();
int bufLen = Convert.ToInt32(data.Length);
int len = ProcessPacket(buf, 0, bufLen, output, outOff);
Reset();
return len;
#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public virtual int DoFinal(Span<byte> output)
{
byte[] buf = data.GetBuffer();
int bufLen = Convert.ToInt32(data.Length);
int len = ProcessPacket(buf.AsSpan(0, bufLen), output);
Reset();
return len;
}
#endif
public virtual byte[] GetMac()
{
return Arrays.Clone(mac);
}
public virtual int GetUpdateOutputSize(int len)
{
return len;
}
public virtual int GetOutputSize(int len)
{
return len + macSize;
}
public virtual void Reset()
{
Arrays.Fill(G1, (byte)0);
Arrays.Fill(buffer, (byte)0);
Arrays.Fill(counter, (byte)0);
Arrays.Fill(macBlock, (byte)0);
counter[0] = 0x01;
data.SetLength(0);
associatedText.SetLength(0);
if (initialAssociatedText != null)
{
ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
}
}
private void intToBytes(
int num,
byte[] outBytes,
int outOff)
{
outBytes[outOff + 3] = (byte)(num >> 24);
outBytes[outOff + 2] = (byte)(num >> 16);
outBytes[outOff + 1] = (byte)(num >> 8);
outBytes[outOff] = (byte)num;
}
private byte getFlag(bool authTextPresents, int macSize)
{
StringBuilder flagByte = new StringBuilder();
if (authTextPresents)
{
flagByte.Append("1");
}
else
{
flagByte.Append("0");
}
switch (macSize)
{
case 8:
flagByte.Append("010"); // binary 2
break;
case 16:
flagByte.Append("011"); // binary 3
break;
case 32:
flagByte.Append("100"); // binary 4
break;
case 48:
flagByte.Append("101"); // binary 5
break;
case 64:
flagByte.Append("110"); // binary 6
break;
}
string binaryNb = Convert.ToString(Nb_ - 1, 2);
while (binaryNb.Length < 4)
{
binaryNb = new StringBuilder(binaryNb).Insert(0, "0").ToString();
}
flagByte.Append(binaryNb);
return (byte)Convert.ToInt32(flagByte.ToString(), 2);
}
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 10ee1b085eab17c499ed6a8b5e685402
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,250 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes
{
/**
* Implements a Gamming or Counter (CTR) mode on top of a DSTU 7624 block cipher.
*/
public class KCtrBlockCipher
: IStreamCipher, IBlockCipherMode
{
private byte[] IV;
private byte[] ofbV;
private byte[] ofbOutV;
private bool initialised;
private int byteCount;
private readonly int blockSize;
private readonly IBlockCipher cipher;
/**
* Basic constructor.
*
* @param cipher the block cipher to be used as the basis of the
* feedback mode.
*/
public KCtrBlockCipher(IBlockCipher cipher)
{
this.cipher = cipher;
this.IV = new byte[cipher.GetBlockSize()];
this.blockSize = cipher.GetBlockSize();
this.ofbV = new byte[cipher.GetBlockSize()];
this.ofbOutV = new byte[cipher.GetBlockSize()];
}
/**
* return the underlying block cipher that we are wrapping.
*
* @return the underlying block cipher that we are wrapping.
*/
public IBlockCipher UnderlyingCipher => cipher;
/**
* Initialise the cipher and, possibly, the initialisation vector (IV).
* If an IV isn't passed as part of the parameter, the IV will be all zeros.
* An IV which is too short is handled in FIPS compliant fashion.
*
* @param forEncryption if true the cipher is initialised for
* encryption, if false for decryption.
* @param param the key and other data required by the cipher.
* @exception ArgumentException if the parameters argument is
* inappropriate.
*/
public void Init(
bool forEncryption,
ICipherParameters parameters)
{
this.initialised = true;
if (parameters is ParametersWithIV)
{
ParametersWithIV ivParam = (ParametersWithIV)parameters;
byte[] iv = ivParam.GetIV();
int diff = IV.Length - iv.Length;
Array.Clear(IV, 0, IV.Length);
Array.Copy(iv, 0, IV, diff, iv.Length);
parameters = ivParam.Parameters;
}
else
{
throw new ArgumentException("Invalid parameter passed");
}
// if it's null, key is to be reused.
if (parameters != null)
{
cipher.Init(true, parameters);
}
Reset();
}
/**
* return the algorithm name and mode.
*
* @return the name of the underlying algorithm followed by "/KCTR"
* and the block size in bits.
*/
public string AlgorithmName
{
get { return cipher.AlgorithmName + "/KCTR"; }
}
public bool IsPartialBlockOkay
{
get { return true; }
}
/**
* return the block size we are operating at.
*
* @return the block size we are operating at (in bytes).
*/
public int GetBlockSize()
{
return cipher.GetBlockSize();
}
public byte ReturnByte(byte input)
{
return CalculateByte(input);
}
public void ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
{
Check.DataLength(input, inOff, len, "input buffer too small");
Check.OutputLength(output, outOff, len, "output buffer too short");
int inStart = inOff;
int inEnd = inOff + len;
int outStart = outOff;
while (inStart < inEnd)
{
output[outStart++] = CalculateByte(input[inStart++]);
}
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public void ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output)
{
Check.OutputLength(output, input.Length, "output buffer too short");
for (int i = 0; i < input.Length; ++i)
{
output[i] = CalculateByte(input[i]);
}
}
#endif
protected byte CalculateByte(byte b)
{
if (byteCount == 0)
{
incrementCounterAt(0);
checkCounter();
cipher.ProcessBlock(ofbV, 0, ofbOutV, 0);
return (byte)(ofbOutV[byteCount++] ^ b);
}
byte rv = (byte)(ofbOutV[byteCount++] ^ b);
if (byteCount == ofbV.Length)
{
byteCount = 0;
}
return rv;
}
/**
* Process one block of input from the array in and write it to
* the out array.
*
* @param input the array containing the input data.
* @param inOff offset into the in array the data starts at.
* @param output the array the output data will be copied into.
* @param outOff the offset into the out array the output will start at.
* @exception DataLengthException if there isn't enough data in in, or
* space in out.
* @exception InvalidOperationException if the cipher isn't initialised.
* @return the number of bytes processed and produced.
*/
public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
{
int blockSize = GetBlockSize();
Check.DataLength(input, inOff, blockSize, "input buffer too short");
Check.OutputLength(output, outOff, blockSize, "output buffer too short");
ProcessBytes(input, inOff, blockSize, output, outOff);
return blockSize;
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
{
int blockSize = GetBlockSize();
Check.DataLength(input, blockSize, "input buffer too short");
Check.OutputLength(output, blockSize, "output buffer too short");
ProcessBytes(input[..blockSize], output);
return blockSize;
}
#endif
/**
* reset the chaining vector back to the IV and reset the underlying
* cipher.
*/
public void Reset()
{
if (initialised)
{
cipher.ProcessBlock(IV, 0, ofbV, 0);
}
byteCount = 0;
}
private void incrementCounterAt(int pos)
{
int i = pos;
while (i < ofbV.Length)
{
if (++ofbV[i++] != 0)
{
break;
}
}
}
private void checkCounter()
{
// TODO:
// if the IV is the same as the blocksize we assume the user knows what they are doing
// if (IV.length < ofbV.length)
// {
// for (int i = 0; i != IV.length; i++)
// {
// if (ofbV[i] != IV[i])
// {
// throw new IllegalStateException("Counter in KCTR mode out of range.");
// }
// }
// }
}
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1b0202ca81530df4b96670290d02e774
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,734 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using System.Collections.Generic;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes
{
/// <summary>An implementation of <a href="https://tools.ietf.org/html/rfc7253">RFC 7253 on The OCB
/// Authenticated-Encryption Algorithm</a>.</summary>
/// <remarks>
/// For those still concerned about the original patents around this, please see:
/// <para>https://mailarchive.ietf.org/arch/msg/cfrg/qLTveWOdTJcLn4HP3ev-vrj05Vg/</para>
/// Text reproduced below:
/// <para>
/// Phillip Rogaway&lt;rogaway@cs.ucdavis.edu&gt; Sat, 27 February 2021 02:46 UTC
///
/// I can confirm that I have abandoned all OCB patents and placed into the public domain all OCB-related IP of
/// mine. While I have been telling people this for quite some time, I don't think I ever made a proper announcement
/// to the CFRG or on the OCB webpage. Consider that done.
/// </para>
/// </remarks>
public class OcbBlockCipher
: IAeadBlockCipher
{
private const int BLOCK_SIZE = 16;
private readonly IBlockCipher hashCipher;
private readonly IBlockCipher mainCipher;
/*
* CONFIGURATION
*/
private bool forEncryption;
private int macSize;
private byte[] initialAssociatedText;
/*
* KEY-DEPENDENT
*/
// NOTE: elements are lazily calculated
private IList<byte[]> L;
private byte[] L_Asterisk, L_Dollar;
/*
* NONCE-DEPENDENT
*/
private byte[] KtopInput = null;
private byte[] Stretch = new byte[24];
private byte[] OffsetMAIN_0 = new byte[16];
/*
* PER-ENCRYPTION/DECRYPTION
*/
private byte[] hashBlock, mainBlock;
private int hashBlockPos, mainBlockPos;
private long hashBlockCount, mainBlockCount;
private byte[] OffsetHASH;
private byte[] Sum;
private byte[] OffsetMAIN = new byte[16];
private byte[] Checksum;
// NOTE: The MAC value is preserved after doFinal
private byte[] macBlock;
public OcbBlockCipher(IBlockCipher hashCipher, IBlockCipher mainCipher)
{
if (hashCipher == null)
throw new ArgumentNullException("hashCipher");
if (hashCipher.GetBlockSize() != BLOCK_SIZE)
throw new ArgumentException("must have a block size of " + BLOCK_SIZE, "hashCipher");
if (mainCipher == null)
throw new ArgumentNullException("mainCipher");
if (mainCipher.GetBlockSize() != BLOCK_SIZE)
throw new ArgumentException("must have a block size of " + BLOCK_SIZE, "mainCipher");
if (!hashCipher.AlgorithmName.Equals(mainCipher.AlgorithmName))
throw new ArgumentException("'hashCipher' and 'mainCipher' must be the same algorithm");
this.hashCipher = hashCipher;
this.mainCipher = mainCipher;
}
public virtual string AlgorithmName => mainCipher.AlgorithmName + "/OCB";
public virtual IBlockCipher UnderlyingCipher => mainCipher;
public virtual void Init(bool forEncryption, ICipherParameters parameters)
{
bool oldForEncryption = this.forEncryption;
this.forEncryption = forEncryption;
this.macBlock = null;
KeyParameter keyParameter;
byte[] N;
if (parameters is AeadParameters aeadParameters)
{
N = aeadParameters.GetNonce();
initialAssociatedText = aeadParameters.GetAssociatedText();
int macSizeBits = aeadParameters.MacSize;
if (macSizeBits < 64 || macSizeBits > 128 || macSizeBits % 8 != 0)
throw new ArgumentException("Invalid value for MAC size: " + macSizeBits);
macSize = macSizeBits / 8;
keyParameter = aeadParameters.Key;
}
else if (parameters is ParametersWithIV parametersWithIV)
{
N = parametersWithIV.GetIV();
initialAssociatedText = null;
macSize = 16;
keyParameter = (KeyParameter) parametersWithIV.Parameters;
}
else
{
throw new ArgumentException("invalid parameters passed to OCB");
}
this.hashBlock = new byte[16];
this.mainBlock = new byte[forEncryption ? BLOCK_SIZE : (BLOCK_SIZE + macSize)];
if (N == null)
{
N = new byte[0];
}
if (N.Length > 15)
{
throw new ArgumentException("IV must be no more than 15 bytes");
}
/*
* KEY-DEPENDENT INITIALISATION
*/
if (keyParameter != null)
{
// hashCipher always used in forward mode
hashCipher.Init(true, keyParameter);
mainCipher.Init(forEncryption, keyParameter);
KtopInput = null;
}
else if (oldForEncryption != forEncryption)
{
throw new ArgumentException("cannot change encrypting state without providing key.");
}
this.L_Asterisk = new byte[16];
hashCipher.ProcessBlock(L_Asterisk, 0, L_Asterisk, 0);
this.L_Dollar = OCB_double(L_Asterisk);
this.L = new List<byte[]>();
this.L.Add(OCB_double(L_Dollar));
/*
* NONCE-DEPENDENT AND PER-ENCRYPTION/DECRYPTION INITIALISATION
*/
int bottom = ProcessNonce(N);
int bits = bottom % 8, bytes = bottom / 8;
if (bits == 0)
{
Array.Copy(Stretch, bytes, OffsetMAIN_0, 0, 16);
}
else
{
for (int i = 0; i < 16; ++i)
{
uint b1 = Stretch[bytes];
uint b2 = Stretch[++bytes];
this.OffsetMAIN_0[i] = (byte) ((b1 << bits) | (b2 >> (8 - bits)));
}
}
this.hashBlockPos = 0;
this.mainBlockPos = 0;
this.hashBlockCount = 0;
this.mainBlockCount = 0;
this.OffsetHASH = new byte[16];
this.Sum = new byte[16];
Array.Copy(OffsetMAIN_0, 0, OffsetMAIN, 0, 16);
this.Checksum = new byte[16];
if (initialAssociatedText != null)
{
ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
}
}
protected virtual int ProcessNonce(byte[] N)
{
byte[] nonce = new byte[16];
Array.Copy(N, 0, nonce, nonce.Length - N.Length, N.Length);
nonce[0] = (byte)(macSize << 4);
nonce[15 - N.Length] |= 1;
int bottom = nonce[15] & 0x3F;
nonce[15] &= 0xC0;
/*
* When used with incrementing nonces, the cipher is only applied once every 64 inits.
*/
if (KtopInput == null || !Arrays.AreEqual(nonce, KtopInput))
{
byte[] Ktop = new byte[16];
KtopInput = nonce;
hashCipher.ProcessBlock(KtopInput, 0, Ktop, 0);
Array.Copy(Ktop, 0, Stretch, 0, 16);
for (int i = 0; i < 8; ++i)
{
Stretch[16 + i] = (byte)(Ktop[i] ^ Ktop[i + 1]);
}
}
return bottom;
}
public virtual int GetBlockSize()
{
return BLOCK_SIZE;
}
public virtual byte[] GetMac()
{
return macBlock == null
? new byte[macSize]
: Arrays.Clone(macBlock);
}
public virtual int GetOutputSize(int len)
{
int totalData = len + mainBlockPos;
if (forEncryption)
{
return totalData + macSize;
}
return totalData < macSize ? 0 : totalData - macSize;
}
public virtual int GetUpdateOutputSize(int len)
{
int totalData = len + mainBlockPos;
if (!forEncryption)
{
if (totalData < macSize)
{
return 0;
}
totalData -= macSize;
}
return totalData - totalData % BLOCK_SIZE;
}
public virtual void ProcessAadByte(byte input)
{
hashBlock[hashBlockPos] = input;
if (++hashBlockPos == hashBlock.Length)
{
ProcessHashBlock();
}
}
public virtual void ProcessAadBytes(byte[] input, int off, int len)
{
for (int i = 0; i < len; ++i)
{
hashBlock[hashBlockPos] = input[off + i];
if (++hashBlockPos == hashBlock.Length)
{
ProcessHashBlock();
}
}
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public virtual void ProcessAadBytes(ReadOnlySpan<byte> input)
{
for (int i = 0; i < input.Length; ++i)
{
hashBlock[hashBlockPos] = input[i];
if (++hashBlockPos == hashBlock.Length)
{
ProcessHashBlock();
}
}
}
#endif
public virtual int ProcessByte(byte input, byte[] output, int outOff)
{
mainBlock[mainBlockPos] = input;
if (++mainBlockPos == mainBlock.Length)
{
ProcessMainBlock(output, outOff);
return BLOCK_SIZE;
}
return 0;
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public virtual int ProcessByte(byte input, Span<byte> output)
{
mainBlock[mainBlockPos] = input;
if (++mainBlockPos == mainBlock.Length)
{
ProcessMainBlock(output);
return BLOCK_SIZE;
}
return 0;
}
#endif
public virtual int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
{
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
return ProcessBytes(input.AsSpan(inOff, len), Spans.FromNullable(output, outOff));
#else
int resultLen = 0;
for (int i = 0; i < len; ++i)
{
mainBlock[mainBlockPos] = input[inOff + i];
if (++mainBlockPos == mainBlock.Length)
{
ProcessMainBlock(output, outOff + resultLen);
resultLen += BLOCK_SIZE;
}
}
return resultLen;
#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public virtual int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output)
{
int len = input.Length;
int resultLen = 0;
for (int i = 0; i < len; ++i)
{
mainBlock[mainBlockPos] = input[i];
if (++mainBlockPos == mainBlock.Length)
{
ProcessMainBlock(output[resultLen..]);
resultLen += BLOCK_SIZE;
}
}
return resultLen;
}
#endif
public virtual int DoFinal(byte[] output, int outOff)
{
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
return DoFinal(output.AsSpan(outOff));
#else
/*
* For decryption, get the tag from the end of the message
*/
byte[] tag = null;
if (!forEncryption) {
if (mainBlockPos < macSize)
throw new InvalidCipherTextException("data too short");
mainBlockPos -= macSize;
tag = new byte[macSize];
Array.Copy(mainBlock, mainBlockPos, tag, 0, macSize);
}
/*
* HASH: Process any final partial block; compute final hash value
*/
if (hashBlockPos > 0)
{
OCB_extend(hashBlock, hashBlockPos);
UpdateHASH(L_Asterisk);
}
/*
* OCB-ENCRYPT/OCB-DECRYPT: Process any final partial block
*/
if (mainBlockPos > 0)
{
if (forEncryption)
{
OCB_extend(mainBlock, mainBlockPos);
Xor(Checksum, mainBlock);
}
Xor(OffsetMAIN, L_Asterisk);
byte[] Pad = new byte[16];
hashCipher.ProcessBlock(OffsetMAIN, 0, Pad, 0);
Xor(mainBlock, Pad);
Check.OutputLength(output, outOff, mainBlockPos, "output buffer too short");
Array.Copy(mainBlock, 0, output, outOff, mainBlockPos);
if (!forEncryption)
{
OCB_extend(mainBlock, mainBlockPos);
Xor(Checksum, mainBlock);
}
}
/*
* OCB-ENCRYPT/OCB-DECRYPT: Compute raw tag
*/
Xor(Checksum, OffsetMAIN);
Xor(Checksum, L_Dollar);
hashCipher.ProcessBlock(Checksum, 0, Checksum, 0);
Xor(Checksum, Sum);
this.macBlock = new byte[macSize];
Array.Copy(Checksum, 0, macBlock, 0, macSize);
/*
* Validate or append tag and reset this cipher for the next run
*/
int resultLen = mainBlockPos;
if (forEncryption)
{
Check.OutputLength(output, outOff, resultLen + macSize, "output buffer too short");
// Append tag to the message
Array.Copy(macBlock, 0, output, outOff + resultLen, macSize);
resultLen += macSize;
}
else
{
// Compare the tag from the message with the calculated one
if (!Arrays.ConstantTimeAreEqual(macBlock, tag))
throw new InvalidCipherTextException("mac check in OCB failed");
}
Reset(false);
return resultLen;
#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public virtual int DoFinal(Span<byte> output)
{
/*
* For decryption, get the tag from the end of the message
*/
byte[] tag = null;
if (!forEncryption)
{
if (mainBlockPos < macSize)
throw new InvalidCipherTextException("data too short");
mainBlockPos -= macSize;
tag = new byte[macSize];
Array.Copy(mainBlock, mainBlockPos, tag, 0, macSize);
}
/*
* HASH: Process any final partial block; compute final hash value
*/
if (hashBlockPos > 0)
{
OCB_extend(hashBlock, hashBlockPos);
UpdateHASH(L_Asterisk);
}
/*
* OCB-ENCRYPT/OCB-DECRYPT: Process any final partial block
*/
if (mainBlockPos > 0)
{
if (forEncryption)
{
OCB_extend(mainBlock, mainBlockPos);
Xor(Checksum, mainBlock);
}
Xor(OffsetMAIN, L_Asterisk);
byte[] Pad = new byte[16];
hashCipher.ProcessBlock(OffsetMAIN, 0, Pad, 0);
Xor(mainBlock, Pad);
Check.OutputLength(output, mainBlockPos, "output buffer too short");
mainBlock.AsSpan(0, mainBlockPos).CopyTo(output);
if (!forEncryption)
{
OCB_extend(mainBlock, mainBlockPos);
Xor(Checksum, mainBlock);
}
}
/*
* OCB-ENCRYPT/OCB-DECRYPT: Compute raw tag
*/
Xor(Checksum, OffsetMAIN);
Xor(Checksum, L_Dollar);
hashCipher.ProcessBlock(Checksum, 0, Checksum, 0);
Xor(Checksum, Sum);
this.macBlock = new byte[macSize];
Array.Copy(Checksum, 0, macBlock, 0, macSize);
/*
* Validate or append tag and reset this cipher for the next run
*/
int resultLen = mainBlockPos;
if (forEncryption)
{
// Append tag to the message
Check.OutputLength(output, resultLen + macSize, "output buffer too short");
macBlock.AsSpan(0, macSize).CopyTo(output[resultLen..]);
resultLen += macSize;
}
else
{
// Compare the tag from the message with the calculated one
if (!Arrays.ConstantTimeAreEqual(macBlock, tag))
throw new InvalidCipherTextException("mac check in OCB failed");
}
Reset(false);
return resultLen;
}
#endif
public virtual void Reset()
{
Reset(true);
}
protected virtual void Clear(byte[] bs)
{
if (bs != null)
{
Array.Clear(bs, 0, bs.Length);
}
}
protected virtual byte[] GetLSub(int n)
{
while (n >= L.Count)
{
L.Add(OCB_double(L[L.Count - 1]));
}
return L[n];
}
protected virtual void ProcessHashBlock()
{
/*
* HASH: Process any whole blocks
*/
UpdateHASH(GetLSub(OCB_ntz(++hashBlockCount)));
hashBlockPos = 0;
}
protected virtual void ProcessMainBlock(byte[] output, int outOff)
{
Check.DataLength(output, outOff, BLOCK_SIZE, "Output buffer too short");
/*
* OCB-ENCRYPT/OCB-DECRYPT: Process any whole blocks
*/
if (forEncryption)
{
Xor(Checksum, mainBlock);
mainBlockPos = 0;
}
Xor(OffsetMAIN, GetLSub(OCB_ntz(++mainBlockCount)));
Xor(mainBlock, OffsetMAIN);
mainCipher.ProcessBlock(mainBlock, 0, mainBlock, 0);
Xor(mainBlock, OffsetMAIN);
Array.Copy(mainBlock, 0, output, outOff, 16);
if (!forEncryption)
{
Xor(Checksum, mainBlock);
Array.Copy(mainBlock, BLOCK_SIZE, mainBlock, 0, macSize);
mainBlockPos = macSize;
}
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
protected virtual void ProcessMainBlock(Span<byte> output)
{
Check.DataLength(output, BLOCK_SIZE, "output buffer too short");
/*
* OCB-ENCRYPT/OCB-DECRYPT: Process any whole blocks
*/
if (forEncryption)
{
Xor(Checksum, mainBlock);
mainBlockPos = 0;
}
Xor(OffsetMAIN, GetLSub(OCB_ntz(++mainBlockCount)));
Xor(mainBlock, OffsetMAIN);
mainCipher.ProcessBlock(mainBlock, 0, mainBlock, 0);
Xor(mainBlock, OffsetMAIN);
mainBlock.AsSpan(0, BLOCK_SIZE).CopyTo(output);
if (!forEncryption)
{
Xor(Checksum, mainBlock);
Array.Copy(mainBlock, BLOCK_SIZE, mainBlock, 0, macSize);
mainBlockPos = macSize;
}
}
#endif
protected virtual void Reset(bool clearMac)
{
Clear(hashBlock);
Clear(mainBlock);
hashBlockPos = 0;
mainBlockPos = 0;
hashBlockCount = 0;
mainBlockCount = 0;
Clear(OffsetHASH);
Clear(Sum);
Array.Copy(OffsetMAIN_0, 0, OffsetMAIN, 0, 16);
Clear(Checksum);
if (clearMac)
{
macBlock = null;
}
if (initialAssociatedText != null)
{
ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
}
}
protected virtual void UpdateHASH(byte[] LSub)
{
Xor(OffsetHASH, LSub);
Xor(hashBlock, OffsetHASH);
hashCipher.ProcessBlock(hashBlock, 0, hashBlock, 0);
Xor(Sum, hashBlock);
}
protected static byte[] OCB_double(byte[] block)
{
byte[] result = new byte[16];
int carry = ShiftLeft(block, result);
/*
* NOTE: This construction is an attempt at a constant-time implementation.
*/
result[15] ^= (byte)(0x87 >> ((1 - carry) << 3));
return result;
}
protected static void OCB_extend(byte[] block, int pos)
{
block[pos] = (byte) 0x80;
while (++pos < 16)
{
block[pos] = 0;
}
}
protected static int OCB_ntz(long x)
{
if (x == 0)
{
return 64;
}
int n = 0;
ulong ux = (ulong)x;
while ((ux & 1UL) == 0UL)
{
++n;
ux >>= 1;
}
return n;
}
protected static int ShiftLeft(byte[] block, byte[] output)
{
int i = 16;
uint bit = 0;
while (--i >= 0)
{
uint b = block[i];
output[i] = (byte) ((b << 1) | bit);
bit = (b >> 7) & 1;
}
return (int)bit;
}
protected static void Xor(byte[] block, byte[] val)
{
for (int i = 15; i >= 0; --i)
{
block[i] ^= val[i];
}
}
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2230f3f7676b64c429e0e63970bc1554
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,182 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes
{
/**
* implements a Output-FeedBack (OFB) mode on top of a simple cipher.
*/
public class OfbBlockCipher
: IBlockCipherMode
{
private byte[] IV;
private byte[] ofbV;
private byte[] ofbOutV;
private readonly int blockSize;
private readonly IBlockCipher cipher;
/**
* Basic constructor.
*
* @param cipher the block cipher to be used as the basis of the
* feedback mode.
* @param blockSize the block size in bits (note: a multiple of 8)
*/
public OfbBlockCipher(
IBlockCipher cipher,
int blockSize)
{
this.cipher = cipher;
this.blockSize = blockSize / 8;
this.IV = new byte[cipher.GetBlockSize()];
this.ofbV = new byte[cipher.GetBlockSize()];
this.ofbOutV = new byte[cipher.GetBlockSize()];
}
/**
* return the underlying block cipher that we are wrapping.
*
* @return the underlying block cipher that we are wrapping.
*/
public IBlockCipher UnderlyingCipher => cipher;
/**
* Initialise the cipher and, possibly, the initialisation vector (IV).
* If an IV isn't passed as part of the parameter, the IV will be all zeros.
* An IV which is too short is handled in FIPS compliant fashion.
*
* @param forEncryption if true the cipher is initialised for
* encryption, if false for decryption.
* @param param the key and other data required by the cipher.
* @exception ArgumentException if the parameters argument is
* inappropriate.
*/
public void Init(
bool forEncryption, //ignored by this OFB mode
ICipherParameters parameters)
{
if (parameters is ParametersWithIV ivParam)
{
byte[] iv = ivParam.GetIV();
if (iv.Length < IV.Length)
{
// prepend the supplied IV with zeros (per FIPS PUB 81)
Array.Copy(iv, 0, IV, IV.Length - iv.Length, iv.Length);
for (int i = 0; i < IV.Length - iv.Length; i++)
{
IV[i] = 0;
}
}
else
{
Array.Copy(iv, 0, IV, 0, IV.Length);
}
parameters = ivParam.Parameters;
}
Reset();
// if it's null, key is to be reused.
if (parameters != null)
{
cipher.Init(true, parameters);
}
}
/**
* return the algorithm name and mode.
*
* @return the name of the underlying algorithm followed by "/OFB"
* and the block size in bits
*/
public string AlgorithmName
{
get { return cipher.AlgorithmName + "/OFB" + (blockSize * 8); }
}
public bool IsPartialBlockOkay
{
get { return true; }
}
/**
* return the block size we are operating at (in bytes).
*
* @return the block size we are operating at (in bytes).
*/
public int GetBlockSize()
{
return blockSize;
}
public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
{
Check.DataLength(input, inOff, blockSize, "input buffer too short");
Check.OutputLength(output, outOff, blockSize, "output buffer too short");
cipher.ProcessBlock(ofbV, 0, ofbOutV, 0);
//
// XOR the ofbV with the plaintext producing the cipher text (and
// the next input block).
//
for (int i = 0; i < blockSize; i++)
{
output[outOff + i] = (byte)(ofbOutV[i] ^ input[inOff + i]);
}
//
// change over the input block.
//
Array.Copy(ofbV, blockSize, ofbV, 0, ofbV.Length - blockSize);
Array.Copy(ofbOutV, 0, ofbV, ofbV.Length - blockSize, blockSize);
return blockSize;
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
{
Check.DataLength(input, blockSize, "input buffer too short");
Check.OutputLength(output, blockSize, "output buffer too short");
cipher.ProcessBlock(ofbV, ofbOutV);
//
// XOR the ofbV with the plaintext producing the cipher text (and
// the next input block).
//
for (int i = 0; i < blockSize; i++)
{
output[i] = (byte)(ofbOutV[i] ^ input[i]);
}
//
// change over the input block.
//
Array.Copy(ofbV, blockSize, ofbV, 0, ofbV.Length - blockSize);
Array.Copy(ofbOutV, 0, ofbV, ofbV.Length - blockSize, blockSize);
return blockSize;
}
#endif
/**
* reset the feedback vector back to the IV and reset the underlying
* cipher.
*/
public void Reset()
{
Array.Copy(IV, 0, ofbV, 0, IV.Length);
}
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 155ab1dea42532a47bb238a7232b5908
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,409 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes
{
/**
* Implements OpenPGP's rather strange version of Cipher-FeedBack (CFB) mode
* on top of a simple cipher. This class assumes the IV has been prepended
* to the data stream already, and just accomodates the reset after
* (blockSize + 2) bytes have been read.
* <p>
* For further info see <a href="http://www.ietf.org/rfc/rfc2440.html">RFC 2440</a>.
* </p>
*/
public class OpenPgpCfbBlockCipher
: IBlockCipherMode
{
private byte[] IV;
private byte[] FR;
private byte[] FRE;
private readonly IBlockCipher cipher;
private readonly int blockSize;
private int count;
private bool forEncryption;
/**
* Basic constructor.
*
* @param cipher the block cipher to be used as the basis of the
* feedback mode.
*/
public OpenPgpCfbBlockCipher(
IBlockCipher cipher)
{
this.cipher = cipher;
this.blockSize = cipher.GetBlockSize();
this.IV = new byte[blockSize];
this.FR = new byte[blockSize];
this.FRE = new byte[blockSize];
}
/**
* return the underlying block cipher that we are wrapping.
*
* @return the underlying block cipher that we are wrapping.
*/
public IBlockCipher UnderlyingCipher => cipher;
/**
* return the algorithm name and mode.
*
* @return the name of the underlying algorithm followed by "/PGPCFB"
* and the block size in bits.
*/
public string AlgorithmName
{
get { return cipher.AlgorithmName + "/OpenPGPCFB"; }
}
public bool IsPartialBlockOkay
{
get { return true; }
}
/**
* return the block size we are operating at.
*
* @return the block size we are operating at (in bytes).
*/
public int GetBlockSize()
{
return cipher.GetBlockSize();
}
public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
{
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
return forEncryption
? EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff))
: DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
#else
return forEncryption
? EncryptBlock(input, inOff, output, outOff)
: DecryptBlock(input, inOff, output, outOff);
#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
{
return forEncryption
? EncryptBlock(input, output)
: DecryptBlock(input, output);
}
#endif
/**
* reset the chaining vector back to the IV and reset the underlying
* cipher.
*/
public void Reset()
{
count = 0;
Array.Copy(IV, 0, FR, 0, FR.Length);
}
/**
* Initialise the cipher and, possibly, the initialisation vector (IV).
* If an IV isn't passed as part of the parameter, the IV will be all zeros.
* An IV which is too short is handled in FIPS compliant fashion.
*
* @param forEncryption if true the cipher is initialised for
* encryption, if false for decryption.
* @param parameters the key and other data required by the cipher.
* @exception ArgumentException if the parameters argument is
* inappropriate.
*/
public void Init(bool forEncryption, ICipherParameters parameters)
{
this.forEncryption = forEncryption;
if (parameters is ParametersWithIV ivParam)
{
byte[] iv = ivParam.GetIV();
if (iv.Length < IV.Length)
{
// prepend the supplied IV with zeros (per FIPS PUB 81)
Array.Copy(iv, 0, IV, IV.Length - iv.Length, iv.Length);
for (int i = 0; i < IV.Length - iv.Length; i++)
{
IV[i] = 0;
}
}
else
{
Array.Copy(iv, 0, IV, 0, IV.Length);
}
parameters = ivParam.Parameters;
}
Reset();
cipher.Init(true, parameters);
}
/**
* Encrypt one byte of data according to CFB mode.
* @param data the byte to encrypt
* @param blockOff offset in the current block
* @returns the encrypted byte
*/
private byte EncryptByte(byte data, int blockOff)
{
return (byte)(FRE[blockOff] ^ data);
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
{
Check.DataLength(input, blockSize, "input buffer too short");
Check.OutputLength(output, blockSize, "output buffer too short");
if (count > blockSize)
{
FR[blockSize - 2] = output[0] = EncryptByte(input[0], blockSize - 2);
FR[blockSize - 1] = output[1] = EncryptByte(input[1], blockSize - 1);
cipher.ProcessBlock(FR, FRE);
for (int n = 2; n < blockSize; n++)
{
FR[n - 2] = output[n] = EncryptByte(input[n], n - 2);
}
}
else if (count == 0)
{
cipher.ProcessBlock(FR, FRE);
for (int n = 0; n < blockSize; n++)
{
FR[n] = output[n] = EncryptByte(input[n], n);
}
count += blockSize;
}
else if (count == blockSize)
{
cipher.ProcessBlock(FR, FRE);
output[0] = EncryptByte(input[0], 0);
output[1] = EncryptByte(input[1], 1);
//
// do reset
//
Array.Copy(FR, 2, FR, 0, blockSize - 2);
output[..2].CopyTo(FR.AsSpan(blockSize - 2));
cipher.ProcessBlock(FR, FRE);
for (int n = 2; n < blockSize; n++)
{
FR[n - 2] = output[n] = EncryptByte(input[n], n - 2);
}
count += blockSize;
}
return blockSize;
}
private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
{
Check.DataLength(input, blockSize, "input buffer too short");
Check.OutputLength(output, blockSize, "output buffer too short");
if (count > blockSize)
{
byte inVal = input[0];
FR[blockSize - 2] = inVal;
output[0] = EncryptByte(inVal, blockSize - 2);
inVal = input[1];
FR[blockSize - 1] = inVal;
output[1] = EncryptByte(inVal, blockSize - 1);
cipher.ProcessBlock(FR, FRE);
for (int n = 2; n < blockSize; n++)
{
inVal = input[n];
FR[n - 2] = inVal;
output[n] = EncryptByte(inVal, n - 2);
}
}
else if (count == 0)
{
cipher.ProcessBlock(FR, FRE);
for (int n = 0; n < blockSize; n++)
{
FR[n] = input[n];
output[n] = EncryptByte(input[n], n);
}
count += blockSize;
}
else if (count == blockSize)
{
cipher.ProcessBlock(FR, 0, FRE, 0);
byte inVal1 = input[0];
byte inVal2 = input[1];
output[0] = EncryptByte(inVal1, 0);
output[1] = EncryptByte(inVal2, 1);
Array.Copy(FR, 2, FR, 0, blockSize - 2);
FR[blockSize - 2] = inVal1;
FR[blockSize - 1] = inVal2;
cipher.ProcessBlock(FR, 0, FRE, 0);
for (int n = 2; n < blockSize; n++)
{
byte inVal = input[n];
FR[n - 2] = inVal;
output[n] = EncryptByte(inVal, n - 2);
}
count += blockSize;
}
return blockSize;
}
#else
private int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
{
Check.DataLength(input, inOff, blockSize, "input buffer too short");
Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
if (count > blockSize)
{
FR[blockSize - 2] = outBytes[outOff] = EncryptByte(input[inOff], blockSize - 2);
FR[blockSize - 1] = outBytes[outOff + 1] = EncryptByte(input[inOff + 1], blockSize - 1);
cipher.ProcessBlock(FR, 0, FRE, 0);
for (int n = 2; n < blockSize; n++)
{
FR[n - 2] = outBytes[outOff + n] = EncryptByte(input[inOff + n], n - 2);
}
}
else if (count == 0)
{
cipher.ProcessBlock(FR, 0, FRE, 0);
for (int n = 0; n < blockSize; n++)
{
FR[n] = outBytes[outOff + n] = EncryptByte(input[inOff + n], n);
}
count += blockSize;
}
else if (count == blockSize)
{
cipher.ProcessBlock(FR, 0, FRE, 0);
outBytes[outOff] = EncryptByte(input[inOff], 0);
outBytes[outOff + 1] = EncryptByte(input[inOff + 1], 1);
//
// do reset
//
Array.Copy(FR, 2, FR, 0, blockSize - 2);
Array.Copy(outBytes, outOff, FR, blockSize - 2, 2);
cipher.ProcessBlock(FR, 0, FRE, 0);
for (int n = 2; n < blockSize; n++)
{
FR[n - 2] = outBytes[outOff + n] = EncryptByte(input[inOff + n], n - 2);
}
count += blockSize;
}
return blockSize;
}
private int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
{
Check.DataLength(input, inOff, blockSize, "input buffer too short");
Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
if (count > blockSize)
{
byte inVal = input[inOff];
FR[blockSize - 2] = inVal;
outBytes[outOff] = EncryptByte(inVal, blockSize - 2);
inVal = input[inOff + 1];
FR[blockSize - 1] = inVal;
outBytes[outOff + 1] = EncryptByte(inVal, blockSize - 1);
cipher.ProcessBlock(FR, 0, FRE, 0);
for (int n = 2; n < blockSize; n++)
{
inVal = input[inOff + n];
FR[n - 2] = inVal;
outBytes[outOff + n] = EncryptByte(inVal, n - 2);
}
}
else if (count == 0)
{
cipher.ProcessBlock(FR, 0, FRE, 0);
for (int n = 0; n < blockSize; n++)
{
FR[n] = input[inOff + n];
outBytes[outOff + n] = EncryptByte(input[inOff + n], n);
}
count += blockSize;
}
else if (count == blockSize)
{
cipher.ProcessBlock(FR, 0, FRE, 0);
byte inVal1 = input[inOff];
byte inVal2 = input[inOff + 1];
outBytes[outOff ] = EncryptByte(inVal1, 0);
outBytes[outOff + 1] = EncryptByte(inVal2, 1);
Array.Copy(FR, 2, FR, 0, blockSize - 2);
FR[blockSize - 2] = inVal1;
FR[blockSize - 1] = inVal2;
cipher.ProcessBlock(FR, 0, FRE, 0);
for (int n = 2; n < blockSize; n++)
{
byte inVal = input[inOff + n];
FR[n - 2] = inVal;
outBytes[outOff + n] = EncryptByte(inVal, n - 2);
}
count += blockSize;
}
return blockSize;
}
#endif
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a5cda7b7eef6b9442b7823faaccbbd70
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,139 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Math;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes
{
/**
* Implements the Segmented Integer Counter (SIC) mode on top of a simple
* block cipher.
*/
public class SicBlockCipher
: IBlockCipherMode
{
private readonly IBlockCipher cipher;
private readonly int blockSize;
private readonly byte[] counter;
private readonly byte[] counterOut;
private byte[] IV;
/**
* Basic constructor.
*
* @param c the block cipher to be used.
*/
public SicBlockCipher(IBlockCipher cipher)
{
this.cipher = cipher;
this.blockSize = cipher.GetBlockSize();
this.counter = new byte[blockSize];
this.counterOut = new byte[blockSize];
this.IV = new byte[blockSize];
}
/**
* return the underlying block cipher that we are wrapping.
*
* @return the underlying block cipher that we are wrapping.
*/
public IBlockCipher UnderlyingCipher => cipher;
public virtual void Init(
bool forEncryption, //ignored by this CTR mode
ICipherParameters parameters)
{
ParametersWithIV ivParam = parameters as ParametersWithIV;
if (ivParam == null)
throw new ArgumentException("CTR/SIC mode requires ParametersWithIV", "parameters");
this.IV = Arrays.Clone(ivParam.GetIV());
if (blockSize < IV.Length)
throw new ArgumentException("CTR/SIC mode requires IV no greater than: " + blockSize + " bytes.");
int maxCounterSize = System.Math.Min(8, blockSize / 2);
if (blockSize - IV.Length > maxCounterSize)
throw new ArgumentException("CTR/SIC mode requires IV of at least: " + (blockSize - maxCounterSize) + " bytes.");
// if null it's an IV changed only.
if (ivParam.Parameters != null)
{
cipher.Init(true, ivParam.Parameters);
}
Reset();
}
public virtual string AlgorithmName
{
get { return cipher.AlgorithmName + "/SIC"; }
}
public virtual bool IsPartialBlockOkay
{
get { return true; }
}
public virtual int GetBlockSize()
{
return cipher.GetBlockSize();
}
public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
{
cipher.ProcessBlock(counter, 0, counterOut, 0);
//
// XOR the counterOut with the plaintext producing the cipher text
//
for (int i = 0; i < counterOut.Length; i++)
{
output[outOff + i] = (byte)(counterOut[i] ^ input[inOff + i]);
}
// Increment the counter
int j = counter.Length;
while (--j >= 0 && ++counter[j] == 0)
{
}
return counter.Length;
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
{
cipher.ProcessBlock(counter, 0, counterOut, 0);
//
// XOR the counterOut with the plaintext producing the cipher text
//
for (int i = 0; i < counterOut.Length; i++)
{
output[i] = (byte)(counterOut[i] ^ input[i]);
}
// Increment the counter
int j = counter.Length;
while (--j >= 0 && ++counter[j] == 0)
{
}
return counter.Length;
}
#endif
public virtual void Reset()
{
Arrays.Fill(counter, (byte)0);
Array.Copy(IV, 0, counter, 0, IV.Length);
}
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3e5d0376c6b84f343ae4ebb978626670
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7ff0bc1a8cbd285449fb86b525082d47
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,42 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes.Gcm
{
public class BasicGcmExponentiator
: IGcmExponentiator
{
private GcmUtilities.FieldElement x;
public void Init(byte[] x)
{
GcmUtilities.AsFieldElement(x, out this.x);
}
public void ExponentiateX(long pow, byte[] output)
{
GcmUtilities.FieldElement y;
GcmUtilities.One(out y);
if (pow > 0)
{
GcmUtilities.FieldElement powX = x;
do
{
if ((pow & 1L) != 0)
{
GcmUtilities.Multiply(ref y, ref powX);
}
GcmUtilities.Square(ref powX);
pow >>= 1;
}
while (pow > 0);
}
GcmUtilities.AsBytes(ref y, output);
}
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 32321bd134c65cc4cac0fb4bde7e360d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,26 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes.Gcm
{
public class BasicGcmMultiplier
: IGcmMultiplier
{
private GcmUtilities.FieldElement H;
public void Init(byte[] H)
{
GcmUtilities.AsFieldElement(H, out this.H);
}
public void MultiplyH(byte[] x)
{
GcmUtilities.AsFieldElement(x, out var T);
GcmUtilities.Multiply(ref T, ref H);
GcmUtilities.AsBytes(ref T, x);
}
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3f85532d10251c946bc54a31bd727126
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,302 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using System.Diagnostics;
#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER || UNITY_2021_2_OR_NEWER
using System.Runtime.CompilerServices;
#endif
#if NETCOREAPP3_0_OR_GREATER
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
#endif
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Math.Raw;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes.Gcm
{
internal abstract class GcmUtilities
{
internal struct FieldElement
{
internal ulong n0, n1;
}
private const uint E1 = 0xe1000000;
private const ulong E1UL = (ulong)E1 << 32;
internal static void One(out FieldElement x)
{
x.n0 = 1UL << 63;
x.n1 = 0UL;
}
#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER || UNITY_2021_2_OR_NEWER
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
internal static void AsBytes(ulong x0, ulong x1, byte[] z)
{
Pack.UInt64_To_BE(x0, z, 0);
Pack.UInt64_To_BE(x1, z, 8);
}
#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER || UNITY_2021_2_OR_NEWER
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
internal static void AsBytes(ref FieldElement x, byte[] z)
{
AsBytes(x.n0, x.n1, z);
}
#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER || UNITY_2021_2_OR_NEWER
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
internal static void AsFieldElement(byte[] x, out FieldElement z)
{
z.n0 = Pack.BE_To_UInt64(x, 0);
z.n1 = Pack.BE_To_UInt64(x, 8);
}
internal static void DivideP(ref FieldElement x, out FieldElement z)
{
ulong x0 = x.n0, x1 = x.n1;
ulong m = (ulong)((long)x0 >> 63);
x0 ^= (m & E1UL);
z.n0 = (x0 << 1) | (x1 >> 63);
z.n1 = (x1 << 1) | (ulong)(-(long)m);
}
internal static void Multiply(byte[] x, byte[] y)
{
AsFieldElement(x, out FieldElement X);
AsFieldElement(y, out FieldElement Y);
Multiply(ref X, ref Y);
AsBytes(ref X, x);
}
internal static void Multiply(ref FieldElement x, ref FieldElement y)
{
ulong z0, z1, z2, z3;
#if NETCOREAPP3_0_OR_GREATER
if (Pclmulqdq.IsSupported)
{
var X = Vector128.Create(x.n1, x.n0);
var Y = Vector128.Create(y.n1, y.n0);
var Z0 = Pclmulqdq.CarrylessMultiply(X, Y, 0x00);
var Z1 = Sse2.Xor(
Pclmulqdq.CarrylessMultiply(X, Y, 0x01),
Pclmulqdq.CarrylessMultiply(X, Y, 0x10));
var Z2 = Pclmulqdq.CarrylessMultiply(X, Y, 0x11);
ulong t3 = Z0.GetElement(0);
ulong t2 = Z0.GetElement(1) ^ Z1.GetElement(0);
ulong t1 = Z2.GetElement(0) ^ Z1.GetElement(1);
ulong t0 = Z2.GetElement(1);
z0 = (t0 << 1) | (t1 >> 63);
z1 = (t1 << 1) | (t2 >> 63);
z2 = (t2 << 1) | (t3 >> 63);
z3 = (t3 << 1);
}
else
#endif
{
/*
* "Three-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein.
*
* Without access to the high part of a 64x64 product x * y, we use a bit reversal to calculate it:
* rev(x) * rev(y) == rev((x * y) << 1)
*/
ulong x0 = x.n0, x1 = x.n1;
ulong y0 = y.n0, y1 = y.n1;
ulong x0r = Longs.Reverse(x0), x1r = Longs.Reverse(x1);
ulong y0r = Longs.Reverse(y0), y1r = Longs.Reverse(y1);
ulong h0 = Longs.Reverse(ImplMul64(x0r, y0r));
ulong h1 = ImplMul64(x0, y0) << 1;
ulong h2 = Longs.Reverse(ImplMul64(x1r, y1r));
ulong h3 = ImplMul64(x1, y1) << 1;
ulong h4 = Longs.Reverse(ImplMul64(x0r ^ x1r, y0r ^ y1r));
ulong h5 = ImplMul64(x0 ^ x1, y0 ^ y1) << 1;
z0 = h0;
z1 = h1 ^ h0 ^ h2 ^ h4;
z2 = h2 ^ h1 ^ h3 ^ h5;
z3 = h3;
}
Debug.Assert(z3 << 63 == 0);
z1 ^= z3 ^ (z3 >> 1) ^ (z3 >> 2) ^ (z3 >> 7);
// z2 ^= (z3 << 63) ^ (z3 << 62) ^ (z3 << 57);
z2 ^= (z3 << 62) ^ (z3 << 57);
z0 ^= z2 ^ (z2 >> 1) ^ (z2 >> 2) ^ (z2 >> 7);
z1 ^= (z2 << 63) ^ (z2 << 62) ^ (z2 << 57);
x.n0 = z0;
x.n1 = z1;
}
internal static void MultiplyP7(ref FieldElement x)
{
ulong x0 = x.n0, x1 = x.n1;
ulong c = x1 << 57;
x.n0 = (x0 >> 7) ^ c ^ (c >> 1) ^ (c >> 2) ^ (c >> 7);
x.n1 = (x1 >> 7) | (x0 << 57);
}
internal static void MultiplyP8(ref FieldElement x)
{
ulong x0 = x.n0, x1 = x.n1;
ulong c = x1 << 56;
x.n0 = (x0 >> 8) ^ c ^ (c >> 1) ^ (c >> 2) ^ (c >> 7);
x.n1 = (x1 >> 8) | (x0 << 56);
}
internal static void MultiplyP8(ref FieldElement x, out FieldElement y)
{
ulong x0 = x.n0, x1 = x.n1;
ulong c = x1 << 56;
y.n0 = (x0 >> 8) ^ c ^ (c >> 1) ^ (c >> 2) ^ (c >> 7);
y.n1 = (x1 >> 8) | (x0 << 56);
}
internal static void MultiplyP16(ref FieldElement x)
{
ulong x0 = x.n0, x1 = x.n1;
ulong c = x1 << 48;
x.n0 = (x0 >> 16) ^ c ^ (c >> 1) ^ (c >> 2) ^ (c >> 7);
x.n1 = (x1 >> 16) | (x0 << 48);
}
internal static void Square(ref FieldElement x)
{
ulong z1 = Interleave.Expand64To128Rev(x.n0, out ulong z0);
ulong z3 = Interleave.Expand64To128Rev(x.n1, out ulong z2);
Debug.Assert(z3 << 63 == 0);
z1 ^= z3 ^ (z3 >> 1) ^ (z3 >> 2) ^ (z3 >> 7);
// z2 ^= (z3 << 63) ^ (z3 << 62) ^ (z3 << 57);
z2 ^= (z3 << 62) ^ (z3 << 57);
Debug.Assert(z2 << 63 == 0);
z0 ^= z2 ^ (z2 >> 1) ^ (z2 >> 2) ^ (z2 >> 7);
// z1 ^= (z2 << 63) ^ (z2 << 62) ^ (z2 << 57);
z1 ^= (z2 << 62) ^ (z2 << 57);
x.n0 = z0;
x.n1 = z1;
}
internal static void Xor(byte[] x, byte[] y)
{
int i = 0;
do
{
x[i] ^= y[i]; ++i;
x[i] ^= y[i]; ++i;
x[i] ^= y[i]; ++i;
x[i] ^= y[i]; ++i;
}
while (i < 16);
}
internal static void Xor(byte[] x, byte[] y, int yOff)
{
int i = 0;
do
{
x[i] ^= y[yOff + i]; ++i;
x[i] ^= y[yOff + i]; ++i;
x[i] ^= y[yOff + i]; ++i;
x[i] ^= y[yOff + i]; ++i;
}
while (i < 16);
}
internal static void Xor(byte[] x, byte[] y, int yOff, int yLen)
{
while (--yLen >= 0)
{
x[yLen] ^= y[yOff + yLen];
}
}
internal static void Xor(byte[] x, int xOff, byte[] y, int yOff, int len)
{
while (--len >= 0)
{
x[xOff + len] ^= y[yOff + len];
}
}
internal static void Xor(ref FieldElement x, ref FieldElement y)
{
x.n0 ^= y.n0;
x.n1 ^= y.n1;
}
internal static void Xor(ref FieldElement x, ref FieldElement y, out FieldElement z)
{
z.n0 = x.n0 ^ y.n0;
z.n1 = x.n1 ^ y.n1;
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
internal static void Xor(Span<byte> x, ReadOnlySpan<byte> y)
{
int i = 0;
do
{
x[i] ^= y[i]; ++i;
x[i] ^= y[i]; ++i;
x[i] ^= y[i]; ++i;
x[i] ^= y[i]; ++i;
}
while (i < 16);
}
internal static void Xor(Span<byte> x, ReadOnlySpan<byte> y, int len)
{
for (int i = 0; i < len; ++i)
{
x[i] ^= y[i];
}
}
#endif
private static ulong ImplMul64(ulong x, ulong y)
{
ulong x0 = x & 0x1111111111111111UL;
ulong x1 = x & 0x2222222222222222UL;
ulong x2 = x & 0x4444444444444444UL;
ulong x3 = x & 0x8888888888888888UL;
ulong y0 = y & 0x1111111111111111UL;
ulong y1 = y & 0x2222222222222222UL;
ulong y2 = y & 0x4444444444444444UL;
ulong y3 = y & 0x8888888888888888UL;
ulong z0 = (x0 * y0) ^ (x1 * y3) ^ (x2 * y2) ^ (x3 * y1);
ulong z1 = (x0 * y1) ^ (x1 * y0) ^ (x2 * y3) ^ (x3 * y2);
ulong z2 = (x0 * y2) ^ (x1 * y1) ^ (x2 * y0) ^ (x3 * y3);
ulong z3 = (x0 * y3) ^ (x1 * y2) ^ (x2 * y1) ^ (x3 * y0);
z0 &= 0x1111111111111111UL;
z1 &= 0x2222222222222222UL;
z2 &= 0x4444444444444444UL;
z3 &= 0x8888888888888888UL;
return z0 | z1 | z2 | z3;
}
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b541d643c97f0db4c8ac41882cf50f7a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,14 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes.Gcm
{
public interface IGcmExponentiator
{
void Init(byte[] x);
void ExponentiateX(long pow, byte[] output);
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7a73c4e1c5866f746971e04ec6972cf8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,14 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes.Gcm
{
public interface IGcmMultiplier
{
void Init(byte[] H);
void MultiplyH(byte[] x);
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d084b7e75aa6eda4bbd8804fad7c3c98
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,63 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using System.Collections.Generic;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes.Gcm
{
public class Tables1kGcmExponentiator
: IGcmExponentiator
{
// A lookup table of the power-of-two powers of 'x'
// - lookupPowX2[i] = x^(2^i)
private IList<GcmUtilities.FieldElement> lookupPowX2;
public void Init(byte[] x)
{
GcmUtilities.FieldElement y;
GcmUtilities.AsFieldElement(x, out y);
if (lookupPowX2 != null && y.Equals(lookupPowX2[0]))
return;
lookupPowX2 = new List<GcmUtilities.FieldElement>(8);
lookupPowX2.Add(y);
}
public void ExponentiateX(long pow, byte[] output)
{
GcmUtilities.FieldElement y;
GcmUtilities.One(out y);
int bit = 0;
while (pow > 0)
{
if ((pow & 1L) != 0)
{
EnsureAvailable(bit);
GcmUtilities.FieldElement powX2 = (GcmUtilities.FieldElement)lookupPowX2[bit];
GcmUtilities.Multiply(ref y, ref powX2);
}
++bit;
pow >>= 1;
}
GcmUtilities.AsBytes(ref y, output);
}
private void EnsureAvailable(int bit)
{
int count = lookupPowX2.Count;
if (count <= bit)
{
GcmUtilities.FieldElement powX2 = (GcmUtilities.FieldElement)lookupPowX2[count - 1];
do
{
GcmUtilities.Square(ref powX2);
lookupPowX2.Add(powX2);
}
while (++count <= bit);
}
}
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 292216b0bad4fea4d9a088d66834fbb9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,72 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes.Gcm
{
public class Tables4kGcmMultiplier
: IGcmMultiplier
{
private byte[] H;
private GcmUtilities.FieldElement[] T;
public void Init(byte[] H)
{
if (T == null)
{
T = new GcmUtilities.FieldElement[256];
}
else if (Arrays.AreEqual(this.H, H))
{
return;
}
this.H = Arrays.Clone(H);
// T[0] = 0
// T[1] = H.p^7
GcmUtilities.AsFieldElement(this.H, out T[1]);
GcmUtilities.MultiplyP7(ref T[1]);
for (int n = 1; n < 128; ++n)
{
// T[2.n] = T[n].p^-1
GcmUtilities.DivideP(ref T[n], out T[n << 1]);
// T[2.n + 1] = T[2.n] + T[1]
GcmUtilities.Xor(ref T[n << 1], ref T[1], out T[(n << 1) + 1]);
}
}
public void MultiplyH(byte[] x)
{
//GcmUtilities.FieldElement z = T[x[15]];
//for (int i = 14; i >= 0; --i)
//{
// GcmUtilities.MultiplyP8(ref z);
// GcmUtilities.Xor(ref z, ref T[x[i]]);
//}
//GcmUtilities.AsBytes(ref z, x);
int pos = x[15];
ulong z0 = T[pos].n0, z1 = T[pos].n1;
for (int i = 14; i >= 0; --i)
{
pos = x[i];
ulong c = z1 << 56;
z1 = T[pos].n1 ^ ((z1 >> 8) | (z0 << 56));
z0 = T[pos].n0 ^ (z0 >> 8) ^ c ^ (c >> 1) ^ (c >> 2) ^ (c >> 7);
}
GcmUtilities.AsBytes(z0, z1, x);
}
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a5498d1413e74d74e8430fe02dbf6d89
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,84 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes.Gcm
{
public class Tables64kGcmMultiplier
: IGcmMultiplier
{
private byte[] H;
private GcmUtilities.FieldElement[][] T;
public void Init(byte[] H)
{
if (T == null)
{
T = new GcmUtilities.FieldElement[16][];
}
else if (Arrays.AreEqual(this.H, H))
{
return;
}
this.H = Arrays.Clone(H);
for (int i = 0; i < 16; ++i)
{
GcmUtilities.FieldElement[] t = T[i] = new GcmUtilities.FieldElement[256];
// t[0] = 0
if (i == 0)
{
// t[1] = H.p^7
GcmUtilities.AsFieldElement(this.H, out t[1]);
GcmUtilities.MultiplyP7(ref t[1]);
}
else
{
// t[1] = T[i-1][1].p^8
GcmUtilities.MultiplyP8(ref T[i - 1][1], out t[1]);
}
for (int n = 1; n < 128; ++n)
{
// t[2.n] = t[n].p^-1
GcmUtilities.DivideP(ref t[n], out t[n << 1]);
// t[2.n + 1] = t[2.n] + t[1]
GcmUtilities.Xor(ref t[n << 1], ref t[1], out t[(n << 1) + 1]);
}
}
}
public void MultiplyH(byte[] x)
{
//GcmUtilities.FieldElement z = T[15][x[15]];
//for (int i = 14; i >= 0; --i)
//{
// GcmUtilities.Xor(ref z, ref T[i][x[i]]);
//}
//GcmUtilities.AsBytes(ref z, x);
GcmUtilities.FieldElement[] t = T[15];
int tPos = x[15];
ulong z0 = t[tPos].n0, z1 = t[tPos].n1;
for (int i = 14; i >= 0; --i)
{
t = T[i];
tPos = x[i];
z0 ^= t[tPos].n0;
z1 ^= t[tPos].n1;
}
GcmUtilities.AsBytes(z0, z1, x);
}
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2bc5f428ffb5c774eacfa6b8f8fc91f1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,93 @@
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes.Gcm
{
public sealed class Tables8kGcmMultiplier
: IGcmMultiplier
{
private byte[] H;
private GcmUtilities.FieldElement[][] T;
public void Init(byte[] H)
{
if (T == null)
{
T = new GcmUtilities.FieldElement[2][];
}
else if (Arrays.AreEqual(this.H, H))
{
return;
}
this.H = Arrays.Clone(H);
for (int i = 0; i < 2; ++i)
{
GcmUtilities.FieldElement[] t = T[i] = new GcmUtilities.FieldElement[256];
// t[0] = 0
if (i == 0)
{
// t[1] = H.p^7
GcmUtilities.AsFieldElement(this.H, out t[1]);
GcmUtilities.MultiplyP7(ref t[1]);
}
else
{
// t[1] = T[i-1][1].p^8
GcmUtilities.MultiplyP8(ref T[i - 1][1], out t[1]);
}
for (int n = 1; n < 128; ++n)
{
// t[2.n] = t[n].p^-1
GcmUtilities.DivideP(ref t[n], out t[n << 1]);
// t[2.n + 1] = t[2.n] + t[1]
GcmUtilities.Xor(ref t[n << 1], ref t[1], out t[(n << 1) + 1]);
}
}
}
uint[] z = new uint[4];
public void MultiplyH(byte[] x)
{
GcmUtilities.FieldElement[] T0 = T[0], T1 = T[1];
//GcmUtilities.FieldElement z;
//GcmUtilities.Xor(ref T0[x[14]], ref T1[x[15]], out z);
//for (int i = 12; i >= 0; i -= 2)
//{
// GcmUtilities.MultiplyP16(ref z);
// GcmUtilities.Xor(ref z, ref T0[x[i]]);
// GcmUtilities.Xor(ref z, ref T1[x[i + 1]]);
//}
//GcmUtilities.AsBytes(ref z, x);
int vPos = x[15];
int uPos = x[14];
ulong z1 = T0[uPos].n1 ^ T1[vPos].n1;
ulong z0 = T0[uPos].n0 ^ T1[vPos].n0;
for (int i = 12; i >= 0; i -= 2)
{
vPos = x[i + 1];
uPos = x[i];
ulong c = z1 << 48;
z1 = T0[uPos].n1 ^ T1[vPos].n1 ^ ((z1 >> 16) | (z0 << 48));
z0 = T0[uPos].n0 ^ T1[vPos].n0 ^ (z0 >> 16) ^ c ^ (c >> 1) ^ (c >> 2) ^ (c >> 7);
}
GcmUtilities.AsBytes(z0, z1, x);
}
}
}
#pragma warning restore
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: af0e47802908a1e4686d49b38f408d0d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: