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

org.bouncycastle.crypto.fips.FipsDSA 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.fips;

import java.math.BigInteger;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.SecureRandom;

import org.bouncycastle.crypto.AsymmetricPrivateKey;
import org.bouncycastle.crypto.AsymmetricPublicKey;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.asymmetric.AsymmetricDSAPrivateKey;
import org.bouncycastle.crypto.asymmetric.AsymmetricDSAPublicKey;
import org.bouncycastle.crypto.asymmetric.AsymmetricKeyPair;
import org.bouncycastle.crypto.asymmetric.DSADomainParameters;
import org.bouncycastle.crypto.asymmetric.DSAValidationParameters;
import org.bouncycastle.crypto.internal.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.internal.Digest;
import org.bouncycastle.crypto.internal.EngineProvider;
import org.bouncycastle.crypto.internal.Permissions;
import org.bouncycastle.crypto.internal.PrimeCertaintyCalculator;
import org.bouncycastle.crypto.internal.params.DsaKeyGenerationParameters;
import org.bouncycastle.crypto.internal.params.DsaParameterGenerationParameters;
import org.bouncycastle.crypto.internal.params.DsaParameters;
import org.bouncycastle.crypto.internal.params.DsaPrivateKeyParameters;
import org.bouncycastle.crypto.internal.params.DsaPublicKeyParameters;
import org.bouncycastle.crypto.internal.params.DsaValidationParameters;
import org.bouncycastle.crypto.internal.params.ParametersWithRandom;
import org.bouncycastle.crypto.internal.test.ConsistencyTest;
import org.bouncycastle.math.internal.Primes;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.util.test.FixedSecureRandom;
import org.bouncycastle.util.test.TestRandomBigInteger;

/**
 * Source class for FIPS approved implementations of DSA based algorithms.
 */
public final class FipsDSA
{
    /**
     * DSA key marker, can be used for creating general purpose DSA keys.
     */
    public static final FipsAlgorithm ALGORITHM = new FipsAlgorithm("DSA", null);

    /**
     * DSA algorithm parameter source - default is SHA-1
     */
    public static final Parameters DSA = new Parameters();

    private static final EngineProvider ENGINE_PROVIDER;

    static
    {
        EngineProvider provider = new DsaProvider();

        // FSM_STATE:3.DSA.0,"POWER ON SELF-TEST", "DSA SIGN VERIFY KAT", "The module is performing ECDSA sign and verify KAT self-test"
        // FSM_TRANS:3.DSA.0,"POWER ON SELF-TEST", "DSA SIGN VERIFY KAT", "Invoke DSA Sign/Verify KAT self-test"
        provider.createEngine();
        // FSM_TRANS:3.DSA.1,"DSA SIGN VERIFY KAT", "POWER ON SELF-TEST", "DSA Sign/Verify KAT self-test successful completion"

        ENGINE_PROVIDER = provider;
    }

    private FipsDSA()
    {

    }

    private static DsaParameters getDomainParams(DSADomainParameters dsaParams)
    {
        return new DsaParameters(dsaParams.getP(), dsaParams.getQ(), dsaParams.getG());
    }

    private static DsaPrivateKeyParameters getLwKey(final AsymmetricDSAPrivateKey privKey)
    {
        return AccessController.doPrivileged(new PrivilegedAction()
        {
            public DsaPrivateKeyParameters run()
            {
                return new DsaPrivateKeyParameters(privKey.getX(), getDomainParams(privKey.getDomainParameters()));
            }
        });
    }

    private static void validateKeyPair(AsymmetricCipherKeyPair kp)
    {
        SelfTestExecutor.validate(ALGORITHM, kp, new ConsistencyTest()
        {
            public boolean hasTestPassed(AsymmetricCipherKeyPair kp)
            {
                final byte[] data = Hex.decode("576a1f885e3420128c8a656097ba7d8bb4c6f1b1853348cf2ba976971dbdbefc");

                DsaSigner signer = new DsaSigner();

                signer.init(true, new ParametersWithRandom(kp.getPrivate(), Utils.testRandom));

                BigInteger[] rv = signer.generateSignature(data);

                signer.init(false, kp.getPublic());

                return signer.verifySignature(data, rv[0], rv[1]);
            }
        });
    }

