Merge pull request #343 from h2o/kazuho/cert-ed25519

add support for ed25519 certificates
diff --git a/include/picotls.h b/include/picotls.h
index 6134467..a3050c1 100644
--- a/include/picotls.h
+++ b/include/picotls.h
@@ -124,6 +124,7 @@
 #define PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256 0x0804
 #define PTLS_SIGNATURE_RSA_PSS_RSAE_SHA384 0x0805
 #define PTLS_SIGNATURE_RSA_PSS_RSAE_SHA512 0x0806
+#define PTLS_SIGNATURE_ED25519 0x0807
 
 /* ESNI */
 #define PTLS_ESNI_VERSION_DRAFT03 0xff02
diff --git a/include/picotls/openssl.h b/include/picotls/openssl.h
index e528fdd..8e26cf8 100644
--- a/include/picotls/openssl.h
+++ b/include/picotls/openssl.h
@@ -50,6 +50,9 @@
 #define PTLS_OPENSSL_HAS_SECP521R1 1 /* deprecated; use HAVE_ */
 extern ptls_key_exchange_algorithm_t ptls_openssl_secp521r1;
 #endif
+#ifdef EVP_PKEY_ED25519
+#define PTLS_OPENSSL_HAVE_ED25519 1
+#endif
 #if defined(NID_X25519) && !defined(LIBRESSL_VERSION_NUMBER)
 #define PTLS_OPENSSL_HAVE_X25519 1
 #define PTLS_OPENSSL_HAS_X25519 1 /* deprecated; use HAVE_ */
diff --git a/lib/openssl.c b/lib/openssl.c
index 36344a9..80613bb 100644
--- a/lib/openssl.c
+++ b/lib/openssl.c
@@ -102,19 +102,29 @@
 static const struct st_ptls_openssl_signature_scheme_t secp521r1_signature_schemes[] = {
     {PTLS_SIGNATURE_ECDSA_SECP521R1_SHA512, EVP_sha512}, {UINT16_MAX, NULL}};
 #endif
+#if PTLS_OPENSSL_HAVE_ED25519
+static const struct st_ptls_openssl_signature_scheme_t ed25519_signature_schemes[] = {{PTLS_SIGNATURE_ED25519, NULL},
+                                                                                      {UINT16_MAX, NULL}};
+#endif
 
 /**
  * The default list sent in ClientHello.signature_algorithms. ECDSA certificates are preferred.
  */
-static const uint16_t default_signature_schemes[] = {PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256,
+static const uint16_t default_signature_schemes[] = {
+#if PTLS_OPENSSL_HAVE_ED25519
+    PTLS_SIGNATURE_ED25519,
+#endif
+    PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256,
 #if PTLS_OPENSSL_HAVE_SECP384R1
-                                                     PTLS_SIGNATURE_ECDSA_SECP384R1_SHA384,
+    PTLS_SIGNATURE_ECDSA_SECP384R1_SHA384,
 #endif
 #if PTLS_OPENSSL_HAVE_SECP521R1
-                                                     PTLS_SIGNATURE_ECDSA_SECP521R1_SHA512,
+    PTLS_SIGNATURE_ECDSA_SECP521R1_SHA512,
 #endif
-                                                     PTLS_SIGNATURE_RSA_PSS_RSAE_SHA512,    PTLS_SIGNATURE_RSA_PSS_RSAE_SHA384,
-                                                     PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256,    UINT16_MAX};
+    PTLS_SIGNATURE_RSA_PSS_RSAE_SHA512,
+    PTLS_SIGNATURE_RSA_PSS_RSAE_SHA384,
+    PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256,
+    UINT16_MAX};
 
 static const struct st_ptls_openssl_signature_scheme_t *lookup_signature_schemes(EVP_PKEY *key)
 {
@@ -145,6 +155,11 @@
         }
         EC_KEY_free(eckey);
     } break;
+#if PTLS_OPENSSL_HAVE_ED25519
+    case EVP_PKEY_ED25519:
+        schemes = ed25519_signature_schemes;
+        break;
+#endif
     default:
         break;
     }
@@ -677,9 +692,11 @@
     }
 }
 
