KCcmBlockCipher
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Utilities;
using System;
using System.IO;
using System.Text;
namespace Org.BouncyCastle.Crypto.Modes
{
public class KCcmBlockCipher : IAeadBlockCipher, IAeadCipher
{
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";
public virtual IBlockCipher UnderlyingCipher => engine;
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)
{
AeadParameters aeadParameters = parameters as AeadParameters;
ICipherParameters parameters2;
if (aeadParameters != null) {
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 {
ParametersWithIV parametersWithIV = parameters as ParametersWithIV;
if (parametersWithIV == null)
throw new ArgumentException("Invalid parameters specified");
nonce = parametersWithIV.GetIV();
macSize = engine.GetBlockSize();
initialAssociatedText = null;
parameters2 = parametersWithIV.Parameters;
}
mac = new byte[macSize];
this.forEncryption = forEncryption;
engine.Init(true, parameters2);
counter[0] = 1;
if (initialAssociatedText != null)
ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
}
public virtual int GetBlockSize()
{
return engine.GetBlockSize();
}
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(true, macSize);
engine.ProcessBlock(G1, 0, macBlock, 0);
intToBytes(assocLen, buffer, 0);
if (assocLen <= engine.GetBlockSize() - Nb_) {
for (int i = 0; i < assocLen; i++) {
buffer[i + Nb_] ^= assocText[assocOff + i];
}
for (int j = 0; j < engine.GetBlockSize(); j++) {
macBlock[j] ^= buffer[j];
}
engine.ProcessBlock(macBlock, 0, macBlock, 0);
} else {
for (int k = 0; k < engine.GetBlockSize(); k++) {
macBlock[k] ^= buffer[k];
}
engine.ProcessBlock(macBlock, 0, macBlock, 0);
for (int num = assocLen; num != 0; num -= engine.GetBlockSize()) {
for (int l = 0; l < engine.GetBlockSize(); l++) {
macBlock[l] ^= 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 = Convert.ToInt32(associatedText.Length);
int dataLen = Convert.ToInt32(data.Length) - ((!forEncryption) ? macSize : 0);
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, output, outOff);
num -= engine.GetBlockSize();
inOff += engine.GetBlockSize();
outOff += engine.GetBlockSize();
}
for (int i = 0; i < counter.Length; i++) {
s[i] += 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 num2 = len / engine.GetBlockSize();
for (int k = 0; k < num2; k++) {
ProcessBlock(input, inOff, output, outOff);
inOff += engine.GetBlockSize();
outOff += engine.GetBlockSize();
}
if (len > inOff) {
for (int l = 0; l < counter.Length; l++) {
s[l] += 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++) {
s[n] += 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[] array = new byte[macSize];
Array.Copy(buffer, 0, array, 0, macSize);
if (!Arrays.FixedTimeEquals(mac, array))
throw new InvalidCipherTextException("mac check failed");
Reset();
return len - macSize;
}
private void CalculateMac(byte[] authText, int authOff, int len)
{
int blockSize = engine.GetBlockSize();
int num = len;
while (num > 0) {
for (int i = 0; i < blockSize; i++) {
macBlock[i] ^= authText[authOff + i];
}
engine.ProcessBlock(macBlock, 0, macBlock, 0);
num -= blockSize;
authOff += blockSize;
}
}
private void ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
{
for (int i = 0; i < counter.Length; i++) {
s[i] += 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]);
}
}
public virtual int DoFinal(byte[] output, int outOff)
{
byte[] input = data.GetBuffer();
int len = Convert.ToInt32(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(0);
associatedText.SetLength(0);
if (initialAssociatedText != null)
ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
}
private void intToBytes(int num, byte[] outBytes, int outOff)
{
outBytes[outOff + 3] = (byte)(num >> 24);
outBytes[outOff + 2] = (byte)(num >> 16);
outBytes[outOff + 1] = (byte)(num >> 8);
outBytes[outOff] = (byte)num;
}
private byte getFlag(bool authTextPresents, int macSize)
{
StringBuilder 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);
}
}
}