[openssl-commits] [openssl] master update

Dr. Stephen Henson steve at openssl.org
Tue May 30 19:39:52 UTC 2017


The branch master has been updated
       via  04dec1ab34df70c1588d42cc394e8fa8b5f3191c (commit)
       via  74e783610483ebd4692d41f8c3a7fc57fe6cdfe1 (commit)
       via  978b700bf9531fa9bee0e69aa292f0f2be0f1251 (commit)
       via  4328dd41582bcdca8e4f51f0a3abadfafa2163ee (commit)
       via  684c41c83fdae24b44cdd7af9e71da505d803145 (commit)
       via  9f889cc177a5fcdec47f1fc4f7162d34a7305385 (commit)
       via  9f98fbad4720db05485958868363a0296cf6ec99 (commit)
       via  b85236966b8518a23fe87d0d0f7c6cf435cc3878 (commit)
       via  1e8c4a9b7a93ecf7936f4fba0632cd82ddfe950a (commit)
       via  ca23d3e089f1b807bfa088f6d916b0aa55594717 (commit)
       via  42a3008aa406429394ff2ae03114d0ac47214e0a (commit)
       via  a13727e5df57f77b39ad2b57378e8511aa9dcb37 (commit)
       via  8ecade8bfb85b1d8a473ac12c1589ddbd5a7a431 (commit)
       via  9691a749c8c694fa212bfd8a93ae91786ab463af (commit)
       via  d4d001df371f2ccd13eee7f9d7f4c6594a080168 (commit)
       via  06c6d05faea05ced44a580c5a2f8bf38a4f3017a (commit)
       via  bbbfee30bd3f733940c4ca03300fb7bda46b28de (commit)
       via  7dd6de9fddf41db98647925b9fbbc162370b385b (commit)
       via  f723c98e2d6d932e4cb95b3ac0e398bdbe61ee98 (commit)
      from  1f2aff257dc7f700edd5234f0530396be5f9c19b (commit)


- Log -----------------------------------------------------------------
commit 04dec1ab34df70c1588d42cc394e8fa8b5f3191c
Author: Dr. Stephen Henson <steve at openssl.org>
Date:   Thu May 25 14:53:32 2017 +0100

    Clear sensitive data in ED25519_sign
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3503)

commit 74e783610483ebd4692d41f8c3a7fc57fe6cdfe1
Author: Dr. Stephen Henson <steve at openssl.org>
Date:   Sun Apr 30 13:42:35 2017 +0100

    Add Ed25519 documentation
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3503)

commit 978b700bf9531fa9bee0e69aa292f0f2be0f1251
Author: Dr. Stephen Henson <steve at openssl.org>
Date:   Wed Apr 26 18:46:31 2017 +0100

    no-ec fix
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3503)

commit 4328dd41582bcdca8e4f51f0a3abadfafa2163ee
Author: Dr. Stephen Henson <steve at openssl.org>
Date:   Fri Apr 21 13:36:15 2017 +0100

    Add Ed25519 verify test.
    
    Add Ed25519 certificate verify test using certificate from
    draft-ietf-curdle-pkix-04 and custom generated root certificate.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3503)

commit 684c41c83fdae24b44cdd7af9e71da505d803145
Author: Dr. Stephen Henson <steve at openssl.org>
Date:   Tue Apr 25 20:34:58 2017 +0100

    Add custom sig_info_set for ED25519
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3503)

commit 9f889cc177a5fcdec47f1fc4f7162d34a7305385
Author: Dr. Stephen Henson <steve at openssl.org>
Date:   Sat May 20 03:06:26 2017 +0100

    make errors
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3503)

commit 9f98fbad4720db05485958868363a0296cf6ec99
Author: Dr. Stephen Henson <steve at openssl.org>
Date:   Fri Apr 7 01:17:40 2017 +0100

    Add custom ASN.1 sign and verify
    
    Since ED25519 doesn't have an associated digest it needs custom sign/verify
    routines to handle ASN.1 signatures.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3503)

commit b85236966b8518a23fe87d0d0f7c6cf435cc3878
Author: Dr. Stephen Henson <steve at openssl.org>
Date:   Fri Apr 7 03:34:09 2017 +0100

    Add ED25519 as signature OID
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3503)

commit 1e8c4a9b7a93ecf7936f4fba0632cd82ddfe950a
Author: Dr. Stephen Henson <steve at openssl.org>
Date:   Sat May 20 03:05:13 2017 +0100

    Add RFC8032 tests and additional cases
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3503)

commit ca23d3e089f1b807bfa088f6d916b0aa55594717
Author: Dr. Stephen Henson <steve at openssl.org>
Date:   Sat May 20 03:08:20 2017 +0100

    make errors
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3503)

commit 42a3008aa406429394ff2ae03114d0ac47214e0a
Author: Dr. Stephen Henson <steve at openssl.org>
Date:   Wed Apr 5 21:47:57 2017 +0100

    ED25519 public key method.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3503)

commit a13727e5df57f77b39ad2b57378e8511aa9dcb37
Author: Dr. Stephen Henson <steve at openssl.org>
Date:   Wed Apr 5 16:48:32 2017 +0100

    add method
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3503)

commit 8ecade8bfb85b1d8a473ac12c1589ddbd5a7a431
Author: Dr. Stephen Henson <steve at openssl.org>
Date:   Wed Apr 5 19:05:21 2017 +0100

    Add ED25519 ASN.1 method
    
    Make X25519 key method more flexible by removing hard coding of NID_X25519
    OID. Since the parameters and key syntax between ED25519 and X25519 are
    almost identical they can share a lot of common code.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3503)

commit 9691a749c8c694fa212bfd8a93ae91786ab463af
Author: Dr. Stephen Henson <steve at openssl.org>
Date:   Wed Apr 5 14:13:48 2017 +0100

    Add EdDSA algorithm OIDs from draft-ietf-curdle-pkix-04
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3503)

commit d4d001df371f2ccd13eee7f9d7f4c6594a080168
Author: Dr. Stephen Henson <steve at openssl.org>
Date:   Wed Apr 5 16:09:57 2017 +0100

    Make Ed25519 consistent with X25519
    
    Rename and change ED25519_keypair_from_seed to ED25519_public_from_private
    to be consistent with X25519 API.
    
    Modidy ED25519_sign to take separate public key argument instead of
    requiring it to follow the private key.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3503)

commit 06c6d05faea05ced44a580c5a2f8bf38a4f3017a
Author: Dr. Stephen Henson <steve at openssl.org>
Date:   Tue Apr 4 22:56:41 2017 +0100

    Add Ed25519 algorithm.
    
    Reinstate Ed25519 algorithm to curv25519.c this is largely just a copy of
    the code from BoringSSL with some adjustments so it compiles under OpenSSL.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3503)

commit bbbfee30bd3f733940c4ca03300fb7bda46b28de
Author: Dr. Stephen Henson <steve at openssl.org>
Date:   Sat May 20 00:00:13 2017 +0100

    make errors
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3503)

commit 7dd6de9fddf41db98647925b9fbbc162370b385b
Author: Dr. Stephen Henson <steve at openssl.org>
Date:   Sat May 20 03:18:32 2017 +0100

    Allow NULL md for custom signing methods
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3503)

commit f723c98e2d6d932e4cb95b3ac0e398bdbe61ee98
Author: Dr. Stephen Henson <steve at openssl.org>
Date:   Fri May 19 21:31:46 2017 +0100

    Add support for custom digestsign/digestverify methods.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3503)

-----------------------------------------------------------------------

Summary of changes:
 crypto/asn1/a_sign.c               |    9 +-
 crypto/asn1/standard_methods.h     |    3 +
 crypto/ec/curve25519.c             | 1322 ++++++++++++++++++++++++++++++++++++
 crypto/ec/ec_err.c                 |    5 +-
 crypto/ec/ec_lcl.h                 |    7 +
 crypto/ec/ecx_meth.c               |  178 ++++-
 crypto/evp/evp_err.c               |    2 +
 crypto/evp/m_sigver.c              |   22 +-
 crypto/evp/pmeth_lib.c             |    3 +
 crypto/include/internal/asn1_int.h |    1 +
 crypto/include/internal/evp_int.h  |    6 +
 crypto/objects/obj_dat.h           |   20 +-
 crypto/objects/obj_mac.num         |    2 +
 crypto/objects/obj_xref.h          |    1 +
 crypto/objects/obj_xref.txt        |    1 +
 crypto/objects/objects.txt         |    5 +-
 doc/man3/EVP_DigestSignInit.pod    |    7 +-
 doc/man3/EVP_DigestVerifyInit.pod  |    8 +-
 doc/man7/Ed25519.pod               |   67 ++
 include/openssl/ec.h               |    3 +
 include/openssl/evp.h              |    2 +
 include/openssl/obj_mac.h          |    8 +
 test/certs/ee-ed25519.pem          |    9 +
 test/certs/root-ed25519.pem        |    9 +
 test/evppkey.txt                   |  118 ++++
 test/recipes/25-test_verify.t      |   13 +-
 26 files changed, 1798 insertions(+), 33 deletions(-)
 create mode 100644 doc/man7/Ed25519.pod
 create mode 100644 test/certs/ee-ed25519.pem
 create mode 100644 test/certs/root-ed25519.pem