-static int do_sign(EVP_PKEY *key, ptls_buffer_t *outbuf, ptls_iovec_t input, const EVP_MD *md)
+static int do_sign(EVP_PKEY *key, const struct st_ptls_openssl_signature_scheme_t *scheme, ptls_buffer_t *outbuf,
+                   ptls_iovec_t input)
 {
     EVP_MD_CTX *ctx = NULL;
+    const EVP_MD *md = scheme->scheme_md != NULL ? scheme->scheme_md() : NULL;
     EVP_PKEY_CTX *pkey_ctx;
     size_t siglen;
     int ret;
@@ -688,38 +705,58 @@
         ret = PTLS_ERROR_NO_MEMORY;
         goto Exit;
     }
+
     if (EVP_DigestSignInit(ctx, &pkey_ctx, md, NULL, key) != 1) {
         ret = PTLS_ERROR_LIBRARY;
         goto Exit;
     }
-    if (EVP_PKEY_id(key) == EVP_PKEY_RSA) {
-        if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) {
+
+#if PTLS_OPENSSL_HAVE_ED25519
+    if (EVP_PKEY_id(key) == EVP_PKEY_ED25519) {
+        /* ED25519 requires the use of the all-at-once function that appeared in OpenSSL 1.1.1, hence different path */
+        if (EVP_DigestSign(ctx, NULL, &siglen, input.base, input.len) != 1) {
             ret = PTLS_ERROR_LIBRARY;
             goto Exit;
         }
-        if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1) != 1) {
+        if ((ret = ptls_buffer_reserve(outbuf, siglen)) != 0)
+            goto Exit;
+        if (EVP_DigestSign(ctx, outbuf->base + outbuf->off, &siglen, input.base, input.len) != 1) {
             ret = PTLS_ERROR_LIBRARY;
             goto Exit;
         }
-        if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, md) != 1) {
+    } else
+#endif
+    {
+        if (EVP_PKEY_id(key) == EVP_PKEY_RSA) {
+            if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) {
+                ret = PTLS_ERROR_LIBRARY;
+                goto Exit;
+            }
+            if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1) != 1) {
+                ret = PTLS_ERROR_LIBRARY;
+                goto Exit;
+            }
+            if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, md) != 1) {
+                ret = PTLS_ERROR_LIBRARY;
+                goto Exit;
+            }
+        }
+        if (EVP_DigestSignUpdate(ctx, input.base, input.len) != 1) {
+            ret = PTLS_ERROR_LIBRARY;
+            goto Exit;
+        }
+        if (EVP_DigestSignFinal(ctx, NULL, &siglen) != 1) {
+            ret = PTLS_ERROR_LIBRARY;
+            goto Exit;
+        }
+        if ((ret = ptls_buffer_reserve(outbuf, siglen)) != 0)
+            goto Exit;
+        if (EVP_DigestSignFinal(ctx, outbuf->base + outbuf->off, &siglen) != 1) {
             ret = PTLS_ERROR_LIBRARY;
             goto Exit;
         }
     }
-    if (EVP_DigestSignUpdate(ctx, input.base, input.len) != 1) {
-        ret = PTLS_ERROR_LIBRARY;
-        goto Exit;
-    }
-    if (EVP_DigestSignFinal(ctx, NULL, &siglen) != 1) {
-        ret = PTLS_ERROR_LIBRARY;
-        goto Exit;
-    }
-    if ((ret = ptls_buffer_reserve(outbuf, siglen)) != 0)
-        goto Exit;
-    if (EVP_DigestSignFinal(ctx, outbuf->base + outbuf->off, &siglen) != 1) {
-        ret = PTLS_ERROR_LIBRARY;
-        goto Exit;
-    }
+
     outbuf->off += siglen;
 
     ret = 0;
@@ -1019,7 +1056,7 @@
 
 Found:
     *selected_algorithm = scheme->scheme_id;
-    return do_sign(self->key, outbuf, input, scheme->scheme_md());
+    return do_sign(self->key, scheme, outbuf, input);
 }
 
 static X509 *to_x509(ptls_iovec_t vec)
@@ -1054,32 +1091,50 @@
         ret = PTLS_ERROR_NO_MEMORY;
         goto Exit;
     }
-    if (EVP_DigestVerifyInit(ctx, &pkey_ctx, scheme->scheme_md(), NULL, key) != 1) {
-        ret = PTLS_ERROR_LIBRARY;
-        goto Exit;
-    }
-    if (EVP_PKEY_id(key) == EVP_PKEY_RSA) {
-        if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) {
+
+#if PTLS_OPENSSL_HAVE_ED25519
+    if (EVP_PKEY_id(key) == EVP_PKEY_ED25519) {
+        /* ED25519 requires the use of the all-at-once function that appeared in OpenSSL 1.1.1, hence different path */
+        if (EVP_DigestVerifyInit(ctx, &pkey_ctx, NULL, NULL, key) != 1) {
             ret = PTLS_ERROR_LIBRARY;
             goto Exit;
         }
-        if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1) != 1) {
+        if (EVP_DigestVerify(ctx, signature.base, signature.len, data.base, data.len) != 1) {
             ret = PTLS_ERROR_LIBRARY;
             goto Exit;
         }
-        if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, scheme->scheme_md()) != 1) {
+    } else
+#endif
+    {
+        if (EVP_DigestVerifyInit(ctx, &pkey_ctx, scheme->scheme_md(), NULL, key) != 1) {
             ret = PTLS_ERROR_LIBRARY;
             goto Exit;
         }
+
+        if (EVP_PKEY_id(key) == EVP_PKEY_RSA) {
+            if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) {
+                ret = PTLS_ERROR_LIBRARY;
+                goto Exit;
+            }
+            if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1) != 1) {
+                ret = PTLS_ERROR_LIBRARY;
+                goto Exit;
+            }
+            if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, scheme->scheme_md()) != 1) {
+                ret = PTLS_ERROR_LIBRARY;
+                goto Exit;
+            }
+        }
+        if (EVP_DigestVerifyUpdate(ctx, data.base, data.len) != 1) {
+            ret = PTLS_ERROR_LIBRARY;
+            goto Exit;
+        }
+        if (EVP_DigestVerifyFinal(ctx, signature.base, signature.len) != 1) {
+            ret = PTLS_ALERT_DECRYPT_ERROR;
+            goto Exit;
+        }
     }
-    if (EVP_DigestVerifyUpdate(ctx, data.base, data.len) != 1) {
-        ret = PTLS_ERROR_LIBRARY;
-        goto Exit;
-    }
-    if (EVP_DigestVerifyFinal(ctx, signature.base, signature.len) != 1) {
-        ret = PTLS_ALERT_DECRYPT_ERROR;
-        goto Exit;
-    }
+
     ret = 0;
 
 Exit:
diff --git a/t/assets/ed25519/cert.pem b/t/assets/ed25519/cert.pem
new file mode 100644
index 0000000..56e0cf9
--- /dev/null
+++ b/t/assets/ed25519/cert.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICOTCCASGgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDEw9waWNv
+dGxzIHRlc3QgY2EwHhcNMjEwMzIxMjMxNjU2WhcNMzEwMzE5MjMxNjU2WjAjMSEw
+HwYDVQQDDBhlZDI1NTE5LnRlc3QuZXhhbXBsZS5jb20wKjAFBgMrZXADIQDKBvBk
+m3KyTPvBdVJTrtmR5V7/OtlJTmZmPg972cKuEaN7MHkwCQYDVR0TBAIwADAsBglg
+hkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0O
+BBYEFD2F2ds0fQ6SbWUHViucoIl/kY8BMB8GA1UdIwQYMBaAFL95ypeyYHgglqpG
+V5zfp7Ij9SVjMA0GCSqGSIb3DQEBCwUAA4IBAQBN6/tmJrdkPDxGQ+kbGuh6KlaT
+FzywvfyaArpOoNWRHuDgzyOYOJ7XCaChW4GeVk+zRLxC1ZVrbn9kL5LDP9oKdnTn
+dPOsj4Zmn3er9zPlRgauvGAd5DuHk1n3fdIKhw/zusB3MK5iQWIsmSx9jFEx+8Sf
+CMe5aFqzZO1JHRaR/yrKLxxhabhqjP7Xad6Dz9uMQpOPEddOi93iun589yTI40d3
+UrB81XJW9Ll1SnCs8qYl99D9Kcq8OWjFjxrKiEFHw0z8oWag+M8bgqnjvYZOZln4
+Mnm9mOJtsGjtuQ+wvUMt3AWhJ4iI0eEebXxDjnZFMoipXa/L3B+h/DnnH2GN
+-----END CERTIFICATE-----
diff --git a/t/assets/ed25519/key.pem b/t/assets/ed25519/key.pem
new file mode 100644
index 0000000..6461749
--- /dev/null
+++ b/t/assets/ed25519/key.pem
@@ -0,0 +1,3 @@
+-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VwBCIEIFh3irfeqAxMuC/lkrh12Q5Qz/h5JfkvSDhgncoYF1t8
+-----END PRIVATE KEY-----
diff --git a/t/assets/ed25519/pub.pem b/t/assets/ed25519/pub.pem
new file mode 100644
index 0000000..04b8564
--- /dev/null
+++ b/t/assets/ed25519/pub.pem
@@ -0,0 +1,3 @@
+-----BEGIN PUBLIC KEY-----
+MCowBQYDK2VwAyEAygbwZJtyskz7wXVSU67ZkeVe/zrZSU5mZj4Pe9nCrhE=
+-----END PUBLIC KEY-----
diff --git a/t/assets/secp384r1/cert.pem b/t/assets/secp384r1/cert.pem
index 8d33af8..8c03b51 100644
--- a/t/assets/secp384r1/cert.pem
+++ b/t/assets/secp384r1/cert.pem
@@ -1,52 +1,3 @@
-Certificate:
-    Data:
-        Version: 3 (0x2)
-        Serial Number: 3 (0x3)
-        Signature Algorithm: sha256WithRSAEncryption
-        Issuer: CN=picotls test ca
-        Validity
-            Not Before: Mar 22 00:14:34 2021 GMT
-            Not After : Mar 20 00:14:34 2031 GMT
-        Subject: CN=secp384r1.test.example.com
-        Subject Public Key Info:
-            Public Key Algorithm: id-ecPublicKey
-                Public-Key: (384 bit)
-                pub:
-                    04:01:0f:f2:01:e8:b2:24:58:9b:4a:80:21:44:2c:
-                    56:19:86:d8:41:f3:f1:7e:00:b2:01:94:ff:38:c5:
-                    d6:29:83:36:3e:6d:dc:38:88:93:5f:c2:11:9f:de:
-                    04:65:bd:41:cf:8a:3c:f6:6f:0b:5b:07:51:6c:f1:
-                    59:20:9b:16:79:35:d2:0d:f6:7c:3f:86:8f:c2:1d:
-                    54:2d:2b:f5:25:4c:89:a8:62:b1:9f:80:1f:f1:c2:
-                    dc:9e:4b:a6:b8:c5:5a
-                ASN1 OID: secp384r1
-                NIST CURVE: P-384
-        X509v3 extensions:
-            X509v3 Basic Constraints: 
-                CA:FALSE
-            Netscape Comment: 
-                OpenSSL Generated Certificate
-            X509v3 Subject Key Identifier: 
-                C3:D2:A4:8C:90:23:54:D8:9F:ED:59:94:83:09:0D:D5:22:83:A0:F1
-            X509v3 Authority Key Identifier: 
-                keyid:BF:79:CA:97:B2:60:78:20:96:AA:46:57:9C:DF:A7:B2:23:F5:25:63
-
-    Signature Algorithm: sha256WithRSAEncryption
-         37:c8:be:48:a7:68:57:4e:aa:92:b9:19:0d:f8:84:4b:9d:94:
-         32:26:ba:d4:1d:4e:1f:88:69:a0:68:d7:4f:48:05:c7:80:2a:
-         04:d9:67:76:4c:c0:49:93:69:1b:20:58:ce:44:8b:a6:54:8a:
-         54:a1:00:93:52:d1:a3:ad:a0:cf:e6:18:30:64:20:a9:4c:8c:
-         ed:c2:7a:7b:7a:c3:8c:c0:65:4f:16:7a:b1:9b:3e:03:10:6c:
-         9f:6c:33:fc:2d:d5:a0:75:93:78:c6:db:21:5d:23:20:15:46:
-         3d:bb:77:16:41:43:71:58:de:d6:0c:be:b5:6f:27:87:c3:91:
-         99:3c:a7:fe:48:3f:7d:7d:03:ac:b5:8c:ce:f1:70:1e:4f:7c:
-         42:28:d8:95:98:ed:6b:17:3b:51:ac:ff:ee:cb:79:c1:6c:2e:
-         39:4c:ab:f5:c9:a2:42:6b:a8:1c:38:17:4d:49:78:ba:10:7f:
-         25:5d:6c:f4:01:20:2e:17:bf:e3:42:0f:7b:43:01:b7:d4:85:
-         2d:0e:f2:3b:c8:82:76:43:ca:8b:4b:af:1c:05:18:ef:da:7c:
-         3c:ad:30:90:fd:4b:f7:00:11:bd:99:7f:9f:6d:67:0e:80:c4:
-         08:e9:cf:2f:38:e3:d9:ff:ad:7e:99:5a:39:47:7b:c1:4a:b7:
-         af:54:e0:c5
 -----BEGIN CERTIFICATE-----
 MIIChzCCAW+gAwIBAgIBAzANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDEw9waWNv
 dGxzIHRlc3QgY2EwHhcNMjEwMzIyMDAxNDM0WhcNMzEwMzIwMDAxNDM0WjAlMSMw
