8208698: Improved ECC Implementation

New implementation of ECDH and ECDSA forsome prime-order curves

Reviewed-by: ascarpino
diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDHKeyAgreement.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDHKeyAgreement.java
index fbb2838..9540a2d 100644
--- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDHKeyAgreement.java
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDHKeyAgreement.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,14 +25,19 @@
 
 package sun.security.ec;
 
+import java.math.*;
 import java.security.*;
 import java.security.interfaces.*;
 import java.security.spec.*;
+import java.util.Optional;
 
 import javax.crypto.*;
 import javax.crypto.spec.*;
 
+import sun.security.util.ArrayUtil;
 import sun.security.util.ECUtil;
+import sun.security.util.math.*;
+import sun.security.ec.point.*;
 
 /**
  * KeyAgreement implementation for ECDH.
@@ -44,8 +49,8 @@
     // private key, if initialized
     private ECPrivateKey privateKey;
 
-    // encoded public point, non-null between doPhase() & generateSecret() only
-    private byte[] publicValue;
+    // public key, non-null between doPhase() & generateSecret() only
+    private ECPublicKey publicKey;
 
     // length of the secret to be derived
     private int secretLen;
@@ -65,7 +70,7 @@
                         ("Key must be instance of PrivateKey");
         }
         privateKey = (ECPrivateKey) ECKeyFactory.toECKey(key);
-        publicValue = null;
+        publicKey = null;
     }
 
     // see JCE spec
@@ -87,7 +92,7 @@
         if (privateKey == null) {
             throw new IllegalStateException("Not initialized");
         }
-        if (publicValue != null) {
+        if (publicKey != null) {
             throw new IllegalStateException("Phase already executed");
         }
         if (!lastPhase) {
@@ -99,40 +104,74 @@
                 ("Key must be a PublicKey with algorithm EC");
         }
 
-        ECPublicKey ecKey = (ECPublicKey)key;
-        ECParameterSpec params = ecKey.getParams();
+        this.publicKey = (ECPublicKey) key;
 
-        if (ecKey instanceof ECPublicKeyImpl) {
-            publicValue = ((ECPublicKeyImpl)ecKey).getEncodedPublicValue();
-        } else { // instanceof ECPublicKey
-            publicValue =
-                ECUtil.encodePoint(ecKey.getW(), params.getCurve());
-        }
+        ECParameterSpec params = publicKey.getParams();
         int keyLenBits = params.getCurve().getField().getFieldSize();
         secretLen = (keyLenBits + 7) >> 3;
 
         return null;
     }
 
+    private static void validateCoordinate(BigInteger c, BigInteger mod) {
+        if (c.compareTo(BigInteger.ZERO) < 0) {
+            throw new ProviderException("invalid coordinate");
+        }
+
+        if (c.compareTo(mod) >= 0) {
+            throw new ProviderException("invalid coordinate");
+        }
+    }
+
+    /*
+     * Check whether a public key is valid. Throw ProviderException
+     * if it is not valid or could not be validated.
+     */
+    private static void validate(ECOperations ops, ECPublicKey key) {
+
+        // ensure that integers are in proper range
+        BigInteger x = key.getW().getAffineX();
+        BigInteger y = key.getW().getAffineY();
+
+        BigInteger p = ops.getField().getSize();
+        validateCoordinate(x, p);
+        validateCoordinate(y, p);
+
+        // ensure the point is on the curve
+        EllipticCurve curve = key.getParams().getCurve();
+        BigInteger rhs = x.modPow(BigInteger.valueOf(3), p).add(curve.getA()
+            .multiply(x)).add(curve.getB()).mod(p);
+        BigInteger lhs = y.modPow(BigInteger.valueOf(2), p).mod(p);
+        if (!rhs.equals(lhs)) {
+            throw new ProviderException("point is not on curve");
+        }
+
+        // check the order of the point
+        ImmutableIntegerModuloP xElem = ops.getField().getElement(x);
+        ImmutableIntegerModuloP yElem = ops.getField().getElement(y);
+        AffinePoint affP = new AffinePoint(xElem, yElem);
+        byte[] order = key.getParams().getOrder().toByteArray();
+        ArrayUtil.reverse(order);
+        Point product = ops.multiply(affP, order);
+        if (!ops.isNeutral(product)) {
+            throw new ProviderException("point has incorrect order");
+        }
+
+    }
+
     // see JCE spec
     @Override
     protected byte[] engineGenerateSecret() throws IllegalStateException {
-        if ((privateKey == null) || (publicValue == null)) {
+        if ((privateKey == null) || (publicKey == null)) {
             throw new IllegalStateException("Not initialized correctly");
         }
 
-        byte[] s = privateKey.getS().toByteArray();
-        byte[] encodedParams =                   // DER OID
-            ECUtil.encodeECParameterSpec(null, privateKey.getParams());
-
-        try {
-
-            return deriveKey(s, publicValue, encodedParams);
-
-        } catch (GeneralSecurityException e) {
-            throw new ProviderException("Could not derive key", e);
-        }
-
+        Optional<byte[]> resultOpt = deriveKeyImpl(privateKey, publicKey);
+        byte[] result = resultOpt.orElseGet(
+            () -> deriveKeyNative(privateKey, publicKey)
+        );
+        publicKey = null;
+        return result;
     }
 
     // see JCE spec
@@ -141,7 +180,8 @@
             offset) throws IllegalStateException, ShortBufferException {
         if (offset + secretLen > sharedSecret.length) {
             throw new ShortBufferException("Need " + secretLen
-                + " bytes, only " + (sharedSecret.length - offset) + " available");
+                + " bytes, only " + (sharedSecret.length - offset)
+                + " available");
         }
         byte[] secret = engineGenerateSecret();
         System.arraycopy(secret, 0, sharedSecret, offset, secret.length);
@@ -163,6 +203,78 @@
         return new SecretKeySpec(engineGenerateSecret(), "TlsPremasterSecret");
     }
 
