Merge pull request #342 from h2o/kazuho/verify-sign-sha384512

fixes issues re certificate verification using sha384, sha512
diff --git a/include/picotls.h b/include/picotls.h
index ee3b941..6134467 100644
--- a/include/picotls.h
+++ b/include/picotls.h
@@ -575,9 +575,15 @@
  * The implementor of the callback should use that as the opportunity to free any temporary data allocated for the verify_sign
  * callback.
  */
-PTLS_CALLBACK_TYPE(int, verify_certificate, ptls_t *tls,
-                   int (**verify_sign)(void *verify_ctx, ptls_iovec_t data, ptls_iovec_t sign), void **verify_data,
-                   ptls_iovec_t *certs, size_t num_certs);
+typedef struct st_ptls_verify_certificate_t {
+    int (*cb)(struct st_ptls_verify_certificate_t *self, ptls_t *tls,
+              int (**verify_sign)(void *verify_ctx, uint16_t algo, ptls_iovec_t data, ptls_iovec_t sign), void **verify_data,
+              ptls_iovec_t *certs, size_t num_certs);
+    /**
+     * list of signature algorithms being supported, terminated by UINT16_MAX
+     */
+    const uint16_t *algos;
+} ptls_verify_certificate_t;
 /**
  * Encrypt-and-signs (or verify-and-decrypts) a ticket (server-only).
  * When used for encryption (i.e., is_encrypt being set), the function should return 0 if successful, or else a non-zero value.
diff --git a/include/picotls/openssl.h b/include/picotls/openssl.h
index 2cc8c96..e528fdd 100644
--- a/include/picotls/openssl.h
+++ b/include/picotls/openssl.h
@@ -52,7 +52,7 @@
 #endif
 #if defined(NID_X25519) && !defined(LIBRESSL_VERSION_NUMBER)
 #define PTLS_OPENSSL_HAVE_X25519 1
-#define PTLS_OPENSSL_HAS_X25519 1  /* deprecated; use HAVE_ */
+#define PTLS_OPENSSL_HAS_X25519 1 /* deprecated; use HAVE_ */
 extern ptls_key_exchange_algorithm_t ptls_openssl_x25519;
 #endif
 #ifndef OPENSSL_NO_BF
@@ -91,13 +91,13 @@
 
 struct st_ptls_openssl_signature_scheme_t {
     uint16_t scheme_id;
-    const EVP_MD *scheme_md;
+    const EVP_MD *(*scheme_md)(void);
 };
 
 typedef struct st_ptls_openssl_sign_certificate_t {
     ptls_sign_certificate_t super;
     EVP_PKEY *key;
-    struct st_ptls_openssl_signature_scheme_t schemes[4]; /* terminated by .scheme_id == UINT16_MAX */
+    const struct st_ptls_openssl_signature_scheme_t *schemes; /* terminated by .scheme_id == UINT16_MAX */
 } ptls_openssl_sign_certificate_t;
 
 int ptls_openssl_init_sign_certificate(ptls_openssl_sign_certificate_t *self, EVP_PKEY *key);
diff --git a/lib/openssl.c b/lib/openssl.c
index 3221a7e..36344a9 100644
--- a/lib/openssl.c
+++ b/lib/openssl.c
@@ -88,6 +88,70 @@
 
 #endif
 