diff --git a/t/assets/secp521r1/cert.pem b/t/assets/secp521r1/cert.pem
index f9a963c..28a76c1 100644
--- a/t/assets/secp521r1/cert.pem
+++ b/t/assets/secp521r1/cert.pem
@@ -1,54 +1,3 @@
-Certificate:
-    Data:
-        Version: 3 (0x2)
-        Serial Number: 4 (0x4)
-        Signature Algorithm: sha256WithRSAEncryption
-        Issuer: CN=picotls test ca
-        Validity
-            Not Before: Mar 22 00:34:45 2021 GMT
-            Not After : Mar 20 00:34:45 2031 GMT
-        Subject: CN=secp521r1.test.example.com
-        Subject Public Key Info:
-            Public Key Algorithm: id-ecPublicKey
-                Public-Key: (521 bit)
-                pub:
-                    04:00:2c:95:0a:72:ae:8e:93:32:bf:9f:2b:f4:bf:
-                    f7:1c:bd:29:33:d3:f1:83:1f:f4:ed:ce:7f:7f:e4:
-                    82:d6:e9:2a:62:8f:25:22:e2:ee:0b:bf:27:07:79:
-                    a7:e3:00:01:f0:fb:13:f2:9c:fc:5d:06:68:60:12:
-                    39:e7:69:63:f8:70:62:00:7b:13:92:2f:58:f9:a9:
-                    b3:70:3a:39:40:29:73:a2:a3:ba:1a:d5:cd:ec:73:
-                    88:2e:ca:81:80:55:1b:95:ff:9a:bf:a9:35:f4:e5:
-                    f5:f3:95:fe:aa:73:bc:3d:bd:4a:c3:0b:e2:11:65:
-                    c3:a2:4b:b8:41:32:16:a1:08:e3:e1:51:92
-                ASN1 OID: secp521r1
-                NIST CURVE: P-521
-        X509v3 extensions:
-            X509v3 Basic Constraints: 
-                CA:FALSE
-            Netscape Comment: 
-                OpenSSL Generated Certificate
-            X509v3 Subject Key Identifier: 
-                BF:5B:DB:18:6B:81:70:CB:39:CC:47:53:18:75:8B:90:9E:66:5B:08
-            X509v3 Authority Key Identifier: 
-                keyid:BF:79:CA:97:B2:60:78:20:96:AA:46:57:9C:DF:A7:B2:23:F5:25:63
-
-    Signature Algorithm: sha256WithRSAEncryption
-         12:c1:bc:3d:8b:bb:24:bc:e0:01:ab:a1:51:15:0a:9e:aa:8a:
-         3a:49:5b:0c:d0:ed:29:3c:01:94:18:af:49:8f:50:af:a6:75:
-         7f:f8:e3:ff:0e:0b:66:ca:95:4c:9e:2e:d7:d1:06:ea:db:42:
-         32:d7:b6:7e:14:0b:ca:86:25:57:01:76:76:24:bc:a2:a7:6b:
-         e1:9a:3e:00:fc:a9:db:b3:a9:ec:89:a2:dd:e8:30:3d:e5:64:
-         eb:e0:d5:c6:60:c9:4c:a7:3a:61:69:0d:03:11:f9:61:ae:7e:
-         16:87:7f:af:10:da:08:11:d7:54:a1:5f:97:f1:3a:78:8c:5a:
-         0c:f8:a2:0c:fa:ae:17:d5:8c:1a:84:d5:77:b7:fd:c1:5f:f9:
-         56:9d:79:be:ba:5d:a1:17:1a:10:94:e7:09:49:48:e6:08:d7:
-         ee:68:64:19:17:c5:91:b0:b0:e0:0d:42:67:de:9c:3c:5f:34:
-         d7:1d:f5:98:a8:66:29:87:65:fe:b6:15:ab:47:1e:42:10:62:
-         dd:cb:86:0f:68:3e:5d:51:0a:22:c3:07:3d:e5:cb:44:2b:89:
-         9c:33:dd:8f:db:88:b4:0a:8c:d7:da:a1:19:55:18:87:26:c0:
-         e2:2e:ee:b7:1c:64:31:27:97:8d:09:d7:6d:ab:2f:0b:4c:97:
-         0f:5a:d3:eb
 -----BEGIN CERTIFICATE-----
 MIICrTCCAZWgAwIBAgIBBDANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDEw9waWNv
 dGxzIHRlc3QgY2EwHhcNMjEwMzIyMDAzNDQ1WhcNMzEwMzIwMDAzNDQ1WjAlMSMw
