Add SSL_get0_peer_verify_algorithms.

Callers who use SSL_get0_certificate_types today will find an empty list
in TLS 1.3, which removed it. To provide feature parity, add an accessor
for the signature algorithms list. SSL_get_signature_algorithm_key_type
can be used to map it to a key type.

"Peer signature algorithms" was already taken in the public API by
SSL_get_peer_signature_algorithm to refer to which the peer selected, so
I named this matching SSL_CTX_set_verify_algorithm_prefs.

Change-Id: I12d411d7350e744ed9f88c610df48e0d9fc13256
Reviewed-on: https://boringssl-review.googlesource.com/29684
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: Adam Vartanian <flooey@google.com>
Reviewed-by: Steven Valdez <svaldez@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index a16a367..5fe5853 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -883,14 +883,28 @@
 
 // SSL_get0_certificate_types, for a client, sets |*out_types| to an array
 // containing the client certificate types requested by a server. It returns the
-// length of the array.
+// length of the array. Note this list is always empty in TLS 1.3. The server
+// will instead send signature algorithms. See
+// |SSL_get0_peer_verify_algorithms|.
 //
 // The behavior of this function is undefined except during the callbacks set by
 // by |SSL_CTX_set_cert_cb| and |SSL_CTX_set_client_cert_cb| or when the
 // handshake is paused because of them.
-OPENSSL_EXPORT size_t SSL_get0_certificate_types(SSL *ssl,
+OPENSSL_EXPORT size_t SSL_get0_certificate_types(const SSL *ssl,
                                                  const uint8_t **out_types);
 
+// SSL_get0_peer_verify_algorithms sets |*out_sigalgs| to an array containing
+// the signature algorithms the peer is able to verify. It returns the length of
+// the array. Note these values are only sent starting TLS 1.2 and only
+// mandatory starting TLS 1.3. If not sent, the empty array is returned. For the
+// historical client certificate types list, see |SSL_get0_certificate_types|.
+//
+// The behavior of this function is undefined except during the callbacks set by
+// by |SSL_CTX_set_cert_cb| and |SSL_CTX_set_client_cert_cb| or when the
+// handshake is paused because of them.
+OPENSSL_EXPORT size_t
+SSL_get0_peer_verify_algorithms(const SSL *ssl, const uint16_t **out_sigalgs);
+
 // SSL_certs_clear resets the private key, leaf certificate, and certificate
 // chain of |ssl|.
 OPENSSL_EXPORT void SSL_certs_clear(SSL *ssl);
diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc
index 572e79d..c96307d 100644
--- a/ssl/ssl_lib.cc
+++ b/ssl/ssl_lib.cc
@@ -2156,13 +2156,23 @@
   return ssl->s3->negotiated_token_binding_param;
 }
 
-size_t SSL_get0_certificate_types(SSL *ssl, const uint8_t **out_types) {
-  if (ssl->server || ssl->s3->hs == NULL) {
-    *out_types = NULL;
-    return 0;
+size_t SSL_get0_certificate_types(const SSL *ssl, const uint8_t **out_types) {
+  Span<const uint8_t> types;
+  if (!ssl->server && ssl->s3->hs != nullptr) {
+    types = ssl->s3->hs->certificate_types;
   }
-  *out_types = ssl->s3->hs->certificate_types.data();
-  return ssl->s3->hs->certificate_types.size();
+  *out_types = types.data();
+  return types.size();
+}
+
+size_t SSL_get0_peer_verify_algorithms(const SSL *ssl,
+                                       const uint16_t **out_sigalgs) {
+  Span<const uint16_t> sigalgs;
+  if (ssl->s3->hs != nullptr) {
+    sigalgs = ssl->s3->hs->peer_sigalgs;
+  }
+  *out_sigalgs = sigalgs.data();
+  return sigalgs.size();
 }
 
 EVP_PKEY *SSL_get_privatekey(const SSL *ssl) {
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index d88e389..f5fac2b 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -8854,6 +8854,55 @@
 		}
 	}
 
+	// Test the peer's verify preferences are available.
+	for _, ver := range tlsVersions {
+		if ver.version < VersionTLS12 {
+			continue
+		}
+		testCases = append(testCases, testCase{
+			name: "ClientAuth-PeerVerifyPrefs-" + ver.name,
+			config: Config{
+				MaxVersion: ver.version,
+				ClientAuth: RequireAnyClientCert,
+				VerifySignatureAlgorithms: []signatureAlgorithm{
+					signatureRSAPSSWithSHA256,
+					signatureEd25519,
+					signatureECDSAWithP256AndSHA256,
+				},
+			},
+			tls13Variant: ver.tls13Variant,
+			flags: []string{
+				"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+				"-key-file", path.Join(*resourceDir, rsaKeyFile),
+				"-expect-peer-verify-pref", strconv.Itoa(int(signatureRSAPSSWithSHA256)),
+				"-expect-peer-verify-pref", strconv.Itoa(int(signatureEd25519)),
+				"-expect-peer-verify-pref", strconv.Itoa(int(signatureECDSAWithP256AndSHA256)),
+			},
+		})
+
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "ServerAuth-PeerVerifyPrefs-" + ver.name,
+			config: Config{
+				MaxVersion: ver.version,
+				VerifySignatureAlgorithms: []signatureAlgorithm{
+					signatureRSAPSSWithSHA256,
+					signatureEd25519,
+					signatureECDSAWithP256AndSHA256,
+				},
+			},
+			tls13Variant: ver.tls13Variant,
+			flags: []string{
+				"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+				"-key-file", path.Join(*resourceDir, rsaKeyFile),
+				"-expect-peer-verify-pref", strconv.Itoa(int(signatureRSAPSSWithSHA256)),
+				"-expect-peer-verify-pref", strconv.Itoa(int(signatureEd25519)),
+				"-expect-peer-verify-pref", strconv.Itoa(int(signatureECDSAWithP256AndSHA256)),
+			},
+		})
+
+	}
+
 	// Test that algorithm selection takes the key type into account.
 	testCases = append(testCases, testCase{
 		name: "ClientAuth-SignatureType",
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index 3b9bcd9..1731431 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -217,6 +217,8 @@
 const Flag<std::vector<int>> kIntVectorFlags[] = {
   { "-signing-prefs", &TestConfig::signing_prefs },
   { "-verify-prefs", &TestConfig::verify_prefs },
+  { "-expect-peer-verify-pref",
+    &TestConfig::expected_peer_verify_prefs },
 };
 
 bool ParseFlag(char *flag, int argc, char **argv, int *i,
@@ -833,9 +835,38 @@
   return ret;
 }
 
+static bool CheckPeerVerifyPrefs(SSL *ssl) {
+  const TestConfig *config = GetTestConfig(ssl);
+  if (!config->expected_peer_verify_prefs.empty()) {
+    const uint16_t *peer_sigalgs;
+    size_t num_peer_sigalgs =
+        SSL_get0_peer_verify_algorithms(ssl, &peer_sigalgs);
+    if (config->expected_peer_verify_prefs.size() != num_peer_sigalgs) {
+      fprintf(stderr,
+              "peer verify preferences length mismatch (got %zu, wanted %zu)\n",
+              num_peer_sigalgs, config->expected_peer_verify_prefs.size());
+      return false;
+    }
+    for (size_t i = 0; i < num_peer_sigalgs; i++) {
+      if (static_cast<int>(peer_sigalgs[i]) !=
+          config->expected_peer_verify_prefs[i]) {
+        fprintf(stderr,
+                "peer verify preference %zu mismatch (got %04x, wanted %04x\n",
+                i, peer_sigalgs[i], config->expected_peer_verify_prefs[i]);
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
 static bool CheckCertificateRequest(SSL *ssl) {
   const TestConfig *config = GetTestConfig(ssl);
 
+  if (!CheckPeerVerifyPrefs(ssl)) {
+    return false;
+  }
+
   if (!config->expected_certificate_types.empty()) {
     const uint8_t *certificate_types;
     size_t certificate_types_len =
@@ -1375,8 +1406,9 @@
 static int CertCallback(SSL *ssl, void *arg) {
   const TestConfig *config = GetTestConfig(ssl);
 
-  // Check the CertificateRequest metadata is as expected.
-  if (!SSL_is_server(ssl) && !CheckCertificateRequest(ssl)) {
+  // Check the peer certificate metadata is as expected.
+  if ((!SSL_is_server(ssl) && !CheckCertificateRequest(ssl)) ||
+      !CheckPeerVerifyPrefs(ssl)) {
     return -1;
   }
 
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index 6f815b5..fef029f 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -32,6 +32,7 @@
   bool fallback_scsv = false;
   std::vector<int> signing_prefs;
   std::vector<int> verify_prefs;
+  std::vector<int> expected_peer_verify_prefs;
   std::string key_file;
   std::string cert_file;
   std::string expected_server_name;