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;