+static const struct st_ptls_openssl_signature_scheme_t rsa_signature_schemes[] = {{PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256, EVP_sha256},
+                                                                                  {PTLS_SIGNATURE_RSA_PSS_RSAE_SHA384, EVP_sha384},
+                                                                                  {PTLS_SIGNATURE_RSA_PSS_RSAE_SHA512, EVP_sha512},
+                                                                                  {UINT16_MAX, NULL}};
+static const struct st_ptls_openssl_signature_scheme_t secp256r1_signature_schemes[] = {
+    {PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256, EVP_sha256}, {UINT16_MAX, NULL}};
+#if PTLS_OPENSSL_HAVE_SECP384R1
+static const struct st_ptls_openssl_signature_scheme_t secp384r1_signature_schemes[] = {
+    {PTLS_SIGNATURE_ECDSA_SECP384R1_SHA384, EVP_sha384}, {UINT16_MAX, NULL}};
+#endif
+#if PTLS_OPENSSL_HAVE_SECP521R1
+static const struct st_ptls_openssl_signature_scheme_t secp521r1_signature_schemes[] = {
+    {PTLS_SIGNATURE_ECDSA_SECP521R1_SHA512, EVP_sha512}, {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,
+#if PTLS_OPENSSL_HAVE_SECP384R1
+                                                     PTLS_SIGNATURE_ECDSA_SECP384R1_SHA384,
+#endif
+#if PTLS_OPENSSL_HAVE_SECP521R1
+                                                     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};
+
+static const struct st_ptls_openssl_signature_scheme_t *lookup_signature_schemes(EVP_PKEY *key)
+{
+    const struct st_ptls_openssl_signature_scheme_t *schemes = NULL;
+
+    switch (EVP_PKEY_id(key)) {
+    case EVP_PKEY_RSA:
+        schemes = rsa_signature_schemes;
+        break;
+    case EVP_PKEY_EC: {
+        EC_KEY *eckey = EVP_PKEY_get1_EC_KEY(key);
+        switch (EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey))) {
+        case NID_X9_62_prime256v1:
+            schemes = secp256r1_signature_schemes;
+            break;
+#if PTLS_OPENSSL_HAVE_SECP384R1
+        case NID_secp384r1:
+            schemes = secp384r1_signature_schemes;
+            break;
+#endif
+#if PTLS_OPENSSL_HAVE_SECP521R1
+        case NID_secp521r1:
+            schemes = secp521r1_signature_schemes;
+            break;
+#endif
+        default:
+            break;
+        }
+        EC_KEY_free(eckey);
+    } break;
+    default:
+        break;
+    }
+
+    return schemes;
+}
+
 void ptls_openssl_random_bytes(void *buf, size_t len)
 {
     int ret = RAND_bytes(buf, (int)len);
@@ -637,7 +701,7 @@
             ret = PTLS_ERROR_LIBRARY;
             goto Exit;
         }
-        if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, EVP_sha256()) != 1) {
+        if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, md) != 1) {
             ret = PTLS_ERROR_LIBRARY;
             goto Exit;
         }
@@ -944,7 +1008,7 @@
     ptls_openssl_sign_certificate_t *self = (ptls_openssl_sign_certificate_t *)_self;
     const struct st_ptls_openssl_signature_scheme_t *scheme;
 
-    /* select the algorithm */
+    /* select the algorithm (driven by server-side preference of `self->schemes`) */
     for (scheme = self->schemes; scheme->scheme_id != UINT16_MAX; ++scheme) {
         size_t i;
         for (i = 0; i != num_algorithms; ++i)
@@ -955,7 +1019,7 @@
 
 Found:
     *selected_algorithm = scheme->scheme_id;
-    return do_sign(self->key, outbuf, input, scheme->scheme_md);
+    return do_sign(self->key, outbuf, input, scheme->scheme_md());
 }
 
 static X509 *to_x509(ptls_iovec_t vec)
@@ -964,9 +1028,10 @@
     return d2i_X509(NULL, &p, (long)vec.len);
 }
 
-static int verify_sign(void *verify_ctx, ptls_iovec_t data, ptls_iovec_t signature)
+static int verify_sign(void *verify_ctx, uint16_t algo, ptls_iovec_t data, ptls_iovec_t signature)
 {
     EVP_PKEY *key = verify_ctx;
+    const struct st_ptls_openssl_signature_scheme_t *scheme;
     EVP_MD_CTX *ctx = NULL;
     EVP_PKEY_CTX *pkey_ctx = NULL;
     int ret = 0;
@@ -974,11 +1039,22 @@
     if (data.base == NULL)
         goto Exit;
 
+    if ((scheme = lookup_signature_schemes(key)) == NULL) {
+        ret = PTLS_ERROR_LIBRARY;
+        goto Exit;
+    }
+    for (; scheme->scheme_id != UINT16_MAX; ++scheme)
+        if (scheme->scheme_id == algo)
+            goto SchemeFound;
+    ret = PTLS_ALERT_ILLEGAL_PARAMETER;
+    goto Exit;
+
+SchemeFound:
     if ((ctx = EVP_MD_CTX_create()) == NULL) {
         ret = PTLS_ERROR_NO_MEMORY;
         goto Exit;
     }
-    if (EVP_DigestVerifyInit(ctx, &pkey_ctx, EVP_sha256(), NULL, key) != 1) {
+    if (EVP_DigestVerifyInit(ctx, &pkey_ctx, scheme->scheme_md(), NULL, key) != 1) {
         ret = PTLS_ERROR_LIBRARY;
         goto Exit;
     }
@@ -991,7 +1067,7 @@
             ret = PTLS_ERROR_LIBRARY;
             goto Exit;
         }
-        if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, EVP_sha256()) != 1) {
+        if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, scheme->scheme_md()) != 1) {
             ret = PTLS_ERROR_LIBRARY;
             goto Exit;
         }
@@ -1016,50 +1092,9 @@
 int ptls_openssl_init_sign_certificate(ptls_openssl_sign_certificate_t *self, EVP_PKEY *key)
 {
     *self = (ptls_openssl_sign_certificate_t){{sign_certificate}};
-    size_t scheme_index = 0;
 
-#define PUSH_SCHEME(id, md)                                                                                                        \
-    self->schemes[scheme_index++] = (struct st_ptls_openssl_signature_scheme_t)                                                    \
-    {                                                                                                                              \
-        id, md                                                                                                                     \
-    }
-
-    switch (EVP_PKEY_id(key)) {
-    case EVP_PKEY_RSA:
-        PUSH_SCHEME(PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256, EVP_sha256());
-        PUSH_SCHEME(PTLS_SIGNATURE_RSA_PSS_RSAE_SHA384, EVP_sha384());
-        PUSH_SCHEME(PTLS_SIGNATURE_RSA_PSS_RSAE_SHA512, EVP_sha512());
-        break;
-    case EVP_PKEY_EC: {
-        EC_KEY *eckey = EVP_PKEY_get1_EC_KEY(key);
-        switch (EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey))) {
-        case NID_X9_62_prime256v1:
-            PUSH_SCHEME(PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256, EVP_sha256());
-            break;
-#if defined(NID_secp384r1) && !OPENSSL_NO_SHA384
-        case NID_secp384r1:
-            PUSH_SCHEME(PTLS_SIGNATURE_ECDSA_SECP384R1_SHA384, EVP_sha384());
-            break;
-#endif
-#if defined(NID_secp384r1) && !OPENSSL_NO_SHA512
-        case NID_secp521r1:
-            PUSH_SCHEME(PTLS_SIGNATURE_ECDSA_SECP521R1_SHA512, EVP_sha512());
-            break;
-#endif
-        default:
-            EC_KEY_free(eckey);
-            return PTLS_ERROR_INCOMPATIBLE_KEY;
-        }
-        EC_KEY_free(eckey);
-    } break;
-    default:
+    if ((self->schemes = lookup_signature_schemes(key)) == NULL)
         return PTLS_ERROR_INCOMPATIBLE_KEY;
-    }
-    PUSH_SCHEME(UINT16_MAX, NULL);
-    assert(scheme_index <= PTLS_ELEMENTSOF(self->schemes));
-
-#undef PUSH_SCHEME
-
     EVP_PKEY_up_ref(key);
     self->key = key;
 
@@ -1200,8 +1235,9 @@
     return ret;
 }
 
-static int verify_cert(ptls_verify_certificate_t *_self, ptls_t *tls, int (**verifier)(void *, ptls_iovec_t, ptls_iovec_t),
-                       void **verify_data, ptls_iovec_t *certs, size_t num_certs)
+static int verify_cert(ptls_verify_certificate_t *_self, ptls_t *tls,
+                       int (**verifier)(void *, uint16_t, ptls_iovec_t, ptls_iovec_t), void **verify_data, ptls_iovec_t *certs,
+                       size_t num_certs)
 {
     ptls_openssl_verify_certificate_t *self = (ptls_openssl_verify_certificate_t *)_self;
     X509 *cert = NULL;
@@ -1246,7 +1282,7 @@
 
 int ptls_openssl_init_verify_certificate(ptls_openssl_verify_certificate_t *self, X509_STORE *store)
 {
-    *self = (ptls_openssl_verify_certificate_t){{verify_cert}};
+    *self = (ptls_openssl_verify_certificate_t){{verify_cert, default_signature_schemes}};
 
     if (store != NULL) {
         X509_STORE_up_ref(store);
@@ -1286,8 +1322,9 @@
     return NULL;
 }
 
-static int verify_raw_cert(ptls_verify_certificate_t *_self, ptls_t *tls, int (**verifier)(void *, ptls_iovec_t, ptls_iovec_t),
-                           void **verify_data, ptls_iovec_t *certs, size_t num_certs)
+static int verify_raw_cert(ptls_verify_certificate_t *_self, ptls_t *tls,
+                           int (**verifier)(void *, uint16_t algo, ptls_iovec_t, ptls_iovec_t), void **verify_data,
+                           ptls_iovec_t *certs, size_t num_certs)
 {
     ptls_openssl_raw_pubkey_verify_certificate_t *self = (ptls_openssl_raw_pubkey_verify_certificate_t *)_self;
     int ret = PTLS_ALERT_BAD_CERTIFICATE;
@@ -1323,7 +1360,7 @@
 int ptls_openssl_raw_pubkey_init_verify_certificate(ptls_openssl_raw_pubkey_verify_certificate_t *self, EVP_PKEY *expected_pubkey)
 {
     EVP_PKEY_up_ref(expected_pubkey);
-    *self = (ptls_openssl_raw_pubkey_verify_certificate_t){{verify_raw_cert}, expected_pubkey};
+    *self = (ptls_openssl_raw_pubkey_verify_certificate_t){{verify_raw_cert, default_signature_schemes}, expected_pubkey};
     return 0;
 }
 void ptls_openssl_raw_pubkey_dispose_verify_certificate(ptls_openssl_raw_pubkey_verify_certificate_t *self)
diff --git a/lib/picotls.c b/lib/picotls.c
index 0f21196..210d530 100644
--- a/lib/picotls.c
+++ b/lib/picotls.c
@@ -257,7 +257,7 @@
      * will be used by the client and the server (if require_client_authentication is set).
      */
     struct {
-        int (*cb)(void *verify_ctx, ptls_iovec_t data, ptls_iovec_t signature);
+        int (*cb)(void *verify_ctx, uint16_t algo, ptls_iovec_t data, ptls_iovec_t signature);
         void *verify_ctx;
     } certificate_verify;
     /**
@@ -1519,15 +1519,16 @@
     return ret;
 }
 
-static int push_signature_algorithms(ptls_buffer_t *sendbuf)
+static int push_signature_algorithms(ptls_verify_certificate_t *vc, ptls_buffer_t *sendbuf)
 {
+    /* The list sent when verify callback is not registered */
+    static const uint16_t default_algos[] = {PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256, PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256,
+                                             PTLS_SIGNATURE_RSA_PKCS1_SHA256, PTLS_SIGNATURE_RSA_PKCS1_SHA1, UINT16_MAX};
     int ret;
 
     ptls_buffer_push_block(sendbuf, 2, {
-        ptls_buffer_push16(sendbuf, PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256);
-        ptls_buffer_push16(sendbuf, PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256);
-        ptls_buffer_push16(sendbuf, PTLS_SIGNATURE_RSA_PKCS1_SHA256);
-        ptls_buffer_push16(sendbuf, PTLS_SIGNATURE_RSA_PKCS1_SHA1);
+        for (const uint16_t *p = vc != NULL ? vc->algos : default_algos; *p != UINT16_MAX; ++p)
+            ptls_buffer_push16(sendbuf, *p);
     });
 
     ret = 0;
@@ -2059,7 +2060,7 @@
                 });
             });
             buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SIGNATURE_ALGORITHMS, {
-                if ((ret = push_signature_algorithms(sendbuf)) != 0)
+                if ((ret = push_signature_algorithms(tls->ctx->verify_certificate, sendbuf)) != 0)
                     goto Exit;
             });
             buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SUPPORTED_GROUPS, {
@@ -2867,19 +2868,10 @@
         src = end;
     });
 
-    /* validate */
-    switch (algo) {
-    case PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256:
-    case PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256:
-        /* ok */
-        break;
-    default:
-        ret = PTLS_ALERT_ILLEGAL_PARAMETER;
-        goto Exit;
-    }
     signdata_size = build_certificate_verify_signdata(signdata, tls->key_schedule, context_string);
     if (tls->certificate_verify.cb != NULL) {
-        ret = tls->certificate_verify.cb(tls->certificate_verify.verify_ctx, ptls_iovec_init(signdata, signdata_size), signature);
+        ret = tls->certificate_verify.cb(tls->certificate_verify.verify_ctx, algo, ptls_iovec_init(signdata, signdata_size),
+                                         signature);
     } else {
         ret = 0;
     }
@@ -4094,7 +4086,7 @@
                 /* extensions */
                 ptls_buffer_push_block(sendbuf, 2, {
                     buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SIGNATURE_ALGORITHMS, {
-                        if ((ret = push_signature_algorithms(sendbuf)) != 0)
+                        if ((ret = push_signature_algorithms(tls->ctx->verify_certificate, sendbuf)) != 0)
                             goto Exit;
                     });
                 });
@@ -4387,7 +4379,7 @@
             free(tls->client.certificate_request.context.base);
     }
     if (tls->certificate_verify.cb != NULL) {
-        tls->certificate_verify.cb(tls->certificate_verify.verify_ctx, ptls_iovec_init(NULL, 0), ptls_iovec_init(NULL, 0));
+        tls->certificate_verify.cb(tls->certificate_verify.verify_ctx, 0, ptls_iovec_init(NULL, 0), ptls_iovec_init(NULL, 0));
     }
     if (tls->pending_handshake_secret != NULL) {
         ptls_clear_memory(tls->pending_handshake_secret, PTLS_MAX_DIGEST_SIZE);
diff --git a/t/assets/ec256-key-pair.pem b/t/assets/ec256-key-pair.pem
deleted file mode 100644
index ce786ed..0000000
--- a/t/assets/ec256-key-pair.pem
+++ /dev/null
@@ -1,5 +0,0 @@
------BEGIN EC PRIVATE KEY-----
-MHcCAQEEIBswGBU5+SsrFYQupnJ/GVf1bYhBEmJiBpxLL4jXZRrpoAoGCCqGSM49
-AwEHoUQDQgAEWF0BvlHl/ZVaoApefcN5+emI6cjSDbR3aP843VWgMLfxNqvmWut0
-KsoRQC2OHJ+Z8HoLZcNnA7Mc3/ypHSUqrw==
------END EC PRIVATE KEY-----
diff --git a/t/assets/ec256-pub.pem b/t/assets/ec256-pub.pem
deleted file mode 100644
index 76a3396..0000000
--- a/t/assets/ec256-pub.pem
+++ /dev/null
@@ -1,4 +0,0 @@
------BEGIN PUBLIC KEY-----
-MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWF0BvlHl/ZVaoApefcN5+emI6cjS
-DbR3aP843VWgMLfxNqvmWut0KsoRQC2OHJ+Z8HoLZcNnA7Mc3/ypHSUqrw==
------END PUBLIC KEY-----
diff --git a/t/assets/server.crt b/t/assets/rsa/cert.pem
similarity index 100%
rename from t/assets/server.crt
rename to t/assets/rsa/cert.pem
diff --git a/t/assets/server.key b/t/assets/rsa/key.pem
similarity index 100%
rename from t/assets/server.key
rename to t/assets/rsa/key.pem
diff --git a/t/assets/server.pub b/t/assets/rsa/pub.pem
similarity index 100%
rename from t/assets/server.pub
rename to t/assets/rsa/pub.pem
diff --git a/t/assets/secp256r1/cert.pem b/t/assets/secp256r1/cert.pem
new file mode 100644
index 0000000..ef2ae2c
--- /dev/null
+++ b/t/assets/secp256r1/cert.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICYDCCAUigAwIBAgIBATANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDEw9waWNv
+dGxzIHRlc3QgY2EwHhcNMTgwMjIzMDUzMTA0WhcNMjgwMjIxMDUzMTA0WjAbMRkw
+FwYDVQQDExB0ZXN0LmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
+QgAE2silQFS6M9oYqUF/SVPfYOamPbaOUzqf3RkUXqsDz7z7NpgWJI8HKW0V2E8w
+6Alk+xT8hnzUBsL9neiZP0iMK6N7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0E
+HxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFO4whhah
+0mmtZOTXd2uy/VxPAaK1MB8GA1UdIwQYMBaAFL95ypeyYHgglqpGV5zfp7Ij9SVj
+MA0GCSqGSIb3DQEBCwUAA4IBAQCPrJwBbYGqjK5dtRZ06ujrJluxZtVr1E15DW2H
+qba/dC3Bsi5StkvKDQFFOFga0mptIJhaUbBvLD8PEojtfAmldAAhPUvSLVSqU4tk
++R7qpYrnYV5WklI2PqBoWZx9s+hcS3du3ijtGJGpnDnSlsyYBYx03B4SWzi9Vsuj
+6OEqWivSMkXBEIUgbGs06maRDi64ZIefB7wjTyOtvonfCphH6WMC00H0LaTO3ePY
+QQj+30fA52OOH/BLxa6rwLo4PuOQnAi9dRy5uFRDHZlC4KK3dbsUA3ma9gfYpasr
+OnCLd4Vwipg4mzUJ9mJrKUqnp/k73tjIkFfydiojCwFoxpry
+-----END CERTIFICATE-----
diff --git a/t/assets/secp256r1/key.pem b/t/assets/secp256r1/key.pem
new file mode 100644
index 0000000..71ba2ed
--- /dev/null
+++ b/t/assets/secp256r1/key.pem
@@ -0,0 +1,8 @@
+-----BEGIN EC PARAMETERS-----
+BggqhkjOPQMBBw==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIMF0tPle/noBDr5K6DOyNhP8Zellkag5npqA+6vR/7o6oAoGCCqGSM49
+AwEHoUQDQgAE2silQFS6M9oYqUF/SVPfYOamPbaOUzqf3RkUXqsDz7z7NpgWJI8H
+KW0V2E8w6Alk+xT8hnzUBsL9neiZP0iMKw==
+-----END EC PRIVATE KEY-----
diff --git a/t/assets/secp256r1/pub.pem b/t/assets/secp256r1/pub.pem
new file mode 100644
index 0000000..0402946
--- /dev/null
+++ b/t/assets/secp256r1/pub.pem
@@ -0,0 +1,4 @@
+-----BEGIN PUBLIC KEY-----
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2silQFS6M9oYqUF/SVPfYOamPbaO
+Uzqf3RkUXqsDz7z7NpgWJI8HKW0V2E8w6Alk+xT8hnzUBsL9neiZP0iMKw==
+-----END PUBLIC KEY-----
diff --git a/t/assets/secp384r1/cert.pem b/t/assets/secp384r1/cert.pem
new file mode 100644
index 0000000..8d33af8
--- /dev/null
+++ b/t/assets/secp384r1/cert.pem
@@ -0,0 +1,65 @@
+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
+IQYDVQQDDBpzZWNwMzg0cjEudGVzdC5leGFtcGxlLmNvbTB2MBAGByqGSM49AgEG
+BSuBBAAiA2IABAEP8gHosiRYm0qAIUQsVhmG2EHz8X4AsgGU/zjF1imDNj5t3DiI
+k1/CEZ/eBGW9Qc+KPPZvC1sHUWzxWSCbFnk10g32fD+Gj8IdVC0r9SVMiahisZ+A
+H/HC3J5LprjFWqN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNT
+TCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFMPSpIyQI1TYn+1ZlIMJ
+DdUig6DxMB8GA1UdIwQYMBaAFL95ypeyYHgglqpGV5zfp7Ij9SVjMA0GCSqGSIb3
+DQEBCwUAA4IBAQA3yL5Ip2hXTqqSuRkN+IRLnZQyJrrUHU4fiGmgaNdPSAXHgCoE
+2Wd2TMBJk2kbIFjORIumVIpUoQCTUtGjraDP5hgwZCCpTIztwnp7esOMwGVPFnqx
+mz4DEGyfbDP8LdWgdZN4xtshXSMgFUY9u3cWQUNxWN7WDL61byeHw5GZPKf+SD99
+fQOstYzO8XAeT3xCKNiVmO1rFztRrP/uy3nBbC45TKv1yaJCa6gcOBdNSXi6EH8l
+XWz0ASAuF7/jQg97QwG31IUtDvI7yIJ2Q8qLS68cBRjv2nw8rTCQ/Uv3ABG9mX+f
+bWcOgMQI6c8vOOPZ/61+mVo5R3vBSrevVODF
+-----END CERTIFICATE-----
diff --git a/t/assets/secp384r1/key.pem b/t/assets/secp384r1/key.pem
new file mode 100644
index 0000000..849b07d
--- /dev/null
+++ b/t/assets/secp384r1/key.pem
@@ -0,0 +1,9 @@
+-----BEGIN EC PARAMETERS-----
+BgUrgQQAIg==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDDKRvwSCQcP5gx5uH0nUkITtNjhXTyWQOBxixOqvBviKE+8ypVluHbg
+Ya/evmF1A52gBwYFK4EEACKhZANiAAQBD/IB6LIkWJtKgCFELFYZhthB8/F+ALIB
+lP84xdYpgzY+bdw4iJNfwhGf3gRlvUHPijz2bwtbB1Fs8VkgmxZ5NdIN9nw/ho/C
+HVQtK/UlTImoYrGfgB/xwtyeS6a4xVo=
+-----END EC PRIVATE KEY-----
diff --git a/t/assets/secp384r1/pub.pem b/t/assets/secp384r1/pub.pem
new file mode 100644
index 0000000..2238c41
--- /dev/null
+++ b/t/assets/secp384r1/pub.pem
@@ -0,0 +1,5 @@
+-----BEGIN PUBLIC KEY-----
+MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEAQ/yAeiyJFibSoAhRCxWGYbYQfPxfgCy
+AZT/OMXWKYM2Pm3cOIiTX8IRn94EZb1Bz4o89m8LWwdRbPFZIJsWeTXSDfZ8P4aP
+wh1ULSv1JUyJqGKxn4Af8cLcnkumuMVa
+-----END PUBLIC KEY-----
diff --git a/t/assets/secp521r1/cert.pem b/t/assets/secp521r1/cert.pem
new file mode 100644
index 0000000..f9a963c
--- /dev/null
+++ b/t/assets/secp521r1/cert.pem
@@ -0,0 +1,68 @@
+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
+IQYDVQQDDBpzZWNwNTIxcjEudGVzdC5leGFtcGxlLmNvbTCBmzAQBgcqhkjOPQIB
+BgUrgQQAIwOBhgAEACyVCnKujpMyv58r9L/3HL0pM9Pxgx/07c5/f+SC1ukqYo8l
+IuLuC78nB3mn4wAB8PsT8pz8XQZoYBI552lj+HBiAHsTki9Y+amzcDo5QClzoqO6
+GtXN7HOILsqBgFUblf+av6k19OX185X+qnO8Pb1KwwviEWXDoku4QTIWoQjj4VGS
+o3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRl
+ZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUv1vbGGuBcMs5zEdTGHWLkJ5mWwgwHwYD
+VR0jBBgwFoAUv3nKl7JgeCCWqkZXnN+nsiP1JWMwDQYJKoZIhvcNAQELBQADggEB
+ABLBvD2LuyS84AGroVEVCp6qijpJWwzQ7Sk8AZQYr0mPUK+mdX/44/8OC2bKlUye
+LtfRBurbQjLXtn4UC8qGJVcBdnYkvKKna+GaPgD8qduzqeyJot3oMD3lZOvg1cZg
+yUynOmFpDQMR+WGufhaHf68Q2ggR11ShX5fxOniMWgz4ogz6rhfVjBqE1Xe3/cFf
++Vadeb66XaEXGhCU5wlJSOYI1+5oZBkXxZGwsOANQmfenDxfNNcd9ZioZimHZf62
+FatHHkIQYt3Lhg9oPl1RCiLDBz3ly0QriZwz3Y/biLQKjNfaoRlVGIcmwOIu7rcc
+ZDEnl40J122rLwtMlw9a0+s=
+-----END CERTIFICATE-----
diff --git a/t/assets/secp521r1/key.pem b/t/assets/secp521r1/key.pem
new file mode 100644
index 0000000..8ec1749
--- /dev/null
+++ b/t/assets/secp521r1/key.pem
@@ -0,0 +1,10 @@
+-----BEGIN EC PARAMETERS-----
+BgUrgQQAIw==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIHcAgEBBEIA2fGi1/ysuxTrmnDf6rdqCDzvPomZKLbOCNAckjas5FDIYvpYoYI9
+0gUpNTZWhL/50Wckg1kxmUNR10lRDb4bNKmgBwYFK4EEACOhgYkDgYYABAAslQpy
+ro6TMr+fK/S/9xy9KTPT8YMf9O3Of3/kgtbpKmKPJSLi7gu/Jwd5p+MAAfD7E/Kc
+/F0GaGASOedpY/hwYgB7E5IvWPmps3A6OUApc6KjuhrVzexziC7KgYBVG5X/mr+p
+NfTl9fOV/qpzvD29SsML4hFlw6JLuEEyFqEI4+FRkg==
+-----END EC PRIVATE KEY-----
diff --git a/t/assets/secp521r1/pub.pem b/t/assets/secp521r1/pub.pem
new file mode 100644
index 0000000..656e591
--- /dev/null
+++ b/t/assets/secp521r1/pub.pem
@@ -0,0 +1,6 @@
+-----BEGIN PUBLIC KEY-----
+MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQALJUKcq6OkzK/nyv0v/ccvSkz0/GD
+H/Ttzn9/5ILW6SpijyUi4u4LvycHeafjAAHw+xPynPxdBmhgEjnnaWP4cGIAexOS
+L1j5qbNwOjlAKXOio7oa1c3sc4guyoGAVRuV/5q/qTX05fXzlf6qc7w9vUrDC+IR
+ZcOiS7hBMhahCOPhUZI=
+-----END PUBLIC KEY-----
diff --git a/t/e2e.t b/t/e2e.t
index e6a4ff9..d72fc24 100755
--- a/t/e2e.t
+++ b/t/e2e.t
@@ -70,34 +70,23 @@
     is $resp, "hello";
 };
 
-subtest "raw-certificates-rsa" => sub {
-    my $guard = spawn_server("rsa", qw(-r - -i t/assets/hello.txt));
-    my $resp = `$cli -v -r t/assets/server.pub 127.0.0.1 $port 2> /dev/null`;
-    is $resp, "hello";
+# 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)) {
+        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`;
+            is $resp, "hello";
+        };
+    }
 };
 
-
-subtest "raw-certificates-ec" => sub {
-    my $guard = spawn_server("ec", qw(-r - -i t/assets/hello.txt));
-    my $resp = `$cli -v -r t/assets/ec256-pub.pem 127.0.0.1 $port 2> /dev/null`;
-    is $resp, "hello";
-};
-
-
 done_testing;
 
 sub spawn_server {
     my $key_type = shift;
-    my @cmd;
-    if ($key_type eq "rsa") {
-        my $ext = "crt";
-        $ext = "pub" if (grep(/^-r$/, @_));
-        @cmd = ($cli, "-k", "t/assets/server.key", "-c", "t/assets/server.$ext", @_, "127.0.0.1", $port);
-    } elsif ($key_type eq "ec") {
-        @cmd = ($cli, "-k", "t/assets/ec256-key-pair.pem", "-c", "t/assets/ec256-pub.pem", @_, "127.0.0.1", $port);
-    } else {
-        die "Unexpected key type: $key_type";
-    }
+    my $cert_type = grep(/^-r$/, @_) ? "pub" : "cert";
+    my @cmd = ($cli, "-k", "t/assets/$key_type/key.pem", "-c", "t/assets/$key_type/$cert_type.pem", @_, "127.0.0.1", $port);
     my $pid = fork;
     die "fork failed:$!"
         unless defined $pid;
diff --git a/t/openssl.c b/t/openssl.c
index fb52775..a6f0a06 100644
--- a/t/openssl.c
+++ b/t/openssl.c
@@ -129,47 +129,57 @@
 #endif
 }
 
+static void test_sign_verify(EVP_PKEY *key, const struct st_ptls_openssl_signature_scheme_t *schemes)
+{
+    for (size_t i = 0; schemes[i].scheme_id != UINT16_MAX; ++i) {
+        note("scheme 0x%04x", schemes[i].scheme_id);
+        const void *message = "hello world";
+        ptls_buffer_t sigbuf;
+        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);
+        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);
+
+        ptls_buffer_dispose(&sigbuf);
+    }
+}
+
 static void test_rsa_sign(void)
 {
     ptls_openssl_sign_certificate_t *sc = (ptls_openssl_sign_certificate_t *)ctx->sign_certificate;
-
-    const void *message = "hello world";
-    ptls_buffer_t sigbuf;
-    uint8_t sigbuf_small[1024];
-
-    ptls_buffer_init(&sigbuf, sigbuf_small, sizeof(sigbuf_small));
-    ok(do_sign(sc->key, &sigbuf, ptls_iovec_init(message, strlen(message)), EVP_sha256()) == 0);
-    EVP_PKEY_up_ref(sc->key);
-    ok(verify_sign(sc->key, ptls_iovec_init(message, strlen(message)), ptls_iovec_init(sigbuf.base, sigbuf.off)) == 0);
-
-    ptls_buffer_dispose(&sigbuf);
+    test_sign_verify(sc->key, sc->schemes);
 }
 
-static void test_ecdsa_sign(void)
+static void do_test_ecdsa_sign(int nid, const struct st_ptls_openssl_signature_scheme_t *schemes)
 {
     EVP_PKEY *pkey;
 
     { /* create pkey */
-        EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+        EC_KEY *eckey = EC_KEY_new_by_curve_name(nid);
         EC_KEY_generate_key(eckey);
         pkey = EVP_PKEY_new();
         EVP_PKEY_set1_EC_KEY(pkey, eckey);
         EC_KEY_free(eckey);
     }
 
-    const char *message = "hello world";
-    ptls_buffer_t sigbuf;
-    uint8_t sigbuf_small[1024];
-
-    ptls_buffer_init(&sigbuf, sigbuf_small, sizeof(sigbuf_small));
-    ok(do_sign(pkey, &sigbuf, ptls_iovec_init(message, strlen(message)), EVP_sha256()) == 0);
-    EVP_PKEY_up_ref(pkey);
-    ok(verify_sign(pkey, ptls_iovec_init(message, strlen(message)), ptls_iovec_init(sigbuf.base, sigbuf.off)) == 0);
-
-    ptls_buffer_dispose(&sigbuf);
+    test_sign_verify(pkey, schemes);
     EVP_PKEY_free(pkey);
 }
 
+static void test_ecdsa_sign(void)
+{
+    do_test_ecdsa_sign(NID_X9_62_prime256v1, secp256r1_signature_schemes);
+#if PTLS_OPENSSL_HAVE_SECP384R1
+    do_test_ecdsa_sign(NID_secp384r1, secp384r1_signature_schemes);
+#endif
+#if PTLS_OPENSSL_HAVE_SECP521R1
+    do_test_ecdsa_sign(NID_secp521r1, secp521r1_signature_schemes);
+#endif
+}
+
 static X509 *x509_from_pem(const char *pem)
 {
     BIO *bio = BIO_new_mem_buf((void *)pem, (int)strlen(pem));