changeset 59330:0050836e5934

8166597: Crypto support for the EdDSA Signature Algorithm Reviewed-by: weijun, mullan, wetmore
author ascarpino
date Mon, 18 May 2020 09:42:52 -0700
parents 81d1f8a9c43d
children d1ad49003c3d
files make/jdk/src/classes/build/tools/intpoly/FieldGen.java src/java.base/share/classes/java/security/interfaces/EdECKey.java src/java.base/share/classes/java/security/interfaces/EdECPrivateKey.java src/java.base/share/classes/java/security/interfaces/EdECPublicKey.java src/java.base/share/classes/java/security/spec/EdDSAParameterSpec.java src/java.base/share/classes/java/security/spec/EdECPoint.java src/java.base/share/classes/java/security/spec/EdECPrivateKeySpec.java src/java.base/share/classes/java/security/spec/EdECPublicKeySpec.java src/java.base/share/classes/java/security/spec/NamedParameterSpec.java src/java.base/share/classes/module-info.java src/java.base/share/classes/sun/security/pkcs/PKCS7.java src/java.base/share/classes/sun/security/provider/SHA3.java src/java.base/share/classes/sun/security/provider/SHAKE256.java src/java.base/share/classes/sun/security/tools/keytool/Main.java src/java.base/share/classes/sun/security/util/KeyUtil.java src/java.base/share/classes/sun/security/util/SecurityProviderConstants.java src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomial.java src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomial1305.java src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomial25519.java src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomial448.java src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomialModBinP.java src/java.base/share/classes/sun/security/x509/AlgorithmId.java src/jdk.crypto.ec/share/classes/sun/security/ec/ParametersMap.java src/jdk.crypto.ec/share/classes/sun/security/ec/SunEC.java src/jdk.crypto.ec/share/classes/sun/security/ec/XECParameters.java src/jdk.crypto.ec/share/classes/sun/security/ec/ed/Ed25519Operations.java src/jdk.crypto.ec/share/classes/sun/security/ec/ed/Ed448Operations.java src/jdk.crypto.ec/share/classes/sun/security/ec/ed/EdDSAAlgorithmParameters.java src/jdk.crypto.ec/share/classes/sun/security/ec/ed/EdDSAKeyFactory.java src/jdk.crypto.ec/share/classes/sun/security/ec/ed/EdDSAKeyPairGenerator.java src/jdk.crypto.ec/share/classes/sun/security/ec/ed/EdDSAOperations.java src/jdk.crypto.ec/share/classes/sun/security/ec/ed/EdDSAParameters.java src/jdk.crypto.ec/share/classes/sun/security/ec/ed/EdDSAPrivateKeyImpl.java src/jdk.crypto.ec/share/classes/sun/security/ec/ed/EdDSAPublicKeyImpl.java src/jdk.crypto.ec/share/classes/sun/security/ec/ed/EdDSASignature.java src/jdk.crypto.ec/share/classes/sun/security/ec/ed/EdECOperations.java src/jdk.crypto.ec/share/classes/sun/security/ec/point/ExtendedHomogeneousPoint.java src/jdk.crypto.ec/share/classes/sun/security/ec/point/Point.java src/jdk.crypto.ec/share/classes/sun/security/ec/point/ProjectivePoint.java test/jdk/sun/security/ec/ed/EdECKeyFormat.java test/jdk/sun/security/ec/ed/TestEdDSA.java test/jdk/sun/security/ec/ed/TestEdOps.java test/jdk/sun/security/ec/xec/TestXECOps.java test/jdk/sun/security/ec/xec/XECIterative.java test/jdk/sun/security/util/math/TestIntegerModuloP.java test/lib/jdk/test/lib/Convert.java test/micro/org/openjdk/bench/javax/crypto/full/SignatureBench.java
diffstat 47 files changed, 4697 insertions(+), 155 deletions(-) [+]
line wrap: on
line diff
--- a/make/jdk/src/classes/build/tools/intpoly/FieldGen.java	Mon May 18 09:28:06 2020 -0700
+++ b/make/jdk/src/classes/build/tools/intpoly/FieldGen.java	Mon May 18 09:42:52 2020 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2020, 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
@@ -21,7 +21,6 @@
  * questions.
  */
 
-
 /*
  * This file is used to generated optimized finite field implementations.
  */
@@ -170,6 +169,19 @@
             o521crSequence(19), orderFieldSmallCrSequence(19)
     );
 
