Add server-side support for asynchronous signing.

The RSA key exchange needs decryption and is still unsupported.

Change-Id: I8c13b74e25a5424356afbe6e97b5f700a56de41f
Reviewed-on: https://boringssl-review.googlesource.com/5467
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/ssl3.h b/include/openssl/ssl3.h
index faf69ab..0694e56 100644
--- a/include/openssl/ssl3.h
+++ b/include/openssl/ssl3.h
@@ -634,6 +634,8 @@
 #define SSL3_ST_SW_CERT_B (0x141 | SSL_ST_ACCEPT)
 #define SSL3_ST_SW_KEY_EXCH_A (0x150 | SSL_ST_ACCEPT)
 #define SSL3_ST_SW_KEY_EXCH_B (0x151 | SSL_ST_ACCEPT)
+#define SSL3_ST_SW_KEY_EXCH_C (0x152 | SSL_ST_ACCEPT)
+#define SSL3_ST_SW_KEY_EXCH_D (0x153 | SSL_ST_ACCEPT)
 #define SSL3_ST_SW_CERT_REQ_A (0x160 | SSL_ST_ACCEPT)
 #define SSL3_ST_SW_CERT_REQ_B (0x161 | SSL_ST_ACCEPT)
 #define SSL3_ST_SW_SRVR_DONE_A (0x170 | SSL_ST_ACCEPT)
diff --git a/ssl/d1_srvr.c b/ssl/d1_srvr.c
index 32aa591..7a7d1ea 100644
--- a/ssl/d1_srvr.c
+++ b/ssl/d1_srvr.c
@@ -246,6 +246,8 @@
 
       case SSL3_ST_SW_KEY_EXCH_A:
       case SSL3_ST_SW_KEY_EXCH_B:
+      case SSL3_ST_SW_KEY_EXCH_C:
+      case SSL3_ST_SW_KEY_EXCH_D:
         alg_a = s->s3->tmp.new_cipher->algorithm_auth;
 
         /* Send a ServerKeyExchange message if:
diff --git a/ssl/internal.h b/ssl/internal.h
index f19a265..a1600f8 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -346,6 +346,10 @@
 
 /* Private key operations. */
 
+/* ssl_has_private_key returns one if |ssl| has a private key
+ * configured and zero otherwise. */
+int ssl_has_private_key(SSL *ssl);
+
 /* ssl_private_key_* call the corresponding function on the
  * |SSL_PRIVATE_KEY_METHOD| for |ssl|, if configured. Otherwise, they implement
  * the operation with |EVP_PKEY|. */
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index cf98bb6..a1ed39a 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -1959,7 +1959,7 @@
     uint8_t *p = ssl_handshake_start(s);
     size_t signature_length = 0;
     unsigned long n = 0;