    /**
     * Parameters for DSA key pair generation.
     */
    public static final class KeyGenParameters
         extends FipsParameters
    {
        private final DSADomainParameters domainParameters;

        /**
         * Base constructor for the default algorithm ID.
         *
         * @param domainParameters DSA domain parameters representing the parameter set any generated keys will be for.
         */
        public KeyGenParameters(DSADomainParameters domainParameters)
        {
            super(ALGORITHM);
            this.domainParameters = domainParameters;
        }

        /**
         * Return the DSA domain parameters for this object.
         *
         * @return the DSA domain parameter set.
         */
        public DSADomainParameters getDomainParameters()
        {
            return domainParameters;
        }
    }

    /**
     * Parameters for DSA signatures.
     */
    public static final class Parameters
        extends FipsParameters
    {
        private final FipsDigestAlgorithm digestAlgorithm;

        Parameters()
        {
            super(FipsDSA.ALGORITHM);
            this.digestAlgorithm = FipsSHS.Algorithm.SHA1;
        }

        private Parameters(FipsDigestAlgorithm digestAlgorithm)
        {
            super(FipsDSA.ALGORITHM);

            if (digestAlgorithm == null && CryptoServicesRegistrar.isInApprovedOnlyMode())
            {
                PrivilegedUtils.checkPermission(Permissions.TlsNullDigestEnabled);
            }

            this.digestAlgorithm = digestAlgorithm;
        }

        /**
         * Return the algorithm for the underlying digest these parameters will use.
         *
         * @return the digest algorithm
         */
        public FipsDigestAlgorithm getDigestAlgorithm()
        {
            return digestAlgorithm;
        }

        /**
         * Return a new parameter set with for the passed in digest algorithm.
         *
         * @param digestAlgorithm the digest to use for signature generation.
         * @return a new parameter for signature generation.
         */
        public Parameters withDigestAlgorithm(FipsDigestAlgorithm digestAlgorithm)
        {
            return new Parameters(digestAlgorithm);
        }
    }