diff --git a/t/cli.c b/t/cli.c
index bf21b81..db6bf82 100644
--- a/t/cli.c
+++ b/t/cli.c
@@ -379,6 +379,17 @@
 #if PTLS_OPENSSL_HAVE_X25519
            ", X25519"
 #endif
+           "\n"
+           "Supported signature algorithms: rsa, secp256r1"
+#if PTLS_OPENSSL_HAVE_SECP384R1
+           ", secp384r1"
+#endif
+#if PTLS_OPENSSL_HAVE_SECP521R1
+           ", secp521r1"
+#endif
+#if PTLS_OPENSSL_HAVE_ED25519
+           ", ed25519"
+#endif
            "\n\n",
            cmd);
 }
diff --git a/t/e2e.t b/t/e2e.t
index d72fc24..ada3109 100755
--- a/t/e2e.t
+++ b/t/e2e.t
@@ -72,7 +72,15 @@
 
 # This test acts as an end-to-end testing of the certificate verifier of the OpenSSL backend.
 subtest "raw-public-keys" => sub {
-    for my $key_type (qw(rsa secp256r1 secp384r1 secp521r1)) {
+    my @key_types = do {
+        my $help = `$cli -h`;
+        $help =~ /^Supported signature algorithms:\s*(.*)\s*$/m
+            or die "failed to extract list of supported signature algorithms from $cli -h";
+        split /,\s*/, $1;
+    };
+    die "unexpected list of supported signature algorithms: @key_types"
+        unless grep /^rsa$/, @key_types;
+    for my $key_type (@key_types) {
         subtest $key_type => sub {
             my $guard = spawn_server($key_type, qw(-r - -i t/assets/hello.txt));
             my $resp = `$cli -v -r t/assets/$key_type/pub.pem 127.0.0.1 $port 2> /dev/null`;
diff --git a/t/openssl.c b/t/openssl.c
index a6f0a06..ff47eee 100644
--- a/t/openssl.c
+++ b/t/openssl.c
@@ -138,7 +138,7 @@
         uint8_t sigbuf_small[1024];
 
         ptls_buffer_init(&sigbuf, sigbuf_small, sizeof(sigbuf_small));
-        ok(do_sign(key, &sigbuf, ptls_iovec_init(message, strlen(message)), schemes[i].scheme_md()) == 0);
+        ok(do_sign(key, schemes + i, &sigbuf, ptls_iovec_init(message, strlen(message))) == 0);
         EVP_PKEY_up_ref(key);
         ok(verify_sign(key, schemes[i].scheme_id, ptls_iovec_init(message, strlen(message)),
                        ptls_iovec_init(sigbuf.base, sigbuf.off)) == 0);
@@ -180,6 +180,23 @@
 #endif
 }
 
+static void test_ed25519_sign(void)
+{
+#if PTLS_OPENSSL_HAVE_ED25519
+    EVP_PKEY *pkey = NULL;
+
+    { /* create pkey */
+        EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, NULL);
+        EVP_PKEY_keygen_init(pctx);
+        EVP_PKEY_keygen(pctx, &pkey);
+        EVP_PKEY_CTX_free(pctx);
+    }
+
+    test_sign_verify(pkey, ed25519_signature_schemes);
+    EVP_PKEY_free(pkey);
+#endif
+}
+
 static X509 *x509_from_pem(const char *pem)
 {
     BIO *bio = BIO_new_mem_buf((void *)pem, (int)strlen(pem));
@@ -318,6 +335,7 @@
 
     subtest("rsa-sign", test_rsa_sign);
     subtest("ecdsa-sign", test_ecdsa_sign);
+    subtest("ed25519-sign", test_ed25519_sign);
     subtest("cert-verify", test_cert_verify);
     subtest("picotls", test_picotls);
     test_picotls_esni(esni_private_keys);