Move the Digest/Sign split for SignatureAlgorithms to a lower level.

In order to delay the digest of the handshake transcript and unify
around message-based signing callbacks, a copy of the transcript is kept
around until we are sure there is no certificate authentication.

This removes support for SSL_PRIVATE_KEY_METHOD as a client in SSL 3.0.

Change-Id: If8999a19ca021b4ff439319ab91e2cd2103caa64
Reviewed-on: https://boringssl-review.googlesource.com/8561
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/err/ssl.errordata b/crypto/err/ssl.errordata
index 64249ad..700f9df 100644
--- a/crypto/err/ssl.errordata
+++ b/crypto/err/ssl.errordata
@@ -171,6 +171,7 @@
 SSL,238,UNSUPPORTED_COMPRESSION_ALGORITHM
 SSL,239,UNSUPPORTED_ELLIPTIC_CURVE
 SSL,240,UNSUPPORTED_PROTOCOL
+SSL,252,UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY
 SSL,241,WRONG_CERTIFICATE_TYPE
 SSL,242,WRONG_CIPHER_RETURNED
 SSL,243,WRONG_CURVE
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 641b94a..627c288 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -4655,6 +4655,7 @@
 #define SSL_R_X509_VERIFICATION_SETUP_PROBLEMS 249
 #define SSL_R_SHUTDOWN_WHILE_IN_INIT 250
 #define SSL_R_INVALID_OUTER_RECORD_TYPE 251
+#define SSL_R_UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY 252
 #define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000
 #define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010
 #define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020
diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c
index b4123dd..e3cd102 100644
--- a/ssl/handshake_client.c
+++ b/ssl/handshake_client.c
@@ -880,11 +880,10 @@
     goto f_err;
   }
 
-  /* If doing a full handshake with TLS 1.2, the server may request a client
-   * certificate which requires hashing the handshake transcript under a
-   * different hash. Otherwise, the handshake buffer may be released. */
-  if (ssl->hit || ssl3_protocol_version(ssl) < TLS1_2_VERSION ||
-      !ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
+  /* If doing a full handshake, the server may request a client certificate
+   * which requires hashing the handshake transcript. Otherwise, the handshake
+   * buffer may be released. */
+  if (ssl->hit || !ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
     ssl3_free_handshake_buffer(ssl);
   }
 
@@ -1120,7 +1119,6 @@
 }
 
 static int ssl3_get_server_key_exchange(SSL *ssl) {
-  EVP_MD_CTX md_ctx;
   int al, ok;
   EVP_PKEY *pkey = NULL;
   DH *dh = NULL;
@@ -1158,7 +1156,6 @@
 
   uint32_t alg_k = ssl->s3->tmp.new_cipher->algorithm_mkey;
   uint32_t alg_a = ssl->s3->tmp.new_cipher->algorithm_auth;
-  EVP_MD_CTX_init(&md_ctx);
 
   if (alg_a & SSL_aPSK) {
     CBS psk_identity_hint;
@@ -1336,21 +1333,25 @@
       goto f_err;
     }
 
-    const EVP_MD *md = tls12_get_hash(signature_algorithm);
-    if (md == NULL) {
-      al = SSL_AD_ILLEGAL_PARAMETER;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SIGNATURE_TYPE);
+    CBB transcript;
+    uint8_t *transcript_data;
+    size_t transcript_len;
+    if (!CBB_init(&transcript, 2*SSL3_RANDOM_SIZE + CBS_len(&parameter)) ||
+        !CBB_add_bytes(&transcript, ssl->s3->client_random, SSL3_RANDOM_SIZE) ||
+        !CBB_add_bytes(&transcript, ssl->s3->server_random, SSL3_RANDOM_SIZE) ||
+        !CBB_add_bytes(&transcript, CBS_data(&parameter), CBS_len(&parameter)) ||
+        !CBB_finish(&transcript, &transcript_data, &transcript_len)) {
+      CBB_cleanup(&transcript);
+      al = SSL_AD_INTERNAL_ERROR;
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
       goto f_err;
     }
-    int sig_ok = EVP_DigestVerifyInit(&md_ctx, NULL, md, NULL, pkey) &&
-                 EVP_DigestVerifyUpdate(&md_ctx, ssl->s3->client_random,
-                                        SSL3_RANDOM_SIZE) &&
-                 EVP_DigestVerifyUpdate(&md_ctx, ssl->s3->server_random,
-                                        SSL3_RANDOM_SIZE) &&
-                 EVP_DigestVerifyUpdate(&md_ctx, CBS_data(&parameter),
-                                        CBS_len(&parameter)) &&
-                 EVP_DigestVerifyFinal(&md_ctx, CBS_data(&signature),
-                                       CBS_len(&signature));
+
+    int sig_ok = ssl_public_key_verify(
+        ssl, CBS_data(&signature), CBS_len(&signature), signature_algorithm,
+        pkey, transcript_data, transcript_len);
+    OPENSSL_free(transcript_data);
+
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
     sig_ok = 1;
     ERR_clear_error();
@@ -1372,7 +1373,6 @@
     }
   }
   EVP_PKEY_free(pkey);