    /**
     * Parameters for DSA domain parameter generation.
     */
    public static final class DomainGenParameters
        extends FipsParameters
    {
        private final int L;
        private final int N;
        private final int certainty;

        private final BigInteger p;
        private final BigInteger q;
        private final byte[] seed;
        private final int usageIndex;

        /**
         * Construct just from strength (L) with a default value for N (160 for 1024, 256 for greater)
         * and a default certainty.
         *
         * @param strength desired length of prime P in bits (the effective key size).
         */
        public DomainGenParameters(int strength)
        {
            this(strength, (strength > 1024) ? 256 : 160, PrimeCertaintyCalculator.getDefaultCertainty(strength));      // Valid N for 2048/3072 , N for 1024
        }

        /**
         * Construct just from strength (L) with a default value for N (160 for 1024, 256 for greater).
         *
         * @param strength desired length of prime P in bits (the effective key size).
         * @param certainty certainty level for prime number generation.
         */
        public DomainGenParameters(int strength, int certainty)
        {
            this(strength, (strength > 1024) ? 256 : 160, certainty);            // Valid N for 2048/3072 , N for 1024
        }

        /**
         * Construct without a usage index, this will do a random construction of G.
         *
         * @param L desired length of prime P in bits (the effective key size).
         * @param N desired length of prime Q in bits.
         * @param certainty certainty level for prime number generation.
         */
        public DomainGenParameters(int L, int N, int certainty)
        {
            this(L, N, certainty, null, null, null, -1);
        }

        /**
         * Construct for a specific usage index - this has the effect of using verifiable canonical generation of G.
         *
         * @param L desired length of prime P in bits (the effective key size).
         * @param N desired length of prime Q in bits.
         * @param certainty certainty level for prime number generation.
         * @param usageIndex a valid usage index.
         */
        public DomainGenParameters(int L, int N, int certainty, int usageIndex)
        {
            this(L, N, certainty, null, null, null, usageIndex);
        }

        /**
         * Construct from initial prime values, this will do a random construction of G.
         *
         * @param p the prime P.
         * @param q the prime Q.
         */
        public DomainGenParameters(BigInteger p, BigInteger q)
        {
            this(p.bitLength(), q.bitLength(), 0, p, q, null, -1);
        }

        /**
         * Construct for a specific usage index and initial prime values - this has the effect of using verifiable canonical generation of G.
         *
         * @param p the prime P.
         * @param q the prime Q.
         * @param seed seed used in the generation of (p, q).
         * @param usageIndex a valid usage index.
         */
        public DomainGenParameters(BigInteger p, BigInteger q, byte[] seed, int usageIndex)
        {
            this(p.bitLength(), q.bitLength(), 0, p, q, Arrays.clone(seed), usageIndex);
        }

        DomainGenParameters(int L, int N, int certainty, BigInteger p, BigInteger q, byte[] seed, int usageIndex)
        {
            super(ALGORITHM);

            if (CryptoServicesRegistrar.isInApprovedOnlyMode())
            {
                if (p == null && certainty < PrimeCertaintyCalculator.getDefaultCertainty(L))
                {
                    throw new FipsUnapprovedOperationError("Prime generation certainty " + certainty + " inadequate for parameters of " + L + " bits", this.getAlgorithm());
                }
            }

            if (usageIndex > 255)
            {
                throw new IllegalArgumentException("Usage index must be in range 0 to 255 (or -1 to ignore)");
            }

            this.L = L;
            this.N = N;
            this.certainty = certainty;
            this.p = p;
            this.q = q;
            this.seed = seed;
            this.usageIndex = usageIndex;
        }
    }
    
    /**
     * Domain parameter generator for DSA.
     */
    public static final class DomainParametersGenerator
    {
        private final SecureRandom random;
        private final DomainGenParameters parameters;
        private final FipsDigestAlgorithm digestAlgorithm;

        /**
         * Default constructor using SHA-256 as the digest.
         *
         * @param parameters domain generation parameters.
         * @param random a source of randomness for the parameter generation.
         */
        public DomainParametersGenerator(DomainGenParameters parameters, SecureRandom random)
        {
            this(FipsSHS.Algorithm.SHA256, parameters, random);
        }

        /**
         * Base constructor.
         *
         * @param digestAlgorithm digest to use in prime calculations.
         * @param parameters domain generation parameters.
         * @param random a source of randomness for the parameter generation.
         */
        public DomainParametersGenerator(FipsDigestAlgorithm digestAlgorithm, DomainGenParameters parameters, SecureRandom random)
        {
            if (CryptoServicesRegistrar.isInApprovedOnlyMode())
            {
                int effSizeInBits = parameters.L;

                if (effSizeInBits != 2048 && effSizeInBits != 3072)
                {
                    throw new FipsUnapprovedOperationError("Attempt to create parameters with unapproved key size [" + effSizeInBits + "]", ALGORITHM);
                }

                Utils.validateRandom(random, Utils.getAsymmetricSecurityStrength(effSizeInBits), ALGORITHM, "Attempt to create parameters with unapproved RNG");
            }

            this.digestAlgorithm = digestAlgorithm;
            this.parameters = parameters;
            this.random = random;
        }

