public virtual int DoFinal(
            byte[]  outBytes,
            int outOff)
        {
            InitCipher();

            int extra = bufOff;

            byte[] tmp = new byte[bufBlock.Length];

            bufOff = 0;

            if (forEncryption)
            {
                Check.OutputLength(outBytes, outOff, extra + macSize, "Output buffer too short");

                cipher.ProcessBlock(bufBlock, 0, tmp, 0);

                Array.Copy(tmp, 0, outBytes, outOff, extra);

                mac.BlockUpdate(tmp, 0, extra);

                CalculateMac();

                Array.Copy(macBlock, 0, outBytes, outOff + extra, macSize);

                Reset(false);

                return(extra + macSize);
            }
            else
            {
                if (extra < macSize)
                {
                    throw new InvalidCipherTextException("data too short");
                }

                Check.OutputLength(outBytes, outOff, extra - macSize, "Output buffer too short");

                if (extra > macSize)
                {
                    mac.BlockUpdate(bufBlock, 0, extra - macSize);

                    cipher.ProcessBlock(bufBlock, 0, tmp, 0);

                    Array.Copy(tmp, 0, outBytes, outOff, extra - macSize);
                }

                CalculateMac();

                if (!VerifyMac(bufBlock, extra - macSize))
                {
                    throw new InvalidCipherTextException("mac check in EAX failed");
                }

                Reset(false);

                return(extra - macSize);
            }
        }
Exemple #2
0
        /**
         * Process a packet of data for either CCM decryption or encryption.
         *
         * @param in data for processing.
         * @param inOff offset at which data starts in the input array.
         * @param inLen length of the data in the input array.
         * @param output output array.
         * @param outOff offset into output array to start putting processed bytes.
         * @return the number of bytes added to output.
         * @throws IllegalStateException if the cipher is not appropriately set up.
         * @throws InvalidCipherTextException if the input data is truncated or the mac check fails.
         * @throws DataLengthException if output buffer too short.
         */
        public virtual int ProcessPacket(byte[] input, int inOff, int inLen, byte[] output, int outOff)
        {
            // TODO: handle null keyParam (e.g. via RepeatedKeySpec)
            // Need to keep the CTR and CBC Mac parts around and reset
            if (keyParam == null)
            {
                throw new InvalidOperationException("CCM cipher unitialized.");
            }

            int n = nonce.Length;
            int q = 15 - n;

            if (q < 4)
            {
                int limitLen = 1 << (8 * q);
                if (inLen >= limitLen)
                {
                    throw new InvalidOperationException("CCM packet too large for choice of q.");
                }
            }

            byte[] iv = new byte[BlockSize];
            iv[0] = (byte)((q - 1) & 0x7);
            nonce.CopyTo(iv, 1);

            IBlockCipher ctrCipher = new SicBlockCipher(cipher);

            ctrCipher.Init(forEncryption, new ParametersWithIV(keyParam, iv));

            int outputLen;
            int inIndex  = inOff;
            int outIndex = outOff;

            if (forEncryption)
            {
                outputLen = inLen + macSize;
                Check.OutputLength(output, outOff, outputLen, "Output buffer too short.");

                CalculateMac(input, inOff, inLen, macBlock);

                byte[] encMac = new byte[BlockSize];
                ctrCipher.ProcessBlock(macBlock, 0, encMac, 0);   // S0

                while (inIndex < (inOff + inLen - BlockSize))     // S1...
                {
                    ctrCipher.ProcessBlock(input, inIndex, output, outIndex);
                    outIndex += BlockSize;
                    inIndex  += BlockSize;
                }

                byte[] block = new byte[BlockSize];

                Array.Copy(input, inIndex, block, 0, inLen + inOff - inIndex);

                ctrCipher.ProcessBlock(block, 0, block, 0);

                Array.Copy(block, 0, output, outIndex, inLen + inOff - inIndex);

                Array.Copy(encMac, 0, output, outOff + inLen, macSize);
            }
            else
            {
                if (inLen < macSize)
                {
                    throw new InvalidCipherTextException("data too short");
                }

                outputLen = inLen - macSize;
                Check.OutputLength(output, outOff, outputLen, "Output buffer too short.");

                Array.Copy(input, inOff + outputLen, macBlock, 0, macSize);

                ctrCipher.ProcessBlock(macBlock, 0, macBlock, 0);

                for (int i = macSize; i != macBlock.Length; i++)
                {
                    macBlock[i] = 0;
                }

                while (inIndex < (inOff + outputLen - BlockSize))
                {
                    ctrCipher.ProcessBlock(input, inIndex, output, outIndex);
                    outIndex += BlockSize;
                    inIndex  += BlockSize;
                }

                byte[] block = new byte[BlockSize];

                Array.Copy(input, inIndex, block, 0, outputLen - (inIndex - inOff));

                ctrCipher.ProcessBlock(block, 0, block, 0);

                Array.Copy(block, 0, output, outIndex, outputLen - (inIndex - inOff));

                byte[] calculatedMacBlock = new byte[BlockSize];

                CalculateMac(output, outOff, outputLen, calculatedMacBlock);

                if (!Arrays.ConstantTimeAreEqual(macBlock, calculatedMacBlock))
                {
                    throw new InvalidCipherTextException("mac check in CCM failed");
                }
            }

            return(outputLen);
        }