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

OcbBlockCipher

An implementation of RFC 7253 on The OCB Authenticated-Encryption Algorithm.
using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Utilities; using System; using System.Collections.Generic; namespace Org.BouncyCastle.Crypto.Modes { public class OcbBlockCipher : IAeadBlockCipher, IAeadCipher { private const int BLOCK_SIZE = 16; private readonly IBlockCipher hashCipher; private readonly IBlockCipher mainCipher; private bool forEncryption; private int macSize; private byte[] initialAssociatedText; private IList<byte[]> L; private byte[] L_Asterisk; private byte[] L_Dollar; private byte[] KtopInput; private byte[] Stretch = new byte[24]; private byte[] OffsetMAIN_0 = new byte[16]; private byte[] hashBlock; private byte[] mainBlock; private int hashBlockPos; private int mainBlockPos; private long hashBlockCount; private long mainBlockCount; private byte[] OffsetHASH; private byte[] Sum; private byte[] OffsetMAIN = new byte[16]; private byte[] Checksum; private byte[] macBlock; public virtual string AlgorithmName => mainCipher.AlgorithmName + "/OCB"; public virtual IBlockCipher UnderlyingCipher => mainCipher; public OcbBlockCipher(IBlockCipher hashCipher, IBlockCipher mainCipher) { if (hashCipher == null) throw new ArgumentNullException("hashCipher"); int num; if (hashCipher.GetBlockSize() != 16) { num = 16; throw new ArgumentException("must have a block size of " + num.ToString(), "hashCipher"); } if (mainCipher == null) throw new ArgumentNullException("mainCipher"); if (mainCipher.GetBlockSize() != 16) { num = 16; throw new ArgumentException("must have a block size of " + num.ToString(), "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 void Init(bool forEncryption, ICipherParameters parameters) { bool flag = this.forEncryption; this.forEncryption = forEncryption; macBlock = null; AeadParameters aeadParameters = parameters as AeadParameters; byte[] array; KeyParameter keyParameter; if (aeadParameters != null) { array = aeadParameters.GetNonce(); initialAssociatedText = aeadParameters.GetAssociatedText(); int num = aeadParameters.MacSize; if (num < 64 || num > 128 || num % 8 != 0) throw new ArgumentException("Invalid value for MAC size: " + num.ToString()); macSize = num / 8; keyParameter = aeadParameters.Key; } else { ParametersWithIV parametersWithIV = parameters as ParametersWithIV; if (parametersWithIV == null) throw new ArgumentException("invalid parameters passed to OCB"); array = parametersWithIV.GetIV(); initialAssociatedText = null; macSize = 16; keyParameter = (KeyParameter)parametersWithIV.Parameters; } hashBlock = new byte[16]; mainBlock = new byte[forEncryption ? 16 : (16 + macSize)]; if (array.Length > 15) throw new ArgumentException("IV must be no more than 15 bytes"); if (keyParameter != null) { hashCipher.Init(true, keyParameter); mainCipher.Init(forEncryption, keyParameter); KtopInput = null; } else if (flag != forEncryption) { throw new ArgumentException("cannot change encrypting state without providing key."); } L_Asterisk = new byte[16]; hashCipher.ProcessBlock(L_Asterisk, 0, L_Asterisk, 0); L_Dollar = OCB_double(L_Asterisk); L = new List<byte[]>(); L.Add(OCB_double(L_Dollar)); int num2 = ProcessNonce(array); int num3 = num2 % 8; int num4 = num2 / 8; if (num3 == 0) Array.Copy(Stretch, num4, OffsetMAIN_0, 0, 16); else { for (int i = 0; i < 16; i++) { uint num5 = Stretch[num4]; uint num6 = Stretch[++num4]; OffsetMAIN_0[i] = (byte)((num5 << num3) | (num6 >> 8 - num3)); } } hashBlockPos = 0; mainBlockPos = 0; hashBlockCount = 0; mainBlockCount = 0; OffsetHASH = new byte[16]; Sum = new byte[16]; Array.Copy(OffsetMAIN_0, 0, OffsetMAIN, 0, 16); Checksum = new byte[16]; if (initialAssociatedText != null) ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length); } protected virtual int ProcessNonce(byte[] N) { byte[] array = new byte[16]; Array.Copy(N, 0, array, array.Length - N.Length, N.Length); array[0] = (byte)(macSize << 4); array[15 - N.Length] |= 1; int result = array[15] & 63; array[15] &= 192; if (KtopInput == null || !Arrays.AreEqual(array, KtopInput)) { byte[] array2 = new byte[16]; KtopInput = array; hashCipher.ProcessBlock(KtopInput, 0, array2, 0); Array.Copy(array2, 0, Stretch, 0, 16); for (int i = 0; i < 8; i++) { Stretch[16 + i] = (byte)(array2[i] ^ array2[i + 1]); } } return result; } public virtual int GetBlockSize() { return 16; } public virtual byte[] GetMac() { if (macBlock != null) return Arrays.Clone(macBlock); return new byte[macSize]; } public virtual int GetOutputSize(int len) { int num = len + mainBlockPos; if (forEncryption) return num + macSize; if (num >= macSize) return num - macSize; return 0; } public virtual int GetUpdateOutputSize(int len) { int num = len + mainBlockPos; if (!forEncryption) { if (num < macSize) return 0; num -= macSize; } return num - num % 16; } 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 16; } return 0; } public virtual int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { int num = 0; for (int i = 0; i < len; i++) { mainBlock[mainBlockPos] = input[inOff + i]; if (++mainBlockPos == mainBlock.Length) { ProcessMainBlock(output, outOff + num); num += 16; } } return num; } public virtual int DoFinal(byte[] output, int outOff) { byte[] array = null; if (!forEncryption) { if (mainBlockPos < macSize) throw new InvalidCipherTextException("data too short"); mainBlockPos -= macSize; array = new byte[macSize]; Array.Copy(mainBlock, mainBlockPos, array, 0, macSize); } if (hashBlockPos > 0) { OCB_extend(hashBlock, hashBlockPos); UpdateHASH(L_Asterisk); } if (mainBlockPos > 0) { if (forEncryption) { OCB_extend(mainBlock, mainBlockPos); Xor(Checksum, mainBlock); } Xor(OffsetMAIN, L_Asterisk); byte[] array2 = new byte[16]; hashCipher.ProcessBlock(OffsetMAIN, 0, array2, 0); Xor(mainBlock, array2); 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); } } Xor(Checksum, OffsetMAIN); Xor(Checksum, L_Dollar); hashCipher.ProcessBlock(Checksum, 0, Checksum, 0); Xor(Checksum, Sum); macBlock = new byte[macSize]; Array.Copy(Checksum, 0, macBlock, 0, macSize); int num = mainBlockPos; if (forEncryption) { Check.OutputLength(output, outOff, num + macSize, "output buffer too short"); Array.Copy(macBlock, 0, output, outOff + num, macSize); num += macSize; } else if (!Arrays.FixedTimeEquals(macBlock, array)) { throw new InvalidCipherTextException("mac check in OCB failed"); } Reset(false); return num; } 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(L[L.Count - 1])); } return L[n]; } protected virtual void ProcessHashBlock() { UpdateHASH(GetLSub(OCB_ntz(++hashBlockCount))); hashBlockPos = 0; } protected virtual void ProcessMainBlock(byte[] output, int outOff) { Check.OutputLength(output, outOff, 16, "output buffer too short"); 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, 16, mainBlock, 0, macSize); mainBlockPos = macSize; } } protected virtual void Reset(bool clearMac) { 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[] array = new byte[16]; int num = ShiftLeft(block, array); array[15] ^= (byte)(135 >> (1 - num << 3)); return array; } protected static void OCB_extend(byte[] block, int pos) { block[pos] = 128; while (++pos < 16) { block[pos] = 0; } } protected static int OCB_ntz(long x) { return Longs.NumberOfTrailingZeros(x); } protected static int ShiftLeft(byte[] block, byte[] output) { int num = 16; uint num2 = 0; while (--num >= 0) { uint num3 = block[num]; output[num] = (byte)((num3 << 1) | num2); num2 = ((num3 >> 7) & 1); } return (int)num2; } protected static void Xor(byte[] block, byte[] val) { Bytes.XorTo(16, val, block); } } }