<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.1" />

Grain128AeadEngine

public sealed class Grain128AeadEngine : IAeadCipher
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 void ProcessAadBytes(ReadOnlySpan<byte> input) { CheckAad(); m_aadData.Write(input); } public int ProcessByte(byte input, byte[] output, int outOff) { return ProcessBytes(new byte[1] { input }, 0, 1, output, outOff); } public unsafe int ProcessByte(byte input, Span<byte> output) { byte* intPtr = stackalloc byte[1]; *intPtr = input; Span<byte> span = new Span<byte>(intPtr, 1); return ProcessBytes(span, output); } public int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { Check.DataLength(input, inOff, len, "input buffer too short"); return ProcessBytes(input.AsSpan(inOff, len), output.AsSpan(outOff)); } public int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output) { int updateOutputSize = GetUpdateOutputSize(input.Length); Check.OutputLength(output, updateOutputSize, "output buffer too short"); CheckData(); switch (m_state) { case State.DecData: for (int j = 0; j < input.Length; j++) { m_buf[m_bufPos] = input[j]; if (++m_bufPos == m_buf.Length) { ProcessBufferDecrypt(m_buf.AsSpan(0, 64), output); output = output.Slice(64, output.Length - 64); Array.Copy(m_buf, 64, m_buf, 0, 8); m_bufPos = 8; } } break; case State.EncData: for (int i = 0; i < input.Length; i++) { m_buf[m_bufPos] = input[i]; if (++m_bufPos == 64) { ProcessBufferEncrypt(m_buf.AsSpan(0, 64), output); output = output.Slice(64, output.Length - 64); m_bufPos = 0; } } break; default: throw new InvalidOperationException(); } return updateOutputSize; } public int DoFinal(byte[] output, int outOff) { return DoFinal(output.AsSpan(outOff)); } public int DoFinal(Span<byte> output) { int outputSize = GetOutputSize(0); Check.OutputLength(output, 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.AsSpan(0, outputSize), output); 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.AsSpan(0, m_bufPos), output); int bufPos = m_bufPos; output = output.Slice(bufPos, output.Length - bufPos); } FinishData(State.EncFinal); m_mac.CopyTo(output); 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(ReadOnlySpan<byte> input, Span<byte> output) { int i = 0; for (int length = input.Length; i < length; i++) { uint num = input[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[i] = (byte)num2; } } private void ProcessBufferEncrypt(ReadOnlySpan<byte> input, Span<byte> output) { int i = 0; for (int length = input.Length; i < length; i++) { uint num = 0; uint num2 = input[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[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)); } } }