        /**
         * Generate a new set of DSA domain parameters.
         *
         * @return a new set of DSADomainParameters
         */
        public DSADomainParameters generateDomainParameters()
        {
            if (parameters.p != null)
            {
                if (parameters.seed != null && parameters.usageIndex >= 0)
                {
                    BigInteger g = DsaParametersGenerator.calculateGenerator_FIPS186_3_Verifiable(FipsSHS.createDigest(digestAlgorithm), parameters.p, parameters.q, parameters.seed, parameters.usageIndex);

                    return new DSADomainParameters(parameters.p, parameters.q, g, new DSAValidationParameters(parameters.seed, -1, parameters.usageIndex));
                }
                else
                {
                    BigInteger g = DsaParametersGenerator.calculateGenerator_FIPS186_3_Unverifiable(parameters.p, parameters.q, random);

                    return new DSADomainParameters(parameters.p, parameters.q, g, null);
                }
            }
            else
            {
                DsaParametersGenerator pGen = new DsaParametersGenerator(FipsSHS.createDigest(digestAlgorithm));

                DsaParameterGenerationParameters params = new DsaParameterGenerationParameters(
                    parameters.L, parameters.N, parameters.certainty, random, parameters.usageIndex);
                pGen.init(params);

                DsaParameters p = pGen.generateParameters();

                DsaValidationParameters validationParameters = p.getValidationParameters();

                return new DSADomainParameters(p.getP(), p.getQ(), p.getG(), new DSAValidationParameters(validationParameters.getSeed(), validationParameters.getCounter(), validationParameters.getUsageIndex()));
            }
        }
    }

    /**
     * Domain parameter validator for DSA.
     */
    public static final class DomainParametersValidator
    {
        private final Version version;
        private final FipsDigestAlgorithm digestAlgorithm;
        private final SecureRandom random;
        /**
         * Base constructor - for 186-4
         *
         * @param digestAlgorithm digest to use in prime calculations.
         * @param random source of randomness for prime number testing.
         */
        public DomainParametersValidator(FipsDigestAlgorithm digestAlgorithm, SecureRandom random)
        {
            this(Version.FIPS_PUB_186_4, digestAlgorithm, random);
        }

        /**
         * Base constructor.
         *
         * @param version the version of DSS the validator is for.
         * @param digestAlgorithm digest to use in prime calculations.
         * @param random source of randomness for prime number testing.
         */
        public DomainParametersValidator(Version version, FipsDigestAlgorithm digestAlgorithm, SecureRandom random)
        {
            if (Version.FIPS_PUB_186_2 == version && digestAlgorithm != FipsSHS.Algorithm.SHA1)
            {
                throw new IllegalArgumentException("186-2 can only validate with SHA-1");
            }

            if (CryptoServicesRegistrar.isInApprovedOnlyMode())
            {
                Utils.validateRandom(random, "FIPS SecureRandom required for DSA parameter validation in approved mode.");
            }

            this.version = version;
            this.digestAlgorithm = digestAlgorithm;
            this.random = random;
        }

        private static int getMinimumIterations(int L)
        {
            // Values based on FIPS 186-4 C.3 Table C.1
            return L <= 1024 ? 40 : (48 + 8 * ((L - 1) / 1024));
        }

