Introduce SSL_get_signature_algorithm_used.

Change-Id: Ifed5f5ef022a8c0d6bd57d196f9b4c308ca4f213
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/95087
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
Commit-Queue: James Buckland <jbuckland@google.com>
diff --git a/include/openssl/prefix_symbols.h b/include/openssl/prefix_symbols.h
index 23a98ae..c25242d 100644
--- a/include/openssl/prefix_symbols.h
+++ b/include/openssl/prefix_symbols.h
@@ -2254,6 +2254,7 @@
 #pragma redefine_extname SSL_get_signature_algorithm_digest BORINGSSL_ADD_USER_LABEL_AND_PREFIX(SSL_get_signature_algorithm_digest)
 #pragma redefine_extname SSL_get_signature_algorithm_key_type BORINGSSL_ADD_USER_LABEL_AND_PREFIX(SSL_get_signature_algorithm_key_type)
 #pragma redefine_extname SSL_get_signature_algorithm_name BORINGSSL_ADD_USER_LABEL_AND_PREFIX(SSL_get_signature_algorithm_name)
+#pragma redefine_extname SSL_get_signature_algorithm_used BORINGSSL_ADD_USER_LABEL_AND_PREFIX(SSL_get_signature_algorithm_used)
 #pragma redefine_extname SSL_get_srtp_profiles BORINGSSL_ADD_USER_LABEL_AND_PREFIX(SSL_get_srtp_profiles)
 #pragma redefine_extname SSL_get_ticket_age_skew BORINGSSL_ADD_USER_LABEL_AND_PREFIX(SSL_get_ticket_age_skew)
 #pragma redefine_extname SSL_get_tls_channel_id BORINGSSL_ADD_USER_LABEL_AND_PREFIX(SSL_get_tls_channel_id)
@@ -5367,6 +5368,7 @@
 #define SSL_get_signature_algorithm_digest BORINGSSL_ADD_PREFIX(SSL_get_signature_algorithm_digest)
 #define SSL_get_signature_algorithm_key_type BORINGSSL_ADD_PREFIX(SSL_get_signature_algorithm_key_type)
 #define SSL_get_signature_algorithm_name BORINGSSL_ADD_PREFIX(SSL_get_signature_algorithm_name)
+#define SSL_get_signature_algorithm_used BORINGSSL_ADD_PREFIX(SSL_get_signature_algorithm_used)
 #define SSL_get_srtp_profiles BORINGSSL_ADD_PREFIX(SSL_get_srtp_profiles)
 #define SSL_get_ticket_age_skew BORINGSSL_ADD_PREFIX(SSL_get_ticket_age_skew)
 #define SSL_get_tls_channel_id BORINGSSL_ADD_PREFIX(SSL_get_tls_channel_id)
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 1a2ac58..055b3e0 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -5467,6 +5467,17 @@
 OPENSSL_EXPORT size_t SSL_get_server_random(const SSL *ssl, uint8_t *out,
                                             size_t max_out);
 
+// SSL_get_signature_algorithm_used returns the signature algorithm that |ssl|
+// used, or will use, to generate a signature in the current handshake. If not
+// applicable (e.g. if |ssl| did not authenticate itself with a certificate), it
+// returns zero.
+//
+// This function only returns a value during the handshake. After the handshake
+// is complete, the value is discarded. If needed after the handshake, callers
+// may save the value at |SSL_CB_HANDSHAKE_DONE| with
+// |SSL_CTX_set_info_callback|.
+OPENSSL_EXPORT uint16_t SSL_get_signature_algorithm_used(const SSL *ssl);
+
 // SSL_get_pending_cipher returns the cipher suite for the current handshake or
 // NULL if one has not been negotiated yet or there is no pending handshake.
 OPENSSL_EXPORT const SSL_CIPHER *SSL_get_pending_cipher(const SSL *ssl);
diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc
index a878c9c..89702ea 100644
--- a/ssl/ssl_lib.cc
+++ b/ssl/ssl_lib.cc
@@ -3121,6 +3121,14 @@
   return max_out;
 }
 
+uint16_t SSL_get_signature_algorithm_used(const SSL *ssl) {
+  SSL_HANDSHAKE *hs = ssl->s3->hs.get();
+  if (hs == nullptr) {
+    return 0;
+  }
+  return hs->signature_algorithm;
+}
+
 const SSL_CIPHER *SSL_get_pending_cipher(const SSL *ssl) {
   SSL_HANDSHAKE *hs = ssl->s3->hs.get();
   if (hs == nullptr) {
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index 7a6da95..a876718 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -11646,5 +11646,59 @@
 }
 #endif  // OPENSSL_THREADS
 
+static uint16_t g_client_sigalg_used = 0;
+static uint16_t g_server_sigalg_used = 0;
+
+static void SignatureAlgorithmUsedInfoCallback(const SSL *ssl, int type,
+                                               int value) {
+  if (type == SSL_CB_HANDSHAKE_DONE) {
+    if (SSL_is_server(ssl)) {
+      g_server_sigalg_used = SSL_get_signature_algorithm_used(ssl);
+    } else {
+      g_client_sigalg_used = SSL_get_signature_algorithm_used(ssl);
+    }
+  }
+}
+
+TEST(SSLTest, SignatureAlgorithmUsed) {
+  g_client_sigalg_used = 0;
+  g_server_sigalg_used = 0;
+
+  bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
+  bssl::UniquePtr<SSL_CTX> server_ctx(
+      CreateContextWithTestCertificate(TLS_method()));
+  ASSERT_TRUE(client_ctx);
+  ASSERT_TRUE(server_ctx);
+
+  // By setting a single signature algorithm, we force the handshake to use
+  // that algorithm, and we can check that it is reported as the signature
+  // algorithm used.
+  const uint16_t kPref = SSL_SIGN_RSA_PSS_RSAE_SHA384;
+  static const uint16_t kPrefs[] = {kPref};
+  ASSERT_TRUE(SSL_CTX_set_signing_algorithm_prefs(
+      server_ctx.get(), kPrefs, std::size(kPrefs)));
+  ASSERT_TRUE(SSL_CTX_set_signing_algorithm_prefs(
+      client_ctx.get(), kPrefs, std::size(kPrefs)));
+
+  SSL_CTX_set_info_callback(client_ctx.get(),
+                            SignatureAlgorithmUsedInfoCallback);
+  SSL_CTX_set_info_callback(server_ctx.get(),
+                            SignatureAlgorithmUsedInfoCallback);
+
+  bssl::UniquePtr<SSL> client, server;
+  ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(),
+                                     server_ctx.get()));
+
+  EXPECT_EQ(g_server_sigalg_used, kPref);
+  // There is no client signature algorithm.
+  EXPECT_EQ(g_client_sigalg_used, 0u);
+
+  // After the handshake completes, the handshake object is reset, so the API
+  // returns 0.
+  EXPECT_EQ(SSL_get_signature_algorithm_used(client.get()), 0u);
+  EXPECT_EQ(SSL_get_signature_algorithm_used(server.get()), 0u);
+}
+
 }  // namespace
 BSSL_NAMESPACE_END
+