diff --git a/crypto/asn1/a_sign.c b/crypto/asn1/a_sign.c
index 4e93b5a..5683cbb 100644
--- a/crypto/asn1/a_sign.c
+++ b/crypto/asn1/a_sign.c
@@ -147,7 +147,7 @@ int ASN1_item_sign_ctx(const ASN1_ITEM *it,
     type = EVP_MD_CTX_md(ctx);
     pkey = EVP_PKEY_CTX_get0_pkey(EVP_MD_CTX_pkey_ctx(ctx));
 
-    if (type == NULL || pkey == NULL) {
+    if (pkey == NULL) {
         ASN1err(ASN1_F_ASN1_ITEM_SIGN_CTX, ASN1_R_CONTEXT_NOT_INITIALISED);
         goto err;
     }
@@ -172,10 +172,15 @@ int ASN1_item_sign_ctx(const ASN1_ITEM *it,
             ASN1err(ASN1_F_ASN1_ITEM_SIGN_CTX, ERR_R_EVP_LIB);
         if (rv <= 1)
             goto err;
-    } else
+    } else {
         rv = 2;
+    }
 
     if (rv == 2) {
+        if (type == NULL) {
+            ASN1err(ASN1_F_ASN1_ITEM_SIGN_CTX, ASN1_R_CONTEXT_NOT_INITIALISED);
+            goto err;
+        }
         if (!OBJ_find_sigid_by_algs(&signid,
                                     EVP_MD_nid(type),
                                     pkey->ameth->pkey_id)) {
diff --git a/crypto/asn1/standard_methods.h b/crypto/asn1/standard_methods.h
index a0fd881..f5fa128 100644
--- a/crypto/asn1/standard_methods.h
+++ b/crypto/asn1/standard_methods.h
@@ -49,5 +49,8 @@ static const EVP_PKEY_ASN1_METHOD *standard_methods[] = {
 #ifndef OPENSSL_NO_SIPHASH
     &siphash_asn1_meth,
 #endif
+#ifndef OPENSSL_NO_EC
+    &ed25519_asn1_meth,
+#endif
 };
 
diff --git a/crypto/ec/curve25519.c b/crypto/ec/curve25519.c
index e535823..77f5494 100644
--- a/crypto/ec/curve25519.c
+++ b/crypto/ec/curve25519.c
@@ -14,6 +14,7 @@
 
 #include <string.h>
 #include "ec_lcl.h"
+#include <openssl/sha.h>
 
 
 /* fe means field element. Here the field is \Z/(2^255-19). An element t,
@@ -746,6 +747,30 @@ static void fe_cmov(fe f, const fe g, unsigned b) {
   }
 }
 
+/* return 0 if f == 0
+ * return 1 if f != 0
+ *
+ * Preconditions:
+ *    |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. */
+static int fe_isnonzero(const fe f) {
+  uint8_t s[32];
+  static const uint8_t zero[32] = {0};
+  fe_tobytes(s, f);
+
+  return CRYPTO_memcmp(s, zero, sizeof(zero)) != 0;
+}
+
+/* return 1 if f is in {1,3,5,...,q-2}
+ * return 0 if f is in {0,2,4,...,q-1}
+ *
+ * Preconditions:
+ *    |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. */
+static int fe_isnegative(const fe f) {
+  uint8_t s[32];
+  fe_tobytes(s, f);
+  return s[0] & 1;
+}
+
 /* h = 2 * f * f
  * Can overlap h with f.
  *
@@ -898,6 +923,63 @@ static void fe_sq2(fe h, const fe f) {
   h[9] = h9;
 }
 
+static void fe_pow22523(fe out, const fe z) {
+  fe t0;
+  fe t1;
+  fe t2;
+  int i;
+
+  fe_sq(t0, z);
+  fe_sq(t1, t0);
+  for (i = 1; i < 2; ++i) {
+    fe_sq(t1, t1);
+  }
+  fe_mul(t1, z, t1);
+  fe_mul(t0, t0, t1);
+  fe_sq(t0, t0);
+  fe_mul(t0, t1, t0);
+  fe_sq(t1, t0);
+  for (i = 1; i < 5; ++i) {
+    fe_sq(t1, t1);
+  }
+  fe_mul(t0, t1, t0);
+  fe_sq(t1, t0);
+  for (i = 1; i < 10; ++i) {
+    fe_sq(t1, t1);
+  }
+  fe_mul(t1, t1, t0);
+  fe_sq(t2, t1);
+  for (i = 1; i < 20; ++i) {
+    fe_sq(t2, t2);
+  }
+  fe_mul(t1, t2, t1);
+  fe_sq(t1, t1);
+  for (i = 1; i < 10; ++i) {
+    fe_sq(t1, t1);
+  }
+  fe_mul(t0, t1, t0);
+  fe_sq(t1, t0);
+  for (i = 1; i < 50; ++i) {
+    fe_sq(t1, t1);
+  }
+  fe_mul(t1, t1, t0);
+  fe_sq(t2, t1);
+  for (i = 1; i < 100; ++i) {
+    fe_sq(t2, t2);
+  }
+  fe_mul(t1, t2, t1);
+  fe_sq(t1, t1);
+  for (i = 1; i < 50; ++i) {
+    fe_sq(t1, t1);
+  }
+  fe_mul(t0, t1, t0);
+  fe_sq(t0, t0);
+  for (i = 1; i < 2; ++i) {
+    fe_sq(t0, t0);
+  }
+  fe_mul(out, t0, z);
+}
+
 /* ge means group element.
 
  * Here the group is the set of pairs (x,y) of field elements (see fe.h)
@@ -943,6 +1025,85 @@ typedef struct {
   fe T2d;
 } ge_cached;
 
+static void ge_tobytes(uint8_t *s, const ge_p2 *h) {
+  fe recip;
+  fe x;
+  fe y;
+
+  fe_invert(recip, h->Z);
+  fe_mul(x, h->X, recip);
+  fe_mul(y, h->Y, recip);
+  fe_tobytes(s, y);
+  s[31] ^= fe_isnegative(x) << 7;
+}
+
+static void ge_p3_tobytes(uint8_t *s, const ge_p3 *h) {
+  fe recip;
+  fe x;
+  fe y;
+
+  fe_invert(recip, h->Z);
+  fe_mul(x, h->X, recip);
+  fe_mul(y, h->Y, recip);
+  fe_tobytes(s, y);
+  s[31] ^= fe_isnegative(x) << 7;
+}
+
+static const fe d = {-10913610, 13857413, -15372611, 6949391,   114729,
+                     -8787816,  -6275908, -3247719,  -18696448, -12055116};
+
+static const fe sqrtm1 = {-32595792, -7943725,  9377950,  3500415, 12389472,
+                          -272473,   -25146209, -2005654, 326686,  11406482};
+
+static int ge_frombytes_vartime(ge_p3 *h, const uint8_t *s) {
+  fe u;
+  fe v;
+  fe v3;
+  fe vxx;
+  fe check;
+
+  fe_frombytes(h->Y, s);
+  fe_1(h->Z);
+  fe_sq(u, h->Y);
+  fe_mul(v, u, d);
+  fe_sub(u, u, h->Z); /* u = y^2-1 */
+  fe_add(v, v, h->Z); /* v = dy^2+1 */
+
+  fe_sq(v3, v);
+  fe_mul(v3, v3, v); /* v3 = v^3 */
+  fe_sq(h->X, v3);
+  fe_mul(h->X, h->X, v);
+  fe_mul(h->X, h->X, u); /* x = uv^7 */
+
+  fe_pow22523(h->X, h->X); /* x = (uv^7)^((q-5)/8) */
+  fe_mul(h->X, h->X, v3);
+  fe_mul(h->X, h->X, u); /* x = uv^3(uv^7)^((q-5)/8) */
+
+  fe_sq(vxx, h->X);
+  fe_mul(vxx, vxx, v);
+  fe_sub(check, vxx, u); /* vx^2-u */
+  if (fe_isnonzero(check)) {
+    fe_add(check, vxx, u); /* vx^2+u */
+    if (fe_isnonzero(check)) {
+      return -1;
+    }
+    fe_mul(h->X, h->X, sqrtm1);
+  }
+
+  if (fe_isnegative(h->X) != (s[31] >> 7)) {
+    fe_neg(h->X, h->X);
+  }
+
+  fe_mul(h->T, h->X, h->Y);
+  return 0;
+}
+
+static void ge_p2_0(ge_p2 *h) {
+  fe_0(h->X);
+  fe_1(h->Y);
+  fe_1(h->Z);
+}
+
 static void ge_p3_0(ge_p3 *h) {
   fe_0(h->X);
   fe_1(h->Y);
@@ -963,6 +1124,17 @@ static void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p) {
   fe_copy(r->Z, p->Z);
 }
 
+static const fe d2 = {-21827239, -5839606,  -30745221, 13898782, 229458,
+                      15978800,  -12551817, -6495438,  29715968, 9444199};
+
+/* r = p */
+static void ge_p3_to_cached(ge_cached *r, const ge_p3 *p) {
+  fe_add(r->YplusX, p->Y, p->X);
+  fe_sub(r->YminusX, p->Y, p->X);
+  fe_copy(r->Z, p->Z);
+  fe_mul(r->T2d, p->T, d2);
+}
+
 /* r = p */
 static void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p) {
   fe_mul(r->X, p->X, p->T);
@@ -1016,6 +1188,56 @@ static void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) {
   fe_sub(r->T, t0, r->T);
 }
 
+/* r = p - q */
+static void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) {
+  fe t0;
+
+  fe_add(r->X, p->Y, p->X);
+  fe_sub(r->Y, p->Y, p->X);
+  fe_mul(r->Z, r->X, q->yminusx);
+  fe_mul(r->Y, r->Y, q->yplusx);
+  fe_mul(r->T, q->xy2d, p->T);
+  fe_add(t0, p->Z, p->Z);
+  fe_sub(r->X, r->Z, r->Y);
+  fe_add(r->Y, r->Z, r->Y);
+  fe_sub(r->Z, t0, r->T);
+  fe_add(r->T, t0, r->T);
+}
+
+/* r = p + q */
+static void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) {
+  fe t0;
+
+  fe_add(r->X, p->Y, p->X);
+  fe_sub(r->Y, p->Y, p->X);
+  fe_mul(r->Z, r->X, q->YplusX);
+  fe_mul(r->Y, r->Y, q->YminusX);
+  fe_mul(r->T, q->T2d, p->T);
+  fe_mul(r->X, p->Z, q->Z);
+  fe_add(t0, r->X, r->X);
+  fe_sub(r->X, r->Z, r->Y);
+  fe_add(r->Y, r->Z, r->Y);
+  fe_add(r->Z, t0, r->T);
+  fe_sub(r->T, t0, r->T);
+}
+
+/* r = p - q */
+static void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) {
+  fe t0;
+
+  fe_add(r->X, p->Y, p->X);
+  fe_sub(r->Y, p->Y, p->X);
+  fe_mul(r->Z, r->X, q->YminusX);
+  fe_mul(r->Y, r->Y, q->YplusX);
+  fe_mul(r->T, q->T2d, p->T);
+  fe_mul(r->X, p->Z, q->Z);
+  fe_add(t0, r->X, r->X);
+  fe_sub(r->X, r->Z, r->Y);
+  fe_add(r->Y, r->Z, r->Y);
+  fe_sub(r->Z, t0, r->T);
+  fe_add(r->T, t0, r->T);
+}
+
 static uint8_t equal(signed char b, signed char c) {
   uint8_t ub = b;
   uint8_t uc = c;
@@ -3363,6 +3585,1106 @@ static void x25519_scalar_mult(uint8_t out[32], const uint8_t scalar[32],
   x25519_scalar_mult_generic(out, scalar, point);
 }
 
+static void slide(signed char *r, const uint8_t *a) {
+  int i;
+  int b;
+  int k;
+
+  for (i = 0; i < 256; ++i) {
+    r[i] = 1 & (a[i >> 3] >> (i & 7));
+  }
+
+  for (i = 0; i < 256; ++i) {
+    if (r[i]) {
+      for (b = 1; b <= 6 && i + b < 256; ++b) {
+        if (r[i + b]) {
+          if (r[i] + (r[i + b] << b) <= 15) {
+            r[i] += r[i + b] << b;
+            r[i + b] = 0;
+          } else if (r[i] - (r[i + b] << b) >= -15) {
+            r[i] -= r[i + b] << b;
+            for (k = i + b; k < 256; ++k) {
+              if (!r[k]) {
+                r[k] = 1;
+                break;
+              }
+              r[k] = 0;
+            }
+          } else {
+            break;
+          }
+        }
+      }
+    }
+  }
+}
+
+static const ge_precomp Bi[8] = {
+    {
+        {25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626,
+         -11754271, -6079156, 2047605},
+        {-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692,
+         5043384, 19500929, -15469378},
+        {-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919,
+         11864899, -24514362, -4438546},
+    },
+    {
+        {15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600,
+         -14772189, 28944400, -1550024},
+        {16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577,
+         -11775962, 7689662, 11199574},
+        {30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774,
+         10017326, -17749093, -9920357},
+    },
+    {
+        {10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885,
+         14515107, -15438304, 10819380},
+        {4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668,
+         12483688, -12668491, 5581306},
+        {19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350,
+         13850243, -23678021, -15815942},
+    },
+    {
+        {5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852,
+         5230134, -23952439, -15175766},
+        {-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025,
+         16520125, 30598449, 7715701},
+        {28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660,
+         1370708, 29794553, -1409300},
+    },
+    {
+        {-22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211,
+         -1361450, -13062696, 13821877},
+        {-6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028,
+         -7212327, 18853322, -14220951},
+        {4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358,
+         -10431137, 2207753, -3209784},
+    },
+    {
+        {-25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364,
+         -663000, -31111463, -16132436},
+        {25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789,
+         15725684, 171356, 6466918},
+        {23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339,
+         -14088058, -30714912, 16193877},
+    },
+    {
+        {-33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398,
+         4729455, -18074513, 9256800},
+        {-25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405,
+         9761698, -19827198, 630305},
+        {-13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551,
+         -15960994, -2449256, -14291300},
+    },
+    {
+        {-3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575,
+         15033784, 25105118, -7894876},
+        {-24326370, 15950226, -31801215, -14592823, -11662737, -5090925,
+         1573892, -2625887, 2198790, -15804619},
+        {-3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022,
+         -16236442, -32461234, -12290683},
+    },
+};
+
+/* r = a * A + b * B
+ * where a = a[0]+256*a[1]+...+256^31 a[31].
+ * and b = b[0]+256*b[1]+...+256^31 b[31].
+ * B is the Ed25519 base point (x,4/5) with x positive. */
+static void ge_double_scalarmult_vartime(ge_p2 *r, const uint8_t *a,
+                                         const ge_p3 *A, const uint8_t *b) {
+  signed char aslide[256];
+  signed char bslide[256];
+  ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */
+  ge_p1p1 t;
+  ge_p3 u;
+  ge_p3 A2;
+  int i;
+
+  slide(aslide, a);
+  slide(bslide, b);
+
+  ge_p3_to_cached(&Ai[0], A);
+  ge_p3_dbl(&t, A);
+  ge_p1p1_to_p3(&A2, &t);
+  ge_add(&t, &A2, &Ai[0]);
+  ge_p1p1_to_p3(&u, &t);
+  ge_p3_to_cached(&Ai[1], &u);
+  ge_add(&t, &A2, &Ai[1]);
+  ge_p1p1_to_p3(&u, &t);
+  ge_p3_to_cached(&Ai[2], &u);
+  ge_add(&t, &A2, &Ai[2]);
+  ge_p1p1_to_p3(&u, &t);
+  ge_p3_to_cached(&Ai[3], &u);
+  ge_add(&t, &A2, &Ai[3]);
+  ge_p1p1_to_p3(&u, &t);
+  ge_p3_to_cached(&Ai[4], &u);
+  ge_add(&t, &A2, &Ai[4]);
+  ge_p1p1_to_p3(&u, &t);
+  ge_p3_to_cached(&Ai[5], &u);
+  ge_add(&t, &A2, &Ai[5]);
+  ge_p1p1_to_p3(&u, &t);
+  ge_p3_to_cached(&Ai[6], &u);
+  ge_add(&t, &A2, &Ai[6]);
+  ge_p1p1_to_p3(&u, &t);
+  ge_p3_to_cached(&Ai[7], &u);
+
+  ge_p2_0(r);
+
+  for (i = 255; i >= 0; --i) {
+    if (aslide[i] || bslide[i]) {
+      break;
+    }
+  }
+
+  for (; i >= 0; --i) {
+    ge_p2_dbl(&t, r);
+
+    if (aslide[i] > 0) {
+      ge_p1p1_to_p3(&u, &t);
+      ge_add(&t, &u, &Ai[aslide[i] / 2]);
+    } else if (aslide[i] < 0) {
+      ge_p1p1_to_p3(&u, &t);
+      ge_sub(&t, &u, &Ai[(-aslide[i]) / 2]);
+    }
+
+    if (bslide[i] > 0) {
+      ge_p1p1_to_p3(&u, &t);
+      ge_madd(&t, &u, &Bi[bslide[i] / 2]);
+    } else if (bslide[i] < 0) {
+      ge_p1p1_to_p3(&u, &t);
+      ge_msub(&t, &u, &Bi[(-bslide[i]) / 2]);
+    }
+
+    ge_p1p1_to_p2(r, &t);
+  }
+}
+
+/* The set of scalars is \Z/l
+ * where l = 2^252 + 27742317777372353535851937790883648493. */
+
+/* Input:
+ *   s[0]+256*s[1]+...+256^63*s[63] = s
+ *
+ * Output:
+ *   s[0]+256*s[1]+...+256^31*s[31] = s mod l
+ *   where l = 2^252 + 27742317777372353535851937790883648493.
+ *   Overwrites s in place. */
+static void x25519_sc_reduce(uint8_t *s) {
+  int64_t s0 = 2097151 & load_3(s);
+  int64_t s1 = 2097151 & (load_4(s + 2) >> 5);
+  int64_t s2 = 2097151 & (load_3(s + 5) >> 2);
+  int64_t s3 = 2097151 & (load_4(s + 7) >> 7);
+  int64_t s4 = 2097151 & (load_4(s + 10) >> 4);
+  int64_t s5 = 2097151 & (load_3(s + 13) >> 1);
+  int64_t s6 = 2097151 & (load_4(s + 15) >> 6);
+  int64_t s7 = 2097151 & (load_3(s + 18) >> 3);
+  int64_t s8 = 2097151 & load_3(s + 21);
+  int64_t s9 = 2097151 & (load_4(s + 23) >> 5);
+  int64_t s10 = 2097151 & (load_3(s + 26) >> 2);
+  int64_t s11 = 2097151 & (load_4(s + 28) >> 7);
+  int64_t s12 = 2097151 & (load_4(s + 31) >> 4);
+  int64_t s13 = 2097151 & (load_3(s + 34) >> 1);
+  int64_t s14 = 2097151 & (load_4(s + 36) >> 6);
+  int64_t s15 = 2097151 & (load_3(s + 39) >> 3);
+  int64_t s16 = 2097151 & load_3(s + 42);
+  int64_t s17 = 2097151 & (load_4(s + 44) >> 5);
+  int64_t s18 = 2097151 & (load_3(s + 47) >> 2);
+  int64_t s19 = 2097151 & (load_4(s + 49) >> 7);
+  int64_t s20 = 2097151 & (load_4(s + 52) >> 4);
+  int64_t s21 = 2097151 & (load_3(s + 55) >> 1);
+  int64_t s22 = 2097151 & (load_4(s + 57) >> 6);
+  int64_t s23 = (load_4(s + 60) >> 3);
+  int64_t carry0;
+  int64_t carry1;
+  int64_t carry2;
+  int64_t carry3;
+  int64_t carry4;
+  int64_t carry5;
+  int64_t carry6;
+  int64_t carry7;
+  int64_t carry8;
+  int64_t carry9;
+  int64_t carry10;
+  int64_t carry11;
+  int64_t carry12;
+  int64_t carry13;
+  int64_t carry14;
+  int64_t carry15;
+  int64_t carry16;
+
+  s11 += s23 * 666643;
+  s12 += s23 * 470296;
+  s13 += s23 * 654183;
+  s14 -= s23 * 997805;
+  s15 += s23 * 136657;
+  s16 -= s23 * 683901;
+  s23 = 0;
+
+  s10 += s22 * 666643;
+  s11 += s22 * 470296;
+  s12 += s22 * 654183;
+  s13 -= s22 * 997805;
+  s14 += s22 * 136657;
+  s15 -= s22 * 683901;
+  s22 = 0;
+
+  s9 += s21 * 666643;
+  s10 += s21 * 470296;
+  s11 += s21 * 654183;
+  s12 -= s21 * 997805;
+  s13 += s21 * 136657;
+  s14 -= s21 * 683901;
+  s21 = 0;
+
+  s8 += s20 * 666643;
+  s9 += s20 * 470296;
+  s10 += s20 * 654183;
+  s11 -= s20 * 997805;
+  s12 += s20 * 136657;
+  s13 -= s20 * 683901;
+  s20 = 0;
+
+  s7 += s19 * 666643;
+  s8 += s19 * 470296;
+  s9 += s19 * 654183;
+  s10 -= s19 * 997805;
+  s11 += s19 * 136657;
+  s12 -= s19 * 683901;
+  s19 = 0;
+
+  s6 += s18 * 666643;
+  s7 += s18 * 470296;
+  s8 += s18 * 654183;
+  s9 -= s18 * 997805;
+  s10 += s18 * 136657;
+  s11 -= s18 * 683901;
+  s18 = 0;
+
+  carry6 = (s6 + (1 << 20)) >> 21;
+  s7 += carry6;
+  s6 -= carry6 << 21;
+  carry8 = (s8 + (1 << 20)) >> 21;
+  s9 += carry8;
+  s8 -= carry8 << 21;
+  carry10 = (s10 + (1 << 20)) >> 21;
+  s11 += carry10;
+  s10 -= carry10 << 21;
+  carry12 = (s12 + (1 << 20)) >> 21;
+  s13 += carry12;
+  s12 -= carry12 << 21;
+  carry14 = (s14 + (1 << 20)) >> 21;
+  s15 += carry14;
+  s14 -= carry14 << 21;
+  carry16 = (s16 + (1 << 20)) >> 21;
+  s17 += carry16;
+  s16 -= carry16 << 21;
+
+  carry7 = (s7 + (1 << 20)) >> 21;
+  s8 += carry7;
+  s7 -= carry7 << 21;
+  carry9 = (s9 + (1 << 20)) >> 21;
+  s10 += carry9;
+  s9 -= carry9 << 21;
+  carry11 = (s11 + (1 << 20)) >> 21;
+  s12 += carry11;
+  s11 -= carry11 << 21;
+  carry13 = (s13 + (1 << 20)) >> 21;
+  s14 += carry13;
+  s13 -= carry13 << 21;
+  carry15 = (s15 + (1 << 20)) >> 21;
+  s16 += carry15;
+  s15 -= carry15 << 21;
+
+  s5 += s17 * 666643;
+  s6 += s17 * 470296;
+  s7 += s17 * 654183;
+  s8 -= s17 * 997805;
+  s9 += s17 * 136657;
+  s10 -= s17 * 683901;
+  s17 = 0;
+
+  s4 += s16 * 666643;
+  s5 += s16 * 470296;
+  s6 += s16 * 654183;
+  s7 -= s16 * 997805;
+  s8 += s16 * 136657;
+  s9 -= s16 * 683901;
+  s16 = 0;
+
+  s3 += s15 * 666643;
+  s4 += s15 * 470296;
+  s5 += s15 * 654183;
+  s6 -= s15 * 997805;
+  s7 += s15 * 136657;
+  s8 -= s15 * 683901;
+  s15 = 0;
+
+  s2 += s14 * 666643;
+  s3 += s14 * 470296;
+  s4 += s14 * 654183;
+  s5 -= s14 * 997805;
+  s6 += s14 * 136657;
+  s7 -= s14 * 683901;
+  s14 = 0;
+
+  s1 += s13 * 666643;
+  s2 += s13 * 470296;
+  s3 += s13 * 654183;
+  s4 -= s13 * 997805;
+  s5 += s13 * 136657;
+  s6 -= s13 * 683901;
+  s13 = 0;
+
+  s0 += s12 * 666643;
+  s1 += s12 * 470296;
+  s2 += s12 * 654183;
+  s3 -= s12 * 997805;
+  s4 += s12 * 136657;
+  s5 -= s12 * 683901;
+  s12 = 0;
+
+  carry0 = (s0 + (1 << 20)) >> 21;
+  s1 += carry0;
+  s0 -= carry0 << 21;
+  carry2 = (s2 + (1 << 20)) >> 21;
+  s3 += carry2;
+  s2 -= carry2 << 21;
+  carry4 = (s4 + (1 << 20)) >> 21;
+  s5 += carry4;
+  s4 -= carry4 << 21;
+  carry6 = (s6 + (1 << 20)) >> 21;
+  s7 += carry6;
+  s6 -= carry6 << 21;
+  carry8 = (s8 + (1 << 20)) >> 21;
+  s9 += carry8;
+  s8 -= carry8 << 21;
+  carry10 = (s10 + (1 << 20)) >> 21;
+  s11 += carry10;
+  s10 -= carry10 << 21;
+
+  carry1 = (s1 + (1 << 20)) >> 21;
+  s2 += carry1;
+  s1 -= carry1 << 21;
+  carry3 = (s3 + (1 << 20)) >> 21;
+  s4 += carry3;
+  s3 -= carry3 << 21;
+  carry5 = (s5 + (1 << 20)) >> 21;
+  s6 += carry5;
+  s5 -= carry5 << 21;
+  carry7 = (s7 + (1 << 20)) >> 21;
+  s8 += carry7;
+  s7 -= carry7 << 21;
+  carry9 = (s9 + (1 << 20)) >> 21;
+  s10 += carry9;
+  s9 -= carry9 << 21;
+  carry11 = (s11 + (1 << 20)) >> 21;
+  s12 += carry11;
+  s11 -= carry11 << 21;
+
+  s0 += s12 * 666643;
+  s1 += s12 * 470296;
+  s2 += s12 * 654183;
+  s3 -= s12 * 997805;
+  s4 += s12 * 136657;
+  s5 -= s12 * 683901;
+  s12 = 0;
+
+  carry0 = s0 >> 21;
+  s1 += carry0;
+  s0 -= carry0 << 21;
+  carry1 = s1 >> 21;
+  s2 += carry1;
+  s1 -= carry1 << 21;
+  carry2 = s2 >> 21;
+  s3 += carry2;
+  s2 -= carry2 << 21;
+  carry3 = s3 >> 21;
+  s4 += carry3;
+  s3 -= carry3 << 21;
+  carry4 = s4 >> 21;
+  s5 += carry4;
+  s4 -= carry4 << 21;
+  carry5 = s5 >> 21;
+  s6 += carry5;
+  s5 -= carry5 << 21;
+  carry6 = s6 >> 21;
+  s7 += carry6;
+  s6 -= carry6 << 21;
+  carry7 = s7 >> 21;
+  s8 += carry7;
+  s7 -= carry7 << 21;
+  carry8 = s8 >> 21;
+  s9 += carry8;
+  s8 -= carry8 << 21;
+  carry9 = s9 >> 21;
+  s10 += carry9;
+  s9 -= carry9 << 21;
+  carry10 = s10 >> 21;
+  s11 += carry10;
+  s10 -= carry10 << 21;
+  carry11 = s11 >> 21;
+  s12 += carry11;
+  s11 -= carry11 << 21;
+
+  s0 += s12 * 666643;
+  s1 += s12 * 470296;
+  s2 += s12 * 654183;
+  s3 -= s12 * 997805;
+  s4 += s12 * 136657;
+  s5 -= s12 * 683901;
+  s12 = 0;
+
+  carry0 = s0 >> 21;
+  s1 += carry0;
+  s0 -= carry0 << 21;
+  carry1 = s1 >> 21;
+  s2 += carry1;
+  s1 -= carry1 << 21;
+  carry2 = s2 >> 21;
+  s3 += carry2;
+  s2 -= carry2 << 21;
+  carry3 = s3 >> 21;
+  s4 += carry3;
+  s3 -= carry3 << 21;
+  carry4 = s4 >> 21;
+  s5 += carry4;
+  s4 -= carry4 << 21;
+  carry5 = s5 >> 21;
+  s6 += carry5;
+  s5 -= carry5 << 21;
+  carry6 = s6 >> 21;
+  s7 += carry6;
+  s6 -= carry6 << 21;
+  carry7 = s7 >> 21;
+  s8 += carry7;
+  s7 -= carry7 << 21;
+  carry8 = s8 >> 21;
+  s9 += carry8;
+  s8 -= carry8 << 21;
+  carry9 = s9 >> 21;
+  s10 += carry9;
+  s9 -= carry9 << 21;
+  carry10 = s10 >> 21;
+  s11 += carry10;
+  s10 -= carry10 << 21;
+
+  s[0] = s0 >> 0;
+  s[1] = s0 >> 8;
+  s[2] = (s0 >> 16) | (s1 << 5);
+  s[3] = s1 >> 3;
+  s[4] = s1 >> 11;
+  s[5] = (s1 >> 19) | (s2 << 2);
+  s[6] = s2 >> 6;
+  s[7] = (s2 >> 14) | (s3 << 7);
+  s[8] = s3 >> 1;
+  s[9] = s3 >> 9;
+  s[10] = (s3 >> 17) | (s4 << 4);
+  s[11] = s4 >> 4;
+  s[12] = s4 >> 12;
+  s[13] = (s4 >> 20) | (s5 << 1);
+  s[14] = s5 >> 7;
+  s[15] = (s5 >> 15) | (s6 << 6);
+  s[16] = s6 >> 2;
+  s[17] = s6 >> 10;
+  s[18] = (s6 >> 18) | (s7 << 3);
+  s[19] = s7 >> 5;
+  s[20] = s7 >> 13;
+  s[21] = s8 >> 0;
+  s[22] = s8 >> 8;
+  s[23] = (s8 >> 16) | (s9 << 5);
+  s[24] = s9 >> 3;
+  s[25] = s9 >> 11;
+  s[26] = (s9 >> 19) | (s10 << 2);
+  s[27] = s10 >> 6;
+  s[28] = (s10 >> 14) | (s11 << 7);
+  s[29] = s11 >> 1;
+  s[30] = s11 >> 9;
+  s[31] = s11 >> 17;
+}
+
+/* Input:
+ *   a[0]+256*a[1]+...+256^31*a[31] = a
+ *   b[0]+256*b[1]+...+256^31*b[31] = b
+ *   c[0]+256*c[1]+...+256^31*c[31] = c
+ *
+ * Output:
+ *   s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l
+ *   where l = 2^252 + 27742317777372353535851937790883648493. */
+static void sc_muladd(uint8_t *s, const uint8_t *a, const uint8_t *b,
+                      const uint8_t *c) {
+  int64_t a0 = 2097151 & load_3(a);
+  int64_t a1 = 2097151 & (load_4(a + 2) >> 5);
+  int64_t a2 = 2097151 & (load_3(a + 5) >> 2);
+  int64_t a3 = 2097151 & (load_4(a + 7) >> 7);
+  int64_t a4 = 2097151 & (load_4(a + 10) >> 4);
+  int64_t a5 = 2097151 & (load_3(a + 13) >> 1);
+  int64_t a6 = 2097151 & (load_4(a + 15) >> 6);
+  int64_t a7 = 2097151 & (load_3(a + 18) >> 3);
+  int64_t a8 = 2097151 & load_3(a + 21);
+  int64_t a9 = 2097151 & (load_4(a + 23) >> 5);
+  int64_t a10 = 2097151 & (load_3(a + 26) >> 2);
+  int64_t a11 = (load_4(a + 28) >> 7);
+  int64_t b0 = 2097151 & load_3(b);
+  int64_t b1 = 2097151 & (load_4(b + 2) >> 5);
+  int64_t b2 = 2097151 & (load_3(b + 5) >> 2);
+  int64_t b3 = 2097151 & (load_4(b + 7) >> 7);
+  int64_t b4 = 2097151 & (load_4(b + 10) >> 4);
+  int64_t b5 = 2097151 & (load_3(b + 13) >> 1);
+  int64_t b6 = 2097151 & (load_4(b + 15) >> 6);
+  int64_t b7 = 2097151 & (load_3(b + 18) >> 3);
+  int64_t b8 = 2097151 & load_3(b + 21);
+  int64_t b9 = 2097151 & (load_4(b + 23) >> 5);
+  int64_t b10 = 2097151 & (load_3(b + 26) >> 2);
+  int64_t b11 = (load_4(b + 28) >> 7);
+  int64_t c0 = 2097151 & load_3(c);
+  int64_t c1 = 2097151 & (load_4(c + 2) >> 5);
+  int64_t c2 = 2097151 & (load_3(c + 5) >> 2);
+  int64_t c3 = 2097151 & (load_4(c + 7) >> 7);
+  int64_t c4 = 2097151 & (load_4(c + 10) >> 4);
+  int64_t c5 = 2097151 & (load_3(c + 13) >> 1);
+  int64_t c6 = 2097151 & (load_4(c + 15) >> 6);
+  int64_t c7 = 2097151 & (load_3(c + 18) >> 3);
+  int64_t c8 = 2097151 & load_3(c + 21);
+  int64_t c9 = 2097151 & (load_4(c + 23) >> 5);
+  int64_t c10 = 2097151 & (load_3(c + 26) >> 2);
+  int64_t c11 = (load_4(c + 28) >> 7);
+  int64_t s0;
+  int64_t s1;
+  int64_t s2;
+  int64_t s3;
+  int64_t s4;
+  int64_t s5;
+  int64_t s6;
+  int64_t s7;
+  int64_t s8;
+  int64_t s9;
+  int64_t s10;
+  int64_t s11;
+  int64_t s12;
+  int64_t s13;
+  int64_t s14;
+  int64_t s15;
+  int64_t s16;
+  int64_t s17;
+  int64_t s18;
+  int64_t s19;
+  int64_t s20;
+  int64_t s21;
+  int64_t s22;
+  int64_t s23;
+  int64_t carry0;
+  int64_t carry1;
+  int64_t carry2;
+  int64_t carry3;
+  int64_t carry4;
+  int64_t carry5;
+  int64_t carry6;
+  int64_t carry7;
+  int64_t carry8;
+  int64_t carry9;
+  int64_t carry10;
+  int64_t carry11;
+  int64_t carry12;
+  int64_t carry13;
+  int64_t carry14;
+  int64_t carry15;
+  int64_t carry16;
+  int64_t carry17;
+  int64_t carry18;
+  int64_t carry19;
+  int64_t carry20;
+  int64_t carry21;
+  int64_t carry22;
+
+  s0 = c0 + a0 * b0;
+  s1 = c1 + a0 * b1 + a1 * b0;
+  s2 = c2 + a0 * b2 + a1 * b1 + a2 * b0;
+  s3 = c3 + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0;
+  s4 = c4 + a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0;
+  s5 = c5 + a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0;
+  s6 = c6 + a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + a6 * b0;
+  s7 = c7 + a0 * b7 + a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 +
+       a6 * b1 + a7 * b0;
+  s8 = c8 + a0 * b8 + a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 +
+       a6 * b2 + a7 * b1 + a8 * b0;
+  s9 = c9 + a0 * b9 + a1 * b8 + a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 +
+       a6 * b3 + a7 * b2 + a8 * b1 + a9 * b0;
+  s10 = c10 + a0 * b10 + a1 * b9 + a2 * b8 + a3 * b7 + a4 * b6 + a5 * b5 +
+        a6 * b4 + a7 * b3 + a8 * b2 + a9 * b1 + a10 * b0;
+  s11 = c11 + a0 * b11 + a1 * b10 + a2 * b9 + a3 * b8 + a4 * b7 + a5 * b6 +
+        a6 * b5 + a7 * b4 + a8 * b3 + a9 * b2 + a10 * b1 + a11 * b0;
+  s12 = a1 * b11 + a2 * b10 + a3 * b9 + a4 * b8 + a5 * b7 + a6 * b6 + a7 * b5 +
+        a8 * b4 + a9 * b3 + a10 * b2 + a11 * b1;
+  s13 = a2 * b11 + a3 * b10 + a4 * b9 + a5 * b8 + a6 * b7 + a7 * b6 + a8 * b5 +
+        a9 * b4 + a10 * b3 + a11 * b2;
+  s14 = a3 * b11 + a4 * b10 + a5 * b9 + a6 * b8 + a7 * b7 + a8 * b6 + a9 * b5 +
+        a10 * b4 + a11 * b3;
+  s15 = a4 * b11 + a5 * b10 + a6 * b9 + a7 * b8 + a8 * b7 + a9 * b6 + a10 * b5 +
+        a11 * b4;
+  s16 = a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5;
+  s17 = a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6;
+  s18 = a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7;
+  s19 = a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8;
+  s20 = a9 * b11 + a10 * b10 + a11 * b9;
+  s21 = a10 * b11 + a11 * b10;
+  s22 = a11 * b11;
+  s23 = 0;
+
+  carry0 = (s0 + (1 << 20)) >> 21;
+  s1 += carry0;
+  s0 -= carry0 << 21;
+  carry2 = (s2 + (1 << 20)) >> 21;
+  s3 += carry2;
+  s2 -= carry2 << 21;
+  carry4 = (s4 + (1 << 20)) >> 21;
+  s5 += carry4;
+  s4 -= carry4 << 21;
+  carry6 = (s6 + (1 << 20)) >> 21;
+  s7 += carry6;
+  s6 -= carry6 << 21;
+  carry8 = (s8 + (1 << 20)) >> 21;
+  s9 += carry8;
+  s8 -= carry8 << 21;
+  carry10 = (s10 + (1 << 20)) >> 21;
+  s11 += carry10;
+  s10 -= carry10 << 21;
+  carry12 = (s12 + (1 << 20)) >> 21;
+  s13 += carry12;
+  s12 -= carry12 << 21;
+  carry14 = (s14 + (1 << 20)) >> 21;
+  s15 += carry14;
+  s14 -= carry14 << 21;
+  carry16 = (s16 + (1 << 20)) >> 21;
+  s17 += carry16;
+  s16 -= carry16 << 21;
+  carry18 = (s18 + (1 << 20)) >> 21;
+  s19 += carry18;
+  s18 -= carry18 << 21;
+  carry20 = (s20 + (1 << 20)) >> 21;
+  s21 += carry20;
+  s20 -= carry20 << 21;
+  carry22 = (s22 + (1 << 20)) >> 21;
+  s23 += carry22;
+  s22 -= carry22 << 21;
+
+  carry1 = (s1 + (1 << 20)) >> 21;
+  s2 += carry1;
+  s1 -= carry1 << 21;
+  carry3 = (s3 + (1 << 20)) >> 21;
+  s4 += carry3;
+  s3 -= carry3 << 21;
+  carry5 = (s5 + (1 << 20)) >> 21;
+  s6 += carry5;
+  s5 -= carry5 << 21;
+  carry7 = (s7 + (1 << 20)) >> 21;
+  s8 += carry7;
+  s7 -= carry7 << 21;
+  carry9 = (s9 + (1 << 20)) >> 21;
+  s10 += carry9;
+  s9 -= carry9 << 21;
+  carry11 = (s11 + (1 << 20)) >> 21;
+  s12 += carry11;
+  s11 -= carry11 << 21;
+  carry13 = (s13 + (1 << 20)) >> 21;
+  s14 += carry13;
+  s13 -= carry13 << 21;
+  carry15 = (s15 + (1 << 20)) >> 21;
+  s16 += carry15;
+  s15 -= carry15 << 21;
+  carry17 = (s17 + (1 << 20)) >> 21;
+  s18 += carry17;
+  s17 -= carry17 << 21;
+  carry19 = (s19 + (1 << 20)) >> 21;
+  s20 += carry19;
+  s19 -= carry19 << 21;
+  carry21 = (s21 + (1 << 20)) >> 21;
+  s22 += carry21;
+  s21 -= carry21 << 21;
+
+  s11 += s23 * 666643;
+  s12 += s23 * 470296;
+  s13 += s23 * 654183;
+  s14 -= s23 * 997805;
+  s15 += s23 * 136657;
+  s16 -= s23 * 683901;
+  s23 = 0;
+
+  s10 += s22 * 666643;
+  s11 += s22 * 470296;
+  s12 += s22 * 654183;
+  s13 -= s22 * 997805;
+  s14 += s22 * 136657;
+  s15 -= s22 * 683901;
+  s22 = 0;
+
+  s9 += s21 * 666643;
+  s10 += s21 * 470296;
+  s11 += s21 * 654183;
+  s12 -= s21 * 997805;
+  s13 += s21 * 136657;
+  s14 -= s21 * 683901;
+  s21 = 0;
+
+  s8 += s20 * 666643;
+  s9 += s20 * 470296;
+  s10 += s20 * 654183;
+  s11 -= s20 * 997805;
+  s12 += s20 * 136657;
+  s13 -= s20 * 683901;
+  s20 = 0;
+
+  s7 += s19 * 666643;
+  s8 += s19 * 470296;
+  s9 += s19 * 654183;
+  s10 -= s19 * 997805;
+  s11 += s19 * 136657;
+  s12 -= s19 * 683901;
+  s19 = 0;
+
+  s6 += s18 * 666643;
+  s7 += s18 * 470296;
+  s8 += s18 * 654183;
+  s9 -= s18 * 997805;
+  s10 += s18 * 136657;
+  s11 -= s18 * 683901;
+  s18 = 0;
+
+  carry6 = (s6 + (1 << 20)) >> 21;
+  s7 += carry6;
+  s6 -= carry6 << 21;
+  carry8 = (s8 + (1 << 20)) >> 21;
+  s9 += carry8;
+  s8 -= carry8 << 21;
+  carry10 = (s10 + (1 << 20)) >> 21;
+  s11 += carry10;
+  s10 -= carry10 << 21;
+  carry12 = (s12 + (1 << 20)) >> 21;
+  s13 += carry12;
+  s12 -= carry12 << 21;
+  carry14 = (s14 + (1 << 20)) >> 21;
+  s15 += carry14;
+  s14 -= carry14 << 21;
+  carry16 = (s16 + (1 << 20)) >> 21;
+  s17 += carry16;
+  s16 -= carry16 << 21;
+
+  carry7 = (s7 + (1 << 20)) >> 21;
+  s8 += carry7;
+  s7 -= carry7 << 21;
+  carry9 = (s9 + (1 << 20)) >> 21;
+  s10 += carry9;
+  s9 -= carry9 << 21;
+  carry11 = (s11 + (1 << 20)) >> 21;
+  s12 += carry11;
+  s11 -= carry11 << 21;
+  carry13 = (s13 + (1 << 20)) >> 21;
+  s14 += carry13;
+  s13 -= carry13 << 21;
+  carry15 = (s15 + (1 << 20)) >> 21;
+  s16 += carry15;
+  s15 -= carry15 << 21;
+
+  s5 += s17 * 666643;
+  s6 += s17 * 470296;
+  s7 += s17 * 654183;
+  s8 -= s17 * 997805;
+  s9 += s17 * 136657;
+  s10 -= s17 * 683901;
+  s17 = 0;
+
+  s4 += s16 * 666643;
+  s5 += s16 * 470296;
+  s6 += s16 * 654183;
+  s7 -= s16 * 997805;
+  s8 += s16 * 136657;
+  s9 -= s16 * 683901;
+  s16 = 0;
+
+  s3 += s15 * 666643;
+  s4 += s15 * 470296;
+  s5 += s15 * 654183;
+  s6 -= s15 * 997805;
+  s7 += s15 * 136657;
+  s8 -= s15 * 683901;
+  s15 = 0;
+
+  s2 += s14 * 666643;
+  s3 += s14 * 470296;
+  s4 += s14 * 654183;
+  s5 -= s14 * 997805;
+  s6 += s14 * 136657;
+  s7 -= s14 * 683901;
+  s14 = 0;
+
+  s1 += s13 * 666643;
+  s2 += s13 * 470296;
+  s3 += s13 * 654183;
+  s4 -= s13 * 997805;
+  s5 += s13 * 136657;
+  s6 -= s13 * 683901;
+  s13 = 0;
+
+  s0 += s12 * 666643;
+  s1 += s12 * 470296;
+  s2 += s12 * 654183;
+  s3 -= s12 * 997805;
+  s4 += s12 * 136657;
+  s5 -= s12 * 683901;
+  s12 = 0;
+
+  carry0 = (s0 + (1 << 20)) >> 21;
+  s1 += carry0;
+  s0 -= carry0 << 21;
+  carry2 = (s2 + (1 << 20)) >> 21;
+  s3 += carry2;
+  s2 -= carry2 << 21;
+  carry4 = (s4 + (1 << 20)) >> 21;
+  s5 += carry4;
+  s4 -= carry4 << 21;
+  carry6 = (s6 + (1 << 20)) >> 21;
+  s7 += carry6;
+  s6 -= carry6 << 21;
+  carry8 = (s8 + (1 << 20)) >> 21;
+  s9 += carry8;
+  s8 -= carry8 << 21;
+  carry10 = (s10 + (1 << 20)) >> 21;
+  s11 += carry10;
+  s10 -= carry10 << 21;
+
+  carry1 = (s1 + (1 << 20)) >> 21;
+  s2 += carry1;
+  s1 -= carry1 << 21;
+  carry3 = (s3 + (1 << 20)) >> 21;
+  s4 += carry3;
+  s3 -= carry3 << 21;
+  carry5 = (s5 + (1 << 20)) >> 21;
+  s6 += carry5;
+  s5 -= carry5 << 21;
+  carry7 = (s7 + (1 << 20)) >> 21;
+  s8 += carry7;
+  s7 -= carry7 << 21;
+  carry9 = (s9 + (1 << 20)) >> 21;
+  s10 += carry9;
+  s9 -= carry9 << 21;
+  carry11 = (s11 + (1 << 20)) >> 21;
+  s12 += carry11;
+  s11 -= carry11 << 21;
+
+  s0 += s12 * 666643;
+  s1 += s12 * 470296;
+  s2 += s12 * 654183;
+  s3 -= s12 * 997805;
+  s4 += s12 * 136657;
+  s5 -= s12 * 683901;
+  s12 = 0;
+
+  carry0 = s0 >> 21;
+  s1 += carry0;
+  s0 -= carry0 << 21;
+  carry1 = s1 >> 21;
+  s2 += carry1;
+  s1 -= carry1 << 21;
+  carry2 = s2 >> 21;
+  s3 += carry2;
+  s2 -= carry2 << 21;
+  carry3 = s3 >> 21;
+  s4 += carry3;
+  s3 -= carry3 << 21;
+  carry4 = s4 >> 21;
+  s5 += carry4;
+  s4 -= carry4 << 21;
+  carry5 = s5 >> 21;
+  s6 += carry5;
+  s5 -= carry5 << 21;
+  carry6 = s6 >> 21;
+  s7 += carry6;
+  s6 -= carry6 << 21;
+  carry7 = s7 >> 21;
+  s8 += carry7;
+  s7 -= carry7 << 21;
+  carry8 = s8 >> 21;
+  s9 += carry8;
+  s8 -= carry8 << 21;
+  carry9 = s9 >> 21;
+  s10 += carry9;
+  s9 -= carry9 << 21;
+  carry10 = s10 >> 21;
+  s11 += carry10;
+  s10 -= carry10 << 21;
+  carry11 = s11 >> 21;
+  s12 += carry11;
+  s11 -= carry11 << 21;
+
+  s0 += s12 * 666643;
+  s1 += s12 * 470296;
+  s2 += s12 * 654183;
+  s3 -= s12 * 997805;
+  s4 += s12 * 136657;
+  s5 -= s12 * 683901;
+  s12 = 0;
+
+  carry0 = s0 >> 21;
+  s1 += carry0;
+  s0 -= carry0 << 21;
+  carry1 = s1 >> 21;
+  s2 += carry1;
+  s1 -= carry1 << 21;
+  carry2 = s2 >> 21;
+  s3 += carry2;
+  s2 -= carry2 << 21;
+  carry3 = s3 >> 21;
+  s4 += carry3;
+  s3 -= carry3 << 21;
+  carry4 = s4 >> 21;
+  s5 += carry4;
+  s4 -= carry4 << 21;
+  carry5 = s5 >> 21;
+  s6 += carry5;
+  s5 -= carry5 << 21;
+  carry6 = s6 >> 21;
+  s7 += carry6;
+  s6 -= carry6 << 21;
+  carry7 = s7 >> 21;
+  s8 += carry7;
+  s7 -= carry7 << 21;
+  carry8 = s8 >> 21;
+  s9 += carry8;
+  s8 -= carry8 << 21;
+  carry9 = s9 >> 21;
+  s10 += carry9;
+  s9 -= carry9 << 21;
+  carry10 = s10 >> 21;
+  s11 += carry10;
+  s10 -= carry10 << 21;
+
+  s[0] = s0 >> 0;
+  s[1] = s0 >> 8;
+  s[2] = (s0 >> 16) | (s1 << 5);
+  s[3] = s1 >> 3;
+  s[4] = s1 >> 11;
+  s[5] = (s1 >> 19) | (s2 << 2);
+  s[6] = s2 >> 6;
+  s[7] = (s2 >> 14) | (s3 << 7);
+  s[8] = s3 >> 1;
+  s[9] = s3 >> 9;
+  s[10] = (s3 >> 17) | (s4 << 4);
+  s[11] = s4 >> 4;
+  s[12] = s4 >> 12;
+  s[13] = (s4 >> 20) | (s5 << 1);
+  s[14] = s5 >> 7;
+  s[15] = (s5 >> 15) | (s6 << 6);
+  s[16] = s6 >> 2;
+  s[17] = s6 >> 10;
+  s[18] = (s6 >> 18) | (s7 << 3);
+  s[19] = s7 >> 5;
+  s[20] = s7 >> 13;
+  s[21] = s8 >> 0;
+  s[22] = s8 >> 8;
+  s[23] = (s8 >> 16) | (s9 << 5);
+  s[24] = s9 >> 3;
+  s[25] = s9 >> 11;
+  s[26] = (s9 >> 19) | (s10 << 2);
+  s[27] = s10 >> 6;
+  s[28] = (s10 >> 14) | (s11 << 7);
+  s[29] = s11 >> 1;
+  s[30] = s11 >> 9;
+  s[31] = s11 >> 17;
+}
+
+int ED25519_sign(uint8_t *out_sig, const uint8_t *message, size_t message_len,
+                 const uint8_t public_key[32], const uint8_t private_key[32]) {
+  uint8_t az[SHA512_DIGEST_LENGTH];
+  uint8_t nonce[SHA512_DIGEST_LENGTH];
+  ge_p3 R;
+  uint8_t hram[SHA512_DIGEST_LENGTH];
+  SHA512_CTX hash_ctx;
+
+  SHA512_Init(&hash_ctx);
+  SHA512_Update(&hash_ctx, private_key, 32);
+  SHA512_Final(az, &hash_ctx);
+
+  az[0] &= 248;
+  az[31] &= 63;
+  az[31] |= 64;
+
+  SHA512_Init(&hash_ctx);
+  SHA512_Update(&hash_ctx, az + 32, 32);
+  SHA512_Update(&hash_ctx, message, message_len);
+  SHA512_Final(nonce, &hash_ctx);
+
+  x25519_sc_reduce(nonce);
+  ge_scalarmult_base(&R, nonce);
+  ge_p3_tobytes(out_sig, &R);
+
+  SHA512_Init(&hash_ctx);
+  SHA512_Update(&hash_ctx, out_sig, 32);
+  SHA512_Update(&hash_ctx, public_key, 32);
+  SHA512_Update(&hash_ctx, message, message_len);
+  SHA512_Final(hram, &hash_ctx);
+
+  x25519_sc_reduce(hram);
+  sc_muladd(out_sig + 32, hram, az, nonce);
+
+  OPENSSL_cleanse(&hash_ctx, sizeof(hash_ctx));
+  OPENSSL_cleanse(nonce, sizeof(nonce));
+  OPENSSL_cleanse(az, sizeof(az));
+
+  return 1;
+}
+
+int ED25519_verify(const uint8_t *message, size_t message_len,
+                   const uint8_t signature[64], const uint8_t public_key[32]) {
+  ge_p3 A;
+  uint8_t pkcopy[32];
+  uint8_t rcopy[32];
+  uint8_t scopy[32];
+  SHA512_CTX hash_ctx;
+  ge_p2 R;
+  uint8_t rcheck[32];
+  uint8_t h[SHA512_DIGEST_LENGTH];
+
+  if ((signature[63] & 224) != 0 ||
+      ge_frombytes_vartime(&A, public_key) != 0) {
+    return 0;
+  }
+
+  fe_neg(A.X, A.X);
+  fe_neg(A.T, A.T);
+
+  memcpy(pkcopy, public_key, 32);
+  memcpy(rcopy, signature, 32);
+  memcpy(scopy, signature + 32, 32);
+
+  SHA512_Init(&hash_ctx);
+  SHA512_Update(&hash_ctx, signature, 32);
+  SHA512_Update(&hash_ctx, public_key, 32);
+  SHA512_Update(&hash_ctx, message, message_len);
+  SHA512_Final(h, &hash_ctx);
+
+  x25519_sc_reduce(h);
+
+  ge_double_scalarmult_vartime(&R, h, &A, scopy);
+
+  ge_tobytes(rcheck, &R);
+
+  return CRYPTO_memcmp(rcheck, rcopy, sizeof(rcheck)) == 0;
+}
+
+void ED25519_public_from_private(uint8_t out_public_key[32],
+                                 const uint8_t private_key[32]) {
+  uint8_t az[SHA512_DIGEST_LENGTH];
+  ge_p3 A;
+
+  SHA512(private_key, 32, az);
+
+  az[0] &= 248;
+  az[31] &= 63;
+  az[31] |= 64;
+
+  ge_scalarmult_base(&A, az);
+  ge_p3_tobytes(out_public_key, &A);
+}
+
 int X25519(uint8_t out_shared_key[32], const uint8_t private_key[32],
            const uint8_t peer_public_value[32]) {
   static const uint8_t kZeros[32] = {0};
diff --git a/crypto/ec/ec_err.c b/crypto/ec/ec_err.c
index e4c2c1c..981ed20 100644
--- a/crypto/ec/ec_err.c
+++ b/crypto/ec/ec_err.c
@@ -1,6 +1,6 @@
 /*
  * Generated by util/mkerr.pl DO NOT EDIT
- * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved.
  *
  * Licensed under the OpenSSL license (the "License").  You may not use
  * this file except in compliance with the License.  You can obtain a copy
@@ -34,6 +34,7 @@ static ERR_STRING_DATA EC_str_functs[] = {
     {ERR_FUNC(EC_F_ECDSA_SIGN_SETUP), "ECDSA_sign_setup"},
     {ERR_FUNC(EC_F_ECDSA_SIG_NEW), "ECDSA_SIG_new"},
     {ERR_FUNC(EC_F_ECDSA_VERIFY), "ECDSA_verify"},
+    {ERR_FUNC(EC_F_ECD_ITEM_VERIFY), "ecd_item_verify"},
     {ERR_FUNC(EC_F_ECKEY_PARAM2TYPE), "eckey_param2type"},
     {ERR_FUNC(EC_F_ECKEY_PARAM_DECODE), "eckey_param_decode"},
     {ERR_FUNC(EC_F_ECKEY_PRIV_DECODE), "eckey_priv_decode"},
@@ -194,6 +195,8 @@ static ERR_STRING_DATA EC_str_functs[] = {
     {ERR_FUNC(EC_F_OSSL_ECDH_COMPUTE_KEY), "ossl_ecdh_compute_key"},
     {ERR_FUNC(EC_F_OSSL_ECDSA_SIGN_SIG), "ossl_ecdsa_sign_sig"},
     {ERR_FUNC(EC_F_OSSL_ECDSA_VERIFY_SIG), "ossl_ecdsa_verify_sig"},
+    {ERR_FUNC(EC_F_PKEY_ECD_CTRL), "pkey_ecd_ctrl"},
+    {ERR_FUNC(EC_F_PKEY_ECD_DIGESTSIGN), "pkey_ecd_digestsign"},
     {ERR_FUNC(EC_F_PKEY_ECX_DERIVE), "pkey_ecx_derive"},
     {ERR_FUNC(EC_F_PKEY_EC_CTRL), "pkey_ec_ctrl"},
     {ERR_FUNC(EC_F_PKEY_EC_CTRL_STR), "pkey_ec_ctrl_str"},
diff --git a/crypto/ec/ec_lcl.h b/crypto/ec/ec_lcl.h
index 7d72a52..10c0ac1 100644
--- a/crypto/ec/ec_lcl.h
+++ b/crypto/ec/ec_lcl.h
@@ -608,6 +608,13 @@ int ossl_ecdsa_verify(int type, const unsigned char *dgst, int dgst_len,
 int ossl_ecdsa_verify_sig(const unsigned char *dgst, int dgst_len,
                           const ECDSA_SIG *sig, EC_KEY *eckey);
 
+int ED25519_sign(uint8_t *out_sig, const uint8_t *message, size_t message_len,
+                 const uint8_t public_key[32], const uint8_t private_key[32]);
+int ED25519_verify(const uint8_t *message, size_t message_len,
+                   const uint8_t signature[64], const uint8_t public_key[32]);
+void ED25519_public_from_private(uint8_t out_public_key[32],
+                                 const uint8_t private_key[32]);
+
 int X25519(uint8_t out_shared_key[32], const uint8_t private_key[32],
            const uint8_t peer_public_value[32]);
 void X25519_public_from_private(uint8_t out_public_value[32],
diff --git a/crypto/ec/ecx_meth.c b/crypto/ec/ecx_meth.c
index 06e3911..715fe06 100644
--- a/crypto/ec/ecx_meth.c
+++ b/crypto/ec/ecx_meth.c
@@ -20,6 +20,8 @@
 #define X25519_BITS          253
 #define X25519_SECURITY_BITS 128
 
+#define ED25519_SIGSIZE      64
+
 typedef struct {
     unsigned char pubkey[X25519_KEYLEN];
     unsigned char *privkey;
@@ -32,7 +34,7 @@ typedef enum {
 } ecx_key_op_t;
 
 /* Setup EVP_PKEY using public, private or generation */
-static int ecx_key_op(EVP_PKEY *pkey, const X509_ALGOR *palg,
+static int ecx_key_op(EVP_PKEY *pkey, int id, const X509_ALGOR *palg,
                       const unsigned char *p, int plen, ecx_key_op_t op)
 {
     X25519_KEY *xkey;
@@ -76,16 +78,21 @@ static int ecx_key_op(EVP_PKEY *pkey, const X509_ALGOR *palg,
                 OPENSSL_free(xkey);
                 return 0;
             }
-            xkey->privkey[0] &= 248;
-            xkey->privkey[31] &= 127;
-            xkey->privkey[31] |= 64;
+            if (id == NID_X25519) {
+                xkey->privkey[0] &= 248;
+                xkey->privkey[31] &= 127;
+                xkey->privkey[31] |= 64;
+            }
         } else {
             memcpy(xkey->privkey, p, X25519_KEYLEN);
         }
-        X25519_public_from_private(xkey->pubkey, xkey->privkey);
+        if (id == NID_X25519)
+            X25519_public_from_private(xkey->pubkey, xkey->privkey);
+        else
+            ED25519_public_from_private(xkey->pubkey, xkey->privkey);
     }
 
-    EVP_PKEY_assign(pkey, NID_X25519, xkey);
+    EVP_PKEY_assign(pkey, id, xkey);
     return 1;
 }
 
@@ -105,8 +112,8 @@ static int ecx_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey)
         return 0;
     }
 