        /**
         * Validate P and Q against the passed in seed and counter.
         *
         * @param p the prime P.
         * @param q the prime Q.
         * @param seed the seed P and Q were derived from.
         * @param counter the number of iterations required to derive P.
         * @return true if the P and Q values are the expected ones, false otherwise.
         */
        public boolean isValidPAndQ(BigInteger p, BigInteger q, byte[] seed, int counter)
        {
            Digest hash = FipsSHS.createDigest(digestAlgorithm);

            if (Version.FIPS_PUB_186_2 == version)
            {
                if (p.bitLength() != 1024 || q.bitLength() != 160 || counter > 4095)
                {
                    return false;
                }

                if (seed.length < 20)
                {
                    return false;
                }

                BigInteger computed_q = digest(hash, seed).xor(digest(hash, seedPlus1(seed)));

                computed_q = computed_q.setBit(0).setBit(159);

                if (!q.equals(computed_q) || !isProbablePrime(q, getMinimumIterations(1024)))
                {
                    return false;
                }

                BigInteger extra = BigInteger.ONE.shiftLeft(64);

                int i = 0;
                byte[] offset = Arrays.clone(seed);
                inc(offset);

                boolean computedPIsPrime = false;
                BigInteger computed_p = null;
                while (i <= counter)
                {
                    BigInteger W = BigInteger.ZERO;
                    for (int j = 0; j <= 5; j++)
                    {
                        inc(offset);
                        W = W.add(digest(hash, offset).shiftLeft(160 * j));
                    }
                    // (V[6] mod 2**63) * 2**960
                    inc(offset);
                    W = W.add(
                        digest(hash, offset).mod(extra).shiftLeft(160 * 6));

                    BigInteger X = W.setBit(1023);
                    BigInteger c = X.mod(q.shiftLeft(1));

                    computed_p = X.subtract(c.subtract(BigInteger.ONE));

                    if (computed_p.bitLength() == 1024)
                    {
                        if (isProbablePrime(computed_p, getMinimumIterations(1024)))
                        {
                            computedPIsPrime = true;
                            break;
                        }
                    }
                    i++;
                }

                if (i != counter || !p.equals(computed_p) || !computedPIsPrime)
                {
                    return false;
                }
            }
            else
            {
                int L = p.bitLength();
                int N = q.bitLength();

                if (!(L == 1024 && N == 160)
                    && !(L == 2048 && N == 224)
                    && !(L == 2048 && N == 256)
                    && !(L == 3072 && N == 256))
                {
                    return false;
                }

                if (counter > (4 * L - 1))
                {
                    return false;
                }

                if (seed.length * 8 < N)
                {
                    return false;
                }

                BigInteger twoPowNminus1 = BigInteger.ONE.shiftLeft(N - 1);
                BigInteger U = digest(hash, seed).mod(twoPowNminus1);

                BigInteger computed_q = U.setBit(0).setBit(N - 1);

                if (!q.equals(computed_q) || !isProbablePrime(q, getMinimumIterations(L)))
                {
                    return false;
                }

                int outlen = hash.getDigestSize() * 8;

                int n = (L + outlen - 1) / outlen - 1;
                int b = L - (n * outlen);
                BigInteger extra = BigInteger.ONE.shiftLeft(b);

                int i = 0;
                byte[] offset = Arrays.clone(seed);

                boolean computedPIsPrime = false;
                BigInteger computed_p = null;

                while (i <= counter)
                {
                    BigInteger W = BigInteger.ZERO;
                    for (int j = 0; j < n; j++)
                    {
                        inc(offset);
                        W = W.add(digest(hash, offset).shiftLeft(outlen * j));
                    }

                    inc(offset);
                    W = W.add(digest(hash, offset).mod(extra).shiftLeft(outlen * n));

                    BigInteger X = W.setBit(L - 1);
                    BigInteger c = X.mod(q.shiftLeft(1));

                    computed_p = X.subtract(c.subtract(BigInteger.ONE));

                    if (computed_p.bitLength() == L)
                    {
                        if (isProbablePrime(computed_p, getMinimumIterations(L)))
                        {
                            computedPIsPrime = true;
                            break;
                        }
                    }
                    i++;
                }

                if (i != counter || !p.equals(computed_p) || !computedPIsPrime)
                {
                    return false;
                }
            }

            return true;
        }

        /**
         * Do a partial validation of g against p and q.
         *
         * @param p the prime P.
         * @param q the prime Q.
         * @param g the generator G associated with P and Q.
         * @return true if the generator is partially valid, false otherwise.
         */
        public boolean isPartiallyValidG(BigInteger p, BigInteger q, BigInteger g)
        {
            if (BigInteger.valueOf(2).compareTo(g) > 0 || p.subtract(BigInteger.ONE).compareTo(g) < 0)
            {
                return false;
            }

            return g.modPow(q, p).equals(BigInteger.ONE);
        }

