419 lines
10 KiB
C#
419 lines
10 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Text;
|
|
using Org.BouncyCastle.Crypto.Parameters;
|
|
using Org.BouncyCastle.Utilities;
|
|
|
|
namespace Org.BouncyCastle.Crypto.Modes;
|
|
|
|
public class KCcmBlockCipher : IAeadBlockCipher
|
|
{
|
|
private static readonly int BYTES_IN_INT = 4;
|
|
|
|
private static readonly int BITS_IN_BYTE = 8;
|
|
|
|
private static readonly int MAX_MAC_BIT_LENGTH = 512;
|
|
|
|
private static readonly int MIN_MAC_BIT_LENGTH = 64;
|
|
|
|
private IBlockCipher engine;
|
|
|
|
private int macSize;
|
|
|
|
private bool forEncryption;
|
|
|
|
private byte[] initialAssociatedText;
|
|
|
|
private byte[] mac;
|
|
|
|
private byte[] macBlock;
|
|
|
|
private byte[] nonce;
|
|
|
|
private byte[] G1;
|
|
|
|
private byte[] buffer;
|
|
|
|
private byte[] s;
|
|
|
|
private byte[] counter;
|
|
|
|
private readonly MemoryStream associatedText = new MemoryStream();
|
|
|
|
private readonly MemoryStream data = new MemoryStream();
|
|
|
|
private int Nb_ = 4;
|
|
|
|
public virtual string AlgorithmName => engine.AlgorithmName + "/KCCM";
|
|
|
|
private void setNb(int Nb)
|
|
{
|
|
if (Nb == 4 || Nb == 6 || Nb == 8)
|
|
{
|
|
Nb_ = Nb;
|
|
return;
|
|
}
|
|
throw new ArgumentException("Nb = 4 is recommended by DSTU7624 but can be changed to only 6 or 8 in this implementation");
|
|
}
|
|
|
|
public KCcmBlockCipher(IBlockCipher engine)
|
|
: this(engine, 4)
|
|
{
|
|
}
|
|
|
|
public KCcmBlockCipher(IBlockCipher engine, int Nb)
|
|
{
|
|
this.engine = engine;
|
|
macSize = engine.GetBlockSize();
|
|
nonce = new byte[engine.GetBlockSize()];
|
|
initialAssociatedText = new byte[engine.GetBlockSize()];
|
|
mac = new byte[engine.GetBlockSize()];
|
|
macBlock = new byte[engine.GetBlockSize()];
|
|
G1 = new byte[engine.GetBlockSize()];
|
|
buffer = new byte[engine.GetBlockSize()];
|
|
s = new byte[engine.GetBlockSize()];
|
|
counter = new byte[engine.GetBlockSize()];
|
|
setNb(Nb);
|
|
}
|
|
|
|
public virtual void Init(bool forEncryption, ICipherParameters parameters)
|
|
{
|
|
ICipherParameters parameters2;
|
|
if (parameters is AeadParameters)
|
|
{
|
|
AeadParameters aeadParameters = (AeadParameters)parameters;
|
|
if (aeadParameters.MacSize > MAX_MAC_BIT_LENGTH || aeadParameters.MacSize < MIN_MAC_BIT_LENGTH || aeadParameters.MacSize % 8 != 0)
|
|
{
|
|
throw new ArgumentException("Invalid mac size specified");
|
|
}
|
|
nonce = aeadParameters.GetNonce();
|
|
macSize = aeadParameters.MacSize / BITS_IN_BYTE;
|
|
initialAssociatedText = aeadParameters.GetAssociatedText();
|
|
parameters2 = aeadParameters.Key;
|
|
}
|
|
else
|
|
{
|
|
if (!(parameters is ParametersWithIV))
|
|
{
|
|
throw new ArgumentException("Invalid parameters specified");
|
|
}
|
|
nonce = ((ParametersWithIV)parameters).GetIV();
|
|
macSize = engine.GetBlockSize();
|
|
initialAssociatedText = null;
|
|
parameters2 = ((ParametersWithIV)parameters).Parameters;
|
|
}
|
|
mac = new byte[macSize];
|
|
this.forEncryption = forEncryption;
|
|
engine.Init(forEncryption: true, parameters2);
|
|
counter[0] = 1;
|
|
if (initialAssociatedText != null)
|
|
{
|
|
ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
|
|
}
|
|
}
|
|
|
|
public virtual int GetBlockSize()
|
|
{
|
|
return engine.GetBlockSize();
|
|
}
|
|
|
|
public virtual IBlockCipher GetUnderlyingCipher()
|
|
{
|
|
return engine;
|
|
}
|
|
|
|
public virtual void ProcessAadByte(byte input)
|
|
{
|
|
associatedText.WriteByte(input);
|
|
}
|
|
|
|
public virtual void ProcessAadBytes(byte[] input, int inOff, int len)
|
|
{
|
|
associatedText.Write(input, inOff, len);
|
|
}
|
|
|
|
private void ProcessAAD(byte[] assocText, int assocOff, int assocLen, int dataLen)
|
|
{
|
|
if (assocLen - assocOff < engine.GetBlockSize())
|
|
{
|
|
throw new ArgumentException("authText buffer too short");
|
|
}
|
|
if (assocLen % engine.GetBlockSize() != 0)
|
|
{
|
|
throw new ArgumentException("padding not supported");
|
|
}
|
|
Array.Copy(nonce, 0, G1, 0, nonce.Length - Nb_ - 1);
|
|
intToBytes(dataLen, buffer, 0);
|
|
Array.Copy(buffer, 0, G1, nonce.Length - Nb_ - 1, BYTES_IN_INT);
|
|
G1[G1.Length - 1] = getFlag(authTextPresents: true, macSize);
|
|
engine.ProcessBlock(G1, 0, macBlock, 0);
|
|
intToBytes(assocLen, buffer, 0);
|
|
if (assocLen <= engine.GetBlockSize() - Nb_)
|
|
{
|
|
for (int i = 0; i < assocLen; i++)
|
|
{
|
|
byte[] array2;
|
|
byte[] array = (array2 = buffer);
|
|
int num = i + Nb_;
|
|
nint num2 = num;
|
|
array[num] = (byte)(array2[num2] ^ assocText[assocOff + i]);
|
|
}
|
|
for (int j = 0; j < engine.GetBlockSize(); j++)
|
|
{
|
|
byte[] array2;
|
|
byte[] array3 = (array2 = macBlock);
|
|
int num3 = j;
|
|
nint num2 = num3;
|
|
array3[num3] = (byte)(array2[num2] ^ buffer[j]);
|
|
}
|
|
engine.ProcessBlock(macBlock, 0, macBlock, 0);
|
|
return;
|
|
}
|
|
for (int k = 0; k < engine.GetBlockSize(); k++)
|
|
{
|
|
byte[] array2;
|
|
byte[] array4 = (array2 = macBlock);
|
|
int num4 = k;
|
|
nint num2 = num4;
|
|
array4[num4] = (byte)(array2[num2] ^ buffer[k]);
|
|
}
|
|
engine.ProcessBlock(macBlock, 0, macBlock, 0);
|
|
for (int num5 = assocLen; num5 != 0; num5 -= engine.GetBlockSize())
|
|
{
|
|
for (int l = 0; l < engine.GetBlockSize(); l++)
|
|
{
|
|
byte[] array2;
|
|
byte[] array5 = (array2 = macBlock);
|
|
int num6 = l;
|
|
nint num2 = num6;
|
|
array5[num6] = (byte)(array2[num2] ^ assocText[l + assocOff]);
|
|
}
|
|
engine.ProcessBlock(macBlock, 0, macBlock, 0);
|
|
assocOff += engine.GetBlockSize();
|
|
}
|
|
}
|
|
|
|
public virtual int ProcessByte(byte input, byte[] output, int outOff)
|
|
{
|
|
data.WriteByte(input);
|
|
return 0;
|
|
}
|
|
|
|
public virtual int ProcessBytes(byte[] input, int inOff, int inLen, byte[] output, int outOff)
|
|
{
|
|
Check.DataLength(input, inOff, inLen, "input buffer too short");
|
|
data.Write(input, inOff, inLen);
|
|
return 0;
|
|
}
|
|
|
|
public int ProcessPacket(byte[] input, int inOff, int len, byte[] output, int outOff)
|
|
{
|
|
Check.DataLength(input, inOff, len, "input buffer too short");
|
|
Check.OutputLength(output, outOff, len, "output buffer too short");
|
|
if (associatedText.Length > 0)
|
|
{
|
|
byte[] assocText = associatedText.GetBuffer();
|
|
int assocLen = (int)associatedText.Length;
|
|
int dataLen = (int)(forEncryption ? data.Length : ((int)data.Length - macSize));
|
|
ProcessAAD(assocText, 0, assocLen, dataLen);
|
|
}
|
|
if (forEncryption)
|
|
{
|
|
Check.DataLength(len % engine.GetBlockSize() != 0, "partial blocks not supported");
|
|
CalculateMac(input, inOff, len);
|
|
engine.ProcessBlock(nonce, 0, s, 0);
|
|
int num = len;
|
|
while (num > 0)
|
|
{
|
|
ProcessBlock(input, inOff, len, output, outOff);
|
|
num -= engine.GetBlockSize();
|
|
inOff += engine.GetBlockSize();
|
|
outOff += engine.GetBlockSize();
|
|
}
|
|
for (int i = 0; i < counter.Length; i++)
|
|
{
|
|
byte[] array2;
|
|
byte[] array = (array2 = s);
|
|
int num2 = i;
|
|
nint num3 = num2;
|
|
array[num2] = (byte)(array2[num3] + counter[i]);
|
|
}
|
|
engine.ProcessBlock(s, 0, buffer, 0);
|
|
for (int j = 0; j < macSize; j++)
|
|
{
|
|
output[outOff + j] = (byte)(buffer[j] ^ macBlock[j]);
|
|
}
|
|
Array.Copy(macBlock, 0, mac, 0, macSize);
|
|
Reset();
|
|
return len + macSize;
|
|
}
|
|
Check.DataLength((len - macSize) % engine.GetBlockSize() != 0, "partial blocks not supported");
|
|
engine.ProcessBlock(nonce, 0, s, 0);
|
|
int num4 = len / engine.GetBlockSize();
|
|
for (int k = 0; k < num4; k++)
|
|
{
|
|
ProcessBlock(input, inOff, len, output, outOff);
|
|
inOff += engine.GetBlockSize();
|
|
outOff += engine.GetBlockSize();
|
|
}
|
|
if (len > inOff)
|
|
{
|
|
for (int l = 0; l < counter.Length; l++)
|
|
{
|
|
byte[] array2;
|
|
byte[] array3 = (array2 = s);
|
|
int num5 = l;
|
|
nint num3 = num5;
|
|
array3[num5] = (byte)(array2[num3] + counter[l]);
|
|
}
|
|
engine.ProcessBlock(s, 0, buffer, 0);
|
|
for (int m = 0; m < macSize; m++)
|
|
{
|
|
output[outOff + m] = (byte)(buffer[m] ^ input[inOff + m]);
|
|
}
|
|
outOff += macSize;
|
|
}
|
|
for (int n = 0; n < counter.Length; n++)
|
|
{
|
|
byte[] array2;
|
|
byte[] array4 = (array2 = s);
|
|
int num6 = n;
|
|
nint num3 = num6;
|
|
array4[num6] = (byte)(array2[num3] + counter[n]);
|
|
}
|
|
engine.ProcessBlock(s, 0, buffer, 0);
|
|
Array.Copy(output, outOff - macSize, buffer, 0, macSize);
|
|
CalculateMac(output, 0, outOff - macSize);
|
|
Array.Copy(macBlock, 0, mac, 0, macSize);
|
|
byte[] array5 = new byte[macSize];
|
|
Array.Copy(buffer, 0, array5, 0, macSize);
|
|
if (!Arrays.ConstantTimeAreEqual(mac, array5))
|
|
{
|
|
throw new InvalidCipherTextException("mac check failed");
|
|
}
|
|
Reset();
|
|
return len - macSize;
|
|
}
|
|
|
|
private void ProcessBlock(byte[] input, int inOff, int len, byte[] output, int outOff)
|
|
{
|
|
for (int i = 0; i < counter.Length; i++)
|
|
{
|
|
byte[] array2;
|
|
byte[] array = (array2 = s);
|
|
int num = i;
|
|
nint num2 = num;
|
|
array[num] = (byte)(array2[num2] + counter[i]);
|
|
}
|
|
engine.ProcessBlock(s, 0, buffer, 0);
|
|
for (int j = 0; j < engine.GetBlockSize(); j++)
|
|
{
|
|
output[outOff + j] = (byte)(buffer[j] ^ input[inOff + j]);
|
|
}
|
|
}
|
|
|
|
private void CalculateMac(byte[] authText, int authOff, int len)
|
|
{
|
|
int num = len;
|
|
while (num > 0)
|
|
{
|
|
for (int i = 0; i < engine.GetBlockSize(); i++)
|
|
{
|
|
byte[] array2;
|
|
byte[] array = (array2 = macBlock);
|
|
int num2 = i;
|
|
nint num3 = num2;
|
|
array[num2] = (byte)(array2[num3] ^ authText[authOff + i]);
|
|
}
|
|
engine.ProcessBlock(macBlock, 0, macBlock, 0);
|
|
num -= engine.GetBlockSize();
|
|
authOff += engine.GetBlockSize();
|
|
}
|
|
}
|
|
|
|
public virtual int DoFinal(byte[] output, int outOff)
|
|
{
|
|
byte[] input = data.GetBuffer();
|
|
int len = (int)data.Length;
|
|
int result = ProcessPacket(input, 0, len, output, outOff);
|
|
Reset();
|
|
return result;
|
|
}
|
|
|
|
public virtual byte[] GetMac()
|
|
{
|
|
return Arrays.Clone(mac);
|
|
}
|
|
|
|
public virtual int GetUpdateOutputSize(int len)
|
|
{
|
|
return len;
|
|
}
|
|
|
|
public virtual int GetOutputSize(int len)
|
|
{
|
|
return len + macSize;
|
|
}
|
|
|
|
public virtual void Reset()
|
|
{
|
|
Arrays.Fill(G1, 0);
|
|
Arrays.Fill(buffer, 0);
|
|
Arrays.Fill(counter, 0);
|
|
Arrays.Fill(macBlock, 0);
|
|
counter[0] = 1;
|
|
data.SetLength(0L);
|
|
associatedText.SetLength(0L);
|
|
if (initialAssociatedText != null)
|
|
{
|
|
ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
|
|
}
|
|
}
|
|
|
|
private void intToBytes(int num, byte[] outBytes, int outOff)
|
|
{
|
|
outBytes[outOff + 3] = (byte)(num >> 24);
|
|
outBytes[outOff + 2] = (byte)(num >> 16);
|
|
outBytes[outOff + 1] = (byte)(num >> 8);
|
|
outBytes[outOff] = (byte)num;
|
|
}
|
|
|
|
private byte getFlag(bool authTextPresents, int macSize)
|
|
{
|
|
StringBuilder stringBuilder = new StringBuilder();
|
|
if (authTextPresents)
|
|
{
|
|
stringBuilder.Append("1");
|
|
}
|
|
else
|
|
{
|
|
stringBuilder.Append("0");
|
|
}
|
|
switch (macSize)
|
|
{
|
|
case 8:
|
|
stringBuilder.Append("010");
|
|
break;
|
|
case 16:
|
|
stringBuilder.Append("011");
|
|
break;
|
|
case 32:
|
|
stringBuilder.Append("100");
|
|
break;
|
|
case 48:
|
|
stringBuilder.Append("101");
|
|
break;
|
|
case 64:
|
|
stringBuilder.Append("110");
|
|
break;
|
|
}
|
|
string text = Convert.ToString(Nb_ - 1, 2);
|
|
while (text.Length < 4)
|
|
{
|
|
text = new StringBuilder(text).Insert(0, "0").ToString();
|
|
}
|
|
stringBuilder.Append(text);
|
|
return (byte)Convert.ToInt32(stringBuilder.ToString(), 2);
|
|
}
|
|
}
|