-  EVP_MD_CTX_cleanup(&md_ctx);
   return 1;
 
 f_err:
@@ -1382,7 +1382,6 @@
   DH_free(dh);
   EC_POINT_free(srvr_ecpoint);
   EC_KEY_free(ecdh);
-  EVP_MD_CTX_cleanup(&md_ctx);
   return -1;
 }
 
@@ -1840,23 +1839,44 @@
     goto err;
   }
 
-  size_t sig_len;
+  size_t sig_len = max_sig_len;
   enum ssl_private_key_result_t sign_result;
   if (ssl->state == SSL3_ST_CW_CERT_VRFY_A) {
-    /* Compute the digest. In TLS 1.1 and below, the digest type is also
-     * selected here. */
-    uint8_t digest[EVP_MAX_MD_SIZE];
-    size_t digest_len;
-    if (!ssl3_cert_verify_hash(ssl, digest, &digest_len, signature_algorithm)) {
-      goto err;
+    /* The SSL3 construction for CertificateVerify does not decompose into a
+     * single final digest and signature, and must be special-cased. */
+    if (ssl3_protocol_version(ssl) == SSL3_VERSION) {
+      if (ssl->cert->key_method != NULL) {
+        OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY);
+        goto err;
+      }
+
+      uint8_t digest[EVP_MAX_MD_SIZE];
+      size_t digest_len;
+      if (!ssl3_cert_verify_hash(ssl, digest, &digest_len,
+                                 signature_algorithm)) {
+        goto err;
+      }
+
+      sign_result = ssl_private_key_success;
+
+      EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(ssl->cert->privatekey, NULL);
+      if (pctx == NULL ||
+          !EVP_PKEY_sign_init(pctx) ||
+          !EVP_PKEY_sign(pctx, ptr, &sig_len, digest, digest_len)) {
+        EVP_PKEY_CTX_free(pctx);
+        sign_result = ssl_private_key_failure;
+        goto err;
+      }
+      EVP_PKEY_CTX_free(pctx);
+    } else {
+      sign_result = ssl_private_key_sign(
+          ssl, ptr, &sig_len, max_sig_len, signature_algorithm,
+          (const uint8_t *)ssl->s3->handshake_buffer->data,
+          ssl->s3->handshake_buffer->length);
     }
 
     /* The handshake buffer is no longer necessary. */
     ssl3_free_handshake_buffer(ssl);
-
-    /* Sign the digest. */
-    sign_result = ssl_private_key_sign(ssl, ptr, &sig_len, max_sig_len,
-                                       signature_algorithm, digest, digest_len);
   } else {
     assert(ssl->state == SSL3_ST_CW_CERT_VRFY_B);
     sign_result =
diff --git a/ssl/handshake_server.c b/ssl/handshake_server.c
index e0e1cd7..d302f11 100644
--- a/ssl/handshake_server.c
+++ b/ssl/handshake_server.c
@@ -1036,10 +1036,8 @@
     goto f_err;
   }
 
-  /* In TLS 1.2, client authentication requires hashing the handshake transcript
-   * under a different hash. Otherwise, release the handshake buffer. */
-  if (!ssl->s3->tmp.cert_request ||
-      ssl3_protocol_version(ssl) < TLS1_2_VERSION) {
+  /* Release the handshake buffer if client authentication isn't required. */
+  if (!ssl->s3->tmp.cert_request) {
     ssl3_free_handshake_buffer(ssl);
   }
 
@@ -1279,33 +1277,26 @@
     size_t sig_len;
     enum ssl_private_key_result_t sign_result;
     if (ssl->state == SSL3_ST_SW_KEY_EXCH_A) {
-      /* Compute the digest and sign it. */
-      uint8_t digest[EVP_MAX_MD_SIZE];
-      unsigned digest_len = 0;
-      EVP_MD_CTX md_ctx;
-      EVP_MD_CTX_init(&md_ctx);
-
-      const EVP_MD *md = tls12_get_hash(signature_algorithm);
-      if (md == NULL) {
-        OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SIGNATURE_TYPE);
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      CBB transcript;
+      uint8_t *transcript_data;
+      size_t transcript_len;
+      if (!CBB_init(&transcript,
+                    2*SSL3_RANDOM_SIZE + ssl->s3->tmp.server_params_len) ||
+          !CBB_add_bytes(&transcript, ssl->s3->client_random, SSL3_RANDOM_SIZE) ||
+          !CBB_add_bytes(&transcript, ssl->s3->server_random, SSL3_RANDOM_SIZE) ||
+          !CBB_add_bytes(&transcript, ssl->s3->tmp.server_params,
+                         ssl->s3->tmp.server_params_len) ||
+          !CBB_finish(&transcript, &transcript_data, &transcript_len)) {
+        CBB_cleanup(&transcript);
+        OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
         goto err;
       }
 
-      int digest_ret =
-          EVP_DigestInit_ex(&md_ctx, md, NULL) &&
-          EVP_DigestUpdate(&md_ctx, ssl->s3->client_random, SSL3_RANDOM_SIZE) &&
-          EVP_DigestUpdate(&md_ctx, ssl->s3->server_random, SSL3_RANDOM_SIZE) &&
-          EVP_DigestUpdate(&md_ctx, ssl->s3->tmp.server_params,
-                           ssl->s3->tmp.server_params_len) &&
-          EVP_DigestFinal_ex(&md_ctx, digest, &digest_len);
-      EVP_MD_CTX_cleanup(&md_ctx);
-      if (!digest_ret) {
-        goto err;
-      }
       sign_result = ssl_private_key_sign(ssl, ptr, &sig_len, max_sig_len,
-                                         signature_algorithm, digest,
-                                         digest_len);
+                                         signature_algorithm, transcript_data,
+                                         transcript_len);
+      OPENSSL_free(transcript_data);
     } else {
       assert(ssl->state == SSL3_ST_SW_KEY_EXCH_B);
       sign_result =
@@ -1876,9 +1867,6 @@
   CBS certificate_verify, signature;
   X509 *peer = ssl->session->peer;
   EVP_PKEY *pkey = NULL;
-  uint8_t digest[EVP_MAX_MD_SIZE];
-  size_t digest_length;
-  EVP_PKEY_CTX *pctx = NULL;
 
   /* Only RSA and ECDSA client certificates are supported, so a
    * CertificateVerify is required if and only if there's a client certificate.
@@ -1927,19 +1915,6 @@
     signature_algorithm = SSL_SIGN_ECDSA_SHA1;
   }
 
-  /* Compute the digest. */
-  if (!ssl3_cert_verify_hash(ssl, digest, &digest_length,
-                             signature_algorithm)) {
-    goto err;
-  }
-
-  /* The handshake buffer is no longer necessary, and we may hash the current
-   * message.*/
-  ssl3_free_handshake_buffer(ssl);
-  if (!ssl3_hash_current_message(ssl)) {
-    goto err;
-  }
-
   /* Parse and verify the signature. */
   if (!CBS_get_u16_length_prefixed(&certificate_verify, &signature) ||
       CBS_len(&certificate_verify) != 0) {
@@ -1948,22 +1923,40 @@
     goto f_err;
   }
 
-  pctx = EVP_PKEY_CTX_new(pkey, NULL);
-  if (pctx == NULL) {
-    goto err;
+  int sig_ok;
+  /* The SSL3 construction for CertificateVerify does not decompose into a
+   * single final digest and signature, and must be special-cased. */
+  if (ssl3_protocol_version(ssl) == SSL3_VERSION) {
+    if (ssl->cert->key_method != NULL) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY);
+      goto err;
+    }
+
+    uint8_t digest[EVP_MAX_MD_SIZE];
+    size_t digest_len;
+    if (!ssl3_cert_verify_hash(ssl, digest, &digest_len,
+                               signature_algorithm)) {
+      goto err;
+    }
+
+    const EVP_MD *md = tls12_get_hash(signature_algorithm);
+    assert(md != NULL);
+
+    EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(pkey, NULL);
+    sig_ok = pctx != NULL &&
+             EVP_PKEY_verify_init(pctx) &&
+             EVP_PKEY_CTX_set_signature_md(pctx, md) &&
+             EVP_PKEY_verify(pctx, CBS_data(&signature), CBS_len(&signature),
+                             digest, digest_len);
+
+    EVP_PKEY_CTX_free(pctx);
+  } else {
+    sig_ok = ssl_public_key_verify(
+        ssl, CBS_data(&signature), CBS_len(&signature), signature_algorithm,
+        pkey, (const uint8_t *)ssl->s3->handshake_buffer->data,
+        ssl->s3->handshake_buffer->length);
   }
 
-  const EVP_MD *md = tls12_get_hash(signature_algorithm);
-  if (md == NULL) {
-    al = SSL_AD_ILLEGAL_PARAMETER;
-    OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SIGNATURE_TYPE);
-    goto f_err;
-  }
-
-  int sig_ok = EVP_PKEY_verify_init(pctx) &&
-               EVP_PKEY_CTX_set_signature_md(pctx, md) &&
-               EVP_PKEY_verify(pctx, CBS_data(&signature), CBS_len(&signature),
-                               digest, digest_length);
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
   sig_ok = 1;
   ERR_clear_error();
@@ -1974,6 +1967,13 @@
     goto f_err;
   }
 
+  /* The handshake buffer is no longer necessary, and we may hash the current
+   * message.*/
+  ssl3_free_handshake_buffer(ssl);
+  if (!ssl3_hash_current_message(ssl)) {
+    goto err;
+  }
+
   ret = 1;
 
   if (0) {
@@ -1982,7 +1982,6 @@
   }
 
 err:
-  EVP_PKEY_CTX_free(pctx);
   EVP_PKEY_free(pkey);
 
   return ret;
diff --git a/ssl/internal.h b/ssl/internal.h
index d0eca69..ab79dcc 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -490,6 +490,12 @@
 enum ssl_private_key_result_t ssl_private_key_decrypt_complete(
     SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out);
 
+/* ssl_public_key_verify verifies that the |signature| is valid for the public
+ * key |pkey| and input |in|, using the |signature_algorithm| specified. */
+int ssl_public_key_verify(
+    SSL *ssl, const uint8_t *signature, size_t signature_len,
+    uint16_t signature_algorithm, EVP_PKEY *pkey,
+    const uint8_t *in, size_t in_len);
 
 /* Custom extensions */
 
@@ -865,7 +871,6 @@
              size_t label_len, const uint8_t *seed1, size_t seed1_len,
              const uint8_t *seed2, size_t seed2_len);
   int (*final_finish_mac)(SSL *ssl, int from_server, uint8_t *out);
-  int (*cert_verify_mac)(SSL *, int, uint8_t *);
 };
 
 /* lengths of messages */
@@ -1008,12 +1013,10 @@
  * handshake hash. It returns one on success and zero on allocation failure. */
 int ssl3_hash_current_message(SSL *ssl);
 
-/* ssl3_cert_verify_hash writes the CertificateVerify hash into the bytes
- * pointed to by |out| and writes the number of bytes to |*out_len|. |out| must
- * have room for EVP_MAX_MD_SIZE bytes. For TLS 1.2 and up,
- * |signature_algorithm| is used to determine the hash function, otherwise the
- * hash function depends on the private key type. It returns one on success and
- * zero on failure. */
+/* ssl3_cert_verify_hash writes the SSL 3.0 CertificateVerify hash into the
+ * bytes pointed to by |out| and writes the number of bytes to |*out_len|. |out|
+ * must have room for EVP_MAX_MD_SIZE bytes. It returns one on success and zero
+ * on failure. */
 int ssl3_cert_verify_hash(SSL *ssl, uint8_t *out, size_t *out_len,
                           uint16_t signature_algorithm);
 
diff --git a/ssl/s3_both.c b/ssl/s3_both.c
index 8dee4d8..f28e50f 100644
--- a/ssl/s3_both.c
+++ b/ssl/s3_both.c
@@ -463,55 +463,6 @@
                                     ssl->init_num + header_len);
 }
 
-/* ssl3_cert_verify_hash is documented as needing EVP_MAX_MD_SIZE because that
- * is sufficient pre-TLS1.2 as well. */
-OPENSSL_COMPILE_ASSERT(EVP_MAX_MD_SIZE > MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH,
-                       combined_tls_hash_fits_in_max);
-
-int ssl3_cert_verify_hash(SSL *ssl, uint8_t *out, size_t *out_len,
-                          uint16_t signature_algorithm) {
-  /* For TLS v1.2 send signature algorithm and signature using
-   * agreed digest and cached handshake records. Otherwise, use
-   * SHA1 or MD5 + SHA1 depending on key type.  */
-  if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
-    EVP_MD_CTX mctx;
-    unsigned len;
-
-    const EVP_MD *md = tls12_get_hash(signature_algorithm);
-    if (md == NULL) {
-      return 0;
-    }
-
-    EVP_MD_CTX_init(&mctx);
-    if (!EVP_DigestInit_ex(&mctx, md, NULL) ||
-        !EVP_DigestUpdate(&mctx, ssl->s3->handshake_buffer->data,
-                          ssl->s3->handshake_buffer->length) ||
-        !EVP_DigestFinal(&mctx, out, &len)) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_EVP_LIB);
-      EVP_MD_CTX_cleanup(&mctx);
-      return 0;
-    }
-    *out_len = len;
-  } else if (signature_algorithm == SSL_SIGN_RSA_PKCS1_MD5_SHA1) {
-    if (ssl->s3->enc_method->cert_verify_mac(ssl, NID_md5, out) == 0 ||
-        ssl->s3->enc_method->cert_verify_mac(ssl, NID_sha1,
-                                             out + MD5_DIGEST_LENGTH) == 0) {
-      return 0;
-    }
-    *out_len = MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH;
-  } else if (signature_algorithm == SSL_SIGN_ECDSA_SHA1) {
-    if (ssl->s3->enc_method->cert_verify_mac(ssl, NID_sha1, out) == 0) {
-      return 0;
-    }
-    *out_len = SHA_DIGEST_LENGTH;
-  } else {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
-  }
-
-  return 1;
-}
-
 int ssl_verify_alarm_type(long type) {
   int al;
 
diff --git a/ssl/s3_enc.c b/ssl/s3_enc.c
index 04aa08c..d70f373 100644
--- a/ssl/s3_enc.c
+++ b/ssl/s3_enc.c
@@ -146,10 +146,6 @@
 
 #include "internal.h"
 
-
-static int ssl3_handshake_mac(SSL *ssl, int md_nid, const char *sender,
-                              size_t sender_len, uint8_t *p);
-
 static int ssl3_prf(const SSL *ssl, uint8_t *out, size_t out_len,
                     const uint8_t *secret, size_t secret_len, const char *label,
                     size_t label_len, const uint8_t *seed1, size_t seed1_len,
@@ -294,31 +290,6 @@
   return 1;
 }
 
-static int ssl3_cert_verify_mac(SSL *ssl, int md_nid, uint8_t *p) {
-  return ssl3_handshake_mac(ssl, md_nid, NULL, 0, p);
-}
-
-static int ssl3_final_finish_mac(SSL *ssl, int from_server, uint8_t *out) {
-  const char *sender = from_server ? SSL3_MD_SERVER_FINISHED_CONST
-                                   : SSL3_MD_CLIENT_FINISHED_CONST;
-  const size_t sender_len = 4;
-  int ret, sha1len;
-  ret = ssl3_handshake_mac(ssl, NID_md5, sender, sender_len, out);
-  if (ret == 0) {
-    return 0;
-  }
-
-  out += ret;
-
-  sha1len = ssl3_handshake_mac(ssl, NID_sha1, sender, sender_len, out);
-  if (sha1len == 0) {
-    return 0;
-  }
-
-  ret += sha1len;
-  return ret;
-}
-
 static int ssl3_handshake_mac(SSL *ssl, int md_nid, const char *sender,
                               size_t sender_len, uint8_t *p) {
   unsigned int ret;
@@ -385,10 +356,57 @@
   return ret;
 }
 
+static int ssl3_final_finish_mac(SSL *ssl, int from_server, uint8_t *out) {
+  const char *sender = from_server ? SSL3_MD_SERVER_FINISHED_CONST
+                                   : SSL3_MD_CLIENT_FINISHED_CONST;
+  const size_t sender_len = 4;
+  int ret, sha1len;
+  ret = ssl3_handshake_mac(ssl, NID_md5, sender, sender_len, out);
+  if (ret == 0) {
+    return 0;
+  }
 
+  out += ret;
+
+  sha1len = ssl3_handshake_mac(ssl, NID_sha1, sender, sender_len, out);
+  if (sha1len == 0) {
+    return 0;
+  }
+
+  ret += sha1len;
+  return ret;
+}
+
+/* ssl3_cert_verify_hash is documented as needing EVP_MAX_MD_SIZE because that
+ * is sufficient pre-TLS1.2 as well. */
+OPENSSL_COMPILE_ASSERT(EVP_MAX_MD_SIZE > MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH,
+                       combined_tls_hash_fits_in_max);
+
+int ssl3_cert_verify_hash(SSL *ssl, uint8_t *out, size_t *out_len,
+                          uint16_t signature_algorithm) {
+  assert(ssl3_protocol_version(ssl) == SSL3_VERSION);
+
+  if (signature_algorithm == SSL_SIGN_RSA_PKCS1_MD5_SHA1) {
+    if (ssl3_handshake_mac(ssl, NID_md5, NULL, 0, out) == 0 ||
+        ssl3_handshake_mac(ssl, NID_sha1, NULL, 0,
+                           out + MD5_DIGEST_LENGTH) == 0) {
+      return 0;
+    }
+    *out_len = MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH;
+  } else if (signature_algorithm == SSL_SIGN_ECDSA_SHA1) {
+    if (ssl3_handshake_mac(ssl, NID_sha1, NULL, 0, out) == 0) {
+      return 0;
+    }
+    *out_len = SHA_DIGEST_LENGTH;
+  } else {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return 0;
+  }
+
+  return 1;
+}
 
 const SSL3_ENC_METHOD SSLv3_enc_data = {
     ssl3_prf,
     ssl3_final_finish_mac,
-    ssl3_cert_verify_mac,
 };
diff --git a/ssl/ssl_rsa.c b/ssl/ssl_rsa.c
index 66e73b4..a65f6f4 100644
--- a/ssl/ssl_rsa.c
+++ b/ssl/ssl_rsa.c
@@ -372,9 +372,24 @@
     return ssl_private_key_failure;
   }
 
+  EVP_MD_CTX mctx;
+  uint8_t hash[EVP_MAX_MD_SIZE];
+  unsigned hash_len;
+
+  EVP_MD_CTX_init(&mctx);
+  if (!EVP_DigestInit_ex(&mctx, md, NULL) ||
+      !EVP_DigestUpdate(&mctx, in, in_len) ||
+      !EVP_DigestFinal(&mctx, hash, &hash_len)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_EVP_LIB);
+    EVP_MD_CTX_cleanup(&mctx);
+    return 0;
+  }
+  EVP_MD_CTX_cleanup(&mctx);
+
+
   if (ssl->cert->key_method != NULL) {
-    return ssl->cert->key_method->sign(ssl, out, out_len, max_out, md, in,
-                                       in_len);
+    return ssl->cert->key_method->sign(ssl, out, out_len, max_out, md, hash,
+                                       hash_len);
   }
 
   enum ssl_private_key_result_t ret = ssl_private_key_failure;
@@ -386,7 +401,7 @@
   size_t len = max_out;
   if (!EVP_PKEY_sign_init(ctx) ||
       !EVP_PKEY_CTX_set_signature_md(ctx, md) ||
-      !EVP_PKEY_sign(ctx, out, &len, in, in_len)) {
+      !EVP_PKEY_sign(ctx, out, &len, hash, hash_len)) {
     goto end;
   }
   *out_len = len;
@@ -403,6 +418,23 @@
   return ssl->cert->key_method->sign_complete(ssl, out, out_len, max_out);
 }
 
+int ssl_public_key_verify(SSL *ssl, const uint8_t *signature,
+                          size_t signature_len, uint16_t signature_algorithm,
+                          EVP_PKEY *pkey, const uint8_t *in, size_t in_len) {
+  const EVP_MD *md = tls12_get_hash(signature_algorithm);
+  if (md == NULL) {
+    return 0;
+  }
+
+  EVP_MD_CTX md_ctx;
+  EVP_MD_CTX_init(&md_ctx);
+  int ret = EVP_DigestVerifyInit(&md_ctx, NULL, md, NULL, pkey) &&
+            EVP_DigestVerifyUpdate(&md_ctx, in, in_len) &&
+            EVP_DigestVerifyFinal(&md_ctx, signature, signature_len);
+  EVP_MD_CTX_cleanup(&md_ctx);
+  return ret;
+}
+
 enum ssl_private_key_result_t ssl_private_key_decrypt(
     SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
     const uint8_t *in, size_t in_len) {
diff --git a/ssl/t1_enc.c b/ssl/t1_enc.c
index b599207..453adf3 100644
--- a/ssl/t1_enc.c
+++ b/ssl/t1_enc.c
@@ -389,29 +389,6 @@
   return 1;
 }
 
-static int tls1_cert_verify_mac(SSL *ssl, int md_nid, uint8_t *out) {
-  const EVP_MD_CTX *ctx_template;
-  if (md_nid == NID_md5) {
-    ctx_template = &ssl->s3->handshake_md5;
-  } else if (md_nid == EVP_MD_CTX_type(&ssl->s3->handshake_hash)) {
-    ctx_template = &ssl->s3->handshake_hash;
-  } else {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_REQUIRED_DIGEST);
-    return 0;
-  }
-
-  EVP_MD_CTX ctx;
-  EVP_MD_CTX_init(&ctx);
-  if (!EVP_MD_CTX_copy_ex(&ctx, ctx_template)) {
-    EVP_MD_CTX_cleanup(&ctx);
-    return 0;
-  }
-  unsigned ret;
-  EVP_DigestFinal_ex(&ctx, out, &ret);
-  EVP_MD_CTX_cleanup(&ctx);
-  return ret;
-}
-
 static int append_digest(const EVP_MD_CTX *ctx, uint8_t *out, size_t *out_len,
                          size_t max_out) {
   int ret = 0;
@@ -558,5 +535,4 @@
 const SSL3_ENC_METHOD TLSv1_enc_data = {
     tls1_prf,
     tls1_final_finish_mac,
-    tls1_cert_verify_mac,
 };