#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR) #pragma warning disable using System; using System.IO; using System.Text; using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters; using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities; namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes { public class KCcmBlockCipher: IAeadBlockCipher { 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; private void setNb(int Nb) { if (Nb == 4 || Nb == 6 || Nb == 8) { Nb_ = Nb; } else { throw new ArgumentException("Nb = 4 is recommended by DSTU7624 but can be changed to only 6 or 8 in this implementation"); } } /// /// Base constructor. Nb value is set to 4. /// /// base cipher to use under CCM. public KCcmBlockCipher(IBlockCipher engine): this(engine, 4) { } /// /// Constructor allowing Nb configuration. /// /// Nb is a parameter specified in CCM mode of DSTU7624 standard. /// This parameter specifies maximum possible length of input.It should /// be calculated as follows: Nb = 1 / 8 * (-3 + log[2]Nmax) + 1, /// where Nmax - length of input message in bits.For practical reasons /// Nmax usually less than 4Gb, e.g. for Nmax = 2^32 - 1, Nb = 4. /// /// base cipher to use under CCM. /// Nb value to use. public KCcmBlockCipher(IBlockCipher engine, int Nb) { this.engine = engine; this.macSize = engine.GetBlockSize(); this.nonce = new byte[engine.GetBlockSize()]; this.initialAssociatedText = new byte[engine.GetBlockSize()]; this.mac = new byte[engine.GetBlockSize()]; this.macBlock = new byte[engine.GetBlockSize()]; this.G1 = new byte[engine.GetBlockSize()]; this.buffer = new byte[engine.GetBlockSize()]; this.s = new byte[engine.GetBlockSize()]; this.counter = new byte[engine.GetBlockSize()]; setNb(Nb); } public virtual void Init(bool forEncryption, ICipherParameters parameters) { ICipherParameters cipherParameters; if (parameters is AeadParameters) { AeadParameters param = (AeadParameters)parameters; if (param.MacSize > MAX_MAC_BIT_LENGTH || param.MacSize < MIN_MAC_BIT_LENGTH || param.MacSize % 8 != 0) { throw new ArgumentException("Invalid mac size specified"); } nonce = param.GetNonce(); macSize = param.MacSize / BITS_IN_BYTE; initialAssociatedText = param.GetAssociatedText(); cipherParameters = param.Key; } else if (parameters is ParametersWithIV) { nonce = ((ParametersWithIV)parameters).GetIV(); macSize = engine.GetBlockSize(); // use default blockSize for MAC if it is not specified initialAssociatedText = null; cipherParameters = ((ParametersWithIV)parameters).Parameters; } else { throw new ArgumentException("Invalid parameters specified"); } this.mac = new byte[macSize]; this.forEncryption = forEncryption; engine.Init(true, cipherParameters); counter[0] = 0x01; // defined in standard if (initialAssociatedText != null) { ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length); } } public virtual String AlgorithmName { get { return engine.AlgorithmName + "/KCCM"; } } public virtual int GetBlockSize() { return engine.GetBlockSize(); } public virtual IBlockCipher GetUnderlyingCipher() { return engine; } 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); // for G1 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); // for G2 if (assocLen <= engine.GetBlockSize() - Nb_) { for (int byteIndex = 0; byteIndex < assocLen; byteIndex++) { buffer[byteIndex + Nb_] ^= assocText[assocOff + byteIndex]; } for (int byteIndex = 0; byteIndex < engine.GetBlockSize(); byteIndex++) { macBlock[byteIndex] ^= buffer[byteIndex]; } engine.ProcessBlock(macBlock, 0, macBlock, 0); return; } for (int byteIndex = 0; byteIndex < engine.GetBlockSize(); byteIndex++) { macBlock[byteIndex] ^= buffer[byteIndex]; } engine.ProcessBlock(macBlock, 0, macBlock, 0); int authLen = assocLen; while (authLen != 0) { for (int byteIndex = 0; byteIndex < engine.GetBlockSize(); byteIndex++) { macBlock[byteIndex] ^= assocText[byteIndex + assocOff]; } engine.ProcessBlock(macBlock, 0, macBlock, 0); assocOff += engine.GetBlockSize(); authLen -= 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) { #if PORTABLE || NETFX_CORE byte[] aad = associatedText.ToArray(); int aadLen = aad.Length; #else byte[] aad = associatedText.GetBuffer(); int aadLen = (int)associatedText.Length; #endif int dataLen = forEncryption ? (int)data.Length : ((int)data.Length - macSize); ProcessAAD(aad, 0, aadLen, dataLen); } if (forEncryption) { Check.DataLength(len % engine.GetBlockSize() != 0, "partial blocks not supported"); CalculateMac(input, inOff, len); engine.ProcessBlock(nonce, 0, s, 0); int totalLength = len; while (totalLength > 0) { ProcessBlock(input, inOff, len, output, outOff); totalLength -= engine.GetBlockSize(); inOff += engine.GetBlockSize(); outOff += engine.GetBlockSize(); } for (int byteIndex = 0; byteIndex inOff) { for (int byteIndex = 0; byteIndex 0) { for (int byteIndex = 0; byteIndex < engine.GetBlockSize(); byteIndex++) { macBlock[byteIndex] ^= authText[authOff + byteIndex]; } engine.ProcessBlock(macBlock, 0, macBlock, 0); totalLen -= engine.GetBlockSize(); authOff += engine.GetBlockSize(); } } public virtual int DoFinal(byte[] output, int outOff) { #if PORTABLE || NETFX_CORE byte[] buf = data.ToArray(); int bufLen = buf.Length; #else byte[] buf = data.GetBuffer(); int bufLen = (int)data.Length; #endif int len = ProcessPacket(buf, 0, bufLen, output, outOff); Reset(); return len; } 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, (byte)0); Arrays.Fill(buffer, (byte)0); Arrays.Fill(counter, (byte)0); Arrays.Fill(macBlock, (byte)0); counter[0] = 0x01; 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 flagByte = new StringBuilder(); if (authTextPresents) { flagByte.Append("1"); } else { flagByte.Append("0"); } switch (macSize) { case 8: flagByte.Append("010"); // binary 2 break; case 16: flagByte.Append("011"); // binary 3 break; case 32: flagByte.Append("100"); // binary 4 break; case 48: flagByte.Append("101"); // binary 5 break; case 64: flagByte.Append("110"); // binary 6 break; } String binaryNb = Convert.ToString(Nb_ - 1, 2); while (binaryNb.Length < 4) { binaryNb = new StringBuilder(binaryNb).Insert(0, "0").ToString(); } flagByte.Append(binaryNb); return (byte)Convert.ToInt32(flagByte.ToString(), 2); } } } #pragma warning restore #endif