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

org.bouncycastle.crypto.fips.FipsDH Maven / Gradle / Ivy

Go to download

The FIPS 140-2 Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms certified to FIPS 140-2 level 1. This jar contains the debug version JCE provider and low-level API for the BC-FJA version 1.0.2.3, FIPS Certificate #3514. Please note the debug jar is not certified.

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.AsymmetricDHPrivateKey;
import org.bouncycastle.crypto.asymmetric.AsymmetricDHPublicKey;
import org.bouncycastle.crypto.asymmetric.AsymmetricKeyPair;
import org.bouncycastle.crypto.asymmetric.DHDomainParameters;
import org.bouncycastle.crypto.asymmetric.DHDomainParametersID;
import org.bouncycastle.crypto.asymmetric.DHDomainParametersIndex;
import org.bouncycastle.crypto.asymmetric.DHValidationParameters;
import org.bouncycastle.crypto.asymmetric.DSADomainParameters;
import org.bouncycastle.crypto.asymmetric.DSAValidationParameters;
import org.bouncycastle.crypto.internal.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.internal.PrimeCertaintyCalculator;
import org.bouncycastle.crypto.internal.params.DhKeyGenerationParameters;
import org.bouncycastle.crypto.internal.params.DhParameters;
import org.bouncycastle.crypto.internal.params.DhPrivateKeyParameters;
import org.bouncycastle.crypto.internal.params.DhPublicKeyParameters;
import org.bouncycastle.crypto.internal.params.DhuPrivateParameters;
import org.bouncycastle.crypto.internal.params.DhuPublicParameters;
import org.bouncycastle.crypto.internal.params.MqvPrivateParameters;
import org.bouncycastle.crypto.internal.params.MqvPublicParameters;
import org.bouncycastle.crypto.internal.test.ConsistencyTest;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.util.encoders.Hex;

/**
 * Source class for FIPS approved mode Diffie-Hellman implementations.
 */
public final class FipsDH
{
    private static final int MIN_FIPS_KEY_STRENGTH = 2048;       // 112 bits of security

    static final FipsEngineProvider AGREEMENT_PROVIDER;
    static final FipsEngineProvider MQV_PROVIDER;
    static final FipsEngineProvider DHU_PROVIDER;

    private enum Variations
    {
        DH,
        MQV,
        DHU
    }

    /**
     * Basic Diffie-Hellman key marker, can be used for creating general purpose Diffie-Hellman keys.
     */
    public static final FipsAlgorithm ALGORITHM = new FipsAlgorithm("DH");

    private static final FipsAlgorithm ALGORITHM_DH = new FipsAlgorithm("DH", Variations.DH);
    private static final FipsAlgorithm ALGORITHM_MQV = new FipsAlgorithm("DH", Variations.MQV);
    private static final FipsAlgorithm ALGORITHM_DHU = new FipsAlgorithm("DH", Variations.DHU);

    /**
     * Regular Diffie-Hellman algorithm marker.
     */
    public static final AgreementParameters DH = new AgreementParameters();
    /**
     * Regular MQV algorithm marker.
     */
    public static final MQVAgreementParametersBuilder MQV = new MQVAgreementParametersBuilder();

    /**
     * Unified Diffie-Hellman algorithm marker.
     */
    public static final DHUAgreementParametersBuilder DHU = new DHUAgreementParametersBuilder();

    /**
     * An enumeration of DHDomainParametersID for some pre-defined DH parameter sets.
     */
    public enum DomainParameterID
        implements DHDomainParametersID
    {
        ffdhe2048("ffdhe2048"),
        ffdhe3072("ffdhe3072"),
        ffdhe4096("ffdhe4096"),
        ffdhe6144("ffdhe6144"),
        ffdhe8192("ffdhe8192"),
        modp2048("modp2048"),
        modp3072("modp3072"),
        modp4096("modp4096"),
        modp6144("modp6144"),
        modp8192("modp8192");

        private final String name;

        DomainParameterID(String name)
        {
            this.name = name;
        }

        public String getName()
        {
            return name;
        }
    }

    static
    {
        // FSM_STATE:5.DH.0,"KAS CVL Primitive 'Z' computation KAT", "The module is performing KAS CVL Primitive 'Z' computation KAT verify KAT self-test"
        // FSM_TRANS:5.DH.0.0,"CONDITIONAL TEST", "KAS CVL Primitive 'Z' computation KAT", "Invoke KAS CVL Primitive 'Z' computation KAT self-test"
        ffPrimitiveZTest();
        // FSM_TRANS:5.DH.0.1,"KAS CVL Primitive 'Z' computation KAT", "CONDITIONAL TEST", "KAS CVL Primitive 'Z' computation KAT self-test successful completion"
        // FSM_TRANS:5.DH.0.2,"KAS CVL Primitive 'Z' computation KAT", "SOFT ERROR", "KAS CVL Primitive 'Z' computation KAT self-test failed"

        AGREEMENT_PROVIDER = new AgreementProvider();
        MQV_PROVIDER = new MqvProvider();
        DHU_PROVIDER = new DhuProvider();

        // FSM_STATE:5.DH.1,"FF AGREEMENT KAT", "The module is performing FF Key Agreement verify KAT self-test"
        // FSM_TRANS:5.DH.1.0,"CONDITIONAL TEST", "FF AGREEMENT KAT", "Invoke FF Diffie-Hellman/MQV  KAT self-test"
        AGREEMENT_PROVIDER.createEngine();
        MQV_PROVIDER.createEngine();
        DHU_PROVIDER.createEngine();
        // FSM_TRANS:5.DH.1.1,"FF AGREEMENT KAT", "CONDITIONAL TEST", "FF Diffie-Hellman/MQV KAT self-test successful completion"
        // FSM_TRANS:5.DH.1.2,"FF AGREEMENT KAT", "SOFT ERROR", "FF Diffie-Hellman/MQV KAT self-test failed"
    }

    private FipsDH()
    {

    }

    /**
     * Parameters for Diffie-Hellman key pair generation.
     */
    public static final class KeyGenParameters
        extends FipsParameters
    {
        private final DHDomainParameters domainParameters;

        /**
         * Constructor for the default algorithm ID.
         *
         * @param domainParameters Diffie-Hellman domain parameters any generated keys will be for.
         */
        public KeyGenParameters(DHDomainParameters domainParameters)
        {
            this(ALGORITHM, domainParameters);
        }

        /**
         * Base constructor for specifying an algorithm ID from a parameter set.
         *
         * @param parameters       the parameters containing the algorithm the generated keys are for.
         * @param domainParameters Diffie-Hellman domain parameters any generated keys will be for.
         */
        public KeyGenParameters(AgreementParameters parameters, DHDomainParameters domainParameters)
        {
            this(parameters.getAlgorithm(), domainParameters);
        }

        /**
         * Base constructor for specifying an algorithm ID from an MQV builder.
         *
         * @param builder          the parameters containing the algorithm the generated keys are for.
         * @param domainParameters Diffie-Hellman domain parameters any generated keys will be for.
         */
        public KeyGenParameters(MQVAgreementParametersBuilder builder, DHDomainParameters domainParameters)
        {
            this(builder.getAlgorithm(), domainParameters);
        }

        /**
         * Base constructor for specifying an algorithm ID from an Diffie-Hellman Unified builder.
         *
         * @param builder          the parameters containing the algorithm the generated keys are for.
         * @param domainParameters Diffie-Hellman domain parameters any generated keys will be for.
         */
        public KeyGenParameters(DHUAgreementParametersBuilder builder, DHDomainParameters domainParameters)
        {
            this(builder.getAlgorithm(), domainParameters);
        }

        /**
         * Base constructor for specifying an algorithm ID.
         *
         * @param algorithm        the particular algorithm generated keys are for.
         * @param domainParameters Diffie-Hellman domain parameters any generated keys will be for.
         */
        private KeyGenParameters(FipsAlgorithm algorithm, DHDomainParameters domainParameters)
        {
            super(algorithm);
            this.domainParameters = domainParameters;
        }

        /**
         * Return the Diffie-Hellman domain parameters for this object.
         *
         * @return the Diffie-Hellman domain parameter set.
         */
        public DHDomainParameters getDomainParameters()
        {
            return domainParameters;
        }
    }

    /**
     * Parameters for Diffie-Hellman based key agreement.
     */
    public static final class AgreementParameters
        extends FipsAgreementParameters
    {
        /**
         * Default constructor which specifies returning the raw secret on agreement calculation.
         */
        AgreementParameters()
        {
            this(null);
        }

        private AgreementParameters(FipsAlgorithm digestAlgorithm)
        {
            super(ALGORITHM, digestAlgorithm);
        }

        private AgreementParameters(FipsKDF.PRF prfAlgorithm, byte[] salt)
        {
            super(ALGORITHM, prfAlgorithm, salt);
        }

        private AgreementParameters(FipsAlgorithm agreementAlgorithm, FipsKDF.AgreementKDFParametersBuilder kdfType, byte[] iv, int outputSize)
        {
            super(agreementAlgorithm, kdfType, iv, outputSize);
        }

        /**
         * Add a digest algorithm to process the Z value with.
         *
         * @param digestAlgorithm digest algorithm to use.
         * @return a new parameter set, including the digest algorithm
         */
        public AgreementParameters withDigest(FipsAlgorithm digestAlgorithm)
        {
            return new AgreementParameters(digestAlgorithm);
        }

        /**
         * Add a PRF algorithm and salt to process the Z value with (as in SP 800-56C)
         *
         * @param prfAlgorithm PRF represent the MAC/HMAC algorithm to use.
         * @param salt         the salt to use to initialise the PRF
         * @return a new parameter set, including the digest algorithm
         */
        public AgreementParameters withPRF(FipsKDF.PRF prfAlgorithm, byte[] salt)
        {
            return new AgreementParameters(prfAlgorithm, salt);
        }

        /**
         * Add a KDF to process the Z value with. The outputSize parameter determines how many bytes
         * will be generated.
         *
         * @param kdfType    KDF algorithm type to use for parameter creation.
         * @param iv         the iv parameter for KDF initialization.
         * @param outputSize the size of the output to be generated from the KDF.
         * @return a new parameter set, the KDF definition.
         */
        public AgreementParameters withKDF(FipsKDF.AgreementKDFParametersBuilder kdfType, byte[] iv, int outputSize)
        {
            return new AgreementParameters(this.getAlgorithm(), kdfType, iv, outputSize);
        }
    }

    /**
     * Initial builder for MQV parameters.
     */
    public static final class MQVAgreementParametersBuilder
        extends FipsParameters
    {
        MQVAgreementParametersBuilder()
        {
            super(ALGORITHM_MQV);
        }

        /**
         * Constructor for DH MQV parameters from an ephemeral public/private key pair. This constructor
         * will result in an agreement which returns the raw calculated agreement value, or shared secret.
         *
         * @param ephemeralKeyPair       our ephemeral public/private key pair.
         * @param otherPartyEphemeralKey the other party's ephemeral public key.
         */
        public MQVAgreementParameters using(AsymmetricKeyPair ephemeralKeyPair, AsymmetricDHPublicKey otherPartyEphemeralKey)
        {
            return new MQVAgreementParameters((AsymmetricDHPublicKey)ephemeralKeyPair.getPublicKey(), (AsymmetricDHPrivateKey)ephemeralKeyPair.getPrivateKey(), otherPartyEphemeralKey, null);
        }

        /**
         * Constructor for DH MQV parameters which assumes later calculation of our ephemeral public key. This constructor
         * will result in an agreement which returns the raw calculated agreement value, or shared secret.
         *
         * @param ephemeralPrivateKey    our ephemeral private key.
         * @param otherPartyEphemeralKey the other party's ephemeral public key.
         */
        public MQVAgreementParameters using(AsymmetricDHPrivateKey ephemeralPrivateKey, AsymmetricDHPublicKey otherPartyEphemeralKey)
        {
            return new MQVAgreementParameters(null, ephemeralPrivateKey, otherPartyEphemeralKey, null);
        }

        /**
         * Constructor for DH MQV parameters which results in an agreement returning the raw value.
         *
         * @param ephemeralPublicKey     our ephemeral public key.
         * @param ephemeralPrivateKey    our ephemeral private key.
         * @param otherPartyEphemeralKey the other party's ephemeral public key.
         */
        public MQVAgreementParameters using(AsymmetricDHPublicKey ephemeralPublicKey, AsymmetricDHPrivateKey ephemeralPrivateKey, AsymmetricDHPublicKey otherPartyEphemeralKey)
        {
            return new MQVAgreementParameters(ephemeralPublicKey, ephemeralPrivateKey, otherPartyEphemeralKey, null);
        }
    }

    /**
     * Parameters for Diffie-Hellman based key agreement using MQV.
     */
    public static final class MQVAgreementParameters
        extends FipsAgreementParameters
    {
        private final AsymmetricDHPublicKey ephemeralPublicKey;
        private final AsymmetricDHPrivateKey ephemeralPrivateKey;
        private final AsymmetricDHPublicKey otherPartyEphemeralKey;

        private MQVAgreementParameters(AsymmetricDHPublicKey ephemeralPublicKey, AsymmetricDHPrivateKey ephemeralPrivateKey, AsymmetricDHPublicKey otherPartyEphemeralKey, FipsAlgorithm digestAlgorithm)
        {
            super(ALGORITHM_MQV, digestAlgorithm);

            this.ephemeralPublicKey = ephemeralPublicKey;
            this.ephemeralPrivateKey = ephemeralPrivateKey;
            this.otherPartyEphemeralKey = otherPartyEphemeralKey;
        }

        private MQVAgreementParameters(AsymmetricDHPublicKey ephemeralPublicKey, AsymmetricDHPrivateKey ephemeralPrivateKey, AsymmetricDHPublicKey otherPartyEphemeralKey, FipsKDF.PRF prfAlgorithm, byte[] salt)
        {
            super(ALGORITHM_MQV, prfAlgorithm, salt);

            this.ephemeralPublicKey = ephemeralPublicKey;
            this.ephemeralPrivateKey = ephemeralPrivateKey;
            this.otherPartyEphemeralKey = otherPartyEphemeralKey;
        }

        private MQVAgreementParameters(AsymmetricDHPublicKey ephemeralPublicKey, AsymmetricDHPrivateKey ephemeralPrivateKey, AsymmetricDHPublicKey otherPartyEphemeralKey, FipsKDF.AgreementKDFParametersBuilder kdfType, byte[] iv, int outputSize)
        {
            super(ALGORITHM_MQV, kdfType, iv, outputSize);

            this.ephemeralPublicKey = ephemeralPublicKey;
            this.ephemeralPrivateKey = ephemeralPrivateKey;
            this.otherPartyEphemeralKey = otherPartyEphemeralKey;
        }

        /**
         * Return our ephemeral public key, if present.
         *
         * @return our ephemeral public key, or null.
         */
        public AsymmetricDHPublicKey getEphemeralPublicKey()
        {
            return ephemeralPublicKey;
        }

        /**
         * Return our ephemeral private key.
         *
         * @return our ephemeral private key.
         */
        public AsymmetricDHPrivateKey getEphemeralPrivateKey()
        {
            return ephemeralPrivateKey;
        }

        /**
         * Return the other party's ephemeral public key.
         *
         * @return the other party's ephemeral public key.
         */
        public AsymmetricDHPublicKey getOtherPartyEphemeralKey()
        {
            return otherPartyEphemeralKey;
        }

        /**
         * Add a digest algorithm to process the Z value with.
         *
         * @param digestAlgorithm digest algorithm to use.
         * @return a new parameter set, including the digest algorithm
         */
        public MQVAgreementParameters withDigest(FipsAlgorithm digestAlgorithm)
        {
            return new MQVAgreementParameters(this.ephemeralPublicKey, this.ephemeralPrivateKey, this.otherPartyEphemeralKey, digestAlgorithm);
        }

        /**
         * Add a PRF algorithm and salt to process the Z value with (as in SP 800-56C)
         *
         * @param prfAlgorithm PRF represent the MAC/HMAC algorithm to use.
         * @param salt         the salt to use to initialise the PRF
         * @return a new parameter set, including the digest algorithm
         */
        public MQVAgreementParameters withPRF(FipsKDF.PRF prfAlgorithm, byte[] salt)
        {
            return new MQVAgreementParameters(this.ephemeralPublicKey, this.ephemeralPrivateKey, this.otherPartyEphemeralKey, prfAlgorithm, salt);
        }

        /**
         * Add a KDF to process the Z value with. The outputSize parameter determines how many bytes
         * will be generated.
         *
         * @param kdfType    KDF algorithm type to use for parameter creation.
         * @param iv         the iv parameter for KDF initialization.
         * @param outputSize the size of the output to be generated from the KDF.
         * @return a new parameter set, the KDF definition.
         */
        public MQVAgreementParameters withKDF(FipsKDF.AgreementKDFParametersBuilder kdfType, byte[] iv, int outputSize)
        {
            return new MQVAgreementParameters(this.ephemeralPublicKey, this.ephemeralPrivateKey, this.otherPartyEphemeralKey, kdfType, iv, outputSize);
        }
    }

    /**
     * Initial builder for DHU parameters.
     */
    public static final class DHUAgreementParametersBuilder
        extends FipsParameters
    {
        DHUAgreementParametersBuilder()
        {
            super(ALGORITHM_DHU);
        }

        /**
         * Constructor for DH DHU parameters from an ephemeral public/private key pair. This constructor
         * will result in an agreement which returns the raw calculated agreement value, or shared secret.
         *
         * @param ephemeralKeyPair       our ephemeral public/private key pair.
         * @param otherPartyEphemeralKey the other party's ephemeral public key.
         */
        public DHUAgreementParameters using(AsymmetricKeyPair ephemeralKeyPair, AsymmetricDHPublicKey otherPartyEphemeralKey)
        {
            return new DHUAgreementParameters((AsymmetricDHPublicKey)ephemeralKeyPair.getPublicKey(), (AsymmetricDHPrivateKey)ephemeralKeyPair.getPrivateKey(), otherPartyEphemeralKey, null);
        }

        /**
         * Constructor for DH DHU parameters which assumes later calculation of our ephemeral public key. This constructor
         * will result in an agreement which returns the raw calculated agreement value, or shared secret.
         *
         * @param ephemeralPrivateKey    our ephemeral private key.
         * @param otherPartyEphemeralKey the other party's ephemeral public key.
         */
        public DHUAgreementParameters using(AsymmetricDHPrivateKey ephemeralPrivateKey, AsymmetricDHPublicKey otherPartyEphemeralKey)
        {
            return new DHUAgreementParameters(null, ephemeralPrivateKey, otherPartyEphemeralKey, null);
        }

        /**
         * Constructor for DH DHU parameters which results in an agreement returning the raw value.
         *
         * @param ephemeralPublicKey     our ephemeral public key.
         * @param ephemeralPrivateKey    our ephemeral private key.
         * @param otherPartyEphemeralKey the other party's ephemeral public key.
         */
        public DHUAgreementParameters using(AsymmetricDHPublicKey ephemeralPublicKey, AsymmetricDHPrivateKey ephemeralPrivateKey, AsymmetricDHPublicKey otherPartyEphemeralKey)
        {
            return new DHUAgreementParameters(ephemeralPublicKey, ephemeralPrivateKey, otherPartyEphemeralKey, null);
        }
    }

    /**
     * Parameters for Diffie-Hellman based key agreement using DHU.
     */
    public static final class DHUAgreementParameters
        extends FipsAgreementParameters
    {
        private final AsymmetricDHPublicKey ephemeralPublicKey;
        private final AsymmetricDHPrivateKey ephemeralPrivateKey;
        private final AsymmetricDHPublicKey otherPartyEphemeralKey;

        private DHUAgreementParameters(AsymmetricDHPublicKey ephemeralPublicKey, AsymmetricDHPrivateKey ephemeralPrivateKey, AsymmetricDHPublicKey otherPartyEphemeralKey, FipsAlgorithm digestAlgorithm)
        {
            super(ALGORITHM_DHU, digestAlgorithm);

            this.ephemeralPublicKey = ephemeralPublicKey;
            this.ephemeralPrivateKey = ephemeralPrivateKey;
            this.otherPartyEphemeralKey = otherPartyEphemeralKey;
        }

        private DHUAgreementParameters(AsymmetricDHPublicKey ephemeralPublicKey, AsymmetricDHPrivateKey ephemeralPrivateKey, AsymmetricDHPublicKey otherPartyEphemeralKey, FipsKDF.PRF prfAlgorithm, byte[] salt)
        {
            super(ALGORITHM_DHU, prfAlgorithm, salt);

            this.ephemeralPublicKey = ephemeralPublicKey;
            this.ephemeralPrivateKey = ephemeralPrivateKey;
            this.otherPartyEphemeralKey = otherPartyEphemeralKey;
        }

        private DHUAgreementParameters(AsymmetricDHPublicKey ephemeralPublicKey, AsymmetricDHPrivateKey ephemeralPrivateKey, AsymmetricDHPublicKey otherPartyEphemeralKey, FipsKDF.AgreementKDFParametersBuilder kdfType, byte[] iv, int outputSize)
        {
            super(ALGORITHM_DHU, kdfType, iv, outputSize);

            this.ephemeralPublicKey = ephemeralPublicKey;
            this.ephemeralPrivateKey = ephemeralPrivateKey;
            this.otherPartyEphemeralKey = otherPartyEphemeralKey;
        }

        /**
         * Return our ephemeral public key, if present.
         *
         * @return our ephemeral public key, or null.
         */
        public AsymmetricDHPublicKey getEphemeralPublicKey()
        {
            return ephemeralPublicKey;
        }

        /**
         * Return our ephemeral private key.
         *
         * @return our ephemeral private key.
         */
        public AsymmetricDHPrivateKey getEphemeralPrivateKey()
        {
            return ephemeralPrivateKey;
        }

        /**
         * Return the other party's ephemeral public key.
         *
         * @return the other party's ephemeral public key.
         */
        public AsymmetricDHPublicKey getOtherPartyEphemeralKey()
        {
            return otherPartyEphemeralKey;
        }

        /**
         * Add a digest algorithm to process the Z value with.
         *
         * @param digestAlgorithm digest algorithm to use.
         * @return a new parameter set, including the digest algorithm
         */
        public DHUAgreementParameters withDigest(FipsAlgorithm digestAlgorithm)
        {
            return new DHUAgreementParameters(this.ephemeralPublicKey, this.ephemeralPrivateKey, this.otherPartyEphemeralKey, digestAlgorithm);
        }

        /**
         * Add a PRF algorithm and salt to process the Z value with (as in SP 800-56C)
         *
         * @param prfAlgorithm PRF represent the MAC/HMAC algorithm to use.
         * @param salt         the salt to use to initialise the PRF
         * @return a new parameter set, including the digest algorithm
         */
        public DHUAgreementParameters withPRF(FipsKDF.PRF prfAlgorithm, byte[] salt)
        {
            return new DHUAgreementParameters(this.ephemeralPublicKey, this.ephemeralPrivateKey, this.otherPartyEphemeralKey, prfAlgorithm, salt);
        }

        /**
         * Add a KDF to process the Z value with. The outputSize parameter determines how many bytes
         * will be generated.
         *
         * @param kdfType    KDF algorithm type to use for parameter creation.
         * @param iv         the iv parameter for KDF initialization.
         * @param outputSize the size of the output to be generated from the KDF.
         * @return a new parameter set, the KDF definition.
         */
        public DHUAgreementParameters withKDF(FipsKDF.AgreementKDFParametersBuilder kdfType, byte[] iv, int outputSize)
        {
            return new DHUAgreementParameters(this.ephemeralPublicKey, this.ephemeralPrivateKey, this.otherPartyEphemeralKey, kdfType, iv, outputSize);
        }
    }

    /**
     * Parameters for generating Diffie-Hellman domain parameters.
     */
    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);
        }

        private 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;
        }
    }

    /**
     * Generator class for Diffie-Hellman domain parameters.
     */
    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)
                {
                    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 Diffie-Hellman domain parameters.
         *
         * @return a new set of DHDomainParameters
         */
        public DHDomainParameters generateDomainParameters()
        {
            if (parameters.L < MIN_FIPS_KEY_STRENGTH)
            {
                if (CryptoServicesRegistrar.isInApprovedOnlyMode())
                {
                    throw new FipsUnapprovedOperationError("Requested DH parameter strength too small for approved mode: " + parameters.L);
                }

                DhParametersGenerator pGen = new DhParametersGenerator();

                pGen.init(parameters.L, parameters.certainty, random);

                DhParameters p = pGen.generateParameters();

                return new DHDomainParameters(p.getP(), p.getQ(), p.getG(), p.getJ(), null);
            }

            FipsDSA.DomainGenParameters params = new FipsDSA.DomainGenParameters(parameters.L, parameters.N, parameters.certainty, parameters.p, parameters.q, parameters.seed, parameters.usageIndex);
            FipsDSA.DomainParametersGenerator pGen = new FipsDSA.DomainParametersGenerator(digestAlgorithm, params, random);

            DSADomainParameters domainParameters = pGen.generateDomainParameters();
            DSAValidationParameters vParams = domainParameters.getValidationParameters();

            if (vParams != null)
            {
                return new DHDomainParameters(domainParameters.getP(), domainParameters.getQ(), domainParameters.getG(), null, new DHValidationParameters(vParams.getSeed(), vParams.getCounter(), vParams.getUsageIndex()));
            }
            else
            {
                return new DHDomainParameters(domainParameters.getP(), domainParameters.getQ(), domainParameters.getG());
            }
        }
    }

    /**
     * Key pair generator for Diffie-Hellman key pairs.
     */
    public static final class KeyPairGenerator
        extends FipsAsymmetricKeyPairGenerator
    {
        private final DhKeyPairGenerator engine = new DhKeyPairGenerator();
        private final DHDomainParameters domainParameters;
        private final DhKeyGenerationParameters param;

        /**
         * Construct a key pair generator for Diffie-Hellman 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 sizeInBits = keyGenParameters.domainParameters.getP().bitLength();
                if (sizeInBits < MIN_FIPS_KEY_STRENGTH)
                {
                    throw new FipsUnapprovedOperationError("Attempt to create key of less than " + MIN_FIPS_KEY_STRENGTH + " bits", keyGenParameters.getAlgorithm());
                }

                Utils.validateKeyPairGenRandom(random, Utils.getAsymmetricSecurityStrength(sizeInBits), ALGORITHM);
            }

            this.param = new DhKeyGenerationParameters(random, getDomainParams(keyGenParameters.getDomainParameters()));
            this.domainParameters = keyGenParameters.getDomainParameters();
            this.engine.init(param);
        }

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

            DhPublicKeyParameters pubKey = (DhPublicKeyParameters)kp.getPublic();
            DhPrivateKeyParameters prvKey = (DhPrivateKeyParameters)kp.getPrivate();

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


            // FSM_STATE:5.DH.2, "DH PAIRWISE CONSISTENCY TEST", "The module is performing DH Pairwise Consistency self-test"
            // FSM_TRANS:5.DH.2.0,"CONDITIONAL TEST", "DH PAIRWISE CONSISTENCY TEST", "Invoke DH Pairwise Consistency test"
            validateKeyPair(algorithm, kp);
            // FSM_TRANS:5.DH.2.1,"DH PAIRWISE CONSISTENCY TEST", "CONDITIONAL TEST", "DH Pairwise Consistency test successful"
            // FSM_TRANS:5.DH.2.2,"DH PAIRWISE CONSISTENCY TEST", "SOFT ERROR", "DH Pairwise Consistency test failed"

            return new AsymmetricKeyPair(new AsymmetricDHPublicKey(algorithm, domainParameters, pubKey.getY()), new AsymmetricDHPrivateKey(algorithm, domainParameters, prvKey.getX()));
        }
    }

    /**
     * Factory for Agreement operators based on Diffie-Hellman
     */
    public static final class DHAgreementFactory
        extends FipsAgreementFactory
    {
        /**
         * Return an Agreement operator based on the regular Diffie-Hellman algorithm.
         *
         * @param key        the private key to initialize the Agreement with.
         * @param parameters the parameters for configuring the agreement.
         * @return a new Agreement operator for Diffie-Hellman.
         */
        @Override
        public FipsAgreement createAgreement(AsymmetricPrivateKey key, final AgreementParameters parameters)
        {
            AsymmetricDHPrivateKey dhKey = (AsymmetricDHPrivateKey)key;
            DhPrivateKeyParameters lwDhKey = getLwKey(dhKey);

            final DhBasicAgreement dh = AGREEMENT_PROVIDER.createEngine();

            dh.init(lwDhKey);

            return new FipsAgreement()
            {
                @Override
                public AgreementParameters getParameters()
                {
                    return parameters;
                }

                @Override
                public byte[] calculate(AsymmetricPublicKey key)
                {
                    AsymmetricDHPublicKey dhKey = (AsymmetricDHPublicKey)key;
                    DhPublicKeyParameters lwDhKey = new DhPublicKeyParameters(dhKey.getY(), getDomainParams(dhKey.getDomainParameters()));

                    int length = dh.getFieldSize();
                    BigInteger z = dh.calculateAgreement(lwDhKey);
                    byte[] zBytes = BigIntegers.asUnsignedByteArray(length, z);

                    return FipsKDF.processZBytes(zBytes, parameters);
                }
            };
        }
    }

    /**
     * Factory for Unified Agreement operators based on Diffie-Hellman
     */
    public static final class DHUAgreementFactory
        extends FipsAgreementFactory
    {
        /**
         * Return an Agreement operator based on the regular Diffie-Hellman algorithm.
         *
         * @param key        the private key to initialize the Agreement with.
         * @param parameters the parameters for configuring the agreement.
         * @return a new Agreement operator for Diffie-Hellman.
         */
        @Override
        public FipsAgreement createAgreement(AsymmetricPrivateKey key, final DHUAgreementParameters parameters)
        {
            AsymmetricDHPrivateKey dhKey = (AsymmetricDHPrivateKey)key;
            DhuPrivateParameters lwDhKey = new DhuPrivateParameters(getLwKey(dhKey), getLwKey(parameters.ephemeralPrivateKey));

            final DhuBasicAgreement dh = DHU_PROVIDER.createEngine();

            dh.init(lwDhKey);

            return new FipsAgreement()
            {
                @Override
                public DHUAgreementParameters getParameters()
                {
                    return parameters;
                }

                @Override
                public byte[] calculate(AsymmetricPublicKey key)
                {
                    AsymmetricDHPublicKey dhKey = (AsymmetricDHPublicKey)key;
                    DhPublicKeyParameters lwDhKey = new DhPublicKeyParameters(dhKey.getY(), getDomainParams(dhKey.getDomainParameters()));

                    DhuPublicParameters dhuParams = new DhuPublicParameters(lwDhKey,
                        new DhPublicKeyParameters(parameters.otherPartyEphemeralKey.getY(), lwDhKey.getParameters()));

                    byte[] zBytes = dh.calculateAgreement(dhuParams);

                    return FipsKDF.processZBytes(zBytes, parameters);
                }
            };
        }
    }

    /**
     * Factory for Agreement operators based on MQV
     */
    public static final class MQVAgreementFactory
        extends FipsAgreementFactory
    {
        /**
         * Return an Agreement operator based on MQV using Diffie-Hellman keys.
         *
         * @param key        the private key to initialize the Agreement with.
         * @param parameters the parameters for configuring the agreement.
         * @return a new Agreement operator for MQV.
         */
        @Override
        public FipsAgreement createAgreement(AsymmetricPrivateKey key, final MQVAgreementParameters parameters)
        {
            AsymmetricDHPrivateKey dhKey = (AsymmetricDHPrivateKey)key;
            DhPrivateKeyParameters lwDHKey = getLwKey(dhKey);

            final MqvBasicAgreement mqv = MQV_PROVIDER.createEngine();

            mqv.init(new MqvPrivateParameters(lwDHKey, parameters.ephemeralPrivateKey == null ? lwDHKey : getLwKey(parameters.ephemeralPrivateKey)));

            return new FipsAgreement()
            {
                @Override
                public MQVAgreementParameters getParameters()
                {
                    return parameters;
                }

                @Override
                public byte[] calculate(AsymmetricPublicKey key)
                {
                    AsymmetricDHPublicKey dhKey = (AsymmetricDHPublicKey)key;
                    DhPublicKeyParameters lwDhKey = new DhPublicKeyParameters(dhKey.getY(), getDomainParams(dhKey.getDomainParameters()));

                    int length = mqv.getFieldSize();
                    AsymmetricDHPublicKey ephPublicKey = parameters.getOtherPartyEphemeralKey();
                    BigInteger z = mqv.calculateAgreement(new MqvPublicParameters(lwDhKey, new DhPublicKeyParameters(ephPublicKey.getY(), getDomainParams(ephPublicKey.getDomainParameters()))));
                    byte[] zBytes = BigIntegers.asUnsignedByteArray(length, z);

                    return FipsKDF.processZBytes(zBytes, parameters);
                }
            };
        }
    }

    private static void validateKeyPair(FipsAlgorithm algorithm, AsymmetricCipherKeyPair keyPair)
    {
        Variations variation = (algorithm == ALGORITHM) ? Variations.DH : (Variations)algorithm.basicVariation();

        switch (variation)
        {
        case DH:
            SelfTestExecutor.validate(algorithm, keyPair, new ConsistencyTest()
            {
                public boolean hasTestPassed(AsymmetricCipherKeyPair kp)
                    throws Exception
                {
                    DhBasicAgreement agreement = new DhBasicAgreement();

                    agreement.init(kp.getPrivate());

                    BigInteger agree1 = agreement.calculateAgreement(kp.getPublic());

                    AsymmetricCipherKeyPair testKP = getTestKeyPair(kp);

                    agreement.init(testKP.getPrivate());

                    BigInteger agree2 = agreement.calculateAgreement(testKP.getPublic());

                    agreement.init(kp.getPrivate());

                    BigInteger agree3 = agreement.calculateAgreement(testKP.getPublic());

                    agreement.init(testKP.getPrivate());

                    BigInteger agree4 = agreement.calculateAgreement(kp.getPublic());

                    return !agree1.equals(agree2) && !agree1.equals(agree3) && agree3.equals(agree4);
                }
            });
            break;
        case MQV:
            SelfTestExecutor.validate(algorithm, keyPair, new ConsistencyTest()
            {
                public boolean hasTestPassed(AsymmetricCipherKeyPair kp)
                    throws Exception
                {
                    MqvBasicAgreement agreement = new MqvBasicAgreement();

                    agreement.init(new MqvPrivateParameters((DhPrivateKeyParameters)kp.getPrivate(), (DhPrivateKeyParameters)kp.getPrivate()));

                    BigInteger agree1 = agreement.calculateAgreement(new MqvPublicParameters((DhPublicKeyParameters)kp.getPublic(), (DhPublicKeyParameters)kp.getPublic()));

                    AsymmetricCipherKeyPair testSKP = getTestKeyPair(kp);
                    AsymmetricCipherKeyPair testEKP = getTestKeyPair(kp);

                    agreement.init(new MqvPrivateParameters((DhPrivateKeyParameters)kp.getPrivate(), (DhPrivateKeyParameters)kp.getPrivate()));

                    BigInteger agree2 = agreement.calculateAgreement(new MqvPublicParameters((DhPublicKeyParameters)testSKP.getPublic(), (DhPublicKeyParameters)testEKP.getPublic()));

                    agreement.init(new MqvPrivateParameters((DhPrivateKeyParameters)testSKP.getPrivate(), (DhPrivateKeyParameters)testEKP.getPrivate()));

                    BigInteger agree3 = agreement.calculateAgreement(new MqvPublicParameters((DhPublicKeyParameters)kp.getPublic(), (DhPublicKeyParameters)kp.getPublic()));

                    return !agree1.equals(agree2) && agree2.equals(agree3);
                }
            });
            break;
        case DHU:
            SelfTestExecutor.validate(algorithm, keyPair, new ConsistencyTest()
            {
                public boolean hasTestPassed(AsymmetricCipherKeyPair kp)
                    throws Exception
                {
                    DhuBasicAgreement agreement = new DhuBasicAgreement();

                    agreement.init(new DhuPrivateParameters((DhPrivateKeyParameters)kp.getPrivate(), (DhPrivateKeyParameters)kp.getPrivate()));

                    byte[] agree1 = agreement.calculateAgreement(new DhuPublicParameters((DhPublicKeyParameters)kp.getPublic(), (DhPublicKeyParameters)kp.getPublic()));

                    AsymmetricCipherKeyPair testSKP = getTestKeyPair(kp);
                    AsymmetricCipherKeyPair testEKP = getTestKeyPair(kp);

                    agreement.init(new DhuPrivateParameters((DhPrivateKeyParameters)kp.getPrivate(), (DhPrivateKeyParameters)kp.getPrivate()));

                    byte[] agree2 = agreement.calculateAgreement(new DhuPublicParameters((DhPublicKeyParameters)testSKP.getPublic(), (DhPublicKeyParameters)testEKP.getPublic()));

                    agreement.init(new DhuPrivateParameters((DhPrivateKeyParameters)testSKP.getPrivate(), (DhPrivateKeyParameters)testEKP.getPrivate()));

                    byte[] agree3 = agreement.calculateAgreement(new DhuPublicParameters((DhPublicKeyParameters)kp.getPublic(), (DhPublicKeyParameters)kp.getPublic()));

                    return !Arrays.areEqual(agree1, agree2) && Arrays.areEqual(agree2, agree3);
                }
            });
            break;
        default:
            throw new IllegalStateException("Unhandled DH algorithm: " + algorithm.getName());
        }
    }

    private static class AgreementProvider
        extends FipsEngineProvider
    {
        static final BigInteger expected = new BigInteger("b9fab69d21269e002d6b9aed81176320e597a74894dc0827ac7bab12579425b8fd8f067be4d5a2b77cdd018d267f574df6ba4abf22fa354935acaf9edfac9e382b339b1cadd65e43dd7fa842a1c15116dd48d38015232e1bc3447cf52a39997510aaed5bf7e598f43c1d955c50566edb334af270fc904f38ab2d82024fd86718fbe3cd3d397a49c6be00eec903432855ce755ad5661a3730c281d2b182aaa99b7b77607f8394016a3481ba09109932ce2c964312094a260e9b905aed2a63edf308f95822c9876b61c45648263e4f230fa9f9b49c7916abe698d0a77af04032075b3978423822b919bd46a1e892e5404778133128825958059c9606dfa3b93c6e", 16);

        public DhBasicAgreement createEngine()
        {
            return SelfTestExecutor.validate(ALGORITHM_DH, new DhBasicAgreement(), new VariantKatTest()
            {
                @Override
                void evaluate(DhBasicAgreement engine)
                    throws Exception
                {
                    AsymmetricCipherKeyPair kp = getKATKeyPair();

                    AsymmetricCipherKeyPair testOther = getTestKeyPair(kp);

                    engine.init(kp.getPrivate());

                    if (!expected.equals(engine.calculateAgreement(testOther.getPublic())))
                    {
                        fail("KAT DH agreement not verified");
                    }
                }
            });
        }
    }

    private static class MqvProvider
        extends FipsEngineProvider
    {
        static final BigInteger expected = new BigInteger("fb6ad631bae112b5d2a24fa6e821cd3b1f5e70c352b66b83d348a1e4c825bb3b58048f8b1551ee01880d0e513b55e62b2f5389946716561bdb922b86841c311ebe34debb24f0f21a09fa5e383787b36d7c1998b4d6aa9a895859258217c303fedabceb7fe8f1021330a1780e93aca20088394d15b98dfbf5ef3820678feaca7b5c58ebf2b72f0ed4cddd4d8c70ef3a6d34c88f18d4f7bcdca07d5d8194f9db00f7f900c9bf7d7c25cccc94dd94c4a7dc03c3b6fa77dcf2725d3f850da278a5d1d16cf99e2abedbb5ba444bd0bdf7d6decef0d1d1f72f17e255ec9a4edb1c5db833fb65e8b604b00bd542043ed2562c1e1f5cce36fbfe4ba400e61c32622d6dd7", 16);

        public MqvBasicAgreement createEngine()
        {
            return SelfTestExecutor.validate(ALGORITHM_MQV, new MqvBasicAgreement(), new VariantKatTest()
            {
                @Override
                void evaluate(MqvBasicAgreement engine)
                    throws Exception
                {
                    AsymmetricCipherKeyPair kp = getKATKeyPair();

                    AsymmetricCipherKeyPair testSKP = getTestKeyPair(kp);
                    AsymmetricCipherKeyPair testEKP = getTestKeyPair(kp);

                    engine.init(new MqvPrivateParameters((DhPrivateKeyParameters)kp.getPrivate(), (DhPrivateKeyParameters)kp.getPrivate()));

                    BigInteger calculated = engine.calculateAgreement(new MqvPublicParameters((DhPublicKeyParameters)testSKP.getPublic(), (DhPublicKeyParameters)testEKP.getPublic()));

                    if (!expected.equals(calculated))
                    {
                        fail("KAT DH MQV agreement not verified");
                    }
                }
            });
        }
    }

    private static class DhuProvider
        extends FipsEngineProvider
    {
        static final byte[] expected = Hex.decode("b9fab69d21269e002d6b9aed81176320e597a74894dc0827ac7bab12579425b8fd8f067be4d5a2b77cdd018d267f574df6ba4abf22fa354935acaf9edfac9e382b339b1cadd65e43dd7fa842a1c15116dd48d38015232e1bc3447cf52a39997510aaed5bf7e598f43c1d955c50566edb334af270fc904f38ab2d82024fd86718fbe3cd3d397a49c6be00eec903432855ce755ad5661a3730c281d2b182aaa99b7b77607f8394016a3481ba09109932ce2c964312094a260e9b905aed2a63edf308f95822c9876b61c45648263e4f230fa9f9b49c7916abe698d0a77af04032075b3978423822b919bd46a1e892e5404778133128825958059c9606dfa3b93c6eb9fab69d21269e002d6b9aed81176320e597a74894dc0827ac7bab12579425b8fd8f067be4d5a2b77cdd018d267f574df6ba4abf22fa354935acaf9edfac9e382b339b1cadd65e43dd7fa842a1c15116dd48d38015232e1bc3447cf52a39997510aaed5bf7e598f43c1d955c50566edb334af270fc904f38ab2d82024fd86718fbe3cd3d397a49c6be00eec903432855ce755ad5661a3730c281d2b182aaa99b7b77607f8394016a3481ba09109932ce2c964312094a260e9b905aed2a63edf308f95822c9876b61c45648263e4f230fa9f9b49c7916abe698d0a77af04032075b3978423822b919bd46a1e892e5404778133128825958059c9606dfa3b93c6e");

        public DhuBasicAgreement createEngine()
        {
            return SelfTestExecutor.validate(ALGORITHM_DHU, new DhuBasicAgreement(), new VariantKatTest()
            {
                @Override
                void evaluate(DhuBasicAgreement engine)
                    throws Exception
                {
                    AsymmetricCipherKeyPair kp = getKATKeyPair();

                    AsymmetricCipherKeyPair testSKP = getTestKeyPair(kp);
                    AsymmetricCipherKeyPair testEKP = getTestKeyPair(kp);

                    engine.init(new DhuPrivateParameters((DhPrivateKeyParameters)kp.getPrivate(), (DhPrivateKeyParameters)kp.getPrivate()));

                    byte[] calculated = engine.calculateAgreement(new DhuPublicParameters((DhPublicKeyParameters)testSKP.getPublic(), (DhPublicKeyParameters)testEKP.getPublic()));

                    if (!Arrays.areEqual(expected, calculated))
                    {
                        fail("KAT DH DHU agreement not verified");
                    }
                }
            });
        }
    }

    private static void ffPrimitiveZTest()
    {
        SelfTestExecutor.validate(ALGORITHM, new VariantInternalKatTest(ALGORITHM)
        {
            @Override
            void evaluate()
                throws Exception
            {
                AsymmetricCipherKeyPair kp = getKATKeyPair();

                DhPrivateKeyParameters priv = (DhPrivateKeyParameters)kp.getPrivate();
                DhPublicKeyParameters pub = (DhPublicKeyParameters)kp.getPublic();

                if (!pub.getY().equals(priv.getParameters().getG().modPow(priv.getX(), priv.getParameters().getP())))
                {
                    fail("FF primitive 'Z' computation failed");
                }
            }
        });
    }

    private static AsymmetricCipherKeyPair getKATKeyPair()
    {
        DHDomainParameters dhDp = DHDomainParametersIndex.lookupDomainParameters(DomainParameterID.ffdhe2048);

        DhParameters dhParameters = new DhParameters(dhDp.getP(), dhDp.getG(), dhDp.getQ());
        BigInteger x = new BigInteger("80d54802e42ce811d122ce2657c303013fc33c2f08f8ff1a9c4ebfd1", 16);
        BigInteger y = new BigInteger(
            "f9a4d8edb52efa7ffd00bc2e632b79c69eba8949f7ba23a6feb2d27278e96cbd7fe158484286c07f91144a268539eeffb306844898"
                + "c5efa845070489bcdc756c6858dcb242629f91b2714a33c0efebcb4b0832dba33b12db491dcded86f497094a52a3091a4bdf832d4f"
                + "36cb0cd7ab05e24b2adea4d746806d9776cebe45b0938c8a7f323db0497f865e8d992839ce018d54b68c5808a97fb035c83c304690"
                + "e6fff83dfd13be0186bdf0531cc416f9189fe87b1c92ce569578e9f55c874c0111a1e155f4dd876069424d38c94beb47f890d082eb"
                + "9183a7ce3c6819c420ca91ba969549835314df899fc766ac2acc9d6b9de5b0a9570ca4cfb6187e049fbe6f10", 16);
        return new AsymmetricCipherKeyPair(new DhPublicKeyParameters(y, dhParameters), new DhPrivateKeyParameters(x, dhParameters));
    }

    private static AsymmetricCipherKeyPair getTestKeyPair(AsymmetricCipherKeyPair kp)
    {
        DhPrivateKeyParameters privKey = (DhPrivateKeyParameters)kp.getPrivate();
        DhParameters dhParams = privKey.getParameters();

        BigInteger testD = privKey.getX().multiply(BigInteger.valueOf(7)).mod(privKey.getX());

        if (testD.compareTo(BigInteger.valueOf(2)) < 0)
        {
            testD = new BigInteger("0102030405060708090a0b0c0d0e0f101112131415161718", 16);
        }

        DhPrivateKeyParameters testPriv = new DhPrivateKeyParameters(testD, dhParams);
        DhPublicKeyParameters testPub = new DhPublicKeyParameters(dhParams.getG().modPow(testD, dhParams.getP()), dhParams);

        return new AsymmetricCipherKeyPair(testPub, testPriv);
    }

    private static DhParameters getDomainParams(DHDomainParameters dhParameters)
    {
        return new DhParameters(dhParameters.getP(), dhParameters.getG(), dhParameters.getQ(), dhParameters.getM(), dhParameters.getL(), dhParameters.getJ());
    }

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




© 2015 - 2025 Weber Informatics LLC | Privacy Policy