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

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); } public virtual void ProcessAadBytes(ReadOnlySpan<byte> input) { associatedText.Write(input); } 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 ProcessByte(byte input, Span<byte> output) { 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 virtual int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output) { data.Write(input); 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"); return ProcessPacket(input.AsSpan(inOff, len), output.AsSpan(outOff)); } public unsafe int ProcessPacket(ReadOnlySpan<byte> input, Span<byte> output) { int length = input.Length; Check.OutputLength(output, length, "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); } int blockSize = engine.GetBlockSize(); int num = 0; int num3; if (forEncryption) { Check.DataLength(length % blockSize != 0, "partial blocks not supported"); CalculateMac(input); engine.ProcessBlock(nonce, s); int num2 = length; while (num2 > 0) { num3 = num; ReadOnlySpan<byte> input2 = input.Slice(num3, input.Length - num3); num3 = num; ProcessBlock(input2, output.Slice(num3, output.Length - num3)); num2 -= blockSize; num += blockSize; } for (int i = 0; i < counter.Length; i++) { s[i] += counter[i]; } engine.ProcessBlock(s, buffer); for (int j = 0; j < macSize; j++) { output[num + j] = (byte)(buffer[j] ^ macBlock[j]); } Array.Copy(macBlock, 0, mac, 0, macSize); Reset(); return length + macSize; } Check.DataLength((length - macSize) % blockSize != 0, "partial blocks not supported"); engine.ProcessBlock(nonce, 0, s, 0); int num4 = length / engine.GetBlockSize(); for (int k = 0; k < num4; k++) { num3 = num; ReadOnlySpan<byte> input3 = input.Slice(num3, input.Length - num3); num3 = num; ProcessBlock(input3, output.Slice(num3, output.Length - num3)); num += blockSize; } if (length > num) { for (int l = 0; l < counter.Length; l++) { s[l] += counter[l]; } engine.ProcessBlock(s, buffer); for (int m = 0; m < macSize; m++) { output[num + m] = (byte)(buffer[m] ^ input[num + m]); } num += macSize; } for (int n = 0; n < counter.Length; n++) { s[n] += counter[n]; } engine.ProcessBlock(s, buffer); num3 = num - macSize; Span<byte> span = output.Slice(num3, num - num3); span.CopyTo(buffer); CalculateMac(output.Slice(0, num - macSize)); Array.Copy(macBlock, 0, mac, 0, macSize); if (macSize <= 64) { num3 = macSize; span = new Span<byte>(stackalloc byte[(int)(uint)num3], num3); } else span = new byte[macSize]; Span<byte> span2 = span; span2.CopyFrom(buffer); if (!Arrays.FixedTimeEquals(mac.AsSpan(0, macSize), span2)) throw new InvalidCipherTextException("mac check failed"); Reset(); return length - macSize; } private void CalculateMac(ReadOnlySpan<byte> authText) { int blockSize = engine.GetBlockSize(); while (!authText.IsEmpty) { for (int i = 0; i < blockSize; i++) { macBlock[i] ^= authText[i]; } engine.ProcessBlock(macBlock, macBlock); int num = blockSize; authText = authText.Slice(num, authText.Length - num); } } private void ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output) { for (int i = 0; i < counter.Length; i++) { s[i] += counter[i]; } engine.ProcessBlock(s, buffer); int blockSize = engine.GetBlockSize(); for (int j = 0; j < blockSize; j++) { output[j] = (byte)(buffer[j] ^ input[j]); } } public virtual int DoFinal(byte[] output, int outOff) { return DoFinal(output.AsSpan(outOff)); } public virtual int DoFinal(Span<byte> output) { byte[] array = data.GetBuffer(); int length = Convert.ToInt32(data.Length); int result = ProcessPacket(array.AsSpan(0, length), output); 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); } } }