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-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 java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.nist.NISTNamedCurves;
import org.bouncycastle.asn1.sec.SECObjectIdentifiers;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
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.InvalidSignatureException;
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.ECDomainParametersIndex;
import org.bouncycastle.crypto.asymmetric.NamedECDomainParameters;
import org.bouncycastle.crypto.internal.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.internal.Digest;
import org.bouncycastle.crypto.internal.ExtendedDigest;
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.BigIntegers;
import org.bouncycastle.util.Properties;
import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.util.test.TestRandomBigInteger;
import org.bouncycastle.util.test.TestRandomData;

/**
 * Source class for FIPS approved implementations of Elliptic Curve algorithms.
 */
public final class FipsEC
{
    private static final Logger LOG = Logger.getLogger(FipsEC.class.getName());

    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 static final AsymmetricCipherKeyPair katKeyPair = getKATKeyPair();
    private static final AsymmetricCipherKeyPair f2mKatKeyPair = getF2mKATKeyPair();

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

    /**
     * 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 Deterministic DSA algorithm parameter source - default is SHA-1
     */
    public static final DSAParameters DDSA = new DSAParameters(new FipsAlgorithm(ALGORITHM.getName(), FipsEC.Variations.ECDDSA), 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 DsaProvider 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:5.EC.0,"EC CVL Primitive 'Z' Gf(p) computation KAT", "The module is performing EC CVL Primitive 'Z' Gf(p) computation KAT verify KAT self-test"
        // FSM_TRANS:5.EC.0.1,"CONDITIONAL TEST", "EC CVL Primitive 'Z' Gf(p) computation KAT", "Invoke EC CVL Primitive 'Z' Gf(p) computation KAT self-test"
        ecPrimitiveZTest();
        // FSM_TRANS:5.EC.0.2,"EC CVL Primitive 'Z' Gf(p) computation KAT", "CONDITIONAL TEST", "EC CVL Primitive 'Z' Gf(p) computation KAT self-test successful completion"
        // FSM_TRANS:5.EC.0.2,"EC CVL Primitive 'Z' Gf(p) computation KAT", "SOFT ERROR", "EC CVL Primitive 'Z' Gf(p) computation KAT self-test failed"

        // FSM_STATE:5.EC.1,"EC CVL Primitive 'Z' Gf(2m) computation KAT", "The module is performing EC CVL Primitive 'Z' Gf(2m) computation KAT verify KAT self-test"
        // FSM_TRANS:5.EC.1.0,"CONDITIONAL TEST", "EC CVL Primitive 'Z' computation KAT", "Invoke EC CVL Primitive 'Z' Gf(2m) computation KAT self-test"
        ecF2mPrimitiveZTest();
        // FSM_TRANS:5.EC.1.1,"EC CVL Primitive 'Z' Gf(2m) computation KAT", "CONDITIONAL TEST", "EC CVL Primitive 'Z' Gf(2m) computation KAT self-test successful completion"
        // FSM_TRANS:5.EC.1.2,"EC CVL Primitive 'Z' Gf(2m) computation KAT", "SOFT ERROR", "EC CVL Primitive 'Z' Gf(2m) computation KAT self-test failed"

        // FSM_STATE:5.EC.2,"ECDSA Gf(p) SIGN VERIFY KAT", "The module is performing ECDSA Gf(p) sign and verify KAT self-test"
        // FSM_TRANS:5.EC.2.0,"CONDITIONAL TEST", "ECDSA SIGN VERIFY KAT", "Invoke ECDSA Gf(p) Sign/Verify  KAT self-test"
        EcDsaSigner signer = DSA_PROVIDER.createEngine(true);
        DSA_PROVIDER.createEngine(false);
        // FSM_TRANS:5.EC.2.1,"ECDSA Gf(p) SIGN VERIFY KAT", "CONDITIONAL TEST", "ECDSA Gf(p) Sign/Verify  KAT self-test successful completion"
        // FSM_TRANS:5.EC.2.2,"ECDSA Gf(p) SIGN VERIFY KAT", "SOFT ERROR", "ECDSA Gf(p) Sign/Verify  KAT self-test failed"

        // FSM_STATE:5.EC.3,"ECDSA Gf(2m) SIGN VERIFY KAT", "The module is performing ECDSA Gf(2m) sign and verify KAT self-test"
        // FSM_TRANS:5.EC.3.0,"CONDITIONAL TEST", "ECDSA SIGN VERIFY KAT", "Invoke ECDSA Gf(2m) Sign/Verify  KAT self-test"
        f2mDsaTest(signer);
        // FSM_TRANS:5.EC.3.1,"ECDSA Gf(2m) SIGN VERIFY KAT", "CONDITIONAL TEST", "ECDSA Gf(2m) Sign/Verify  KAT self-test successful completion"
        // FSM_TRANS:5.EC.3.2,"ECDSA Gf(2m) SIGN VERIFY KAT", "SOFT ERROR", "ECDSA Gf(2m) Sign/Verify  KAT self-test failed"
    }

