up
This commit is contained in:
245
Assets/BestHTTP/SecureProtocol/crypto/modes/CbcBlockCipher.cs
Normal file
245
Assets/BestHTTP/SecureProtocol/crypto/modes/CbcBlockCipher.cs
Normal file
@@ -0,0 +1,245 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
|
||||
using System;
|
||||
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Modes
|
||||
{
|
||||
/**
|
||||
* implements Cipher-Block-Chaining (CBC) mode on top of a simple cipher.
|
||||
*/
|
||||
public class CbcBlockCipher
|
||||
: IBlockCipher
|
||||
{
|
||||
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 GetUnderlyingCipher()
|
||||
{
|
||||
return 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)
|
||||
{
|
||||
ParametersWithIV ivParam = (ParametersWithIV)parameters;
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process one block of input from the array in and write it to
|
||||
* the out array.
|
||||
*
|
||||
* @param in the array containing the input data.
|
||||
* @param inOff offset into the in array the data starts at.
|
||||
* @param out 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)
|
||||
{
|
||||
return (encrypting)
|
||||
? EncryptBlock(input, inOff, output, outOff)
|
||||
: DecryptBlock(input, inOff, output, outOff);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
cipher.Reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the appropriate chaining step for CBC mode encryption.
|
||||
*
|
||||
* @param in the array containing the data to be encrypted.
|
||||
* @param inOff offset into the in array the data starts at.
|
||||
* @param out the array the encrypted 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.
|
||||
*/
|
||||
private int EncryptBlock(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
byte[] outBytes,
|
||||
int outOff)
|
||||
{
|
||||
if ((inOff + blockSize) > input.Length)
|
||||
{
|
||||
throw new DataLengthException("input buffer too short");
|
||||
}
|
||||
|
||||
/*
|
||||
* XOR the cbcV and the input,
|
||||
* then encrypt the cbcV
|
||||
*/
|
||||
for (int i = 0; i < blockSize; i++)
|
||||
{
|
||||
cbcV[i] ^= input[inOff + i];
|
||||
}
|
||||
|
||||
int length = cipher.ProcessBlock(cbcV, 0, outBytes, outOff);
|
||||
|
||||
/*
|
||||
* copy ciphertext to cbcV
|
||||
*/
|
||||
Array.Copy(outBytes, outOff, cbcV, 0, cbcV.Length);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the appropriate chaining step for CBC mode decryption.
|
||||
*
|
||||
* @param in the array containing the data to be decrypted.
|
||||
* @param inOff offset into the in array the data starts at.
|
||||
* @param out the array the decrypted 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.
|
||||
*/
|
||||
private int DecryptBlock(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
byte[] outBytes,
|
||||
int outOff)
|
||||
{
|
||||
if ((inOff + blockSize) > input.Length)
|
||||
{
|
||||
throw new DataLengthException("input buffer too short");
|
||||
}
|
||||
|
||||
Array.Copy(input, inOff, cbcNextV, 0, blockSize);
|
||||
|
||||
int length = cipher.ProcessBlock(input, inOff, outBytes, outOff);
|
||||
|
||||
/*
|
||||
* XOR the cbcV and the output
|
||||
*/
|
||||
for (int i = 0; i < blockSize; i++)
|
||||
{
|
||||
outBytes[outOff + i] ^= cbcV[i];
|
||||
}
|
||||
|
||||
/*
|
||||
* swap the back up buffer into next position
|
||||
*/
|
||||
byte[] tmp;
|
||||
|
||||
tmp = cbcV;
|
||||
cbcV = cbcNextV;
|
||||
cbcNextV = tmp;
|
||||
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0d6530dbcc5b447e8a5976d6a80cfa1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
453
Assets/BestHTTP/SecureProtocol/crypto/modes/CcmBlockCipher.cs
Normal file
453
Assets/BestHTTP/SecureProtocol/crypto/modes/CcmBlockCipher.cs
Normal file
@@ -0,0 +1,453 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Macs;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Utilities;
|
||||
|
||||
namespace 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 GetUnderlyingCipher()
|
||||
{
|
||||
return cipher;
|
||||
}
|
||||
|
||||
public virtual void Init(
|
||||
bool forEncryption,
|
||||
ICipherParameters parameters)
|
||||
{
|
||||
this.forEncryption = forEncryption;
|
||||
|
||||
ICipherParameters cipherParameters;
|
||||
if (parameters is AeadParameters)
|
||||
{
|
||||
AeadParameters param = (AeadParameters) parameters;
|
||||
|
||||
nonce = param.GetNonce();
|
||||
initialAssociatedText = param.GetAssociatedText();
|
||||
macSize = param.MacSize / 8;
|
||||
cipherParameters = param.Key;
|
||||
}
|
||||
else if (parameters is ParametersWithIV)
|
||||
{
|
||||
ParametersWithIV param = (ParametersWithIV) parameters;
|
||||
|
||||
nonce = param.GetIV();
|
||||
initialAssociatedText = null;
|
||||
macSize = macBlock.Length / 2;
|
||||
cipherParameters = param.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
|
||||
{
|
||||
get { return 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);
|
||||
}
|
||||
|
||||
public virtual int ProcessByte(
|
||||
byte input,
|
||||
byte[] outBytes,
|
||||
int outOff)
|
||||
{
|
||||
data.WriteByte(input);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public virtual int DoFinal(
|
||||
byte[] outBytes,
|
||||
int outOff)
|
||||
{
|
||||
#if PORTABLE || NETFX_CORE
|
||||
byte[] input = data.ToArray();
|
||||
int inLen = input.Length;
|
||||
#else
|
||||
byte[] input = data.GetBuffer();
|
||||
int inLen = (int)data.Position;
|
||||
#endif
|
||||
|
||||
int len = ProcessPacket(input, 0, inLen, outBytes, outOff);
|
||||
|
||||
Reset();
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
public virtual void Reset()
|
||||
{
|
||||
cipher.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 = (int)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;
|
||||
}
|
||||
|
||||
private int CalculateMac(byte[] data, int dataOff, int dataLen, 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 = 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.Position > 0)
|
||||
{
|
||||
#if PORTABLE || NETFX_CORE
|
||||
byte[] input = associatedText.ToArray();
|
||||
int len = input.Length;
|
||||
#else
|
||||
byte[] input = associatedText.GetBuffer();
|
||||
int len = (int)associatedText.Position;
|
||||
#endif
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private int GetAssociatedTextLength()
|
||||
{
|
||||
return (int)associatedText.Length + ((initialAssociatedText == null) ? 0 : initialAssociatedText.Length);
|
||||
}
|
||||
|
||||
private bool HasAssociatedText()
|
||||
{
|
||||
return GetAssociatedTextLength() > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 18bad243fabea4f049a5c2441f9ed3f0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
228
Assets/BestHTTP/SecureProtocol/crypto/modes/CfbBlockCipher.cs
Normal file
228
Assets/BestHTTP/SecureProtocol/crypto/modes/CfbBlockCipher.cs
Normal file
@@ -0,0 +1,228 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
|
||||
using System;
|
||||
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Modes
|
||||
{
|
||||
/**
|
||||
* implements a Cipher-FeedBack (CFB) mode on top of a simple cipher.
|
||||
*/
|
||||
public class CfbBlockCipher
|
||||
: IBlockCipher
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 GetUnderlyingCipher()
|
||||
{
|
||||
return 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process one block of input from the array in and write it to
|
||||
* the out array.
|
||||
*
|
||||
* @param in the array containing the input data.
|
||||
* @param inOff offset into the in array the data starts at.
|
||||
* @param out 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)
|
||||
{
|
||||
return (encrypting)
|
||||
? EncryptBlock(input, inOff, output, outOff)
|
||||
: DecryptBlock(input, inOff, output, outOff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the appropriate processing for CFB mode encryption.
|
||||
*
|
||||
* @param in the array containing the data to be encrypted.
|
||||
* @param inOff offset into the in array the data starts at.
|
||||
* @param out the array the encrypted 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 EncryptBlock(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
byte[] outBytes,
|
||||
int outOff)
|
||||
{
|
||||
if ((inOff + blockSize) > input.Length)
|
||||
{
|
||||
throw new DataLengthException("input buffer too short");
|
||||
}
|
||||
if ((outOff + blockSize) > outBytes.Length)
|
||||
{
|
||||
throw new DataLengthException("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;
|
||||
}
|
||||
/**
|
||||
* Do the appropriate processing for CFB mode decryption.
|
||||
*
|
||||
* @param in the array containing the data to be decrypted.
|
||||
* @param inOff offset into the in array the data starts at.
|
||||
* @param out the array the encrypted 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 DecryptBlock(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
byte[] outBytes,
|
||||
int outOff)
|
||||
{
|
||||
if ((inOff + blockSize) > input.Length)
|
||||
{
|
||||
throw new DataLengthException("input buffer too short");
|
||||
}
|
||||
if ((outOff + blockSize) > outBytes.Length)
|
||||
{
|
||||
throw new DataLengthException("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;
|
||||
}
|
||||
/**
|
||||
* reset the chaining vector back to the IV and reset the underlying
|
||||
* cipher.
|
||||
*/
|
||||
public void Reset()
|
||||
{
|
||||
Array.Copy(IV, 0, cfbV, 0, IV.Length);
|
||||
cipher.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 170d26d41fe79464981e6696e7f818ca
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
257
Assets/BestHTTP/SecureProtocol/crypto/modes/CtsBlockCipher.cs
Normal file
257
Assets/BestHTTP/SecureProtocol/crypto/modes/CtsBlockCipher.cs
Normal file
@@ -0,0 +1,257 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
|
||||
namespace 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;
|
||||
|
||||
/**
|
||||
* Create a buffered block cipher that uses Cipher Text Stealing
|
||||
*
|
||||
* @param cipher the underlying block cipher this buffering object wraps.
|
||||
*/
|
||||
public CtsBlockCipher(
|
||||
IBlockCipher cipher)
|
||||
{
|
||||
// TODO Should this test for acceptable ones instead?
|
||||
if (cipher is OfbBlockCipher || cipher is CfbBlockCipher)
|
||||
throw new ArgumentException("CtsBlockCipher can only accept ECB, or CBC ciphers");
|
||||
|
||||
this.cipher = cipher;
|
||||
|
||||
blockSize = cipher.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 = cipher.ProcessBlock(buf, 0, output, outOff);
|
||||
Debug.Assert(resultLen == blockSize);
|
||||
|
||||
Array.Copy(buf, blockSize, buf, 0, blockSize);
|
||||
bufOff = blockSize;
|
||||
}
|
||||
|
||||
buf[bufOff++] = input;
|
||||
|
||||
return resultLen;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 outLength!");
|
||||
}
|
||||
|
||||
int blockSize = GetBlockSize();
|
||||
int outLength = GetUpdateOutputSize(length);
|
||||
|
||||
if (outLength > 0)
|
||||
{
|
||||
if ((outOff + outLength) > output.Length)
|
||||
{
|
||||
throw new DataLengthException("output buffer too short");
|
||||
}
|
||||
}
|
||||
|
||||
int resultLen = 0;
|
||||
int gapLen = buf.Length - bufOff;
|
||||
|
||||
if (length > gapLen)
|
||||
{
|
||||
Array.Copy(input, inOff, buf, bufOff, gapLen);
|
||||
|
||||
resultLen += cipher.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 += cipher.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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = cipher.GetBlockSize();
|
||||
int length = bufOff - blockSize;
|
||||
byte[] block = new byte[blockSize];
|
||||
|
||||
if (forEncryption)
|
||||
{
|
||||
cipher.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];
|
||||
}
|
||||
|
||||
IBlockCipher c = (cipher is CbcBlockCipher)
|
||||
? ((CbcBlockCipher)cipher).GetUnderlyingCipher()
|
||||
: cipher;
|
||||
|
||||
c.ProcessBlock(buf, blockSize, output, outOff);
|
||||
|
||||
Array.Copy(block, 0, output, outOff + blockSize, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] lastBlock = new byte[blockSize];
|
||||
|
||||
IBlockCipher c = (cipher is CbcBlockCipher)
|
||||
? ((CbcBlockCipher)cipher).GetUnderlyingCipher()
|
||||
: cipher;
|
||||
|
||||
c.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);
|
||||
|
||||
cipher.ProcessBlock(block, 0, output, outOff);
|
||||
Array.Copy(lastBlock, 0, output, outOff + blockSize, length);
|
||||
}
|
||||
|
||||
int offset = bufOff;
|
||||
|
||||
Reset();
|
||||
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eaa7642f859b644abaccfaad1c7cdc3a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
383
Assets/BestHTTP/SecureProtocol/crypto/modes/EAXBlockCipher.cs
Normal file
383
Assets/BestHTTP/SecureProtocol/crypto/modes/EAXBlockCipher.cs
Normal file
@@ -0,0 +1,383 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
|
||||
using System;
|
||||
|
||||
using Org.BouncyCastle.Crypto.Macs;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Utilities;
|
||||
|
||||
namespace 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
|
||||
{
|
||||
get { return cipher.GetUnderlyingCipher().AlgorithmName + "/EAX"; }
|
||||
}
|
||||
|
||||
public virtual IBlockCipher GetUnderlyingCipher()
|
||||
{
|
||||
return 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 param = (AeadParameters) parameters;
|
||||
|
||||
nonce = param.GetNonce();
|
||||
initialAssociatedText = param.GetAssociatedText();
|
||||
macSize = param.MacSize / 8;
|
||||
keyParam = param.Key;
|
||||
}
|
||||
else if (parameters is ParametersWithIV)
|
||||
{
|
||||
ParametersWithIV param = (ParametersWithIV) parameters;
|
||||
|
||||
nonce = param.GetIV();
|
||||
initialAssociatedText = null;
|
||||
macSize = mac.GetMacSize() / 2;
|
||||
keyParam = param.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);
|
||||
}
|
||||
|
||||
public virtual int ProcessByte(
|
||||
byte input,
|
||||
byte[] outBytes,
|
||||
int outOff)
|
||||
{
|
||||
InitCipher();
|
||||
|
||||
return Process(input, outBytes, outOff);
|
||||
}
|
||||
|
||||
public virtual int ProcessBytes(
|
||||
byte[] inBytes,
|
||||
int inOff,
|
||||
int len,
|
||||
byte[] outBytes,
|
||||
int outOff)
|
||||
{
|
||||
InitCipher();
|
||||
|
||||
int resultLen = 0;
|
||||
|
||||
for (int i = 0; i != len; i++)
|
||||
{
|
||||
resultLen += Process(inBytes[inOff + i], outBytes, outOff + resultLen);
|
||||
}
|
||||
|
||||
return resultLen;
|
||||
}
|
||||
|
||||
public virtual int DoFinal(
|
||||
byte[] outBytes,
|
||||
int outOff)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 22aa0030c62014175b7ef80bf2c393a1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
540
Assets/BestHTTP/SecureProtocol/crypto/modes/GCMBlockCipher.cs
Normal file
540
Assets/BestHTTP/SecureProtocol/crypto/modes/GCMBlockCipher.cs
Normal file
@@ -0,0 +1,540 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
|
||||
using System;
|
||||
|
||||
using Org.BouncyCastle.Crypto.Macs;
|
||||
using Org.BouncyCastle.Crypto.Modes.Gcm;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Crypto.Utilities;
|
||||
using Org.BouncyCastle.Utilities;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Modes
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements the Galois/Counter mode (GCM) detailed in
|
||||
/// NIST Special Publication 800-38D.
|
||||
/// </summary>
|
||||
public class GcmBlockCipher
|
||||
: IAeadBlockCipher
|
||||
{
|
||||
private const int BlockSize = 16;
|
||||
|
||||
private readonly IBlockCipher cipher;
|
||||
private readonly IGcmMultiplier multiplier;
|
||||
private IGcmExponentiator exp;
|
||||
|
||||
// These fields are set by Init and not modified by processing
|
||||
private bool forEncryption;
|
||||
private int macSize;
|
||||
private byte[] nonce;
|
||||
private byte[] initialAssociatedText;
|
||||
private byte[] H;
|
||||
private byte[] J0;
|
||||
|
||||
// These fields are modified during processing
|
||||
private byte[] bufBlock;
|
||||
private byte[] macBlock;
|
||||
private byte[] S, S_at, S_atPre;
|
||||
private byte[] counter;
|
||||
private uint blocksRemaining;
|
||||
private int bufOff;
|
||||
private ulong totalLength;
|
||||
private byte[] atBlock;
|
||||
private int atBlockPos;
|
||||
private ulong atLength;
|
||||
private ulong atLengthPre;
|
||||
|
||||
public GcmBlockCipher(
|
||||
IBlockCipher c)
|
||||
: this(c, null)
|
||||
{
|
||||
}
|
||||
|
||||
public GcmBlockCipher(
|
||||
IBlockCipher c,
|
||||
IGcmMultiplier m)
|
||||
{
|
||||
if (c.GetBlockSize() != BlockSize)
|
||||
throw new ArgumentException("cipher required with a block size of " + BlockSize + ".");
|
||||
|
||||
if (m == null)
|
||||
{
|
||||
// TODO Consider a static property specifying default multiplier
|
||||
m = new Tables8kGcmMultiplier();
|
||||
}
|
||||
|
||||
this.cipher = c;
|
||||
this.multiplier = m;
|
||||
}
|
||||
|
||||
public virtual string AlgorithmName
|
||||
{
|
||||
get { return cipher.AlgorithmName + "/GCM"; }
|
||||
}
|
||||
|
||||
public IBlockCipher GetUnderlyingCipher()
|
||||
{
|
||||
return cipher;
|
||||
}
|
||||
|
||||
public virtual int GetBlockSize()
|
||||
{
|
||||
return BlockSize;
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// MAC sizes from 32 bits to 128 bits (must be a multiple of 8) are supported. The default is 128 bits.
|
||||
/// Sizes less than 96 are not recommended, but are supported for specialized applications.
|
||||
/// </remarks>
|
||||
public virtual void Init(
|
||||
bool forEncryption,
|
||||
ICipherParameters parameters)
|
||||
{
|
||||
this.forEncryption = forEncryption;
|
||||
this.macBlock = null;
|
||||
|
||||
KeyParameter keyParam;
|
||||
|
||||
if (parameters is AeadParameters)
|
||||
{
|
||||
AeadParameters param = (AeadParameters)parameters;
|
||||
|
||||
nonce = param.GetNonce();
|
||||
initialAssociatedText = param.GetAssociatedText();
|
||||
|
||||
int macSizeBits = param.MacSize;
|
||||
if (macSizeBits < 32 || macSizeBits > 128 || macSizeBits % 8 != 0)
|
||||
{
|
||||
throw new ArgumentException("Invalid value for MAC size: " + macSizeBits);
|
||||
}
|
||||
|
||||
macSize = macSizeBits / 8;
|
||||
keyParam = param.Key;
|
||||
}
|
||||
else if (parameters is ParametersWithIV)
|
||||
{
|
||||
ParametersWithIV param = (ParametersWithIV)parameters;
|
||||
|
||||
nonce = param.GetIV();
|
||||
initialAssociatedText = null;
|
||||
macSize = 16;
|
||||
keyParam = (KeyParameter)param.Parameters;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("invalid parameters passed to GCM");
|
||||
}
|
||||
|
||||
int bufLength = forEncryption ? BlockSize : (BlockSize + macSize);
|
||||
this.bufBlock = new byte[bufLength];
|
||||
|
||||
if (nonce == null || nonce.Length < 1)
|
||||
{
|
||||
throw new ArgumentException("IV must be at least 1 byte");
|
||||
}
|
||||
|
||||
// TODO Restrict macSize to 16 if nonce length not 12?
|
||||
|
||||
// Cipher always used in forward mode
|
||||
// if keyParam is null we're reusing the last key.
|
||||
if (keyParam != null)
|
||||
{
|
||||
cipher.Init(true, keyParam);
|
||||
|
||||
this.H = new byte[BlockSize];
|
||||
cipher.ProcessBlock(H, 0, H, 0);
|
||||
|
||||
// if keyParam is null we're reusing the last key and the multiplier doesn't need re-init
|
||||
multiplier.Init(H);
|
||||
exp = null;
|
||||
}
|
||||
else if (this.H == null)
|
||||
{
|
||||
throw new ArgumentException("Key must be specified in initial init");
|
||||
}
|
||||
|
||||
this.J0 = new byte[BlockSize];
|
||||
|
||||
if (nonce.Length == 12)
|
||||
{
|
||||
Array.Copy(nonce, 0, J0, 0, nonce.Length);
|
||||
this.J0[BlockSize - 1] = 0x01;
|
||||
}
|
||||
else
|
||||
{
|
||||
gHASH(J0, nonce, nonce.Length);
|
||||
byte[] X = new byte[BlockSize];
|
||||
Pack.UInt64_To_BE((ulong)nonce.Length * 8UL, X, 8);
|
||||
gHASHBlock(J0, X);
|
||||
}
|
||||
|
||||
this.S = new byte[BlockSize];
|
||||
this.S_at = new byte[BlockSize];
|
||||
this.S_atPre = new byte[BlockSize];
|
||||
this.atBlock = new byte[BlockSize];
|
||||
this.atBlockPos = 0;
|
||||
this.atLength = 0;
|
||||
this.atLengthPre = 0;
|
||||
this.counter = Arrays.Clone(J0);
|
||||
this.blocksRemaining = uint.MaxValue - 1; // page 8, len(P) <= 2^39 - 256, 1 block used by tag
|
||||
this.bufOff = 0;
|
||||
this.totalLength = 0;
|
||||
|
||||
if (initialAssociatedText != null)
|
||||
{
|
||||
ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual byte[] GetMac()
|
||||
{
|
||||
return Arrays.Clone(macBlock);
|
||||
}
|
||||
|
||||
public virtual int GetOutputSize(
|
||||
int len)
|
||||
{
|
||||
int totalData = len + bufOff;
|
||||
|
||||
if (forEncryption)
|
||||
{
|
||||
return totalData + macSize;
|
||||
}
|
||||
|
||||
return totalData < macSize ? 0 : totalData - macSize;
|
||||
}
|
||||
|
||||
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 void ProcessAadByte(byte input)
|
||||
{
|
||||
atBlock[atBlockPos] = input;
|
||||
if (++atBlockPos == BlockSize)
|
||||
{
|
||||
// Hash each block as it fills
|
||||
gHASHBlock(S_at, atBlock);
|
||||
atBlockPos = 0;
|
||||
atLength += BlockSize;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ProcessAadBytes(byte[] inBytes, int inOff, int len)
|
||||
{
|
||||
for (int i = 0; i < len; ++i)
|
||||
{
|
||||
atBlock[atBlockPos] = inBytes[inOff + i];
|
||||
if (++atBlockPos == BlockSize)
|
||||
{
|
||||
// Hash each block as it fills
|
||||
gHASHBlock(S_at, atBlock);
|
||||
atBlockPos = 0;
|
||||
atLength += BlockSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InitCipher()
|
||||
{
|
||||
if (atLength > 0)
|
||||
{
|
||||
Array.Copy(S_at, 0, S_atPre, 0, BlockSize);
|
||||
atLengthPre = atLength;
|
||||
}
|
||||
|
||||
// Finish hash for partial AAD block
|
||||
if (atBlockPos > 0)
|
||||
{
|
||||
gHASHPartial(S_atPre, atBlock, 0, atBlockPos);
|
||||
atLengthPre += (uint)atBlockPos;
|
||||
}
|
||||
|
||||
if (atLengthPre > 0)
|
||||
{
|
||||
Array.Copy(S_atPre, 0, S, 0, BlockSize);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual int ProcessByte(
|
||||
byte input,
|
||||
byte[] output,
|
||||
int outOff)
|
||||
{
|
||||
bufBlock[bufOff] = input;
|
||||
if (++bufOff == bufBlock.Length)
|
||||
{
|
||||
OutputBlock(output, outOff);
|
||||
return BlockSize;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public virtual int ProcessBytes(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
int len,
|
||||
byte[] output,
|
||||
int outOff)
|
||||
{
|
||||
if (input.Length < (inOff + len))
|
||||
throw new DataLengthException("Input buffer too short");
|
||||
|
||||
int resultLen = 0;
|
||||
|
||||
for (int i = 0; i < len; ++i)
|
||||
{
|
||||
bufBlock[bufOff] = input[inOff + i];
|
||||
if (++bufOff == bufBlock.Length)
|
||||
{
|
||||
OutputBlock(output, outOff + resultLen);
|
||||
resultLen += BlockSize;
|
||||
}
|
||||
}
|
||||
|
||||
return resultLen;
|
||||
}
|
||||
|
||||
private void OutputBlock(byte[] output, int offset)
|
||||
{
|
||||
Check.OutputLength(output, offset, BlockSize, "Output buffer too short");
|
||||
if (totalLength == 0)
|
||||
{
|
||||
InitCipher();
|
||||
}
|
||||
gCTRBlock(bufBlock, output, offset);
|
||||
if (forEncryption)
|
||||
{
|
||||
bufOff = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Copy(bufBlock, BlockSize, bufBlock, 0, macSize);
|
||||
bufOff = macSize;
|
||||
}
|
||||
}
|
||||
|
||||
public int DoFinal(byte[] output, int outOff)
|
||||
{
|
||||
if (totalLength == 0)
|
||||
{
|
||||
InitCipher();
|
||||
}
|
||||
|
||||
int extra = bufOff;
|
||||
|
||||
if (forEncryption)
|
||||
{
|
||||
Check.OutputLength(output, outOff, extra + macSize, "Output buffer too short");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (extra < macSize)
|
||||
throw new InvalidCipherTextException("data too short");
|
||||
|
||||
extra -= macSize;
|
||||
|
||||
Check.OutputLength(output, outOff, extra, "Output buffer too short");
|
||||
}
|
||||
|
||||
if (extra > 0)
|
||||
{
|
||||
gCTRPartial(bufBlock, 0, extra, output, outOff);
|
||||
}
|
||||
|
||||
atLength += (uint)atBlockPos;
|
||||
|
||||
if (atLength > atLengthPre)
|
||||
{
|
||||
/*
|
||||
* Some AAD was sent after the cipher started. We determine the difference b/w the hash value
|
||||
* we actually used when the cipher started (S_atPre) and the final hash value calculated (S_at).
|
||||
* Then we carry this difference forward by multiplying by H^c, where c is the number of (full or
|
||||
* partial) cipher-text blocks produced, and adjust the current hash.
|
||||
*/
|
||||
|
||||
// Finish hash for partial AAD block
|
||||
if (atBlockPos > 0)
|
||||
{
|
||||
gHASHPartial(S_at, atBlock, 0, atBlockPos);
|
||||
}
|
||||
|
||||
// Find the difference between the AAD hashes
|
||||
if (atLengthPre > 0)
|
||||
{
|
||||
GcmUtilities.Xor(S_at, S_atPre);
|
||||
}
|
||||
|
||||
// Number of cipher-text blocks produced
|
||||
long c = (long)(((totalLength * 8) + 127) >> 7);
|
||||
|
||||
// Calculate the adjustment factor
|
||||
byte[] H_c = new byte[16];
|
||||
if (exp == null)
|
||||
{
|
||||
exp = new Tables1kGcmExponentiator();
|
||||
exp.Init(H);
|
||||
}
|
||||
exp.ExponentiateX(c, H_c);
|
||||
|
||||
// Carry the difference forward
|
||||
GcmUtilities.Multiply(S_at, H_c);
|
||||
|
||||
// Adjust the current hash
|
||||
GcmUtilities.Xor(S, S_at);
|
||||
}
|
||||
|
||||
// Final gHASH
|
||||
byte[] X = new byte[BlockSize];
|
||||
Pack.UInt64_To_BE(atLength * 8UL, X, 0);
|
||||
Pack.UInt64_To_BE(totalLength * 8UL, X, 8);
|
||||
|
||||
gHASHBlock(S, X);
|
||||
|
||||
// T = MSBt(GCTRk(J0,S))
|
||||
byte[] tag = new byte[BlockSize];
|
||||
cipher.ProcessBlock(J0, 0, tag, 0);
|
||||
GcmUtilities.Xor(tag, S);
|
||||
|
||||
int resultLen = extra;
|
||||
|
||||
// We place into macBlock our calculated value for T
|
||||
this.macBlock = new byte[macSize];
|
||||
Array.Copy(tag, 0, macBlock, 0, macSize);
|
||||
|
||||
if (forEncryption)
|
||||
{
|
||||
// Append T to the message
|
||||
Array.Copy(macBlock, 0, output, outOff + bufOff, macSize);
|
||||
resultLen += macSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Retrieve the T value from the message and compare to calculated one
|
||||
byte[] msgMac = new byte[macSize];
|
||||
Array.Copy(bufBlock, extra, msgMac, 0, macSize);
|
||||
if (!Arrays.ConstantTimeAreEqual(this.macBlock, msgMac))
|
||||
throw new InvalidCipherTextException("mac check in GCM failed");
|
||||
}
|
||||
|
||||
Reset(false);
|
||||
|
||||
return resultLen;
|
||||
}
|
||||
|
||||
public virtual void Reset()
|
||||
{
|
||||
Reset(true);
|
||||
}
|
||||
|
||||
private void Reset(
|
||||
bool clearMac)
|
||||
{
|
||||
cipher.Reset();
|
||||
|
||||
S = new byte[BlockSize];
|
||||
S_at = new byte[BlockSize];
|
||||
S_atPre = new byte[BlockSize];
|
||||
atBlock = new byte[BlockSize];
|
||||
atBlockPos = 0;
|
||||
atLength = 0;
|
||||
atLengthPre = 0;
|
||||
counter = Arrays.Clone(J0);
|
||||
blocksRemaining = uint.MaxValue - 1;
|
||||
bufOff = 0;
|
||||
totalLength = 0;
|
||||
|
||||
if (bufBlock != null)
|
||||
{
|
||||
Arrays.Fill(bufBlock, 0);
|
||||
}
|
||||
|
||||
if (clearMac)
|
||||
{
|
||||
macBlock = null;
|
||||
}
|
||||
|
||||
if (initialAssociatedText != null)
|
||||
{
|
||||
ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
|
||||
}
|
||||
}
|
||||
|
||||
private void gCTRBlock(byte[] block, byte[] output, int outOff)
|
||||
{
|
||||
byte[] tmp = GetNextCounterBlock();
|
||||
|
||||
GcmUtilities.Xor(tmp, block);
|
||||
Array.Copy(tmp, 0, output, outOff, BlockSize);
|
||||
|
||||
gHASHBlock(S, forEncryption ? tmp : block);
|
||||
|
||||
totalLength += BlockSize;
|
||||
}
|
||||
|
||||
private void gCTRPartial(byte[] buf, int off, int len, byte[] output, int outOff)
|
||||
{
|
||||
byte[] tmp = GetNextCounterBlock();
|
||||
|
||||
GcmUtilities.Xor(tmp, buf, off, len);
|
||||
Array.Copy(tmp, 0, output, outOff, len);
|
||||
|
||||
gHASHPartial(S, forEncryption ? tmp : buf, 0, len);
|
||||
|
||||
totalLength += (uint)len;
|
||||
}
|
||||
|
||||
private void gHASH(byte[] Y, byte[] b, int len)
|
||||
{
|
||||
for (int pos = 0; pos < len; pos += BlockSize)
|
||||
{
|
||||
int num = System.Math.Min(len - pos, BlockSize);
|
||||
gHASHPartial(Y, b, pos, num);
|
||||
}
|
||||
}
|
||||
|
||||
private void gHASHBlock(byte[] Y, byte[] b)
|
||||
{
|
||||
GcmUtilities.Xor(Y, b);
|
||||
multiplier.MultiplyH(Y);
|
||||
}
|
||||
|
||||
private void gHASHPartial(byte[] Y, byte[] b, int off, int len)
|
||||
{
|
||||
GcmUtilities.Xor(Y, b, off, len);
|
||||
multiplier.MultiplyH(Y);
|
||||
}
|
||||
|
||||
private byte[] GetNextCounterBlock()
|
||||
{
|
||||
if (blocksRemaining == 0)
|
||||
throw new InvalidOperationException("Attempt to process too many blocks");
|
||||
|
||||
blocksRemaining--;
|
||||
|
||||
uint c = 1;
|
||||
c += counter[15]; counter[15] = (byte)c; c >>= 8;
|
||||
c += counter[14]; counter[14] = (byte)c; c >>= 8;
|
||||
c += counter[13]; counter[13] = (byte)c; c >>= 8;
|
||||
c += counter[12]; counter[12] = (byte)c;
|
||||
|
||||
byte[] tmp = new byte[BlockSize];
|
||||
// TODO Sure would be nice if ciphers could operate on int[]
|
||||
cipher.ProcessBlock(counter, 0, tmp, 0);
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f1106778ff743498b89fc01203768166
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
231
Assets/BestHTTP/SecureProtocol/crypto/modes/GOFBBlockCipher.cs
Normal file
231
Assets/BestHTTP/SecureProtocol/crypto/modes/GOFBBlockCipher.cs
Normal file
@@ -0,0 +1,231 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
|
||||
using System;
|
||||
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Modes
|
||||
{
|
||||
/**
|
||||
* implements the GOST 28147 OFB counter mode (GCTR).
|
||||
*/
|
||||
public class GOfbBlockCipher
|
||||
: IBlockCipher
|
||||
{
|
||||
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 GetUnderlyingCipher()
|
||||
{
|
||||
return 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process one block of input from the array in and write it to
|
||||
* the out array.
|
||||
*
|
||||
* @param in the array containing the input data.
|
||||
* @param inOff offset into the in array the data starts at.
|
||||
* @param out 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)
|
||||
{
|
||||
if ((inOff + blockSize) > input.Length)
|
||||
{
|
||||
throw new DataLengthException("input buffer too short");
|
||||
}
|
||||
|
||||
if ((outOff + blockSize) > output.Length)
|
||||
{
|
||||
throw new DataLengthException("output buffer too short");
|
||||
}
|
||||
|
||||
if (firstStep)
|
||||
{
|
||||
firstStep = false;
|
||||
cipher.ProcessBlock(ofbV, 0, ofbOutV, 0);
|
||||
N3 = bytesToint(ofbOutV, 0);
|
||||
N4 = bytesToint(ofbOutV, 4);
|
||||
}
|
||||
N3 += C2;
|
||||
N4 += C1;
|
||||
intTobytes(N3, ofbV, 0);
|
||||
intTobytes(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;
|
||||
}
|
||||
|
||||
/**
|
||||
* reset the feedback vector back to the IV and reset the underlying
|
||||
* cipher.
|
||||
*/
|
||||
public void Reset()
|
||||
{
|
||||
Array.Copy(IV, 0, ofbV, 0, IV.Length);
|
||||
|
||||
cipher.Reset();
|
||||
}
|
||||
|
||||
//array of bytes to type int
|
||||
private int bytesToint(
|
||||
byte[] inBytes,
|
||||
int inOff)
|
||||
{
|
||||
return (int)((inBytes[inOff + 3] << 24) & 0xff000000) + ((inBytes[inOff + 2] << 16) & 0xff0000) +
|
||||
((inBytes[inOff + 1] << 8) & 0xff00) + (inBytes[inOff] & 0xff);
|
||||
}
|
||||
|
||||
//int to array of bytes
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3008300e761cc48a99bba74960fff771
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
109
Assets/BestHTTP/SecureProtocol/crypto/modes/IAeadBlockCipher.cs
Normal file
109
Assets/BestHTTP/SecureProtocol/crypto/modes/IAeadBlockCipher.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Modes
|
||||
{
|
||||
/// <summary>
|
||||
/// A block cipher mode that includes authenticated encryption with a streaming mode
|
||||
/// and optional associated data.</summary>
|
||||
/// <see cref="AeadParameters"/>
|
||||
public interface IAeadBlockCipher
|
||||
{
|
||||
/// <summary>The name of the algorithm this cipher implements.</summary>
|
||||
string AlgorithmName { get; }
|
||||
|
||||
/// <summary>The block cipher underlying this algorithm.</summary>
|
||||
IBlockCipher GetUnderlyingCipher();
|
||||
|
||||
/// <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);
|
||||
|
||||
/// <returns>The block size for this cipher, in bytes.</returns>
|
||||
int GetBlockSize();
|
||||
|
||||
/// <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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e804d368f67c4a409dad23d93d06e7e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
567
Assets/BestHTTP/SecureProtocol/crypto/modes/OCBBlockCipher.cs
Normal file
567
Assets/BestHTTP/SecureProtocol/crypto/modes/OCBBlockCipher.cs
Normal file
@@ -0,0 +1,567 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Utilities;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Modes
|
||||
{
|
||||
/**
|
||||
* An implementation of <a href="http://tools.ietf.org/html/rfc7253">RFC 7253 on The OCB
|
||||
* Authenticated-Encryption Algorithm</a>, licensed per:
|
||||
*
|
||||
* <blockquote><p><a href="http://www.cs.ucdavis.edu/~rogaway/ocb/license1.pdf">License for
|
||||
* Open-Source Software Implementations of OCB</a> (Jan 9, 2013) - 'License 1'<br/>
|
||||
* Under this license, you are authorized to make, use, and distribute open-source software
|
||||
* implementations of OCB. This license terminates for you if you sue someone over their open-source
|
||||
* software implementation of OCB claiming that you have a patent covering their implementation.
|
||||
* </p><p>
|
||||
* This is a non-binding summary of a legal document (the link above). The parameters of the license
|
||||
* are specified in the license document and that document is controlling.</p></blockquote>
|
||||
*/
|
||||
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 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 IBlockCipher GetUnderlyingCipher()
|
||||
{
|
||||
return mainCipher;
|
||||
}
|
||||
|
||||
public virtual string AlgorithmName
|
||||
{
|
||||
get { return mainCipher.AlgorithmName + "/OCB"; }
|
||||
}
|
||||
|
||||
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 aeadParameters = (AeadParameters) parameters;
|
||||
|
||||
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 parametersWithIV = (ParametersWithIV) parameters;
|
||||
|
||||
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 = Platform.CreateArrayList();
|
||||
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 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public virtual int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
public virtual int DoFinal(byte[] output, int outOff)
|
||||
{
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
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((byte[]) L[L.Count - 1]));
|
||||
}
|
||||
return (byte[])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;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Reset(bool clearMac)
|
||||
{
|
||||
hashCipher.Reset();
|
||||
mainCipher.Reset();
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e87d917585fd3419ab0b1860525ed55b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
186
Assets/BestHTTP/SecureProtocol/crypto/modes/OfbBlockCipher.cs
Normal file
186
Assets/BestHTTP/SecureProtocol/crypto/modes/OfbBlockCipher.cs
Normal file
@@ -0,0 +1,186 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
|
||||
using System;
|
||||
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Modes
|
||||
{
|
||||
/**
|
||||
* implements a Output-FeedBack (OFB) mode on top of a simple cipher.
|
||||
*/
|
||||
public class OfbBlockCipher
|
||||
: IBlockCipher
|
||||
{
|
||||
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 GetUnderlyingCipher()
|
||||
{
|
||||
return 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)
|
||||
{
|
||||
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 "/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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process one block of input from the array in and write it to
|
||||
* the out array.
|
||||
*
|
||||
* @param in the array containing the input data.
|
||||
* @param inOff offset into the in array the data starts at.
|
||||
* @param out 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)
|
||||
{
|
||||
if ((inOff + blockSize) > input.Length)
|
||||
{
|
||||
throw new DataLengthException("input buffer too short");
|
||||
}
|
||||
|
||||
if ((outOff + blockSize) > output.Length)
|
||||
{
|
||||
throw new DataLengthException("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;
|
||||
}
|
||||
|
||||
/**
|
||||
* reset the feedback vector back to the IV and reset the underlying
|
||||
* cipher.
|
||||
*/
|
||||
public void Reset()
|
||||
{
|
||||
Array.Copy(IV, 0, ofbV, 0, IV.Length);
|
||||
|
||||
cipher.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d259215b8984042c991745c07d5fcb59
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,341 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
|
||||
using System;
|
||||
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
|
||||
namespace 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
|
||||
: IBlockCipher
|
||||
{
|
||||
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 GetUnderlyingCipher()
|
||||
{
|
||||
return 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process one block of input from the array in and write it to
|
||||
* the out array.
|
||||
*
|
||||
* @param in the array containing the input data.
|
||||
* @param inOff offset into the in array the data starts at.
|
||||
* @param out 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)
|
||||
{
|
||||
return (forEncryption) ? EncryptBlock(input, inOff, output, outOff) : DecryptBlock(input, inOff, output, outOff);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
cipher.Reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
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();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the appropriate processing for CFB IV mode encryption.
|
||||
*
|
||||
* @param in the array containing the data to be encrypted.
|
||||
* @param inOff offset into the in array the data starts at.
|
||||
* @param out the array the encrypted 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.
|
||||
*/
|
||||
private int EncryptBlock(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
byte[] outBytes,
|
||||
int outOff)
|
||||
{
|
||||
if ((inOff + blockSize) > input.Length)
|
||||
{
|
||||
throw new DataLengthException("input buffer too short");
|
||||
}
|
||||
|
||||
if ((outOff + blockSize) > outBytes.Length)
|
||||
{
|
||||
throw new DataLengthException("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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the appropriate processing for CFB IV mode decryption.
|
||||
*
|
||||
* @param in the array containing the data to be decrypted.
|
||||
* @param inOff offset into the in array the data starts at.
|
||||
* @param out the array the encrypted 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.
|
||||
*/
|
||||
private int DecryptBlock(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
byte[] outBytes,
|
||||
int outOff)
|
||||
{
|
||||
if ((inOff + blockSize) > input.Length)
|
||||
{
|
||||
throw new DataLengthException("input buffer too short");
|
||||
}
|
||||
|
||||
if ((outOff + blockSize) > outBytes.Length)
|
||||
{
|
||||
throw new DataLengthException("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[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
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c3be24a5876346d280a3c39f8a24379
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
124
Assets/BestHTTP/SecureProtocol/crypto/modes/SicBlockCipher.cs
Normal file
124
Assets/BestHTTP/SecureProtocol/crypto/modes/SicBlockCipher.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
|
||||
using System;
|
||||
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Math;
|
||||
|
||||
using Org.BouncyCastle.Utilities;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Modes
|
||||
{
|
||||
/**
|
||||
* Implements the Segmented Integer Counter (SIC) mode on top of a simple
|
||||
* block cipher.
|
||||
*/
|
||||
public class SicBlockCipher
|
||||
: IBlockCipher
|
||||
{
|
||||
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 virtual IBlockCipher GetUnderlyingCipher()
|
||||
{
|
||||
return 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;
|
||||
}
|
||||
|
||||
public virtual void Reset()
|
||||
{
|
||||
Arrays.Fill(counter, (byte)0);
|
||||
Array.Copy(IV, 0, counter, 0, IV.Length);
|
||||
cipher.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2ffd856d6c9dd48aea0fbb1fb66e9019
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/BestHTTP/SecureProtocol/crypto/modes/gcm.meta
Normal file
8
Assets/BestHTTP/SecureProtocol/crypto/modes/gcm.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 11524718565b740b6a99c4cdcb06ce9f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
323
Assets/BestHTTP/SecureProtocol/crypto/modes/gcm/GcmUtilities.cs
Normal file
323
Assets/BestHTTP/SecureProtocol/crypto/modes/gcm/GcmUtilities.cs
Normal file
@@ -0,0 +1,323 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
|
||||
using System;
|
||||
|
||||
using Org.BouncyCastle.Crypto.Utilities;
|
||||
using Org.BouncyCastle.Utilities;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Modes.Gcm
|
||||
{
|
||||
internal abstract class GcmUtilities
|
||||
{
|
||||
private const uint E1 = 0xe1000000;
|
||||
private const ulong E1L = (ulong)E1 << 32;
|
||||
|
||||
private static uint[] GenerateLookup()
|
||||
{
|
||||
uint[] lookup = new uint[256];
|
||||
|
||||
for (int c = 0; c < 256; ++c)
|
||||
{
|
||||
uint v = 0;
|
||||
for (int i = 7; i >= 0; --i)
|
||||
{
|
||||
if ((c & (1 << i)) != 0)
|
||||
{
|
||||
v ^= (E1 >> (7 - i));
|
||||
}
|
||||
}
|
||||
lookup[c] = v;
|
||||
}
|
||||
|
||||
return lookup;
|
||||
}
|
||||
|
||||
private static readonly uint[] LOOKUP = GenerateLookup();
|
||||
|
||||
internal static byte[] OneAsBytes()
|
||||
{
|
||||
byte[] tmp = new byte[16];
|
||||
tmp[0] = 0x80;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
internal static uint[] OneAsUints()
|
||||
{
|
||||
uint[] tmp = new uint[4];
|
||||
tmp[0] = 0x80000000;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
internal static ulong[] OneAsUlongs()
|
||||
{
|
||||
ulong[] tmp = new ulong[2];
|
||||
tmp[0] = 1UL << 63;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
internal static byte[] AsBytes(uint[] x)
|
||||
{
|
||||
return Pack.UInt32_To_BE(x);
|
||||
}
|
||||
|
||||
internal static void AsBytes(uint[] x, byte[] z)
|
||||
{
|
||||
Pack.UInt32_To_BE(x, z, 0);
|
||||
}
|
||||
|
||||
internal static byte[] AsBytes(ulong[] x)
|
||||
{
|
||||
byte[] z = new byte[16];
|
||||
Pack.UInt64_To_BE(x, z, 0);
|
||||
return z;
|
||||
}
|
||||
|
||||
internal static void AsBytes(ulong[] x, byte[] z)
|
||||
{
|
||||
Pack.UInt64_To_BE(x, z, 0);
|
||||
}
|
||||
|
||||
internal static uint[] AsUints(byte[] bs)
|
||||
{
|
||||
uint[] output = new uint[4];
|
||||
Pack.BE_To_UInt32(bs, 0, output);
|
||||
return output;
|
||||
}
|
||||
|
||||
internal static void AsUints(byte[] bs, uint[] output)
|
||||
{
|
||||
Pack.BE_To_UInt32(bs, 0, output);
|
||||
}
|
||||
|
||||
internal static ulong[] AsUlongs(byte[] x)
|
||||
{
|
||||
ulong[] z = new ulong[2];
|
||||
Pack.BE_To_UInt64(x, 0, z);
|
||||
return z;
|
||||
}
|
||||
|
||||
public static void AsUlongs(byte[] x, ulong[] z)
|
||||
{
|
||||
Pack.BE_To_UInt64(x, 0, z);
|
||||
}
|
||||
|
||||
internal static void Multiply(byte[] x, byte[] y)
|
||||
{
|
||||
uint[] t1 = GcmUtilities.AsUints(x);
|
||||
uint[] t2 = GcmUtilities.AsUints(y);
|
||||
GcmUtilities.Multiply(t1, t2);
|
||||
GcmUtilities.AsBytes(t1, x);
|
||||
}
|
||||
|
||||
internal static void Multiply(uint[] x, uint[] y)
|
||||
{
|
||||
uint r00 = x[0], r01 = x[1], r02 = x[2], r03 = x[3];
|
||||
uint r10 = 0, r11 = 0, r12 = 0, r13 = 0;
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
int bits = (int)y[i];
|
||||
for (int j = 0; j < 32; ++j)
|
||||
{
|
||||
uint m1 = (uint)(bits >> 31); bits <<= 1;
|
||||
r10 ^= (r00 & m1);
|
||||
r11 ^= (r01 & m1);
|
||||
r12 ^= (r02 & m1);
|
||||
r13 ^= (r03 & m1);
|
||||
|
||||
uint m2 = (uint)((int)(r03 << 31) >> 8);
|
||||
r03 = (r03 >> 1) | (r02 << 31);
|
||||
r02 = (r02 >> 1) | (r01 << 31);
|
||||
r01 = (r01 >> 1) | (r00 << 31);
|
||||
r00 = (r00 >> 1) ^ (m2 & E1);
|
||||
}
|
||||
}
|
||||
|
||||
x[0] = r10;
|
||||
x[1] = r11;
|
||||
x[2] = r12;
|
||||
x[3] = r13;
|
||||
}
|
||||
|
||||
internal static void Multiply(ulong[] x, ulong[] y)
|
||||
{
|
||||
ulong r00 = x[0], r01 = x[1], r10 = 0, r11 = 0;
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
long bits = (long)y[i];
|
||||
for (int j = 0; j < 64; ++j)
|
||||
{
|
||||
ulong m1 = (ulong)(bits >> 63); bits <<= 1;
|
||||
r10 ^= (r00 & m1);
|
||||
r11 ^= (r01 & m1);
|
||||
|
||||
ulong m2 = (ulong)((long)(r01 << 63) >> 8);
|
||||
r01 = (r01 >> 1) | (r00 << 63);
|
||||
r00 = (r00 >> 1) ^ (m2 & E1L);
|
||||
}
|
||||
}
|
||||
|
||||
x[0] = r10;
|
||||
x[1] = r11;
|
||||
}
|
||||
|
||||
// P is the value with only bit i=1 set
|
||||
internal static void MultiplyP(uint[] x)
|
||||
{
|
||||
uint m = (uint)((int)ShiftRight(x) >> 8);
|
||||
x[0] ^= (m & E1);
|
||||
}
|
||||
|
||||
internal static void MultiplyP(uint[] x, uint[] z)
|
||||
{
|
||||
uint m = (uint)((int)ShiftRight(x, z) >> 8);
|
||||
z[0] ^= (m & E1);
|
||||
}
|
||||
|
||||
internal static void MultiplyP8(uint[] x)
|
||||
{
|
||||
// for (int i = 8; i != 0; --i)
|
||||
// {
|
||||
// MultiplyP(x);
|
||||
// }
|
||||
|
||||
uint c = ShiftRightN(x, 8);
|
||||
x[0] ^= LOOKUP[c >> 24];
|
||||
}
|
||||
|
||||
internal static void MultiplyP8(uint[] x, uint[] y)
|
||||
{
|
||||
uint c = ShiftRightN(x, 8, y);
|
||||
y[0] ^= LOOKUP[c >> 24];
|
||||
}
|
||||
|
||||
internal static uint ShiftRight(uint[] x)
|
||||
{
|
||||
uint b = x[0];
|
||||
x[0] = b >> 1;
|
||||
uint c = b << 31;
|
||||
b = x[1];
|
||||
x[1] = (b >> 1) | c;
|
||||
c = b << 31;
|
||||
b = x[2];
|
||||
x[2] = (b >> 1) | c;
|
||||
c = b << 31;
|
||||
b = x[3];
|
||||
x[3] = (b >> 1) | c;
|
||||
return b << 31;
|
||||
}
|
||||
|
||||
internal static uint ShiftRight(uint[] x, uint[] z)
|
||||
{
|
||||
uint b = x[0];
|
||||
z[0] = b >> 1;
|
||||
uint c = b << 31;
|
||||
b = x[1];
|
||||
z[1] = (b >> 1) | c;
|
||||
c = b << 31;
|
||||
b = x[2];
|
||||
z[2] = (b >> 1) | c;
|
||||
c = b << 31;
|
||||
b = x[3];
|
||||
z[3] = (b >> 1) | c;
|
||||
return b << 31;
|
||||
}
|
||||
|
||||
internal static uint ShiftRightN(uint[] x, int n)
|
||||
{
|
||||
uint b = x[0]; int nInv = 32 - n;
|
||||
x[0] = b >> n;
|
||||
uint c = b << nInv;
|
||||
b = x[1];
|
||||
x[1] = (b >> n) | c;
|
||||
c = b << nInv;
|
||||
b = x[2];
|
||||
x[2] = (b >> n) | c;
|
||||
c = b << nInv;
|
||||
b = x[3];
|
||||
x[3] = (b >> n) | c;
|
||||
return b << nInv;
|
||||
}
|
||||
|
||||
internal static uint ShiftRightN(uint[] x, int n, uint[] z)
|
||||
{
|
||||
uint b = x[0]; int nInv = 32 - n;
|
||||
z[0] = b >> n;
|
||||
uint c = b << nInv;
|
||||
b = x[1];
|
||||
z[1] = (b >> n) | c;
|
||||
c = b << nInv;
|
||||
b = x[2];
|
||||
z[2] = (b >> n) | c;
|
||||
c = b << nInv;
|
||||
b = x[3];
|
||||
z[3] = (b >> n) | c;
|
||||
return b << nInv;
|
||||
}
|
||||
|
||||
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 yLen)
|
||||
{
|
||||
while (--yLen >= 0)
|
||||
{
|
||||
x[yLen] ^= y[yOff + yLen];
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Xor(byte[] x, byte[] y, byte[] z)
|
||||
{
|
||||
int i = 0;
|
||||
do
|
||||
{
|
||||
z[i] = (byte)(x[i] ^ y[i]); ++i;
|
||||
z[i] = (byte)(x[i] ^ y[i]); ++i;
|
||||
z[i] = (byte)(x[i] ^ y[i]); ++i;
|
||||
z[i] = (byte)(x[i] ^ y[i]); ++i;
|
||||
}
|
||||
while (i < 16);
|
||||
}
|
||||
|
||||
internal static void Xor(uint[] x, uint[] y)
|
||||
{
|
||||
x[0] ^= y[0];
|
||||
x[1] ^= y[1];
|
||||
x[2] ^= y[2];
|
||||
x[3] ^= y[3];
|
||||
}
|
||||
|
||||
internal static void Xor(uint[] x, uint[] y, uint[] z)
|
||||
{
|
||||
z[0] = x[0] ^ y[0];
|
||||
z[1] = x[1] ^ y[1];
|
||||
z[2] = x[2] ^ y[2];
|
||||
z[3] = x[3] ^ y[3];
|
||||
}
|
||||
|
||||
internal static void Xor(ulong[] x, ulong[] y)
|
||||
{
|
||||
x[0] ^= y[0];
|
||||
x[1] ^= y[1];
|
||||
}
|
||||
|
||||
internal static void Xor(ulong[] x, ulong[] y, ulong[] z)
|
||||
{
|
||||
z[0] = x[0] ^ y[0];
|
||||
z[1] = x[1] ^ y[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9161f2ba060f64038a7d15dc37938e02
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,14 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
|
||||
using System;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Modes.Gcm
|
||||
{
|
||||
public interface IGcmExponentiator
|
||||
{
|
||||
void Init(byte[] x);
|
||||
void ExponentiateX(long pow, byte[] output);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2de0b4d04da7432b876404ab38fd59b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,14 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
|
||||
using System;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Modes.Gcm
|
||||
{
|
||||
public interface IGcmMultiplier
|
||||
{
|
||||
void Init(byte[] H);
|
||||
void MultiplyH(byte[] x);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e6b652ec310a4ff39aa6e37f36e9454
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,63 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
using Org.BouncyCastle.Utilities;
|
||||
|
||||
namespace 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 lookupPowX2;
|
||||
|
||||
public void Init(byte[] x)
|
||||
{
|
||||
uint[] y = GcmUtilities.AsUints(x);
|
||||
if (lookupPowX2 != null && Arrays.AreEqual(y, (uint[])lookupPowX2[0]))
|
||||
return;
|
||||
|
||||
lookupPowX2 = Platform.CreateArrayList(8);
|
||||
lookupPowX2.Add(y);
|
||||
}
|
||||
|
||||
public void ExponentiateX(long pow, byte[] output)
|
||||
{
|
||||
uint[] y = GcmUtilities.OneAsUints();
|
||||
int bit = 0;
|
||||
while (pow > 0)
|
||||
{
|
||||
if ((pow & 1L) != 0)
|
||||
{
|
||||
EnsureAvailable(bit);
|
||||
GcmUtilities.Multiply(y, (uint[])lookupPowX2[bit]);
|
||||
}
|
||||
++bit;
|
||||
pow >>= 1;
|
||||
}
|
||||
|
||||
GcmUtilities.AsBytes(y, output);
|
||||
}
|
||||
|
||||
private void EnsureAvailable(int bit)
|
||||
{
|
||||
int count = lookupPowX2.Count;
|
||||
if (count <= bit)
|
||||
{
|
||||
uint[] tmp = (uint[])lookupPowX2[count - 1];
|
||||
do
|
||||
{
|
||||
tmp = Arrays.Clone(tmp);
|
||||
GcmUtilities.Multiply(tmp, tmp);
|
||||
lookupPowX2.Add(tmp);
|
||||
}
|
||||
while (++count <= bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9baec52f99b21430ea2a70d383656640
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,107 @@
|
||||
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
||||
|
||||
using System;
|
||||
|
||||
using Org.BouncyCastle.Crypto.Utilities;
|
||||
using Org.BouncyCastle.Utilities;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Modes.Gcm
|
||||
{
|
||||
public class Tables8kGcmMultiplier
|
||||
: IGcmMultiplier
|
||||
{
|
||||
private byte[] H;
|
||||
private uint[][][] M;
|
||||
|
||||
public void Init(byte[] H)
|
||||
{
|
||||
if (M == null)
|
||||
{
|
||||
M = new uint[32][][];
|
||||
}
|
||||
else if (Arrays.AreEqual(this.H, H))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.H = Arrays.Clone(H);
|
||||
|
||||
M[0] = new uint[16][];
|
||||
M[1] = new uint[16][];
|
||||
M[0][0] = new uint[4];
|
||||
M[1][0] = new uint[4];
|
||||
M[1][8] = GcmUtilities.AsUints(H);
|
||||
|
||||
for (int j = 4; j >= 1; j >>= 1)
|
||||
{
|
||||
uint[] tmp = (uint[])M[1][j + j].Clone();
|
||||
GcmUtilities.MultiplyP(tmp);
|
||||
M[1][j] = tmp;
|
||||
}
|
||||
|
||||
{
|
||||
uint[] tmp = (uint[])M[1][1].Clone();
|
||||
GcmUtilities.MultiplyP(tmp);
|
||||
M[0][8] = tmp;
|
||||
}
|
||||
|
||||
for (int j = 4; j >= 1; j >>= 1)
|
||||
{
|
||||
uint[] tmp = (uint[])M[0][j + j].Clone();
|
||||
GcmUtilities.MultiplyP(tmp);
|
||||
M[0][j] = tmp;
|
||||
}
|
||||
|
||||
for (int i = 0; ; )
|
||||
{
|
||||
for (int j = 2; j < 16; j += j)
|
||||
{
|
||||
for (int k = 1; k < j; ++k)
|
||||
{
|
||||
uint[] tmp = (uint[])M[i][j].Clone();
|
||||
GcmUtilities.Xor(tmp, M[i][k]);
|
||||
M[i][j + k] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
if (++i == 32) return;
|
||||
|
||||
if (i > 1)
|
||||
{
|
||||
M[i] = new uint[16][];
|
||||
M[i][0] = new uint[4];
|
||||
for (int j = 8; j > 0; j >>= 1)
|
||||
{
|
||||
uint[] tmp = (uint[])M[i - 2][j].Clone();
|
||||
GcmUtilities.MultiplyP8(tmp);
|
||||
M[i][j] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void MultiplyH(byte[] x)
|
||||
{
|
||||
uint[] z = new uint[4];
|
||||
for (int i = 15; i >= 0; --i)
|
||||
{
|
||||
//GcmUtilities.Xor(z, M[i + i][x[i] & 0x0f]);
|
||||
uint[] m = M[i + i][x[i] & 0x0f];
|
||||
z[0] ^= m[0];
|
||||
z[1] ^= m[1];
|
||||
z[2] ^= m[2];
|
||||
z[3] ^= m[3];
|
||||
//GcmUtilities.Xor(z, M[i + i + 1][(x[i] & 0xf0) >> 4]);
|
||||
m = M[i + i + 1][(x[i] & 0xf0) >> 4];
|
||||
z[0] ^= m[0];
|
||||
z[1] ^= m[1];
|
||||
z[2] ^= m[2];
|
||||
z[3] ^= m[3];
|
||||
}
|
||||
|
||||
Pack.UInt32_To_BE(z, x, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f51ca98d86f5a499b849d03b824036ac
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user