-    if (!X509_PUBKEY_set0_param(pk, OBJ_nid2obj(NID_X25519), V_ASN1_UNDEF,
-                                NULL, penc, X25519_KEYLEN)) {
+    if (!X509_PUBKEY_set0_param(pk, OBJ_nid2obj(pkey->ameth->pkey_id),
+                                V_ASN1_UNDEF, NULL, penc, X25519_KEYLEN)) {
         OPENSSL_free(penc);
         ECerr(EC_F_ECX_PUB_ENCODE, ERR_R_MALLOC_FAILURE);
         return 0;
@@ -122,7 +129,8 @@ static int ecx_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey)
 
     if (!X509_PUBKEY_get0_param(NULL, &p, &pklen, &palg, pubkey))
         return 0;
-    return ecx_key_op(pkey, palg, p, pklen, X25519_PUBLIC);
+    return ecx_key_op(pkey, pkey->ameth->pkey_id, palg, p, pklen,
+                      X25519_PUBLIC);
 }
 
 static int ecx_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
@@ -155,7 +163,7 @@ static int ecx_priv_decode(EVP_PKEY *pkey, const PKCS8_PRIV_KEY_INFO *p8)
         plen = ASN1_STRING_length(oct);
     }
 
-    rv = ecx_key_op(pkey, palg, p, plen, X25519_PRIVATE);
+    rv = ecx_key_op(pkey, pkey->ameth->pkey_id, palg, p, plen, X25519_PRIVATE);
     ASN1_OCTET_STRING_free(oct);
     return rv;
 }
@@ -182,7 +190,7 @@ static int ecx_priv_encode(PKCS8_PRIV_KEY_INFO *p8, const EVP_PKEY *pkey)
         return 0;
     }
 
-    if (!PKCS8_pkey_set0(p8, OBJ_nid2obj(NID_X25519), 0,
+    if (!PKCS8_pkey_set0(p8, OBJ_nid2obj(pkey->ameth->pkey_id), 0,
                          V_ASN1_UNDEF, NULL, penc, penclen)) {
         OPENSSL_clear_free(penc, penclen);
         ECerr(EC_F_ECX_PRIV_ENCODE, ERR_R_MALLOC_FAILURE);
@@ -227,13 +235,15 @@ static int ecx_key_print(BIO *bp, const EVP_PKEY *pkey, int indent,
 {
     const X25519_KEY *xkey = pkey->pkey.ptr;
 
+    const char *nm = OBJ_nid2ln(pkey->ameth->pkey_id);
+
     if (op == X25519_PRIVATE) {
         if (xkey == NULL || xkey->privkey == NULL) {
             if (BIO_printf(bp, "%*s<INVALID PRIVATE KEY>\n", indent, "") <= 0)
                 return 0;
             return 1;
         }
-        if (BIO_printf(bp, "%*sX25519 Private-Key:\n", indent, "") <= 0)
+        if (BIO_printf(bp, "%*s%s Private-Key:\n", indent, "", nm) <= 0)
             return 0;
         if (BIO_printf(bp, "%*spriv:\n", indent, "") <= 0)
             return 0;
@@ -245,7 +255,7 @@ static int ecx_key_print(BIO *bp, const EVP_PKEY *pkey, int indent,
                 return 0;
             return 1;
         }
-        if (BIO_printf(bp, "%*sX25519 Public-Key:\n", indent, "") <= 0)
+        if (BIO_printf(bp, "%*s%s Public-Key:\n", indent, "", nm) <= 0)
             return 0;
     }
     if (BIO_printf(bp, "%*spub:\n", indent, "") <= 0)
@@ -272,7 +282,7 @@ static int ecx_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2)
     switch (op) {
 
     case ASN1_PKEY_CTRL_SET1_TLS_ENCPT:
-        return ecx_key_op(pkey, NULL, arg2, arg1, X25519_PUBLIC);
+        return ecx_key_op(pkey, NID_X25519, NULL, arg2, arg1, X25519_PUBLIC);
 
     case ASN1_PKEY_CTRL_GET1_TLS_ENCPT:
         if (pkey->pkey.ptr != NULL) {
@@ -324,9 +334,87 @@ const EVP_PKEY_ASN1_METHOD ecx25519_asn1_meth = {
     NULL
 };
 
+static int ecd_size(const EVP_PKEY *pkey)
+{
+    return ED25519_SIGSIZE;
+}
+
+static int ecd_item_verify(EVP_MD_CTX *ctx, const ASN1_ITEM *it, void *asn,
+                           X509_ALGOR *sigalg, ASN1_BIT_STRING *str,
+                           EVP_PKEY *pkey)
+{
+    const ASN1_OBJECT *obj;
+    int ptype;
+
+    X509_ALGOR_get0(&obj, &ptype, NULL, sigalg);
+    /* Sanity check: make sure it is ED25519 with absent parameters */
+    if (OBJ_obj2nid(obj) != NID_ED25519 || ptype != V_ASN1_UNDEF) {
+        ECerr(EC_F_ECD_ITEM_VERIFY, EC_R_INVALID_ENCODING);
+        return 0;
+    }
+
+    if (!EVP_DigestVerifyInit(ctx, NULL, NULL, NULL, pkey))
+        return 0;
+
+    return 2;
+}
+
+static int ecd_item_sign(EVP_MD_CTX *ctx, const ASN1_ITEM *it, void *asn,
+                         X509_ALGOR *alg1, X509_ALGOR *alg2,
+                         ASN1_BIT_STRING *str)
+{
+    /* Set algorithms identifiers */
+    X509_ALGOR_set0(alg1, OBJ_nid2obj(NID_ED25519), V_ASN1_UNDEF, NULL);
+    if (alg2)
+        X509_ALGOR_set0(alg2, OBJ_nid2obj(NID_ED25519), V_ASN1_UNDEF, NULL);
+    /* Algorithm idetifiers set: carry on as normal */
+    return 3;
+}
+
+static int ecd_sig_info_set(X509_SIG_INFO *siginf, const X509_ALGOR *alg,
+                            const ASN1_STRING *sig)
+{
+    X509_SIG_INFO_set(siginf, NID_undef, NID_ED25519, X25519_SECURITY_BITS,
+                      X509_SIG_INFO_TLS);
+    return 1;
+}
+
+const EVP_PKEY_ASN1_METHOD ed25519_asn1_meth = {
+    NID_ED25519,
+    NID_ED25519,
+    0,
+    "ED25519",
+    "OpenSSL ED25519 algorithm",
+
+    ecx_pub_decode,
+    ecx_pub_encode,
+    ecx_pub_cmp,
+    ecx_pub_print,
+
+    ecx_priv_decode,
+    ecx_priv_encode,
+    ecx_priv_print,
+
+    ecd_size,
+    ecx_bits,
+    ecx_security_bits,
+
+    0, 0, 0, 0,
+    ecx_cmp_parameters,
+    0, 0,
+
+    ecx_free,
+    0,
+    NULL,
+    NULL,
+    ecd_item_verify,
+    ecd_item_sign,
+    ecd_sig_info_set
+};
+
 static int pkey_ecx_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
 {
-    return ecx_key_op(pkey, NULL, NULL, 0, X25519_KEYGEN);
+    return ecx_key_op(pkey, ctx->pmeth->pkey_id, NULL, NULL, 0, X25519_KEYGEN);
 }
 
 static int pkey_ecx_derive(EVP_PKEY_CTX *ctx, unsigned char *key,
@@ -371,3 +459,63 @@ const EVP_PKEY_METHOD ecx25519_pkey_meth = {
     pkey_ecx_ctrl,
     0
 };
+
+static int pkey_ecd_digestsign(EVP_MD_CTX *ctx, unsigned char *sig,
+                               size_t *siglen, const unsigned char *tbs,
+                               size_t tbslen)
+{
+    const X25519_KEY *edkey = EVP_MD_CTX_pkey_ctx(ctx)->pkey->pkey.ptr;
+
+    if (sig == NULL) {
+        *siglen = ED25519_SIGSIZE;
+        return 1;
+    }
+    if (*siglen < ED25519_SIGSIZE) {
+        ECerr(EC_F_PKEY_ECD_DIGESTSIGN, EC_R_BUFFER_TOO_SMALL);
+        return 0;
+    }
+
+    if (ED25519_sign(sig, tbs, tbslen, edkey->pubkey, edkey->privkey) == 0)
+        return 0;
+    *siglen = ED25519_SIGSIZE;
+    return 1;
+}
+
+static int pkey_ecd_digestverify(EVP_MD_CTX *ctx, const unsigned char *sig,
+                                 size_t siglen, const unsigned char *tbs,
+                                 size_t tbslen)
+{
+    const X25519_KEY *edkey = EVP_MD_CTX_pkey_ctx(ctx)->pkey->pkey.ptr;
+
+    if (siglen != ED25519_SIGSIZE)
+        return 0;
+
+    return ED25519_verify(tbs, tbslen, sig, edkey->pubkey);
+}
+
+static int pkey_ecd_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
+{
+    switch (type) {
+    case EVP_PKEY_CTRL_MD:
+        /* Only NULL allowed as digest */
+        if (p2 == NULL)
+            return 1;
+        ECerr(EC_F_PKEY_ECD_CTRL, EC_R_INVALID_DIGEST_TYPE);
+        return 0;
+
+    case EVP_PKEY_CTRL_DIGESTINIT:
+        return 1;
+    }
+    return -2;
+}
+
+const EVP_PKEY_METHOD ed25519_pkey_meth = {
+    NID_ED25519, EVP_PKEY_FLAG_SIGCTX_CUSTOM,
+    0, 0, 0, 0, 0, 0,
+    pkey_ecx_keygen,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    pkey_ecd_ctrl,
+    0,
+    pkey_ecd_digestsign,
+    pkey_ecd_digestverify
+};
diff --git a/crypto/evp/evp_err.c b/crypto/evp/evp_err.c
index eb73a5a..315129a 100644
--- a/crypto/evp/evp_err.c
+++ b/crypto/evp/evp_err.c
@@ -93,6 +93,7 @@ static ERR_STRING_DATA EVP_str_functs[] = {
     {ERR_FUNC(EVP_F_PKEY_SET_TYPE), "pkey_set_type"},
     {ERR_FUNC(EVP_F_RC2_MAGIC_TO_METH), "rc2_magic_to_meth"},
     {ERR_FUNC(EVP_F_RC5_CTRL), "rc5_ctrl"},
+    {ERR_FUNC(EVP_F_UPDATE), "update"},
     {0, NULL}
 };
 
@@ -143,6 +144,7 @@ static ERR_STRING_DATA EVP_str_reasons[] = {
     {ERR_REASON(EVP_R_NO_DIGEST_SET), "no digest set"},
     {ERR_REASON(EVP_R_NO_KEY_SET), "no key set"},
     {ERR_REASON(EVP_R_NO_OPERATION_SET), "no operation set"},
+    {ERR_REASON(EVP_R_ONLY_ONESHOT_SUPPORTED), "only oneshot supported"},
     {ERR_REASON(EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE),
      "operation not supported for this keytype"},
     {ERR_REASON(EVP_R_OPERATON_NOT_INITIALIZED), "operaton not initialized"},
diff --git a/crypto/evp/m_sigver.c b/crypto/evp/m_sigver.c
index d53e1d6..be6bb21 100644
--- a/crypto/evp/m_sigver.c
+++ b/crypto/evp/m_sigver.c
@@ -15,6 +15,12 @@
 #include "internal/evp_int.h"
 #include "evp_locl.h"
 
+static int update(EVP_MD_CTX *ctx, const void *data, size_t datalen)
+{
+    EVPerr(EVP_F_UPDATE, EVP_R_ONLY_ONESHOT_SUPPORTED);
+    return 0;
+}
+
 static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
                           const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey,
                           int ver)
@@ -43,15 +49,23 @@ static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
             if (ctx->pctx->pmeth->verifyctx_init(ctx->pctx, ctx) <= 0)
                 return 0;
             ctx->pctx->operation = EVP_PKEY_OP_VERIFYCTX;
-        } else if (EVP_PKEY_verify_init(ctx->pctx) <= 0)
+        } else if (ctx->pctx->pmeth->digestverify != 0) {
+            ctx->pctx->operation = EVP_PKEY_OP_VERIFY;
+            ctx->update = update;
+        } else if (EVP_PKEY_verify_init(ctx->pctx) <= 0) {
             return 0;
+        }
     } else {
         if (ctx->pctx->pmeth->signctx_init) {
             if (ctx->pctx->pmeth->signctx_init(ctx->pctx, ctx) <= 0)
                 return 0;
             ctx->pctx->operation = EVP_PKEY_OP_SIGNCTX;
-        } else if (EVP_PKEY_sign_init(ctx->pctx) <= 0)
+        } else if (ctx->pctx->pmeth->digestsign != 0) {
+            ctx->pctx->operation = EVP_PKEY_OP_SIGN;
+            ctx->update = update;
+        } else if (EVP_PKEY_sign_init(ctx->pctx) <= 0) {
             return 0;
+        }
     }
     if (EVP_PKEY_CTX_set_signature_md(ctx->pctx, type) <= 0)
         return 0;
@@ -138,6 +152,8 @@ int EVP_DigestSignFinal(EVP_MD_CTX *ctx, unsigned char *sigret,
 int EVP_DigestSign(EVP_MD_CTX *ctx, unsigned char *sigret, size_t *siglen,
                    const unsigned char *tbs, size_t tbslen)
 {
+    if (ctx->pctx->pmeth->digestsign != NULL)
+        return ctx->pctx->pmeth->digestsign(ctx, sigret, siglen, tbs, tbslen);
     if (sigret != NULL && EVP_DigestSignUpdate(ctx, tbs, tbslen) <= 0)
         return 0;
     return EVP_DigestSignFinal(ctx, sigret, siglen);
@@ -179,6 +195,8 @@ int EVP_DigestVerifyFinal(EVP_MD_CTX *ctx, const unsigned char *sig,
 int EVP_DigestVerify(EVP_MD_CTX *ctx, const unsigned char *sigret,
                      size_t siglen, const unsigned char *tbs, size_t tbslen)
 {
+    if (ctx->pctx->pmeth->digestverify != NULL)
+        return ctx->pctx->pmeth->digestverify(ctx, sigret, siglen, tbs, tbslen);
     if (EVP_DigestVerifyUpdate(ctx, tbs, tbslen) <= 0)
         return -1;
     return EVP_DigestVerifyFinal(ctx, sigret, siglen);
diff --git a/crypto/evp/pmeth_lib.c b/crypto/evp/pmeth_lib.c
index a204901..fd83570 100644
--- a/crypto/evp/pmeth_lib.c
+++ b/crypto/evp/pmeth_lib.c
@@ -55,6 +55,9 @@ static const EVP_PKEY_METHOD *standard_methods[] = {
 #ifndef OPENSSL_NO_SIPHASH
     &siphash_pkey_meth,
 #endif
+#ifndef OPENSSL_NO_EC
+    &ed25519_pkey_meth,
+#endif
 };
 
 DECLARE_OBJ_BSEARCH_CMP_FN(const EVP_PKEY_METHOD *, const EVP_PKEY_METHOD *,
diff --git a/crypto/include/internal/asn1_int.h b/crypto/include/internal/asn1_int.h
index 6e6e028..a2e2b17 100644
--- a/crypto/include/internal/asn1_int.h
+++ b/crypto/include/internal/asn1_int.h
@@ -64,6 +64,7 @@ extern const EVP_PKEY_ASN1_METHOD dhx_asn1_meth;
 extern const EVP_PKEY_ASN1_METHOD dsa_asn1_meths[5];
 extern const EVP_PKEY_ASN1_METHOD eckey_asn1_meth;
 extern const EVP_PKEY_ASN1_METHOD ecx25519_asn1_meth;
+extern const EVP_PKEY_ASN1_METHOD ed25519_asn1_meth;
 extern const EVP_PKEY_ASN1_METHOD poly1305_asn1_meth;
 
 extern const EVP_PKEY_ASN1_METHOD hmac_asn1_meth;
diff --git a/crypto/include/internal/evp_int.h b/crypto/include/internal/evp_int.h
index 0b0d878..635e7b5 100644
--- a/crypto/include/internal/evp_int.h
+++ b/crypto/include/internal/evp_int.h
@@ -70,6 +70,11 @@ struct evp_pkey_method_st {
     int (*derive) (EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen);
     int (*ctrl) (EVP_PKEY_CTX *ctx, int type, int p1, void *p2);
     int (*ctrl_str) (EVP_PKEY_CTX *ctx, const char *type, const char *value);
+    int (*digestsign) (EVP_MD_CTX *ctx, unsigned char *sig, size_t *siglen,
+                       const unsigned char *tbs, size_t tbslen);
+    int (*digestverify) (EVP_MD_CTX *ctx, const unsigned char *sig,
+                         size_t siglen, const unsigned char *tbs,
+                         size_t tbslen);
 } /* EVP_PKEY_METHOD */ ;
 
 DEFINE_STACK_OF_CONST(EVP_PKEY_METHOD)
@@ -82,6 +87,7 @@ extern const EVP_PKEY_METHOD dhx_pkey_meth;
 extern const EVP_PKEY_METHOD dsa_pkey_meth;
 extern const EVP_PKEY_METHOD ec_pkey_meth;
 extern const EVP_PKEY_METHOD ecx25519_pkey_meth;
+extern const EVP_PKEY_METHOD ed25519_pkey_meth;
 extern const EVP_PKEY_METHOD hmac_pkey_meth;
 extern const EVP_PKEY_METHOD rsa_pkey_meth;
 extern const EVP_PKEY_METHOD rsa_pss_pkey_meth;
diff --git a/crypto/objects/obj_dat.h b/crypto/objects/obj_dat.h
index dd8d94f..d6742cb 100644
--- a/crypto/objects/obj_dat.h
+++ b/crypto/objects/obj_dat.h
@@ -10,7 +10,7 @@
  */
 
 /* Serialized OID's */
-static const unsigned char so[6911] = {
+static const unsigned char so[6917] = {
     0x2A,0x86,0x48,0x86,0xF7,0x0D,                 /* [    0] OBJ_rsadsi */
     0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,            /* [    6] OBJ_pkcs */
     0x2A,0x86,0x48,0x86,0xF7,0x0D,0x02,0x02,       /* [   13] OBJ_md2 */
@@ -977,9 +977,11 @@ static const unsigned char so[6911] = {
     0x2A,0x83,0x1A,0x8C,0x9A,0x6E,0x01,0x01,0x0E,  /* [ 6881] OBJ_aria_256_ofb128 */
     0x2A,0x83,0x1A,0x8C,0x9A,0x6E,0x01,0x01,0x0F,  /* [ 6890] OBJ_aria_256_ctr */
     0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x02,0x2F,  /* [ 6899] OBJ_id_smime_aa_signingCertificateV2 */
+    0x2B,0x65,0x70,                                /* [ 6910] OBJ_ED25519 */
+    0x2B,0x65,0x71,                                /* [ 6913] OBJ_ED448 */
 };
 
-#define NUM_NID 1087
+#define NUM_NID 1089
 static const ASN1_OBJECT nid_objs[NUM_NID] = {
     {"UNDEF", "undefined", NID_undef},
     {"rsadsi", "RSA Data Security, Inc.", NID_rsadsi, 6, &so[0]},
@@ -2068,9 +2070,11 @@ static const ASN1_OBJECT nid_objs[NUM_NID] = {
     {"ARIA-192-CFB8", "aria-192-cfb8", NID_aria_192_cfb8},
     {"ARIA-256-CFB8", "aria-256-cfb8", NID_aria_256_cfb8},
     {"id-smime-aa-signingCertificateV2", "id-smime-aa-signingCertificateV2", NID_id_smime_aa_signingCertificateV2, 11, &so[6899]},
+    {"ED25519", "ED25519", NID_ED25519, 3, &so[6910]},
+    {"ED448", "ED448", NID_ED448, 3, &so[6913]},
 };
 
-#define NUM_SN 1078
+#define NUM_SN 1080
 static const unsigned int sn_objs[NUM_SN] = {
      364,    /* "AD_DVCS" */
      419,    /* "AES-128-CBC" */
@@ -2209,6 +2213,8 @@ static const unsigned int sn_objs[NUM_SN] = {
       70,    /* "DSA-SHA1-old" */
       67,    /* "DSA-old" */
      297,    /* "DVCS" */
+    1087,    /* "ED25519" */
+    1088,    /* "ED448" */
       99,    /* "GN" */
     1036,    /* "HKDF" */
      855,    /* "HMAC" */
@@ -3152,7 +3158,7 @@ static const unsigned int sn_objs[NUM_SN] = {
      160,    /* "x509Crl" */
 };
 
-#define NUM_LN 1078
+#define NUM_LN 1080
 static const unsigned int ln_objs[NUM_LN] = {
      363,    /* "AD Time Stamping" */
      405,    /* "ANSI X9.62" */
@@ -3175,6 +3181,8 @@ static const unsigned int ln_objs[NUM_LN] = {
      382,    /* "Directory" */
      392,    /* "Domain" */
      132,    /* "E-mail Protection" */
+    1087,    /* "ED25519" */
+    1088,    /* "ED448" */
      389,    /* "Enterprises" */
      384,    /* "Experimental" */
      372,    /* "Extended OCSP Status" */
@@ -4234,7 +4242,7 @@ static const unsigned int ln_objs[NUM_LN] = {
      125,    /* "zlib compression" */
 };
 
-#define NUM_OBJ 972
+#define NUM_OBJ 974
 static const unsigned int obj_objs[NUM_OBJ] = {
        0,    /* OBJ_undef                        0 */
      181,    /* OBJ_iso                          1 */
@@ -4259,6 +4267,8 @@ static const unsigned int obj_objs[NUM_OBJ] = {
      381,    /* OBJ_iana                         1 3 6 1 */
     1034,    /* OBJ_X25519                       1 3 101 110 */
     1035,    /* OBJ_X448                         1 3 101 111 */
+    1087,    /* OBJ_ED25519                      1 3 101 112 */
+    1088,    /* OBJ_ED448                        1 3 101 113 */
      677,    /* OBJ_certicom_arc                 1 3 132 */
      394,    /* OBJ_selected_attribute_types     2 5 1 5 */
       13,    /* OBJ_commonName                   2 5 4 3 */
diff --git a/crypto/objects/obj_mac.num b/crypto/objects/obj_mac.num
index ca8dcdb..8edcefd 100644
--- a/crypto/objects/obj_mac.num
+++ b/crypto/objects/obj_mac.num
@@ -1084,3 +1084,5 @@ aria_128_cfb8		1083
 aria_192_cfb8		1084
 aria_256_cfb8		1085
 id_smime_aa_signingCertificateV2		1086
+ED25519		1087
+ED448		1088
diff --git a/crypto/objects/obj_xref.h b/crypto/objects/obj_xref.h
index d09aa71..075907b 100644
--- a/crypto/objects/obj_xref.h
+++ b/crypto/objects/obj_xref.h
@@ -73,6 +73,7 @@ static const nid_triple sigoid_srt[] = {
      NID_id_GostR3410_2012_256},
     {NID_id_tc26_signwithdigest_gost3410_2012_512, NID_id_GostR3411_2012_512,
      NID_id_GostR3410_2012_512},
+    {NID_ED25519, NID_undef, NID_ED25519},
 };
 
 static const nid_triple *const sigoid_srt_xref[] = {
diff --git a/crypto/objects/obj_xref.txt b/crypto/objects/obj_xref.txt
index 981103b..c36695c 100644
--- a/crypto/objects/obj_xref.txt
+++ b/crypto/objects/obj_xref.txt
@@ -17,6 +17,7 @@ ripemd160WithRSA	ripemd160 rsaEncryption
 # AlgorithmIdentifier. The digest "undef" indicates the public key
 # method should handle this explicitly.
 rsassaPss		undef	rsaEncryption
+ED25519		    undef	ED25519
 
 # Alternative deprecated OIDs. By using the older "rsa" OID this
 # type will be recognized by not normally used.
diff --git a/crypto/objects/objects.txt b/crypto/objects/objects.txt
index ceb03a3..324b63a 100644
--- a/crypto/objects/objects.txt
+++ b/crypto/objects/objects.txt
@@ -1494,9 +1494,12 @@ secg-scheme 14 3 : dhSinglePass-cofactorDH-sha512kdf-scheme
 id-pkinit 4                     : pkInitClientAuth      : PKINIT Client Auth
 id-pkinit 5                     : pkInitKDC             : Signing KDC Response
 
-# New curves from draft-ietf-curdle-pkix-00
+# New algorithms from draft-ietf-curdle-pkix-04
 1 3 101 110 : X25519
 1 3 101 111 : X448
+1 3 101 112 : ED25519
+1 3 101 113 : ED448
+
 
 # NIDs for cipher key exchange
                             : KxRSA        : kx-rsa
diff --git a/doc/man3/EVP_DigestSignInit.pod b/doc/man3/EVP_DigestSignInit.pod
index 45f337b..4990eee 100644
--- a/doc/man3/EVP_DigestSignInit.pod
+++ b/doc/man3/EVP_DigestSignInit.pod
@@ -59,9 +59,10 @@ The B<EVP> interface to digital signatures should almost always be used in
 preference to the low level interfaces. This is because the code then becomes
 transparent to the algorithm used and much more flexible.
 
-EVP_DigestSign() is a single part operation which signs a single block of data
-in one function. It is equivalent to calling EVP_DigestSignUpdate() and
-EVP_DigestSignFinal().
+EVP_DigestSign() is a one shot operation which signs a single block of data
+in one function. For algorithms that support streaming it is equivalent to
+calling EVP_DigestSignUpdate() and EVP_DigestSignFinal(). For algorithms which
+do not support streaming (e.g. PureEdDSA) it is the only way to sign data.
 
 In previous versions of OpenSSL there was a link between message digest types
 and public key algorithms. This meant that "clone" digests such as EVP_dss1()
diff --git a/doc/man3/EVP_DigestVerifyInit.pod b/doc/man3/EVP_DigestVerifyInit.pod
index 0132c06..c48b559 100644
--- a/doc/man3/EVP_DigestVerifyInit.pod
+++ b/doc/man3/EVP_DigestVerifyInit.pod
@@ -57,9 +57,11 @@ The B<EVP> interface to digital signatures should almost always be used in
 preference to the low level interfaces. This is because the code then becomes
 transparent to the algorithm used and much more flexible.
 
-EVP_DigesVerify() is a single part operation which verifies a single block of
-data in one function. It is equivalent to calling EVP_DigestVerifyUpdate() and
-EVP_DigestVerifyFinal().
+EVP_DigesVerify() is a one shot operation which verifies a single block of
+data in one function. For algorithms that support streaming it is equivalent
+to calling EVP_DigestVerifyUpdate() and EVP_DigestVerifyFinal(). For
+algorithms which do not support streaming (e.g. PureEdDSA) it is the only way
+to verify data.
 
 In previous versions of OpenSSL there was a link between message digest types
 and public key algorithms. This meant that "clone" digests such as EVP_dss1()
diff --git a/doc/man7/Ed25519.pod b/doc/man7/Ed25519.pod
new file mode 100644
index 0000000..39a1f19
--- /dev/null
+++ b/doc/man7/Ed25519.pod
@@ -0,0 +1,67 @@
+=pod
+
+=head1 NAME
+
+Ed25519 - EVP_PKEY Ed25519 support
+
+=head1 DESCRIPTION
+
+The B<Ed25519> EVP_PKEY implementation supports key generation, one shot
+digest sign and digest verify using PureEdDSA and B<Ed25519> (see RFC8032).
+It has associated private and public key formats compatible with
+draft-ietf-curdle-pkix-04.
+
+No additional parameters can be set during key generation one shot signing or
+verification. In particular, because PureEdDSA is used, when signing or
+verifying a digest must B<NOT> be specified.
+
+=head1 NOTES
+
+The PureEdDSA algorithm does not support the the streaming mechanism
+of other signature algorithms using, for example, EVP_DigestUpdate().
+The message to sign or verify must be passed using the one shot
+EVP_DigestSign() asn EVP_DigestVerify() functions.
+
+When calling EVP_DigestSignInit() or EVP_DigestSignUpdate() the
+digest parameter B<MUST> be set to B<NULL>.
+
+Applications wishing to sign certificates (or other structures such as
+CRLs or certificate requests) using Ed25519 can either use X509_sign()
+or X509_sign_ctx() in the usual way.
+
+A context for the B<Ed25519> algorithm can be obtained by calling:
+
+ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(NID_ED25519, NULL);
+
+=head1 EXAMPLE
+
+This example generates an B<ED25519> private key and writes it to standard
+output in PEM format:
+
+ #include <openssl/evp.h>
+ #include <openssl/pem.h>
+ ...
+ EVP_PKEY *pkey = NULL;
+ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(NID_ED25519, NULL);
+ EVP_PKEY_keygen_init(pctx);
+ EVP_PKEY_keygen(pctx, &pkey);
+ EVP_PKEY_CTX_free(pctx);
+ PEM_write_PrivateKey(stdout, pkey, NULL, NULL, 0, NULL, NULL);
+
+=head1 SEE ALSO
+
+L<EVP_PKEY_CTX_new(3)>,
+L<EVP_PKEY_keygen(3)>,
+L<EVP_DigestSignInit(3)>,
+L<EVP_DigestVerifyInit(3)>,
+
+=head1 COPYRIGHT
+
+Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the OpenSSL license (the "License").  You may not use
+this file except in compliance with the License.  You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/include/openssl/ec.h b/include/openssl/ec.h
index 0562dd3..205afff 100644
--- a/include/openssl/ec.h
+++ b/include/openssl/ec.h
@@ -1384,6 +1384,7 @@ int ERR_load_EC_strings(void);
 # define EC_F_ECDSA_SIGN_SETUP                            248
 # define EC_F_ECDSA_SIG_NEW                               265
 # define EC_F_ECDSA_VERIFY                                253
+# define EC_F_ECD_ITEM_VERIFY                             272
 # define EC_F_ECKEY_PARAM2TYPE                            223
 # define EC_F_ECKEY_PARAM_DECODE                          212
 # define EC_F_ECKEY_PRIV_DECODE                           213
@@ -1507,6 +1508,8 @@ int ERR_load_EC_strings(void);
 # define EC_F_OSSL_ECDH_COMPUTE_KEY                       247
 # define EC_F_OSSL_ECDSA_SIGN_SIG                         249
 # define EC_F_OSSL_ECDSA_VERIFY_SIG                       250
+# define EC_F_PKEY_ECD_CTRL                               270
+# define EC_F_PKEY_ECD_DIGESTSIGN                         271
 # define EC_F_PKEY_ECX_DERIVE                             269
 # define EC_F_PKEY_EC_CTRL                                197
 # define EC_F_PKEY_EC_CTRL_STR                            198
diff --git a/include/openssl/evp.h b/include/openssl/evp.h
index 26a8b7c..0105dd0 100644
--- a/include/openssl/evp.h
+++ b/include/openssl/evp.h
@@ -1593,6 +1593,7 @@ int ERR_load_EVP_strings(void);
 # define EVP_F_PKEY_SET_TYPE                              158
 # define EVP_F_RC2_MAGIC_TO_METH                          109
 # define EVP_F_RC5_CTRL                                   125
+# define EVP_F_UPDATE                                     173
 
 /* Reason codes. */
 # define EVP_R_AES_KEY_SETUP_FAILED                       143
@@ -1637,6 +1638,7 @@ int ERR_load_EVP_strings(void);
 # define EVP_R_NO_DIGEST_SET                              139
 # define EVP_R_NO_KEY_SET                                 154
 # define EVP_R_NO_OPERATION_SET                           149
+# define EVP_R_ONLY_ONESHOT_SUPPORTED                     177
 # define EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE   150
 # define EVP_R_OPERATON_NOT_INITIALIZED                   151
 # define EVP_R_PARTIALLY_OVERLAPPING                      162
diff --git a/include/openssl/obj_mac.h b/include/openssl/obj_mac.h
index 0db6c7d..f888990 100644
--- a/include/openssl/obj_mac.h
+++ b/include/openssl/obj_mac.h
@@ -4613,6 +4613,14 @@
 #define NID_X448                1035
 #define OBJ_X448                1L,3L,101L,111L
 
+#define SN_ED25519              "ED25519"
+#define NID_ED25519             1087
+#define OBJ_ED25519             1L,3L,101L,112L
+
+#define SN_ED448                "ED448"
+#define NID_ED448               1088
+#define OBJ_ED448               1L,3L,101L,113L
+
 #define SN_kx_rsa               "KxRSA"
 #define LN_kx_rsa               "kx-rsa"
 #define NID_kx_rsa              1037
diff --git a/test/certs/ee-ed25519.pem b/test/certs/ee-ed25519.pem
new file mode 100644
index 0000000..3f4b5b2
--- /dev/null
+++ b/test/certs/ee-ed25519.pem
@@ -0,0 +1,9 @@
+-----BEGIN CERTIFICATE-----
+MIIBLDCB36ADAgECAghWAUdKKo3DMDAFBgMrZXAwGTEXMBUGA1UEAwwOSUVURiBUZX
+N0IERlbW8wHhcNMTYwODAxMTIxOTI0WhcNNDAxMjMxMjM1OTU5WjAZMRcwFQYDVQQD
+DA5JRVRGIFRlc3QgRGVtbzAqMAUGAytlbgMhAIUg8AmJMKdUdIt93LQ+91oNvzoNJj
+ga9OukqY6qm05qo0UwQzAPBgNVHRMBAf8EBTADAQEAMA4GA1UdDwEBAAQEAwIDCDAg
+BgNVHQ4BAQAEFgQUmx9e7e0EM4Xk97xiPFl1uQvIuzswBQYDK2VwA0EAryMB/t3J5v
+/BzKc9dNZIpDmAgs3babFOTQbs+BolzlDUwsPrdGxO3YNGhW7Ibz3OGhhlxXrCe1Cg
+w1AH9efZBw==
+-----END CERTIFICATE-----
diff --git a/test/certs/root-ed25519.pem b/test/certs/root-ed25519.pem
new file mode 100644
index 0000000..e509d54
--- /dev/null
+++ b/test/certs/root-ed25519.pem
@@ -0,0 +1,9 @@
+-----BEGIN CERTIFICATE-----
+MIIBODCB66ADAgECAgkAhPEIPRzjLZUwBQYDK2VwMBkxFzAVBgNVBAMMDklFVEYg
+VGVzdCBEZW1vMB4XDTE3MDQxOTIxMzYzOVoXDTQxMDIxMjIxMzYzOVowGTEXMBUG
+A1UEAwwOSUVURiBUZXN0IERlbW8wKjAFBgMrZXADIQAZv0QJaYTN/oVBusFn3DuW
+yFCGqjC2tssMXDitcDFm4aNQME4wHQYDVR0OBBYEFKKMwfhuWWDT4DrnXJYsl6jU
+SCk8MB8GA1UdIwQYMBaAFKKMwfhuWWDT4DrnXJYsl6jUSCk8MAwGA1UdEwQFMAMB
+Af8wBQYDK2VwA0EAa6iEoQZBWB1MhCzASv5HuFM7fR5Nz2/KM7GxYjQWsfvK2Ds1
+jaPSG7Lx4uywIndMafp5CoPoFr6yLBkt+NZLAg==
+-----END CERTIFICATE-----
diff --git a/test/evppkey.txt b/test/evppkey.txt
index 064d7c9..6a69fab 100644
--- a/test/evppkey.txt
+++ b/test/evppkey.txt
@@ -17320,3 +17320,121 @@ OneShotDigestSign = SHA1
 Key = RSA-2048
 Input = "Hello World"
 Output = 3da3ca2bdd1b23a231b0e3c49d95d5959f9398c27a1e534c7e6baf1d2682304d3b6b229385b1edf483f5ef6f9b35bf10c519a302bb2f79c564e1a59ba71aa2fa36df96c942c43e8d9bd4702b5f61c12a078ae2b34d0de221fc8f9f936b79a67c89d11ba5da8c63a1370d0e824c6b661123e9b58b143ff533cf362cbdad70e65b419a6d45723bf22db3c76bb8f5337c5c5c93cb6f38b30d0c835b54c23405ca4217dd0b755f3712ebad285d9e0c02655f6ce5ce6fed78f3c81843de325f628055eef57f280dee0c3170050137ee599b9ab7f2b5d3c5f831777ea05a5eb097c70bad1a7214dadae12d7960bb9425390c7d25a79985e1e3c28ad422ff93c808f4b5
+
+Title = ED25519 tests from RFC8032
+
+PrivateKey=ED25519-1
+-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VwBCIEIJ1hsZ3v/VpguoRK9JLsLMREScVpezJpGXA7rAMcrn9g
+-----END PRIVATE KEY-----
+PrivateKey=ED25519-2
+-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VwBCIEIEzNCJso/5banbbDRuwRTg9bijGfNaumJNqM9u1PuKb7
+-----END PRIVATE KEY-----
+PrivateKey=ED25519-3
+-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VwBCIEIMWqjfQ/n4N77bdELzHct7Fm04U1B28JS4XOOi4LRFj3
+-----END PRIVATE KEY-----
+PrivateKey=ED25519-4
+-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VwBCIEIPXldnzxUzGVF2MPImh2uGyBYMxYO8ATdExr8lX1zA7l
+-----END PRIVATE KEY-----
+PrivateKey=ED25519-5
+-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VwBCIEIIM/5iQJI3udYux3WHUgkR6adZzsHRl1W32pAbltyj1C
+-----END PRIVATE KEY-----
+
+PublicKey=ED25519-1-PUBLIC
+-----BEGIN PUBLIC KEY-----
+MCowBQYDK2VwAyEA11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=
+-----END PUBLIC KEY-----
+PublicKey=ED25519-2-PUBLIC
+-----BEGIN PUBLIC KEY-----
+MCowBQYDK2VwAyEAPUAXw+hDiVqStwqnTRt+vJyYLM8uxJaMwM1V8Sr0Zgw=
+-----END PUBLIC KEY-----
+PublicKey=ED25519-3-PUBLIC
+-----BEGIN PUBLIC KEY-----
+MCowBQYDK2VwAyEA/FHNjmIYoaONpH7QAjDwWAgW7RO6MwOsXeuRFUiQgCU=
+-----END PUBLIC KEY-----
+PublicKey=ED25519-4-PUBLIC
+-----BEGIN PUBLIC KEY-----
+MCowBQYDK2VwAyEAJ4EX/BRMcjQPZ9DyMW6Dhs7/vyskKMnFH+98WX8dQm4=
+-----END PUBLIC KEY-----
+PublicKey=ED25519-5-PUBLIC
+-----BEGIN PUBLIC KEY-----
+MCowBQYDK2VwAyEA7Bcrk61eVjv0kyxw4SRQNMNUZ+8u/U1k6/gZaDRn4r8=
+-----END PUBLIC KEY-----
+
+PrivPubKeyPair = ED25519-1:ED25519-1-PUBLIC
+
+OneShotDigestSign = NULL
+Key = ED25519-1
+Input = ""
+Output = e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b
+
+PrivPubKeyPair = ED25519-2:ED25519-2-PUBLIC
+
+OneShotDigestSign = NULL
+Key = ED25519-2
+Input = 72
+Output = 92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00
+
+PrivPubKeyPair = ED25519-3:ED25519-3-PUBLIC
+
+OneShotDigestSign = NULL
+Key = ED25519-3
+Input = af82
+Output = 6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a
+
+PrivPubKeyPair = ED25519-4:ED25519-4-PUBLIC
+
+OneShotDigestSign = NULL
+Key = ED25519-4
+Input = 08b8b2b733424243760fe426a4b54908632110a66c2f6591eabd3345e3e4eb98fa6e264bf09efe12ee50f8f54e9f77b1e355f6c50544e23fb1433ddf73be84d879de7c0046dc4996d9e773f4bc9efe5738829adb26c81b37c93a1b270b20329d658675fc6ea534e0810a4432826bf58c941efb65d57a338bbd2e26640f89ffbc1a858efcb8550ee3a5e1998bd177e93a7363c344fe6b199ee5d02e82d522c4feba15452f80288a821a579116ec6dad2b3b310da903401aa62100ab5d1a36553e06203b33890cc9b832f79ef80560ccb9a39ce767967ed628c6ad573cb116dbefefd75499da96bd68a8a97b928a8bbc103b6621fcde2beca1231d206be6cd9ec7aff6f6c94fcd7204ed3455c68c83f4a41da4af2b74ef5c53f1d8ac70bdcb7ed185ce81bd84359d44254d95629e9855a94a7c1958d1f8ada5d0532ed8a5aa3fb2d17ba70eb6248e594e1a2297acbbb39d502f1a8c6eb6f1ce22b3de1a1f40cc24554119a831a9aad6079cad88425de6bde1a9187ebb6092cf67bf2b13fd65f27088d78b7e883c8759d2c4f5c65adb7553878ad575f9fad878e80a0c9ba63bcbcc2732e69485bbc9c90bfbd62481d9089beccf80cfe2df16a2cf65bd92dd597b0707e0917af48bbb75fed413d238f5555a7a569d80c3414a8d0859dc65a46128bab27af87a71314f318c782b23ebfe808b82b0ce26401d2e22f04d83d1255dc51addd3b75a2b1ae0784504df543af8969be3ea7082ff7fc9888c144da2af58429ec96031dbcad3dad9af0dcbaaaf268cb8fcffead94f3c7ca495e056a9b47acdb751fb73e666c6c655ade8297297d07ad1ba5e43f1bca32301651339e22904cc8c42f58c30c04aafdb038dda0847dd988dcda6f3bfd15c4b4c4525004aa06eeff8ca61783aacec57fb3d1f92b0fe2fd1a85f6724517b65e614ad6808d6f6ee34dff7310fdc82aebfd904b01e1dc54b2927094b2db68d6f903b68401adebf5a7e08d78ff4ef5d63653a65040cf9bfd4aca7984a74d37145986780fc0b16ac451649de6188a7dbdf191f64b5fc5e2ab47b57f7f7276cd419c17a3ca8e1b939ae49e488acba6b965610b5480109c8b17b80e1b7b750dfc7598d5d5011fd2dcc5600a32ef5b52a1ecc820e308aa342721aac0943bf6686b64b2579376504ccc493d97e6aed3fb0f9cd71a43dd497f01f17c0e2cb3797aa2a2f256656168e6c496afc5fb93246f6b1116398a346f1a641f3b041e989f7914f90cc2c7fff357876e506b50d334ba77c225bc307ba537152f3f1610e4eafe595f6d9d90d11faa933a15ef1369546868a7f3a45a96768d40fd9d03412c091c6315cf4fde7cb68606937380db2eaaa707b4c4185c32eddcdd306705e4dc1ffc872eeee475a64dfac86aba41c0618983f8741c5ef68d3a101e8a3b8cac60c905c15fc910840b94c00a0b9d0
+Output = 0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350aa5371b1508f9f4528ecea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03
+
+PrivPubKeyPair = ED25519-5:ED25519-5-PUBLIC
+
+OneShotDigestSign = NULL
+Key = ED25519-5
+Input = ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f
+Output = dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b58909351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704
+
+# Verify test
+OneShotDigestVerify = NULL
+Key = ED25519-1-PUBLIC
+Input = ""
+Output = e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b
+
+# Corrupted input
+OneShotDigestVerify = NULL
+Key = ED25519-1-PUBLIC
+Input = "bad"
+Output = e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b
+Result = VERIFY_ERROR
+
+# Corrupted signature
+OneShotDigestVerify = NULL
+Key = ED25519-1-PUBLIC
+Input = ""
+Output = e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100c
+Result = VERIFY_ERROR
+
+PrivPubKeyPair = ED25519-1:ED25519-2-PUBLIC
+Result = KEYPAIR_MISMATCH
+
+# Make sure update calls return an error
+DigestSign = NULL
+Key = ED25519-1
+Input = "Test"
+Result = DIGESTUPDATE_ERROR
+
+DigestVerify = NULL
+Key = ED25519-1-PUBLIC
+Input = "Test"
+Result = DIGESTUPDATE_ERROR
+
+# Attempt to set invalid digest
+DigestSign = SHA256
+Key = ED25519-1
+Result = DIGESTSIGNINIT_ERROR
diff --git a/test/recipes/25-test_verify.t b/test/recipes/25-test_verify.t
index 9c425c0..001d37a 100644
--- a/test/recipes/25-test_verify.t
+++ b/test/recipes/25-test_verify.t
@@ -12,6 +12,7 @@ use warnings;
 
 use File::Spec::Functions qw/canonpath/;
 use OpenSSL::Test qw/:DEFAULT srctop_file/;
+use OpenSSL::Test::Utils;
 
 setup("test_verify");
 
@@ -26,7 +27,7 @@ sub verify {
     run(app([@args]));
 }
 
-plan tests => 125;
+plan tests => 126;
 
 # Canonical success
 ok(verify("ee-cert", "sslserver", ["root-cert"], ["ca-cert"]),
@@ -340,3 +341,13 @@ ok(!verify("ee-pss-sha1-cert", "sslserver", ["root-cert"], ["ca-cert"], "-auth_l
 
 ok(verify("ee-pss-sha256-cert", "sslserver", ["root-cert"], ["ca-cert"], "-auth_level", "2"),
     "PSS signature using SHA256 and auth level 2");
+
+SKIP: {
+    skip "Ed25519 is not supported by this OpenSSL build", 1
+	      if disabled("ec");
+
+    # ED25519 certificate from draft-ietf-curdle-pkix-04
+    ok(verify("ee-ed25519", "sslserver", ["root-ed25519"], []),
+       "ED25519 signature");
+
+}


More information about the openssl-commits mailing list