Add server-side support for asynchronous RSA decryption.

Change-Id: I6df623f3e9bc88acc52043f16b34649b7af67663
Reviewed-on: https://boringssl-review.googlesource.com/5531
Reviewed-by: Adam Langley <alangley@gmail.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 6c9c645..ae40437 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -966,7 +966,37 @@
    * it is an error to call |sign_complete| if there is no pending |sign|
    * operation in progress on |ssl|. */
   enum ssl_private_key_result_t (*sign_complete)(SSL *ssl, uint8_t *out,
-                                                 size_t *out_len, size_t max_out);
+                                                 size_t *out_len,
+                                                 size_t max_out);
+
+  /* decrypt decrypts |in_len| bytes of encrypted data from |in|. On success it
+   * returns |ssl_private_key_success|, writes at most |max_out| bytes of
+   * decrypted data to |out| and sets |*out_len| to the actual number of bytes
+   * written. On failure it returns |ssl_private_key_failure|. If the operation
+   * has not completed, it returns |ssl_private_key_retry|. The caller should
+   * arrange for the high-level operation on |ssl| to be retried when the
+   * operation is completed, which will result in a call to |decrypt_complete|.
+   * This function only works with RSA keys and should perform a raw RSA
+   * decryption operation with no padding.
+   *
+   * It is an error to call |decrypt| while another private key operation is in
+   * progress on |ssl|. */
+  enum ssl_private_key_result_t (*decrypt)(SSL *ssl, uint8_t *out,
+                                           size_t *out_len, size_t max_out,
+                                           const uint8_t *in, size_t in_len);
+
+  /* decrypt_complete completes a pending |decrypt| operation. If the operation
+   * has completed, it returns |ssl_private_key_success| and writes the result
+   * to |out| as in |decrypt|. Otherwise, it returns |ssl_private_key_failure|
+   * on failure and |ssl_private_key_retry| if the operation is still in
+   * progress.
+   *
+   * |decrypt_complete| may be called arbitrarily many times before completion,
+   * but it is an error to call |decrypt_complete| if there is no pending
+   * |decrypt| operation in progress on |ssl|. */
+  enum ssl_private_key_result_t (*decrypt_complete)(SSL *ssl, uint8_t *out,
+                                                    size_t *out_len,
+                                                    size_t max_out);
 } SSL_PRIVATE_KEY_METHOD;
 
 /* SSL_set_private_key_method configures a custom private key on |ssl|.
diff --git a/include/openssl/ssl3.h b/include/openssl/ssl3.h
index dc53f07..0d013d5 100644
--- a/include/openssl/ssl3.h
+++ b/include/openssl/ssl3.h
@@ -382,6 +382,7 @@
 #define SSL3_ST_SR_CERT_B (0x181 | SSL_ST_ACCEPT)
 #define SSL3_ST_SR_KEY_EXCH_A (0x190 | SSL_ST_ACCEPT)
 #define SSL3_ST_SR_KEY_EXCH_B (0x191 | SSL_ST_ACCEPT)
+#define SSL3_ST_SR_KEY_EXCH_C (0x192 | SSL_ST_ACCEPT)
 #define SSL3_ST_SR_CERT_VRFY_A (0x1A0 | SSL_ST_ACCEPT)
 #define SSL3_ST_SR_CERT_VRFY_B (0x1A1 | SSL_ST_ACCEPT)
 #define SSL3_ST_SR_CHANGE (0x1B0 | SSL_ST_ACCEPT)
diff --git a/ssl/d1_srvr.c b/ssl/d1_srvr.c
index 267bdf1..f1e8826 100644
--- a/ssl/d1_srvr.c
+++ b/ssl/d1_srvr.c
@@ -330,6 +330,7 @@
 
       case SSL3_ST_SR_KEY_EXCH_A:
       case SSL3_ST_SR_KEY_EXCH_B:
+      case SSL3_ST_SR_KEY_EXCH_C:
         ret = ssl3_get_client_key_exchange(s);
         if (ret <= 0) {
           goto end;
diff --git a/ssl/internal.h b/ssl/internal.h
index 4d0f968..0a6c31c 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -464,6 +464,13 @@
 enum ssl_private_key_result_t ssl_private_key_sign_complete(
     SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out);
 
+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);
+
+enum ssl_private_key_result_t ssl_private_key_decrypt_complete(
+    SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out);
+
 
 /* Custom extensions */
 
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index 2c915e6..c9b8172 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -399,6 +399,7 @@
 
       case SSL3_ST_SR_KEY_EXCH_A:
       case SSL3_ST_SR_KEY_EXCH_B:
+      case SSL3_ST_SR_KEY_EXCH_C:
         ret = ssl3_get_client_key_exchange(s);
         if (ret <= 0) {
           goto end;
@@ -1586,16 +1587,13 @@
 }
 
 int ssl3_get_client_key_exchange(SSL *s) {
-  int al, ok;
-  long n;
+  int al;
   CBS client_key_exchange;
   uint32_t alg_k;
   uint32_t alg_a;
   uint8_t *premaster_secret = NULL;
   size_t premaster_secret_len = 0;
-  RSA *rsa = NULL;
   uint8_t *decrypt_buf = NULL;
-  EVP_PKEY *pkey = NULL;
   BIGNUM *pub = NULL;
   DH *dh_srvr;
 
@@ -1606,17 +1604,18 @@
   unsigned int psk_len = 0;
   uint8_t psk[PSK_MAX_PSK_LEN];
 
-  n = s->method->ssl_get_message(s, SSL3_ST_SR_KEY_EXCH_A,
-                                 SSL3_ST_SR_KEY_EXCH_B,
-                                 SSL3_MT_CLIENT_KEY_EXCHANGE, 2048, /* ??? */
-                                 ssl_hash_message, &ok);
-
-  if (!ok) {
-    return n;
+  if (s->state == SSL3_ST_SR_KEY_EXCH_A ||
+      s->state == SSL3_ST_SR_KEY_EXCH_B) {
+    int ok;
+    const long n = s->method->ssl_get_message(
+        s, SSL3_ST_SR_KEY_EXCH_A, SSL3_ST_SR_KEY_EXCH_B,
+        SSL3_MT_CLIENT_KEY_EXCHANGE, 2048 /* ??? */, ssl_hash_message, &ok);
+    if (!ok) {
+      return n;
+    }
   }
 
-  CBS_init(&client_key_exchange, s->init_msg, n);
-
+  CBS_init(&client_key_exchange, s->init_msg, s->init_num);
   alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
   alg_a = s->s3->tmp.new_cipher->algorithm_auth;
 
@@ -1673,57 +1672,8 @@
     CBS encrypted_premaster_secret;
     uint8_t rand_premaster_secret[SSL_MAX_MASTER_KEY_LENGTH];
     uint8_t good;
-    size_t rsa_size, decrypt_len, premaster_index, j;
-
-    pkey = s->cert->privatekey;
-    if (pkey == NULL || pkey->type != EVP_PKEY_RSA || pkey->pkey.rsa == NULL) {
-      al = SSL_AD_HANDSHAKE_FAILURE;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_RSA_CERTIFICATE);
-      goto f_err;
-    }
-    rsa = pkey->pkey.rsa;
-
-    /* TLS and [incidentally] DTLS{0xFEFF} */
-    if (s->version > SSL3_VERSION) {
-      CBS copy = client_key_exchange;
-      if (!CBS_get_u16_length_prefixed(&client_key_exchange,
-                                       &encrypted_premaster_secret) ||
-          CBS_len(&client_key_exchange) != 0) {
-        if (!(s->options & SSL_OP_TLS_D5_BUG)) {
-          al = SSL_AD_DECODE_ERROR;
-          OPENSSL_PUT_ERROR(SSL, SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG);
-          goto f_err;
-        } else {
-          CRYPTO_STATIC_MUTEX_lock_write(&g_d5_bug_lock);
-          g_d5_bug_use_count++;
-          CRYPTO_STATIC_MUTEX_unlock(&g_d5_bug_lock);
-
-          encrypted_premaster_secret = copy;
-        }
-      }
-    } else {
-      encrypted_premaster_secret = client_key_exchange;
-    }
-
-    /* Reject overly short RSA keys because we want to be sure that the buffer
-     * size makes it safe to iterate over the entire size of a premaster secret
-     * (SSL_MAX_MASTER_KEY_LENGTH). The actual expected size is larger due to
-     * RSA padding, but the bound is sufficient to be safe. */
-    rsa_size = RSA_size(rsa);
-    if (rsa_size < SSL_MAX_MASTER_KEY_LENGTH) {
-      al = SSL_AD_DECRYPT_ERROR;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED);
-      goto f_err;
-    }
-
-    /* We must not leak whether a decryption failure occurs because of
-     * Bleichenbacher's attack on PKCS #1 v1.5 RSA padding (see RFC 2246,
-     * section 7.4.7.1). The code follows that advice of the TLS RFC and
-     * generates a random premaster secret for the case that the decrypt fails.
-     * See https://tools.ietf.org/html/rfc5246#section-7.4.7.1 */
-    if (!RAND_bytes(rand_premaster_secret, sizeof(rand_premaster_secret))) {
-      goto err;
-    }
+    size_t decrypt_len, premaster_index, j;
+    const size_t rsa_size = ssl_private_key_max_signature_len(s);
 
     /* Allocate a buffer large enough for an RSA decryption. */
     decrypt_buf = OPENSSL_malloc(rsa_size);
@@ -1732,13 +1682,72 @@
       goto err;
     }
 
-    /* Decrypt with no padding. PKCS#1 padding will be removed as part of the
-     * timing-sensitive code below. */
-    if (!RSA_decrypt(rsa, &decrypt_len, decrypt_buf, rsa_size,
-                     CBS_data(&encrypted_premaster_secret),
-                     CBS_len(&encrypted_premaster_secret), RSA_NO_PADDING)) {
-      goto err;
+    enum ssl_private_key_result_t decrypt_result;
+    if (s->state == SSL3_ST_SR_KEY_EXCH_B) {
+      if (!ssl_has_private_key(s) || ssl_private_key_type(s) != EVP_PKEY_RSA) {
+        al = SSL_AD_HANDSHAKE_FAILURE;
+        OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_RSA_CERTIFICATE);
+        goto f_err;
+      }
+      /* TLS and [incidentally] DTLS{0xFEFF} */
+      if (s->version > SSL3_VERSION) {
+        CBS copy = client_key_exchange;
+        if (!CBS_get_u16_length_prefixed(&client_key_exchange,
+                                         &encrypted_premaster_secret) ||
+            CBS_len(&client_key_exchange) != 0) {
+          if (!(s->options & SSL_OP_TLS_D5_BUG)) {
+            al = SSL_AD_DECODE_ERROR;
+            OPENSSL_PUT_ERROR(SSL,
+                              SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG);
+            goto f_err;
+          } else {
+            CRYPTO_STATIC_MUTEX_lock_write(&g_d5_bug_lock);
+            g_d5_bug_use_count++;
+            CRYPTO_STATIC_MUTEX_unlock(&g_d5_bug_lock);
+
+            encrypted_premaster_secret = copy;
+          }
+        }
+      } else {
+        encrypted_premaster_secret = client_key_exchange;
+      }
+
+      /* Reject overly short RSA keys because we want to be sure that the buffer
+       * size makes it safe to iterate over the entire size of a premaster
+       * secret (SSL_MAX_MASTER_KEY_LENGTH). The actual expected size is larger
+       * due to RSA padding, but the bound is sufficient to be safe. */
+      if (rsa_size < SSL_MAX_MASTER_KEY_LENGTH) {
+        al = SSL_AD_DECRYPT_ERROR;
+        OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED);
+        goto f_err;
+      }
+
+      /* Decrypt with no padding. PKCS#1 padding will be removed as part of the
+       * timing-sensitive code below. */
+      decrypt_result = ssl_private_key_decrypt(
+          s, decrypt_buf, &decrypt_len, rsa_size,
+          CBS_data(&encrypted_premaster_secret),
+          CBS_len(&encrypted_premaster_secret));
+    } else {
+      assert(s->state == SSL3_ST_SR_KEY_EXCH_C);
+      /* Complete async decrypt. */
+      decrypt_result = ssl_private_key_decrypt_complete(
+          s, decrypt_buf, &decrypt_len, rsa_size);
     }
+
+    switch (decrypt_result) {
+      case ssl_private_key_success:
+        s->rwstate = SSL_NOTHING;
+        break;
+      case ssl_private_key_failure:
+        s->rwstate = SSL_NOTHING;
+        goto err;
+      case ssl_private_key_retry:
+        s->rwstate = SSL_PRIVATE_KEY_OPERATION;
+        s->state = SSL3_ST_SR_KEY_EXCH_C;
+        goto err;
+    }
+
     if (decrypt_len != rsa_size) {
       /* This should never happen, but do a check so we do not read
        * uninitialized memory. */
@@ -1782,6 +1791,15 @@
     good &= constant_time_eq_8(premaster_secret[1],
                                (unsigned)(s->client_version & 0xff));
 
+    /* We must not leak whether a decryption failure occurs because of
+     * Bleichenbacher's attack on PKCS #1 v1.5 RSA padding (see RFC 2246,
+     * section 7.4.7.1). The code follows that advice of the TLS RFC and
+     * generates a random premaster secret for the case that the decrypt
+     * fails. See https://tools.ietf.org/html/rfc5246#section-7.4.7.1 */
+    if (!RAND_bytes(rand_premaster_secret, sizeof(rand_premaster_secret))) {
+      goto err;
+    }
+
     /* Now copy rand_premaster_secret over premaster_secret using
      * decrypt_good_mask. */
     for (j = 0; j < sizeof(rand_premaster_secret); j++) {
diff --git a/ssl/ssl_rsa.c b/ssl/ssl_rsa.c
index ccd3858..512a41f 100644
--- a/ssl/ssl_rsa.c
+++ b/ssl/ssl_rsa.c
@@ -376,3 +376,33 @@
   /* Only custom keys may be asynchronous. */
   return ssl->cert->key_method->sign_complete(ssl, out, out_len, max_out);
 }
+
+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) {
+  if (ssl->cert->key_method != NULL) {
+    return ssl->cert->key_method->decrypt(ssl, out, out_len, max_out, in,
+                                          in_len);
+  }
+
+  if (ssl_private_key_type(ssl) != EVP_PKEY_RSA) {
+    /* Decrypt operations are only supported for RSA keys. */
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return ssl_private_key_failure;
+  }
+
+  enum ssl_private_key_result_t ret = ssl_private_key_failure;
+  RSA *rsa = ssl->cert->privatekey->pkey.rsa;
+  /* Decrypt with no padding. PKCS#1 padding will be removed as part
+   * of the timing-sensitive code by the caller. */
+  if (RSA_decrypt(rsa, out_len, out, max_out, in, in_len, RSA_NO_PADDING)) {
+    ret = ssl_private_key_success;
+  }
+  return ret;
+}
+
+enum ssl_private_key_result_t ssl_private_key_decrypt_complete(
+    SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out) {
+  /* Only custom keys may be asynchronous. */
+  return ssl->cert->key_method->decrypt_complete(ssl, out, out_len, max_out);
+}
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 17a91ad..3f4e9cf 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -97,10 +97,10 @@
   bool handshake_done = false;
   // private_key is the underlying private key used when testing custom keys.
   ScopedEVP_PKEY private_key;
-  std::vector<uint8_t> signature;
-  // signature_retries is the number of times an asynchronous sign operation has
-  // been retried.
-  unsigned signature_retries = 0;
+  std::vector<uint8_t> private_key_result;
+  // private_key_retries is the number of times an asynchronous private key
+  // operation has been retried.
+  unsigned private_key_retries = 0;
   bool got_new_session = false;
 };
 
@@ -153,7 +153,7 @@
     SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
     const EVP_MD *md, const uint8_t *in, size_t in_len) {
   TestState *test_state = GetTestState(ssl);
-  if (!test_state->signature.empty()) {
+  if (!test_state->private_key_result.empty()) {
     fprintf(stderr, "AsyncPrivateKeySign called with operation pending.\n");
     abort();
   }
@@ -171,42 +171,103 @@
       !EVP_PKEY_sign(ctx.get(), nullptr, &len, in, in_len)) {
     return ssl_private_key_failure;
   }
-  test_state->signature.resize(len);
-  if (!EVP_PKEY_sign(ctx.get(), bssl::vector_data(&test_state->signature), &len,
-                     in, in_len)) {
+  test_state->private_key_result.resize(len);
+  if (!EVP_PKEY_sign(ctx.get(), bssl::vector_data(
+          &test_state->private_key_result), &len, in, in_len)) {
     return ssl_private_key_failure;
   }
-  test_state->signature.resize(len);
+  test_state->private_key_result.resize(len);
 
-  // The signature will be released asynchronously in |AsyncPrivateKeySignComplete|.
+  // The signature will be released asynchronously in
+  // |AsyncPrivateKeySignComplete|.
   return ssl_private_key_retry;
 }
 
 static ssl_private_key_result_t AsyncPrivateKeySignComplete(
     SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out) {
   TestState *test_state = GetTestState(ssl);
-  if (test_state->signature.empty()) {
+  if (test_state->private_key_result.empty()) {
     fprintf(stderr,
             "AsyncPrivateKeySignComplete called without operation pending.\n");
     abort();
   }
 
-  if (test_state->signature_retries < 2) {
+  if (test_state->private_key_retries < 2) {
     // Only return the signature on the second attempt, to test both incomplete
     // |sign| and |sign_complete|.
     return ssl_private_key_retry;
   }
 
-  if (max_out < test_state->signature.size()) {
+  if (max_out < test_state->private_key_result.size()) {
     fprintf(stderr, "Output buffer too small.\n");
     return ssl_private_key_failure;
   }
-  memcpy(out, bssl::vector_data(&test_state->signature),
-         test_state->signature.size());
-  *out_len = test_state->signature.size();
+  memcpy(out, bssl::vector_data(&test_state->private_key_result),
+         test_state->private_key_result.size());
+  *out_len = test_state->private_key_result.size();
 
-  test_state->signature.clear();
-  test_state->signature_retries = 0;
+  test_state->private_key_result.clear();
+  test_state->private_key_retries = 0;
+  return ssl_private_key_success;
+}
+
+static ssl_private_key_result_t AsyncPrivateKeyDecrypt(
+    SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
+    const uint8_t *in, size_t in_len) {
+  TestState *test_state = GetTestState(ssl);
+  if (!test_state->private_key_result.empty()) {
+    fprintf(stderr,
+            "AsyncPrivateKeyDecrypt called with operation pending.\n");
+    abort();
+  }
+
+  EVP_PKEY *pkey = test_state->private_key.get();
+  if (pkey->type != EVP_PKEY_RSA || pkey->pkey.rsa == NULL) {
+    fprintf(stderr,
+            "AsyncPrivateKeyDecrypt called with incorrect key type.\n");
+    abort();
+  }
+  RSA *rsa = pkey->pkey.rsa;
+  test_state->private_key_result.resize(RSA_size(rsa));
+  if (!RSA_decrypt(rsa, out_len,
+                   bssl::vector_data(&test_state->private_key_result),
+                   RSA_size(rsa), in, in_len, RSA_NO_PADDING)) {
+    return ssl_private_key_failure;
+  }
+
+  test_state->private_key_result.resize(*out_len);
+
+  // The decryption will be released asynchronously in
+  // |AsyncPrivateKeyDecryptComplete|.
+  return ssl_private_key_retry;
+}
+
+static ssl_private_key_result_t AsyncPrivateKeyDecryptComplete(
+    SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out) {
+  TestState *test_state = GetTestState(ssl);
+  if (test_state->private_key_result.empty()) {
+    fprintf(stderr,
+            "AsyncPrivateKeyDecryptComplete called without operation "
+            "pending.\n");
+    abort();
+  }
+
+  if (test_state->private_key_retries < 2) {
+    // Only return the decryption on the second attempt, to test both incomplete
+    // |decrypt| and |decrypt_complete|.
+    return ssl_private_key_retry;
+  }
+
+  if (max_out < test_state->private_key_result.size()) {
+    fprintf(stderr, "Output buffer too small.\n");
+    return ssl_private_key_failure;
+  }
+  memcpy(out, bssl::vector_data(&test_state->private_key_result),
+         test_state->private_key_result.size());
+  *out_len = test_state->private_key_result.size();
+
+  test_state->private_key_result.clear();
+  test_state->private_key_retries = 0;
   return ssl_private_key_success;
 }
 
@@ -215,6 +276,8 @@
     AsyncPrivateKeyMaxSignatureLen,
     AsyncPrivateKeySign,
     AsyncPrivateKeySignComplete,
+    AsyncPrivateKeyDecrypt,
+    AsyncPrivateKeyDecryptComplete
 };
 
 template<typename T>
@@ -250,7 +313,7 @@
   }
 
   if (!config->key_file.empty()) {
-    if (config->use_async_private_key) {
+    if (config->async) {
       test_state->private_key = LoadPrivateKey(config->key_file.c_str());
       if (!test_state->private_key) {
         return false;
@@ -789,7 +852,7 @@
       // The handshake will resume without a second call to the early callback.
       return InstallCertificate(ssl);
     case SSL_ERROR_WANT_PRIVATE_KEY_OPERATION:
-      test_state->signature_retries++;
+      test_state->private_key_retries++;
       return true;
     default:
       return false;
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index fb1e46f..17fdff9 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -2585,7 +2585,7 @@
 	// TLS client auth.
 	tests = append(tests, testCase{
 		testType: clientTest,
-		name:     "ClientAuth-Client",
+		name:     "ClientAuth-RSA-Client",
 		config: Config{
 			ClientAuth: RequireAnyClientCert,
 		},
@@ -2594,35 +2594,50 @@
 			"-key-file", path.Join(*resourceDir, rsaKeyFile),
 		},
 	})
+	tests = append(tests, testCase{
+		testType: clientTest,
+		name:     "ClientAuth-ECDSA-Client",
+		config: Config{
+			ClientAuth: RequireAnyClientCert,
+		},
+		flags: []string{
+			"-cert-file", path.Join(*resourceDir, ecdsaCertificateFile),
+			"-key-file", path.Join(*resourceDir, ecdsaKeyFile),
+		},
+	})
 	if async {
+		// Test async keys against each key exchange.
 		tests = append(tests, testCase{
-			testType: clientTest,
-			name:     "ClientAuth-Client-AsyncKey",
+			testType: serverTest,
+			name:     "Basic-Server-RSA",
 			config: Config{
-				ClientAuth: RequireAnyClientCert,
+				CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
 			},
 			flags: []string{
 				"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
 				"-key-file", path.Join(*resourceDir, rsaKeyFile),
-				"-use-async-private-key",
 			},
 		})
 		tests = append(tests, testCase{
 			testType: serverTest,
-			name:     "Basic-Server-RSAAsyncKey",
+			name:     "Basic-Server-ECDHE-RSA",
+			config: Config{
+				CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			},
 			flags: []string{
 				"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
 				"-key-file", path.Join(*resourceDir, rsaKeyFile),
-				"-use-async-private-key",
 			},
 		})
 		tests = append(tests, testCase{
 			testType: serverTest,
-			name:     "Basic-Server-ECDSAAsyncKey",
+			name:     "Basic-Server-ECDHE-ECDSA",
+			config: Config{
+				CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
+			},
 			flags: []string{
 				"-cert-file", path.Join(*resourceDir, ecdsaCertificateFile),
 				"-key-file", path.Join(*resourceDir, ecdsaKeyFile),
-				"-use-async-private-key",
 			},
 		})
 	}
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index 54492df..abec0e1 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -78,7 +78,6 @@
   { "-use-export-context", &TestConfig::use_export_context },
   { "-no-legacy-server-connect", &TestConfig::no_legacy_server_connect },
   { "-tls-unique", &TestConfig::tls_unique },
-  { "-use-async-private-key", &TestConfig::use_async_private_key },
   { "-expect-ticket-renewal", &TestConfig::expect_ticket_renewal },
   { "-expect-no-session", &TestConfig::expect_no_session },
   { "-use-ticket-callback", &TestConfig::use_ticket_callback },
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index af1dd80..23fa1f11 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -79,7 +79,6 @@
   bool use_export_context = false;
   bool no_legacy_server_connect = false;
   bool tls_unique = false;
-  bool use_async_private_key = false;
   bool expect_ticket_renewal = false;
   bool expect_no_session = false;
   bool use_ticket_callback = false;