+    private static
+    Optional<byte[]> deriveKeyImpl(ECPrivateKey priv, ECPublicKey pubKey) {
+
+        ECParameterSpec ecSpec = priv.getParams();
+        EllipticCurve curve = ecSpec.getCurve();
+        Optional<ECOperations> opsOpt = ECOperations.forParameters(ecSpec);
+        if (opsOpt.isEmpty()) {
+            return Optional.empty();
+        }
+        ECOperations ops = opsOpt.get();
+        if (! (priv instanceof ECPrivateKeyImpl)) {
+            return Optional.empty();
+        }
+        ECPrivateKeyImpl privImpl = (ECPrivateKeyImpl) priv;
+        byte[] sArr = privImpl.getArrayS();
+
+        // to match the native implementation, validate the public key here
+        // and throw ProviderException if it is invalid
+        validate(ops, pubKey);
+
+        IntegerFieldModuloP field = ops.getField();
+        // convert s array into field element and multiply by the cofactor
+        MutableIntegerModuloP scalar = field.getElement(sArr).mutable();
+        SmallValue cofactor =
+            field.getSmallValue(priv.getParams().getCofactor());
+        scalar.setProduct(cofactor);
+        int keySize = (curve.getField().getFieldSize() + 7) / 8;
+        byte[] privArr = scalar.asByteArray(keySize);
+
+        ImmutableIntegerModuloP x =
+            field.getElement(pubKey.getW().getAffineX());
+        ImmutableIntegerModuloP y =
+            field.getElement(pubKey.getW().getAffineY());
+        AffinePoint affPub = new AffinePoint(x, y);
+        Point product = ops.multiply(affPub, privArr);
+        if (ops.isNeutral(product)) {
+            throw new ProviderException("Product is zero");
+        }
+        AffinePoint affProduct = product.asAffine();
+
+        byte[] result = affProduct.getX().asByteArray(keySize);
+        ArrayUtil.reverse(result);
+
+        return Optional.of(result);
+    }
+
+    private static
+    byte[] deriveKeyNative(ECPrivateKey privateKey, ECPublicKey publicKey) {
+
+        ECParameterSpec params = privateKey.getParams();
+        byte[] s = privateKey.getS().toByteArray();
+        byte[] encodedParams =                   // DER OID
+            ECUtil.encodeECParameterSpec(null, params);
+
+        byte[] publicValue;
+        if (publicKey instanceof ECPublicKeyImpl) {
+            ECPublicKeyImpl ecPub = (ECPublicKeyImpl) publicKey;
+            publicValue = ecPub.getEncodedPublicValue();
+        } else { // instanceof ECPublicKey
+            publicValue =
+                ECUtil.encodePoint(publicKey.getW(), params.getCurve());
+        }
+
+        try {
+            return deriveKey(s, publicValue, encodedParams);
+
+        } catch (GeneralSecurityException e) {
+            throw new ProviderException("Could not derive key", e);
+        }
+    }
+
+
     /**
      * Generates a secret key using the public and private keys.
      *
diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSAOperations.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSAOperations.java
new file mode 100644
index 0000000..3c2f9eb
--- /dev/null
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSAOperations.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ec;
+
+import sun.security.ec.point.*;
+import sun.security.util.ArrayUtil;
+import sun.security.util.math.*;
+import static sun.security.ec.ECOperations.IntermediateValueException;
+
+import java.security.ProviderException;
+import java.security.spec.*;
+import java.util.Optional;
+
+public class ECDSAOperations {
+
+    public static class Seed {
+        private final byte[] seedValue;
+
+        public Seed(byte[] seedValue) {
+            this.seedValue = seedValue;
+        }
+
+        public byte[] getSeedValue() {
+            return seedValue;
+        }
+    }
+
+    public static class Nonce {
+        private final byte[] nonceValue;
+
+        public Nonce(byte[] nonceValue) {
+            this.nonceValue = nonceValue;
+        }
+
+        public byte[] getNonceValue() {
+            return nonceValue;
+        }
+    }
+
+    private final ECOperations ecOps;
+    private final AffinePoint basePoint;
+
+    public ECDSAOperations(ECOperations ecOps, ECPoint basePoint) {
+        this.ecOps = ecOps;
+        this.basePoint = toAffinePoint(basePoint, ecOps.getField());
+    }
+
+    public ECOperations getEcOperations() {
+        return ecOps;
+    }
+
+    public AffinePoint basePointMultiply(byte[] scalar) {
+        return ecOps.multiply(basePoint, scalar).asAffine();
+    }
+
+    public static AffinePoint toAffinePoint(ECPoint point,
+        IntegerFieldModuloP field) {
+
+        ImmutableIntegerModuloP affineX = field.getElement(point.getAffineX());
+        ImmutableIntegerModuloP affineY = field.getElement(point.getAffineY());
+        return new AffinePoint(affineX, affineY);
+    }
+
+    public static
+    Optional<ECDSAOperations> forParameters(ECParameterSpec ecParams) {
+        Optional<ECOperations> curveOps =
+            ECOperations.forParameters(ecParams);
+        return curveOps.map(
+            ops -> new ECDSAOperations(ops, ecParams.getGenerator())
+        );
+    }
+
+    /**
+     *
+     * Sign a digest using the provided private key and seed.
+     * IMPORTANT: The private key is a scalar represented using a
+     * little-endian byte array. This is backwards from the conventional
+     * representation in ECDSA. The routines that produce and consume this
+     * value uses little-endian, so this deviation from convention removes
+     * the requirement to swap the byte order. The returned signature is in
+     * the conventional byte order.
+     *
+     * @param privateKey the private key scalar as a little-endian byte array
+     * @param digest the digest to be signed
+     * @param seed the seed that will be used to produce the nonce. This object
+     *             should contain an array that is at least 64 bits longer than
+     *             the number of bits required to represent the group order.
+     * @return the ECDSA signature value
+     * @throws IntermediateValueException if the signature cannot be produced
+     *      due to an unacceptable intermediate or final value. If this
+     *      exception is thrown, then the caller should discard the nonnce and
+     *      try again with an entirely new nonce value.
+     */
+    public byte[] signDigest(byte[] privateKey, byte[] digest, Seed seed)
+        throws IntermediateValueException {
+
+        byte[] nonceArr = ecOps.seedToScalar(seed.getSeedValue());
+
+        Nonce nonce = new Nonce(nonceArr);
+        return signDigest(privateKey, digest, nonce);
+    }
+
+    /**
+     *
+     * Sign a digest using the provided private key and nonce.
+     * IMPORTANT: The private key and nonce are scalars represented by a
+     * little-endian byte array. This is backwards from the conventional
+     * representation in ECDSA. The routines that produce and consume these
+     * values use little-endian, so this deviation from convention removes
+     * the requirement to swap the byte order. The returned signature is in
+     * the conventional byte order.
+     *
+     * @param privateKey the private key scalar as a little-endian byte array
+     * @param digest the digest to be signed
+     * @param nonce the nonce object containing a little-endian scalar value.
+     * @return the ECDSA signature value
+     * @throws IntermediateValueException if the signature cannot be produced
+     *      due to an unacceptable intermediate or final value. If this
+     *      exception is thrown, then the caller should discard the nonnce and
+     *      try again with an entirely new nonce value.
+     */
+    public byte[] signDigest(byte[] privateKey, byte[] digest, Nonce nonce)
+        throws IntermediateValueException {
+
+        IntegerFieldModuloP orderField = ecOps.getOrderField();
+        int orderBits = orderField.getSize().bitLength();
+        if (orderBits % 8 != 0 && orderBits < digest.length * 8) {
+            // This implementation does not support truncating digests to
+            // a length that is not a multiple of 8.
+            throw new ProviderException("Invalid digest length");
+        }
+
+        byte[] k = nonce.getNonceValue();
+        // check nonce length
+        int length = (orderField.getSize().bitLength() + 7) / 8;
+        if (k.length != length) {
+            throw new ProviderException("Incorrect nonce length");
+        }
+
+        MutablePoint R = ecOps.multiply(basePoint, k);
+        IntegerModuloP r = R.asAffine().getX();
+        // put r into the correct field by fully reducing to an array
+        byte[] temp = new byte[length];
+        r.asByteArray(temp);
+        r = orderField.getElement(temp);
+        // store r in result
+        r.asByteArray(temp);
+        byte[] result = new byte[2 * length];
+        ArrayUtil.reverse(temp);
+        System.arraycopy(temp, 0, result, 0, length);
+        // compare r to 0
+        if (ECOperations.allZero(temp)) {
+            throw new IntermediateValueException();
+        }
+
+        IntegerModuloP dU = orderField.getElement(privateKey);
+        int lengthE = Math.min(length, digest.length);
+        byte[] E = new byte[lengthE];
+        System.arraycopy(digest, 0, E, 0, lengthE);
+        ArrayUtil.reverse(E);
+        IntegerModuloP e = orderField.getElement(E);
+        IntegerModuloP kElem = orderField.getElement(k);
+        IntegerModuloP kInv = kElem.multiplicativeInverse();
+        MutableIntegerModuloP s = r.mutable();
+        s.setProduct(dU).setSum(e).setProduct(kInv);
+        // store s in result
+        s.asByteArray(temp);
+        ArrayUtil.reverse(temp);
+        System.arraycopy(temp, 0, result, length, length);
+        // compare s to 0
+        if (ECOperations.allZero(temp)) {
+            throw new IntermediateValueException();
+        }
+
+        return result;
+
+    }
+
+}
diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSASignature.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSASignature.java
index 497c600..6070d22 100644
--- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSASignature.java
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSASignature.java
@@ -32,9 +32,11 @@
 import java.security.*;
 import java.security.interfaces.*;
 import java.security.spec.*;
+import java.util.Optional;
 
 import sun.security.jca.JCAUtil;
 import sun.security.util.*;
+import static sun.security.ec.ECOperations.IntermediateValueException;
 
 /**
  * ECDSA signature implementation. This class currently supports the
@@ -147,7 +149,7 @@
         // Stores the precomputed message digest value.
         @Override
         protected void engineUpdate(byte[] b, int off, int len)
-                throws SignatureException {
+        throws SignatureException {
             if (offset >= precomputedDigest.length) {
                 offset = RAW_ECDSA_MAX + 1;
                 return;
@@ -172,7 +174,7 @@
         }
 
         @Override
-        protected void resetDigest(){
+        protected void resetDigest() {
             offset = 0;
         }
 
@@ -222,14 +224,14 @@
     // Nested class for SHA224withECDSA signatures
     public static final class SHA224 extends ECDSASignature {
         public SHA224() {
-           super("SHA-224");
+            super("SHA-224");
         }
     }
 
     // Nested class for SHA224withECDSAinP1363Format signatures
     public static final class SHA224inP1363Format extends ECDSASignature {
         public SHA224inP1363Format() {
-           super("SHA-224", true);
+            super("SHA-224", true);
         }
     }
 
@@ -278,7 +280,7 @@
     // initialize for verification. See JCA doc
     @Override
     protected void engineInitVerify(PublicKey publicKey)
-            throws InvalidKeyException {
+    throws InvalidKeyException {
         this.publicKey = (ECPublicKey) ECKeyFactory.toECKey(publicKey);
 
         // Should check that the supplied key is appropriate for signature
@@ -290,14 +292,14 @@
     // initialize for signing. See JCA doc
     @Override
     protected void engineInitSign(PrivateKey privateKey)
-            throws InvalidKeyException {
+    throws InvalidKeyException {
         engineInitSign(privateKey, null);
     }
 
     // initialize for signing. See JCA doc
     @Override
     protected void engineInitSign(PrivateKey privateKey, SecureRandom random)
-            throws InvalidKeyException {
+    throws InvalidKeyException {
         this.privateKey = (ECPrivateKey) ECKeyFactory.toECKey(privateKey);
 
         // Should check that the supplied key is appropriate for signature
@@ -337,7 +339,7 @@
     // update the signature with the plaintext data. See JCA doc
     @Override
     protected void engineUpdate(byte[] b, int off, int len)
-            throws SignatureException {
+    throws SignatureException {
         messageDigest.update(b, off, len);
         needsReset = true;
     }
@@ -354,20 +356,67 @@
         needsReset = true;
     }
 
-    // sign the data and return the signature. See JCA doc
-    @Override
-    protected byte[] engineSign() throws SignatureException {
+    private byte[] signDigestImpl(ECDSAOperations ops, int seedBits,
+        byte[] digest, ECPrivateKeyImpl privImpl, SecureRandom random)
+        throws SignatureException {
+
+        byte[] seedBytes = new byte[(seedBits + 7) / 8];
+        byte[] s = privImpl.getArrayS();
+
+        // Attempt to create the signature in a loop that uses new random input
+        // each time. The chance of failure is very small assuming the
+        // implementation derives the nonce using extra bits
+        int numAttempts = 128;
+        for (int i = 0; i < numAttempts; i++) {
+            random.nextBytes(seedBytes);
+            ECDSAOperations.Seed seed = new ECDSAOperations.Seed(seedBytes);
+            try {
+                return ops.signDigest(s, digest, seed);
+            } catch (IntermediateValueException ex) {
+                // try again in the next iteration
+            }
+        }
+
+        throw new SignatureException("Unable to produce signature after "
+            + numAttempts + " attempts");
+    }
+
+
+    private Optional<byte[]> signDigestImpl(ECPrivateKey privateKey,
+        byte[] digest, SecureRandom random) throws SignatureException {
+
+        if (! (privateKey instanceof ECPrivateKeyImpl)) {
+            return Optional.empty();
+        }
+        ECPrivateKeyImpl privImpl = (ECPrivateKeyImpl) privateKey;
+        ECParameterSpec params = privateKey.getParams();
+
+        // seed is the key size + 64 bits
+        int seedBits = params.getOrder().bitLength() + 64;
+        Optional<ECDSAOperations> opsOpt =
+            ECDSAOperations.forParameters(params);
+        if (opsOpt.isEmpty()) {
+            return Optional.empty();
+        } else {
+            byte[] sig = signDigestImpl(opsOpt.get(), seedBits, digest,
+                privImpl, random);
+            return Optional.of(sig);
+        }
+    }
+
+    private byte[] signDigestNative(ECPrivateKey privateKey, byte[] digest,
+        SecureRandom random) throws SignatureException {
+
         byte[] s = privateKey.getS().toByteArray();
         ECParameterSpec params = privateKey.getParams();
+
         // DER OID
         byte[] encodedParams = ECUtil.encodeECParameterSpec(null, params);
         int keySize = params.getCurve().getField().getFieldSize();
 
         // seed is twice the key size (in bytes) plus 1
         byte[] seed = new byte[(((keySize + 7) >> 3) + 1) * 2];
-        if (random == null) {
-            random = JCAUtil.getSecureRandom();
-        }
+
         random.nextBytes(seed);
 
         // random bits needed for timing countermeasures
@@ -375,14 +424,32 @@
         // values must be non-zero to enable countermeasures
         timingArgument |= 1;
 
-        byte[] sig;
         try {
-            sig = signDigest(getDigestValue(), s, encodedParams, seed,
+            return signDigest(digest, s, encodedParams, seed,
                 timingArgument);
         } catch (GeneralSecurityException e) {
             throw new SignatureException("Could not sign data", e);
         }
 
+    }
+
+    // sign the data and return the signature. See JCA doc
+    @Override
+    protected byte[] engineSign() throws SignatureException {
+
+        if (random == null) {
+            random = JCAUtil.getSecureRandom();
+        }
+
+        byte[] digest = getDigestValue();
+        Optional<byte[]> sigOpt = signDigestImpl(privateKey, digest, random);
+        byte[] sig;
+        if (sigOpt.isPresent()) {
+            sig = sigOpt.get();
+        } else {
+            sig = signDigestNative(privateKey, digest, random);
+        }
+
         if (p1363Format) {
             return sig;
         } else {
@@ -400,7 +467,7 @@
         byte[] encodedParams = ECUtil.encodeECParameterSpec(null, params);
 
         if (publicKey instanceof ECPublicKeyImpl) {
-            w = ((ECPublicKeyImpl)publicKey).getEncodedPublicValue();
+            w = ((ECPublicKeyImpl) publicKey).getEncodedPublicValue();
         } else { // instanceof ECPublicKey
             w = ECUtil.encodePoint(publicKey.getW(), params.getCurve());
         }
@@ -423,13 +490,13 @@
     @Override
     @Deprecated
     protected void engineSetParameter(String param, Object value)
-            throws InvalidParameterException {
+    throws InvalidParameterException {
         throw new UnsupportedOperationException("setParameter() not supported");
     }
 
     @Override
     protected void engineSetParameter(AlgorithmParameterSpec params)
-            throws InvalidAlgorithmParameterException {
+    throws InvalidAlgorithmParameterException {
         if (params != null) {
             throw new InvalidAlgorithmParameterException("No parameter accepted");
         }
@@ -439,7 +506,7 @@
     @Override
     @Deprecated
     protected Object engineGetParameter(String param)
-            throws InvalidParameterException {
+    throws InvalidParameterException {
         throw new UnsupportedOperationException("getParameter() not supported");
     }
 
@@ -464,7 +531,7 @@
             out.putInteger(r);
             out.putInteger(s);
             DerValue result =
-                new DerValue(DerValue.tag_Sequence, out.toByteArray());
+            new DerValue(DerValue.tag_Sequence, out.toByteArray());
 
             return result.toByteArray();
 
@@ -497,9 +564,9 @@
             // r and s each occupy half the array
             byte[] result = new byte[k << 1];
             System.arraycopy(rBytes, 0, result, k - rBytes.length,
-                rBytes.length);
+            rBytes.length);
             System.arraycopy(sBytes, 0, result, result.length - sBytes.length,
-                sBytes.length);
+            sBytes.length);
             return result;
 
         } catch (Exception e) {
@@ -539,13 +606,13 @@
      * @return byte[] the signature.
      */
     private static native byte[] signDigest(byte[] digest, byte[] s,
-        byte[] encodedParams, byte[] seed, int timing)
-            throws GeneralSecurityException;
+                                            byte[] encodedParams, byte[] seed, int timing)
+        throws GeneralSecurityException;
 
     /**
      * Verifies the signed digest using the public key.
      *
-     * @param signedDigest the signature to be verified. It is encoded
+     * @param signature the signature to be verified. It is encoded
      *        as a concatenation of the key's R and S values.
      * @param digest the digest to be used.
      * @param w the public key's W point (in uncompressed form).
@@ -554,6 +621,6 @@
      * @return boolean true if the signature is successfully verified.
      */
     private static native boolean verifySignedDigest(byte[] signature,
-        byte[] digest, byte[] w, byte[] encodedParams)
-            throws GeneralSecurityException;
+                                                     byte[] digest, byte[] w, byte[] encodedParams)
+        throws GeneralSecurityException;
 }
diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECKeyPairGenerator.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECKeyPairGenerator.java
index f8cd24c..7736fc9 100644
--- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECKeyPairGenerator.java
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECKeyPairGenerator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -33,13 +33,15 @@
 import java.security.spec.ECParameterSpec;
 import java.security.spec.ECPoint;
 import java.security.spec.InvalidParameterSpecException;
+import java.security.spec.*;
+import java.util.Optional;
 
-import sun.security.ec.ECPrivateKeyImpl;
-import sun.security.ec.ECPublicKeyImpl;
 import sun.security.jca.JCAUtil;
-import sun.security.util.ECParameters;
 import sun.security.util.ECUtil;
+import sun.security.util.math.*;
+import sun.security.ec.point.*;
 import static sun.security.util.SecurityProviderConstants.DEF_EC_KEY_SIZE;
+import static sun.security.ec.ECOperations.IntermediateValueException;
 
 /**
  * EC keypair generator.
@@ -90,14 +92,14 @@
         ECParameterSpec ecSpec = null;
 
         if (params instanceof ECParameterSpec) {
-            ecSpec = ECUtil.getECParameterSpec(null,
-                                                    (ECParameterSpec)params);
+            ECParameterSpec ecParams = (ECParameterSpec) params;
+            ecSpec = ECUtil.getECParameterSpec(null, ecParams);
             if (ecSpec == null) {
                 throw new InvalidAlgorithmParameterException(
                     "Unsupported curve: " + params);
             }
         } else if (params instanceof ECGenParameterSpec) {
-            String name = ((ECGenParameterSpec)params).getName();
+            String name = ((ECGenParameterSpec) params).getName();
             ecSpec = ECUtil.getECParameterSpec(null, name);
             if (ecSpec == null) {
                 throw new InvalidAlgorithmParameterException(
@@ -112,8 +114,7 @@
         ensureCurveIsSupported(ecSpec);
         this.params = ecSpec;
 
-        this.keySize =
-            ((ECParameterSpec)this.params).getCurve().getField().getFieldSize();
+        this.keySize = ecSpec.getCurve().getField().getFieldSize();
         this.random = random;
     }
 
@@ -141,41 +142,99 @@
     @Override
     public KeyPair generateKeyPair() {
 
-        byte[] encodedParams =
-            ECUtil.encodeECParameterSpec(null, (ECParameterSpec)params);
-
-        // seed is twice the key size (in bytes) plus 1
-        byte[] seed = new byte[(((keySize + 7) >> 3) + 1) * 2];
         if (random == null) {
             random = JCAUtil.getSecureRandom();
         }
-        random.nextBytes(seed);
 
         try {
-
-            Object[] keyBytes = generateECKeyPair(keySize, encodedParams, seed);
-
-            // The 'params' object supplied above is equivalent to the native
-            // one so there is no need to fetch it.
-            // keyBytes[0] is the encoding of the native private key
-            BigInteger s = new BigInteger(1, (byte[])keyBytes[0]);
-
-            PrivateKey privateKey =
-                new ECPrivateKeyImpl(s, (ECParameterSpec)params);
-
-            // keyBytes[1] is the encoding of the native public key
-            ECPoint w = ECUtil.decodePoint((byte[])keyBytes[1],
-                ((ECParameterSpec)params).getCurve());
-            PublicKey publicKey =
-                new ECPublicKeyImpl(w, (ECParameterSpec)params);
-
-            return new KeyPair(publicKey, privateKey);
-
-        } catch (Exception e) {
-            throw new ProviderException(e);
+            Optional<KeyPair> kp = generateKeyPairImpl(random);
+            if (kp.isPresent()) {
+                return kp.get();
+            }
+            return generateKeyPairNative(random);
+        } catch (Exception ex) {
+            throw new ProviderException(ex);
         }
     }
 
+    private byte[] generatePrivateScalar(SecureRandom random,
+        ECOperations ecOps, int seedSize) {
+        // Attempt to create the private scalar in a loop that uses new random
+        // input each time. The chance of failure is very small assuming the
+        // implementation derives the nonce using extra bits
+        int numAttempts = 128;
+        byte[] seedArr = new byte[seedSize];
+        for (int i = 0; i < numAttempts; i++) {
+            random.nextBytes(seedArr);
+            try {
+                return ecOps.seedToScalar(seedArr);
+            } catch (IntermediateValueException ex) {
+                // try again in the next iteration
+            }
+        }
+
+        throw new ProviderException("Unable to produce private key after "
+                                         + numAttempts + " attempts");
+    }
+
+    private Optional<KeyPair> generateKeyPairImpl(SecureRandom random)
+        throws InvalidKeyException {
+
+        ECParameterSpec ecParams = (ECParameterSpec) params;
+
+        Optional<ECOperations> opsOpt = ECOperations.forParameters(ecParams);
+        if (opsOpt.isEmpty()) {
+            return Optional.empty();
+        }
+        ECOperations ops = opsOpt.get();
+        IntegerFieldModuloP field = ops.getField();
+        int numBits = ecParams.getOrder().bitLength();
+        int seedBits = numBits + 64;
+        int seedSize = (seedBits + 7) / 8;
+        byte[] privArr = generatePrivateScalar(random, ops, seedSize);
+
+        ECPoint genPoint = ecParams.getGenerator();
+        ImmutableIntegerModuloP x = field.getElement(genPoint.getAffineX());
+        ImmutableIntegerModuloP y = field.getElement(genPoint.getAffineY());
+        AffinePoint affGen = new AffinePoint(x, y);
+        Point pub = ops.multiply(affGen, privArr);
+        AffinePoint affPub = pub.asAffine();
+
+        PrivateKey privateKey = new ECPrivateKeyImpl(privArr, ecParams);
+
+        ECPoint w = new ECPoint(affPub.getX().asBigInteger(),
+            affPub.getY().asBigInteger());
+        PublicKey publicKey = new ECPublicKeyImpl(w, ecParams);
+
+        return Optional.of(new KeyPair(publicKey, privateKey));
+    }
+
+    private KeyPair generateKeyPairNative(SecureRandom random)
+        throws Exception {
+
+        ECParameterSpec ecParams = (ECParameterSpec) params;
+        byte[] encodedParams = ECUtil.encodeECParameterSpec(null, ecParams);
+
+        // seed is twice the key size (in bytes) plus 1
+        byte[] seed = new byte[(((keySize + 7) >> 3) + 1) * 2];
+        random.nextBytes(seed);
+        Object[] keyBytes = generateECKeyPair(keySize, encodedParams, seed);
+
+        // The 'params' object supplied above is equivalent to the native
+        // one so there is no need to fetch it.
+        // keyBytes[0] is the encoding of the native private key
+        BigInteger s = new BigInteger(1, (byte[]) keyBytes[0]);
+
+        PrivateKey privateKey = new ECPrivateKeyImpl(s, ecParams);
+
+        // keyBytes[1] is the encoding of the native public key
+        byte[] pubKey = (byte[]) keyBytes[1];
+        ECPoint w = ECUtil.decodePoint(pubKey, ecParams.getCurve());
+        PublicKey publicKey = new ECPublicKeyImpl(w, ecParams);
+
+        return new KeyPair(publicKey, privateKey);
+    }
+
     private void checkKeySize(int keySize) throws InvalidParameterException {
         if (keySize < KEY_SIZE_MIN) {
             throw new InvalidParameterException
@@ -190,7 +249,9 @@
 
     /**
      * Checks whether the curve in the encoded parameters is supported by the
-     * native implementation.
+     * native implementation. Some curve operations will be performed by the
+     * Java implementation, but not all of them. So native support is still
+     * required for all curves.
      *
      * @param encodedParams encoded parameters in the same form accepted
      *    by generateECKeyPair
diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECOperations.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECOperations.java
new file mode 100644
index 0000000..2995ef7
--- /dev/null
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECOperations.java
@@ -0,0 +1,493 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ec;
+
+import sun.security.ec.point.*;
+import sun.security.util.math.*;
+import sun.security.util.math.intpoly.*;
+
+import java.math.BigInteger;
+import java.security.ProviderException;
+import java.security.spec.ECFieldFp;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.EllipticCurve;
+import java.util.Map;
+import java.util.Optional;
+
+/*
+ * Elliptic curve point arithmetic for prime-order curves where a=-3.
+ * Formulas are derived from "Complete addition formulas for prime order
+ * elliptic curves" by Renes, Costello, and Batina.
+ */
+
+public class ECOperations {
+
+    /*
+     * An exception indicating a problem with an intermediate value produced
+     * by some part of the computation. For example, the signing operation
+     * will throw this exception to indicate that the r or s value is 0, and
+     * that the signing operation should be tried again with a different nonce.
+     */
+    static class IntermediateValueException extends Exception {
+        private static final long serialVersionUID = 1;
+    }
+
+    static final Map<BigInteger, IntegerFieldModuloP> fields = Map.of(
+        IntegerPolynomialP256.MODULUS, new IntegerPolynomialP256(),
+        IntegerPolynomialP384.MODULUS, new IntegerPolynomialP384(),
+        IntegerPolynomialP521.MODULUS, new IntegerPolynomialP521()
+    );
+
+    static final Map<BigInteger, IntegerFieldModuloP> orderFields = Map.of(
+        P256OrderField.MODULUS, new P256OrderField(),
+        P384OrderField.MODULUS, new P384OrderField(),
+        P521OrderField.MODULUS, new P521OrderField()
+    );
+
+    public static Optional<ECOperations> forParameters(ECParameterSpec params) {
+
+        EllipticCurve curve = params.getCurve();
+        if (!(curve.getField() instanceof ECFieldFp)) {
+            return Optional.empty();
+        }
+        ECFieldFp primeField = (ECFieldFp) curve.getField();
+
+        BigInteger three = BigInteger.valueOf(3);
+        if (!primeField.getP().subtract(curve.getA()).equals(three)) {
+            return Optional.empty();
+        }
+        IntegerFieldModuloP field = fields.get(primeField.getP());
+        if (field == null) {
+            return Optional.empty();
+        }
+
+        IntegerFieldModuloP orderField = orderFields.get(params.getOrder());
+        if (orderField == null) {
+            return Optional.empty();
+        }
+
+        ImmutableIntegerModuloP b = field.getElement(curve.getB());
+        ECOperations ecOps = new ECOperations(b, orderField);
+        return Optional.of(ecOps);
+    }
+
+    final ImmutableIntegerModuloP b;
+    final SmallValue one;
+    final SmallValue two;
+    final SmallValue three;
+    final SmallValue four;
+    final ProjectivePoint.Immutable neutral;
+    private final IntegerFieldModuloP orderField;
+
+    public ECOperations(IntegerModuloP b, IntegerFieldModuloP orderField) {
+        this.b = b.fixed();
+        this.orderField = orderField;
+
+        this.one = b.getField().getSmallValue(1);
+        this.two = b.getField().getSmallValue(2);
+        this.three = b.getField().getSmallValue(3);
+        this.four = b.getField().getSmallValue(4);
+
+        IntegerFieldModuloP field = b.getField();
+        this.neutral = new ProjectivePoint.Immutable(field.get0(),
+            field.get1(), field.get0());
+    }
+
+    public IntegerFieldModuloP getField() {
+        return b.getField();
+    }
+    public IntegerFieldModuloP getOrderField() {
+        return orderField;
+    }
+
+    protected ProjectivePoint.Immutable getNeutral() {
+        return neutral;
+    }
+
+    public boolean isNeutral(Point p) {
+        ProjectivePoint<?> pp = (ProjectivePoint<?>) p;
+
+        IntegerModuloP z = pp.getZ();
+
+        IntegerFieldModuloP field = z.getField();
+        int byteLength = (field.getSize().bitLength() + 7) / 8;
+        byte[] zBytes = z.asByteArray(byteLength);
+        return allZero(zBytes);
+    }
+
+    byte[] seedToScalar(byte[] seedBytes)
+        throws IntermediateValueException {
+
+        // Produce a nonce from the seed using FIPS 186-4,section B.5.1:
+        // Per-Message Secret Number Generation Using Extra Random Bits
+        // or
+        // Produce a scalar from the seed using FIPS 186-4, section B.4.1:
+        // Key Pair Generation Using Extra Random Bits
+
+        // To keep the implementation simple, sample in the range [0,n)
+        // and throw IntermediateValueException in the (unlikely) event
+        // that the result is 0.
+
+        // Get 64 extra bits and reduce in to the nonce
+        int seedBits = orderField.getSize().bitLength() + 64;
+        if (seedBytes.length * 8 < seedBits) {
+            throw new ProviderException("Incorrect seed length: " +
+            seedBytes.length * 8 + " < " + seedBits);
+        }
+
+        // input conversion only works on byte boundaries
+        // clear high-order bits of last byte so they don't influence nonce
+        int lastByteBits = seedBits % 8;
+        if (lastByteBits != 0) {
+            int lastByteIndex = seedBits / 8;
+            byte mask = (byte) (0xFF >>> (8 - lastByteBits));
+            seedBytes[lastByteIndex] &= mask;
+        }
+
+        int seedLength = (seedBits + 7) / 8;
+        IntegerModuloP scalarElem =
+            orderField.getElement(seedBytes, 0, seedLength, (byte) 0);
+        int scalarLength = (orderField.getSize().bitLength() + 7) / 8;
+        byte[] scalarArr = new byte[scalarLength];
+        scalarElem.asByteArray(scalarArr);
+        if (ECOperations.allZero(scalarArr)) {
+            throw new IntermediateValueException();
+        }
+        return scalarArr;
+    }
+
+    /*
+     * Compare all values in the array to 0 without branching on any value
+     *
+     */
+    public static boolean allZero(byte[] arr) {
+        byte acc = 0;
+        for (int i = 0; i < arr.length; i++) {
+            acc |= arr[i];
+        }
+        return acc == 0;
+    }
+
+    /*
+     * 4-bit branchless array lookup for projective points.
+     */
+    private void lookup4(ProjectivePoint.Immutable[] arr, int index,
+        ProjectivePoint.Mutable result, IntegerModuloP zero) {
+
+        for (int i = 0; i < 16; i++) {
+            int xor = index ^ i;
+            int bit3 = (xor & 0x8) >>> 3;
+            int bit2 = (xor & 0x4) >>> 2;
+            int bit1 = (xor & 0x2) >>> 1;
+            int bit0 = (xor & 0x1);
+            int inverse = bit0 | bit1 | bit2 | bit3;
+            int set = 1 - inverse;
+
+            ProjectivePoint.Immutable pi = arr[i];
+            result.conditionalSet(pi, set);
+        }
+    }
+
+    private void double4(ProjectivePoint.Mutable p, MutableIntegerModuloP t0,
+        MutableIntegerModuloP t1, MutableIntegerModuloP t2,
+        MutableIntegerModuloP t3, MutableIntegerModuloP t4) {
+
+        for (int i = 0; i < 4; i++) {
+            setDouble(p, t0, t1, t2, t3, t4);
+        }
+    }
+
+    /**
+     * Multiply an affine point by a scalar and return the result as a mutable
+     * point.
+     *
+     * @param affineP the point
+     * @param s the scalar as a little-endian array
+     * @return the product
+     */
+    public MutablePoint multiply(AffinePoint affineP, byte[] s) {
+
+        // 4-bit windowed multiply with branchless lookup.
+        // The mixed addition is faster, so it is used to construct the array
+        // at the beginning of the operation.
+
+        IntegerFieldModuloP field = affineP.getX().getField();
+        ImmutableIntegerModuloP zero = field.get0();
+        // temporaries
+        MutableIntegerModuloP t0 = zero.mutable();
+        MutableIntegerModuloP t1 = zero.mutable();
+        MutableIntegerModuloP t2 = zero.mutable();
+        MutableIntegerModuloP t3 = zero.mutable();
+        MutableIntegerModuloP t4 = zero.mutable();
+
+        ProjectivePoint.Mutable result = new ProjectivePoint.Mutable(field);
+        result.getY().setValue(field.get1().mutable());
+
+        ProjectivePoint.Immutable[] pointMultiples =
+            new ProjectivePoint.Immutable[16];
+        // 0P is neutral---same as initial result value
+        pointMultiples[0] = result.fixed();
+
+        ProjectivePoint.Mutable ps = new ProjectivePoint.Mutable(field);
+        ps.setValue(affineP);
+        // 1P = P
+        pointMultiples[1] = ps.fixed();
+
+        // the rest are calculated using mixed point addition
+        for (int i = 2; i < 16; i++) {
+            setSum(ps, affineP, t0, t1, t2, t3, t4);
+            pointMultiples[i] = ps.fixed();
+        }
+
+        ProjectivePoint.Mutable lookupResult = ps.mutable();
+
+        for (int i = s.length - 1; i >= 0; i--) {
+
+            double4(result, t0, t1, t2, t3, t4);
+
+            int high = (0xFF & s[i]) >>> 4;
+            lookup4(pointMultiples, high, lookupResult, zero);
+            setSum(result, lookupResult, t0, t1, t2, t3, t4);
+
+            double4(result, t0, t1, t2, t3, t4);
+
+            int low = 0xF & s[i];
+            lookup4(pointMultiples, low, lookupResult, zero);
+            setSum(result, lookupResult, t0, t1, t2, t3, t4);
+        }
+
+        return result;
+
+    }
+
+    /*
+     * Point double
+     */
+    private void setDouble(ProjectivePoint.Mutable p, MutableIntegerModuloP t0,
+        MutableIntegerModuloP t1, MutableIntegerModuloP t2,
+        MutableIntegerModuloP t3, MutableIntegerModuloP t4) {
+
+        t0.setValue(p.getX()).setSquare();
+        t1.setValue(p.getY()).setSquare();
+        t2.setValue(p.getZ()).setSquare();
+        t3.setValue(p.getX()).setProduct(p.getY());
+        t4.setValue(p.getY()).setProduct(p.getZ());
+
+        t3.setSum(t3);
+        p.getZ().setProduct(p.getX());
+
+        p.getZ().setProduct(two);
+
+        p.getY().setValue(t2).setProduct(b);
+        p.getY().setDifference(p.getZ());
+
+        p.getX().setValue(p.getY()).setProduct(two);
+        p.getY().setSum(p.getX());
+        p.getY().setReduced();
+        p.getX().setValue(t1).setDifference(p.getY());
+
+        p.getY().setSum(t1);
+        p.getY().setProduct(p.getX());
+        p.getX().setProduct(t3);
+
+        t3.setValue(t2).setProduct(two);
+        t2.setSum(t3);
+        p.getZ().setProduct(b);
+
+        t2.setReduced();
+        p.getZ().setDifference(t2);
+        p.getZ().setDifference(t0);
+        t3.setValue(p.getZ()).setProduct(two);
+        p.getZ().setReduced();
+        p.getZ().setSum(t3);
+        t0.setProduct(three);
+
+        t0.setDifference(t2);
+        t0.setProduct(p.getZ());
+        p.getY().setSum(t0);
+
+        t4.setSum(t4);
+        p.getZ().setProduct(t4);
+
+        p.getX().setDifference(p.getZ());
+        p.getZ().setValue(t4).setProduct(t1);
+
+        p.getZ().setProduct(four);
+
+    }
+
+    /*
+     * Mixed point addition. This method constructs new temporaries each time
+     * it is called. For better efficiency, the method that reuses temporaries
+     * should be used if more than one sum will be computed.
+     */
+    public void setSum(MutablePoint p, AffinePoint p2) {
+
+        IntegerModuloP zero = p.getField().get0();
+        MutableIntegerModuloP t0 = zero.mutable();
+        MutableIntegerModuloP t1 = zero.mutable();
+        MutableIntegerModuloP t2 = zero.mutable();
+        MutableIntegerModuloP t3 = zero.mutable();
+        MutableIntegerModuloP t4 = zero.mutable();
+        setSum((ProjectivePoint.Mutable) p, p2, t0, t1, t2, t3, t4);
+
+    }
+
+    /*
+     * Mixed point addition
+     */
+    private void setSum(ProjectivePoint.Mutable p, AffinePoint p2,
+        MutableIntegerModuloP t0, MutableIntegerModuloP t1,
+        MutableIntegerModuloP t2, MutableIntegerModuloP t3,
+        MutableIntegerModuloP t4) {
+
+        t0.setValue(p.getX()).setProduct(p2.getX());
+        t1.setValue(p.getY()).setProduct(p2.getY());
+        t3.setValue(p2.getX()).setSum(p2.getY());
+        t4.setValue(p.getX()).setSum(p.getY());
+        p.getX().setReduced();
+        t3.setProduct(t4);
+        t4.setValue(t0).setSum(t1);
+
+        t3.setDifference(t4);
+        t4.setValue(p2.getY()).setProduct(p.getZ());
+        t4.setSum(p.getY());
+
+        p.getY().setValue(p2.getX()).setProduct(p.getZ());
+        p.getY().setSum(p.getX());
+        t2.setValue(p.getZ());
+        p.getZ().setProduct(b);
+
+        p.getX().setValue(p.getY()).setDifference(p.getZ());
+        p.getX().setReduced();
+        p.getZ().setValue(p.getX()).setProduct(two);
+        p.getX().setSum(p.getZ());
+
+        p.getZ().setValue(t1).setDifference(p.getX());
+        p.getX().setSum(t1);
+        p.getY().setProduct(b);
+
+        t1.setValue(t2).setProduct(two);
+        t2.setSum(t1);
+        t2.setReduced();
+        p.getY().setDifference(t2);
+
+        p.getY().setDifference(t0);
+        p.getY().setReduced();
+        t1.setValue(p.getY()).setProduct(two);
+        p.getY().setSum(t1);
+
+        t1.setValue(t0).setProduct(two);
+        t0.setSum(t1);
+        t0.setDifference(t2);
+
+        t1.setValue(t4).setProduct(p.getY());
+        t2.setValue(t0).setProduct(p.getY());
+        p.getY().setValue(p.getX()).setProduct(p.getZ());
+
+        p.getY().setSum(t2);
+        p.getX().setProduct(t3);
+        p.getX().setDifference(t1);
+
+        p.getZ().setProduct(t4);
+        t1.setValue(t3).setProduct(t0);
+        p.getZ().setSum(t1);
+
+    }
+
+    /*
+     * Projective point addition
+     */
+    private void setSum(ProjectivePoint.Mutable p, ProjectivePoint.Mutable p2,
+        MutableIntegerModuloP t0, MutableIntegerModuloP t1,
+        MutableIntegerModuloP t2, MutableIntegerModuloP t3,
+        MutableIntegerModuloP t4) {
+
+        t0.setValue(p.getX()).setProduct(p2.getX());
+        t1.setValue(p.getY()).setProduct(p2.getY());
+        t2.setValue(p.getZ()).setProduct(p2.getZ());
+
+        t3.setValue(p.getX()).setSum(p.getY());
+        t4.setValue(p2.getX()).setSum(p2.getY());
+        t3.setProduct(t4);
+
+        t4.setValue(t0).setSum(t1);
+        t3.setDifference(t4);
+        t4.setValue(p.getY()).setSum(p.getZ());
+
+        p.getY().setValue(p2.getY()).setSum(p2.getZ());
+        t4.setProduct(p.getY());
+        p.getY().setValue(t1).setSum(t2);
+
+        t4.setDifference(p.getY());
+        p.getX().setSum(p.getZ());
+        p.getY().setValue(p2.getX()).setSum(p2.getZ());
+
+        p.getX().setProduct(p.getY());
+        p.getY().setValue(t0).setSum(t2);
+        p.getY().setAdditiveInverse().setSum(p.getX());
+        p.getY().setReduced();
+
+        p.getZ().setValue(t2).setProduct(b);
+        p.getX().setValue(p.getY()).setDifference(p.getZ());
+        p.getZ().setValue(p.getX()).setProduct(two);
+
+        p.getX().setSum(p.getZ());
+        p.getX().setReduced();
+        p.getZ().setValue(t1).setDifference(p.getX());
+        p.getX().setSum(t1);
+
+        p.getY().setProduct(b);
+        t1.setValue(t2).setSum(t2);
+        t2.setSum(t1);
+        t2.setReduced();
+
+        p.getY().setDifference(t2);
+        p.getY().setDifference(t0);
+        p.getY().setReduced();
+        t1.setValue(p.getY()).setSum(p.getY());
+
+        p.getY().setSum(t1);
+        t1.setValue(t0).setProduct(two);
+        t0.setSum(t1);
+
+        t0.setDifference(t2);
+        t1.setValue(t4).setProduct(p.getY());
+        t2.setValue(t0).setProduct(p.getY());
+
+        p.getY().setValue(p.getX()).setProduct(p.getZ());
+        p.getY().setSum(t2);
+        p.getX().setProduct(t3);
+
+        p.getX().setDifference(t1);
+        p.getZ().setProduct(t4);
+        t1.setValue(t3).setProduct(t0);
+
+        p.getZ().setSum(t1);
+
+    }
+}
+
diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECPrivateKeyImpl.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECPrivateKeyImpl.java
index ef3b876..81c992d 100644
--- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECPrivateKeyImpl.java
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECPrivateKeyImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -32,11 +32,7 @@
 import java.security.interfaces.*;
 import java.security.spec.*;
 
-import sun.security.util.DerInputStream;
-import sun.security.util.DerOutputStream;
-import sun.security.util.DerValue;
-import sun.security.util.ECParameters;
-import sun.security.util.ECUtil;
+import sun.security.util.*;
 import sun.security.x509.AlgorithmId;
 import sun.security.pkcs.PKCS8Key;
 
@@ -68,6 +64,7 @@
     private static final long serialVersionUID = 88695385615075129L;
 
     private BigInteger s;       // private value
+    private byte[] arrayS;      // private value as a little-endian array
     private ECParameterSpec params;
 
     /**
@@ -85,13 +82,25 @@
             throws InvalidKeyException {
         this.s = s;
         this.params = params;
-        // generate the encoding
+        makeEncoding(s);
+
+    }
+
+    ECPrivateKeyImpl(byte[] s, ECParameterSpec params)
+            throws InvalidKeyException {
+        this.arrayS = s.clone();
+        this.params = params;
+        makeEncoding(s);
+    }
+
+    private void makeEncoding(byte[] s) throws InvalidKeyException {
         algid = new AlgorithmId
-            (AlgorithmId.EC_oid, ECParameters.getAlgorithmParameters(params));
+        (AlgorithmId.EC_oid, ECParameters.getAlgorithmParameters(params));
         try {
             DerOutputStream out = new DerOutputStream();
             out.putInteger(1); // version 1
-            byte[] privBytes = ECUtil.trimZeroes(s.toByteArray());
+            byte[] privBytes = s.clone();
+            ArrayUtil.reverse(privBytes);
             out.putOctetString(privBytes);
             DerValue val =
                 new DerValue(DerValue.tag_Sequence, out.toByteArray());
@@ -102,6 +111,31 @@
         }
     }
 
+    private void makeEncoding(BigInteger s) throws InvalidKeyException {
+        algid = new AlgorithmId
+        (AlgorithmId.EC_oid, ECParameters.getAlgorithmParameters(params));
+        try {
+            byte[] sArr = s.toByteArray();
+            // convert to fixed-length array
+            int numOctets = (params.getOrder().bitLength() + 7) / 8;
+            byte[] sOctets = new byte[numOctets];
+            int inPos = Math.max(sArr.length - sOctets.length, 0);
+            int outPos = Math.max(sOctets.length - sArr.length, 0);
+            int length = Math.min(sArr.length, sOctets.length);
+            System.arraycopy(sArr, inPos, sOctets, outPos, length);
+
+            DerOutputStream out = new DerOutputStream();
+            out.putInteger(1); // version 1
+            out.putOctetString(sOctets);
+            DerValue val =
+                new DerValue(DerValue.tag_Sequence, out.toByteArray());
+            key = val.toByteArray();
+        } catch (IOException exc) {
+            // should never occur
+            throw new InvalidKeyException(exc);
+        }
+    }
+
     // see JCA doc
     public String getAlgorithm() {
         return "EC";
@@ -109,9 +143,26 @@
 
     // see JCA doc
     public BigInteger getS() {
+        if (s == null) {
+            byte[] arrCopy = arrayS.clone();
+            ArrayUtil.reverse(arrCopy);
+            s = new BigInteger(1, arrCopy);
+        }
         return s;
     }
 
+    public byte[] getArrayS() {
+        if (arrayS == null) {
+            byte[] arr = getS().toByteArray();
+            ArrayUtil.reverse(arr);
+            int byteLength = (params.getOrder().bitLength() + 7) / 8;
+            arrayS = new byte[byteLength];
+            int length = Math.min(byteLength, arr.length);
+            System.arraycopy(arr, 0, arrayS, 0, length);
+        }
+        return arrayS.clone();
+    }
+
     // see JCA doc
     public ECParameterSpec getParams() {
         return params;
@@ -133,12 +184,13 @@
                 throw new IOException("Version must be 1");
             }
             byte[] privData = data.getOctetString();
-            s = new BigInteger(1, privData);
+            ArrayUtil.reverse(privData);
+            arrayS = privData;
             while (data.available() != 0) {
                 DerValue value = data.getDerValue();
-                if (value.isContextSpecific((byte)0)) {
+                if (value.isContextSpecific((byte) 0)) {
                     // ignore for now
-                } else if (value.isContextSpecific((byte)1)) {
+                } else if (value.isContextSpecific((byte) 1)) {
                     // ignore for now
                 } else {
                     throw new InvalidKeyException("Unexpected value: " + value);
diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/point/AffinePoint.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/point/AffinePoint.java
new file mode 100644
index 0000000..a8b74bd
--- /dev/null
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/point/AffinePoint.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package sun.security.ec.point;
+
+import sun.security.util.math.ImmutableIntegerModuloP;
+
+import java.util.Objects;
+
+/**
+ * Elliptic curve point represented using affine coordinates (x, y). This class
+ * is not part of the sun.security.ec.point.Point hierarchy because it is not
+ * used to hold intermediate values during point arithmetic, and so it does not
+ * have a mutable form.
+ */
+public class AffinePoint {
+
+    private final ImmutableIntegerModuloP x;
+    private final ImmutableIntegerModuloP y;
+
+    public AffinePoint(ImmutableIntegerModuloP x, ImmutableIntegerModuloP y) {
+        this.x = x;
+        this.y = y;
+    }
+
+    public ImmutableIntegerModuloP getX() {
+        return x;
+    }
+
+    public ImmutableIntegerModuloP getY() {
+        return y;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof AffinePoint)) {
+            return false;
+        }
+        AffinePoint p = (AffinePoint) obj;
+        boolean xEquals = x.asBigInteger().equals(p.x.asBigInteger());
+        boolean yEquals = y.asBigInteger().equals(p.y.asBigInteger());
+        return xEquals && yEquals;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(x, y);
+    }
+
+    @Override
+    public String toString() {
+        return "(" + x.asBigInteger().toString() + "," +
+            y.asBigInteger().toString() + ")";
+    }
+}
diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/point/ImmutablePoint.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/point/ImmutablePoint.java
new file mode 100644
index 0000000..7f6c6b3
--- /dev/null
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/point/ImmutablePoint.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ec.point;
+
+/**
+ * An interface for immutable points on an elliptic curve over a finite field.
+ */
+public interface ImmutablePoint extends Point {
+}
diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/point/MutablePoint.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/point/MutablePoint.java
new file mode 100644
index 0000000..cb714ac
--- /dev/null
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/point/MutablePoint.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ec.point;
+
+/**
+ * An interface for mutable points on an elliptic curve over a finite field.
+ */
+public interface MutablePoint extends Point {
+
+    MutablePoint setValue(AffinePoint p);
+    MutablePoint setValue(Point p);
+    MutablePoint conditionalSet(Point p, int set);
+
+}
diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/point/Point.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/point/Point.java
new file mode 100644
index 0000000..b28bd9d
--- /dev/null
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/point/Point.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ec.point;
+
+import sun.security.util.math.IntegerFieldModuloP;
+
+/**
+ * A base interface for points on an elliptic curve over a finite field.
+ * Implementations may use different representations for points, and this
+ * interface creates a common API for manipulating points. This API has no
+ * methods for point arithmetic, which depends on group structure and curve
+ * parameters in addition to point representation.
+ */
+public interface Point {
+
+    IntegerFieldModuloP getField();
+    AffinePoint asAffine();
+
+    ImmutablePoint fixed();
+    MutablePoint mutable();
+
+}
diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/point/ProjectivePoint.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/point/ProjectivePoint.java
new file mode 100644
index 0000000..587f1c7
--- /dev/null
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/point/ProjectivePoint.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package sun.security.ec.point;
+
+import sun.security.util.math.*;
+
+/**
+ * Elliptic curve point in projective coordinates (X, Y, Z) where
+ * an affine point (x, y) is represented using any (X, Y, Z) s.t.
+ * x = X/Z and y = Y/Z.
+ */
+public abstract class ProjectivePoint
+    <T extends IntegerModuloP> implements Point {
+
+    protected final T x;
+    protected final T y;
+    protected final T z;
+
+    protected ProjectivePoint(T x, T y, T z) {
+
+        this.x = x;
+        this.y = y;
+        this.z = z;
+    }
+
+    @Override
+    public IntegerFieldModuloP getField() {
+        return this.x.getField();
+    }
+
+    @Override
+    public Immutable fixed() {
+        return new Immutable(x.fixed(), y.fixed(), z.fixed());
+    }
+
+    @Override
+    public Mutable mutable() {
+        return new Mutable(x.mutable(), y.mutable(), z.mutable());
+    }
+
+    public T getX() {
+        return x;
+    }
+
+    public T getY() {
+        return y;
+    }
+
+    public T getZ() {
+        return z;
+    }
+
+    public AffinePoint asAffine() {
+        IntegerModuloP zInv = z.multiplicativeInverse();
+        return new AffinePoint(x.multiply(zInv), y.multiply(zInv));
+    }
+
+    public static class Immutable
+        extends ProjectivePoint<ImmutableIntegerModuloP>
+        implements ImmutablePoint {
+
+        public Immutable(ImmutableIntegerModuloP x,
+                         ImmutableIntegerModuloP y,
+                         ImmutableIntegerModuloP z) {
+            super(x, y, z);
+        }
+    }
+
+    public static class Mutable
+        extends ProjectivePoint<MutableIntegerModuloP>
+        implements MutablePoint {
+
+        public Mutable(MutableIntegerModuloP x,
+                       MutableIntegerModuloP y,
+                       MutableIntegerModuloP z) {
+            super(x, y, z);
+        }
+
+        public Mutable(IntegerFieldModuloP field) {
+            super(field.get0().mutable(),
+                field.get0().mutable(),
+                field.get0().mutable());
+        }
+
+        @Override
+        public Mutable conditionalSet(Point p, int set) {
+            if (!(p instanceof ProjectivePoint)) {
+                throw new RuntimeException("Incompatible point");
+            }
+            @SuppressWarnings("unchecked")
+            ProjectivePoint<IntegerModuloP> pp =
+                (ProjectivePoint<IntegerModuloP>) p;
+            return conditionalSet(pp, set);
+        }
+
+        private <T extends IntegerModuloP>
+        Mutable conditionalSet(ProjectivePoint<T> pp, int set) {
+
+            x.conditionalSet(pp.x, set);
+            y.conditionalSet(pp.y, set);
+            z.conditionalSet(pp.z, set);
+
+            return this;
+        }
+
+        @Override
+        public Mutable setValue(AffinePoint p) {
+            x.setValue(p.getX());
+            y.setValue(p.getY());
+            z.setValue(p.getX().getField().get1());
+
+            return this;
+        }
+
+        @Override
+        public Mutable setValue(Point p) {
+            if (!(p instanceof ProjectivePoint)) {
+                throw new RuntimeException("Incompatible point");
+            }
+            @SuppressWarnings("unchecked")
+            ProjectivePoint<IntegerModuloP> pp =
+                (ProjectivePoint<IntegerModuloP>) p;
+            return setValue(pp);
+        }
+
+        private <T extends IntegerModuloP>
+        Mutable setValue(ProjectivePoint<T> pp) {
+
+            x.setValue(pp.x);
+            y.setValue(pp.y);
+            z.setValue(pp.z);
+
+            return this;
+        }
+
+    }
+
+}