Grain128AeadEngine
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Utilities;
using Org.BouncyCastle.Utilities;
using System;
using System.IO;
namespace Org.BouncyCastle.Crypto.Engines
{
public sealed class Grain128AeadEngine : IAeadCipher
{
private enum State
{
Uninitialized,
EncInit,
EncAad,
EncData,
EncFinal,
DecInit,
DecAad,
DecData,
DecFinal
}
private const int BufSize = 64;
private const int KeySize = 16;
private const int IVSize = 12;
private const int MacSize = 8;
private readonly byte[] workingKeyAndIV = new byte[28];
private readonly uint[] lfsr = new uint[4];
private readonly uint[] nfsr = new uint[4];
private readonly uint[] authAccSr = new uint[4];
private State m_state;
private readonly MemoryStream m_aadData = new MemoryStream();
private readonly byte[] m_mac = new byte[8];
private readonly byte[] m_buf = new byte[72];
private int m_bufPos;
public string AlgorithmName => "Grain-128AEAD";
public Grain128AeadEngine()
{
m_aadData.Position = 5;
}
public void Init(bool forEncryption, ICipherParameters param)
{
ParametersWithIV obj = param as ParametersWithIV;
if (obj == null)
throw new ArgumentException("Grain-128AEAD Init parameters must include an IV");
if (obj.IVLength != 12)
throw new ArgumentException("Grain-128AEAD requires exactly 12 bytes of IV");
KeyParameter obj2 = obj.Parameters as KeyParameter;
if (obj2 == null)
throw new ArgumentException("Grain-128AEAD Init parameters must include a key");
if (obj2.KeyLength != 16)
throw new ArgumentException("Grain-128AEAD key must be 128 bits long");
obj2.CopyTo(workingKeyAndIV, 0, 16);
obj.CopyIVTo(workingKeyAndIV, 16, 12);
m_state = (forEncryption ? State.EncInit : State.DecInit);
Reset();
}
public int GetOutputSize(int len)
{
int num = System.Math.Max(0, len);
switch (m_state) {
case State.DecInit:
case State.DecAad:
return System.Math.Max(0, num - 8);
case State.DecData:
case State.DecFinal:
return System.Math.Max(0, num + m_bufPos - 8);
case State.EncData:
case State.EncFinal:
return num + m_bufPos + 8;
default:
return num + 8;
}
}
public int GetUpdateOutputSize(int len)
{
int num = System.Math.Max(0, len);
switch (m_state) {
case State.DecInit:
case State.DecAad:
num = System.Math.Max(0, num - 8);
break;
case State.DecData:
case State.DecFinal:
num = System.Math.Max(0, num + m_bufPos - 8);
break;
case State.EncData:
case State.EncFinal:
num += m_bufPos;
break;
}
return num - num % 64;
}
public void ProcessAadByte(byte input)
{
CheckAad();
m_aadData.WriteByte(input);
}
public void ProcessAadBytes(byte[] input, int inOff, int len)
{
Check.DataLength(input, inOff, len, "input buffer too short");
CheckAad();
m_aadData.Write(input, inOff, len);
}
public int ProcessByte(byte input, byte[] output, int outOff)
{
return ProcessBytes(new byte[1] {
input
}, 0, 1, output, outOff);
}
public int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
{
Check.DataLength(input, inOff, len, "input buffer too short");
int updateOutputSize = GetUpdateOutputSize(len);
Check.OutputLength(output, outOff, updateOutputSize, "output buffer too short");
CheckData();
switch (m_state) {
case State.DecData:
for (int j = 0; j < len; j++) {
m_buf[m_bufPos] = input[inOff + j];
if (++m_bufPos == m_buf.Length) {
ProcessBufferDecrypt(m_buf, 0, 64, output, outOff);
outOff += 64;
Array.Copy(m_buf, 64, m_buf, 0, 8);
m_bufPos = 8;
}
}
break;
case State.EncData:
for (int i = 0; i < len; i++) {
m_buf[m_bufPos] = input[inOff + i];
if (++m_bufPos == 64) {
ProcessBufferEncrypt(m_buf, 0, 64, output, outOff);
outOff += 64;
m_bufPos = 0;
}
}
break;
default:
throw new InvalidOperationException();
}
return updateOutputSize;
}
public int DoFinal(byte[] output, int outOff)
{
int outputSize = GetOutputSize(0);
Check.OutputLength(output, outOff, outputSize, "output buffer too short");
CheckData();
switch (m_state) {
case State.DecData:
if (m_bufPos < 8)
throw new InvalidCipherTextException("data too short");
if (outputSize > 0)
ProcessBufferDecrypt(m_buf, 0, outputSize, output, outOff);
FinishData(State.DecFinal);
if (!Arrays.FixedTimeEquals(8, m_mac, 0, m_buf, outputSize))
throw new InvalidCipherTextException("mac check in " + AlgorithmName + " failed");
break;
case State.EncData:
if (m_bufPos > 0) {
ProcessBufferEncrypt(m_buf, 0, m_bufPos, output, outOff);
outOff += m_bufPos;
}
FinishData(State.EncFinal);
Array.Copy(m_mac, 0, output, outOff, 8);
break;
}
Reset(false);
return outputSize;
}
public byte[] GetMac()
{
return (byte[])m_mac.Clone();
}
public void Reset()
{
Reset(true);
}
[Obsolete("Incompatible with the AEAD API; throws NotImplementedException")]
public byte ReturnByte(byte input)
{
throw new NotImplementedException();
}
private void CheckAad()
{
switch (m_state) {
case State.EncAad:
case State.DecAad:
break;
case State.DecInit:
m_state = State.DecAad;
break;
case State.EncInit:
m_state = State.EncAad;
break;
case State.EncData:
case State.DecData:
throw new InvalidOperationException("associated data must be added before plaintext/ciphertext");
case State.EncFinal:
throw new InvalidOperationException(AlgorithmName + " cannot be reused for encryption");
default:
throw new InvalidOperationException(AlgorithmName + " needs to be initialized");
}
}
private void CheckData()
{
switch (m_state) {
case State.EncData:
case State.DecData:
break;
case State.DecInit:
case State.DecAad:
FinishAad(State.DecData);
break;
case State.EncInit:
case State.EncAad:
FinishAad(State.EncData);
break;
case State.EncFinal:
throw new InvalidOperationException(AlgorithmName + " cannot be reused for encryption");
default:
throw new InvalidOperationException(AlgorithmName + " needs to be initialized");
}
}
private void FinishAad(State nextState)
{
byte[] buffer = m_aadData.GetBuffer();
int num = Convert.ToInt32(m_aadData.Length);
int num2 = num - 5;
int num3;
if (num2 < 128) {
num3 = 4;
buffer[num3] = (byte)num2;
} else {
num3 = 5;
uint num4 = (uint)num2;
do {
buffer[--num3] = (byte)num4;
num4 >>= 8;
} while (num4 != 0);
int num5 = 5 - num3;
buffer[--num3] = (byte)(128 | num5);
}
for (int i = num3; i < num; i++) {
uint num6 = buffer[i];
for (int j = 0; j < 8; j++) {
ShiftBit(nfsr, GetOutputNFSR() ^ lfsr[0]);
ShiftBit(lfsr, GetOutputLFSR());
uint num7 = (num6 >> j) & 1;
uint num8 = 0 - num7;
authAccSr[0] ^= (authAccSr[2] & num8);
authAccSr[1] ^= (authAccSr[3] & num8);
ShiftAuth(GetOutput());
ShiftBit(nfsr, GetOutputNFSR() ^ lfsr[0]);
ShiftBit(lfsr, GetOutputLFSR());
}
}
m_state = nextState;
}
private void FinishData(State nextState)
{
authAccSr[0] ^= authAccSr[2];
authAccSr[1] ^= authAccSr[3];
Pack.UInt32_To_LE(authAccSr, 0, 2, m_mac, 0);
m_state = nextState;
}
private uint GetOutput()
{
uint num = nfsr[0] >> 2;
uint num2 = nfsr[0] >> 12;
uint num3 = nfsr[0] >> 15;
uint num4 = nfsr[1] >> 4;
uint num5 = nfsr[1] >> 13;
uint num6 = nfsr[2];
uint num7 = nfsr[2] >> 9;
uint num8 = nfsr[2] >> 25;
uint num9 = nfsr[2] >> 31;
uint num10 = lfsr[0] >> 8;
uint num11 = lfsr[0] >> 13;
uint num12 = lfsr[0] >> 20;
uint num13 = lfsr[1] >> 10;
uint num14 = lfsr[1] >> 28;
uint num15 = lfsr[2] >> 15;
uint num16 = lfsr[2] >> 29;
uint num17 = lfsr[2] >> 30;
return ((num2 & num10) ^ (num11 & num12) ^ (num9 & num13) ^ (num14 & num15) ^ (num2 & num9 & num17) ^ num16 ^ num ^ num3 ^ num4 ^ num5 ^ num6 ^ num7 ^ num8) & 1;
}
private uint GetOutputLFSR()
{
uint num = lfsr[0];
uint num2 = lfsr[0] >> 7;
uint num3 = lfsr[1] >> 6;
uint num4 = lfsr[2] >> 6;
uint num5 = lfsr[2] >> 17;
uint num6 = lfsr[3];
return num ^ num2 ^ num3 ^ num4 ^ num5 ^ num6;
}
private uint GetOutputNFSR()
{
uint num = nfsr[0];
uint num2 = nfsr[0] >> 3;
uint num3 = nfsr[0] >> 11;
uint num4 = nfsr[0] >> 13;
uint num5 = nfsr[0] >> 17;
uint num6 = nfsr[0] >> 18;
uint num7 = nfsr[0] >> 22;
uint num8 = nfsr[0] >> 24;
uint num9 = nfsr[0] >> 25;
uint num10 = nfsr[0] >> 26;
uint num11 = nfsr[0] >> 27;
uint num12 = nfsr[1] >> 8;
uint num13 = nfsr[1] >> 16;
uint num14 = nfsr[1] >> 24;
uint num15 = nfsr[1] >> 27;
uint num16 = nfsr[1] >> 29;
uint num17 = nfsr[2] >> 1;
uint num18 = nfsr[2] >> 3;
uint num19 = nfsr[2] >> 4;
uint num20 = nfsr[2] >> 6;
uint num21 = nfsr[2] >> 14;
uint num22 = nfsr[2] >> 18;
uint num23 = nfsr[2] >> 20;
uint num24 = nfsr[2] >> 24;
uint num25 = nfsr[2] >> 27;
uint num26 = nfsr[2] >> 28;
uint num27 = nfsr[2] >> 29;
uint num28 = nfsr[2] >> 31;
uint num29 = nfsr[3];
return num ^ num10 ^ num14 ^ num25 ^ num29 ^ (num2 & num18) ^ (num3 & num4) ^ (num5 & num6) ^ (num11 & num15) ^ (num12 & num13) ^ (num16 & num17) ^ (num19 & num23) ^ (num7 & num8 & num9) ^ (num20 & num21 & num22) ^ (num24 & num26 & num27 & num28);
}
private void InitGrain()
{
Pack.LE_To_UInt32(workingKeyAndIV, 0, nfsr, 0, 4);
Pack.LE_To_UInt32(workingKeyAndIV, 16, lfsr, 0, 3);
lfsr[3] = 2147483647;
for (int i = 0; i < 320; i++) {
uint output = GetOutput();
ShiftBit(nfsr, GetOutputNFSR() ^ lfsr[0] ^ output);
ShiftBit(lfsr, GetOutputLFSR() ^ output);
}
for (int j = 0; j < 8; j++) {
uint num = workingKeyAndIV[j];
uint num2 = workingKeyAndIV[j + 8];
for (int k = 0; k < 8; k++) {
uint output2 = GetOutput();
ShiftBit(nfsr, GetOutputNFSR() ^ lfsr[0] ^ output2 ^ (num >> k));
ShiftBit(lfsr, GetOutputLFSR() ^ output2 ^ (num2 >> k));
}
}
for (int l = 0; l < 4; l++) {
uint num3 = 0;
for (int m = 0; m < 32; m++) {
uint output3 = GetOutput();
ShiftBit(nfsr, GetOutputNFSR() ^ lfsr[0]);
ShiftBit(lfsr, GetOutputLFSR());
num3 |= output3 << m;
}
authAccSr[l] = num3;
}
}
private void ProcessBufferDecrypt(byte[] input, int inOff, int len, byte[] output, int outOff)
{
for (int i = 0; i < len; i++) {
uint num = input[inOff + i];
uint num2 = 0;
for (int j = 0; j < 8; j++) {
uint num3 = ((num >> j) & 1) ^ GetOutput();
ShiftBit(nfsr, GetOutputNFSR() ^ lfsr[0]);
ShiftBit(lfsr, GetOutputLFSR());
num2 |= num3 << j;
uint num4 = 0 - num3;
authAccSr[0] ^= (authAccSr[2] & num4);
authAccSr[1] ^= (authAccSr[3] & num4);
ShiftAuth(GetOutput());
ShiftBit(nfsr, GetOutputNFSR() ^ lfsr[0]);
ShiftBit(lfsr, GetOutputLFSR());
}
output[outOff + i] = (byte)num2;
}
}
private void ProcessBufferEncrypt(byte[] input, int inOff, int len, byte[] output, int outOff)
{
for (int i = 0; i < len; i++) {
uint num = 0;
uint num2 = input[inOff + i];
for (int j = 0; j < 8; j++) {
uint num3 = (num2 >> j) & 1;
uint num4 = num3 ^ GetOutput();
ShiftBit(nfsr, GetOutputNFSR() ^ lfsr[0]);
ShiftBit(lfsr, GetOutputLFSR());
num |= num4 << j;
uint num5 = 0 - num3;
authAccSr[0] ^= (authAccSr[2] & num5);
authAccSr[1] ^= (authAccSr[3] & num5);
ShiftAuth(GetOutput());
ShiftBit(nfsr, GetOutputNFSR() ^ lfsr[0]);
ShiftBit(lfsr, GetOutputLFSR());
}
output[outOff + i] = (byte)num;
}
}
private void Reset(bool clearMac)
{
m_aadData.SetLength(5);
if (clearMac)
Array.Clear(m_mac, 0, m_mac.Length);
Array.Clear(m_buf, 0, m_buf.Length);
m_bufPos = 0;
switch (m_state) {
case State.DecAad:
case State.DecData:
case State.DecFinal:
m_state = State.DecInit;
break;
case State.EncAad:
case State.EncData:
case State.EncFinal:
m_state = State.EncFinal;
return;
default:
throw new InvalidOperationException(AlgorithmName + " needs to be initialized");
case State.EncInit:
case State.DecInit:
break;
}
InitGrain();
}
private void ShiftAuth(uint val)
{
authAccSr[2] = ((authAccSr[2] >> 1) | (authAccSr[3] << 31));
authAccSr[3] = ((authAccSr[3] >> 1) | (val << 31));
}
private void ShiftBit(uint[] array, uint val)
{
array[0] = ((array[0] >> 1) | (array[1] << 31));
array[1] = ((array[1] >> 1) | (array[2] << 31));
array[2] = ((array[2] >> 1) | (array[3] << 31));
array[3] = ((array[3] >> 1) | (val << 31));
}
}
}