    /**
     * 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"),
        K571("K-571"),
        K409("K-409"),
        K283("K-283"),
        K233("K-233"),
        P521("P-521"),
        P384("P-384"),
        P256("P-256"),
        P224("P-224"),
        brainpoolP224r1("brainpoolP224r1"),
        brainpoolP224t1("brainpoolP224t1"),
        brainpoolP256r1("brainpoolP256r1"),
        brainpoolP256t1("brainpoolP256t1"),
        brainpoolP320r1("brainpoolP320r1"),
        brainpoolP320t1("brainpoolP320t1"),
        brainpoolP384r1("brainpoolP384r1"),
        brainpoolP384t1("brainpoolP384t1"),
        brainpoolP512r1("brainpoolP512r1"),
        brainpoolP512t1("brainpoolP512t1"),
        secp256k1("secp256k1");

        private final String curveName;

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

        public String getCurveName()
        {
            return curveName;
        }
    }

    private static Set approvedKeySet = new HashSet();

    static
    {
        // build approved list for key generation and signature verification based on SP 800-186
        DomainParameterID[] ids = DomainParameterID.values();

        for (int i = 0; i != ids.length; i++)
        {
            String name = ids[i].curveName;

            //The B and K curves are deprecated but still allowed.
            if (Properties.isOverrideSet("org.bouncycastle.ec.enable_f2m") || (name.charAt(0) != 'B' && name.charAt(0) != 'K'))
            {
                approvedKeySet.add(ECNamedCurveTable.getOID(name));
            }
        }
    }

    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);

            if (CryptoServicesRegistrar.isInApprovedOnlyMode())
            {
                validateDomainParameters(domainParameters);
            }
            this.domainParameters = domainParameters;
        }

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

    private static void validateDomainParameters(ECDomainParameters domainParameters)
    {
        ASN1ObjectIdentifier paramOid;
        if (domainParameters instanceof NamedECDomainParameters)
        {
            paramOid = ((NamedECDomainParameters)domainParameters).getID();
        }
        else
        {
            paramOid = ECDomainParametersIndex.lookupOID(domainParameters);
        }

        if (paramOid == null || !approvedKeySet.contains(paramOid))
        {
            throw new FipsUnapprovedOperationError("EC domain parameters not on approved list");
        }
    }

    /**
     * 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);
            }

            if (algorithm.basicVariation() == Variations.ECDDSA && digestAlgorithm == null)
            {
                throw new IllegalArgumentException("ECDDSA cannot be used with a NULL digest");
            }

            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())
            {
                validateDomainParameters(keyGenParameters.getDomainParameters());

                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.EC.4, "EC PAIRWISE CONSISTENCY TEST", "The module is performing EC Pairwise Consistency self-test"
            // FSM_TRANS:5.EC.4.0,"CONDITIONAL TEST", "EC PAIRWISE CONSISTENCY TEST", "Invoke EC Pairwise Consistency test"
            validateKeyPair(algorithm, kp);
            // FSM_TRANS:5.EC.4.1,"EC PAIRWISE CONSISTENCY TEST", "CONDITIONAL TEST", "EC Pairwise Consistency test successful"
            // FSM_TRANS:5.EC.4.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());
                    }
                    validateDomainParameters(ecKey.getDomainParameters());

                    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())
                {
                    validateDomainParameters(ecKey.getDomainParameters());

                    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())
            {
                validateDomainParameters(ecKey.getDomainParameters());

                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())
            {
                validateDomainParameters(ecKey.getDomainParameters());

                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())
            {
                validateDomainParameters(k.getDomainParameters());

                validateCurveSize(key.getAlgorithm(), k.getDomainParameters());

                Utils.checkDigestAlgorithm(LOG, parameters.getDigestAlgorithm(), "org.bouncycastle.ec.allow_sha1_sig");
            }

            Digest digest = (parameters.digestAlgorithm != null) ? FipsSHS.createDigest(parameters.digestAlgorithm) : new NullDigest();
            EcDsaSigner ecdsaSigner;
            if (parameters.getAlgorithm().basicVariation() == Variations.ECDSA)
            {
                ecdsaSigner = DSA_PROVIDER.createEngine(true);
            }
            else
            {
                ExtendedDigest macDigest = FipsSHS.createDigest(parameters.digestAlgorithm);
                if (macDigest == null)
                {
                    throw new IllegalArgumentException("no HMAC support for chosen digest: " + parameters.digestAlgorithm.getName());
                }
                ecdsaSigner = DSA_PROVIDER.createEngine(parameters.digestAlgorithm, new HMacDsaKCalculator(macDigest));
            }
            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(false);
            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);
        }

        /**
         * Create a validator for EC DSA signatures.
         *
         * @param key        the key to initialize the verifier with.
         * @param parameters parameters required to configure the verification.
         * @param signature  the signature the data is to be validated against.
         * @return an OutputVerifier.
         */
        public FipsOutputValidator createValidator(AsymmetricPublicKey key, final DSAParameters parameters, byte[] signature)
            throws InvalidSignatureException
        {
            EcDsaSigner ecdsaSigner = DSA_PROVIDER.createEngine(false);
            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 DSAOutputValidator(ecdsaSigner, digest, parameters, signature);
        }
    }

    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()
    {
        ECDomainParameters p = ECDomainParametersIndex.lookupDomainParameters(SECObjectIdentifiers.secp256r1);
        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 AsymmetricCipherKeyPair getF2mKATKeyPair()
    {
        X9ECParameters p = NISTNamedCurves.getByName("B-233");
        EcDomainParameters params = new EcDomainParameters(new ECDomainParameters(p.getCurve(), p.getG(), p.getN(), p.getH(), p.getSeed()));
        EcPrivateKeyParameters priKey = new EcPrivateKeyParameters(
            new BigInteger("20186677036482506115567393538695075300175221296989956723148347484984008"), // d
            params);

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

        return new AsymmetricCipherKeyPair(pubKey, priKey);
    }

    private static class DsaProvider
        extends FipsEngineProvider
    {
        private static final BigInteger r = new BigInteger("d73cd3722bae6cc0b39065bb4003d8ece1ef2f7a8a55bfd677234b0b3b902650", 16);
        private static final BigInteger s = new BigInteger("d9c88297fefed8441e08dda69554a6452b8a0bd4a0ea1ddb750499f0c2298c2f", 16);

        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 = katKeyPair;
                    byte[] M = Hex.decode("1BD4ED430B0F384B4E8D458EFF1A8A553286D7AC21CB2F6806172EF5F94A06AD");

                    SecureRandom k = new TestRandomData(Hex.decode("a0640d4957f27d091ab1aebc69949d96e5ac2bb283ed5284a5674758b12f08df"));

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

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

                    if (!sig[0].equals(r) || !sig[1].equals(s))
                    {
                        fail("signature incorrect");
                    }

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

                    if (!dsa.verifySignature(M, r, s))
                    {
                        fail("signature fails");
                    }
                }
            });
        }

        public EcDsaSigner createEngine(boolean forSigning)
        {
            // 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 = katKeyPair;
                    byte[] M = Hex.decode("1BD4ED430B0F384B4E8D458EFF1A8A553286D7AC21CB2F6806172EF5F94A06AD");

                    if (forSigning)
                    {
                        SecureRandom k = new TestRandomData(Hex.decode("a0640d4957f27d091ab1aebc69949d96e5ac2bb283ed5284a5674758b12f08df"));

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

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

                        if (!sig[0].equals(r) || !sig[1].equals(s))
                        {
                            fail("signature incorrect");
                        }
                    }
                    else
                    {
                        dsa.init(false, kp.getPublic());

                        if (!dsa.verifySignature(M, r, s))
                        {
                            fail("signature fails");
                        }
                    }
                }
            });
        }

        private static final Map sigKats = new HashMap();

        private static final BigInteger rDetSha1 = new BigInteger("9390526f81255b33c950a4419878801be7bf3e5823468bae22bc6e4e86734ffb", 16);
        private static final BigInteger sDetSha1 = new BigInteger("5a4841d6927d9a3bc5c3b37413d798d6d01fba107411ed4b02986d15becf9670", 16);
        private static final BigInteger rDetSha224 = new BigInteger("b2f31c147179b5c27161b2cc7dcbd0f6a2d74dd5ee3b433928a0fe81a0d77d38", 16);
        private static final BigInteger sDetSha224 = new BigInteger("fb6f8c56b1b66bafc4d3294c0df57bc6ec22d12fd22da0bcb4c4741a5d10a2fd", 16);
        private static final BigInteger rDetSha256 = new BigInteger("522bb76fa0ab8f043b6be30f099b56b12bcc69f247f5a5a39cec4b02fd08b87b", 16);
        private static final BigInteger sDetSha256 = new BigInteger("32fc666aa042b991aa86a1c5f4697418484f81f91485bf8ffa51590131a7a78f", 16);
        private static final BigInteger rDetSha384 = new BigInteger("6d6b76dbf798f27e8950bd0fe6c228d78852d513613423a3ee085cca56bea0ba", 16);
        private static final BigInteger sDetSha384 = new BigInteger("71547e92702e2b6c770dc542144c8dfc5b31e6c8a8c5a8929915d0ec12cfff5", 16);
        private static final BigInteger rDetSha512 = new BigInteger("caa9a9f66852c1744fb40f1b59835337eabb0148f97f1f867ad8246cd282d0a2", 16);
        private static final BigInteger sDetSha512 = new BigInteger("946d6b64469b25d059ec8083e5cda36e4443dc98d2289a890587f49590360814", 16);
        private static final BigInteger rDetSha512_224 = new BigInteger("b92d4c176762df6027431e2b08686eeb3395cde69cf3872ff1ba864caa7f32cd", 16);
        private static final BigInteger sDetSha512_224 = new BigInteger("ab7af8d8646e917744ea5bda468e28d0d413c207135b6604862412a2ef704c40", 16);
        private static final BigInteger rDetSha512_256 = new BigInteger("57e55a88f7ea9f036ea64bd247029c821b10fd167ba6442ba2a9d89660f6c605", 16);
        private static final BigInteger sDetSha512_256 = new BigInteger("763419c86554203db3de94642877b4d74d6ac37f6cf09b58978a7b945243928e", 16);
        private static final BigInteger rDetSha3_224 = new BigInteger("5330797301a8abe67a44cc890f160fabf41dea224d7f9b139f1b3ca48b113fae", 16);
        private static final BigInteger sDetSha3_224 = new BigInteger("a95a916fec76fbbe67fa4f3903d22c20069b46e90aa15bb49e7ff27b1f7a6505", 16);
        private static final BigInteger rDetSha3_256 = new BigInteger("c79d5244c610b6f0f8ca6d848ea77e67c2cc7087b9617e9c93d52a08ee03d70", 16);
        private static final BigInteger sDetSha3_256 = new BigInteger("1ac50fbb370c75ad97d81ca98018e3422209d57fecab4427f236b67f3a148fa7", 16);
        private static final BigInteger rDetSha3_384 = new BigInteger("9f4a4e59dc8ba7dcc35ff3542c37e5d5f3a9df697573cc38156e103731181977", 16);
        private static final BigInteger sDetSha3_384 = new BigInteger("9743b86945e035447613bc3ea86885bb4855b8ff638e02bb841b4685dcc04605", 16);
        private static final BigInteger rDetSha3_512 = new BigInteger("597d41ed723bc9827cab2b011a9a540aa4082024f347d3d168166e29df5029d4", 16);
        private static final BigInteger sDetSha3_512 = new BigInteger("627f032d2252d942fabf533df4f11817fac3fd1a53559d2689a0cc368b22824e", 16);
        private static final BigInteger rDetShake128 = new BigInteger("cacabf72ff5250ff193e201b65a9c0d25ae52a4d02f67f3ad6c817fbd962c254", 16);
        private static final BigInteger sDetShake128 = new BigInteger("96ec4a5b73603df896adc6965e18be02c326cc922b3dd399c1309b0bf14aa0e4", 16);
        private static final BigInteger rDetShake256 = new BigInteger("680d40574d06cbf3c2502b76f91ebe16d4eca4607554d0036b50446526118111", 16);
        private static final BigInteger sDetShake256 = new BigInteger("bedaf371aa2de86dd3d201db52c351f6f0c5fe132a355db0e26f3922e691439e", 16);
        
        static
        {
            sigKats.put(FipsSHS.Algorithm.SHA1, new BigInteger[] { rDetSha1, sDetSha1 });
            sigKats.put(FipsSHS.Algorithm.SHA224, new BigInteger[] { rDetSha224, sDetSha224 });
            sigKats.put(FipsSHS.Algorithm.SHA256, new BigInteger[] { rDetSha256, sDetSha256 });
            sigKats.put(FipsSHS.Algorithm.SHA384, new BigInteger[] { rDetSha384, sDetSha384 });
            sigKats.put(FipsSHS.Algorithm.SHA512, new BigInteger[] { rDetSha512, sDetSha512 });
            sigKats.put(FipsSHS.Algorithm.SHA512_224, new BigInteger[] { rDetSha512_224, sDetSha512_224 });
            sigKats.put(FipsSHS.Algorithm.SHA512_256, new BigInteger[] { rDetSha512_256, sDetSha512_256 });
            sigKats.put(FipsSHS.Algorithm.SHA3_224, new BigInteger[] { rDetSha3_224, sDetSha3_224 });
            sigKats.put(FipsSHS.Algorithm.SHA3_256, new BigInteger[] { rDetSha3_256, sDetSha3_256 });
            sigKats.put(FipsSHS.Algorithm.SHA3_384, new BigInteger[] { rDetSha3_384, sDetSha3_384 });
            sigKats.put(FipsSHS.Algorithm.SHA3_512, new BigInteger[] { rDetSha3_512, sDetSha3_512 });
            sigKats.put(FipsSHS.Algorithm.SHAKE128, new BigInteger[] { rDetShake128, sDetShake128 });
            sigKats.put(FipsSHS.Algorithm.SHAKE256, new BigInteger[] { rDetShake256, sDetShake256 });
        }
        
        public EcDsaSigner createEngine(final FipsDigestAlgorithm digest, HMacDsaKCalculator calculator)
        {
            // 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(calculator), new VariantKatTest()
            {
                void evaluate(EcDsaSigner dsa)
                    throws Exception
                {
                    AsymmetricCipherKeyPair kp = katKeyPair;
                    byte[] M = Hex.decode("1BD4ED430B0F384B4E8D458EFF1A8A553286D7AC21CB2F6806172EF5F94A06AD");

                    dsa.init(true, kp.getPrivate());

                    BigInteger[] sig = dsa.generateSignature(M);
                    BigInteger[] katSig = sigKats.get(digest);

                    if (katSig != null)
                    {
                        // we can just use the KAT.
                        if (!sig[0].equals(katSig[0]) || !sig[1].equals(katSig[1]))
                        {
                            fail("signature incorrect");
                        }
                    }
                    else
                    {
                        // we need to confirm verification
                        dsa.init(false, kp.getPublic());

                        if (!dsa.verifySignature(M, sig[0], sig[1]))
                        {
                            fail("signature check failed");
                        }
                    }
                }
            });
        }
    }

    private static void f2mDsaTest(EcDsaSigner signer)
    {
        SelfTestExecutor.validate(ALGORITHM, signer, new VariantKatTest()
        {
            void evaluate(EcDsaSigner dsa)
                throws Exception
            {
                BigInteger f2mR = new BigInteger(1, Hex.decode("d001312179360f7a557d4686e2faf9740fd3289edbafb5e551402cf1b0"));
                BigInteger f2mS = new BigInteger(1, Hex.decode("9d4c2f24b50ce6b9ac725c7833c495fe703296c038dab05ea7af06cafe"));

                AsymmetricCipherKeyPair kp = f2mKatKeyPair;

                SecureRandom k = new TestRandomBigInteger(233, Hex.decode("640d4957f27d091ab1aebc69949d96e5ac2bb283ed5284a5674758b12f"));
                byte[] M = Hex.decode("1BD4ED430B0F384B4E8D458EFF1A8A553286D7AC21CB2F6806172EF5F94A06AD");

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

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

                if (!sig[0].equals(f2mR) || !sig[1].equals(f2mS))
                {
                    fail("F2m signature incorrect");
                }

                dsa.init(false, kp.getPublic());
                if (!dsa.verifySignature(M, sig[0], sig[1]))
                {
                    fail("F2m 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 = katKeyPair;

                    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 = katKeyPair;

                    AsymmetricCipherKeyPair testOther = getTestKeyPair(kp);

                    agreement.init(kp.getPrivate());

                    if (!expected.equals(agreement.calculateAgreement(testOther.getPublic())))
                    {
                        fail("KAT ECDHC 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 = katKeyPair;

                    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 = katKeyPair;

                    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 void ecF2mPrimitiveZTest()
    {
        SelfTestExecutor.validate(ALGORITHM, new VariantInternalKatTest(ALGORITHM)
        {
            @Override
            void evaluate()
                throws Exception
            {
                X9ECParameters p = NISTNamedCurves.getByName("B-233");
                ECDomainParameters params = new ECDomainParameters(p.getCurve(), p.getG(), p.getN(), p.getH(), p.getSeed());
                BigInteger dValue = new BigInteger("20186677036482506115567393538695075300175221296989956723148347484984008");

                ECPoint Q = params.getCurve().decodePoint(Hex.decode("03000518bce3b1b492c23094dcd7674c8ea6a3bcb7861bd2fb11be1999b796"));

                if (!Q.equals(params.getG().multiply(dValue)))
                {
                    fail("EC primitive F2m '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 - 2025 Weber Informatics LLC | Privacy Policy