All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bouncycastle.crypto.modes.AESCCMPacketCipher Maven / Gradle / Ivy

Go to download

The Long Term Stable (LTS) Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms. This jar contains the JCA/JCE provider and low-level API for the BC LTS version 2.73.6 for Java 8 and later.

The newest version!
package org.bouncycastle.crypto.modes;


import org.bouncycastle.crypto.*;
import org.bouncycastle.crypto.engines.AESNativeCCMPacketCipher;
import org.bouncycastle.crypto.engines.AESPacketCipher;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Bytes;
import org.bouncycastle.util.Pack;
import org.bouncycastle.util.encoders.Hex;

public class AESCCMPacketCipher
        implements AESCCMModePacketCipher
{
    public static AESCCMModePacketCipher newInstance()
    {
        if (CryptoServicesRegistrar.hasEnabledService(NativeServices.AES_CCM_PC))
        {
            return new AESNativeCCMPacketCipher();
        }
        return new AESCCMPacketCipher();
    }

    public AESCCMPacketCipher()
    {
    }

    @Override
    public int getOutputSize(boolean encryption, CipherParameters params, int len)
    {
        if (len < 0)
        {
            throw new IllegalArgumentException(ExceptionMessages.LEN_NEGATIVE);
        }
        int macSize = getMacSize(encryption, params);
        if (encryption)
        {
            return PacketCipherChecks.addCheckInputOverflow(len, macSize);
        }
        else if (len < macSize)
        {
            throw new OutputLengthException(ExceptionMessages.OUTPUT_LENGTH);
        }
        return len - macSize;
    }

    @Override
    public int processPacket(boolean forEncryption, CipherParameters params, byte[] in, int inOff, int inLen,
                             byte[] output, int outOff)
    throws PacketCipherException
    {
        PacketCipherChecks.checkBoundsInput(in, inOff, inLen, output, outOff); // Output length varies for direction.
        byte[] nonce;
        byte[] initialAssociatedText;
        int macSize;
        KeyParameter keyParam;
        byte[] macBlock = new byte[AESPacketCipher.BLOCK_SIZE];
        byte[] counter = new byte[AESPacketCipher.BLOCK_SIZE];
        byte[] counterOut = new byte[16];
        byte[] block = null;
        if (!forEncryption)
        {
            block = new byte[AESPacketCipher.BLOCK_SIZE];
        }
        try
        {
            if (params instanceof AEADParameters)
            {
                AEADParameters param = (AEADParameters) params;
                macSize = getCCMMacSize(forEncryption, param.getMacSize());
                nonce = param.getNonce();
                initialAssociatedText = param.getAssociatedText();
                keyParam = param.getKey();
            }
            else if (params instanceof ParametersWithIV)
            {
                ParametersWithIV param = (ParametersWithIV) params;
                macSize = 8;
                nonce = Arrays.clone(param.getIV());
                initialAssociatedText = null;
                keyParam = (KeyParameter) param.getParameters();
            }
            else
            {
                throw new IllegalArgumentException(ExceptionMessages.CCM_INVALID_PARAMETER);
            }
            PacketCipherChecks.checkInputAndOutputAEAD(forEncryption, in, inOff, inLen, output, outOff, macSize);
            // aeadLengthCheck(forEncryption, inLen, output, outOff, macSize);
            if (nonce == null || nonce.length < 7 || nonce.length > 13)
            {
                throw new IllegalArgumentException(ExceptionMessages.CCM_IV_SIZE);
            }

            PacketCipherChecks.checkKeyLength(keyParam.getKeyLength());

            // checkKeyLength(keyParam, ExceptionMessages.CCM_CIPHER_UNITIALIZED);
        }
        catch (IllegalArgumentException e)
        {
            throw PacketCipherException.from(e);
        }
        // This is not always true for the generalized Rijndael that allows larger block sizes
        int[][] workingKey = AESPacketCipher.generateWorkingKey(true, keyParam.getKey());
        byte[] s = AESPacketCipher.createS(true);
        int q = 15 - nonce.length; // OK because nonce len asserted to be [7,13]
        if (q < 4)
        {
            int limitLen = 1 << (q << 3);
            if (inLen >= limitLen)
            {
                throw PacketCipherException.from(new IllegalStateException("CCM packet too large for choice of q."));
            }
        }
        PacketCipherException exception = null;
        int outputLen = 0;
        try
        {
            counter[0] = (byte) ((q - 1) & 0x7);
            System.arraycopy(nonce, 0, counter, 1, nonce.length);
            int inIndex = inOff;
            int outIndex = outOff;
            if (forEncryption)
            {
                outputLen = PacketCipherChecks.addCheckInputOverflow(inLen, macSize);
                calculateMac(in, inOff, inLen, macBlock, macSize, initialAssociatedText, nonce, workingKey, s);
                ctrProcessBlock(counter, counterOut, macBlock, 0, macBlock, 0, workingKey, s);

                // S0
                System.arraycopy(macBlock, 0, output, outOff + inLen, macSize);
                while (inIndex < (inOff + inLen - AESPacketCipher.BLOCK_SIZE))                 // S1...
                {
                    ctrProcessBlock(counter, counterOut, in, inIndex, output, outIndex, workingKey, s);
                    outIndex += AESPacketCipher.BLOCK_SIZE;
                    inIndex += AESPacketCipher.BLOCK_SIZE;
                }

                System.arraycopy(in, inIndex, macBlock, 0, inLen + inOff - inIndex);
                ctrProcessBlock(counter, counterOut, macBlock, 0, macBlock, 0, workingKey, s);
                System.arraycopy(macBlock, 0, output, outIndex, inLen + inOff - inIndex);
            }
            else
            {
                outputLen = inLen - macSize;
                System.arraycopy(in, inOff + outputLen, macBlock, 0, macSize);
                ctrProcessBlock(counter, counterOut, macBlock, 0, macBlock, 0, workingKey, s);
                Arrays.fill(macBlock, macSize, AESPacketCipher.BLOCK_SIZE, (byte) 0);
                while (inIndex < (inOff + outputLen - AESPacketCipher.BLOCK_SIZE))
                {
                    ctrProcessBlock(counter, counterOut, in, inIndex, output, outIndex, workingKey, s);
                    outIndex += AESPacketCipher.BLOCK_SIZE;
                    inIndex += AESPacketCipher.BLOCK_SIZE;
                }
                System.arraycopy(in, inIndex, block, 0, outputLen - (inIndex - inOff));
                ctrProcessBlock(counter, counterOut, block, 0, block, 0, workingKey, s);
                System.arraycopy(block, 0, output, outIndex, outputLen - (inIndex - inOff));
                calculateMac(output, outOff, outputLen, block, macSize, initialAssociatedText, nonce, workingKey, s);
                if (!Arrays.constantTimeAreEqual(macBlock, block))
                {
                    throw new InvalidCipherTextException("mac check in CCM failed");
                }
            }
        }
        catch (Exception ex)
        {
            exception = PacketCipherException.from(ex);
            throw exception;
        }
        finally
        {
            if (exception != null)
            {
                int l = Math.min(outputLen, output.length - outOff);
                Arrays.clear(output, outOff, l);
            }
        }

        Arrays.clear(workingKey);
        Arrays.clear(macBlock);
        Arrays.clear(counter);
        Arrays.clear(counterOut);
        Arrays.clear(block);
        Arrays.clear(s);
        return outputLen;
    }

    private static void calculateMac(byte[] in, int inOff, int inLen, byte[] macBlock, int macSize,
                                     byte[] aad, byte[] nonce, int[][] workingkey, byte[] s)
    {
        byte[] buf = new byte[AESPacketCipher.BLOCK_SIZE];
        int bufOff = 0;
        if (aad != null && aad.length > 0)
        {
            buf[0] |= 0x40;
        }
        buf[0] |= ((((macSize - 2) >> 1) & 0x7) << 3) | (((15 - nonce.length) - 1) & 0x7);
        System.arraycopy(nonce, 0, buf, 1, nonce.length);
        int q = inLen;
        int count = 1;
        while (q > 0)
        {
            buf[buf.length - count++] = (byte) (q & 0xff);
            q >>>= 8;
        }

        AESPacketCipher.processBlock(true, workingkey, s, buf, 0, macBlock, 0);

        //
        // process associated text
        //
        if (aad != null && aad.length > 0)
        {
            int textLength = aad.length;
            if (textLength < ((1 << 16) - (1 << 8)))
            {
                buf[0] = (byte) (textLength >> 8);
                buf[1] = (byte) textLength;
                bufOff = 2;
            }
            else // can't go any higher than 2^32
            {
                buf[0] = (byte) 0xff;
                buf[1] = (byte) 0xfe;
                buf[2] = (byte) (textLength >> 24);
                buf[3] = (byte) (textLength >> 16);
                buf[4] = (byte) (textLength >> 8);
                buf[5] = (byte) textLength;
                bufOff = 6;
            }


            bufOff = cbcmacUpdate(buf, macBlock, bufOff, aad, 0, aad.length, workingkey, s);
            if (bufOff != 0)
            {
                Arrays.fill(buf, bufOff, AESPacketCipher.BLOCK_SIZE, (byte) 0);
                Bytes.xorTo(AESPacketCipher.BLOCK_SIZE, macBlock, 0, buf, 0);
                AESPacketCipher.processBlock(true, workingkey, s, buf, 0, macBlock, 0);
                bufOff = 0;
            }
        }

        if (inLen != 0)
        {
            bufOff = cbcmacUpdate(buf, macBlock, bufOff, in, inOff, inLen, workingkey, s);
            Arrays.fill(buf, bufOff, AESPacketCipher.BLOCK_SIZE, (byte) 0);
            Bytes.xorTo(AESPacketCipher.BLOCK_SIZE, buf, macBlock);
            AESPacketCipher.processBlock(true, workingkey, s, macBlock, 0, macBlock, 0);
        }

        Arrays.fill(macBlock, macSize, AESPacketCipher.BLOCK_SIZE, (byte) 0);
        Arrays.clear(buf);

    }


    private static int cbcmacUpdate(
            byte[] buf,
            byte[] accumulator,
            int bufOff,
            byte[] in,
            int inOff,
            int len,
            int[][] workingkey,
            byte[] s)
    {
        int gapLen = AESPacketCipher.BLOCK_SIZE - bufOff;
        if (len > gapLen)
        {
            System.arraycopy(in, inOff, buf, bufOff, gapLen);
            Bytes.xorTo(AESPacketCipher.BLOCK_SIZE, buf, accumulator);
            AESPacketCipher.processBlock(true, workingkey, s, accumulator, 0, accumulator, 0);
            bufOff = 0;
            len -= gapLen;
            inOff += gapLen;
            while (len > AESPacketCipher.BLOCK_SIZE)
            {
                Bytes.xor(AESPacketCipher.BLOCK_SIZE, accumulator, 0, in, inOff, accumulator, 0);
                AESPacketCipher.processBlock(true, workingkey, s, accumulator, 0, accumulator, 0);
                len -= AESPacketCipher.BLOCK_SIZE;
                inOff += AESPacketCipher.BLOCK_SIZE;
            }
        }
        System.arraycopy(in, inOff, buf, bufOff, len);
        bufOff += len;
        return bufOff;
    }


    protected static void ctrProcessBlock(byte[] counter, byte[] counterOut, byte[] in, int inOff,
                                          byte[] out, int outOff,
                                          int[][] workingkeys, byte[] s)
    {
        AESPacketCipher.processBlock(true, workingkeys, s, counter, 0, counterOut, 0);
        int i = counter.length;
        while (--i >= 0)
        {
            if (++counter[i] != 0)
            {
                break;
            }
        }
        Bytes.xorTee(16, in, inOff, counterOut, 0, out, outOff);
    }


    @Override
    public String toString()
    {
        return "CCM-PS[Java](AES[Java])";
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy