Support handshake hints for TLS 1.2 full handshakes.

Follow-up work will add support for TLS 1.2 ticket decryption.

Bug: 504
Change-Id: Ieaee37d94562040f1d51227216359bd63db15198
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/53525
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
diff --git a/ssl/ssl_privkey.cc b/ssl/ssl_privkey.cc
index 41a13a2..0843e0b 100644
--- a/ssl/ssl_privkey.cc
+++ b/ssl/ssl_privkey.cc
@@ -201,6 +201,31 @@
     SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len, size_t max_out,
     uint16_t sigalg, Span<const uint8_t> in) {
   SSL *const ssl = hs->ssl;
+  SSL_HANDSHAKE_HINTS *const hints = hs->hints.get();
+  Array<uint8_t> spki;
+  if (hints) {
+    ScopedCBB spki_cbb;
+    if (!CBB_init(spki_cbb.get(), 64) ||
+        !EVP_marshal_public_key(spki_cbb.get(), hs->local_pubkey.get()) ||
+        !CBBFinishArray(spki_cbb.get(), &spki)) {
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      return ssl_private_key_failure;
+    }
+  }
+
+  // Replay the signature from handshake hints if available.
+  if (hints && !hs->hints_requested &&         //
+      sigalg == hints->signature_algorithm &&  //
+      in == hints->signature_input &&
+      MakeConstSpan(spki) == hints->signature_spki &&
+      !hints->signature.empty() &&  //
+      hints->signature.size() <= max_out) {
+    // Signature algorithm and input both match. Reuse the signature from hints.
+    *out_len = hints->signature.size();
+    OPENSSL_memcpy(out, hints->signature.data(), hints->signature.size());
+    return ssl_private_key_success;
+  }
+
   const SSL_PRIVATE_KEY_METHOD *key_method = hs->config->cert->key_method;
   EVP_PKEY *privatekey = hs->config->cert->privatekey.get();
   assert(!hs->can_release_private_key);
@@ -214,21 +239,33 @@
     if (hs->pending_private_key_op) {
       ret = key_method->complete(ssl, out, out_len, max_out);
     } else {
-      ret = key_method->sign(ssl, out, out_len, max_out,
-                             sigalg, in.data(), in.size());
+      ret = key_method->sign(ssl, out, out_len, max_out, sigalg, in.data(),
+                             in.size());
     }
     if (ret == ssl_private_key_failure) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_PRIVATE_KEY_OPERATION_FAILED);
     }
     hs->pending_private_key_op = ret == ssl_private_key_retry;
-    return ret;
+    if (ret != ssl_private_key_success) {
+      return ret;
+    }
+  } else {
+    *out_len = max_out;
+    ScopedEVP_MD_CTX ctx;
+    if (!setup_ctx(ssl, ctx.get(), privatekey, sigalg, false /* sign */) ||
+        !EVP_DigestSign(ctx.get(), out, out_len, in.data(), in.size())) {
+      return ssl_private_key_failure;
+    }
   }
 
-  *out_len = max_out;
-  ScopedEVP_MD_CTX ctx;
-  if (!setup_ctx(ssl, ctx.get(), privatekey, sigalg, false /* sign */) ||
-      !EVP_DigestSign(ctx.get(), out, out_len, in.data(), in.size())) {
-    return ssl_private_key_failure;
+  // Save the hint if applicable.
+  if (hints && hs->hints_requested) {
+    hints->signature_algorithm = sigalg;
+    hints->signature_spki = std::move(spki);
+    if (!hints->signature_input.CopyFrom(in) ||
+        !hints->signature.CopyFrom(MakeConstSpan(out, *out_len))) {
+      return ssl_private_key_failure;
+    }
   }
   return ssl_private_key_success;
 }