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

org.bouncycastle.crypto.general.ChaCha20 Maven / Gradle / Ivy

Go to download

The FIPS 140-3 Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms certified to FIPS 140-3 level 1. This jar contains JCE provider and low-level API for the BC-FJA version 2.0.0, FIPS Certificate #4743. Please see certificate for certified platform details.

There is a newer version: 2.0.0
Show newest version
package org.bouncycastle.crypto.general;

import java.security.SecureRandom;

import org.bouncycastle.crypto.Algorithm;
import org.bouncycastle.crypto.IllegalKeyException;
import org.bouncycastle.crypto.SymmetricKey;
import org.bouncycastle.crypto.SymmetricSecretKey;
import org.bouncycastle.crypto.internal.CipherParameters;
import org.bouncycastle.crypto.internal.KeyGenerationParameters;
import org.bouncycastle.crypto.internal.StreamCipher;
import org.bouncycastle.crypto.internal.ValidatedSymmetricKey;
import org.bouncycastle.crypto.internal.params.KeyParameterImpl;
import org.bouncycastle.crypto.internal.params.ParametersWithIV;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;

/**
 * Source class for implementations of ChaCha20 based algorithms.
 */
public final class ChaCha20
{
    private ChaCha20()
    {
    }

    /**
     * Raw ChaCha20 algorithm, can be used for creating general purpose ChaCha20 keys.
     */
    public static final GeneralAlgorithm ALGORITHM = new GeneralAlgorithm("ChaCha20");

    private static final EngineProvider ENGINE_PROVIDER;

    static
    {
        EngineProvider provider = new EngineProvider();

        provider.createEngine();

        ENGINE_PROVIDER = provider;
    }

    public static final Parameters STREAM = new Parameters(ALGORITHM);

    /**
     * Parameters for ChaCha20 cipher.
     */
    public static final class Parameters
        extends GeneralParametersWithIV
    {
        private Parameters(GeneralAlgorithm algorithm, byte[] iv)
        {
            super(algorithm, 12, iv);
            if (iv != null && iv.length != 12)
            {
                throw new IllegalArgumentException("IV must be 12 bytes long");
            }
        }

        Parameters(GeneralAlgorithm algorithm)
        {
            this(algorithm, null);
        }

        public Parameters withIV(SecureRandom random)
        {
            byte[] iv = new byte[12];
            
            random.nextBytes(iv);

            return create(this.getAlgorithm(), iv);
        }

        protected Parameters create(GeneralAlgorithm algorithm, byte[] iv)
        {
            return new Parameters(algorithm, iv);
        }
    }

    /**
     * ChaCha20 key generator.
     */
    public static final class KeyGenerator
        extends GuardedSymmetricKeyGenerator
    {
        private static final int keySizeInBits = 256;
        
        private final GeneralAlgorithm algorithm;
        private final SecureRandom random;

        public KeyGenerator(SecureRandom random)
        {
            this(ALGORITHM, random);
        }

        public KeyGenerator(GeneralParameters parameterSet, SecureRandom random)
        {
             this((GeneralAlgorithm)parameterSet.getAlgorithm(), random);
        }

        private KeyGenerator(GeneralAlgorithm algorithm, SecureRandom random)
        {
            this.algorithm = algorithm;
            this.random = random;
        }

        public SymmetricKey doGenerateKey()
        {
            CipherKeyGenerator cipherKeyGenerator = new CipherKeyGenerator();

            cipherKeyGenerator.init(new KeyGenerationParameters(random, keySizeInBits));

            return new SymmetricSecretKey(algorithm, cipherKeyGenerator.generateKey());
        }
    }

    /**
     * Factory for basic ChaCha20 encryption/decryption operators.
     */
    public static final class OperatorFactory
        extends GuardedSymmetricStreamOperatorFactory
    {
        @Override
        protected StreamCipher createCipher(boolean forEncryption, SymmetricKey key, Parameters parameters, SecureRandom random)
        {
            StreamCipher cipher = ENGINE_PROVIDER.createEngine();

            CipherParameters params = Utils.getKeyParameter(validateKey(key, parameters.getAlgorithm()));

            if (parameters.iv == null || parameters.iv.length != 12)
            {
                throw new IllegalArgumentException("IV must be 12 bytes long");
            }

            cipher.init(forEncryption, new ParametersWithIV(params, parameters.iv));

            return cipher;
        }
    }

    private static ValidatedSymmetricKey validateKey(SymmetricKey key, Algorithm algorithm)
    {
        ValidatedSymmetricKey vKey = PrivilegedUtils.getValidatedKey(key);

        int keyLength = vKey.getKeySizeInBits();
        if (invalidKeySize(keyLength))
        {
            throw new IllegalKeyException("ChaCha20 key must be 256 bits");
        }

        Utils.checkKeyAlgorithm(vKey, ALGORITHM, algorithm);

        return vKey;
    }

    private static boolean invalidKeySize(int keyLength)
    {
        return keyLength != 256;
    }

    private static final class EngineProvider
        implements org.bouncycastle.crypto.internal.EngineProvider
    {
        static final byte[] input = Hex.decode("00112233445566778899aabbccddeeff");
        static final byte[] output = Hex.decode("39ec094e9d907f1d0524a9cc7401a4b6");
        static final byte[] iv = Hex.decode("000000000000000000000000");
        static final byte[] key = Hex.decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");

        public StreamCipher createEngine()
        {
            return SelfTestExecutor.validate(ALGORITHM, new ChaCha7539Engine(), new VariantKatTest()
            {
                public void evaluate(ChaCha7539Engine engine)
                {

                    byte[] tmp = new byte[input.length];

                    engine.init(true, new ParametersWithIV(new KeyParameterImpl(key), iv));

                    engine.processBytes(input, 0, input.length, tmp, 0);

                    if (!Arrays.areEqual(output, tmp))
                    {
                        fail("Failed self test on encryption");
                    }

                    engine.init(false, new ParametersWithIV(new KeyParameterImpl(key), iv));

                    engine.processBytes(output, 0, output.length, tmp, 0);

                    if (!Arrays.areEqual(input, tmp))
                    {
                        fail("Failed self test on decryption");
                    }
                }
            });
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy