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

org.bouncycastle.crypto.fips.FipsEC 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.asn1.nist.NISTNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.Algorithm;
import org.bouncycastle.crypto.AsymmetricPrivateKey;
import org.bouncycastle.crypto.AsymmetricPublicKey;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.asymmetric.AsymmetricECPrivateKey;
import org.bouncycastle.crypto.asymmetric.AsymmetricECPublicKey;
import org.bouncycastle.crypto.asymmetric.AsymmetricKeyPair;
import org.bouncycastle.crypto.asymmetric.ECDomainParameters;
import org.bouncycastle.crypto.asymmetric.ECDomainParametersID;
import org.bouncycastle.crypto.asymmetric.NamedECDomainParameters;
import org.bouncycastle.crypto.internal.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.internal.Digest;
import org.bouncycastle.crypto.internal.Permissions;
import org.bouncycastle.crypto.internal.params.EcDhuPrivateParameters;
import org.bouncycastle.crypto.internal.params.EcDhuPublicParameters;
import org.bouncycastle.crypto.internal.params.EcDomainParameters;
import org.bouncycastle.crypto.internal.params.EcMqvPrivateParameters;
import org.bouncycastle.crypto.internal.params.EcMqvPublicParameters;
import org.bouncycastle.crypto.internal.params.EcNamedDomainParameters;
import org.bouncycastle.crypto.internal.params.EcPrivateKeyParameters;
import org.bouncycastle.crypto.internal.params.EcPublicKeyParameters;
import org.bouncycastle.crypto.internal.params.ParametersWithRandom;
import org.bouncycastle.crypto.internal.test.ConsistencyTest;
import org.bouncycastle.math.ec.ECConstants;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Properties;
import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.util.test.TestRandomBigInteger;

/**
 * Source class for FIPS approved implementations of Elliptic Curve algorithms.
 */
public final class FipsEC
{
    private static final int MIN_FIPS_FIELD_SIZE = 224;       // 112 bits of security

    private static final BigInteger TEST_D_OFFSET = new BigInteger("deadbeef", 16);  // offset for generating test key pairs.

    private enum Variations
    {
        ECDSA,
        ECDH,
        ECCDH,
        ECMQV,
        ECCDHU
    }

    /**
     * Basic Elliptic Curve key marker, can be used for creating general purpose Elliptic Curve keys.
     */
    public static final FipsAlgorithm ALGORITHM = new FipsAlgorithm("EC");

    private static final FipsAlgorithm ALGORITHM_MQV = new FipsAlgorithm("ECMQV", Variations.ECMQV);

    private static final FipsAlgorithm ALGORITHM_DHU = new FipsAlgorithm("ECCDHU", Variations.ECCDHU);

    /**
     * Elliptic Curve DSA algorithm parameter source - default is SHA-1
     */
    public static final DSAParameters DSA = new DSAParameters(new FipsAlgorithm("ECDSA", Variations.ECDSA), FipsSHS.Algorithm.SHA1);
    /**
     * Elliptic Curve Diffie-Hellman algorithm parameter source.
     */
    public static final AgreementParameters DH = new AgreementParameters(new FipsAlgorithm("ECDH", Variations.ECDH));
    /**
     * Elliptic Curve cofactor Diffie-Hellman algorithm parameter source.
     */
    public static final AgreementParameters CDH = new AgreementParameters(new FipsAlgorithm("ECCDH", Variations.ECCDH));
    /**
     * Elliptic Curve MQV algorithm parameter source.
     */
    public static final MQVAgreementParametersBuilder MQV = new MQVAgreementParametersBuilder();
    /**
     * Elliptic Curve cofactor Diffie-Hellman Unified algorithm parameter source.
     */
    public static final DHUAgreementParametersBuilder CDHU = new DHUAgreementParametersBuilder();


    private static final FipsEngineProvider DSA_PROVIDER;
    private static final FipsEngineProvider DH_PROVIDER;
    private static final FipsEngineProvider CDH_PROVIDER;
    private static final FipsEngineProvider MQV_PROVIDER;
    private static final FipsEngineProvider DHU_PROVIDER;

    static
    {
        DSA_PROVIDER = new DsaProvider();
        DH_PROVIDER = new DhProvider();
        CDH_PROVIDER = new DhcProvider();
        MQV_PROVIDER = new MqvProvider();
        DHU_PROVIDER = new DhuProvider();

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

        // FSM_STATE:3.EC.1,"EC CVL Primitive 'Z' computation KAT", "The module is performing EC CVL Primitive 'Z' computation KAT verify KAT self-test"
        // FSM_TRANS:3.EC.2,"POWER ON SELF-TEST", "EC CVL Primitive 'Z' computation KAT", "Invoke EC CVL Primitive 'Z' computation KAT self-test"
        ecPrimitiveZTest();
        // FSM_TRANS:3.EC.3,"EC CVL Primitive 'Z' computation KAT", "POWER ON SELF-TEST", "EC CVL Primitive 'Z' computation KAT self-test successful completion"
    }

    /**
     * An enumeration of ECDomainParametersID for the NIST defined EC domain parameters.
     */
    public enum DomainParameterID
        implements ECDomainParametersID
    {
        B571("B-571"),
        B409("B-409"),
        B283("B-283"),
        B233("B-233"),
        B163("B-163"),
        K571("K-571"),
        K409("K-409"),
        K283("K-283"),
        K233("K-233"),
        K163("K-163"),
        P521("P-521"),
        P384("P-384"),
        P256("P-256"),
        P224("P-224"),
        P192("P-192");

        private final String curveName;

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

        public String getCurveName()
        {
            return curveName;
        }
    }

    private FipsEC()
    {

    }

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

        /**
         * Constructor for the default algorithm ID.
         *
         * @param domainParameters EC domain parameters representing the curve any generated keys will be for.
         */
        public KeyGenParameters(ECDomainParameters domainParameters)
        {
            this(ALGORITHM, domainParameters);
        }

        /**
         * Constructor for specifying the DSA algorithm explicitly.
         *
         * @param parameters the particular parameter set to generate keys for.
         * @param domainParameters EC domain parameters representing the curve any generated keys will be for.
         */
        public KeyGenParameters(DSAParameters parameters, ECDomainParameters domainParameters)
        {
            this(parameters.getAlgorithm(), domainParameters);
        }

        /**
         * Constructor for specifying an Agreement algorithm explicitly.
         *
         * @param parameters the particular parameter set to generate keys for.
         * @param domainParameters EC domain parameters representing the curve any generated keys will be for.
         */
        public KeyGenParameters(AgreementParameters parameters, ECDomainParameters domainParameters)
        {
            this(parameters.getAlgorithm(), domainParameters);
        }

        /**
         * Constructor for specifying the MQV algorithm explicitly.
         *
         * @param builder the MQV builder.
         * @param domainParameters EC domain parameters representing the curve any generated keys will be for.
         */
        public KeyGenParameters(MQVAgreementParametersBuilder builder, ECDomainParameters domainParameters)
        {
            this(ALGORITHM_MQV, domainParameters);
        }

        /**
         * Constructor for specifying the CDHU algorithm explicitly.
         *
         * @param builder the CDHU builder.
         * @param domainParameters EC domain parameters representing the curve any generated keys will be for.
         */
        public KeyGenParameters(DHUAgreementParametersBuilder builder, ECDomainParameters domainParameters)
        {
            this(ALGORITHM_DHU, domainParameters);
        }

        /**
         * Base constructor for specifying an algorithm ID.
         *
         * @param algorithm the particular algorithm generated keys are for.
         * @param domainParameters EC domain parameters representing the curve any generated keys will be for.
         */
        KeyGenParameters(FipsAlgorithm algorithm, ECDomainParameters domainParameters)
        {
            super(algorithm);
            this.domainParameters = domainParameters;
        }

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

    /**
     * Parameters for EC key agreement.
     */
    public static final class AgreementParameters
        extends FipsAgreementParameters
    {
        /**
         * Default constructor which specifies returning the raw secret on agreement calculation.
         *
         * @param agreementAlgorithm the agreement algorithm (DH or CDH).
         */
        AgreementParameters(FipsAlgorithm agreementAlgorithm)
        {
            this(agreementAlgorithm, null);
        }

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

        private AgreementParameters(FipsAlgorithm agreementAlgorithm, FipsKDF.PRF prfAlgorithm, byte[] salt)
        {
            super(agreementAlgorithm, 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(FipsDigestAlgorithm digestAlgorithm)
        {
            return new AgreementParameters(this.getAlgorithm(), 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(this.getAlgorithm(), 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);
        }
    }

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

        /**
         * Constructor specifying a digest for signature calculation..
         *
         * @param digestAlgorithm the algorithm ID of the digest to use.
         */
        DSAParameters(FipsAlgorithm algorithm, FipsDigestAlgorithm digestAlgorithm)
        {
            super(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 DSAParameters withDigestAlgorithm(FipsDigestAlgorithm digestAlgorithm)
        {
            return new DSAParameters(getAlgorithm(), digestAlgorithm);
        }
    }

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

        /**
         * Constructor for EC 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, AsymmetricECPublicKey otherPartyEphemeralKey)
        {
            return new MQVAgreementParameters((AsymmetricECPublicKey)ephemeralKeyPair.getPublicKey(), (AsymmetricECPrivateKey)ephemeralKeyPair.getPrivateKey(), otherPartyEphemeralKey, null);
        }

        /**
         * Constructor for EC 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(AsymmetricECPrivateKey ephemeralPrivateKey, AsymmetricECPublicKey otherPartyEphemeralKey)
        {
            return new MQVAgreementParameters(null, ephemeralPrivateKey, otherPartyEphemeralKey, null);
        }

        /**
         * Constructor for EC 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(AsymmetricECPublicKey ephemeralPublicKey, AsymmetricECPrivateKey ephemeralPrivateKey, AsymmetricECPublicKey otherPartyEphemeralKey)
        {
            return new MQVAgreementParameters(ephemeralPublicKey, ephemeralPrivateKey, otherPartyEphemeralKey, null);
        }
    }

    /**
     * Parameters for EC MQV key agreement.
     */
    public static final class MQVAgreementParameters
        extends FipsAgreementParameters
    {
        private final AsymmetricECPublicKey ephemeralPublicKey;
        private final AsymmetricECPrivateKey ephemeralPrivateKey;
        private final AsymmetricECPublicKey otherPartyEphemeralKey;

        private MQVAgreementParameters(AsymmetricECPublicKey ephemeralPublicKey, AsymmetricECPrivateKey ephemeralPrivateKey, AsymmetricECPublicKey otherPartyEphemeralKey, FipsAlgorithm digestAlgorithm)
        {
            super(ALGORITHM_MQV, digestAlgorithm);

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

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

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

        private MQVAgreementParameters(AsymmetricECPublicKey ephemeralPublicKey, AsymmetricECPrivateKey ephemeralPrivateKey, AsymmetricECPublicKey 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 AsymmetricECPublicKey getEphemeralPublicKey()
        {
            return ephemeralPublicKey;
        }

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

        /**
         * Return the other party's ephemeral public key.
         *
         * @return the other party's ephemeral public key.
         */
        public AsymmetricECPublicKey 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 builder 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 EC 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, AsymmetricECPublicKey otherPartyEphemeralKey)
        {
            return new DHUAgreementParameters((AsymmetricECPublicKey)ephemeralKeyPair.getPublicKey(), (AsymmetricECPrivateKey)ephemeralKeyPair.getPrivateKey(), otherPartyEphemeralKey, null);
        }

        /**
         * Constructor for EC 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(AsymmetricECPrivateKey ephemeralPrivateKey, AsymmetricECPublicKey otherPartyEphemeralKey)
        {
            return new DHUAgreementParameters(null, ephemeralPrivateKey, otherPartyEphemeralKey, null);
        }

        /**
         * Constructor for EC 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(AsymmetricECPublicKey ephemeralPublicKey, AsymmetricECPrivateKey ephemeralPrivateKey, AsymmetricECPublicKey otherPartyEphemeralKey)
        {
            return new DHUAgreementParameters(ephemeralPublicKey, ephemeralPrivateKey, otherPartyEphemeralKey, null);
        }
    }

    /**
     * Parameters for EC DHU key agreement.
     */
    public static final class DHUAgreementParameters
        extends FipsAgreementParameters
    {
        private final AsymmetricECPublicKey ephemeralPublicKey;
        private final AsymmetricECPrivateKey ephemeralPrivateKey;
        private final AsymmetricECPublicKey otherPartyEphemeralKey;

        private DHUAgreementParameters(AsymmetricECPublicKey ephemeralPublicKey, AsymmetricECPrivateKey ephemeralPrivateKey, AsymmetricECPublicKey otherPartyEphemeralKey, FipsAlgorithm digestAlgorithm)
        {
            super(ALGORITHM_DHU, digestAlgorithm);

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

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

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

        private DHUAgreementParameters(AsymmetricECPublicKey ephemeralPublicKey, AsymmetricECPrivateKey ephemeralPrivateKey, AsymmetricECPublicKey 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 AsymmetricECPublicKey getEphemeralPublicKey()
        {
            return ephemeralPublicKey;
        }

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

        /**
         * Return the other party's ephemeral public key.
         *
         * @return the other party's ephemeral public key.
         */
        public AsymmetricECPublicKey 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 builder 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);
        }
    }
    
    /**
     * EC key pair generator class.
     */
    public static final class KeyPairGenerator
        extends FipsAsymmetricKeyPairGenerator
    {
        private final EcKeyPairGenerator engine = new EcKeyPairGenerator();
        private final ECDomainParameters domainParameters;
        private final EcKeyGenerationParameters param;

        /**
         * Construct a key pair generator for EC 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);

            checkEnabled();

            if (CryptoServicesRegistrar.isInApprovedOnlyMode())
            {
                validateCurveSize(keyGenParameters.getAlgorithm(), keyGenParameters.getDomainParameters());

                Utils.validateKeyPairGenRandom(random, Utils.getECCurveSecurityStrength(keyGenParameters.getDomainParameters().getCurve()), ALGORITHM);
            }

            if (this.getParameters().getAlgorithm().equals(FipsEC.DH.getAlgorithm()) && !ECConstants.ONE.equals(keyGenParameters.domainParameters.getH()))
            {
                this.param = new EcKeyGenerationParameters(getDomainParamsWithInv(keyGenParameters.getDomainParameters()), random);
            }
            else
            {
                this.param = new EcKeyGenerationParameters(getDomainParams(keyGenParameters.getDomainParameters()), random);
            }

            this.domainParameters = keyGenParameters.getDomainParameters();
            this.engine.init(param);
        }

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

            EcPublicKeyParameters pubKey = (EcPublicKeyParameters)kp.getPublic();
            EcPrivateKeyParameters prvKey = (EcPrivateKeyParameters)kp.getPrivate();

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

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

            return new AsymmetricKeyPair(new AsymmetricECPublicKey(algorithm, domainParameters, pubKey.getQ()), new AsymmetricECPrivateKey(algorithm, domainParameters, prvKey.getD(), pubKey.getQ()));
        }
    }

    /**
     * Factory for Agreement operators based on EC Diffie-Hellman and Cofactor Diffie-Hellman.
     */
    public static final class DHAgreementFactory
        extends FipsAgreementFactory
    {
        public DHAgreementFactory()
        {
            checkEnabled();
        }

        /**
         * Return an Agreement operator based on Diffie-Hellman using EC 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 EC DH.
         */
        @Override
        public FipsAgreement createAgreement(AsymmetricPrivateKey key, AgreementParameters parameters)
        {
            if (parameters.getAlgorithm() == FipsEC.DH.getAlgorithm())
            {
                AsymmetricECPrivateKey ecKey = (AsymmetricECPrivateKey)key;

                if (CryptoServicesRegistrar.isInApprovedOnlyMode())
                {
                    // only curves with a co-factor of 1 meet SP800-56A r2
                    if (!BigInteger.ONE.equals(ecKey.getDomainParameters().getH()))
                    {
                        throw new FipsUnapprovedOperationError("ECDH can only be executed on curves with a co-factor of 1 in approved mode", key.getAlgorithm());
                    }
                    validateCurveSize(key.getAlgorithm(), ecKey.getDomainParameters());
                }

                EcPrivateKeyParameters lwECKey;
                if (!BigInteger.ONE.equals(ecKey.getDomainParameters().getH()))
                {
                    // pre-calculate HInv for co-factor clearing.
                    lwECKey = getLwKeyWithInv(ecKey);
                }
                else
                {
                    lwECKey = getLwKey(ecKey);
                }
                
                EcDhBasicAgreement ecdh = DH_PROVIDER.createEngine();

                ecdh.init(lwECKey);

                return new EcDHAgreement(ecdh, parameters);
            }
            else if (parameters.getAlgorithm() == FipsEC.CDH.getAlgorithm())
            {
                AsymmetricECPrivateKey ecKey = (AsymmetricECPrivateKey)key;

                if (CryptoServicesRegistrar.isInApprovedOnlyMode())
                {
                    validateCurveSize(key.getAlgorithm(), ecKey.getDomainParameters());
                }

                EcPrivateKeyParameters lwECKey = getLwKey(ecKey);

                EcDhcBasicAgreement ecdh = CDH_PROVIDER.createEngine();

                ecdh.init(lwECKey);

                return new EcDHAgreement(ecdh, parameters);
            }
            else
            {
                throw new IllegalArgumentException("Incorrect algorithm in parameters for EC DH: " + parameters.getAlgorithm().getName());
            }
        }
    }

    /**
     * Factory for Agreement operators based on EC MQV
     */
    public static final class MQVAgreementFactory
        extends FipsAgreementFactory
    {
        public MQVAgreementFactory()
        {
            checkEnabled();
            if (Properties.isOverrideSet("org.bouncycastle.ec.disable_mqv"))
            {
                throw new UnsupportedOperationException("EC MQV has been disabled by setting \"org.bouncycastle.ec.disable_mqv\"");
            }
        }

        /**
         * Return an Agreement operator based on MQV using EC 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 EC MQV.
         */
        @Override
        public FipsAgreement createAgreement(AsymmetricPrivateKey key, MQVAgreementParameters parameters)
        {
            AsymmetricECPrivateKey ecKey = (AsymmetricECPrivateKey)key;

            if (CryptoServicesRegistrar.isInApprovedOnlyMode())
            {
                validateCurveSize(key.getAlgorithm(), ecKey.getDomainParameters());
            }

            EcPrivateKeyParameters lwECKey = getLwKey(ecKey);

            EcMqvBasicAgreement ecdh = MQV_PROVIDER.createEngine();

            ecdh.init(new EcMqvPrivateParameters(lwECKey, parameters.ephemeralPrivateKey == null ? lwECKey : getLwKey(parameters.ephemeralPrivateKey)));

            return new EcDHAgreement(ecdh, parameters);
        }
    }

    /**
     * Factory for Agreement operators based on EC MQV
     */
    public static final class DHUAgreementFactory
        extends FipsAgreementFactory
    {
        public DHUAgreementFactory()
        {
            checkEnabled();
        }

        /**
         * Return an Agreement operator based on Diffie-Hellman Unified using EC 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 EC Diffie-Hellman Unified.
         */
        @Override
        public FipsAgreement createAgreement(AsymmetricPrivateKey key, DHUAgreementParameters parameters)
        {
            AsymmetricECPrivateKey ecKey = (AsymmetricECPrivateKey)key;

            if (CryptoServicesRegistrar.isInApprovedOnlyMode())
            {
                validateCurveSize(key.getAlgorithm(), ecKey.getDomainParameters());
            }

            EcPrivateKeyParameters lwECKey = getLwKey(ecKey);

            EcDhcuBasicAgreement ecdh = DHU_PROVIDER.createEngine();

            ecdh.init(new EcDhuPrivateParameters(lwECKey, parameters.ephemeralPrivateKey == null ? lwECKey : getLwKey(parameters.ephemeralPrivateKey)));

            return new EcDHUAgreement(ecdh, parameters);
        }
    }

    /**
     * Operator factory for creating EC DSA based signing and verification operators.
     */
    public static final class DSAOperatorFactory
        extends FipsSignatureOperatorFactory
    {
        public DSAOperatorFactory()
        {
            checkEnabled();
        }

        /**
         * Return a generator of EC 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 DSAParameters parameters)
        {
            AsymmetricECPrivateKey k = (AsymmetricECPrivateKey)key;

            if (CryptoServicesRegistrar.isInApprovedOnlyMode())
            {
                validateCurveSize(key.getAlgorithm(), k.getDomainParameters());
            }

            EcDsaSigner ecdsaSigner = DSA_PROVIDER.createEngine();
            Digest digest = (parameters.digestAlgorithm != null) ? FipsSHS.createDigest(parameters.digestAlgorithm) : new NullDigest();

            final EcPrivateKeyParameters privateKeyParameters = getLwKey(k);

            return new DSAOutputSigner(ecdsaSigner, 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 EC 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 DSAParameters parameters)
        {
            EcDsaSigner ecdsaSigner = DSA_PROVIDER.createEngine();
            Digest digest = (parameters.digestAlgorithm != null) ? FipsSHS.createDigest(parameters.digestAlgorithm) : new NullDigest();

            AsymmetricECPublicKey k = (AsymmetricECPublicKey)key;

            EcPublicKeyParameters publicKeyParameters = new EcPublicKeyParameters(k.getW(), getDomainParams(k.getDomainParameters()));

            ecdsaSigner.init(false, publicKeyParameters);

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

    private static void checkEnabled()
    {
        if (Properties.isOverrideSet("org.bouncycastle.ec.disable"))
        {
            throw new UnsupportedOperationException("EC has been disabled by setting \"org.bouncycastle.ec.disable\"");
        }
    }

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

        switch (variation)
        {
        case ECDSA:
            SelfTestExecutor.validate(algorithm, kp, new ConsistencyTest()
            {
                public boolean hasTestPassed(AsymmetricCipherKeyPair kp)
                    throws Exception
                {
                    EcDsaSigner signer = new EcDsaSigner();

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

                    byte[] message = new byte[32]; // size of a SHA-256 hash
                    message[1] = 1;
                    BigInteger[] rs = signer.generateSignature(message);

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

                    return signer.verifySignature(message, rs[0], rs[1]);
                }
            });
            break;
        case ECDH:
            SelfTestExecutor.validate(algorithm, kp, new ConsistencyTest()
            {
                public boolean hasTestPassed(AsymmetricCipherKeyPair kp)
                    throws Exception
                {
                    EcDhBasicAgreement agreement = new EcDhBasicAgreement();

                    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 ECCDH:
            SelfTestExecutor.validate(algorithm, kp, new ConsistencyTest()
            {
                public boolean hasTestPassed(AsymmetricCipherKeyPair kp)
                    throws Exception
                {
                    EcDhcBasicAgreement agreement = new EcDhcBasicAgreement();

                    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 ECMQV:
            SelfTestExecutor.validate(algorithm, kp, new ConsistencyTest()
             {
                 public boolean hasTestPassed(AsymmetricCipherKeyPair kp)
                     throws Exception
                 {
                     EcMqvBasicAgreement agreement = new EcMqvBasicAgreement();

                     agreement.init(new EcMqvPrivateParameters((EcPrivateKeyParameters)kp.getPrivate(), (EcPrivateKeyParameters)kp.getPrivate()));

                     BigInteger agree1 = agreement.calculateAgreement(new EcMqvPublicParameters((EcPublicKeyParameters)kp.getPublic(), (EcPublicKeyParameters)kp.getPublic()));

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

                     agreement.init(new EcMqvPrivateParameters((EcPrivateKeyParameters)kp.getPrivate(), (EcPrivateKeyParameters)kp.getPrivate()));

                     BigInteger agree2 = agreement.calculateAgreement(new EcMqvPublicParameters((EcPublicKeyParameters)testSKP.getPublic(), (EcPublicKeyParameters)testEKP.getPublic()));

                     agreement.init(new EcMqvPrivateParameters((EcPrivateKeyParameters)testSKP.getPrivate(), (EcPrivateKeyParameters)testEKP.getPrivate()));

                     BigInteger agree3 = agreement.calculateAgreement(new EcMqvPublicParameters((EcPublicKeyParameters)kp.getPublic(), (EcPublicKeyParameters)kp.getPublic()));

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

                     agreement.init(new EcDhuPrivateParameters((EcPrivateKeyParameters)kp.getPrivate(), (EcPrivateKeyParameters)kp.getPrivate()));

                     byte[] agree1 = agreement.calculateAgreement(new EcDhuPublicParameters((EcPublicKeyParameters)kp.getPublic(), (EcPublicKeyParameters)kp.getPublic()));

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

                     agreement.init(new EcDhuPrivateParameters((EcPrivateKeyParameters)kp.getPrivate(), (EcPrivateKeyParameters)kp.getPrivate()));

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

                     agreement.init(new EcDhuPrivateParameters((EcPrivateKeyParameters)testSKP.getPrivate(), (EcPrivateKeyParameters)testEKP.getPrivate()));

                     byte[] agree3 = agreement.calculateAgreement(new EcDhuPublicParameters((EcPublicKeyParameters)kp.getPublic(), (EcPublicKeyParameters)kp.getPublic()));

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

    private static AsymmetricCipherKeyPair getKATKeyPair()
    {
        X9ECParameters p = NISTNamedCurves.getByName("P-256");
        EcDomainParameters params = new EcDomainParameters(new ECDomainParameters(p.getCurve(), p.getG(), p.getN(), p.getH(), p.getSeed()));
        EcPrivateKeyParameters priKey = new EcPrivateKeyParameters(
            new BigInteger("20186677036482506117540275567393538695075300175221296989956723148347484984008"), // d
            params);

        // Verify the signature
        EcPublicKeyParameters pubKey = new EcPublicKeyParameters(
            params.getCurve().decodePoint(Hex.decode("03596375E6CE57E0F20294FC46BDFCFD19A39F8161B58695B3EC5B3D16427C274D")), // Q
            params);

        return new AsymmetricCipherKeyPair(pubKey, priKey);
    }

    private static class DsaProvider
        extends FipsEngineProvider
    {
        public EcDsaSigner 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 EcDsaSigner(), new VariantKatTest()
            {
                void evaluate(EcDsaSigner dsa)
                    throws Exception
                {
                    AsymmetricCipherKeyPair kp = getKATKeyPair();

                    SecureRandom k = new TestRandomBigInteger("72546832179840998877302529996971396893172522460793442785601695562409154906335");

                    byte[] M = Hex.decode("1BD4ED430B0F384B4E8D458EFF1A8A553286D7AC21CB2F6806172EF5F94A06AD");

                    dsa.init(true, new ParametersWithRandom(kp.getPrivate(), k));

                    BigInteger[] sig = dsa.generateSignature(M);

                    dsa.init(false, kp.getPublic());
                    if (!dsa.verifySignature(M, sig[0], sig[1]))
                    {
                        fail("signature fails");
                    }
                }
            });
        }
    }

    private static class DhProvider
        extends FipsEngineProvider
    {
        static BigInteger expected = new BigInteger("cad5c428ea0645794bc5634549e08a3ed563bd0cf32e909862e08b41d4b6fc17", 16);

        public EcDhBasicAgreement createEngine()
        {
            return SelfTestExecutor.validate(ALGORITHM, new EcDhBasicAgreement(), new VariantKatTest()
            {
                void evaluate(EcDhBasicAgreement agreement)
                    throws Exception
                {
                    AsymmetricCipherKeyPair kp = getKATKeyPair();

                    AsymmetricCipherKeyPair testOther = getTestKeyPair(kp);

                    agreement.init(kp.getPrivate());

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

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

        public EcDhcBasicAgreement createEngine()
        {
            return SelfTestExecutor.validate(ALGORITHM, new EcDhcBasicAgreement(), new VariantKatTest()
            {
                void evaluate(EcDhcBasicAgreement agreement)
                    throws Exception
                {
                    AsymmetricCipherKeyPair kp = getKATKeyPair();

                    AsymmetricCipherKeyPair testOther = getTestKeyPair(kp);

                    agreement.init(kp.getPrivate());

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

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

        public EcMqvBasicAgreement createEngine()
        {
            return SelfTestExecutor.validate(ALGORITHM, new EcMqvBasicAgreement(), new VariantKatTest()
            {
                void evaluate(EcMqvBasicAgreement agreement)
                    throws Exception
                {
                    AsymmetricCipherKeyPair kp = getKATKeyPair();

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

                    agreement.init(new EcMqvPrivateParameters((EcPrivateKeyParameters)kp.getPrivate(), (EcPrivateKeyParameters)kp.getPrivate()));

                    BigInteger calculated = agreement.calculateAgreement(new EcMqvPublicParameters((EcPublicKeyParameters)testSKP.getPublic(), (EcPublicKeyParameters)testEKP.getPublic()));

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

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

        public EcDhcuBasicAgreement createEngine()
        {
            return SelfTestExecutor.validate(ALGORITHM, new EcDhcuBasicAgreement(), new VariantKatTest()
            {
                void evaluate(EcDhcuBasicAgreement agreement)
                    throws Exception
                {
                    AsymmetricCipherKeyPair kp = getKATKeyPair();

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

                    agreement.init(new EcDhuPrivateParameters((EcPrivateKeyParameters)kp.getPrivate(), (EcPrivateKeyParameters)kp.getPrivate()));

                    byte[] calculated = agreement.calculateAgreement(new EcDhuPublicParameters((EcPublicKeyParameters)testSKP.getPublic(), (EcPublicKeyParameters)testEKP.getPublic()));


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

    private static void ecPrimitiveZTest()
    {
        SelfTestExecutor.validate(ALGORITHM, new VariantInternalKatTest(ALGORITHM)
        {
            @Override
            void evaluate()
                throws Exception
            {
                X9ECParameters p = NISTNamedCurves.getByName("P-256");
                ECDomainParameters params = new ECDomainParameters(p.getCurve(), p.getG(), p.getN(), p.getH(), p.getSeed());
                BigInteger dValue = new BigInteger("20186677036482506117540275567393538695075300175221296989956723148347484984008");

                ECPoint Q = params.getCurve().decodePoint(Hex.decode("03596375E6CE57E0F20294FC46BDFCFD19A39F8161B58695B3EC5B3D16427C274D"));

                if (!Q.equals(params.getG().multiply(dValue)))
                {
                    fail("EC primitive 'Z' computation failed");
                }
            }
        });
    }

    private static AsymmetricCipherKeyPair getTestKeyPair(AsymmetricCipherKeyPair kp)
    {
        EcPrivateKeyParameters privKey = (EcPrivateKeyParameters)kp.getPrivate();
        EcDomainParameters ecDomainParameters = privKey.getParameters();

        BigInteger testD = privKey.getD().add(TEST_D_OFFSET).mod(ecDomainParameters.getN());

        if (testD.compareTo(ECConstants.TWO) < 0)
        {
             testD = testD.add(TEST_D_OFFSET);
        }

        EcPrivateKeyParameters testPriv = new EcPrivateKeyParameters(testD, ecDomainParameters);
        EcPublicKeyParameters testPub = new EcPublicKeyParameters(ecDomainParameters.getG().multiply(testD), ecDomainParameters);

        return new AsymmetricCipherKeyPair(testPub, testPriv);
    }

    private static void validateCurveSize(Algorithm algorithm, ECDomainParameters domainParameters)
    {
        // curve size needs to offer 112 bits of security.
        if (domainParameters.getCurve().getFieldSize() < MIN_FIPS_FIELD_SIZE)
        {
            throw new FipsUnapprovedOperationError("Attempt to use curve with field size less than " + MIN_FIPS_FIELD_SIZE + " bits", algorithm);
        }
    }

    private static EcDomainParameters getDomainParams(ECDomainParameters curveParams)
    {
        if (curveParams instanceof NamedECDomainParameters)
        {
            return new EcNamedDomainParameters((NamedECDomainParameters)curveParams);
        }
        return new EcDomainParameters(curveParams);
    }

    private static EcPrivateKeyParameters getLwKey(final AsymmetricECPrivateKey privKey)
    {
        return AccessController.doPrivileged(new PrivilegedAction()
        {
            public EcPrivateKeyParameters run()
            {
                return new EcPrivateKeyParameters(privKey.getS(), getDomainParams(privKey.getDomainParameters()));
            }
        });
    }

    private static EcDomainParameters getDomainParamsWithInv(ECDomainParameters curveParams)
    {
        if (curveParams instanceof NamedECDomainParameters)
        {
            return new EcNamedDomainParameters((NamedECDomainParameters)curveParams, curveParams.getInverseH());
        }
        return new EcDomainParameters(curveParams, curveParams.getInverseH());
    }

    private static EcPrivateKeyParameters getLwKeyWithInv(final AsymmetricECPrivateKey privKey)
    {
        return AccessController.doPrivileged(new PrivilegedAction()
        {
            public EcPrivateKeyParameters run()
            {
                return new EcPrivateKeyParameters(privKey.getS(), getDomainParamsWithInv(privKey.getDomainParameters()));
            }
        });
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy