changeset 8848:7fdd0d6ef2d3

8208698: Improved ECC Implementation Summary: New implementation of ECDH and ECDSA for some prime-order curves Reviewed-by: ascarpino, andrew Contributed-by: David Alvarez <alvdavi@amazon.com>
author andrew
date Sun, 14 Jul 2019 01:30:40 +0100
parents ad39fe0f5f3f
children dec76917e3f9
files src/share/classes/sun/security/ec/ECDHKeyAgreement.java src/share/classes/sun/security/ec/ECDSAOperations.java src/share/classes/sun/security/ec/ECDSASignature.java src/share/classes/sun/security/ec/ECKeyPairGenerator.java src/share/classes/sun/security/ec/ECOperations.java src/share/classes/sun/security/ec/ECPrivateKeyImpl.java src/share/classes/sun/security/ec/point/AffinePoint.java src/share/classes/sun/security/ec/point/ImmutablePoint.java src/share/classes/sun/security/ec/point/MutablePoint.java src/share/classes/sun/security/ec/point/Point.java src/share/classes/sun/security/ec/point/ProjectivePoint.java src/share/classes/sun/security/util/Function.java src/share/classes/sun/security/util/Optional.java src/share/classes/sun/security/util/Supplier.java
diffstat 14 files changed, 1808 insertions(+), 96 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/sun/security/ec/ECDHKeyAgreement.java	Sat Jul 13 20:39:33 2019 +0100
+++ b/src/share/classes/sun/security/ec/ECDHKeyAgreement.java	Sun Jul 14 01:30:40 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 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,6 +25,7 @@
 
 package sun.security.ec;
 
+import java.math.*;
 import java.security.*;
 import java.security.interfaces.*;
 import java.security.spec.*;
@@ -32,7 +33,12 @@
 import javax.crypto.*;
 import javax.crypto.spec.*;
 
+import sun.security.util.ArrayUtil;
 import sun.security.util.ECUtil;
+import sun.security.util.Optional;
+import sun.security.util.Supplier;
+import sun.security.util.math.*;
+import sun.security.ec.point.*;
 
 /**
  * KeyAgreement implementation for ECDH.
@@ -44,8 +50,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 +71,7 @@
                         ("Key must be instance of PrivateKey");
         }
         privateKey = (ECPrivateKey) ECKeyFactory.toECKey(key);
-        publicValue = null;
+        publicKey = null;
     }
 
     // see JCE spec
@@ -87,7 +93,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 +105,73 @@
                 ("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);
+        return resultOpt.orElseGet(new Supplier<byte[]>() {
+            @Override
+            public byte[] get() { return deriveKeyNative(privateKey, publicKey); }
+        });
     }
 
     // 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.
      *
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/security/ec/ECDSAOperations.java	Sun Jul 14 01:30:40 2019 +0100
@@ -0,0 +1,206 @@
+/*
+ * 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.Function;
+import sun.security.util.Optional;
+import sun.security.util.math.*;
+import static sun.security.ec.ECOperations.IntermediateValueException;
+
+import java.security.ProviderException;
+import java.security.spec.*;
+
+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(final ECParameterSpec ecParams) {
+        Optional<ECOperations> curveOps =
+            ECOperations.forParameters(ecParams);
+        return curveOps.map(new Function<ECOperations, ECDSAOperations>() {
+            @Override
+            public ECDSAOperations apply(ECOperations ops) {
+                return 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;
+
+    }
+
+}
--- a/src/share/classes/sun/security/ec/ECDSASignature.java	Sat Jul 13 20:39:33 2019 +0100
+++ b/src/share/classes/sun/security/ec/ECDSASignature.java	Sun Jul 14 01:30:40 2019 +0100
@@ -35,6 +35,7 @@
 
 import sun.security.jca.JCAUtil;
 import sun.security.util.*;
+import static sun.security.ec.ECOperations.IntermediateValueException;
 
 /**
  * ECDSA signature implementation. This class currently supports the
@@ -113,7 +114,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;
@@ -138,7 +139,7 @@
         }
 
         @Override
-        protected void resetDigest(){
+        protected void resetDigest() {
             offset = 0;
         }
 
@@ -167,7 +168,7 @@
     // Nested class for SHA224withECDSA signatures
     public static final class SHA224 extends ECDSASignature {
         public SHA224() {
-           super("SHA-224");
+            super("SHA-224");
         }
     }
 
@@ -195,7 +196,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
@@ -207,14 +208,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
@@ -254,7 +255,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;
     }
@@ -271,20 +272,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
@@ -293,14 +341,33 @@
         timingArgument |= 1;
 
         try {
-            return encodeSignature(
-                signDigest(getDigestValue(), s, encodedParams, seed,
-                    timingArgument));
+            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);
+        }
+
+        return encodeSignature(sig);
+    }
+
     // verify the data and return the result. See JCA doc
     @Override
     protected boolean engineVerify(byte[] signature) throws SignatureException {
@@ -311,7 +378,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());
         }
@@ -330,7 +397,7 @@
     @Override
     @Deprecated
     protected void engineSetParameter(String param, Object value)
-            throws InvalidParameterException {
+    throws InvalidParameterException {
         throw new UnsupportedOperationException("setParameter() not supported");
     }
 
@@ -338,7 +405,7 @@
     @Override
     @Deprecated
     protected Object engineGetParameter(String param)
-            throws InvalidParameterException {
+    throws InvalidParameterException {
         throw new UnsupportedOperationException("getParameter() not supported");
     }
 
@@ -358,7 +425,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();
 
@@ -391,9 +458,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) {
@@ -433,13 +500,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).
@@ -448,6 +515,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;
 }
--- a/src/share/classes/sun/security/ec/ECKeyPairGenerator.java	Sat Jul 13 20:39:33 2019 +0100
+++ b/src/share/classes/sun/security/ec/ECKeyPairGenerator.java	Sun Jul 14 01:30:40 2019 +0100
@@ -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
@@ -35,12 +35,13 @@
 import java.security.spec.InvalidParameterSpecException;
 
 import sun.security.ec.NamedCurve;
-import sun.security.ec.ECParameters;
-import sun.security.ec.ECPrivateKeyImpl;
-import sun.security.ec.ECPublicKeyImpl;
 import sun.security.jca.JCAUtil;
 import sun.security.util.ECUtil;
+import sun.security.util.Optional;
+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.
@@ -91,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(
@@ -113,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;
     }
 
@@ -142,40 +142,97 @@
     @Override
     public KeyPair generateKeyPair() {
 
-        byte[] encodedParams =
-            ECUtil.encodeECParameterSpec(null, (ECParameterSpec)params);
+        if (random == null) {
+            random = JCAUtil.getSecureRandom();
+        }
+
+        try {
+            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];
-        if (random == null) {
-            random = JCAUtil.getSecureRandom();
-        }
         random.nextBytes(seed);
+        Object[] keyBytes = generateECKeyPair(keySize, encodedParams, seed);
 
-        try {
+        // 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]);
 
-            Object[] keyBytes = generateECKeyPair(keySize, encodedParams, seed);
+        PrivateKey privateKey = new ECPrivateKeyImpl(s, ecParams);
 
-            // The 'params' object supplied above is equivalent to the native
-            // one so there is no need to fetch it.
+        // 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);
 
-            // 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);
-        }
+        return new KeyPair(publicKey, privateKey);
     }
 
     private void checkKeySize(int keySize) throws InvalidParameterException {
@@ -192,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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/security/ec/ECOperations.java	Sun Jul 14 01:30:40 2019 +0100
@@ -0,0 +1,499 @@
+/*
+ * 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.Optional;
+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.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/*
+ * 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;
+
+    static final Map<BigInteger, IntegerFieldModuloP> orderFields;
+
+    static {
+        Map<BigInteger, IntegerFieldModuloP> map = new HashMap<>();
+        map.put(IntegerPolynomialP256.MODULUS, new IntegerPolynomialP256());
+        map.put(IntegerPolynomialP384.MODULUS, new IntegerPolynomialP384());
+        map.put(IntegerPolynomialP521.MODULUS, new IntegerPolynomialP521());
+        fields = Collections.unmodifiableMap(map);
+        map = new HashMap<>();
+        map.put(P256OrderField.MODULUS, new P256OrderField());
+        map.put(P384OrderField.MODULUS, new P384OrderField());
+        map.put(P521OrderField.MODULUS, new P521OrderField());
+        orderFields = Collections.unmodifiableMap(map);
+    }
+
+    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);
+
+    }
+}
--- a/src/share/classes/sun/security/ec/ECPrivateKeyImpl.java	Sat Jul 13 20:39:33 2019 +0100
+++ b/src/share/classes/sun/security/ec/ECPrivateKeyImpl.java	Sun Jul 14 01:30:40 2019 +0100
@@ -64,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;
 
     /**
@@ -81,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());
@@ -98,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";
@@ -105,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;
@@ -129,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);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/security/ec/point/AffinePoint.java	Sun Jul 14 01:30:40 2019 +0100
@@ -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() + ")";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/security/ec/point/ImmutablePoint.java	Sun Jul 14 01:30:40 2019 +0100
@@ -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 {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/security/ec/point/MutablePoint.java	Sun Jul 14 01:30:40 2019 +0100
@@ -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);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/security/ec/point/Point.java	Sun Jul 14 01:30:40 2019 +0100
@@ -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();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/security/ec/point/ProjectivePoint.java	Sun Jul 14 01:30:40 2019 +0100
@@ -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;
+        }
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/security/util/Function.java	Sun Jul 14 01:30:40 2019 +0100
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2010, 2013, 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.util;
+
+/**
+ * Represents a function that accepts one argument and produces a result.
+ *
+ * @param <T> the type of the input to the function
+ * @param <R> the type of the result of the function
+ *
+ * @since 1.8
+ */
+public interface Function<T, R> {
+
+    /**
+     * Applies this function to the given argument.
+     *
+     * @param t the function argument
+     * @return the function result
+     */
+    R apply(T t);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/security/util/Optional.java	Sun Jul 14 01:30:40 2019 +0100
@@ -0,0 +1,271 @@
+/*
+ * Copyright (c) 2012, 2013, 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.util;
+
+import java.util.Objects;
+import java.util.NoSuchElementException;
+
+/**
+ * A container object which may or may not contain a non-null value.
+ * If a value is present, {@code isPresent()} will return {@code true} and
+ * {@code get()} will return the value.
+ *
+ * <p>Additional methods that depend on the presence or absence of a contained
+ * value are provided, such as {@link #orElse(java.lang.Object) orElse()}
+ * (return a default value if value not present) and
+ * {@link #ifPresent(java.util.function.Consumer) ifPresent()} (execute a block
+ * of code if the value is present).
+ *
+ * <p>This is a <a href="../lang/doc-files/ValueBased.html">value-based</a>
+ * class; use of identity-sensitive operations (including reference equality
+ * ({@code ==}), identity hash code, or synchronization) on instances of
+ * {@code Optional} may have unpredictable results and should be avoided.
+ *
+ * @since 1.8
+ */
+public final class Optional<T> {
+    /**
+     * Common instance for {@code empty()}.
+     */
+    private static final Optional<?> EMPTY = new Optional<>();
+
+    /**
+     * If non-null, the value; if null, indicates no value is present
+     */
+    private final T value;
+
+    /**
+     * Constructs an empty instance.
+     *
+     * @implNote Generally only one empty instance, {@link Optional#EMPTY},
+     * should exist per VM.
+     */
+    private Optional() {
+        this.value = null;
+    }
+
+    /**
+     * Returns an empty {@code Optional} instance.  No value is present for this
+     * {@code Optional}.
+     *
+     * @apiNote
+     * Though it may be tempting to do so, avoid testing if an object is empty
+     * by comparing with {@code ==} against instances returned by
+     * {@code Optional.empty()}.  There is no guarantee that it is a singleton.
+     * Instead, use {@link #isPresent()}.
+     *
+     * @param <T> The type of the non-existent value
+     * @return an empty {@code Optional}
+     */
+    public static<T> Optional<T> empty() {
+        @SuppressWarnings("unchecked")
+        Optional<T> t = (Optional<T>) EMPTY;
+        return t;
+    }
+
+
+    /**
+     * Constructs an instance with the described value.
+     *
+     * @param value the non-{@code null} value to describe
+     * @throws NullPointerException if value is {@code null}
+     */
+    private Optional(T value) {
+        this.value = Objects.requireNonNull(value);
+    }
+
+    /**
+     * Returns an {@code Optional} describing the given non-{@code null}
+     * value.
+     *
+     * @param value the value to describe, which must be non-{@code null}
+     * @param <T> the type of the value
+     * @return an {@code Optional} with the value present
+     * @throws NullPointerException if value is {@code null}
+     */
+    public static <T> Optional<T> of(T value) {
+        return new Optional<>(value);
+    }
+
+    /**
+     * Returns an {@code Optional} describing the specified value, if non-null,
+     * otherwise returns an empty {@code Optional}.
+     *
+     * @param <T> the class of the value
+     * @param value the possibly-null value to describe
+     * @return an {@code Optional} with a present value if the specified value
+     * is non-null, otherwise an empty {@code Optional}
+     */
+    public static <T> Optional<T> ofNullable(T value) {
+        return value == null ? new Optional<T>() : of(value);
+    }
+
+    /**
+     * If a value is present, returns the value, otherwise throws
+     * {@code NoSuchElementException}.
+     *
+     * @apiNote
+     * The preferred alternative to this method is {@link #orElseThrow()}.
+     *
+     * @return the non-{@code null} value described by this {@code Optional}
+     * @throws NoSuchElementException if no value is present
+     */
+    public T get() {
+        if (value == null) {
+            throw new NoSuchElementException("No value present");
+        }
+        return value;
+    }
+
+    /**
+     * If a value is present, returns {@code true}, otherwise {@code false}.
+     *
+     * @return {@code true} if a value is present, otherwise {@code false}
+     */
+    public boolean isPresent() {
+        return value != null;
+    }
+
+    /**
+     * If a value is  not present, returns {@code true}, otherwise
+     * {@code false}.
+     *
+     * @return  {@code true} if a value is not present, otherwise {@code false}
+     * @since   11
+     */
+    public boolean isEmpty() {
+        return value == null;
+    }
+
+    /**
+     * If a value is present, apply the provided mapping function to it,
+     * and if the result is non-null, return an {@code Optional} describing the
+     * result.  Otherwise return an empty {@code Optional}.
+     *
+     * @apiNote This method supports post-processing on optional values, without
+     * the need to explicitly check for a return status.  For example, the
+     * following code traverses a stream of file names, selects one that has
+     * not yet been processed, and then opens that file, returning an
+     * {@code Optional<FileInputStream>}:
+     *
+     * <pre>{@code
+     *     Optional<FileInputStream> fis =
+     *         names.stream().filter(name -> !isProcessedYet(name))
+     *                       .findFirst()
+     *                       .map(name -> new FileInputStream(name));
+     * }</pre>
+     *
+     * Here, {@code findFirst} returns an {@code Optional<String>}, and then
+     * {@code map} returns an {@code Optional<FileInputStream>} for the desired
+     * file if one exists.
+     *
+     * @param <U> The type of the result of the mapping function
+     * @param mapper a mapping function to apply to the value, if present
+     * @return an {@code Optional} describing the result of applying a mapping
+     * function to the value of this {@code Optional}, if a value is present,
+     * otherwise an empty {@code Optional}
+     * @throws NullPointerException if the mapping function is null
+     */
+    public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
+        Objects.requireNonNull(mapper);
+        if (!isPresent())
+            return empty();
+        else {
+            return Optional.ofNullable(mapper.apply(value));
+        }
+    }
+
+    /**
+     * Return the value if present, otherwise invoke {@code other} and return
+     * the result of that invocation.
+     *
+     * @param other a {@code Supplier} whose result is returned if no value
+     * is present
+     * @return the value if present otherwise the result of {@code other.get()}
+     * @throws NullPointerException if value is not present and {@code other} is
+     * null
+     */
+    public T orElseGet(Supplier<? extends T> other) {
+        return value != null ? value : other.get();
+    }
+
+    /**
+     * Indicates whether some other object is "equal to" this {@code Optional}.
+     * The other object is considered equal if:
+     * <ul>
+     * <li>it is also an {@code Optional} and;
+     * <li>both instances have no value present or;
+     * <li>the present values are "equal to" each other via {@code equals()}.
+     * </ul>
+     *
+     * @param obj an object to be tested for equality
+     * @return {@code true} if the other object is "equal to" this object
+     *         otherwise {@code false}
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (!(obj instanceof Optional)) {
+            return false;
+        }
+
+        Optional<?> other = (Optional<?>) obj;
+        return Objects.equals(value, other.value);
+    }
+
+    /**
+     * Returns the hash code of the value, if present, otherwise {@code 0}
+     * (zero) if no value is present.
+     *
+     * @return hash code value of the present value or {@code 0} if no value is
+     *         present
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(value);
+    }
+
+    /**
+     * Returns a non-empty string representation of this {@code Optional}
+     * suitable for debugging.  The exact presentation format is unspecified and
+     * may vary between implementations and versions.
+     *
+     * @implSpec
+     * If a value is present the result must include its string representation
+     * in the result.  Empty and present {@code Optional}s must be unambiguously
+     * differentiable.
+     *
+     * @return the string representation of this instance
+     */
+    @Override
+    public String toString() {
+        return value != null
+            ? String.format("Optional[%s]", value)
+            : "Optional.empty";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/security/util/Supplier.java	Sun Jul 14 01:30:40 2019 +0100
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2012, 2013, 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.util;
+
+/**
+ * Represents a supplier of results.
+ *
+ * <p>There is no requirement that a new or distinct result be returned each
+ * time the supplier is invoked.
+ *
+ * <p>This is a <a href="package-summary.html">functional interface</a>
+ * whose functional method is {@link #get()}.
+ *
+ * @param <T> the type of results supplied by this supplier
+ *
+ * @since 1.8
+ */
+public interface Supplier<T> {
+
+    /**
+     * Gets a result.
+     *
+     * @return a result
+     */
+    T get();
+}