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;