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/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;