+    static FieldParams O25519 = new FieldParams(
+            "Curve25519OrderField", 26, 10, 1, 252,
+            "1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed",
+            orderFieldCrSequence(10), orderFieldSmallCrSequence(10)
+    );
+
+    static FieldParams O448 = new FieldParams(
+            "Curve448OrderField", 28, 16, 1, 446,
+            "3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3",
+            //"ffffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3",
+            orderFieldCrSequence(16), orderFieldSmallCrSequence(16)
+    );
+
     private static List<CarryReduce> o521crSequence(int numLimbs) {
 
         // split the full reduce in half, with a carry in between
@@ -212,7 +224,8 @@
     }
 
     static final FieldParams[] ALL_FIELDS = {
-            P256, P384, P521, O256, O384, O521,
+            Curve25519, Curve448,
+            P256, P384, P521, O256, O384, O521, O25519, O448
     };
 
     public static class Term {
@@ -322,6 +335,11 @@
         private Iterable<Term> buildTerms(BigInteger sub) {
             // split a large subtrahend into smaller terms
             // that are aligned with limbs
+            boolean negate = false;
+            if (sub.compareTo(BigInteger.ZERO) < 0) {
+                negate = true;
+                sub = sub.negate();
+            }
             List<Term> result = new ArrayList<Term>();
             BigInteger mod = BigInteger.valueOf(1 << bitsPerLimb);
             int termIndex = 0;
@@ -332,6 +350,9 @@
                     coef = coef - (1 << bitsPerLimb);
                     plusOne = true;
                 }
+                if (negate) {
+                    coef = 0 - coef;
+                }
                 if (coef != 0) {
                     int pow = termIndex * bitsPerLimb;
                     result.add(new Term(pow, -coef));
@@ -619,6 +640,14 @@
         result.appendLine();
         result.appendLine("}");
 
+        StringBuilder coqTerms = new StringBuilder("//");
+        for (Term t : params.getTerms()) {
+            coqTerms.append("(" + t.getPower() + "%nat,");
+            coqTerms.append(t.getCoefficient() + ")::");
+        }
+        coqTerms.append("nil.");
+        result.appendLine(coqTerms.toString());
+
         result.appendLine("private static BigInteger evaluateModulus() {");
         result.incrIndent();
         result.appendLine("BigInteger result = BigInteger.valueOf(2).pow("
@@ -651,6 +680,41 @@
         result.appendLine("}");
 
         result.appendLine("@Override");
+        result.appendLine("protected void reduceIn(long[] limbs, long v, int i) {");
+        result.incrIndent();
+        String c = "v";
+        for (Term t : params.getTerms()) {
+            int reduceBits = params.getPower() - t.getPower();
+            int coefficient = -1 * t.getCoefficient();
+
+            String x = coefficient + " * " + c;
+            String accOp = "+=";
+            String temp = null;
+            if (coefficient == 1) {
+                x = c;
+            } else if (coefficient == -1) {
+                x = c;
+                accOp = "-=";
+            } else {
+                temp = result.getTemporary("long", x);
+                x = temp;
+            }
+
+            if (reduceBits % params.getBitsPerLimb() == 0) {
+                int pos = reduceBits / params.getBitsPerLimb();
+                result.appendLine("limbs[i - " + pos + "] " + accOp + " " + x + ";");
+            } else {
+                int secondPos = reduceBits / params.getBitsPerLimb();
+                int bitOffset = (secondPos + 1) * params.getBitsPerLimb() - reduceBits;
+                int rightBitOffset = params.getBitsPerLimb() - bitOffset;
+                result.appendLine("limbs[i - " + (secondPos + 1) + "] " + accOp + " (" + x + " << " + bitOffset + ") & LIMB_MASK;");
+                result.appendLine("limbs[i - " + secondPos + "] " + accOp + " " + x + " >> " + rightBitOffset + ";");
+            }
+        }
+        result.decrIndent();
+        result.appendLine("}");
+
+        result.appendLine("@Override");
         result.appendLine("protected void finalCarryReduceLast(long[] limbs) {");
         result.incrIndent();
         int extraBits = params.getBitsPerLimb() * params.getNumLimbs()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/security/interfaces/EdECKey.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020, 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 java.security.interfaces;
+
+import java.security.spec.NamedParameterSpec;
+
+/**
+ * An interface for an elliptic curve public/private key as defined by
+ * <a href="https://tools.ietf.org/html/rfc8032">RFC 8032: Edwards-Curve
+ * Digital Signature Algorithm (EdDSA)</a>. These keys are distinct from the
+ * keys represented by {@code ECKey}, and they are intended for use with
+ * algorithms based on RFC 8032 such as the EdDSA {@code Signature} algorithm.
+ * This interface allows access to the algorithm parameters associated with
+ * the key.
+ *
+ * @since 15
+ */
+public interface EdECKey {
+    /**
+     * Returns the algorithm parameters associated with the key.
+     *
+     * @return the associated algorithm parameters.
+     */
+    NamedParameterSpec getParams();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/security/interfaces/EdECPrivateKey.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2020, 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 java.security.interfaces;
+
+import java.security.PrivateKey;
+import java.util.Optional;
+
+/**
+ * An interface for an elliptic curve private key as defined by
+ * <a href="https://tools.ietf.org/html/rfc8032">RFC 8032: Edwards-Curve
+ * Digital Signature Algorithm (EdDSA)</a>. These keys are distinct from the
+ * keys represented by {@code ECPrivateKey}, and they are intended for use
+ * with algorithms based on RFC 8032 such as the EdDSA {@code Signature}
+ * algorithm.
+ * <p>
+ * An Edwards-Curve private key is a bit string. This interface only supports bit
+ * string lengths that are a multiple of 8, and the key is represented using
+ * a byte array.
+ *
+ * @since 15
+ */
+public interface EdECPrivateKey extends EdECKey, PrivateKey {
+
+    /**
+     * Get a copy of the byte array representing the private key. This method
+     * may return an empty {@code Optional} if the implementation is not
+     * willing to produce the private key value.
+     *
+     * @return an {@code Optional} containing the private key byte array.
+     * If the key is not available, then an empty {@code Optional}.
+     */
+    Optional<byte[]> getBytes();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/security/interfaces/EdECPublicKey.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020, 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 java.security.interfaces;
+
+import java.security.PublicKey;
+import java.security.spec.EdECPoint;
+
+/**
+ * An interface for an elliptic curve public key as defined by
+ * <a href="https://tools.ietf.org/html/rfc8032">RFC 8032: Edwards-Curve
+ * Digital Signature Algorithm (EdDSA)</a>. These keys are distinct from the
+ * keys represented by {@code ECPublicKey}, and they are intended for use with
+ * algorithms based on RFC 8032 such as the EdDSA {@code Signature} algorithm.
+ * <p>
+ * An Edwards-Curve public key is a point on the curve, which is represented using an
+ * EdECPoint.
+ *
+ * @since 15
+ */
+public interface EdECPublicKey extends EdECKey, PublicKey {
+
+    /**
+     * Get the point representing the public key.
+     *
+     * @return the {@code EdECPoint} representing the public key.
+     */
+    EdECPoint getPoint();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/security/spec/EdDSAParameterSpec.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2020, 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 java.security.spec;
+
+import java.security.InvalidParameterException;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * A class used to specify EdDSA signature and verification parameters. All
+ * algorithm modes in <a href="https://tools.ietf.org/html/rfc8032">RFC 8032:
+ * Edwards-Curve Digital Signature Algorithm (EdDSA)</a> can be specified using
+ * combinations of the settings in this class.
+ *
+ * <ul>
+ * <li>If prehash is true, then the mode is Ed25519ph or Ed448ph</li>
+ * <li>Otherwise, if a context is present, the mode is Ed25519ctx or Ed448</li>
+ * <li>Otherwise, the mode is Ed25519 or Ed448</li>
+ * </ul>
+ *
+ * @since 15
+ */
+
+public class EdDSAParameterSpec implements AlgorithmParameterSpec {
+
+    private final boolean prehash;
+    private final byte[] context;
+
+    /**
+     * Construct an {@code EdDSAParameterSpec} by specifying whether the prehash mode
+     * is used. No context is provided so this constructor specifies a mode
+     * in which the context is null. Note that this mode may be different
+     * than the mode in which an empty array is used as the context.
+     *
+     * @param prehash whether the prehash mode is specified.
+     */
+    public EdDSAParameterSpec(boolean prehash) {
+        this.prehash = prehash;
+        this.context = null;
+    }
+
+    /**
+     * Construct an {@code EdDSAParameterSpec} by specifying a context and whether the
+     * prehash mode is used. The context may not be null, but it may be an
+     * empty array. The mode used when the context is an empty array may not be
+     * the same as the mode used when the context is absent.
+     *
+     * @param prehash whether the prehash mode is specified.
+     * @param context the context is copied and bound to the signature.
+     * @throws NullPointerException if context is null.
+     * @throws InvalidParameterException if context length is greater than 255.
+     */
+    public EdDSAParameterSpec(boolean prehash, byte[] context) {
+
+        Objects.requireNonNull(context, "context may not be null");
+        if (context.length > 255) {
+            throw new InvalidParameterException("context length cannot be " +
+                "greater than 255");
+        }
+
+        this.prehash = prehash;
+        this.context = context.clone();
+    }
+
+    /**
+     * Get whether the prehash mode is specified.
+     *
+     * @return whether the prehash mode is specified.
+     */
+    public boolean isPrehash() {
+        return prehash;
+    }
+
+    /**
+     * Get the context that the signature will use.
+     *
+     * @return {@code Optional} contains a copy of the context or empty
+     * if context is null.
+     */
+    public Optional<byte[]> getContext() {
+        if (context == null) {
+            return Optional.empty();
+        } else {
+            return Optional.of(context.clone());
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/security/spec/EdECPoint.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2020, 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 java.security.spec;
+
+import java.math.BigInteger;
+import java.util.Objects;
+
+/**
+ * An elliptic curve point used to specify keys as defined by
+ * <a href="https://tools.ietf.org/html/rfc8032">RFC 8032: Edwards-Curve
+ * Digital Signature Algorithm (EdDSA)</a>. These points are distinct from the
+ * points represented by {@code ECPoint}, and they are intended for use with
+ * algorithms based on RFC 8032 such as the EdDSA {@code Signature} algorithm.
+ * <p>
+ * An EdEC point is specified by its y-coordinate value and a boolean that
+ * indicates whether the x-coordinate is odd. The y-coordinate is an
+ * element of the field of integers modulo some value p that is determined by
+ * the algorithm parameters. This field element is represented by a
+ * {@code BigInteger}, and implementations that consume objects of this class
+ * may reject integer values which are not in the range [0, p).
+ *
+ * @since 15
+ */
+
+public final class EdECPoint {
+
+    private final boolean xOdd;
+    private final BigInteger y;
+
+    /**
+     * Construct an EdECPoint.
+     *
+     * @param xOdd whether the x-coordinate is odd.
+     * @param y the y-coordinate, represented using a {@code BigInteger}.
+     *
+     * @throws NullPointerException if {@code y} is null.
+     */
+    public EdECPoint(boolean xOdd, BigInteger y) {
+
+        Objects.requireNonNull(y, "y must not be null");
+
+        this.xOdd = xOdd;
+        this.y = y;
+    }
+
+    /**
+     * Get whether the x-coordinate of the point is odd.
+     *
+     * @return a boolean indicating whether the x-coordinate is odd.
+     */
+    public boolean isXOdd() {
+        return xOdd;
+    }
+
+    /**
+     * Get the y-coordinate of the point.
+     *
+     * @return the y-coordinate, represented using a {@code BigInteger}.
+     */
+    public BigInteger getY() {
+        return y;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/security/spec/EdECPrivateKeySpec.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020, 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 java.security.spec;
+
+import java.util.Objects;
+
+/**
+ * A class representing elliptic curve private keys as defined in
+ * <a href="https://tools.ietf.org/html/rfc8032">RFC 8032: Edwards-Curve
+ * Digital Signature Algorithm (EdDSA)</a>, including the curve and other
+ * algorithm parameters. The private key is a bit string represented using
+ * a byte array. This class only supports bit string lengths that are a
+ * multiple of 8.
+ *
+ * @since 15
+ */
+public final class EdECPrivateKeySpec implements KeySpec {
+
+    private final NamedParameterSpec params;
+    private final byte[] bytes;
+
+    /**
+     * Construct a private key spec using the supplied parameters and
+     * bit string.
+     *
+     * @param params the algorithm parameters.
+     * @param bytes the key as a byte array. This array is copied
+     *              to protect against subsequent modification.
+     *
+     * @throws NullPointerException if {@code params} or {@code bytes}
+     *                              is null.
+     */
+    public EdECPrivateKeySpec(NamedParameterSpec params, byte[] bytes) {
+        Objects.requireNonNull(params, "params must not be null");
+        Objects.requireNonNull(bytes, "bytes must not be null");
+
+        this.params = params;
+        this.bytes = bytes.clone();
+    }
+
+    /**
+     * Get the algorithm parameters that define the curve and other settings.
+     *
+     * @return the algorithm parameters.
+     */
+    public NamedParameterSpec getParams() {
+        return params;
+    }
+
+    /**
+     * Get the byte array representing the private key. A new copy of the array
+     * is returned each time this method is called.
+     *
+     * @return the private key as a byte array.
+     */
+    public byte[] getBytes() {
+        return bytes.clone();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/security/spec/EdECPublicKeySpec.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2020, 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 java.security.spec;
+
+import java.util.Objects;
+
+/**
+ * A class representing elliptic curve public keys as defined in
+ * <a href="https://tools.ietf.org/html/rfc8032">RFC 8032: Edwards-Curve
+ * Digital Signature Algorithm (EdDSA)</a>, including the curve and other
+ * algorithm parameters. The public key is a point on the curve, which is
+ * represented using an {@code EdECPoint}.
+ *
+ * @since 15
+ */
+public final class EdECPublicKeySpec implements KeySpec {
+
+    private final NamedParameterSpec params;
+    private final EdECPoint point;
+
+    /**
+     * Construct a public key spec using the supplied parameters and
+     * point.
+     *
+     * @param params the algorithm parameters.
+     * @param point the point representing the public key.
+     *
+     * @throws NullPointerException if {@code params} or {@code point}
+     *                              is null.
+     */
+    public EdECPublicKeySpec(NamedParameterSpec params, EdECPoint point) {
+        Objects.requireNonNull(params, "params must not be null");
+        Objects.requireNonNull(point, "point must not be null");
+
+        this.params = params;
+        this.point = point;
+    }
+
+    /**
+     * Get the algorithm parameters that define the curve and other settings.
+     *
+     * @return the parameters.
+     */
+    public NamedParameterSpec getParams() {
+        return params;
+    }
+
+    /**
+     * Get the point representing the public key.
+     *
+     * @return the {@code EdECPoint} representing the public key.
+     */
+    public EdECPoint getPoint() {
+        return point;
+    }
+}
--- a/src/java.base/share/classes/java/security/spec/NamedParameterSpec.java	Mon May 18 09:28:06 2020 -0700
+++ b/src/java.base/share/classes/java/security/spec/NamedParameterSpec.java	Mon May 18 09:42:52 2020 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2020, 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
@@ -52,6 +52,22 @@
     public static final NamedParameterSpec X448
         = new NamedParameterSpec("X448");
 
+    /**
+     * The Ed25519 parameters
+     *
+     * @since 15
+     */
+    public static final NamedParameterSpec ED25519
+        = new NamedParameterSpec("Ed25519");
+
+    /**
+     * The Ed448 parameters
+     *
+     * @since 15
+     */
+    public static final NamedParameterSpec ED448
+        = new NamedParameterSpec("Ed448");
+
     private String name;
 
     /**
--- a/src/java.base/share/classes/module-info.java	Mon May 18 09:28:06 2020 -0700
+++ b/src/java.base/share/classes/module-info.java	Mon May 18 09:42:52 2020 -0700
@@ -294,6 +294,7 @@
         java.rmi,
         java.security.jgss,
         jdk.crypto.cryptoki,
+        jdk.crypto.ec,
         jdk.security.auth;
     exports sun.security.provider.certpath to
         java.naming;
--- a/src/java.base/share/classes/sun/security/pkcs/PKCS7.java	Mon May 18 09:28:06 2020 -0700
+++ b/src/java.base/share/classes/sun/security/pkcs/PKCS7.java	Mon May 18 09:42:52 2020 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2020, 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
@@ -829,6 +829,10 @@
         BigInteger serialNumber = signerChain[0].getSerialNumber();
         String encAlg = AlgorithmId.getEncAlgFromSigAlg(signatureAlgorithm);
         String digAlg = AlgorithmId.getDigAlgFromSigAlg(signatureAlgorithm);
+        if (digAlg == null) {
+            throw new UnsupportedOperationException("Unable to determine " +
+                    "the digest algorithm from the signature algorithm.");
+        }
         SignerInfo signerInfo = new SignerInfo(issuerName, serialNumber,
                                                AlgorithmId.get(digAlg), null,
                                                AlgorithmId.get(encAlg),
--- a/src/java.base/share/classes/sun/security/provider/SHA3.java	Mon May 18 09:28:06 2020 -0700
+++ b/src/java.base/share/classes/sun/security/provider/SHA3.java	Mon May 18 09:42:52 2020 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2020, 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
@@ -61,14 +61,16 @@
         0x8000000000008080L, 0x80000001L, 0x8000000080008008L,
     };
 
+    private final byte suffix;
     private byte[] state = new byte[WIDTH];
     private long[] lanes = new long[DM*DM];
 
     /**
      * Creates a new SHA-3 object.
      */
-    SHA3(String name, int digestLength) {
-        super(name, digestLength, (WIDTH - (2 * digestLength)));
+    SHA3(String name, int digestLength, byte suffix, int c) {
+        super(name, digestLength, (WIDTH - c));
+        this.suffix = suffix;
     }
 
     /**
@@ -88,7 +90,7 @@
      */
     void implDigest(byte[] out, int ofs) {
         int numOfPadding =
-            setPaddingBytes(buffer, (int)(bytesProcessed % buffer.length));
+            setPaddingBytes(suffix, buffer, (int)(bytesProcessed % buffer.length));
         if (numOfPadding < 1) {
             throw new ProviderException("Incorrect pad size: " + numOfPadding);
         }
@@ -112,13 +114,13 @@
      * pad10*1 algorithm (section 5.1) and the 2-bit suffix "01" required
      * for SHA-3 hash (section 6.1).
      */
-    private static int setPaddingBytes(byte[] in, int len) {
+    private static int setPaddingBytes(byte suffix, byte[] in, int len) {
         if (len != in.length) {
             // erase leftover values
             Arrays.fill(in, len, in.length, (byte)0);
             // directly store the padding bytes into the input
             // as the specified buffer is allocated w/ size = rateR
-            in[len] |= (byte) 0x06;
+            in[len] |= suffix;
             in[in.length - 1] |= (byte) 0x80;
         }
         return (in.length - len);
@@ -268,7 +270,7 @@
      */
     public static final class SHA224 extends SHA3 {
         public SHA224() {
-            super("SHA3-224", 28);
+            super("SHA3-224", 28, (byte)0x06, 56);
         }
     }
 
@@ -277,7 +279,7 @@
      */
     public static final class SHA256 extends SHA3 {
         public SHA256() {
-            super("SHA3-256", 32);
+            super("SHA3-256", 32, (byte)0x06, 64);
         }
     }
 
@@ -286,7 +288,7 @@
      */
     public static final class SHA384 extends SHA3 {
         public SHA384() {
-            super("SHA3-384", 48);
+            super("SHA3-384", 48, (byte)0x06, 96);
         }
     }
 
@@ -295,7 +297,7 @@
      */
     public static final class SHA512 extends SHA3 {
         public SHA512() {
-            super("SHA3-512", 64);
+            super("SHA3-512", 64, (byte)0x06, 128);
         }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/provider/SHAKE256.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020, 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.provider;
+
+/*
+ * The SHAKE256 extendable output function.
+ */
+public final class SHAKE256 extends SHA3 {
+    public SHAKE256(int d) {
+        super("SHAKE256", d, (byte) 0x1F, 64);
+    }
+
+    public void update(byte in) {
+        engineUpdate(in);
+    }
+    public void update(byte[] in, int off, int len) {
+        engineUpdate(in, off, len);
+    }
+
+    public byte[] digest() {
+        return engineDigest();
+    }
+}
--- a/src/java.base/share/classes/sun/security/tools/keytool/Main.java	Mon May 18 09:28:06 2020 -0700
+++ b/src/java.base/share/classes/sun/security/tools/keytool/Main.java	Mon May 18 09:42:52 2020 -0700
@@ -1870,6 +1870,12 @@
                     keysize = SecurityProviderConstants.DEF_RSA_KEY_SIZE;
                 } else if ("DSA".equalsIgnoreCase(keyAlgName)) {
                     keysize = SecurityProviderConstants.DEF_DSA_KEY_SIZE;
+                } else if ("EdDSA".equalsIgnoreCase(keyAlgName)) {
+                    keysize = SecurityProviderConstants.DEF_ED_KEY_SIZE;
+                } else if ("Ed25519".equalsIgnoreCase(keyAlgName)) {
+                    keysize = 255;
+                } else if ("Ed448".equalsIgnoreCase(keyAlgName)) {
+                    keysize = 448;
                 }
             } else {
                 if ("EC".equalsIgnoreCase(keyAlgName)) {
--- a/src/java.base/share/classes/sun/security/util/KeyUtil.java	Mon May 18 09:28:06 2020 -0700
+++ b/src/java.base/share/classes/sun/security/util/KeyUtil.java	Mon May 18 09:42:52 2020 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2020, 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
@@ -27,10 +27,10 @@
 
 import java.security.AlgorithmParameters;
 import java.security.Key;
-import java.security.PrivilegedAction;
-import java.security.AccessController;
 import java.security.InvalidKeyException;
 import java.security.interfaces.ECKey;
+import java.security.interfaces.EdECKey;
+import java.security.interfaces.EdECPublicKey;
 import java.security.interfaces.RSAKey;
 import java.security.interfaces.DSAKey;
 import java.security.interfaces.DSAParams;
@@ -44,6 +44,7 @@
 import javax.crypto.spec.DHParameterSpec;
 import javax.crypto.spec.DHPublicKeySpec;
 import java.math.BigInteger;
+import java.security.spec.NamedParameterSpec;
 
 import sun.security.jca.JCAUtil;
 
@@ -96,6 +97,16 @@
         } else if (key instanceof DHKey) {
             DHKey pubk = (DHKey)key;
             size = pubk.getParams().getP().bitLength();
+        } else if (key instanceof EdECKey) {
+            String nc = ((EdECKey) key).getParams().getName();
+            if (nc.equalsIgnoreCase(NamedParameterSpec.ED25519.getName())) {
+                size = 255;
+            } else if (nc.equalsIgnoreCase(
+                    NamedParameterSpec.ED448.getName())) {
+                size = 448;
+            } else {
+                size = -1;
+            }
         }   // Otherwise, it may be a unextractable key of PKCS#11, or
             // a key we are not able to handle.
 
--- a/src/java.base/share/classes/sun/security/util/SecurityProviderConstants.java	Mon May 18 09:28:06 2020 -0700
+++ b/src/java.base/share/classes/sun/security/util/SecurityProviderConstants.java	Mon May 18 09:42:52 2020 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2020, 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
@@ -59,6 +59,7 @@
     public static final int DEF_RSASSA_PSS_KEY_SIZE;
     public static final int DEF_DH_KEY_SIZE;
     public static final int DEF_EC_KEY_SIZE;
+    public static final int DEF_ED_KEY_SIZE;
 
     private static final String KEY_LENGTH_PROP =
         "jdk.security.defaultKeySize";
@@ -70,6 +71,7 @@
         int rsaSsaPssKeySize = rsaKeySize; // default to same value as RSA
         int dhKeySize = 2048;
         int ecKeySize = 256;
+        int edKeySize = 255;
 
         if (keyLengthStr != null) {
             try {
@@ -106,6 +108,8 @@
                         dhKeySize = value;
                     } else if (algoName.equals("EC")) {
                         ecKeySize = value;
+                    } else if (algoName.equalsIgnoreCase("EdDSA")) {
+                        edKeySize = value;
                     } else {
                         if (debug != null) {
                             debug.println("Ignoring unsupported algo in " +
@@ -132,5 +136,6 @@
         DEF_RSASSA_PSS_KEY_SIZE = rsaSsaPssKeySize;
         DEF_DH_KEY_SIZE = dhKeySize;
         DEF_EC_KEY_SIZE = ecKeySize;
+        DEF_ED_KEY_SIZE = edKeySize;
     }
 }
--- a/src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomial.java	Mon May 18 09:28:06 2020 -0700
+++ b/src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomial.java	Mon May 18 09:42:52 2020 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2020, 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
@@ -157,12 +157,34 @@
     public SmallValue getSmallValue(int value) {
         int maxMag = 1 << (bitsPerLimb - 1);
         if (Math.abs(value) >= maxMag) {
-            throw new IllegalArgumentException(
-                "max magnitude is " + maxMag);
+            throw new IllegalArgumentException("max magnitude is " + maxMag);
         }
         return new Limb(value);
     }
 
+    protected abstract void reduceIn(long[] c, long v, int i);
+
+    private void reduceHigh(long[] limbs) {
+
+        // conservatively calculate how many reduce operations can be done
+        // before a carry is needed
+        int extraBits = 63 - 2 * bitsPerLimb;
+        int allowedAdds = 1 << extraBits;
+        int carryPeriod = allowedAdds / numLimbs;
+        int reduceCount = 0;
+        for (int i = limbs.length - 1; i >= numLimbs; i--) {
+            reduceIn(limbs, limbs[i], i);
+            limbs[i] = 0;
+
+            reduceCount++;
+            if (reduceCount % carryPeriod == 0) {
+                carry(limbs, 0, i);
+                reduceIn(limbs, limbs[i], i);
+                limbs[i] = 0;
+            }
+        }
+    }
+
     /**
      * This version of encode takes a ByteBuffer that is properly ordered, and
      * may extract larger values (e.g. long) from the ByteBuffer for better
@@ -179,10 +201,12 @@
         if (requiredLimbs > numLimbs) {
             long[] temp = new long[requiredLimbs];
             encodeSmall(buf, length, highByte, temp);
-            // encode does a full carry/reduce
+            reduceHigh(temp);
             System.arraycopy(temp, 0, result, 0, result.length);
+            reduce(result);
         } else {
             encodeSmall(buf, length, highByte, result);
+            postEncodeCarry(result);
         }
     }
 
@@ -226,8 +250,6 @@
             result[limbIndex++] = curLimbValue;
         }
         Arrays.fill(result, limbIndex, result.length, 0);
-
-        postEncodeCarry(result);
     }
 
     protected void encode(byte[] v, int offset, int length, byte highByte,
--- a/src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomial1305.java	Mon May 18 09:28:06 2020 -0700
+++ b/src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomial1305.java	Mon May 18 09:42:52 2020 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2020, 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
@@ -156,7 +156,8 @@
         }
     }
 
-    private void modReduceIn(long[] limbs, int index, long x) {
+    @Override
+    protected void reduceIn(long[] limbs, long x, int index) {
         // this only works when BITS_PER_LIMB * NUM_LIMBS = POWER exactly
         long reducedValue = (x * SUBTRAHEND);
         limbs[index - NUM_LIMBS] += reducedValue;
@@ -166,13 +167,13 @@
     protected void finalCarryReduceLast(long[] limbs) {
         long carry = limbs[numLimbs - 1] >> bitsPerLimb;
         limbs[numLimbs - 1] -= carry << bitsPerLimb;
-        modReduceIn(limbs, numLimbs, carry);
+        reduceIn(limbs, carry, numLimbs);
     }
 
     protected final void modReduce(long[] limbs, int start, int end) {
 
         for (int i = start; i < end; i++) {
-            modReduceIn(limbs, i, limbs[i]);
+            reduceIn(limbs, limbs[i], i);
             limbs[i] = 0;
         }
     }
@@ -203,7 +204,7 @@
         long carry4 = carryValue(new4);
         limbs[4] = new4 - (carry4 << BITS_PER_LIMB);
 
-        modReduceIn(limbs, 5, carry4);
+        reduceIn(limbs, carry4, 5);
         carry(limbs);
     }
 
--- a/src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomial25519.java	Mon May 18 09:28:06 2020 -0700
+++ b/src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomial25519.java	Mon May 18 09:42:52 2020 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2020, 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
@@ -52,6 +52,13 @@
     }
 
     @Override
+    protected void reduceIn(long[] limbs, long v, int i) {
+        long t0 = 19 * v;
+        limbs[i - 10] += (t0 << 5) & LIMB_MASK;
+        limbs[i - 9] += t0 >> 21;
+    }
+
+    @Override
     protected void finalCarryReduceLast(long[] limbs) {
 
         long reducedValue = limbs[numLimbs - 1] >> RIGHT_BIT_OFFSET;
@@ -81,17 +88,6 @@
 
     @Override
     protected void mult(long[] a, long[] b, long[] r) {
-
-        // Use grade-school multiplication into primitives to avoid the
-        // temporary array allocation. This is equivalent to the following
-        // code:
-        //  long[] c = new long[2 * NUM_LIMBS - 1];
-        //  for(int i = 0; i < NUM_LIMBS; i++) {
-        //      for(int j - 0; j < NUM_LIMBS; j++) {
-        //          c[i + j] += a[i] * b[j]
-        //      }
-        //  }
-
         long c0 = (a[0] * b[0]);
         long c1 = (a[0] * b[1]) + (a[1] * b[0]);
         long c2 = (a[0] * b[2]) + (a[1] * b[1]) + (a[2] * b[0]);
@@ -172,7 +168,6 @@
         // carry(0,9)
         carry(r, 0, 9);
     }
-
     @Override
     protected void square(long[] a, long[] r) {
 
--- a/src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomial448.java	Mon May 18 09:28:06 2020 -0700
+++ b/src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomial448.java	Mon May 18 09:42:52 2020 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2020, 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
@@ -45,16 +45,17 @@
         super(BITS_PER_LIMB, NUM_LIMBS, 1, MODULUS);
     }
 
-    private void modReduceIn(long[] limbs, int index, long x) {
-        limbs[index - NUM_LIMBS] += x;
-        limbs[index - NUM_LIMBS / 2] += x;
+    @Override
+    protected void reduceIn(long[] limbs, long v, int i) {
+        limbs[i - 8] += v;
+        limbs[i - 16] += v;
     }
 
     @Override
     protected void finalCarryReduceLast(long[] limbs) {
         long carry = limbs[numLimbs - 1] >> bitsPerLimb;
         limbs[numLimbs - 1] -= carry << bitsPerLimb;
-        modReduceIn(limbs, numLimbs, carry);
+        reduceIn(limbs, carry, numLimbs);
     }
 
     @Override
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/util/math/intpoly/IntegerPolynomialModBinP.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2020, 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.math.intpoly;
+
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+
+/**
+ * The field of integers modulo a binomial prime. This is a general-purpose
+ * field implementation, that is much slower than more specialized classes
+ * like IntegerPolynomial25519. It is suitable when only a small number of
+ * arithmetic operations are required in some field. For example, this class
+ * can be used for operations on scalars/exponents in signature operations.
+ *
+ * This class may only be used for primes of the form 2^a + b.
+ */
+
+public class IntegerPolynomialModBinP extends IntegerPolynomial {
+
+    private final long[] reduceLimbs;
+    private final int bitOffset;
+    private final int limbMask;
+    private final int rightBitOffset;
+    private final int power;
+
+    public IntegerPolynomialModBinP(int bitsPerLimb,
+                                    int numLimbs,
+                                    int power,
+                                    BigInteger subtrahend) {
+        super(bitsPerLimb, numLimbs, 1,
+            BigInteger.valueOf(2).pow(power).subtract(subtrahend));
+
+        boolean negate = false;
+        if (subtrahend.compareTo(BigInteger.ZERO) < 0) {
+            negate = true;
+            subtrahend = subtrahend.negate();
+        }
+        int reduceLimbsLength = subtrahend.bitLength() / bitsPerLimb + 1;
+        reduceLimbs = new long[reduceLimbsLength];
+        ImmutableElement reduceElem = getElement(subtrahend);
+        if (negate) {
+            reduceElem = reduceElem.additiveInverse();
+        }
+        System.arraycopy(reduceElem.limbs, 0, reduceLimbs, 0,
+            reduceLimbs.length);
+
+        // begin test code
+        System.out.println("reduce limbs:");
+        for (int i = 0; i < reduceLimbs.length; i++) {
+            System.out.println(i + ":" + reduceLimbs[i]);
+        }
+        // end test code
+
+        this.power = power;
+        this.bitOffset = numLimbs * bitsPerLimb - power;
+        this.limbMask = -1 >>> (64 - bitsPerLimb);
+        this.rightBitOffset = bitsPerLimb - bitOffset;
+    }
+
+    @Override
+    protected void finalCarryReduceLast(long[] limbs) {
+
+        int extraBits = bitsPerLimb * numLimbs - power;
+        int highBits = bitsPerLimb - extraBits;
+        long c = limbs[numLimbs - 1] >> highBits;
+        limbs[numLimbs - 1] -= c << highBits;
+        for (int j = 0; j < reduceLimbs.length; j++) {
+            int reduceBits = power + extraBits - j * bitsPerLimb;
+            modReduceInBits(limbs, numLimbs, reduceBits, c * reduceLimbs[j]);
+        }
+    }
+
+
+    /**
+     * Allow more general (and slower) input conversion that takes a large
+     * value and reduces it.
+     */
+    @Override
+    public ImmutableElement getElement(byte[] v, int offset, int length,
+                                       byte highByte) {
+
+        long[] result = new long[numLimbs];
+        int numHighBits = 32 - Integer.numberOfLeadingZeros(highByte);
+        int numBits = 8 * length + numHighBits;
+        int requiredLimbs = (numBits + bitsPerLimb - 1) / bitsPerLimb;
+        if (requiredLimbs > numLimbs) {
+            long[] temp = new long[requiredLimbs];
+            encode(v, offset, length, highByte, temp);
+            // encode does a full carry/reduce
+            System.arraycopy(temp, 0, result, 0, result.length);
+        } else {
+            encode(v, offset, length, highByte, result);
+        }
+
+        return new ImmutableElement(result, 0);
+    }
+
+    /**
+     * Multiply a and b, and store the result in c. Requires that
+     * a.length == b.length == numLimbs and c.length >= 2 * numLimbs - 1.
+     * It is allowed for a and b to be the same array.
+     */
+    private void multOnly(long[] a, long[] b, long[] c) {
+        for (int i = 0; i < numLimbs; i++) {
+            for (int j = 0; j < numLimbs; j++) {
+                c[i + j] += a[i] * b[j];
+            }
+        }
+    }
+
+    @Override
+    protected void mult(long[] a, long[] b, long[] r) {
+
+        long[] c = new long[2 * numLimbs];
+        multOnly(a, b, c);
+        carryReduce(c, r);
+    }
+
+    private void modReduceInBits(long[] limbs, int index, int bits, long x) {
+
+        if (bits % bitsPerLimb == 0) {
+            int pos = bits / bitsPerLimb;
+            limbs[index - pos] += x;
+        }
+        else {
+            int secondPos = bits / (bitsPerLimb);
+            int bitOffset = (secondPos + 1) * bitsPerLimb - bits;
+            int rightBitOffset = bitsPerLimb - bitOffset;
+            limbs[index - (secondPos + 1)] += (x << bitOffset) & limbMask;
+            limbs[index - secondPos] += x >> rightBitOffset;
+        }
+    }
+
+    protected void reduceIn(long[] c, long v, int i) {
+
+        for (int j = 0; j < reduceLimbs.length; j++) {
+            modReduceInBits(c, i, power - bitsPerLimb * j, reduceLimbs[j] * v);
+        }
+    }
+
+    private void carryReduce(long[] c, long[] r) {
+
+        // full carry to prevent overflow during reduce
+        carry(c);
+        // Reduce in from all high positions
+        for (int i = c.length - 1; i >= numLimbs; i--) {
+            reduceIn(c, c[i], i);
+            c[i] = 0;
+        }
+        // carry on lower positions that possibly carries out one position
+        carry(c, 0, numLimbs);
+        // reduce in a single position
+        reduceIn(c, c[numLimbs], numLimbs);
+        c[numLimbs] = 0;
+        // final carry
+        carry(c, 0, numLimbs - 1);
+        System.arraycopy(c, 0, r, 0, r.length);
+    }
+
+    @Override
+    protected void reduce(long[] a) {
+        // TODO: optimize this
+        long[] c = new long[a.length + 2];
+        System.arraycopy(a, 0, c, 0, a.length);
+        carryReduce(c, a);
+    }
+
+    @Override
+    protected void square(long[] a, long[] r) {
+
+        long[] c = new long[2 * numLimbs];
+        for (int i = 0; i < numLimbs; i++) {
+            c[2 * i] += a[i] * a[i];
+            for (int j = i + 1; j < numLimbs; j++) {
+                c[i + j] += 2 * a[i] * a[j];
+            }
+        }
+
+        carryReduce(c, r);
+
+    }
+
+    /**
+     * The field of integers modulo the order of the Curve25519 subgroup
+     */
+    public static class Curve25519OrderField extends IntegerPolynomialModBinP {
+
+        public Curve25519OrderField() {
+            super(26, 10, 252,
+                new BigInteger("-27742317777372353535851937790883648493"));
+        }
+    }
+
+    /**
+     * The field of integers modulo the order of the Curve448 subgroup
+     */
+    public static class Curve448OrderField extends IntegerPolynomialModBinP {
+
+        public Curve448OrderField() {
+            super(28, 16, 446,
+                new BigInteger("138180668098951153520073867485154268803366" +
+                    "92474882178609894547503885"));
+        }
+    }
+}
--- a/src/java.base/share/classes/sun/security/x509/AlgorithmId.java	Mon May 18 09:28:06 2020 -0700
+++ b/src/java.base/share/classes/sun/security/x509/AlgorithmId.java	Mon May 18 09:42:52 2020 -0700
@@ -28,11 +28,13 @@
 import java.io.*;
 import java.security.interfaces.RSAKey;
 import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.EdDSAParameterSpec;
 import java.security.spec.InvalidParameterSpecException;
 import java.security.spec.MGF1ParameterSpec;
 import java.security.spec.PSSParameterSpec;
 import java.util.*;
 import java.security.*;
+import java.security.interfaces.*;
 
 import sun.security.rsa.PSSParameters;
 import sun.security.util.*;
@@ -199,7 +201,8 @@
             } else {
                 bytes.putNull();
             }*/
-            if (algid.equals(RSASSA_PSS_oid)) {
+            if (algid.equals(RSASSA_PSS_oid) || algid.equals(ed448_oid)
+                    || algid.equals(ed25519_oid)) {
                 // RFC 4055 3.3: when an RSASSA-PSS key does not require
                 // parameter validation, field is absent.
             } else {
@@ -588,6 +591,12 @@
         if (name.equalsIgnoreCase("SHA512withECDSA")) {
             return AlgorithmId.sha512WithECDSA_oid;
         }
+        if (name.equalsIgnoreCase("ED25519")) {
+            return AlgorithmId.ed25519_oid;
+        }
+        if (name.equalsIgnoreCase("ED448")) {
+            return AlgorithmId.ed448_oid;
+        }
 
         return oidTable().get(name.toUpperCase(Locale.ENGLISH));
     }
@@ -902,6 +911,11 @@
     public static final ObjectIdentifier pbeWithSHA1AndRC2_40_oid =
             ObjectIdentifier.of("1.2.840.113549.1.12.1.6");
 
+    public static final ObjectIdentifier ed25519_oid =
+        ObjectIdentifier.of("1.3.101.112");
+    public static final ObjectIdentifier ed448_oid =
+        ObjectIdentifier.of("1.3.101.113");
+
     static {
         nameTable = new HashMap<>();
         nameTable.put(MD5_oid, "MD5");
@@ -921,6 +935,8 @@
         nameTable.put(DSA_OIW_oid, "DSA");
         nameTable.put(EC_oid, "EC");
         nameTable.put(ECDH_oid, "ECDH");
+        nameTable.put(ed25519_oid, "ED25519");
+        nameTable.put(ed448_oid, "ED448");
 
         nameTable.put(AES_oid, "AES");
 
@@ -1044,6 +1060,8 @@
                     + "withRSA";
             case "RSASSA-PSS":
                 return "RSASSA-PSS";
+            case "EDDSA":
+                return edAlgFromKey(k);
             default:
                 return null;
         }
@@ -1094,6 +1112,8 @@
             return PSSParamsHolder.PSS_384_ID;
         } else if (spec == PSSParamsHolder.PSS_512_SPEC) {
             return PSSParamsHolder.PSS_512_ID;
+        } else if (spec instanceof EdDSAParameterSpec) {
+            return AlgorithmId.get(algName);
         } else {
             try {
                 AlgorithmParameters result =
@@ -1130,6 +1150,14 @@
         }
     }
 
+    private static String edAlgFromKey(PrivateKey k) {
+        if (k instanceof EdECPrivateKey) {
+            EdECPrivateKey edKey = (EdECPrivateKey) k;
+            return edKey.getParams().getName();
+        }
+        return "EdDSA";
+    }
+
     // Values from SP800-57 part 1 rev 4 tables 2 and 3
     private static String ecStrength (int bitLength) {
         if (bitLength >= 512) { // 256 bits of strength
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ParametersMap.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2020, 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.util.ObjectIdentifier;
+import sun.security.x509.AlgorithmId;
+
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.NamedParameterSpec;
+import java.util.Collections;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public class ParametersMap<T> {
+
+    private Map<Integer, T> sizeMap = new HashMap<Integer, T>();
+    private Map<ObjectIdentifier, T> oidMap =
+        new HashMap<ObjectIdentifier, T>();
+    private Map<String, T> nameMap = new HashMap<String, T>();
+
+
+    public void fix() {
+
+        sizeMap = Collections.unmodifiableMap(sizeMap);
+        oidMap = Collections.unmodifiableMap(oidMap);
+        nameMap = Collections.unmodifiableMap(nameMap);
+    }
+
+    public void put(String name, ObjectIdentifier oid, int size, T params) {
+        nameMap.put(name.toLowerCase(), params);
+        oidMap.put(oid, params);
+        sizeMap.put(size, params);
+    }
+
+    public Optional<T> getByOid(ObjectIdentifier id) {
+        return Optional.ofNullable(oidMap.get(id));
+    }
+    public Optional<T> getBySize(int size) {
+        return Optional.ofNullable(sizeMap.get(size));
+    }
+    public Optional<T> getByName(String name) {
+        return Optional.ofNullable(nameMap.get(name.toLowerCase()));
+    }
+
+    // Utility method that is used by the methods below to handle exception
+    // suppliers
+    private static
+    <A, B> Supplier<B> apply(final Function<A, B> func, final A a) {
+        return new Supplier<B>() {
+            @Override
+            public B get() {
+                return func.apply(a);
+            }
+        };
+    }
+
+    /**
+     * Get parameters by key size, or throw an exception if no parameters are
+     * defined for the specified key size. This method is used in several
+     * contexts that should throw different exceptions when the parameters
+     * are not found. The first argument is a function that produces the
+     * desired exception.
+     *
+     * @param exception a function that produces an exception from a string
+     * @param size the desired key size
+     * @param <E> the type of exception that is thrown
+     * @return the parameters for the specified key size
+     * @throws T when suitable parameters do not exist
+     */
+    public
+    <E extends Throwable>
+    T getBySize(Function<String, E> exception,
+                              int size) throws E {
+
+        Optional<T> paramsOpt = getBySize(size);
+        return paramsOpt.orElseThrow(
+            apply(exception, "Unsupported size: " + size));
+    }
+
+    /**
+     * Get parameters by algorithm ID, or throw an exception if no
+     * parameters are defined for the specified ID. This method is used in
+     * several contexts that should throw different exceptions when the
+     * parameters are not found. The first argument is a function that produces
+     * the desired exception.
+     *
+     * @param exception a function that produces an exception from a string
+     * @param algId the algorithm ID
+     * @param <E> the type of exception that is thrown
+     * @return the parameters for the specified algorithm ID
+     * @throws E when suitable parameters do not exist
+     */
+    public
+    <E extends Throwable>
+    T get(Function<String, E> exception,
+                        AlgorithmId algId) throws E {
+
+        Optional<T> paramsOpt = getByOid(algId.getOID());
+        return paramsOpt.orElseThrow(
+            apply(exception, "Unsupported OID: " + algId.getOID()));
+    }
+
+    /**
+     * Get parameters by algorithm parameter spec, or throw an exception if no
+     * parameters are defined for the spec. This method is used in
+     * several contexts that should throw different exceptions when the
+     * parameters are not found. The first argument is a function that produces
+     * the desired exception.
+     *
+     * @param exception a function that produces an exception from a string
+     * @param params the algorithm parameters spec
+     * @param <E> the type of exception that is thrown
+     * @return the parameters for the spec
+     * @throws E when suitable parameters do not exist
+     */
+    public
+    <E extends Throwable>
+    T get(Function<String, E> exception,
+                        AlgorithmParameterSpec params) throws E {
+
+        if (params instanceof NamedParameterSpec) {
+            NamedParameterSpec namedParams = (NamedParameterSpec) params;
+            Optional<T> paramsOpt = getByName(namedParams.getName());
+            return paramsOpt.orElseThrow(
+            apply(exception, "Unsupported name: " + namedParams.getName()));
+        } else {
+            throw exception.apply("Only NamedParameterSpec is supported.");
+        }
+    }
+}
--- a/src/jdk.crypto.ec/share/classes/sun/security/ec/SunEC.java	Mon May 18 09:28:06 2020 -0700
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/SunEC.java	Mon May 18 09:42:52 2020 -0700
@@ -37,6 +37,11 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.regex.Pattern;
+
+import sun.security.ec.ed.EdDSAAlgorithmParameters;
+import sun.security.ec.ed.EdDSAKeyFactory;
+import sun.security.ec.ed.EdDSAKeyPairGenerator;
+import sun.security.ec.ed.EdDSASignature;
 import sun.security.util.CurveDB;
 import sun.security.util.NamedCurve;
 
@@ -116,6 +121,15 @@
             String algo = getAlgorithm();
             try {
                 if (type.equals("Signature")) {
+
+                    if (algo.equalsIgnoreCase("EdDSA")) {
+                        return new EdDSASignature();
+                    } else if (algo.equalsIgnoreCase("Ed25519")) {
+                        return new EdDSASignature.Ed25519();
+                    } else if (algo.equalsIgnoreCase("Ed448")) {
+                        return new EdDSASignature.Ed448();
+                    }
+
                     boolean inP1363 = algo.endsWith("inP1363Format");
                     if (inP1363) {
                         algo = algo.substring(0, algo.length() - 13);
@@ -148,6 +162,12 @@
                         return new XDHKeyFactory.X25519();
                     } else if (algo.equals("X448")) {
                         return new XDHKeyFactory.X448();
+                    } else if (algo.equalsIgnoreCase("EdDSA")) {
+                        return new EdDSAKeyFactory();
+                    } else if (algo.equalsIgnoreCase("Ed25519")) {
+                        return new EdDSAKeyFactory.Ed25519();
+                    } else if (algo.equalsIgnoreCase("Ed448")) {
+                        return new EdDSAKeyFactory.Ed448();
                     }
                 } else  if (type.equals("AlgorithmParameters")) {
                     if (algo.equals("EC")) {
@@ -162,6 +182,12 @@
                         return new XDHKeyPairGenerator.X25519();
                     } else if (algo.equals("X448")) {
                         return new XDHKeyPairGenerator.X448();
+                    } else if (algo.equalsIgnoreCase("EdDSA")) {
+                        return new EdDSAKeyPairGenerator();
+                    } else if (algo.equalsIgnoreCase("Ed25519")) {
+                        return new EdDSAKeyPairGenerator.Ed25519();
+                    } else if (algo.equalsIgnoreCase("Ed448")) {
+                        return new EdDSAKeyPairGenerator.Ed448();
                     }
                 } else  if (type.equals("KeyAgreement")) {
                     if (algo.equals("ECDH")) {
@@ -184,8 +210,7 @@
     }
 
     public SunEC() {
-        super("SunEC", PROVIDER_VER,
-            "Sun Elliptic Curve provider (EC, ECDSA, ECDH)");
+        super("SunEC", PROVIDER_VER, "Sun Elliptic Curve provider");
         AccessController.doPrivileged(new PrivilegedAction<Void>() {
             public Void run() {
                 putEntries();
@@ -255,6 +280,7 @@
             apAttrs));
 
         putXDHEntries();
+        putEdDSAEntries();
 
         /*
          * Signature engines
@@ -350,4 +376,39 @@
             new String[]{"1.3.101.111", "OID.1.3.101.111"}, ATTRS));
 
     }
+
+    private void putEdDSAEntries() {
+
+        HashMap<String, String> ATTRS = new HashMap<>(1);
+        ATTRS.put("ImplementedIn", "Software");
+
+        /* EdDSA does not require native implementation */
+        putService(new ProviderService(this, "KeyFactory",
+            "EdDSA", "sun.security.ec.ed.EdDSAKeyFactory", null, ATTRS));
+        putService(new ProviderService(this, "KeyFactory",
+            "Ed25519", "sun.security.ec.ed.EdDSAKeyFactory.Ed25519",
+            new String[]{"1.3.101.112", "OID.1.3.101.112"}, ATTRS));
+        putService(new ProviderService(this, "KeyFactory",
+            "Ed448", "sun.security.ec.ed.EdDSAKeyFactory.Ed448",
+            new String[]{"1.3.101.113", "OID.1.3.101.113"}, ATTRS));
+
+        putService(new ProviderService(this, "KeyPairGenerator",
+            "EdDSA", "sun.security.ec.ed.EdDSAKeyPairGenerator", null, ATTRS));
+        putService(new ProviderService(this, "KeyPairGenerator",
+            "Ed25519", "sun.security.ec.ed.EdDSAKeyPairGenerator.Ed25519",
+            new String[]{"1.3.101.112", "OID.1.3.101.112"}, ATTRS));
+        putService(new ProviderService(this, "KeyPairGenerator",
+            "Ed448", "sun.security.ec.ed.EdDSAKeyPairGenerator.Ed448",
+            new String[]{"1.3.101.113", "OID.1.3.101.113"}, ATTRS));
+
+        putService(new ProviderService(this, "Signature",
+            "EdDSA", "sun.security.ec.ed.EdDSASignature", null, ATTRS));
+        putService(new ProviderService(this, "Signature",
+            "Ed25519", "sun.security.ec.ed.EdDSASignature.Ed25519",
+            new String[]{"1.3.101.112", "OID.1.3.101.112"}, ATTRS));
+        putService(new ProviderService(this, "Signature",
+            "Ed448", "sun.security.ec.ed.EdDSASignature.Ed448",
+            new String[]{"1.3.101.113", "OID.1.3.101.113"}, ATTRS));
+
+    }
 }
--- a/src/jdk.crypto.ec/share/classes/sun/security/ec/XECParameters.java	Mon May 18 09:28:06 2020 -0700
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/XECParameters.java	Mon May 18 09:42:52 2020 -0700
@@ -29,18 +29,17 @@
 import java.math.BigInteger;
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.NamedParameterSpec;
-import java.util.Collections;
 import java.util.Map;
 import java.util.HashMap;
-import java.util.Optional;
 import java.util.function.Function;
-import java.util.function.Supplier;
 
 import sun.security.util.ObjectIdentifier;
 import sun.security.x509.AlgorithmId;
 
 public class XECParameters {
 
+    static ParametersMap<XECParameters> namedParams = new ParametersMap<>();
+
     // Naming/identification parameters
     private final ObjectIdentifier oid;
     private final String name;
@@ -106,10 +105,6 @@
         return name;
     }
 
-    private static final Map<Integer, XECParameters> SIZE_MAP;
-    private static final Map<ObjectIdentifier, XECParameters> OID_MAP;
-    private static final Map<String, XECParameters> NAME_MAP;
-
     static {
         final BigInteger TWO = BigInteger.valueOf(2);
 
@@ -140,9 +135,7 @@
             // Unable to set X448 parameters---it will be disabled
         }
 
-        SIZE_MAP = Collections.unmodifiableMap(bySize);
-        OID_MAP = Collections.unmodifiableMap(byOid);
-        NAME_MAP = Collections.unmodifiableMap(byName);
+        namedParams.fix();
     }
 
     private static void addParameters(int bits, BigInteger p, int a24,
@@ -154,110 +147,36 @@
         ObjectIdentifier oid = new ObjectIdentifier(objectId);
         XECParameters params =
             new XECParameters(bits, p, a24, basePoint, logCofactor, oid, name);
-        bySize.put(bits, params);
-        byOid.put(oid, params);
-        byName.put(name.toLowerCase(), params);
-    }
-
-    public static Optional<XECParameters> getByOid(ObjectIdentifier id) {
-        return Optional.ofNullable(OID_MAP.get(id));
-    }
-    public static Optional<XECParameters> getBySize(int size) {
-        return Optional.ofNullable(SIZE_MAP.get(size));
-    }
-    public static Optional<XECParameters> getByName(String name) {
-        return Optional.ofNullable(NAME_MAP.get(name.toLowerCase()));
+        namedParams.put(name.toLowerCase(), oid, bits, params);
     }
 
     boolean oidEquals(XECParameters other) {
         return oid.equals(other.getOid());
     }
 
-    // Utility method that is used by the methods below to handle exception
-    // suppliers
-    private static
-    <A, B> Supplier<B> apply(final Function<A, B> func, final A a) {
-        return new Supplier<B>() {
-            @Override
-            public B get() {
-                return func.apply(a);
-            }
-        };
-    }
 
-    /**
-     * Get parameters by key size, or throw an exception if no parameters are
-     * defined for the specified key size. This method is used in several
-     * contexts that should throw different exceptions when the parameters
-     * are not found. The first argument is a function that produces the
-     * desired exception.
-     *
-     * @param exception a function that produces an exception from a string
-     * @param size the desired key size
-     * @param <T> the type of exception that is thrown
-     * @return the parameters for the specified key size
-     * @throws T when suitable parameters do not exist
-     */
     public static
     <T extends Throwable>
     XECParameters getBySize(Function<String, T> exception,
                             int size) throws T {
 
-        Optional<XECParameters> xecParams = getBySize(size);
-        return xecParams.orElseThrow(
-            apply(exception, "Unsupported size: " + size));
+        return namedParams.getBySize(exception, size);
     }
 
-    /**
-     * Get parameters by algorithm ID, or throw an exception if no
-     * parameters are defined for the specified ID. This method is used in
-     * several contexts that should throw different exceptions when the
-     * parameters are not found. The first argument is a function that produces
-     * the desired exception.
-     *
-     * @param exception a function that produces an exception from a string
-     * @param algId the algorithm ID
-     * @param <T> the type of exception that is thrown
-     * @return the parameters for the specified algorithm ID
-     * @throws T when suitable parameters do not exist
-     */
     public static
     <T extends Throwable>
     XECParameters get(Function<String, T> exception,
                       AlgorithmId algId) throws T {
 
-        Optional<XECParameters> xecParams = getByOid(algId.getOID());
-        return xecParams.orElseThrow(
-            apply(exception, "Unsupported OID: " + algId.getOID()));
+        return namedParams.get(exception, algId);
     }
 
-    /**
-     * Get parameters by algorithm parameter spec, or throw an exception if no
-     * parameters are defined for the spec. This method is used in
-     * several contexts that should throw different exceptions when the
-     * parameters are not found. The first argument is a function that produces
-     * the desired exception.
-     *
-     * @param exception a function that produces an exception from a string
-     * @param params the algorithm parameters spec
-     * @param <T> the type of exception that is thrown
-     * @return the parameters for the spec
-     * @throws T when suitable parameters do not exist
-     */
     public static
     <T extends Throwable>
     XECParameters get(Function<String, T> exception,
                       AlgorithmParameterSpec params) throws T {
 
-        if (params instanceof NamedParameterSpec) {
-            NamedParameterSpec namedParams = (NamedParameterSpec) params;
-            Optional<XECParameters> xecParams =
-                getByName(namedParams.getName());
-            return xecParams.orElseThrow(
-                apply(exception, "Unsupported name: " + namedParams.getName()));
-        } else {
-            throw exception.apply("Only NamedParameterSpec is supported.");
-        }
+        return namedParams.get(exception, params);
     }
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ed/Ed25519Operations.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2020, 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.ed;
+
+import sun.security.ec.point.*;
+import sun.security.util.math.*;
+
+import java.math.BigInteger;
+import java.util.function.Function;
+
+/*
+ * Elliptic curve point arithmetic, decoding, and other operations for the
+ * family of curves including edwards25519 and its related group. Though the
+ * operations in this class are optimized for edwards25519, they are correct
+ * for any twisted Edwards curve ax^2 + y^2 = 1 + dx^2y^2 (mod p) with the
+ * following properties:
+ *   1) a = -1 (mod p)
+ *   2) a is square (mod p)
+ *   3) d is not square (mod p)
+ */
+public class Ed25519Operations extends EdECOperations {
+
+    private final SmallValue two;
+    private final ImmutableIntegerModuloP d;
+    private final ExtendedHomogeneousPoint.Immutable basePoint;
+
+    private static final BigInteger TWO = BigInteger.valueOf(2);
+    private static final BigInteger SEVEN = BigInteger.valueOf(7);
+    private final BigInteger sizeMinus5;
+
+    public Ed25519Operations(ImmutableIntegerModuloP d, BigInteger baseX,
+                             BigInteger baseY) {
+
+        this.two = d.getField().getSmallValue(2);
+        this.d = d;
+        this.basePoint = of(new AffinePoint(
+            d.getField().getElement(baseX), d.getField().getElement(baseY)
+        ));
+        this.sizeMinus5 =
+            d.getField().getSize().subtract(BigInteger.valueOf(5));
+    }
+
+    @Override
+    public Point basePointMultiply(byte[] scalar) {
+        return setProduct(basePoint.mutable(), scalar);
+    }
+
+    @Override
+    protected ExtendedHomogeneousPoint.Immutable getNeutral() {
+        IntegerFieldModuloP field = d.getField();
+        return new ExtendedHomogeneousPoint.Immutable(field.get0(),
+            field.get1(), field.get0(), field.get1());
+    }
+
+    @Override
+    protected MutablePoint setSum(MutablePoint p1, MutablePoint p2,
+                                  MutableIntegerModuloP t1,
+                                  MutableIntegerModuloP t2,
+                                  MutableIntegerModuloP t3) {
+
+        ExtendedHomogeneousPoint.Mutable ehp1 =
+            (ExtendedHomogeneousPoint.Mutable) p1;
+        ExtendedHomogeneousPoint.Mutable ehp2 =
+            (ExtendedHomogeneousPoint.Mutable) p2;
+        return setSum(ehp1, ehp2, t1, t2, t3);
+    }
+
+    @Override
+    protected MutablePoint setDouble(MutablePoint p, MutableIntegerModuloP t1,
+        MutableIntegerModuloP t2) {
+
+        ExtendedHomogeneousPoint.Mutable ehp =
+            (ExtendedHomogeneousPoint.Mutable) p;
+        return setDouble(ehp, t1, t2);
+    }
+
+    @Override
+    public ExtendedHomogeneousPoint.Immutable of(AffinePoint p) {
+        return new ExtendedHomogeneousPoint.Immutable(p.getX(), p.getY(),
+            p.getX().multiply(p.getY()), p.getX().getField().get1());
+    }
+
+    @Override
+    public <T extends Throwable>
+    AffinePoint decodeAffinePoint(Function<String, T> exception,
+                                  int xLSB, IntegerModuloP y) throws T {
+
+        IntegerFieldModuloP field = d.getField();
+        BigInteger p = field.getSize();
+        ImmutableIntegerModuloP y2 = y.square();
+        ImmutableIntegerModuloP u = y2.subtract(field.get1());
+        MutableIntegerModuloP v = d.mutable().setProduct(y2)
+            .setSum(field.get1());
+
+        MutableIntegerModuloP x =
+            u.mutable().setProduct(v.pow(BigInteger.valueOf(3)));
+        ImmutableIntegerModuloP uv7pow =
+            u.multiply(v.pow(SEVEN)).pow(sizeMinus5.shiftRight(3));
+        x.setProduct(uv7pow);
+
+        v.setProduct(x).setProduct(x);
+        // v now holds vx^2
+        BigInteger bigVX2 = v.asBigInteger();
+        if (bigVX2.equals(u.asBigInteger())) {
+            // do nothing---x is correct
+        } else if (bigVX2.equals(u.additiveInverse().asBigInteger())) {
+            BigInteger exp = p.subtract(BigInteger.ONE).shiftRight(2);
+            IntegerModuloP twoPow = field.getElement(TWO.modPow(exp, p));
+            x.setProduct(twoPow);
+        } else {
+            throw exception.apply("Invalid point");
+        }
+
+        if (x.asBigInteger().equals(BigInteger.ZERO) && xLSB == 1) {
+            throw exception.apply("Invalid point");
+        }
+
+        if (xLSB != x.asBigInteger().mod(BigInteger.valueOf(2)).intValue()) {
+            x.setAdditiveInverse();
+        }
+
+        return new AffinePoint(x.fixed(), y.fixed());
+    }
+
+    ExtendedHomogeneousPoint.Mutable setSum(
+            ExtendedHomogeneousPoint.Mutable p1,
+            ExtendedHomogeneousPoint.Mutable p2,
+            MutableIntegerModuloP t1,
+            MutableIntegerModuloP t2,
+            MutableIntegerModuloP t3) {
+
+        t1.setValue(p2.getY()).setDifference(p2.getX());
+        // t1 holds y2 - x2
+        t2.setValue(p1.getY()).setDifference(p1.getX()).setProduct(t1);
+        // t2 holds A = (y1 - x1) * (y2 - x2)
+        t1.setValue(p2.getY()).setSum(p2.getX());
+        // t1 holds y2 + x2
+        t3.setValue(p1.getY()).setSum(p1.getX()).setProduct(t1);
+        // t3 holds B = (y1 + x1) * (y2 + x2)
+        p1.getX().setValue(t3).setDifference(t2);
+        // x holds E = B - A
+        t3.setSum(t2);
+        // t3 holds H = B + A, t2 is unused
+        t2.setValue(d).setSum(d).setProduct(p1.getT()).setProduct(p2.getT());
+        // t2 holds C
+        t1.setValue(p1.getZ()).setProduct(p2.getZ()).setProduct(two);
+        // t1 holds D
+        p1.getY().setValue(t1).setSum(t2);
+        // y holds G
+        p1.getZ().setValue(t1).setDifference(t2);
+        // z holds F
+
+        p1.getT().setValue(p1.getX()).setProduct(t3);
+        p1.getX().setProduct(p1.getZ());
+        p1.getZ().setProduct(p1.getY());
+        p1.getY().setProduct(t3);
+
+        return p1;
+
+    }
+
+    protected ExtendedHomogeneousPoint.Mutable setDouble(
+        ExtendedHomogeneousPoint.Mutable p,
+        MutableIntegerModuloP t1, MutableIntegerModuloP t2) {
+
+        t1.setValue(p.getX()).setSum(p.getY()).setSquare();
+        // t1 holds (x + y)^2
+        p.getX().setSquare();
+        // x = A = x^2
+        p.getY().setSquare();
+        // y = B = y^2
+        t2.setValue(p.getX()).setSum(p.getY()).setReduced();
+        // t2 holds H
+        p.getZ().setSquare().setProduct(two);
+        // z holds C
+
+        p.getT().setValue(t2).setDifference(t1);
+        // t holds E
+        t1.setValue(p.getX()).setDifference(p.getY()).setReduced();
+        // t1 holds G
+
+        p.getZ().setSum(t1);
+        // z holds F
+
+        p.getX().setValue(p.getT()).setProduct(p.getZ());
+        p.getY().setValue(t1).setProduct(t2);
+        p.getT().setProduct(t2);
+        p.getZ().setProduct(t1);
+
+        return p;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ed/Ed448Operations.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2020, 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.ed;
+
+import sun.security.ec.point.*;
+import sun.security.util.math.*;
+
+import java.math.BigInteger;
+import java.util.function.Function;
+
+// Arithmetic works for a=1 and non-square d
+/*
+ * Elliptic curve point arithmetic, decoding, and other operations for the
+ * family of curves including edwards448 and its related group. Though the
+ * operations in this class are optimized for edwards448, they are correct
+ * for any untwisted Edwards curve x^2 + y^2 = 1 + dx^2y^2 (mod p) where
+ * d is not square (mod p).
+ */
+public class Ed448Operations extends EdECOperations {
+
+    private final SmallValue two;
+    private final ImmutableIntegerModuloP d;
+    private final ProjectivePoint.Immutable basePoint;
+
+    private static final BigInteger TWO = BigInteger.valueOf(2);
+    private static final BigInteger THREE = BigInteger.valueOf(3);
+    private static final BigInteger FIVE = BigInteger.valueOf(5);
+    private final BigInteger sizeMinus3;
+
+    public Ed448Operations(ImmutableIntegerModuloP d, BigInteger baseX,
+                           BigInteger baseY) {
+
+        this.two = d.getField().getSmallValue(2);
+        this.d = d;
+        this.basePoint = of(new AffinePoint(
+            d.getField().getElement(baseX),
+            d.getField().getElement(baseY)
+        ));
+
+        this.sizeMinus3 = d.getField().getSize().subtract(THREE);
+    }
+
+    @Override
+    public Point basePointMultiply(byte[] scalar) {
+        return setProduct(basePoint.mutable(), scalar);
+    }
+
+    @Override
+    protected ProjectivePoint.Immutable getNeutral() {
+        IntegerFieldModuloP field = d.getField();
+        return new ProjectivePoint.Immutable(field.get0(), field.get1(),
+            field.get1());
+    }
+
+    @Override
+    protected MutablePoint setSum(MutablePoint p1, MutablePoint p2,
+                                  MutableIntegerModuloP t1,
+                                  MutableIntegerModuloP t2,
+                                  MutableIntegerModuloP t3) {
+
+        ProjectivePoint.Mutable ehp1 = (ProjectivePoint.Mutable) p1;
+        ProjectivePoint.Mutable ehp2 = (ProjectivePoint.Mutable) p2;
+        return setSum(ehp1, ehp2, t1, t2, t3);
+    }
+
+    @Override
+    protected MutablePoint setDouble(MutablePoint p, MutableIntegerModuloP t1,
+                                     MutableIntegerModuloP t2) {
+
+        ProjectivePoint.Mutable ehp = (ProjectivePoint.Mutable) p;
+        return setDouble(ehp, t1, t2);
+    }
+
+    @Override
+    public ProjectivePoint.Immutable of(AffinePoint p) {
+        return new ProjectivePoint.Immutable(p.getX(), p.getY(),
+            p.getX().getField().get1());
+    }
+
+    @Override
+    public <T extends Throwable>
+    AffinePoint decodeAffinePoint(Function<String, T> exception, int xLSB,
+                                  IntegerModuloP y) throws T {
+
+        ImmutableIntegerModuloP y2 = y.square();
+        ImmutableIntegerModuloP u = y2.subtract(d.getField().get1());
+        MutableIntegerModuloP v = d.mutable().setProduct(y2)
+            .setDifference(d.getField().get1());
+
+        IntegerModuloP u5v3pow = u.pow(FIVE).multiply(v.pow(THREE))
+            .pow(sizeMinus3.shiftRight(2));
+
+        MutableIntegerModuloP x = v.mutable().setProduct(u.pow(THREE))
+            .setProduct(u5v3pow);
+
+        v.setProduct(x).setProduct(x);
+        // v now holds vx^2
+        if (v.asBigInteger().equals(u.asBigInteger())) {
+            // x is correct
+        } else {
+            throw exception.apply("Invalid point");
+        }
+
+        if (x.asBigInteger().equals(BigInteger.ZERO) && xLSB == 1) {
+            throw exception.apply("Invalid point");
+        }
+
+        if (xLSB != x.asBigInteger().mod(TWO).intValue()) {
+            x.setAdditiveInverse();
+        }
+
+        return new AffinePoint(x.fixed(), y.fixed());
+    }
+
+    ProjectivePoint.Mutable setSum(
+            ProjectivePoint.Mutable p1,
+            ProjectivePoint.Mutable p2,
+            MutableIntegerModuloP t1,
+            MutableIntegerModuloP t2,
+            MutableIntegerModuloP t3) {
+
+        t1.setValue(p1.getX()).setProduct(p2.getX());
+        // t1 holds C
+        t2.setValue(p2.getX()).setSum(p2.getY());
+        p1.getX().setSum(p1.getY()).setProduct(t2);
+        // x holds H
+        p1.getZ().setProduct(p2.getZ());
+        // z holds A
+        p1.getY().setProduct(p2.getY());
+        // y holds D
+
+        t3.setValue(d).setProduct(t1).setProduct(p1.getY());
+        // t3 holds E
+        // do part of the final calculation of x and y to free up t1
+        p1.getX().setDifference(t1).setReduced().setDifference(p1.getY());
+        p1.getY().setDifference(t1);
+        t1.setValue(p1.getZ()).setSquare();
+        // t2 holds B
+
+        t2.setValue(t1).setDifference(t3);
+        // t2 holds F
+        t1.setSum(t3);
+        // t1 holds G
+
+        p1.getX().setProduct(t2).setProduct(p1.getZ());
+        p1.getY().setProduct(t1).setProduct(p1.getZ());
+        p1.getZ().setValue(t2.multiply(t1));
+
+        return p1;
+
+    }
+
+    protected ProjectivePoint.Mutable setDouble(ProjectivePoint.Mutable p,
+                                                MutableIntegerModuloP t1,
+                                                MutableIntegerModuloP t2) {
+
+        t2.setValue(p.getX()).setSquare();
+        // t2 holds C
+        p.getX().setSum(p.getY()).setSquare();
+        // x holds B
+        p.getY().setSquare();
+        // y holds D
+        p.getZ().setSquare();
+        // z holds H
+
+        t1.setValue(t2).setSum(p.getY()).setReduced();
+        // t1 holds E
+        t2.setDifference(p.getY());
+        p.getY().setValue(t1).setProduct(t2);
+
+        p.getZ().setProduct(two);
+        p.getZ().setAdditiveInverse().setSum(t1);
+        // z holds J
+        p.getX().setDifference(t1).setProduct(p.getZ());
+        p.getZ().setProduct(t1);
+
+        return p;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ed/EdDSAAlgorithmParameters.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2020, 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.ed;
+
+import java.io.IOException;
+import java.security.AlgorithmParametersSpi;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.EdDSAParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+
+/**
+ * This AlgorithmParametersSpi only supports NamedParameterSpec.
+ * EdDSAParameterSpec is not support because there is not ASN.1 format
+ */
+
+public class EdDSAAlgorithmParameters extends AlgorithmParametersSpi {
+
+    EdDSAParameterSpec edspec;
+
+    // If no curve is provide, wait engineInit() to provide one.
+    public EdDSAAlgorithmParameters() {
+    }
+
+    /**
+     * NamedParameterSpec can only be used if curve was not specified
+     * as part of getInstance(EdDSA).  If the curve was used, engineInit will
+     * throws an exception for being already initialized.
+     * EdDSAParameterSpec is not support because there is not ASN.1 format
+     *
+     * @param paramSpec NamedParameterSpec curve.
+     *
+     * @throws InvalidParameterSpecException
+     */
+    @Override
+    protected void engineInit(AlgorithmParameterSpec paramSpec)
+            throws InvalidParameterSpecException {
+        if (paramSpec instanceof EdDSAParameterSpec) {
+            edspec = (EdDSAParameterSpec)paramSpec;
+            return;
+        }
+        throw new InvalidParameterSpecException(
+                "Unknown AlgorithmParameterSpec");
+    }
+
+    @Override
+    protected void engineInit(byte[] params) throws IOException {
+        throw new IOException(
+                "EdDSA does not support parameters as a byte array.");
+    }
+
+    @Override
+    protected void engineInit(byte[] params, String format) throws IOException {
+        engineInit(params);
+    }
+
+    @Override
+    protected <T extends AlgorithmParameterSpec> T engineGetParameterSpec(
+            Class<T> paramSpec) throws InvalidParameterSpecException {
+
+        if (paramSpec.isAssignableFrom(ECParameterSpec.class)) {
+            return paramSpec.cast(edspec);
+        }
+        throw new InvalidParameterSpecException(
+                "Only EDDSAParameterSpec supported.");
+    }
+
+    @Override
+    protected byte[] engineGetEncoded() throws IOException {
+        throw new IOException(
+                "EdDSA does not support parameters as a byte array.");
+    }
+
+    @Override
+    protected byte[] engineGetEncoded(String format) throws IOException {
+        throw new IOException(
+                "EdDSA does not support parameters as a byte array.");
+    }
+
+    @Override
+    protected String engineToString() {
+        return edspec.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ed/EdDSAKeyFactory.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2020, 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.ed;
+
+import java.security.KeyFactorySpi;
+import java.security.Key;
+import java.security.PublicKey;
+import java.security.PrivateKey;
+import java.security.InvalidKeyException;
+import java.security.ProviderException;
+import java.security.interfaces.*;
+import java.security.spec.*;
+import java.util.function.Function;
+
+public class EdDSAKeyFactory extends KeyFactorySpi {
+
+    private EdDSAParameters lockedParams = null;
+
+    public EdDSAKeyFactory() {
+        // do nothing
+    }
+
+    protected EdDSAKeyFactory(NamedParameterSpec paramSpec) {
+        lockedParams = EdDSAParameters.get(ProviderException::new, paramSpec);
+    }
+
+    @Override
+    protected Key engineTranslateKey(Key key) throws InvalidKeyException {
+
+        if (key == null) {
+            throw new InvalidKeyException("Key must not be null");
+        }
+
+        if (key instanceof EdECKey) {
+            EdECKey edKey = (EdECKey) key;
+            EdDSAParameters params = EdDSAParameters.get(
+                InvalidKeyException::new, edKey.getParams());
+            checkLockedParams(InvalidKeyException::new, params);
+
+            if (edKey instanceof EdECPublicKey) {
+                EdECPublicKey publicKey = (EdECPublicKey) edKey;
+                return new EdDSAPublicKeyImpl(params, publicKey.getPoint());
+            } else if (edKey instanceof EdECPrivateKey) {
+                EdECPrivateKey privateKey = (EdECPrivateKey) edKey;
+                byte[] privateKeyBytes = privateKey.getBytes().orElseThrow(
+                    () -> new InvalidKeyException("No private key data"));
+                return new EdDSAPrivateKeyImpl(params, privateKeyBytes);
+            } else {
+                throw new InvalidKeyException("Unsupported EdECKey subclass");
+            }
+        } else if (key instanceof PublicKey &&
+                   key.getFormat().equals("X.509")) {
+            EdDSAPublicKeyImpl result =
+                new EdDSAPublicKeyImpl(key.getEncoded());
+            checkLockedParams(InvalidKeyException::new, result.getParams());
+            return result;
+        } else if (key instanceof PrivateKey &&
+                   key.getFormat().equals("PKCS#8")) {
+            EdDSAPrivateKeyImpl result =
+                new EdDSAPrivateKeyImpl(key.getEncoded());
+            checkLockedParams(InvalidKeyException::new, result.getParams());
+            return result;
+        } else {
+            throw new InvalidKeyException("Unsupported key type or format");
+        }
+    }
+
+    private
+    <T extends Throwable>
+    void checkLockedParams(Function<String, T> exception,
+                           NamedParameterSpec spec) throws T {
+
+        EdDSAParameters params = EdDSAParameters.get(exception, spec);
+        checkLockedParams(exception, params);
+    }
+
+    private
+    <T extends Throwable>
+    void checkLockedParams(Function<String, T> exception,
+                           EdDSAParameters params) throws T {
+
+        if (lockedParams != null && lockedParams != params) {
+            throw exception.apply("Parameters must be " +
+                lockedParams.getName());
+        }
+    }
+
+    @Override
+    protected PublicKey engineGeneratePublic(KeySpec keySpec)
+        throws InvalidKeySpecException {
+
+        try {
+             return generatePublicImpl(keySpec);
+        } catch (InvalidKeyException ex) {
+            throw new InvalidKeySpecException(ex);
+        }
+    }
+
+    @Override
+    protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
+        throws InvalidKeySpecException {
+
+        try {
+            return generatePrivateImpl(keySpec);
+        } catch (InvalidKeyException ex) {
+            throw new InvalidKeySpecException(ex);
+        }
+    }
+
+
+    private PublicKey generatePublicImpl(KeySpec keySpec)
+        throws InvalidKeyException, InvalidKeySpecException {
+
+        if (keySpec instanceof X509EncodedKeySpec) {
+            X509EncodedKeySpec x509Spec = (X509EncodedKeySpec) keySpec;
+            EdDSAPublicKeyImpl result =
+                new EdDSAPublicKeyImpl(x509Spec.getEncoded());
+            checkLockedParams(InvalidKeySpecException::new,
+                result.getParams());
+            return result;
+        } else if (keySpec instanceof EdECPublicKeySpec) {
+            EdECPublicKeySpec publicKeySpec = (EdECPublicKeySpec) keySpec;
+            EdDSAParameters params = EdDSAParameters.get(
+                InvalidKeySpecException::new, publicKeySpec.getParams());
+            checkLockedParams(InvalidKeySpecException::new, params);
+            return new EdDSAPublicKeyImpl(params, publicKeySpec.getPoint());
+        } else {
+            throw new InvalidKeySpecException(
+                "Only X509EncodedKeySpec and EdECPublicKeySpec are supported");
+        }
+    }
+
+    private PrivateKey generatePrivateImpl(KeySpec keySpec)
+        throws InvalidKeyException, InvalidKeySpecException {
+
+        if (keySpec instanceof PKCS8EncodedKeySpec) {
+            PKCS8EncodedKeySpec pkcsSpec = (PKCS8EncodedKeySpec) keySpec;
+            EdDSAPrivateKeyImpl result =
+                new EdDSAPrivateKeyImpl(pkcsSpec.getEncoded());
+            checkLockedParams(InvalidKeySpecException::new,
+                result.getParams());
+            return result;
+        } else if (keySpec instanceof EdECPrivateKeySpec) {
+            EdECPrivateKeySpec privateKeySpec = (EdECPrivateKeySpec) keySpec;
+            EdDSAParameters params = EdDSAParameters.get(
+                InvalidKeySpecException::new, privateKeySpec.getParams());
+            checkLockedParams(InvalidKeySpecException::new, params);
+            return new EdDSAPrivateKeyImpl(params, privateKeySpec.getBytes());
+        } else {
+            throw new InvalidKeySpecException(
+                "Only PKCS8EncodedKeySpec and EdECPrivateKeySpec supported");
+        }
+    }
+
+    protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
+            throws InvalidKeySpecException {
+
+        if (key instanceof EdECPublicKey) {
+            checkLockedParams(InvalidKeySpecException::new,
+                ((EdECPublicKey) key).getParams());
+
+            if (X509EncodedKeySpec.class.isAssignableFrom(keySpec)) {
+                if (!key.getFormat().equals("X.509")) {
+                    throw new InvalidKeySpecException("Format is not X.509");
+                }
+                return keySpec.cast(new X509EncodedKeySpec(key.getEncoded()));
+            } else if (EdECPublicKeySpec.class.isAssignableFrom(keySpec)) {
+                EdECPublicKey edKey = (EdECPublicKey) key;
+                return keySpec.cast(
+                    new EdECPublicKeySpec(edKey.getParams(), edKey.getPoint()));
+            } else {
+                throw new InvalidKeySpecException(
+                    "KeySpec must be X509EncodedKeySpec or EdECPublicKeySpec");
+            }
+        } else if (key instanceof EdECPrivateKey) {
+            checkLockedParams(InvalidKeySpecException::new,
+                ((EdECPrivateKey) key).getParams());
+
+            if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) {
+                if (!key.getFormat().equals("PKCS#8")) {
+                    throw new InvalidKeySpecException("Format is not PKCS#8");
+                }
+                return keySpec.cast(new PKCS8EncodedKeySpec(key.getEncoded()));
+            } else if (EdECPrivateKeySpec.class.isAssignableFrom(keySpec)) {
+                EdECPrivateKey edKey = (EdECPrivateKey) key;
+                byte[] scalar = edKey.getBytes().orElseThrow(
+                    () -> new InvalidKeySpecException("No private key value")
+                );
+                return keySpec.cast(
+                    new EdECPrivateKeySpec(edKey.getParams(), scalar));
+            } else {
+                throw new InvalidKeySpecException
+                ("KeySpec must be PKCS8EncodedKeySpec or EdECPrivateKeySpec");
+            }
+        } else {
+            throw new InvalidKeySpecException("Unsupported key type");
+        }
+    }
+
+    public static class Ed25519 extends EdDSAKeyFactory {
+
+        public Ed25519() {
+            super(NamedParameterSpec.ED25519);
+        }
+    }
+
+    public static class Ed448 extends EdDSAKeyFactory {
+
+        public Ed448() {
+            super(NamedParameterSpec.ED448);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ed/EdDSAKeyPairGenerator.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2020, 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.ed;
+
+//import java.security.*;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.KeyPair;
+import java.security.KeyPairGeneratorSpi;
+import java.security.NoSuchAlgorithmException;
+import java.security.ProviderException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.EdECPoint;
+import java.security.spec.NamedParameterSpec;
+
+import sun.security.jca.JCAUtil;
+import sun.security.util.SecurityProviderConstants;
+
+/**
+ * Key pair generator for the EdDSA signature algorithm.
+ */
+public class EdDSAKeyPairGenerator extends KeyPairGeneratorSpi {
+
+    private SecureRandom random = null;
+    private EdDSAOperations ops = null;
+    private EdDSAParameters lockedParams = null;
+
+    public EdDSAKeyPairGenerator() {
+        initialize(SecurityProviderConstants.DEF_ED_KEY_SIZE, null);
+    }
+
+    private EdDSAKeyPairGenerator(NamedParameterSpec paramSpec) {
+        tryInitialize(paramSpec);
+        lockedParams = ops.getParameters();
+    }
+
+    private void tryInitialize(NamedParameterSpec paramSpec) {
+        try {
+            initialize(paramSpec, null);
+        } catch (InvalidAlgorithmParameterException ex) {
+            String name = paramSpec.getName();
+            throw new ProviderException(name + " not supported");
+        }
+    }
+
+    @Override
+    public void initialize(int keySize, SecureRandom random) {
+
+        EdDSAParameters params = EdDSAParameters.getBySize(
+            InvalidParameterException::new, keySize);
+
+        initializeImpl(params, random);
+    }
+
+    @Override
+    public void initialize(AlgorithmParameterSpec params, SecureRandom random)
+            throws InvalidAlgorithmParameterException {
+
+        EdDSAParameters edParams = EdDSAParameters.get(
+            InvalidAlgorithmParameterException::new, params);
+
+        try {
+            initializeImpl(edParams, random);
+        } catch (InvalidParameterException e) {
+            throw new InvalidAlgorithmParameterException(e);
+        }
+    }
+
+    private void initializeImpl(EdDSAParameters params, SecureRandom random) {
+
+        if (lockedParams != null && lockedParams != params) {
+            throw new InvalidParameterException("Parameters must be " +
+                lockedParams.getName());
+        }
+
+        try {
+            this.ops = new EdDSAOperations(params);
+        } catch (NoSuchAlgorithmException ex) {
+            throw new ProviderException(ex);
+        }
+        this.random = random == null ? JCAUtil.getSecureRandom() : random;
+    }
+
+
+    @Override
+    public KeyPair generateKeyPair() {
+
+        byte[] privateKey = ops.generatePrivate(random);
+        EdECPoint publicKey = ops.computePublic(privateKey);
+
+        try {
+            return new KeyPair(
+                new EdDSAPublicKeyImpl(ops.getParameters(), publicKey),
+                new EdDSAPrivateKeyImpl(ops.getParameters(), privateKey)
+            );
+        } catch (InvalidKeyException ex) {
+            throw new ProviderException(ex);
+        }
+    }
+
+    public static class Ed25519 extends EdDSAKeyPairGenerator {
+
+        public Ed25519() {
+            super(NamedParameterSpec.ED25519);
+        }
+    }
+
+    public static class Ed448 extends EdDSAKeyPairGenerator {
+
+        public Ed448() {
+            super(NamedParameterSpec.ED448);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ed/EdDSAOperations.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2020, 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.ed;
+
+import sun.security.ec.point.AffinePoint;
+import sun.security.ec.point.Point;
+import sun.security.util.ArrayUtil;
+import sun.security.util.math.IntegerFieldModuloP;
+import sun.security.util.math.IntegerModuloP;
+import sun.security.util.math.MutableIntegerModuloP;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.SignatureException;
+import java.security.spec.EdDSAParameterSpec;
+import java.security.spec.EdECPoint;
+import java.util.Arrays;
+import java.util.function.Function;
+
+/*
+ * A class containing the operations of the EdDSA signature scheme. The
+ * parameters include an object that performs the elliptic curve point
+ * arithmetic, and EdDSAOperations uses this object to construct the signing
+ * and verification operations.
+ */
+public class EdDSAOperations {
+
+    private final EdDSAParameters params;
+
+    public EdDSAOperations(EdDSAParameters params)
+        throws NoSuchAlgorithmException {
+
+        this.params = params;
+    }
+
+    public EdDSAParameters getParameters() {
+        return params;
+    }
+
+    public byte[] generatePrivate(SecureRandom random) {
+        byte[] result = new byte[params.getKeyLength()];
+        random.nextBytes(result);
+        return result;
+    }
+
+    public EdECPoint computePublic(byte[] privateKey) {
+        byte[] privateKeyHash = params.digest(privateKey);
+        int byteLength = privateKeyHash.length / 2;
+        byte[] s = Arrays.copyOf(privateKeyHash, byteLength);
+        prune(s);
+        IntegerModuloP fieldS = params.getOrderField().getElement(s);
+        fieldS.asByteArray(s);
+        Point A = params.getEdOperations().basePointMultiply(s);
+        return asEdECPoint(A.asAffine());
+    }
+
+    private static EdECPoint asEdECPoint(AffinePoint p) {
+        return new EdECPoint(p.getX().asBigInteger().testBit(0),
+            p.getY().asBigInteger());
+    }
+
+    public byte[] sign(EdDSAParameterSpec sigParams, byte[] privateKey,
+                       byte[] message) {
+
+        byte[] privateKeyHash = params.digest(privateKey);
+
+        int byteLength = privateKeyHash.length / 2;
+        byte[] s = Arrays.copyOf(privateKeyHash, byteLength);
+        prune(s);
+        IntegerModuloP sElem = params.getOrderField().getElement(s);
+        sElem.asByteArray(s);
+        Point A = params.getEdOperations().basePointMultiply(s);
+        byte[] prefix = Arrays.copyOfRange(privateKeyHash,
+            privateKeyHash.length / 2, privateKeyHash.length);
+        byte[] dom = params.dom(sigParams);
+        byte[] r = params.digest(dom, prefix, message);
+
+        // reduce r modulo the order
+        IntegerModuloP fieldR = params.getOrderField().getElement(r);
+        r = new byte[params.getKeyLength()];
+        fieldR.asByteArray(r);
+
+        Point R = params.getEdOperations().basePointMultiply(r);
+
+        byte[] encodedR = encode(byteLength, R);
+        byte[] encodedA = encode(byteLength, A);
+        byte[] k = params.digest(dom, encodedR, encodedA, message);
+
+        // S computation is in group-order field
+        IntegerFieldModuloP subField = params.getOrderField();
+        IntegerModuloP kElem = subField.getElement(k);
+        IntegerModuloP rElem = subField.getElement(r);
+        MutableIntegerModuloP S = kElem.mutable().setProduct(sElem);
+        S.setSum(rElem);
+        // need to be reduced before output conversion
+        S.setReduced();
+        byte[] sArr = S.asByteArray(byteLength);
+        byte[] rArr = encode(byteLength, R);
+
+        byte[] result = new byte[byteLength * 2];
+        System.arraycopy(rArr, 0, result, 0, byteLength);
+        System.arraycopy(sArr, 0, result, byteLength, byteLength);
+        return result;
+    }
+
+    public boolean verify(EdDSAParameterSpec sigParams, AffinePoint affineA,
+                          byte[] publicKey, byte[] message, byte[] signature)
+        throws SignatureException {
+
+        if (signature == null) {
+            throw new SignatureException("signature was null");
+        }
+        byte[] encR = Arrays.copyOf(signature, signature.length / 2);
+        byte[] encS = Arrays.copyOfRange(signature, signature.length / 2,
+            signature.length);
+
+        // reject s if it is too large
+        ArrayUtil.reverse(encS);
+        BigInteger bigS = new BigInteger(1, encS);
+        if (bigS.compareTo(params.getOrderField().getSize()) >= 0) {
+            throw new SignatureException("s is too large");
+        }
+        ArrayUtil.reverse(encS);
+
+        byte[] dom = params.dom(sigParams);
+        AffinePoint affineR = decodeAffinePoint(SignatureException::new, encR);
+        byte[] k = params.digest(dom, encR, publicKey, message);
+        // reduce k to improve performance of multiply
+        IntegerFieldModuloP subField = params.getOrderField();
+        IntegerModuloP kElem = subField.getElement(k);
+        k = kElem.asByteArray(k.length / 2);
+
+        Point pointR = params.getEdOperations().of(affineR);
+        Point pointA = params.getEdOperations().of(affineA);
+
+        EdECOperations edOps = params.getEdOperations();
+        Point lhs = edOps.basePointMultiply(encS);
+        Point rhs = edOps.setSum(edOps.setProduct(pointA.mutable(), k),
+            pointR.mutable());
+
+        return lhs.affineEquals(rhs);
+    }
+
+    public boolean verify(EdDSAParameterSpec sigParams, byte[] publicKey,
+                          byte[] message, byte[] signature)
+        throws InvalidKeyException, SignatureException {
+
+        AffinePoint affineA = decodeAffinePoint(InvalidKeyException::new,
+            publicKey);
+        return verify(sigParams, affineA, publicKey, message, signature);
+    }
+
+    public
+    <T extends Throwable>
+    AffinePoint decodeAffinePoint(Function<String, T> exception, byte[] arr)
+    throws T {
+
+        if (arr.length != params.getKeyLength()) {
+            throw exception.apply("incorrect length");
+        }
+
+        arr = arr.clone();
+        int xLSB = (0xFF & arr[arr.length - 1]) >>> 7;
+        arr[arr.length - 1] &= 0x7F;
+        int yLength = (params.getBits() + 7) >> 3;
+        IntegerModuloP y =
+            params.getField().getElement(arr, 0, yLength, (byte) 0);
+        // reject non-canonical y values
+        ArrayUtil.reverse(arr);
+        BigInteger bigY = new BigInteger(1, arr);
+        if (bigY.compareTo(params.getField().getSize()) >= 0) {
+            throw exception.apply("y value is too large");
+        }
+        return params.getEdOperations().decodeAffinePoint(exception, xLSB, y);
+    }
+
+    public
+    <T extends Throwable>
+    AffinePoint decodeAffinePoint(Function<String, T> exception,
+                                  EdECPoint point)
+        throws T {
+
+        // reject non-canonical y values
+        if (point.getY().compareTo(params.getField().getSize()) >= 0) {
+            throw exception.apply("y value is too large");
+        }
+
+        int xLSB = point.isXOdd() ? 1 : 0;
+        IntegerModuloP y = params.getField().getElement(point.getY());
+        return params.getEdOperations().decodeAffinePoint(exception, xLSB, y);
+    }
+
+    /**
+     * Mask off the high order bits of an encoded integer in an array. The
+     * array is modified in place.
+     *
+     * @param arr an array containing an encoded integer
+     * @param bits the number of bits to keep
+     * @return the number, in range [0,8], of bits kept in the highest byte
+     */
+    private static int maskHighOrder(byte[] arr, int bits) {
+
+        int lastByteIndex = arr.length - 1;
+        int bitsDiff = arr.length * 8 - bits;
+        int highBits = 8 - bitsDiff;
+        byte msbMaskOff = (byte) ((1 << highBits) - 1);
+        arr[lastByteIndex] &= msbMaskOff;
+
+        return highBits;
+    }
+
+    /**
+     * Prune an encoded scalar value by modifying it in place. The extra
+     * high-order bits are masked off, the highest valid bit it set, and the
+     * number is rounded down to a multiple of the co-factor.
+     *
+     * @param k an encoded scalar value
+     * @param bits the number of bits in the scalar
+     * @param logCofactor the base-2 logarithm of the co-factor
+     */
+    private static void prune(byte[] k, int bits, int logCofactor) {
+
+        int lastByteIndex = k.length - 1;
+
+        // mask off unused high-order bits
+        int highBits = maskHighOrder(k, bits);
+
+        // set the highest bit
+        if (highBits == 0) {
+            k[lastByteIndex - 1] |= 0x80;
+        } else {
+            byte msbMaskOn = (byte) (1 << (highBits - 1));
+            k[lastByteIndex] |= msbMaskOn;
+        }
+
+        // round down to a multiple of the co-factor
+        byte lsbMaskOff = (byte) (0xFF << logCofactor);
+        k[0] &= lsbMaskOff;
+    }
+
+    void prune(byte[] arr) {
+        prune(arr, params.getBits(), params.getLogCofactor());
+    }
+
+    private static byte[] encode(int length, Point p) {
+        return encode(length, p.asAffine());
+    }
+
+    private static byte[] encode(int length, AffinePoint p) {
+        byte[] result = p.getY().asByteArray(length);
+        int xLSB = p.getX().asByteArray(1)[0] & 0x01;
+        result[result.length - 1] |= (xLSB << 7);
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ed/EdDSAParameters.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 2020, 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.ed;
+
+import sun.security.ec.ParametersMap;
+import sun.security.provider.SHAKE256;
+import sun.security.util.ObjectIdentifier;
+import sun.security.util.math.*;
+import sun.security.util.math.intpoly.*;
+import sun.security.x509.AlgorithmId;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.security.*;
+import java.security.spec.*;
+import java.util.function.Function;
+
+/*
+ * The set of parameters that defines an instance of the EdDSA signature
+ * scheme.
+ */
+public class EdDSAParameters {
+
+    public interface DigesterFactory {
+        // Default digest creator
+        Digester createDigester();
+
+        // Override this method if multiple key lengths are needed
+        default Digester createDigester(int len) {
+            return createDigester();
+        }
+
+        // Return a digest over all the provided byte arrays
+        default byte[] digest(byte[]... data) {
+            Digester d = createDigester();
+            for (byte[] curData : data) {
+                d.update(curData, 0, curData.length);
+            }
+            return d.digest();
+        }
+    }
+
+    // Hash for Ed25519
+    private static class SHA512DigesterFactory implements DigesterFactory {
+        @Override
+        public Digester createDigester() {
+            try {
+                MessageDigest md = MessageDigest.getInstance("SHA-512");
+                return new MessageDigester(md);
+            } catch (NoSuchAlgorithmException ex) {
+                throw new ProviderException(ex);
+            }
+        }
+    }
+
+    // Hash for Ed448
+    private static class SHAKE256DigesterFactory implements DigesterFactory {
+        @Override
+        // Most usage for Ed448 is 114bytes long
+        public Digester createDigester() {
+            return new SHAKE256Digester(114);
+        }
+
+        // Ed448 uses 64bytes long hasg for the signature message
+        @Override
+        public Digester createDigester(int len) {
+            return new SHAKE256Digester(len);
+        }
+    }
+
+    public interface Digester {
+        void update(byte data);
+        void update(byte[] data, int off, int len);
+        byte[] digest();
+    }
+
+    private static class MessageDigester implements Digester {
+        private final MessageDigest md;
+
+        private MessageDigester(MessageDigest md) {
+            this.md = md;
+        }
+
+        @Override
+        public void update(byte data) {
+            md.update(data);
+        }
+        @Override
+        public void update(byte[] data, int off, int len) {
+            md.update(data, off, len);
+        }
+        @Override
+        public byte[] digest() {
+            return md.digest();
+        }
+    }
+
+    private static class SHAKE256Digester implements Digester {
+        SHAKE256 md;
+
+        SHAKE256Digester(int len) {
+            md = new SHAKE256(len);
+        }
+        @Override
+        public void update(byte data) {
+            md.update(data);
+        }
+        @Override
+        public void update(byte[] data, int off, int len) {
+            md.update(data, off, len);
+        }
+        @Override
+        public byte[] digest() {
+            return md.digest();
+        }
+    }
+
+    static ParametersMap<EdDSAParameters> namedParams = new ParametersMap<>();
+
+    private final String name;
+    private final ObjectIdentifier oid;
+    private final IntegerFieldModuloP field;
+    private final IntegerFieldModuloP orderField;
+    private final ImmutableIntegerModuloP d;
+    private final EdECOperations edOperations;
+    private final DigesterFactory digester;
+    private final int keyLength;
+    private final int bits;
+    private final int logCofactor;
+    private final Function<EdDSAParameterSpec, byte[]> dom;
+
+    public EdDSAParameters(String name, ObjectIdentifier oid,
+                           IntegerFieldModuloP field,
+                           IntegerFieldModuloP orderField,
+                           ImmutableIntegerModuloP d,
+                           EdECOperations edOps,
+                           DigesterFactory digester,
+                           Function<EdDSAParameterSpec, byte[]> dom,
+                           int keyLength, int bits, int logCofactor) {
+        this.oid = oid;
+        this.name = name;
+        this.field = field;
+        this.orderField = orderField;
+        this.d = d;
+        this.edOperations = edOps;
+        this.digester = digester;
+        this.keyLength = keyLength;
+        this.bits = bits;
+        this.logCofactor = logCofactor;
+        this.dom = dom;
+    }
+
+    public String getName() {
+        return name;
+    }
+    public ObjectIdentifier getOid() {
+        return oid;
+    }
+    public IntegerFieldModuloP getField() {
+        return field;
+    }
+    public IntegerFieldModuloP getOrderField() {
+        return orderField;
+    }
+    public ImmutableIntegerModuloP getD() {
+        return d;
+    }
+    public EdECOperations getEdOperations() {
+        return edOperations;
+    }
+    public int getKeyLength() {
+        return keyLength;
+    }
+    public int getBits() {
+        return bits;
+    }
+    public int getLogCofactor() {
+        return logCofactor;
+    }
+
+    public Digester createDigester() {
+        return digester.createDigester();
+    }
+
+    public Digester createDigester(int len) {
+        return digester.createDigester(len);
+    }
+
+    public byte[] digest(byte[]... data) {
+        return digester.digest(data);
+    }
+
+    public byte[] dom(EdDSAParameterSpec sigParams) {
+        return dom.apply(sigParams);
+    }
+
+    private final static String prefixStr25519 =
+        "SigEd25519 no Ed25519 collisions";
+    private final static String prefixStr448 = "SigEd448";
+
+    // Used for Ed25519
+    static byte[] dom2(EdDSAParameterSpec sigParams) {
+        if (!sigParams.isPrehash() && !sigParams.getContext().isPresent()) {
+            return new byte[0];
+        }
+        return domImpl(prefixStr25519, sigParams);
+    }
+
+    // Used for Ed488
+    static byte[] dom4(EdDSAParameterSpec sigParams) {
+        return domImpl(prefixStr448, sigParams);
+    }
+
+    static byte[] domImpl(String prefixStr, EdDSAParameterSpec sigParams) {
+        byte[] prefix = prefixStr.getBytes(StandardCharsets.US_ASCII);
+        byte[] context = sigParams.getContext().orElse(new byte[0]);
+        int length = prefix.length + 2 + context.length;
+        byte[] result = new byte[length];
+        System.arraycopy(prefix, 0, result, 0, prefix.length);
+        byte x = (byte) (sigParams.isPrehash() ? 1 : 0);
+        result[prefix.length] = x;
+        result[prefix.length + 1] = (byte) context.length;
+        System.arraycopy(context, 0, result, prefix.length + 2,
+            context.length);
+        return result;
+    }
+
+    static {
+        // set up Ed25519
+        try {
+            IntegerFieldModuloP ed25519Field = new IntegerPolynomial25519();
+            IntegerFieldModuloP ed25519OrderField = new Curve25519OrderField();
+            BigInteger biD = new BigInteger("3709570593466943934313808350875" +
+                "4565189542113879843219016388785533085940283555");
+            ImmutableIntegerModuloP d = ed25519Field.getElement(biD);
+            BigInteger baseX = new BigInteger("15112221349535400772501151409" +
+                "588531511454012693041857206046113283949847762202");
+            BigInteger baseY = new BigInteger("46316835694926478169428394003" +
+                "475163141307993866256225615783033603165251855960");
+            EdECOperations edOps = new Ed25519Operations(d, baseX, baseY);
+            String name = NamedParameterSpec.ED25519.getName();
+            ObjectIdentifier oid = new ObjectIdentifier("1.3.101.112");
+            int bits = 255;
+            DigesterFactory digester = new SHA512DigesterFactory();
+            EdDSAParameters params = new EdDSAParameters(name, oid,
+                ed25519Field, ed25519OrderField, d, edOps,
+                digester, EdDSAParameters::dom2, 32, bits, 3);
+
+            namedParams.put(name, oid, bits, params);
+
+        } catch (IOException ex) {
+            // Unable to set Ed25519 parameters---it will be disabled
+        }
+
+        // set up Ed448
+        try {
+            IntegerFieldModuloP ed448Field = new IntegerPolynomial448();
+            IntegerFieldModuloP ed448OrderField = new Curve448OrderField();
+            BigInteger biD = ed448Field.getSize().subtract(
+                new BigInteger("39081"));
+            ImmutableIntegerModuloP d = ed448Field.getElement(biD);
+            BigInteger baseX = new BigInteger("224580040295924300187604334" +
+                "099896036246789641632564134246125461686950415467406032909" +
+                "029192869357953282578032075146446173674602635247710");
+            BigInteger baseY = new BigInteger("298819210078481492676017930" +
+                "443930673437544040154080242095928241372331506189835876003" +
+                "536878655418784733982303233503462500531545062832660");
+            EdECOperations edOps = new Ed448Operations(d, baseX, baseY);
+            String name = NamedParameterSpec.ED448.getName();
+            ObjectIdentifier oid = new ObjectIdentifier("1.3.101.113");
+            int bits = 448;
+            DigesterFactory digester = new SHAKE256DigesterFactory();
+            EdDSAParameters params = new EdDSAParameters(name, oid,
+                ed448Field, ed448OrderField, d, edOps,
+                digester, EdDSAParameters::dom4, 57, bits, 2);
+
+            namedParams.put(name, oid, bits, params);
+
+        } catch (IOException ex) {
+            // Unable to set Ed448 parameters---it will be disabled
+        }
+
+        namedParams.fix();
+    }
+
+    public static
+    <T extends Throwable>
+    EdDSAParameters getBySize(Function<String, T> exception,
+                            int size) throws T {
+
+        return namedParams.getBySize(exception, size);
+    }
+
+    public static
+    <T extends Throwable>
+    EdDSAParameters get(Function<String, T> exception,
+                      AlgorithmId algId) throws T {
+
+        return namedParams.get(exception, algId);
+    }
+
+    public static
+    <T extends Throwable>
+    EdDSAParameters get(Function<String, T> exception,
+                      AlgorithmParameterSpec params) throws T {
+
+        return namedParams.get(exception, params);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ed/EdDSAPrivateKeyImpl.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2020, 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.ed;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.ProviderException;
+import java.security.interfaces.EdECPrivateKey;
+import java.util.Optional;
+import java.security.spec.NamedParameterSpec;
+
+import sun.security.pkcs.PKCS8Key;
+import sun.security.x509.AlgorithmId;
+import sun.security.util.*;
+
+public final class EdDSAPrivateKeyImpl
+    extends PKCS8Key implements EdECPrivateKey {
+
+    private static final long serialVersionUID = 1L;
+
+    private final NamedParameterSpec paramSpec;
+    private byte[] h;
+
+    EdDSAPrivateKeyImpl(EdDSAParameters params, byte[] h)
+        throws InvalidKeyException {
+
+        this.paramSpec = new NamedParameterSpec(params.getName());
+        this.algid = new AlgorithmId(params.getOid());
+        this.h = h.clone();
+
+        encodeKey();
+
+        checkLength(params);
+    }
+
+    EdDSAPrivateKeyImpl(byte[] encoded) throws InvalidKeyException {
+
+        decode(encoded);
+        EdDSAParameters params = EdDSAParameters.get(
+            InvalidKeyException::new, algid);
+        paramSpec = new NamedParameterSpec(params.getName());
+
+        decodeKey();
+
+        checkLength(params);
+    }
+
+    private void decodeKey() throws InvalidKeyException {
+        try {
+            DerInputStream derStream = new DerInputStream(key);
+            h = derStream.getOctetString();
+        } catch (IOException ex) {
+            throw new InvalidKeyException(ex);
+        }
+    }
+
+    private void encodeKey() {
+        DerOutputStream derKey = new DerOutputStream();
+        try {
+            derKey.putOctetString(h);
+            this.key = derKey.toByteArray();
+        } catch (IOException ex) {
+            throw new ProviderException(ex);
+        }
+    }
+
+    void checkLength(EdDSAParameters params) throws InvalidKeyException {
+
+        if (params.getKeyLength() != this.h.length) {
+            throw new InvalidKeyException("key length is " + this.h.length +
+                ", key length must be " + params.getKeyLength());
+        }
+    }
+
+    public byte[] getKey() {
+        return h.clone();
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return "EdDSA";
+    }
+
+    @Override
+    public NamedParameterSpec getParams() {
+        return paramSpec;
+    }
+
+    @Override
+    public Optional<byte[]> getBytes() {
+        return Optional.of(getKey());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ed/EdDSAPublicKeyImpl.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2020, 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.ed;
+
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.KeyRep;
+import java.security.interfaces.EdECPublicKey;
+import java.security.spec.EdECPoint;
+import java.security.spec.NamedParameterSpec;
+import java.util.Arrays;
+
+import sun.security.util.BitArray;
+import sun.security.x509.AlgorithmId;
+import sun.security.x509.X509Key;
+
+public final class EdDSAPublicKeyImpl extends X509Key implements EdECPublicKey {
+
+    private static final long serialVersionUID = 1L;
+
+    private final EdECPoint point;
+    private final NamedParameterSpec paramSpec;
+
+    public EdDSAPublicKeyImpl(EdDSAParameters params, EdECPoint point)
+            throws InvalidKeyException {
+        this.paramSpec = new NamedParameterSpec(params.getName());
+        this.algid = new AlgorithmId(params.getOid());
+        this.point = point;
+
+        byte[] encodedPoint = point.getY().toByteArray();
+        reverse(encodedPoint);
+        // array may be too large or too small, depending on the value
+        encodedPoint = Arrays.copyOf(encodedPoint, params.getKeyLength());
+        // set the high-order bit of the encoded point
+        byte msb = (byte) (point.isXOdd() ? 0x80 : 0);
+        encodedPoint[encodedPoint.length - 1] |= msb;
+        setKey(new BitArray(encodedPoint.length * 8, encodedPoint));
+
+        checkLength(params);
+    }
+
+    public EdDSAPublicKeyImpl(byte[] encoded) throws InvalidKeyException {
+        decode(encoded);
+
+        EdDSAParameters params =
+            EdDSAParameters.get(InvalidKeyException::new, algid);
+        this.paramSpec = new NamedParameterSpec(params.getName());
+        // construct the EdECPoint representation
+        byte[] encodedPoint = getKey().toByteArray();
+        byte msb = encodedPoint[encodedPoint.length - 1];
+        encodedPoint[encodedPoint.length - 1] &= (byte) 0x7F;
+        boolean xOdd = (msb & 0x80) != 0;
+        reverse(encodedPoint);
+        BigInteger y = new BigInteger(1, encodedPoint);
+        this.point = new EdECPoint(xOdd, y);
+
+        checkLength(params);
+    }
+
+    void checkLength(EdDSAParameters params) throws InvalidKeyException {
+        if (params.getKeyLength() * 8 != getKey().length()) {
+            throw new InvalidKeyException(
+                "key length must be " + params.getKeyLength());
+        }
+    }
+
+    public byte[] getEncodedPoint() {
+        return getKey().toByteArray();
+    }
+
+    @Override
+    public EdECPoint getPoint() {
+        return point;
+    }
+
+    @Override
+    public NamedParameterSpec getParams() {
+        return paramSpec;
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return "EdDSA";
+    }
+
+    protected Object writeReplace() throws java.io.ObjectStreamException {
+        return new KeyRep(KeyRep.Type.PUBLIC, getAlgorithm(), getFormat(),
+                getEncoded());
+    }
+
+    private static void swap(byte[] arr, int i, int j) {
+        byte tmp = arr[i];
+        arr[i] = arr[j];
+        arr[j] = tmp;
+    }
+
+    private static void reverse(byte [] arr) {
+        int i = 0;
+        int j = arr.length - 1;
+
+        while (i < j) {
+            swap(arr, i, j);
+            i++;
+            j--;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ed/EdDSASignature.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2020, 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.ed;
+
+import sun.security.ec.point.AffinePoint;
+
+import java.io.ByteArrayOutputStream;
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.ProviderException;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.SignatureException;
+import java.security.SignatureSpi;
+import java.security.interfaces.EdECPrivateKey;
+import java.security.interfaces.EdECPublicKey;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.EdDSAParameterSpec;
+import java.security.spec.NamedParameterSpec;
+import java.util.function.Function;
+
+public class EdDSASignature extends SignatureSpi {
+
+    private interface MessageAccumulator {
+        void add(byte b);
+        void add(byte[] data, int off, int len);
+        byte[] getMessage();
+    }
+
+    private static class DigestAccumulator implements MessageAccumulator {
+        private final EdDSAParameters.Digester digester;
+
+        DigestAccumulator(EdDSAParameters.Digester digester) {
+            this.digester = digester;
+        }
+
+        @Override
+        public void add(byte b) {
+            digester.update(b);
+        }
+        @Override
+        public void add(byte[] data, int off, int len) {
+            digester.update(data, off, len);
+        }
+        @Override
+        public byte[] getMessage() {
+            return digester.digest();
+        }
+    }
+
+    private static class MemoryAccumulator implements MessageAccumulator {
+        ByteArrayOutputStream message = new ByteArrayOutputStream();
+
+        @Override
+        public void add(byte b) {
+            message.write(b);
+        }
+        @Override
+        public void add(byte[] data, int off, int len) {
+            message.write(data, off, len);
+        }
+        @Override
+        public byte[] getMessage() {
+            return message.toByteArray();
+        }
+    }
+
+    private byte[] privateKey;
+    private AffinePoint publicKeyPoint;
+    private byte[] publicKeyBytes;
+    private EdDSAOperations ops;
+    private EdDSAParameters lockedParams = null;
+    private MessageAccumulator message = null;
+    private EdDSAParameterSpec sigParams = new EdDSAParameterSpec(false);
+
+    public EdDSASignature() {
+        // do nothing
+    }
+
+    EdDSASignature(NamedParameterSpec paramSpec) {
+        lockedParams = EdDSAParameters.get(ProviderException::new, paramSpec);
+    }
+
+    @Override
+    protected void engineInitVerify(PublicKey publicKey)
+        throws InvalidKeyException {
+
+        if (!(publicKey instanceof EdECPublicKey)) {
+            throw new InvalidKeyException("Unsupported key type");
+        }
+        EdECPublicKey edKey = (EdECPublicKey) publicKey;
+        EdDSAParameters params = EdDSAParameters.get(
+            InvalidKeyException::new, edKey.getParams());
+
+        initImpl(params);
+        this.privateKey = null;
+        this.publicKeyPoint = ops.decodeAffinePoint(InvalidKeyException::new,
+            edKey.getPoint());
+        EdDSAPublicKeyImpl pubKeyImpl = new EdDSAPublicKeyImpl(params,
+            edKey.getPoint());
+        this.publicKeyBytes = pubKeyImpl.getEncodedPoint();
+    }
+
+    @Override
+    protected void engineInitSign(PrivateKey privateKey)
+        throws InvalidKeyException {
+        engineInitSign(privateKey, null);
+    }
+
+    @Override
+    protected void engineInitSign(PrivateKey privateKey, SecureRandom random)
+        throws InvalidKeyException {
+
+        if (!(privateKey instanceof EdECPrivateKey)) {
+            throw new InvalidKeyException("Unsupported key type");
+        }
+        EdECPrivateKey edKey = (EdECPrivateKey) privateKey;
+
+        initImpl(edKey.getParams());
+        this.privateKey = edKey.getBytes().orElseThrow(
+        () -> new InvalidKeyException("No private key value"));
+        this.publicKeyPoint = null;
+        this.publicKeyBytes = null;
+    }
+
+    private
+    <T extends Throwable>
+    void checkLockedParams(Function<String, T> exception,
+                           EdDSAParameters params) throws T {
+        if (lockedParams != null && lockedParams != params) {
+            throw exception.apply("Parameters must be " +
+            lockedParams.getName());
+        }
+    }
+
+    private void ensureMessageInit() throws SignatureException {
+        if (message == null) {
+            initMessage();
+        }
+    }
+
+    private void initMessage() throws SignatureException {
+        if (this.ops == null) {
+            throw new SignatureException("not initialized");
+        }
+        EdDSAParameters params = ops.getParameters();
+
+        if (sigParams.isPrehash()) {
+            this.message = new DigestAccumulator(params.createDigester(64));
+        } else {
+            this.message = new MemoryAccumulator();
+        }
+    }
+
+    @Override
+    protected void engineUpdate(byte b) throws SignatureException {
+        ensureMessageInit();
+        this.message.add(b);
+    }
+
+    @Override
+    protected void engineUpdate(byte[] b, int off, int len)
+        throws SignatureException {
+
+        ensureMessageInit();
+        this.message.add(b, off, len);
+    }
+
+    @Override
+    protected byte[] engineSign() throws SignatureException {
+        if (privateKey == null) {
+            throw new SignatureException("Missing private key");
+        }
+        ensureMessageInit();
+        byte[] result = ops.sign(this.sigParams, this.privateKey,
+            message.getMessage());
+        message = null;
+        return result;
+    }
+
+    @Override
+    protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
+        if (publicKeyBytes == null) {
+            throw new SignatureException("Missing publicKey");
+        }
+        if (message == null) {
+            return false;
+        }
+        boolean result = ops.verify(this.sigParams, this.publicKeyPoint,
+            this.publicKeyBytes, message.getMessage(), sigBytes);
+        message = null;
+        return result;
+    }
+
+    private void initImpl(EdDSAParameters params) throws InvalidKeyException {
+        checkLockedParams(InvalidKeyException::new, params);
+
+        try {
+            this.ops = new EdDSAOperations(params);
+        } catch (NoSuchAlgorithmException ex) {
+            throw new ProviderException(ex);
+        }
+        // message is (re)set to null
+        // it will be initialized on first update
+        this.message = null;
+    }
+
+    private void initImpl(NamedParameterSpec paramSpec)
+        throws InvalidKeyException {
+
+        EdDSAParameters params = EdDSAParameters.get(
+            InvalidKeyException::new, paramSpec);
+        initImpl(params);
+    }
+
+    @Deprecated
+    @Override
+    protected Object engineGetParameter(String param)
+    throws InvalidParameterException {
+        throw new UnsupportedOperationException("getParameter() not supported");
+    }
+
+    @Deprecated
+    @Override
+    protected void engineSetParameter(String param, Object value)
+        throws InvalidParameterException {
+
+        throw new UnsupportedOperationException("setParameter() not supported");
+    }
+
+    @Override
+    protected void engineSetParameter(AlgorithmParameterSpec params)
+        throws InvalidAlgorithmParameterException {
+
+        // by convention, ignore null parameters
+        if (params == null) {
+            return;
+        }
+
+        if (params instanceof EdDSAParameterSpec) {
+            if (message != null) {
+                // sign/verify in progress
+                throw new InvalidParameterException("Cannot change signature " +
+                    "parameters during operation");
+            }
+            EdDSAParameterSpec edDsaParams = (EdDSAParameterSpec) params;
+            checkContextLength(edDsaParams);
+
+            this.sigParams = edDsaParams;
+        } else {
+            throw new InvalidAlgorithmParameterException(
+                "Only EdDSAParameterSpec supported");
+        }
+    }
+
+    private static void checkContextLength(EdDSAParameterSpec edDsaParams)
+        throws InvalidAlgorithmParameterException {
+
+        if (edDsaParams.getContext().isPresent()) {
+            byte[] context = edDsaParams.getContext().get();
+            if (context.length > 255) {
+                throw new InvalidAlgorithmParameterException(
+                "Context is longer than 255 bytes");
+            }
+        }
+    }
+
+    // There is no RFC-defined ASN.1 for prehash and context (RFC 8410)
+    @Override
+    protected AlgorithmParameters engineGetParameters() {
+        return null;
+    }
+
+    public static class Ed25519 extends EdDSASignature {
+
+        public Ed25519() {
+            super(NamedParameterSpec.ED25519);
+        }
+    }
+
+    public static class Ed448 extends EdDSASignature {
+
+        public Ed448() {
+            super(NamedParameterSpec.ED448);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ed/EdECOperations.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2020, 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.ed;
+
+import sun.security.ec.point.*;
+import sun.security.util.math.IntegerModuloP;
+import sun.security.util.math.MutableIntegerModuloP;
+
+import java.util.function.Function;
+
+/*
+ * Base class for Edwards curve ECC implementations.
+ */
+public abstract class EdECOperations {
+
+    // Curve-specific base point multiplication.
+    public abstract Point basePointMultiply(byte[] s);
+
+    // Decode curve-specifics to the affinePoint
+    public abstract <T extends Throwable>
+    AffinePoint decodeAffinePoint(Function<String, T> exception,
+                                  int xLSB, IntegerModuloP y) throws T;
+
+    // Curve specific point from an X,Y point
+    public abstract ImmutablePoint of(AffinePoint p);
+
+    /*
+     * Generic method for taking two classes implementing MutablePoint to be
+     * called by the curve-specific setSum()
+     */
+    public MutablePoint setSum(MutablePoint p1, MutablePoint p2) {
+        MutableIntegerModuloP t1 = p2.getField().get1().mutable();
+        MutableIntegerModuloP t2 = p2.getField().get1().mutable();
+        MutableIntegerModuloP t3 = p2.getField().get1().mutable();
+        return setSum(p1, p2, t1, t2, t3);
+    }
+
+    /*
+     * Generic method for taking a class implementing MutablePoint with a
+     * scalar to returning the point product using curve-specific methods.
+     */
+    public MutablePoint setProduct(MutablePoint p1, byte[] s) {
+        MutablePoint p = p1.mutable();
+        p1.setValue(getNeutral());
+        MutablePoint addResult = getNeutral().mutable();
+        MutableIntegerModuloP t1 = p.getField().get0().mutable();
+        MutableIntegerModuloP t2 = p.getField().get0().mutable();
+        MutableIntegerModuloP t3 = p.getField().get0().mutable();
+
+        for (int i = 0; i < s.length * 8; i++) {
+            addResult.setValue(p1);
+            setSum(addResult, p, t1, t2, t3);
+            int swap = bitAt(s, i);
+            p1.conditionalSet(addResult, swap);
+            setDouble(p, t1, t2);
+        }
+
+        return p1;
+    }
+
+    // Abstract method for constructing the neutral point on the curve
+    protected abstract ImmutablePoint getNeutral();
+
+
+    // Abstract method for Curve-specific point addition
+    protected abstract MutablePoint setSum(MutablePoint p1, MutablePoint p2,
+                                           MutableIntegerModuloP t1,
+                                           MutableIntegerModuloP t2,
+                                           MutableIntegerModuloP t3);
+    // Abstract method for Curve-specific point doubling
+    protected abstract MutablePoint setDouble(MutablePoint p,
+                                              MutableIntegerModuloP t1,
+                                              MutableIntegerModuloP t2);
+
+    private static int bitAt(byte[] arr, int index) {
+        int byteIndex = index / 8;
+        int bitIndex = index % 8;
+        return (arr[byteIndex] & (1 << bitIndex)) >> bitIndex;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/point/ExtendedHomogeneousPoint.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2020, 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.*;
+
+import java.math.BigInteger;
+
+/**
+ * Elliptic curve point in extended homogeneous coordinates (X, Y, T, Z) where
+ * an affine point (x, y) is represented using any (X, Y, T, Z) s.t.
+ * x = X/Z, y = Y/Z, and x*y = T/Z.
+ */
+public abstract class ExtendedHomogeneousPoint
+    <T extends IntegerModuloP> implements Point {
+
+    protected final T x;
+    protected final T y;
+    protected final T t;
+    protected final T z;
+
+    protected ExtendedHomogeneousPoint(T x, T y, T t, T z) {
+
+        this.x = x;
+        this.y = y;
+        this.t = t;
+        this.z = z;
+    }
+
+    @Override
+    public IntegerFieldModuloP getField() {
+        return this.x.getField();
+    }
+    @Override
+    public Immutable fixed() {
+        return new Immutable(x.fixed(), y.fixed(), t.fixed(), z.fixed());
+    }
+
+    @Override
+    public Mutable mutable() {
+        return new Mutable(x.mutable(), y.mutable(), t.mutable(), z.mutable());
+    }
+
+    public T getX() {
+        return x;
+    }
+
+    public T getY() {
+        return y;
+    }
+
+    public T getT() {
+        return t;
+    }
+
+    public T getZ() {
+        return z;
+    }
+
+    public AffinePoint asAffine() {
+        IntegerModuloP zInv = z.multiplicativeInverse();
+        return new AffinePoint(x.multiply(zInv), y.multiply(zInv));
+    }
+
+    private static
+    <T1 extends IntegerModuloP, T2 extends IntegerModuloP>
+    boolean affineEquals(ExtendedHomogeneousPoint<T1> p1,
+                         ExtendedHomogeneousPoint<T2> p2) {
+       MutableIntegerModuloP x1 = p1.getX().mutable().setProduct(p2.getZ());
+       MutableIntegerModuloP x2 = p2.getX().mutable().setProduct(p1.getZ());
+       if (!x1.asBigInteger().equals(x2.asBigInteger())) {
+           return false;
+       }
+
+        MutableIntegerModuloP y1 = p1.getY().mutable().setProduct(p2.getZ());
+        MutableIntegerModuloP y2 = p2.getY().mutable().setProduct(p1.getZ());
+        if (!y1.asBigInteger().equals(y2.asBigInteger())) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public boolean affineEquals(Point p) {
+        if (p instanceof ExtendedHomogeneousPoint) {
+            @SuppressWarnings("unchecked")
+            ExtendedHomogeneousPoint<IntegerModuloP> ehp =
+                (ExtendedHomogeneousPoint<IntegerModuloP>) p;
+            return affineEquals(this, ehp);
+        }
+
+        return asAffine().equals(p.asAffine());
+    }
+
+    public static class Immutable
+        extends ExtendedHomogeneousPoint<ImmutableIntegerModuloP>
+        implements ImmutablePoint {
+
+        public Immutable(ImmutableIntegerModuloP x,
+                         ImmutableIntegerModuloP y,
+                         ImmutableIntegerModuloP t,
+                         ImmutableIntegerModuloP z) {
+            super(x, y, t, z);
+        }
+    }
+
+    public static class Mutable
+        extends ExtendedHomogeneousPoint<MutableIntegerModuloP>
+        implements MutablePoint {
+
+        public Mutable(MutableIntegerModuloP x,
+                       MutableIntegerModuloP y,
+                       MutableIntegerModuloP t,
+                       MutableIntegerModuloP z) {
+            super(x, y, t, z);
+        }
+
+        @Override
+        public Mutable conditionalSet(Point p, int set) {
+            if (!(p instanceof ExtendedHomogeneousPoint)) {
+                throw new RuntimeException("Incompatible point");
+            }
+            @SuppressWarnings("unchecked")
+            ExtendedHomogeneousPoint<IntegerModuloP> ehp =
+                (ExtendedHomogeneousPoint<IntegerModuloP>) p;
+            return conditionalSet(ehp, set);
+        }
+
+        private <T extends IntegerModuloP>
+        Mutable conditionalSet(ExtendedHomogeneousPoint<T> ehp, int set) {
+
+            x.conditionalSet(ehp.x, set);
+            y.conditionalSet(ehp.y, set);
+            t.conditionalSet(ehp.t, set);
+            z.conditionalSet(ehp.z, set);
+
+            return this;
+        }
+
+        @Override
+        public Mutable setValue(AffinePoint p) {
+            x.setValue(p.getX());
+            y.setValue(p.getY());
+            t.setValue(p.getX()).setProduct(p.getY());
+            z.setValue(p.getX().getField().get1());
+
+            return this;
+        }
+
+        @Override
+        public Mutable setValue(Point p) {
+
+            @SuppressWarnings("unchecked")
+            ExtendedHomogeneousPoint<IntegerModuloP> ehp =
+                (ExtendedHomogeneousPoint<IntegerModuloP>) p;
+
+            return setValue(ehp);
+        }
+
+        private <T extends IntegerModuloP>
+        Mutable setValue(ExtendedHomogeneousPoint<T> ehp) {
+
+            x.setValue(ehp.x);
+            y.setValue(ehp.y);
+            t.setValue(ehp.t);
+            z.setValue(ehp.z);
+
+            return this;
+        }
+    }
+
+}
--- a/src/jdk.crypto.ec/share/classes/sun/security/ec/point/Point.java	Mon May 18 09:28:06 2020 -0700
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/point/Point.java	Mon May 18 09:42:52 2020 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2020, 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
@@ -38,6 +38,7 @@
 
     IntegerFieldModuloP getField();
     AffinePoint asAffine();
+    boolean affineEquals(Point p);
 
     ImmutablePoint fixed();
     MutablePoint mutable();
--- a/src/jdk.crypto.ec/share/classes/sun/security/ec/point/ProjectivePoint.java	Mon May 18 09:28:06 2020 -0700
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/point/ProjectivePoint.java	Mon May 18 09:42:52 2020 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2020, 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
@@ -77,6 +77,36 @@
         return new AffinePoint(x.multiply(zInv), y.multiply(zInv));
     }
 
+    private static
+    <T1 extends IntegerModuloP, T2 extends IntegerModuloP>
+    boolean affineEquals(ProjectivePoint<T1> p1,
+                         ProjectivePoint<T2> p2) {
+        MutableIntegerModuloP x1 = p1.getX().mutable().setProduct(p2.getZ());
+        MutableIntegerModuloP x2 = p2.getX().mutable().setProduct(p1.getZ());
+        if (!x1.asBigInteger().equals(x2.asBigInteger())) {
+            return false;
+        }
+
+        MutableIntegerModuloP y1 = p1.getY().mutable().setProduct(p2.getZ());
+        MutableIntegerModuloP y2 = p2.getY().mutable().setProduct(p1.getZ());
+        if (!y1.asBigInteger().equals(y2.asBigInteger())) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public boolean affineEquals(Point p) {
+        if (p instanceof ProjectivePoint) {
+            @SuppressWarnings("unchecked")
+            ProjectivePoint<IntegerModuloP> pp =
+                (ProjectivePoint<IntegerModuloP>) p;
+            return affineEquals(this, pp);
+        }
+
+        return asAffine().equals(p.asAffine());
+    }
+
     public static class Immutable
         extends ProjectivePoint<ImmutableIntegerModuloP>
         implements ImmutablePoint {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/ec/ed/EdECKeyFormat.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2020, 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8166597
+ * @summary Check for correct formatting of EdDSA keys
+ * @library /test/lib
+ * @build jdk.test.lib.Convert
+ * @modules java.base/sun.security.util
+ * @run main EdECKeyFormat
+ */
+
+import java.security.*;
+import java.security.spec.*;
+import java.security.interfaces.*;
+import java.io.*;
+import java.nio.file.*;
+import java.math.*;
+import java.util.*;
+
+import jdk.test.lib.Convert;
+
+import sun.security.util.*;
+
+public class EdECKeyFormat {
+
+    private interface Test {
+        public void runTest(Provider p) throws Exception;
+    }
+
+    private static void forEachProvider(Test t, String algName)
+        throws Exception {
+
+        int tested = 0;
+        for (Provider p : Security.getProviders()) {
+            Provider.Service s = p.getService("KeyPairGenerator", algName);
+            if (s != null) {
+                t.runTest(p);
+                tested++;
+            }
+        }
+        if (tested == 0) {
+            throw new RuntimeException("no service found for " + algName);
+        }
+    }
+
+    private static Map<String, String> privKeys = Map.of(
+        "Ed25519",
+        "302e020100300506032b657004220420d4ee72dbf913584ad5b6d8f1f769f8ad3afe" +
+        "7c28cbf1d4fbe097a88f44755842",
+        "Ed448",
+        "3047020100300506032b6571043b043980998f387e05852d217c1d715b177c24aa7b" +
+        "f3f4c3a72223f4983597b9ab2ed4793c30d871c24388b380d80bb36d963f5c276219" +
+        "b0677fed00"
+    );
+
+    private static List<String> pubKeys = List.of(
+        "302a300506032b657003210019bf44096984cdfe8541bac167dc3b96c85086aa30b6" +
+        "b6cb0c5c38ad703166e1"
+    );
+
+    public static void main(String[] args) throws Exception {
+        privKeyTest("Ed25519");
+        privKeyTest("Ed448");
+        pubKeyTest();
+    }
+
+    private static void pubKeyTest() throws Exception {
+        forEachProvider(EdECKeyFormat::pubKeyTest, "EdDSA");
+    }
+
+    private static void pubKeyTest(Provider p) throws Exception {
+        for (String s : pubKeys) {
+            pubKeyTest(p, s);
+        }
+    }
+
+    private static void pubKeyTest(Provider p, String key) throws Exception {
+        // ensure that a properly-formatted key can be read
+        byte[] encodedKey = Convert.hexStringToByteArray(key);
+        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedKey);
+        KeyFactory kf = KeyFactory.getInstance("EdDSA", p);
+        kf.generatePublic(keySpec);
+    }
+
+    private static void privKeyTest(String algName) throws Exception {
+
+        forEachProvider(p -> privKeyTest(algName, p), algName);
+    }
+
+    private static void privKeyTest(String algName, Provider p)
+        throws Exception {
+
+        System.out.println("Testing " + algName + " in " + p.getName());
+
+        // ensure format produced is correct
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance(algName, p);
+        KeyPair kp = kpg.generateKeyPair();
+        PrivateKey priv = kp.getPrivate();
+        checkPrivKeyFormat(priv.getEncoded());
+        KeyFactory kf = KeyFactory.getInstance(algName, p);
+        PKCS8EncodedKeySpec keySpec =
+            kf.getKeySpec(priv, PKCS8EncodedKeySpec.class);
+        checkPrivKeyFormat(keySpec.getEncoded());
+
+        // ensure that a properly-formatted key can be read
+        byte[] encodedKey = Convert.hexStringToByteArray(privKeys.get(algName));
+        keySpec = new PKCS8EncodedKeySpec(encodedKey);
+        kf.generatePrivate(keySpec);
+    }
+
+    private static void checkPrivKeyFormat(byte[] key) throws IOException {
+        // key value should be nested octet strings
+        DerValue val = new DerValue(new ByteArrayInputStream(key));
+        BigInteger version = val.data.getBigInteger();
+        DerValue algId = val.data.getDerValue();
+        byte[] keyValue = val.data.getOctetString();
+        val = new DerValue(new ByteArrayInputStream(keyValue));
+        if (val.tag != DerValue.tag_OctetString) {
+            throw new RuntimeException("incorrect format");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/ec/ed/TestEdDSA.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,581 @@
+/*
+ * Copyright (c) 2020, 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8166597
+ * @summary EdDSA Signature Known Answer Tests (KAT) from RFC 8032
+ * @library /test/lib
+ * @build jdk.test.lib.Convert
+ * @run main TestEdDSA
+ */
+
+import java.security.*;
+import java.security.spec.*;
+import java.util.*;
+import jdk.test.lib.Convert;
+
+public class TestEdDSA {
+
+    private static SecureRandom random = new SecureRandom();
+
+    public static void main(String[] args) throws Exception {
+        runBasicTests();
+        runKAT();
+        runCurveMixTest();
+    }
+
+    private static void runKAT() throws Exception {
+
+        // "pure" Ed25519
+        runSignTest("Ed25519", null,
+            "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
+            "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
+            "",
+            "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e06522490155" +
+            "5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b");
+
+        runSignTest("Ed25519", null,
+            "4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb",
+            "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c",
+            "72",
+            "92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da" +
+            "085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00");
+
+        runSignTest("Ed25519", null,
+            "c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7",
+            "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025",
+            "af82",
+            "6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac" +
+            "18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a");
+
+        runSignTest("Ed25519", null,
+            "f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5",
+            "278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e",
+            "08b8b2b733424243760fe426a4b54908632110a66c2f6591eabd3345e3e4eb98" +
+            "fa6e264bf09efe12ee50f8f54e9f77b1e355f6c50544e23fb1433ddf73be84d8" +
+            "79de7c0046dc4996d9e773f4bc9efe5738829adb26c81b37c93a1b270b20329d" +
+            "658675fc6ea534e0810a4432826bf58c941efb65d57a338bbd2e26640f89ffbc" +
+            "1a858efcb8550ee3a5e1998bd177e93a7363c344fe6b199ee5d02e82d522c4fe" +
+            "ba15452f80288a821a579116ec6dad2b3b310da903401aa62100ab5d1a36553e" +
+            "06203b33890cc9b832f79ef80560ccb9a39ce767967ed628c6ad573cb116dbef" +
+            "efd75499da96bd68a8a97b928a8bbc103b6621fcde2beca1231d206be6cd9ec7" +
+            "aff6f6c94fcd7204ed3455c68c83f4a41da4af2b74ef5c53f1d8ac70bdcb7ed1" +
+            "85ce81bd84359d44254d95629e9855a94a7c1958d1f8ada5d0532ed8a5aa3fb2" +
+            "d17ba70eb6248e594e1a2297acbbb39d502f1a8c6eb6f1ce22b3de1a1f40cc24" +
+            "554119a831a9aad6079cad88425de6bde1a9187ebb6092cf67bf2b13fd65f270" +
+            "88d78b7e883c8759d2c4f5c65adb7553878ad575f9fad878e80a0c9ba63bcbcc" +
+            "2732e69485bbc9c90bfbd62481d9089beccf80cfe2df16a2cf65bd92dd597b07" +
+            "07e0917af48bbb75fed413d238f5555a7a569d80c3414a8d0859dc65a46128ba" +
+            "b27af87a71314f318c782b23ebfe808b82b0ce26401d2e22f04d83d1255dc51a" +
+            "ddd3b75a2b1ae0784504df543af8969be3ea7082ff7fc9888c144da2af58429e" +
+            "c96031dbcad3dad9af0dcbaaaf268cb8fcffead94f3c7ca495e056a9b47acdb7" +
+            "51fb73e666c6c655ade8297297d07ad1ba5e43f1bca32301651339e22904cc8c" +
+            "42f58c30c04aafdb038dda0847dd988dcda6f3bfd15c4b4c4525004aa06eeff8" +
+            "ca61783aacec57fb3d1f92b0fe2fd1a85f6724517b65e614ad6808d6f6ee34df" +
+            "f7310fdc82aebfd904b01e1dc54b2927094b2db68d6f903b68401adebf5a7e08" +
+            "d78ff4ef5d63653a65040cf9bfd4aca7984a74d37145986780fc0b16ac451649" +
+            "de6188a7dbdf191f64b5fc5e2ab47b57f7f7276cd419c17a3ca8e1b939ae49e4" +
+            "88acba6b965610b5480109c8b17b80e1b7b750dfc7598d5d5011fd2dcc5600a3" +
+            "2ef5b52a1ecc820e308aa342721aac0943bf6686b64b2579376504ccc493d97e" +
+            "6aed3fb0f9cd71a43dd497f01f17c0e2cb3797aa2a2f256656168e6c496afc5f" +
+            "b93246f6b1116398a346f1a641f3b041e989f7914f90cc2c7fff357876e506b5" +
+            "0d334ba77c225bc307ba537152f3f1610e4eafe595f6d9d90d11faa933a15ef1" +
+            "369546868a7f3a45a96768d40fd9d03412c091c6315cf4fde7cb68606937380d" +
+            "b2eaaa707b4c4185c32eddcdd306705e4dc1ffc872eeee475a64dfac86aba41c" +
+            "0618983f8741c5ef68d3a101e8a3b8cac60c905c15fc910840b94c00a0b9d0",
+            "0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350" +
+            "aa5371b1508f9f4528ecea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03");
+
+        runSignTest("Ed25519", null,
+            "833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42",
+            "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf",
+            "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a" +
+            "2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f",
+            "dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b589" +
+            "09351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704");
+
+        // Ed25519ctx
+        byte[] context = Convert.hexStringToByteArray("666f6f");
+        runSignTest("Ed25519", new EdDSAParameterSpec(false, context),
+            "0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6",
+            "dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292",
+            "f726936d19c800494e3fdaff20b276a8",
+            "55a4cc2f70a54e04288c5f4cd1e45a7bb520b36292911876cada7323198dd87a" +
+            "8b36950b95130022907a7fb7c4e9b2d5f6cca685a587b4b21f4b888e4e7edb0d");
+
+       context = Convert.hexStringToByteArray("626172");
+        runSignTest("Ed25519", new EdDSAParameterSpec(false, context),
+            "0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6",
+            "dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292",
+            "f726936d19c800494e3fdaff20b276a8",
+            "fc60d5872fc46b3aa69f8b5b4351d5808f92bcc044606db097abab6dbcb1aee3" +
+            "216c48e8b3b66431b5b186d1d28f8ee15a5ca2df6668346291c2043d4eb3e90d");
+
+        context = Convert.hexStringToByteArray("666f6f");
+        runSignTest("Ed25519", new EdDSAParameterSpec(false, context),
+            "0305334e381af78f141cb666f6199f57bc3495335a256a95bd2a55bf546663f6",
+            "dfc9425e4f968f7f0c29f0259cf5f9aed6851c2bb4ad8bfb860cfee0ab248292",
+            "508e9e6882b979fea900f62adceaca35",
+            "8b70c1cc8310e1de20ac53ce28ae6e7207f33c3295e03bb5c0732a1d20dc6490" +
+            "8922a8b052cf99b7c4fe107a5abb5b2c4085ae75890d02df26269d8945f84b0b");
+
+        context = Convert.hexStringToByteArray("666f6f");
+        runSignTest("Ed25519", new EdDSAParameterSpec(false, context),
+            "ab9c2853ce297ddab85c993b3ae14bcad39b2c682beabc27d6d4eb20711d6560",
+            "0f1d1274943b91415889152e893d80e93275a1fc0b65fd71b4b0dda10ad7d772",
+            "f726936d19c800494e3fdaff20b276a8",
+            "21655b5f1aa965996b3f97b3c849eafba922a0a62992f73b3d1b73106a84ad85" +
+            "e9b86a7b6005ea868337ff2d20a7f5fbd4cd10b0be49a68da2b2e0dc0ad8960f");
+
+        // Ed25519ph
+        runSignTest("Ed25519", new EdDSAParameterSpec(true),
+            "833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42",
+            "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf",
+            "616263",
+            "98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae41" +
+                "31f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406");
+
+        // Ed448
+        runSignTest("Ed448", null,
+            "6c82a562cb808d10d632be89c8513ebf6c929f34ddfa8c9f63c9960ef6e348a3" +
+            "528c8a3fcc2f044e39a3fc5b94492f8f032e7549a20098f95b",
+            "5fd7449b59b461fd2ce787ec616ad46a1da1342485a70e1f8a0ea75d80e96778" +
+            "edf124769b46c7061bd6783df1e50f6cd1fa1abeafe8256180",
+            "",
+            "533a37f6bbe457251f023c0d88f976ae2dfb504a843e34d2074fd823d41a591f" +
+            "2b233f034f628281f2fd7a22ddd47d7828c59bd0a21bfd3980ff0d2028d4b18a" +
+            "9df63e006c5d1c2d345b925d8dc00b4104852db99ac5c7cdda8530a113a0f4db" +
+            "b61149f05a7363268c71d95808ff2e652600");
+
+        runSignTest("Ed448", null,
+            "c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463a" +
+            "fbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e",
+            "43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c086" +
+            "6aea01eb00742802b8438ea4cb82169c235160627b4c3a9480",
+            "03",
+            "26b8f91727bd62897af15e41eb43c377efb9c610d48f2335cb0bd0087810f435" +
+            "2541b143c4b981b7e18f62de8ccdf633fc1bf037ab7cd779805e0dbcc0aae1cb" +
+            "cee1afb2e027df36bc04dcecbf154336c19f0af7e0a6472905e799f1953d2a0f" +
+            "f3348ab21aa4adafd1d234441cf807c03a00");
+
+        context = Convert.hexStringToByteArray("666f6f");
+        runSignTest("Ed448", new EdDSAParameterSpec(false, context),
+            "c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463a" +
+            "fbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e",
+            "43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c086" +
+            "6aea01eb00742802b8438ea4cb82169c235160627b4c3a9480",
+            "03",
+            "d4f8f6131770dd46f40867d6fd5d5055de43541f8c5e35abbcd001b32a89f7d2" +
+            "151f7647f11d8ca2ae279fb842d607217fce6e042f6815ea000c85741de5" +
+            "c8da1144a6a1aba7f96de42505d7a7298524fda538fccbbb754f578c1cad" +
+            "10d54d0d5428407e85dcbc98a49155c13764e66c3c00");
+
+        runSignTest("Ed448", null,
+            "cd23d24f714274e744343237b93290f511f6425f98e64459ff203e898508" +
+            "3ffdf60500553abc0e05cd02184bdb89c4ccd67e187951267eb328",
+            "dcea9e78f35a1bf3499a831b10b86c90aac01cd84b67a0109b55a36e9328" +
+            "b1e365fce161d71ce7131a543ea4cb5f7e9f1d8b00696447001400",
+            "0c3e544074ec63b0265e0c",
+            "1f0a8888ce25e8d458a21130879b840a9089d999aaba039eaf3e3afa090a09d3" +
+            "89dba82c4ff2ae8ac5cdfb7c55e94d5d961a29fe0109941e00b8dbdeea6d3b05" +
+            "1068df7254c0cdc129cbe62db2dc957dbb47b51fd3f213fb8698f064774250a5" +
+            "028961c9bf8ffd973fe5d5c206492b140e00");
+
+        runSignTest("Ed448", null,
+            "258cdd4ada32ed9c9ff54e63756ae582fb8fab2ac721f2c8e676a72768513d93" +
+            "9f63dddb55609133f29adf86ec9929dccb52c1c5fd2ff7e21b",
+            "3ba16da0c6f2cc1f30187740756f5e798d6bc5fc015d7c63cc9510ee3fd44adc" +
+            "24d8e968b6e46e6f94d19b945361726bd75e149ef09817f580",
+            "64a65f3cdedcdd66811e2915",
+            "7eeeab7c4e50fb799b418ee5e3197ff6bf15d43a14c34389b59dd1a7b1b85b4ae904" +
+            "38aca634bea45e3a2695f1270f07fdcdf7c62b8efeaf00b45c2c96ba457eb1a8" +
+            "bf075a3db28e5c24f6b923ed4ad747c3c9e03c7079efb87cb110d3a99861e720" +
+            "03cbae6d6b8b827e4e6c143064ff3c00");
+
+        runSignTest("Ed448", null,
+            "7ef4e84544236752fbb56b8f31a23a10e42814f5f55ca037cdcc11c64c9a3b29" +
+            "49c1bb60700314611732a6c2fea98eebc0266a11a93970100e",
+            "b3da079b0aa493a5772029f0467baebee5a8112d9d3a22532361da294f7bb381" +
+            "5c5dc59e176b4d9f381ca0938e13c6c07b174be65dfa578e80",
+            "64a65f3cdedcdd66811e2915e7",
+            "6a12066f55331b6c22acd5d5bfc5d71228fbda80ae8dec26bdd306743c5027cb" +
+            "4890810c162c027468675ecf645a83176c0d7323a2ccde2d80efe5a1268e" +
+            "8aca1d6fbc194d3f77c44986eb4ab4177919ad8bec33eb47bbb5fc6e2819" +
+            "6fd1caf56b4e7e0ba5519234d047155ac727a1053100");
+
+        runSignTest("Ed448", null,
+            "d65df341ad13e008567688baedda8e9dcdc17dc024974ea5b4227b6530e339bf" +
+            "f21f99e68ca6968f3cca6dfe0fb9f4fab4fa135d5542ea3f01",
+            "df9705f58edbab802c7f8363cfe5560ab1c6132c20a9f1dd163483a26f8ac53a" +
+            "39d6808bf4a1dfbd261b099bb03b3fb50906cb28bd8a081f00",
+            "bd0f6a3747cd561bdddf4640a332461a4a30a12a434cd0bf40d766d9c6d458e5" +
+            "512204a30c17d1f50b5079631f64eb3112182da3005835461113718d1a5ef944",
+            "554bc2480860b49eab8532d2a533b7d578ef473eeb58c98bb2d0e1ce488a98b1" +
+            "8dfde9b9b90775e67f47d4a1c3482058efc9f40d2ca033a0801b63d45b3b722e" +
+            "f552bad3b4ccb667da350192b61c508cf7b6b5adadc2c8d9a446ef003fb05cba" +
+            "5f30e88e36ec2703b349ca229c2670833900");
+
+        runSignTest("Ed448", new EdDSAParameterSpec(false),
+            "2ec5fe3c17045abdb136a5e6a913e32ab75ae68b53d2fc149b77e504132d3756" +
+            "9b7e766ba74a19bd6162343a21c8590aa9cebca9014c636df5",
+            "79756f014dcfe2079f5dd9e718be4171e2ef2486a08f25186f6bff43a9936b9b" +
+            "fe12402b08ae65798a3d81e22e9ec80e7690862ef3d4ed3a00",
+            "15777532b0bdd0d1389f636c5f6b9ba734c90af572877e2d272dd078aa1e567c" +
+            "fa80e12928bb542330e8409f3174504107ecd5efac61ae7504dabe2a602ede89" +
+            "e5cca6257a7c77e27a702b3ae39fc769fc54f2395ae6a1178cab4738e543072f" +
+            "c1c177fe71e92e25bf03e4ecb72f47b64d0465aaea4c7fad372536c8ba516a60" +
+            "39c3c2a39f0e4d832be432dfa9a706a6e5c7e19f397964ca4258002f7c0541b5" +
+            "90316dbc5622b6b2a6fe7a4abffd96105eca76ea7b98816af0748c10df048ce0" +
+            "12d901015a51f189f3888145c03650aa23ce894c3bd889e030d565071c59f409" +
+            "a9981b51878fd6fc110624dcbcde0bf7a69ccce38fabdf86f3bef6044819de11",
+            "c650ddbb0601c19ca11439e1640dd931f43c518ea5bea70d3dcde5f4191fe53f" +
+            "00cf966546b72bcc7d58be2b9badef28743954e3a44a23f880e8d4f1cfce2d7a" +
+            "61452d26da05896f0a50da66a239a8a188b6d825b3305ad77b73fbac0836ecc6" +
+            "0987fd08527c1a8e80d5823e65cafe2a3d00");
+
+        runSignTest("Ed448", null,
+            "872d093780f5d3730df7c212664b37b8a0f24f56810daa8382cd4f" +
+            "a3f77634ec44dc54f1c2ed9bea86fafb7632d8be199ea165f5ad55dd9ce8",
+            "a81b2e8a70a5ac94ffdbcc9badfc3feb0801f258578bb114ad44ece" +
+            "1ec0e799da08effb81c5d685c0c56f64eecaef8cdf11cc38737838cf400",
+            "6ddf802e1aae4986935f7f981ba3f0351d6273c0a0c22c9c0e8339168e675412" +
+            "a3debfaf435ed651558007db4384b650fcc07e3b586a27a4f7a00ac8a6fec2cd" +
+            "86ae4bf1570c41e6a40c931db27b2faa15a8cedd52cff7362c4e6e23daec0fbc" +
+            "3a79b6806e316efcc7b68119bf46bc76a26067a53f296dafdbdc11c77f7777e9" +
+            "72660cf4b6a9b369a6665f02e0cc9b6edfad136b4fabe723d2813db3136cfde9" +
+            "b6d044322fee2947952e031b73ab5c603349b307bdc27bc6cb8b8bbd7bd32321" +
+            "9b8033a581b59eadebb09b3c4f3d2277d4f0343624acc817804728b25ab79717" +
+            "2b4c5c21a22f9c7839d64300232eb66e53f31c723fa37fe387c7d3e50bdf9813" +
+            "a30e5bb12cf4cd930c40cfb4e1fc622592a49588794494d56d24ea4b40c89fc0" +
+            "596cc9ebb961c8cb10adde976a5d602b1c3f85b9b9a001ed3c6a4d3b1437f520" +
+            "96cd1956d042a597d561a596ecd3d1735a8d570ea0ec27225a2c4aaff26306d1" +
+            "526c1af3ca6d9cf5a2c98f47e1c46db9a33234cfd4d81f2c98538a09ebe76998" +
+            "d0d8fd25997c7d255c6d66ece6fa56f11144950f027795e653008f4bd7ca2dee" +
+            "85d8e90f3dc315130ce2a00375a318c7c3d97be2c8ce5b6db41a6254ff264fa6" +
+            "155baee3b0773c0f497c573f19bb4f4240281f0b1f4f7be857a4e59d416c06b4" +
+            "c50fa09e1810ddc6b1467baeac5a3668d11b6ecaa901440016f389f80acc4db9" +
+            "77025e7f5924388c7e340a732e554440e76570f8dd71b7d640b3450d1fd5f041" +
+            "0a18f9a3494f707c717b79b4bf75c98400b096b21653b5d217cf3565c9597456" +
+            "f70703497a078763829bc01bb1cbc8fa04eadc9a6e3f6699587a9e75c94e5bab" +
+            "0036e0b2e711392cff0047d0d6b05bd2a588bc109718954259f1d86678a579a3" +
+            "120f19cfb2963f177aeb70f2d4844826262e51b80271272068ef5b3856fa8535" +
+            "aa2a88b2d41f2a0e2fda7624c2850272ac4a2f561f8f2f7a318bfd5caf969614" +
+            "9e4ac824ad3460538fdc25421beec2cc6818162d06bbed0c40a387192349db67" +
+            "a118bada6cd5ab0140ee273204f628aad1c135f770279a651e24d8c14d75a605" +
+            "9d76b96a6fd857def5e0b354b27ab937a5815d16b5fae407ff18222c6d1ed263" +
+            "be68c95f32d908bd895cd76207ae726487567f9a67dad79abec316f683b17f2d" +
+            "02bf07e0ac8b5bc6162cf94697b3c27cd1fea49b27f23ba2901871962506520c" +
+            "392da8b6ad0d99f7013fbc06c2c17a569500c8a7696481c1cd33e9b14e40b82e" +
+            "79a5f5db82571ba97bae3ad3e0479515bb0e2b0f3bfcd1fd33034efc6245eddd" +
+            "7ee2086ddae2600d8ca73e214e8c2b0bdb2b047c6a464a562ed77b73d2d841c4" +
+            "b34973551257713b753632efba348169abc90a68f42611a40126d7cb21b58695" +
+            "568186f7e569d2ff0f9e745d0487dd2eb997cafc5abf9dd102e62ff66cba87",
+            "e301345a41a39a4d72fff8df69c98075a0cc082b802fc9b2b6bc503f926b65bd" +
+            "df7f4c8f1cb49f6396afc8a70abe6d8aef0db478d4c6b2970076c6a0484fe76d" +
+            "76b3a97625d79f1ce240e7c576750d295528286f719b413de9ada3e8eb78ed57" +
+            "3603ce30d8bb761785dc30dbc320869e1a00");
+
+        runSignTest("Ed448", new EdDSAParameterSpec(true),
+            "833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901" +
+            "b96dca3d42ef7822e0d5104127dc05d6dbefde69e3ab2cec7c867c6e2c49",
+            "259b71c19f83ef77a7abd26524cbdb3161b590a48f7d17de3ee0ba9" +
+            "c52beb743c09428a131d6b1b57303d90d8132c276d5ed3d5d01c0f53880",
+            "616263",
+            "822f6901f7480f3d5f562c592994d9693602875614483256505600bbc281ae38" +
+            "1f54d6bce2ea911574932f52a4e6cadd78769375ec3ffd1b801a0d9b3f4030cd" +
+            "433964b6457ea39476511214f97469b57dd32dbc560a9a94d00bff07620464a3" +
+            "ad203df7dc7ce360c3cd3696d9d9fab90f00");
+
+        runSignTest("Ed448", new EdDSAParameterSpec(true, context),
+            "833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42" +
+            "ef7822e0d5104127dc05d6dbefde69e3ab2cec7c867c6e2c49",
+            "259b71c19f83ef77a7abd26524cbdb3161b590a48f7d17de3ee0ba9c52beb743" +
+            "c09428a131d6b1b57303d90d8132c276d5ed3d5d01c0f53880",
+            "616263",
+            "c32299d46ec8ff02b54540982814dce9a05812f81962b649d528095916a2aa48" +
+            "1065b1580423ef927ecf0af5888f90da0f6a9a85ad5dc3f280d91224ba9911a3" +
+            "653d00e484e2ce232521481c8658df304bb7745a73514cdb9bf3e15784ab7128" +
+            "4f8d0704a608c54a6b62d97beb511d132100");
+
+        System.out.println("All test vectors passed");
+    }
+
+    private static void runSignTest(String algorithm,
+            AlgorithmParameterSpec params, String privateKey, String publicKey,
+            String message, String signature) throws Exception {
+
+        byte[] privKeyBytes = Convert.hexStringToByteArray(privateKey);
+        EdECPoint pubKeyPoint = Convert.hexStringToEdPoint(publicKey);
+        byte[] msgBytes = Convert.hexStringToByteArray(message);
+        byte[] computedSig;
+
+        NamedParameterSpec namedSpec = new NamedParameterSpec(algorithm);
+        EdECPrivateKeySpec privKeySpec =
+            new EdECPrivateKeySpec(namedSpec, privKeyBytes);
+        KeyFactory kf = KeyFactory.getInstance(algorithm);
+        PrivateKey privKey = kf.generatePrivate(privKeySpec);
+        Signature sig = Signature.getInstance(algorithm);
+        if (params != null) {
+            sig.setParameter(params);
+        }
+        sig.initSign(privKey);
+        sig.update(msgBytes);
+        computedSig = sig.sign();
+        if (!Arrays.equals(computedSig,
+            Convert.hexStringToByteArray(signature))) {
+            throw new RuntimeException("Incorrect signature");
+        }
+
+        // test verification
+        sig = Signature.getInstance(algorithm);
+        if (params != null) {
+            sig.setParameter(params);
+        }
+        EdECPublicKeySpec pubKeySpec =
+            new EdECPublicKeySpec(namedSpec, pubKeyPoint);
+        PublicKey pubKey = kf.generatePublic(pubKeySpec);
+        sig.initVerify(pubKey);
+        sig.update(msgBytes);
+        if (!sig.verify(computedSig)) {
+            throw new RuntimeException("Signature did not verify");
+        }
+    }
+
+
+    private static void runBasicTests() throws Exception {
+        runBasicTest("EdDSA", null);
+
+        runBasicTest("EdDSA", 255);
+        runBasicTest("EdDSA", "Ed25519");
+        runBasicTest("Ed25519", null);
+        runBasicTest("1.3.101.112", null);
+        runBasicTest("OID.1.3.101.112", null);
+
+        runBasicTest("EdDSA", 448);
+        runBasicTest("EdDSA", "Ed448");
+        runBasicTest("Ed448", null);
+        runBasicTest("1.3.101.113", null);
+        runBasicTest("OID.1.3.101.113", null);
+    }
+
+    private static void runBasicTest(String name, Object param)
+        throws Exception {
+
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance(name);
+        if (param instanceof Integer) {
+            kpg.initialize((Integer) param, random);
+        } else if (param instanceof String) {
+            kpg.initialize(new NamedParameterSpec((String) param), random);
+        }
+        KeyPair kp = kpg.generateKeyPair();
+
+        Signature sig = Signature.getInstance(name);
+        sig.initSign(kp.getPrivate());
+        byte[] testMessage = new byte[1024];
+        random.nextBytes(testMessage);
+        sig.update(testMessage);
+        byte[] msgSig = sig.sign();
+
+        // sign again, should return false
+        byte[] x = sig.sign();
+        if (Arrays.compare(msgSig, x) == 0) {
+            throw new RuntimeException("Second sign identical, didn't reset");
+        }
+
+        // verify the signature
+        sig.initVerify(kp.getPublic());
+        sig.update(testMessage);
+        if (!sig.verify(msgSig)) {
+            throw new RuntimeException("Valid signature did not verify");
+        }
+
+        // verify again, should return false
+        if (sig.verify(msgSig)) {
+            throw new RuntimeException("Second verify succeeded, didn't reset");
+        }
+
+        // try verifying an incorrect signature
+        testMessage[0] ^= (byte) 0x01;
+        sig.update(testMessage);
+        if (sig.verify(msgSig)) {
+            throw new RuntimeException("Invalid signature verified");
+        }
+
+        KeyFactory kf = KeyFactory.getInstance(name);
+        // Test with X509 and PKCS8 key specs
+        X509EncodedKeySpec pubSpec =
+            kf.getKeySpec(kp.getPublic(), X509EncodedKeySpec.class);
+        PKCS8EncodedKeySpec priSpec =
+            kf.getKeySpec(kp.getPrivate(), PKCS8EncodedKeySpec.class);
+
+        PublicKey pubKey = kf.generatePublic(pubSpec);
+        PrivateKey priKey = kf.generatePrivate(priSpec);
+
+        sig.initSign(priKey);
+        sig.update(testMessage);
+        msgSig = sig.sign();
+        sig.initVerify(pubKey);
+        sig.update(testMessage);
+        if (!sig.verify(msgSig)) {
+            throw new RuntimeException("Valid signature did not verify");
+        }
+
+        // test with EdEC key specs
+        EdECPublicKeySpec edPublic =
+            kf.getKeySpec(kp.getPublic(), EdECPublicKeySpec.class);
+        EdECPrivateKeySpec edPrivate =
+            kf.getKeySpec(kp.getPrivate(), EdECPrivateKeySpec.class);
+        PublicKey pubKey2 = kf.generatePublic(edPublic);
+        PrivateKey priKey2 = kf.generatePrivate(edPrivate);
+        sig.initSign(priKey2);
+        sig.update(testMessage);
+        msgSig = sig.sign();
+        sig.initVerify(pubKey2);
+        sig.update(testMessage);
+        if (!sig.verify(msgSig)) {
+            throw new RuntimeException("Valid signature did not verify");
+        }
+    }
+
+    /*
+     * Ensure that SunEC rejects parameters/points for the wrong curve
+     * when the algorithm ID for a specific curve is specified.
+     */
+    private static void runCurveMixTest() throws Exception {
+        runCurveMixTest("SunEC", "Ed25519", 448);
+        runCurveMixTest("SunEC", "Ed25519", "Ed448");
+        runCurveMixTest("SunEC", "Ed448", 255);
+        runCurveMixTest("SunEC", "Ed448", "Ed25519");
+    }
+
+    private static void runCurveMixTest(String providerName, String name,
+                                        Object param) throws Exception {
+
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance(name,
+            providerName);
+
+        try {
+            if (param instanceof Integer) {
+                kpg.initialize((Integer) param);
+            } else if (param instanceof String) {
+                kpg.initialize(new NamedParameterSpec((String) param));
+            }
+            throw new RuntimeException(name + " KeyPairGenerator accepted "
+                    + param.toString() + " parameters");
+        } catch (InvalidParameterException ex) {
+            if (param instanceof String) {
+                throw new RuntimeException(
+                        "InvalidAlgorithmParameterException expected", ex);
+            }
+            // expected
+
+        } catch (InvalidAlgorithmParameterException ex) {
+            if (param instanceof Integer) {
+                throw new RuntimeException("InvalidParameterException expected",
+                        ex);
+            }
+            // expected
+        }
+
+        // the rest of the test uses the parameter as an algorithm name to
+        // produce keys
+        if (param instanceof Integer) {
+            return;
+        }
+        String otherName = (String) param;
+        KeyPairGenerator otherKpg = KeyPairGenerator.getInstance(otherName,
+            providerName);
+        KeyPair otherKp = otherKpg.generateKeyPair();
+
+        // ensure the KeyFactory rejects incorrect keys
+        KeyFactory kf = KeyFactory.getInstance(name, providerName);
+        try {
+            kf.getKeySpec(otherKp.getPublic(), EdECPublicKeySpec.class);
+            throw new RuntimeException(name + " KeyFactory accepted "
+                + param.toString() + " key");
+        } catch (InvalidKeySpecException ex) {
+            // expected
+        }
+        try {
+            kf.getKeySpec(otherKp.getPrivate(), EdECPrivateKeySpec.class);
+            throw new RuntimeException(name + " KeyFactory accepted "
+                + param.toString() + " key");
+        } catch (InvalidKeySpecException ex) {
+            // expected
+        }
+
+        try {
+            kf.translateKey(otherKp.getPublic());
+            throw new RuntimeException(name + " KeyFactory accepted "
+                + param.toString() + " key");
+        } catch (InvalidKeyException ex) {
+            // expected
+        }
+        try {
+            kf.translateKey(otherKp.getPrivate());
+            throw new RuntimeException(name + " KeyFactory accepted "
+                + param.toString() + " key");
+        } catch (InvalidKeyException ex) {
+            // expected
+        }
+
+        KeyFactory otherKf = KeyFactory.getInstance(otherName, providerName);
+        EdECPublicKeySpec otherPubSpec = otherKf.getKeySpec(otherKp.getPublic(),
+            EdECPublicKeySpec.class);
+        try {
+            kf.generatePublic(otherPubSpec);
+            throw new RuntimeException(name + " KeyFactory accepted "
+                + param.toString() + " key");
+        } catch (InvalidKeySpecException ex) {
+            // expected
+        }
+        EdECPrivateKeySpec otherPriSpec =
+            otherKf.getKeySpec(otherKp.getPrivate(), EdECPrivateKeySpec.class);
+        try {
+            kf.generatePrivate(otherPriSpec);
+            throw new RuntimeException(name + " KeyFactory accepted "
+                + param.toString() + " key");
+        } catch (InvalidKeySpecException ex) {
+            // expected
+        }
+
+        // ensure the Signature rejects incorrect keys
+        Signature sig = Signature.getInstance(name, providerName);
+        try {
+            sig.initSign(otherKp.getPrivate());
+            throw new RuntimeException(name + " Signature accepted "
+                + param.toString() + " signing key");
+        } catch (InvalidKeyException ex) {
+            // expected
+        }
+
+        try {
+            sig.initVerify(otherKp.getPublic());
+            throw new RuntimeException(name + " Signature accepted "
+                + param.toString() + " verification key");
+        } catch (InvalidKeyException ex) {
+            // expected
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/ec/ed/TestEdOps.java	Mon May 18 09:42:52 2020 -0700
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2020, 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8166597
+ * @summary Test EdDSA basic operations
+ * @library /test/lib
+ * @build jdk.test.lib.Convert
+ * @modules java.base/sun.security.provider
+ *          java.base/sun.security.util
+ *          java.base/sun.security.util.math
+ *          java.base/sun.security.util.math.intpoly
+ *          jdk.crypto.ec/sun.security.ec.ed
+ * @run main TestEdOps
+ */
+
+import sun.security.ec.ed.*;
+import java.security.*;
+import java.security.spec.*;
+import java.util.Arrays;
+import sun.security.provider.SHAKE256;
+import jdk.test.lib.Convert;
+
+public class TestEdOps {
+
+    public static void main(String[] args) throws Exception {
+
+        testShake();
+        testVectors();
+        testRandom(1000);
+        testInvalidPoints();
+    }
+
+    private static void testShake() {
+
+        runShakeTest(32, "765db6ab3af389b8c775c8eb99fe72",
+        "ccb6564a655c94d714f80b9f8de9e2610c4478778eac1b9256237dbf90e50581");
+
+        runShakeTest(32,
+            "0e3dcd346c68bc5b5cafe3342a7e0e29272e42fba12a51081251abca989c77a1" +
+            "a501e2",
+            "c934ab7f2148da5ca2ce948432fa72be49420f10e3dbc1906016773d9819cff4");
+
+        runShakeTest(32,
+            "7e4c74f480e60565fe39e483b5204e24753841dec9ef3ec0dadd4e3f91584373" +
+            "fc424084f3267b5ffb8342ad6a683c05cc41f26086f18dceb921e1",
+            "a6450836c02f8fdfe841fbcb4b4fc7dca9bd56019b92582095ee5d11eca45fa0");
+
+        System.out.println("SHAKE256 tests passed");
+    }
+
+    private static void runShakeTest(int outputLen, String msg, String digest) {
+        byte[] msgBytes = Convert.hexStringToByteArray(msg);
+        byte[] digestBytes = Convert.hexStringToByteArray(digest);
+        SHAKE256 md = new SHAKE256(outputLen);
+        md.update(msgBytes, 0, msgBytes.length);
+        byte[] computed = md.digest();
+        if (!Arrays.equals(digestBytes, computed)) {
+            throw new RuntimeException("hash is incorrect");
+        }
+    }
+
+    private static void testVectors() throws Exception {
+        EdDSAParameters ed25519Params = EdDSAParameters.get(
+            RuntimeException::new, NamedParameterSpec.ED25519);
+        EdDSAOperations ops = new EdDSAOperations(ed25519Params);
+
+        runSignTest(ops,
+            "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
+            "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
+            "",
+            "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e06522490155" +
+            "5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b");
+
+        runSignTest(ops,
+            "4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb",
+            "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c",
+            "72",
+            "92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da" +
+            "085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00");
+
+        runSignTest(ops,
+            "c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7",
+            "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025",
+            "af82",
+            "6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac" +
+            "18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a");
+
+        runSignTest(ops,
+            "f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5",
+            "278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e",
+            "08b8b2b733424243760fe426a4b54908632110a66c2f6591eabd3345e3e4eb98" +
+            "fa6e264bf09efe12ee50f8f54e9f77b1e355f6c50544e23fb1433ddf73be84d8" +
+            "79de7c0046dc4996d9e773f4bc9efe5738829adb26c81b37c93a1b270b20329d" +
+            "658675fc6ea534e0810a4432826bf58c941efb65d57a338bbd2e26640f89ffbc" +
+            "1a858efcb8550ee3a5e1998bd177e93a7363c344fe6b199ee5d02e82d522c4fe" +
+            "ba15452f80288a821a579116ec6dad2b3b310da903401aa62100ab5d1a36553e" +
+            "06203b33890cc9b832f79ef80560ccb9a39ce767967ed628c6ad573cb116dbef" +
+            "efd75499da96bd68a8a97b928a8bbc103b6621fcde2beca1231d206be6cd9ec7" +
+            "aff6f6c94fcd7204ed3455c68c83f4a41da4af2b74ef5c53f1d8ac70bdcb7ed1" +
+            "85ce81bd84359d44254d95629e9855a94a7c1958d1f8ada5d0532ed8a5aa3fb2" +
+            "d17ba70eb6248e594e1a2297acbbb39d502f1a8c6eb6f1ce22b3de1a1f40cc24" +
+            "554119a831a9aad6079cad88425de6bde1a9187ebb6092cf67bf2b13fd65f270" +
+            "88d78b7e883c8759d2c4f5c65adb7553878ad575f9fad878e80a0c9ba63bcbcc" +
+            "2732e69485bbc9c90bfbd62481d9089beccf80cfe2df16a2cf65bd92dd597b07" +
+            "07e0917af48bbb75fed413d238f5555a7a569d80c3414a8d0859dc65a46128ba" +
+            "b27af87a71314f318c782b23ebfe808b82b0ce26401d2e22f04d83d1255dc51a" +
+            "ddd3b75a2b1ae0784504df543af8969be3ea7082ff7fc9888c144da2af58429e" +
+            "c96031dbcad3dad9af0dcbaaaf268cb8fcffead94f3c7ca495e056a9b47acdb7" +
+            "51fb73e666c6c655ade8297297d07ad1ba5e43f1bca32301651339e22904cc8c" +
+            "42f58c30c04aafdb038dda0847dd988dcda6f3bfd15c4b4c4525004aa06eeff8" +
+            "ca61783aacec57fb3d1f92b0fe2fd1a85f6724517b65e614ad6808d6f6ee34df" +
+            "f7310fdc82aebfd904b01e1dc54b2927094b2db68d6f903b68401adebf5a7e08" +
+            "d78ff4ef5d63653a65040cf9bfd4aca7984a74d37145986780fc0b16ac451649" +
+            "de6188a7dbdf191f64b5fc5e2ab47b57f7f7276cd419c17a3ca8e1b939ae49e4" +
+            "88acba6b965610b5480109c8b17b80e1b7b750dfc7598d5d5011fd2dcc5600a3" +
+            "2ef5b52a1ecc820e308aa342721aac0943bf6686b64b2579376504ccc493d97e" +
+            "6aed3fb0f9cd71a43dd497f01f17c0e2cb3797aa2a2f256656168e6c496afc5f" +
+            "b93246f6b1116398a346f1a641f3b041e989f7914f90cc2c7fff357876e506b5" +
+            "0d334ba77c225bc307ba537152f3f1610e4eafe595f6d9d90d11faa933a15ef1" +
+            "369546868a7f3a45a96768d40fd9d03412c091c6315cf4fde7cb68606937380d" +
+            "b2eaaa707b4c4185c32eddcdd306705e4dc1ffc872eeee475a64dfac86aba41c" +
+            "0618983f8741c5ef68d3a101e8a3b8cac60c905c15fc910840b94c00a0b9d0",
+            "0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350" +
+            "aa5371b1508f9f4528ecea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03");
+
+        runSignTest(ops,
+            "833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42",
+            "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf",
+            "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a" +
+            "2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f",
+            "dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b589" +
+            "09351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704");
+
+
+        EdDSAParameters ed448Params = EdDSAParameters.get(RuntimeException::new,
+            NamedParameterSpec.ED448);
+        EdDSAOperations ed448Ops = new EdDSAOperations(ed448Params);
+
+        runSignTest(ed448Ops,
+            "c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463a" +
+            "fbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e",
+            "43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c086" +
+            "6aea01eb00742802b8438ea4cb82169c235160627b4c3a9480",
+            "03",
+            "26b8f91727bd62897af15e41eb43c377efb9c610d48f2335cb0bd0087810f435" +
+            "2541b143c4b981b7e18f62de8ccdf633fc1bf037ab7cd779805e0dbcc0aae1cb" +
+            "cee1afb2e027df36bc04dcecbf154336c19f0af7e0a6472905e799f1953d2a0f" +
+            "f3348ab21aa4adafd1d234441cf807c03a00");
+
+        System.out.println("All test vectors passed");
+    }
+
+    private static void runSignTest(EdDSAOperations ops, String privateKey,
+                                    String publicKey, String message,
+                                    String signature) throws Exception {
+
+        EdDSAParameterSpec sigParams = new EdDSAParameterSpec(false);
+
+        byte[] privKeyBytes = Convert.hexStringToByteArray(privateKey);
+        byte[] pubKeyBytes = Convert.hexStringToByteArray(publicKey);
+        byte[] msgBytes = Convert.hexStringToByteArray(message);
+        byte[] computedSig = ops.sign(sigParams, privKeyBytes, msgBytes);
+        if (!Arrays.equals(computedSig,
+                           Convert.hexStringToByteArray(signature))) {
+            throw new RuntimeException("Incorrect signature: " +
+                Convert.byteArrayToHexString(computedSig) + " != " + signature);
+        }
+        // Test public key computation
+        EdECPoint pubPoint = ops.computePublic(privKeyBytes);
+        EdDSAPublicKeyImpl pubKey =
+            new EdDSAPublicKeyImpl(ops.getParameters(), pubPoint);
+        byte[] computedPubKey = pubKey.getEncodedPoint();
+        if (!Arrays.equals(computedPubKey, pubKeyBytes)) {
+            throw new RuntimeException("Incorrect public key");
+        }
+
+        // test verification
+        ops.verify(sigParams, pubKeyBytes, msgBytes, computedSig);
+    }
+
+    private static void testRandom(int count) throws Exception {
+
+        EdDSAParameterSpec sigParams = new EdDSAParameterSpec(false);
+        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
+        random.setSeed(1);
+
+        EdDSAParameters params = EdDSAParameters.get(RuntimeException::new,
+            NamedParameterSpec.ED25519);
+        EdDSAOperations ops = new EdDSAOperations(params);
+
+        long startTime = System.currentTimeMillis();
+
+        for (int i = 0; i < count; i++) {
+            byte[] privKey = ops.generatePrivate(random);
+            byte[] message = new byte[1024];
+            random.nextBytes(message);
+            byte[] sig = ops.sign(sigParams, privKey, message);
+
+            EdECPoint pubKeyPoint = ops.computePublic(privKey);
+            EdDSAPublicKeyImpl pubKey =
+                new EdDSAPublicKeyImpl(params, pubKeyPoint);
+            byte[] encodedPubKey = pubKey.getEncodedPoint();
+            if (!ops.verify(sigParams, encodedPubKey, message, sig)) {
+                throw new RuntimeException("signature did not verify");
+            }
+        }
+
+        long endTime = System.currentTimeMillis();
+        double millisPerIter = (double) (endTime - startTime) / count;
+        System.out.println("Time per keygen+sign+verify: " +
+            millisPerIter + " ms");
+    }
+
+    private static void testInvalidPoints() throws Exception {
+
+        // Ed25519
+
+        // incorrect length
+        testInvalidPoint(NamedParameterSpec.ED25519, "");
+        testInvalidPoint(NamedParameterSpec.ED25519, "ffffff");
+        testInvalidPoint(NamedParameterSpec.ED25519,
+            "abd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f70751" +
+            "1a");
+        // y value too large
+        testInvalidPoint(NamedParameterSpec.ED25519,
+            "8ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe");
+        // not square
+        testInvalidPoint(NamedParameterSpec.ED25519,
+            "d85a980182b10ac7d54bfed3c964073a0ee172f3daa62325af021a68f707511a");
+        // x = 0, but x mod 2 == 1
+        testInvalidPoint(NamedParameterSpec.ED25519,
+            "0100000000000000000000000000000000000000000000000000000000000080");
+
+        // Ed448
+        testInvalidPoint(NamedParameterSpec.ED448, "");
+        testInvalidPoint(NamedParameterSpec.ED448, "ffffff");
+        testInvalidPoint(NamedParameterSpec.ED448,
+            "ab43ba28f430cdfe456ae531545f7ecd0ac834a55c9358c0372bfa0c6c6798c0" +
+            "866aea01eb00742802b8438ea4cb82169c235160627b4c3a9480");
+        // y value too large
+        testInvalidPoint(NamedParameterSpec.ED448,
+            "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" +
+            "fffffffffffffffffffffffffffffffffffffffffffffffffff00");
+        // not square
+        testInvalidPoint(NamedParameterSpec.ED448,
+            "43ba28f430cdfe456ae531545f7ecd0ac834a55c9358c0372bfa0c6c6798c086" +
+            "6aea01eb00742802b8438ea4cb82169c235160627b4c3a9480");
+        // x = 0, but x mod 2 == 1
+        testInvalidPoint(NamedParameterSpec.ED448,
+            "0100000000000000000000000000000000000000000000000000000000000000" +
+            "00000000000000000000000000000000000000000000000080");
+
+    }
+
+    private static void testInvalidPoint(NamedParameterSpec curve,
+                                         String pointStr) throws Exception {
+
+        byte[] encodedPoint = Convert.hexStringToByteArray(pointStr);
+        EdDSAParameters params =
+            EdDSAParameters.get(RuntimeException::new, curve);
+        EdDSAOperations ops = new EdDSAOperations(params);
+        try {
+            ops.decodeAffinePoint(InvalidKeyException::new, encodedPoint);
+            throw new RuntimeException("No exception on invalid point");
+        } catch (InvalidKeyException ex) {
+            // this is expected
+        }
+    }
+}
--- a/test/jdk/sun/security/ec/xec/TestXECOps.java	Mon May 18 09:28:06 2020 -0700
+++ b/test/jdk/sun/security/ec/xec/TestXECOps.java	Mon May 18 09:42:52 2020 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2020, 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,6 +32,8 @@
  */
 
 import sun.security.ec.*;
+
+import java.security.spec.NamedParameterSpec;
 import java.util.*;
 import jdk.test.lib.Convert;
 
@@ -86,7 +88,9 @@
     private void runDiffieHellmanTest(String opName, String a_str,
         String b_str, String result_str) {
 
-        XECParameters settings = XECParameters.getByName(opName).get();
+        NamedParameterSpec paramSpec = new NamedParameterSpec(opName);
+        XECParameters settings =
+            XECParameters.get(RuntimeException::new, paramSpec);
         XECOperations ops = new XECOperations(settings);
 
         byte[] basePoint = Convert.byteToByteArray(settings.getBasePoint(),
@@ -118,7 +122,9 @@
         byte[] u_in = Convert.hexStringToByteArray(u_in_str);
         byte[] u_out_expected = Convert.hexStringToByteArray(u_out_str);
 
-        XECParameters settings = XECParameters.getByName(opName).get();
+        NamedParameterSpec paramSpec = new NamedParameterSpec(opName);
+        XECParameters settings =
+            XECParameters.get(RuntimeException::new, paramSpec);
         XECOperations ops = new XECOperations(settings);
         byte[] u_out = ops.encodedPointMultiply(k_in, u_in);
 
--- a/test/jdk/sun/security/ec/xec/XECIterative.java	Mon May 18 09:28:06 2020 -0700
+++ b/test/jdk/sun/security/ec/xec/XECIterative.java	Mon May 18 09:42:52 2020 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2020, 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
@@ -43,6 +43,7 @@
 import sun.security.ec.*;
 
 import java.io.*;
+import java.security.spec.NamedParameterSpec;
 import java.util.*;
 import jdk.test.lib.Convert;
 
@@ -79,7 +80,9 @@
     private void runIterativeTest(String opName, long start, long end)
         throws IOException {
 
-        XECParameters settings = XECParameters.getByName(opName).get();
+        NamedParameterSpec paramSpec = new NamedParameterSpec(opName);
+        XECParameters settings =
+            XECParameters.get(RuntimeException::new, paramSpec);
         XECOperations ops = new XECOperations(settings);
 
         File vectorFile = new File(System.getProperty("test.src", "."),
--- a/test/jdk/sun/security/util/math/TestIntegerModuloP.java	Mon May 18 09:28:06 2020 -0700
+++ b/test/jdk/sun/security/util/math/TestIntegerModuloP.java	Mon May 18 09:42:52 2020 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2020, 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
@@ -36,6 +36,8 @@
  * @run main TestIntegerModuloP sun.security.util.math.intpoly.P256OrderField 32 8
  * @run main TestIntegerModuloP sun.security.util.math.intpoly.P384OrderField 48 9
  * @run main TestIntegerModuloP sun.security.util.math.intpoly.P521OrderField 66 10
+ * @run main TestIntegerModuloP sun.security.util.math.intpoly.Curve25519OrderField 32 11
+ * @run main TestIntegerModuloP sun.security.util.math.intpoly.Curve448OrderField 56 12
  */
 
 import sun.security.util.math.*;
@@ -104,8 +106,10 @@
         SET_FUNCTIONS.add((a, b, c) ->
             a.setValue(c, 0, c.length, (byte) 0));
         SET_FUNCTIONS.add((a, b, c) ->
-            a.setValue(ByteBuffer.wrap(c, 0, c.length).order(ByteOrder.LITTLE_ENDIAN),
-            c.length, highByte));
+            a.setValue(c, 0, c.length / 2, (byte) 0));
+        SET_FUNCTIONS.add((a, b, c) ->
+            a.setValue(ByteBuffer.wrap(c, 0, c.length / 2).order(ByteOrder.LITTLE_ENDIAN),
+            c.length / 2, highByte));
 
         // array functions return the (possibly modified) value as byte array
         ARRAY_FUNCTIONS.add((a, b ) -> a.asByteArray(length));
@@ -328,7 +332,7 @@
 
             ElemSetFunction setFunc =
                 SET_FUNCTIONS.get(random.nextInt(SET_FUNCTIONS.size()));
-            byte[] valueArr = new byte[length];
+            byte[] valueArr = new byte[2 * length];
             random.nextBytes(valueArr);
             setAndCheck(setFunc, result1, result2, valueArr);
 
--- a/test/lib/jdk/test/lib/Convert.java	Mon May 18 09:28:06 2020 -0700
+++ b/test/lib/jdk/test/lib/Convert.java	Mon May 18 09:42:52 2020 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2020, 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
@@ -24,10 +24,11 @@
 package jdk.test.lib;
 
 import java.math.BigInteger;
+import java.security.spec.EdECPoint;
 
 /**
- * Utility class containing conversions between strings, arrays, and numeric
- * values.
+ * Utility class containing conversions between strings, arrays, numeric
+ * values, and other types.
  */
 
 public class Convert {
@@ -80,6 +81,36 @@
         }
         return result;
     }
+
+    private static EdECPoint byteArrayToEdPoint(byte[] arr) {
+        byte msb = arr[arr.length - 1];
+        boolean xOdd = (msb & 0x80) != 0;
+        arr[arr.length - 1] &= (byte) 0x7F;
+        reverse(arr);
+        BigInteger y = new BigInteger(1, arr);
+        return new EdECPoint(xOdd, y);
+    }
+
+    public static EdECPoint hexStringToEdPoint(String str) {
+        return byteArrayToEdPoint(hexStringToByteArray(str));
+    }
+
+    private static void swap(byte[] arr, int i, int j) {
+        byte tmp = arr[i];
+        arr[i] = arr[j];
+        arr[j] = tmp;
+    }
+
+    private static void reverse(byte [] arr) {
+        int i = 0;
+        int j = arr.length - 1;
+
+        while (i < j) {
+            swap(arr, i, j);
+            i++;
+            j--;
+        }
+    }
 }
 
 
--- a/test/micro/org/openjdk/bench/javax/crypto/full/SignatureBench.java	Mon May 18 09:28:06 2020 -0700
+++ b/test/micro/org/openjdk/bench/javax/crypto/full/SignatureBench.java	Mon May 18 09:42:52 2020 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2020, 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
@@ -57,7 +57,11 @@
 
 
     private String getKeyPairGeneratorName() {
-        String tail = algorithm.substring(algorithm.lastIndexOf("with") + 4);
+        int withIndex = algorithm.lastIndexOf("with");
+        if (withIndex < 0) {
+            return algorithm;
+        }
+        String tail = algorithm.substring(withIndex + 4);
         return "ECDSA".equals(tail) ? "EC" : tail;
     }
 
@@ -122,4 +126,14 @@
 
     }
 
+    public static class EdDSA extends SignatureBench {
+
+        @Param({"EdDSA"})
+        private String algorithm;
+
+        @Param({"255", "448"})
+        private int keyLength;
+
+    }
+
 }