-    assert(s->cert->privatekey != NULL || s->cert->key_method != NULL);
+    assert(ssl_has_private_key(s));
 
     if (s->state == SSL3_ST_CW_CERT_VRFY_A) {
       uint8_t *buf = (uint8_t *)s->init_buf->data;
@@ -2036,8 +2036,7 @@
 /* ssl3_has_client_certificate returns true if a client certificate is
  * configured. */
 static int ssl3_has_client_certificate(SSL *ssl) {
-  return ssl->cert && ssl->cert->x509 && (ssl->cert->privatekey ||
-                                          ssl->cert->key_method);
+  return ssl->cert && ssl->cert->x509 && ssl_has_private_key(ssl);
 }
 
 int ssl3_send_client_certificate(SSL *s) {
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index fba5bcb..dca6207 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -312,6 +312,8 @@
 
       case SSL3_ST_SW_KEY_EXCH_A:
       case SSL3_ST_SW_KEY_EXCH_B:
+      case SSL3_ST_SW_KEY_EXCH_C:
+      case SSL3_ST_SW_KEY_EXCH_D:
         alg_a = s->s3->tmp.new_cipher->algorithm_auth;
 
         /* Send a ServerKeyExchange message if:
@@ -1247,7 +1249,8 @@
   BN_CTX *bn_ctx = NULL;
   const char *psk_identity_hint = NULL;
   size_t psk_identity_hint_len = 0;
-  EVP_PKEY *pkey;
+  size_t sig_len;
+  size_t max_sig_len;
   uint8_t *p, *d;
   int al, i;
   uint32_t alg_k;
@@ -1255,10 +1258,20 @@
   int n;
   CERT *cert;
   BIGNUM *r[4];
-  int nr[4], kn;
+  int nr[4];
   BUF_MEM *buf;
   EVP_MD_CTX md_ctx;
 
+  if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) {
+    if (!ssl_has_private_key(s)) {
+      al = SSL_AD_INTERNAL_ERROR;
+      goto f_err;
+    }
+    max_sig_len = ssl_private_key_max_signature_len(s);
+  } else {
+    max_sig_len = 0;
+  }
+
   EVP_MD_CTX_init(&md_ctx);
   if (s->state == SSL3_ST_SW_KEY_EXCH_A) {
     alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
@@ -1400,19 +1413,7 @@
       n += 2 + nr[i];
     }
 
-    if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) {
-      pkey = s->cert->privatekey;
-      if (pkey == NULL) {
-        al = SSL_AD_DECODE_ERROR;
-        goto f_err;
-      }
-      kn = EVP_PKEY_size(pkey);
-    } else {
-      pkey = NULL;
-      kn = 0;
-    }
-
-    if (!BUF_MEM_grow_clean(buf, n + SSL_HM_HEADER_LENGTH(s) + kn)) {
+    if (!BUF_MEM_grow_clean(buf, n + SSL_HM_HEADER_LENGTH(s) + max_sig_len)) {
       OPENSSL_PUT_ERROR(SSL, ERR_LIB_BUF);
       goto err;
     }
@@ -1453,11 +1454,14 @@
     }
 
     /* not anonymous */
-    if (pkey != NULL) {
-      /* n is the length of the params, they start at &(d[4]) and p points to
+    if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) {
+      /* n is the length of the params, they start at d and p points to
        * the space at the end. */
       const EVP_MD *md;
-      size_t sig_len = EVP_PKEY_size(pkey);
+      uint8_t digest[EVP_MAX_MD_SIZE];
+      unsigned int digest_length;
+
+      const int pkey_type = ssl_private_key_type(s);
 
       /* Determine signature algorithm. */
       if (SSL_USE_SIGALGS(s)) {
@@ -1469,36 +1473,66 @@
           goto f_err;
         }
         p += 2;
-      } else if (pkey->type == EVP_PKEY_RSA) {
+      } else if (pkey_type == EVP_PKEY_RSA) {
         md = EVP_md5_sha1();
       } else {
         md = EVP_sha1();
       }
 
-      if (!EVP_DigestSignInit(&md_ctx, NULL, md, NULL, pkey) ||
-          !EVP_DigestSignUpdate(&md_ctx, s->s3->client_random,
-                                SSL3_RANDOM_SIZE) ||
-          !EVP_DigestSignUpdate(&md_ctx, s->s3->server_random,
-                                SSL3_RANDOM_SIZE) ||
-          !EVP_DigestSignUpdate(&md_ctx, d, n) ||
-          !EVP_DigestSignFinal(&md_ctx, &p[2], &sig_len)) {
+      if (!EVP_DigestInit_ex(&md_ctx, md, NULL) ||
+          !EVP_DigestUpdate(&md_ctx, s->s3->client_random, SSL3_RANDOM_SIZE) ||
+          !EVP_DigestUpdate(&md_ctx, s->s3->server_random, SSL3_RANDOM_SIZE) ||
+          !EVP_DigestUpdate(&md_ctx, d, n) ||
+          !EVP_DigestFinal_ex(&md_ctx, digest, &digest_length)) {
         OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP);
         goto err;
       }
 
-      s2n(sig_len, p);
-      n += sig_len + 2;
-      if (SSL_USE_SIGALGS(s)) {
-        n += 2;
+      const enum ssl_private_key_result_t sign_result = ssl_private_key_sign(
+          s, &p[2], &sig_len, max_sig_len, EVP_MD_CTX_md(&md_ctx),
+          digest, digest_length);
+      if (sign_result == ssl_private_key_retry) {
+        s->rwstate = SSL_PRIVATE_KEY_OPERATION;
+        /* Stash away |p|. */
+        s->init_num = p - ssl_handshake_start(s) + SSL_HM_HEADER_LENGTH(s);
+        s->state = SSL3_ST_SW_KEY_EXCH_B;
+        goto err;
+      } else if (sign_result != ssl_private_key_success) {
+        goto err;
       }
     }
 
-    if (!ssl_set_handshake_header(s, SSL3_MT_SERVER_KEY_EXCHANGE, n)) {
+    s->state = SSL3_ST_SW_KEY_EXCH_C;
+  } else if (s->state == SSL3_ST_SW_KEY_EXCH_B) {
+    /* Complete async sign. */
+    /* Restore |p|. */
+    p = ssl_handshake_start(s) + s->init_num - SSL_HM_HEADER_LENGTH(s);
+    const enum ssl_private_key_result_t sign_result =
+        ssl_private_key_sign_complete(s, &p[2], &sig_len, max_sig_len);
+    if (sign_result == ssl_private_key_retry) {
+      s->rwstate = SSL_PRIVATE_KEY_OPERATION;
+      goto err;
+    } else if (sign_result != ssl_private_key_success) {
       goto err;
     }
+
+    s->rwstate = SSL_NOTHING;
+    s->state = SSL3_ST_SW_KEY_EXCH_C;
   }
 
-  s->state = SSL3_ST_SW_KEY_EXCH_B;
+  if (s->state == SSL3_ST_SW_KEY_EXCH_C) {
+    if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) {
+      s2n(sig_len, p);
+      p += sig_len;
+    }
+    if (!ssl_set_handshake_header(s, SSL3_MT_SERVER_KEY_EXCHANGE,
+                                  p - ssl_handshake_start(s))) {
+      goto err;
+    }
+    s->state = SSL3_ST_SW_KEY_EXCH_D;
+  }
+
+  /* state SSL3_ST_SW_KEY_EXCH_D */
   EVP_MD_CTX_cleanup(&md_ctx);
   return ssl_do_write(s);
 
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index a7d5d5b..72b4c79 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -1834,13 +1834,14 @@
 
   dh_tmp = (c->dh_tmp != NULL || c->dh_tmp_cb != NULL);
 
-  if (s->cert->x509 != NULL && s->cert->privatekey != NULL) {
-    if (s->cert->privatekey->type == EVP_PKEY_RSA) {
+  if (s->cert->x509 != NULL && ssl_has_private_key(s)) {
+    if (ssl_private_key_type(s) == EVP_PKEY_RSA) {
       have_rsa_cert = 1;
-    } else if (s->cert->privatekey->type == EVP_PKEY_EC) {
+    } else if (ssl_private_key_type(s) == EVP_PKEY_EC) {
       have_ecc_cert = 1;
     }
   }
+
   mask_k = 0;
   mask_a = 0;
 
diff --git a/ssl/ssl_rsa.c b/ssl/ssl_rsa.c
index 7b6cd75..da69f8b 100644
--- a/ssl/ssl_rsa.c
+++ b/ssl/ssl_rsa.c
@@ -632,6 +632,10 @@
   ssl->cert->key_method = key_method;
 }
 
+int ssl_has_private_key(SSL *ssl) {
+  return ssl->cert->privatekey != NULL || ssl->cert->key_method != NULL;
+}
+
 int ssl_private_key_type(SSL *ssl) {
   if (ssl->cert->key_method != NULL) {
     return ssl->cert->key_method->type(ssl);
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 571680b..c0d5475 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -205,6 +205,7 @@
   }
   memcpy(out, bssl::vector_data(&test_state->signature),
          test_state->signature.size());
+  *out_len = test_state->signature.size();
 
   test_state->signature.clear();
   test_state->signature_retries = 0;
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index d66dc74..35d0bc6 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -2234,6 +2234,24 @@
 				"-use-async-private-key",
 			},
 		})
+		tests = append(tests, testCase{
+			testType: serverTest,
+			name:     "Basic-Server-RSAAsyncKey",
+			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",
+			flags: []string{
+				"-cert-file", path.Join(*resourceDir, ecdsaCertificateFile),
+				"-key-file", path.Join(*resourceDir, ecdsaKeyFile),
+				"-use-async-private-key",
+			},
+		})
 	}
 	tests = append(tests, testCase{
 		testType: serverTest,