Add SSL_get_server_key_exchange_hash.

This exposes the ServerKeyExchange signature hash type used in the most recent
handshake, for histogramming on the client.

BUG=549662

Change-Id: I8a4e00ac735b1ecd2c2df824112c3a0bc62332a7
Reviewed-on: https://boringssl-review.googlesource.com/6413
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/internal.h b/ssl/internal.h
index aa8c4ef..520131e 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -1294,8 +1294,13 @@
 const EVP_MD *tls1_choose_signing_digest(SSL *ssl);
 
 size_t tls12_get_psigalgs(SSL *s, const uint8_t **psigs);
-int tls12_check_peer_sigalg(const EVP_MD **out_md, int *out_alert, SSL *s,
-                            CBS *cbs, EVP_PKEY *pkey);
+
+/* tls12_check_peer_sigalg checks that |hash| and |signature| are consistent
+ * with |pkey| and |ssl|'s sent, supported signature algorithms and, if so,
+ * writes the relevant digest into |*out_md| and returns 1. Otherwise it
+ * returns 0 and writes an alert into |*out_alert|. */
+int tls12_check_peer_sigalg(SSL *ssl, const EVP_MD **out_md, int *out_alert,
+                            uint8_t hash, uint8_t signature, EVP_PKEY *pkey);
 void ssl_set_client_disabled(SSL *s);
 
 #endif /* OPENSSL_HEADER_SSL_INTERNAL_H */
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index 04c06dd..843403b 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -1244,9 +1244,17 @@
     }
 
     if (SSL_USE_SIGALGS(s)) {
-      if (!tls12_check_peer_sigalg(&md, &al, s, &server_key_exchange, pkey)) {
+      uint8_t hash, signature;
+      if (!CBS_get_u8(&server_key_exchange, &hash) ||
+          !CBS_get_u8(&server_key_exchange, &signature)) {
+        al = SSL_AD_DECODE_ERROR;
+        OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
         goto f_err;
       }
+      if (!tls12_check_peer_sigalg(s, &md, &al, hash, signature, pkey)) {
+        goto f_err;
+      }
+      s->s3->tmp.server_key_exchange_hash = hash;
     } else if (pkey->type == EVP_PKEY_RSA) {
       md = EVP_md5_sha1();
     } else {
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index fad2d0a..8cfa0e6 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -2056,9 +2056,17 @@
   CBS_init(&certificate_verify, s->init_msg, n);
 
   /* Determine the digest type if needbe. */
-  if (SSL_USE_SIGALGS(s) &&
-      !tls12_check_peer_sigalg(&md, &al, s, &certificate_verify, pkey)) {
-    goto f_err;
+  if (SSL_USE_SIGALGS(s)) {
+    uint8_t hash, signature_type;
+    if (!CBS_get_u8(&certificate_verify, &hash) ||
+        !CBS_get_u8(&certificate_verify, &signature_type)) {
+      al = SSL_AD_DECODE_ERROR;
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      goto f_err;
+    }
+    if (!tls12_check_peer_sigalg(s, &md, &al, hash, signature_type, pkey)) {
+      goto f_err;
+    }
   }
 
   /* Compute the digest. */
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 68eb4ac..16c24df 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -2651,6 +2651,9 @@
   }
 
   return 1;
+
+uint8_t SSL_get_server_key_exchange_hash(const SSL *ssl) {
+  return ssl->s3->tmp.server_key_exchange_hash;
 }
 
 int SSL_clear(SSL *ssl) {
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 301d49a..9a29028 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -640,16 +640,11 @@
   return sizeof(tls12_sigalgs);
 }
 
-/* tls12_check_peer_sigalg parses a SignatureAndHashAlgorithm out of |cbs|. It
- * checks it is consistent with |s|'s sent supported signature algorithms and,
- * if so, writes the relevant digest into |*out_md| and returns 1. Otherwise it
- * returns 0 and writes an alert into |*out_alert|. */
-int tls12_check_peer_sigalg(const EVP_MD **out_md, int *out_alert, SSL *s,
-                            CBS *cbs, EVP_PKEY *pkey) {
+int tls12_check_peer_sigalg(SSL *ssl, const EVP_MD **out_md, int *out_alert,
+                            uint8_t hash, uint8_t signature, EVP_PKEY *pkey) {
   const uint8_t *sent_sigs;
   size_t sent_sigslen, i;
   int sigalg = tls12_get_sigid(pkey->type);
-  uint8_t hash, signature;
 
   /* Should never happen */
   if (sigalg == -1) {
@@ -658,13 +653,6 @@
     return 0;
   }
 
-  if (!CBS_get_u8(cbs, &hash) ||
-      !CBS_get_u8(cbs, &signature)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    *out_alert = SSL_AD_DECODE_ERROR;
-    return 0;
-  }
-
   /* Check key type is consistent with signature */
   if (sigalg != signature) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SIGNATURE_TYPE);
@@ -681,8 +669,8 @@
       return 0;
     }
 
-    if (s->server && (!tls1_check_curve_id(s, curve_id) ||
-                      comp_id != TLSEXT_ECPOINTFORMAT_uncompressed)) {
+    if (ssl->server && (!tls1_check_curve_id(ssl, curve_id) ||
+                        comp_id != TLSEXT_ECPOINTFORMAT_uncompressed)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
       *out_alert = SSL_AD_ILLEGAL_PARAMETER;
       return 0;
@@ -690,7 +678,7 @@
   }
 
   /* Check signature matches a type we sent */
-  sent_sigslen = tls12_get_psigalgs(s, &sent_sigs);
+  sent_sigslen = tls12_get_psigalgs(ssl, &sent_sigs);
   for (i = 0; i < sent_sigslen; i += 2, sent_sigs += 2) {
     if (hash == sent_sigs[0] && signature == sent_sigs[1]) {
       break;
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index fe3dd6f..07ba9f5 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -1061,6 +1061,15 @@
     }
   }
 
+  if (config->expect_server_key_exchange_hash != 0 &&
+      config->expect_server_key_exchange_hash !=
+          SSL_get_server_key_exchange_hash(ssl)) {
+    fprintf(stderr, "ServerKeyExchange hash was %d, wanted %d.\n",
+            SSL_get_server_key_exchange_hash(ssl),
+            config->expect_server_key_exchange_hash);
+    return false;
+  }
+
   if (!config->is_server) {
     /* Clients should expect a peer certificate chain iff this was not a PSK
      * cipher suite. */
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 158f082..6ab71cf 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -4026,6 +4026,19 @@
 				},
 			},
 		})
+
+		testCases = append(testCases, testCase{
+			name: "SigningHash-ServerKeyExchange-Verify-" + hash.name,
+			config: Config{
+				CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+				SignatureAndHashes: []signatureAndHash{
+					{signatureRSA, 42},
+					{signatureRSA, hash.id},
+					{signatureRSA, 255},
+				},
+			},
+			flags: []string{"-expect-server-key-exchange-hash", strconv.Itoa(int(hash.id))},
+		})
 	}
 
 	// Test that hash resolution takes the signature type into account.
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index 8b540c3..50e6b23 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -141,6 +141,8 @@
   { "-mtu", &TestConfig::mtu },
   { "-export-keying-material", &TestConfig::export_keying_material },
   { "-expect-total-renegotiations", &TestConfig::expect_total_renegotiations },
+  { "-expect-server-key-exchange-hash",
+    &TestConfig::expect_server_key_exchange_hash },
 };
 
 }  // namespace
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index a72d66b..9f295ae 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -100,6 +100,7 @@
   bool renegotiate_freely = false;
   bool renegotiate_ignore = false;
   bool disable_npn = false;
+  int expect_server_key_exchange_hash = 0;
 };
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_config);