        /**
         * Do a full validation of g against p and q by including the seed and index
         * associated with g's related parameters.
         *
         * @param p the prime P.
         * @param q the prime Q.
         * @param seed the domain parameter seed used to generate p and q.
         * @param index the 8 bit usage index for G.
         * @param g the generator G associated with P and Q.
         * @return true if the generator is partially valid, false otherwise.
         */
        public boolean isValidG(BigInteger p, BigInteger q, byte[] seed, int index, BigInteger g)
        {
            Digest hash = FipsSHS.createDigest(digestAlgorithm);

            if ((index >>> 8) != 0)
            {
                return false;
            }

            if (BigInteger.valueOf(2).compareTo(g) > 0 || p.subtract(BigInteger.ONE).compareTo(g) < 0)
            {
                return false;
            }

            if (!g.modPow(q, p).equals(BigInteger.ONE))
            {
                return false;
            }

            BigInteger e = p.subtract(BigInteger.ONE).divide(q);
            int count = 0;

            byte[] counter = new byte[3];
            counter[0] = (byte)index;
            byte[] U = Arrays.concatenate(seed, Hex.decode("6767656E"), counter);

            BigInteger computed_g = null;
            // in our case the wrap check for count terminates at it's largest value.
            while (++count < (1 << 16))
            {
                inc(U);

                computed_g = digest(hash, U).modPow(e, p);

                if (computed_g.compareTo(BigInteger.ONE) <= 0)
                {
                    continue;
                }

                break;
            }

            return g.equals(computed_g);
        }

        private BigInteger digest(Digest hash, byte[] input)
        {
            byte[] res = new byte[hash.getDigestSize()];

            hash.update(input, 0, input.length);

            hash.doFinal(res, 0);

            return new BigInteger(1, res);
        }

        private byte[] seedPlus1(byte[] seed)
        {
            return inc(Arrays.clone(seed));
        }

        private byte[] inc(byte[] value)
        {
            // increment counter by 1.
            for (int i = value.length - 1; i >= 0 && ++value[i] == 0; i--)
            {
                ; // do nothing - pre-increment and test for 0 in counter does the job.
            }

            return value;
        }

        private boolean isProbablePrime(BigInteger x, int iterations)
        {
            /*
             * Primes class for FIPS 186-4 C.3 primality checking
             */
            return !Primes.hasAnySmallFactors(x) && Primes.isMRProbablePrime(x, random, iterations);
        }

        public enum Version
        {
            FIPS_PUB_186_2,
            FIPS_PUB_186_4
        }
    }

    /**
     * DSA key pair generator.
     */
    public static final class KeyPairGenerator
       extends FipsAsymmetricKeyPairGenerator
    {
        private final DsaKeyPairGenerator engine = new DsaKeyPairGenerator();
        private final DSADomainParameters domainParameters;
        private final DsaKeyGenerationParameters param;

        /**
         * Construct a key pair generator for DSA keys,
         *
         * @param keyGenParameters domain parameters and algorithm for the generated key.
         * @param random a source of randomness for calculating the private value.
         */
        public KeyPairGenerator(KeyGenParameters keyGenParameters, SecureRandom random)
        {
            super(keyGenParameters);

            if (CryptoServicesRegistrar.isInApprovedOnlyMode())
            {
                int effSizeInBits = keyGenParameters.getDomainParameters().getP().bitLength();

                if (effSizeInBits != 2048 && effSizeInBits != 3072)
                {
                    throw new FipsUnapprovedOperationError("Attempt to create key pair with unapproved key size [" + effSizeInBits + "]", keyGenParameters.getAlgorithm());
                }

                Utils.validateKeyPairGenRandom(random, Utils.getAsymmetricSecurityStrength(effSizeInBits), keyGenParameters.getAlgorithm());
            }

            this.domainParameters = keyGenParameters.getDomainParameters();

            this.param = new DsaKeyGenerationParameters(random, getDomainParams(domainParameters));
            this.engine.init(param);
        }

        /**
         * Generate a new DSA key pair.
         *
         * @return a new AsymmetricKeyPair containing a DSA key pair.
         */
        @Override
        public AsymmetricKeyPair generateKeyPair()
        {
            AsymmetricCipherKeyPair kp = engine.generateKeyPair();

            DsaPublicKeyParameters pubKey = (DsaPublicKeyParameters)kp.getPublic();
            DsaPrivateKeyParameters prvKey = (DsaPrivateKeyParameters)kp.getPrivate();

            FipsAlgorithm algorithm = this.getParameters().getAlgorithm();

            // FSM_STATE:5.3, "DSA PAIRWISE CONSISTENCY TEST", "The module is performing DSA Pairwise Consistency self-test"
            // FSM_TRANS:5.DSA.0,"CONDITIONAL TEST", "DSA PAIRWISE CONSISTENCY TEST", "Invoke DSA Pairwise Consistency test"
            validateKeyPair(kp);
            // FSM_TRANS:5.DSA.1,"DSA PAIRWISE CONSISTENCY TEST", "CONDITIONAL TEST", "DSA Pairwise Consistency test successful"

            return new AsymmetricKeyPair(new AsymmetricDSAPublicKey(algorithm, domainParameters, pubKey.getY()), new AsymmetricDSAPrivateKey(algorithm, domainParameters, prvKey.getX()));
        }
    }

    /**
     * Operator factory for creating DSA based signing and verification operators.
     */
    public static final class OperatorFactory
        extends FipsSignatureOperatorFactory
    {
        /**
         * Return a generator of DSA signatures. Note this operator needs to be associated with a SecureRandom to be
         * fully initialised.
         *
         * @param key the key to initialize the signature generator with.
         * @param parameters parameters required to configure the generation.
         * @return an OutputSignerUsingSecureRandom.
         */
        @Override
        public FipsOutputSignerUsingSecureRandom createSigner(AsymmetricPrivateKey key, final Parameters parameters)
        {
            DsaSigner dsaSigner = ENGINE_PROVIDER.createEngine();
            Digest digest = (parameters.digestAlgorithm != null) ? FipsSHS.createDigest(parameters.digestAlgorithm) : new NullDigest();

            AsymmetricDSAPrivateKey k = (AsymmetricDSAPrivateKey)key;

            final DsaPrivateKeyParameters privateKeyParameters = getLwKey(k);

            int effSizeInBits = privateKeyParameters.getParameters().getP().bitLength();

            if (CryptoServicesRegistrar.isInApprovedOnlyMode())
            {
                if (effSizeInBits != 2048 && effSizeInBits != 3072)
                {
                    throw new FipsUnapprovedOperationError("Attempt to create signer with unapproved keysize [" + effSizeInBits + "]", ALGORITHM);
                }
            }

            return new DSAOutputSigner(dsaSigner, digest, parameters, new DSAOutputSigner.Initializer()
            {
                  public void initialize(org.bouncycastle.crypto.internal.DSA signer, SecureRandom random)
                  {
                       signer.init(true, new ParametersWithRandom(privateKeyParameters, random));
                  }
            });
        }

        /**
         * Create a verifier for DSA signatures.
         *
         * @param key the key to initialize the verifier with.
         * @param parameters parameters required to configure the verification.
         * @return an OutputVerifier.
         */
        @Override
        public FipsOutputVerifier createVerifier(AsymmetricPublicKey key, final Parameters parameters)
        {
            DsaSigner dsaSigner = ENGINE_PROVIDER.createEngine();
            Digest digest = (parameters.digestAlgorithm != null) ? FipsSHS.createDigest(parameters.digestAlgorithm) : new NullDigest();

            AsymmetricDSAPublicKey k = (AsymmetricDSAPublicKey)key;

            DsaPublicKeyParameters publicKeyParameters = new DsaPublicKeyParameters(k.getY(), getDomainParams(k.getDomainParameters()));

            int effSizeInBits = publicKeyParameters.getParameters().getP().bitLength();

            if (CryptoServicesRegistrar.isInApprovedOnlyMode())
            {
                if (effSizeInBits != 1024 && effSizeInBits != 2048 && effSizeInBits != 3072)
                {
                    throw new FipsUnapprovedOperationError("Attempt to create verifier with unapproved keysize [" + effSizeInBits + "]", ALGORITHM);
                }
            }

            dsaSigner.init(false, publicKeyParameters);

            return new DSAOutputVerifier(dsaSigner, digest, parameters);
        }
    }

    private static class DsaProvider
        extends FipsEngineProvider
    {
        public DsaSigner createEngine()
        {
            // We do this using a pair-wise consistency test as per the IG 2nd March 2015, Section 9.4
           return SelfTestExecutor.validate(ALGORITHM, new DsaSigner(), new VariantKatTest()
            {
                @Override
                void evaluate(DsaSigner signer)
                    throws Exception
                {
                    BigInteger q = new BigInteger("90EAF4D1AF0708B1B612FF35E0A2997EB9E9D263C9CE659528945C0D", 16);

                    BigInteger p = new BigInteger(
                        "C196BA05AC29E1F9C3C72D56DFFC6154" +
                            "A033F1477AC88EC37F09BE6C5BB95F51C296DD20D1A28A06" +
                            "7CCC4D4316A4BD1DCA55ED1066D438C35AEBAABF57E7DAE4" +
                            "28782A95ECA1C143DB701FD48533A3C18F0FE23557EA7AE6" +
                            "19ECACC7E0B51652A8776D02A425567DED36EABD90CA33A1" +
                            "E8D988F0BBB92D02D1D20290113BB562CE1FC856EEB7CDD9" +
                            "2D33EEA6F410859B179E7E789A8F75F645FAE2E136D252BF" +
                            "FAFF89528945C1ABE705A38DBC2D364AADE99BE0D0AAD82E" +
                            "5320121496DC65B3930E38047294FF877831A16D5228418D" +
                            "E8AB275D7D75651CEFED65F78AFC3EA7FE4D79B35F62A040" +
                            "2A1117599ADAC7B269A59F353CF450E6982D3B1702D9CA83", 16);

                    BigInteger g = new BigInteger(
                        "A59A749A11242C58C894E9E5A91804E8" +
                            "FA0AC64B56288F8D47D51B1EDC4D65444FECA0111D78F35F" +
                            "C9FDD4CB1F1B79A3BA9CBEE83A3F811012503C8117F98E50" +
                            "48B089E387AF6949BF8784EBD9EF45876F2E6A5A495BE64B" +
                            "6E770409494B7FEE1DBB1E4B2BC2A53D4F893D418B715959" +
                            "2E4FFFDF6969E91D770DAEBD0B5CB14C00AD68EC7DC1E574" +
                            "5EA55C706C4A1C5C88964E34D09DEB753AD418C1AD0F4FDF" +
                            "D049A955E5D78491C0B7A2F1575A008CCD727AB376DB6E69" +
                            "5515B05BD412F5B8C2F4C77EE10DA48ABD53F5DD498927EE" +
                            "7B692BBBCDA2FB23A516C5B4533D73980B2A3B60E384ED20" +
                            "0AE21B40D273651AD6060C13D97FD69AA13C5611A51B9085", 16);

                    DsaKeyPairGenerator kpGen = new DsaKeyPairGenerator();

                    kpGen.init(new DsaKeyGenerationParameters(
                        new TestRandomBigInteger(Hex.decode("947813B589EDBA642411AD79205E43CE9B859327A4F84CF4B02628DB058A7B22771EA1852903711B")),
                        new DsaParameters(p, q, g)));

                    AsymmetricCipherKeyPair kp = kpGen.generateKeyPair();

                    signer.init(true, new ParametersWithRandom(kp.getPrivate(), new FixedSecureRandom(
                        new FixedSecureRandom.BigInteger("735959CC4463B8B440E407EECA8A473BF6A6D1FE657546F67D401F05"),
                        new FixedSecureRandom.Data(Hex.decode("01020304")))));

                    byte[] msg = Hex.decode("23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7");

                    BigInteger[] sig = signer.generateSignature(msg);

                    signer.init(false, kp.getPublic());

                    if (!signer.verifySignature(msg, sig[0], sig[1]))
                    {
                        fail("KAT signature not verified");
                    }
                }
            });
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy