From 087020743beaf51c3ce64f45864ac8df26f0c909 Mon Sep 17 00:00:00 2001 From: Christopher Patton Date: Tue, 21 Apr 2026 14:40:14 -0700 Subject: [PATCH 1/3] boring-sys: Bump BoringSSL to 238273995 This is to include X.509 support for MLDSA. BoringSSL now includes *.in and *.cpp files that re needed for the build. --- boring-sys/Cargo.toml | 2 ++ boring-sys/deps/boringssl | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/boring-sys/Cargo.toml b/boring-sys/Cargo.toml index 675a02f34..9d2db4d70 100644 --- a/boring-sys/Cargo.toml +++ b/boring-sys/Cargo.toml @@ -33,6 +33,8 @@ include = [ "/deps/boringssl/crypto/err/*.errordata", "/deps/boringssl/**/*.bzl", "/deps/boringssl/**/*.cc", + "/deps/boringssl/**/*.cpp", + "/deps/boringssl/**/*.in", "/deps/boringssl/**/CMakeLists.txt", "/deps/boringssl/**/sources.cmake", "/deps/boringssl/**/util/go_tests.txt", diff --git a/boring-sys/deps/boringssl b/boring-sys/deps/boringssl index 91a66a59b..238273995 160000 --- a/boring-sys/deps/boringssl +++ b/boring-sys/deps/boringssl @@ -1 +1 @@ -Subproject commit 91a66a59b6c1435120ff83e245d7719411294386 +Subproject commit 2382739958b60b0f3c274a1e5fbd60dc17033681 From 569cb348123944893887eaddbc7e797e499e5785 Mon Sep 17 00:00:00 2001 From: Christopher Patton Date: Tue, 21 Apr 2026 15:48:40 -0700 Subject: [PATCH 2/3] boring-sys: Update boring-pq.patch --- boring-sys/patches/boring-pq.patch | 114 ++++++++++++++++------------- 1 file changed, 64 insertions(+), 50 deletions(-) diff --git a/boring-sys/patches/boring-pq.patch b/boring-sys/patches/boring-pq.patch index f0aa1ea26..0b699ebe2 100644 --- a/boring-sys/patches/boring-pq.patch +++ b/boring-sys/patches/boring-pq.patch @@ -1,7 +1,7 @@ -From cb5689e091f515fc8a42ceaff08d702333e505ed Mon Sep 17 00:00:00 2001 -From: Anthony Ramine -Date: Wed, 3 Dec 2025 11:10:16 +0100 -Subject: [PATCH] Add additional post-quantum key agreements +From e45854b34aa48b1fdefad81906cdb9931e697bbc Mon Sep 17 00:00:00 2001 +From: Christopher Patton +Date: Tue, 21 Apr 2026 15:45:09 -0700 +Subject: [PATCH] Add additional post-quantum key agreement This patch adds: @@ -16,6 +16,8 @@ This patch adds: non post-quantum and a post-quantum keyshare if available. These functions allow one to change the behaviour to only send a single keyshare. + + --- crypto/obj/obj_dat.h | 6 +- crypto/obj/obj_mac.num | 1 + @@ -26,7 +28,7 @@ This patch adds: ssl/internal.h | 12 ++- ssl/ssl_key_share.cc | 111 +++++++++++++++++++++++++++- ssl/ssl_lib.cc | 16 +++- - ssl/ssl_test.cc | 19 ++++- + ssl/ssl_test.cc | 24 +++++- ssl/test/runner/basic_tests.go | 2 + ssl/test/runner/cbc_tests.go | 3 + ssl/test/runner/common.go | 2 +- @@ -35,22 +37,22 @@ This patch adds: ssl/test/runner/extension_tests.go | 3 +- ssl/test/runner/key_update_tests.go | 6 +- tool/client.cc | 9 +++ - 18 files changed, 245 insertions(+), 42 deletions(-) + 18 files changed, 249 insertions(+), 43 deletions(-) diff --git a/crypto/obj/obj_dat.h b/crypto/obj/obj_dat.h -index d8b86dcd2..6dd49ec36 100644 +index 10ad9bb14..3fe6fdec6 100644 --- a/crypto/obj/obj_dat.h +++ b/crypto/obj/obj_dat.h -@@ -15,7 +15,7 @@ - // This file is generated by crypto/obj/objects.go. +@@ -16,7 +16,7 @@ + BSSL_NAMESPACE_BEGIN -#define NUM_NID 971 +#define NUM_NID 972 static const uint8_t kObjectData[] = { /* NID_rsadsi */ -@@ -8799,6 +8799,8 @@ static const ASN1_OBJECT kObjects[NUM_NID] = { +@@ -8800,6 +8800,8 @@ static const ASN1_OBJECT kObjects[NUM_NID] = { {"id-ml-dsa-87", "ML-DSA-87", NID_ML_DSA_87, 9, &kObjectData[6223], 0}, {"id-alg-ml-kem-768", "ML-KEM-768", NID_ML_KEM_768, 9, &kObjectData[6232], 0}, @@ -59,7 +61,7 @@ index d8b86dcd2..6dd49ec36 100644 }; static const uint16_t kNIDsInShortNameOrder[] = { -@@ -8931,6 +8933,7 @@ static const uint16_t kNIDsInShortNameOrder[] = { +@@ -8932,6 +8934,7 @@ static const uint16_t kNIDsInShortNameOrder[] = { 18 /* OU */, 749 /* Oakley-EC2N-3 */, 750 /* Oakley-EC2N-4 */, @@ -67,7 +69,7 @@ index d8b86dcd2..6dd49ec36 100644 9 /* PBE-MD2-DES */, 168 /* PBE-MD2-RC2-64 */, 10 /* PBE-MD5-DES */, -@@ -9854,6 +9857,7 @@ static const uint16_t kNIDsInLongNameOrder[] = { +@@ -9855,6 +9858,7 @@ static const uint16_t kNIDsInLongNameOrder[] = { 366 /* OCSP Nonce */, 371 /* OCSP Service Locator */, 180 /* OCSP Signing */, @@ -111,10 +113,10 @@ index 83a1cf592..7265f15f6 100644 #if defined(__cplusplus) } /* extern C */ diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h -index ff68ba69e..0730e769a 100644 +index 3a2e6dc50..300848e0f 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h -@@ -2550,6 +2550,7 @@ OPENSSL_EXPORT size_t SSL_CTX_get_num_tickets(const SSL_CTX *ctx); +@@ -2579,6 +2579,7 @@ OPENSSL_EXPORT size_t SSL_CTX_get_num_tickets(const SSL_CTX *ctx); #define SSL_GROUP_X25519_MLKEM768 0x11ec #define SSL_GROUP_X25519_KYBER768_DRAFT00 0x6399 #define SSL_GROUP_MLKEM1024 0x0202 @@ -122,7 +124,7 @@ index ff68ba69e..0730e769a 100644 // SSL_CTX_set1_group_ids sets the preferred groups for |ctx| to |group_ids|. // Each element of |group_ids| should be a unique one of the |SSL_GROUP_*| -@@ -5964,6 +5965,20 @@ OPENSSL_EXPORT int SSL_CTX_set1_curves_list(SSL_CTX *ctx, const char *curves); +@@ -6195,6 +6196,20 @@ OPENSSL_EXPORT int SSL_CTX_set1_curves_list(SSL_CTX *ctx, const char *curves); // SSL_set1_curves_list calls |SSL_set1_groups_list|. OPENSSL_EXPORT int SSL_set1_curves_list(SSL *ssl, const char *curves); @@ -144,10 +146,10 @@ index ff68ba69e..0730e769a 100644 // |SSL_get_negotiated_group| to return an unrecognized group. BoringSSL never // returns this value, but we define this constant for compatibility. diff --git a/ssl/extensions.cc b/ssl/extensions.cc -index c5f90688c..e0514fed3 100644 +index 529226876..e2ed74d5f 100644 --- a/ssl/extensions.cc +++ b/ssl/extensions.cc -@@ -101,6 +101,7 @@ static bool tls1_check_duplicate_extensions(const CBS *cbs) { +@@ -106,6 +106,7 @@ static bool tls1_check_duplicate_extensions(const CBS *cbs) { static bool is_post_quantum_group(uint16_t id) { switch (id) { case SSL_GROUP_X25519_KYBER768_DRAFT00: @@ -155,7 +157,7 @@ index c5f90688c..e0514fed3 100644 case SSL_GROUP_X25519_MLKEM768: case SSL_GROUP_MLKEM1024: return true; -@@ -2241,18 +2242,21 @@ bool ssl_setup_key_shares(SSL_HANDSHAKE *hs, uint16_t override_group_id) { +@@ -2413,18 +2414,21 @@ bool ssl_setup_key_shares(SSL_HANDSHAKE *hs, uint16_t override_group_id) { if (!default_key_shares.TryPushBack(supported_group_list[0])) { return false; } @@ -189,10 +191,10 @@ index c5f90688c..e0514fed3 100644 selected_key_shares.emplace(default_key_shares); } diff --git a/ssl/internal.h b/ssl/internal.h -index a69505b47..1f5ce51e6 100644 +index 4d86a6170..1c0d9f2fd 100644 --- a/ssl/internal.h +++ b/ssl/internal.h -@@ -955,7 +955,7 @@ struct NamedGroup { +@@ -913,7 +913,7 @@ struct NamedGroup { Span NamedGroups(); // kNumNamedGroups is the number of supported groups. @@ -201,7 +203,7 @@ index a69505b47..1f5ce51e6 100644 // DefaultSupportedGroupIds returns the list of IDs for the default groups that // are supported when the caller hasn't explicitly configured supported groups. -@@ -3388,6 +3388,11 @@ struct SSL_CONFIG { +@@ -3512,6 +3512,11 @@ struct SSL_CONFIG { // permute_extensions is whether to permute extensions when sending messages. bool permute_extensions : 1; @@ -213,7 +215,7 @@ index a69505b47..1f5ce51e6 100644 // aes_hw_override if set indicates we should override checking for aes // hardware support, and use the value in aes_hw_override_value instead. bool aes_hw_override : 1; -@@ -4015,6 +4020,11 @@ struct ssl_ctx_st : public bssl::RefCounted { +@@ -4149,6 +4154,11 @@ struct ssl_ctx_st : public bssl::RefCounted { // permute_extensions is whether to permute extensions when sending messages. bool permute_extensions : 1; @@ -371,10 +373,10 @@ index d155b5527..4fb08906b 100644 return nullptr; } diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc -index f64b103fb..fe5bb9bc7 100644 +index de4e79efc..4ca2e799d 100644 --- a/ssl/ssl_lib.cc +++ b/ssl/ssl_lib.cc -@@ -397,6 +397,7 @@ ssl_ctx_st::ssl_ctx_st(const SSL_METHOD *ssl_method) +@@ -385,6 +385,7 @@ ssl_ctx_st::ssl_ctx_st(const SSL_METHOD *ssl_method) channel_id_enabled(false), grease_enabled(false), permute_extensions(false), @@ -382,7 +384,7 @@ index f64b103fb..fe5bb9bc7 100644 allow_unknown_alpn_protos(false), false_start_allowed_without_alpn(false), handoff(false), -@@ -527,6 +528,7 @@ SSL *SSL_new(SSL_CTX *ctx) { +@@ -517,6 +518,7 @@ SSL *SSL_new(SSL_CTX *ctx) { ssl->config->retain_only_sha256_of_client_certs = ctx->retain_only_sha256_of_client_certs; ssl->config->permute_extensions = ctx->permute_extensions; @@ -390,7 +392,7 @@ index f64b103fb..fe5bb9bc7 100644 ssl->config->aes_hw_override = ctx->aes_hw_override; ssl->config->aes_hw_override_value = ctx->aes_hw_override_value; ssl->config->compliance_policy = ctx->compliance_policy; -@@ -586,6 +588,7 @@ SSL_CONFIG::SSL_CONFIG(SSL *ssl_arg) +@@ -582,6 +584,7 @@ SSL_CONFIG::SSL_CONFIG(SSL *ssl_arg) jdk11_workaround(false), quic_use_legacy_codepoint(false), permute_extensions(false), @@ -398,7 +400,7 @@ index f64b103fb..fe5bb9bc7 100644 alps_use_new_codepoint(true) { assert(ssl); } -@@ -3331,6 +3334,15 @@ int SSL_set1_curves_list(SSL *ssl, const char *curves) { +@@ -3372,6 +3375,15 @@ int SSL_set1_curves_list(SSL *ssl, const char *curves) { return SSL_set1_groups_list(ssl, curves); } @@ -414,7 +416,7 @@ index f64b103fb..fe5bb9bc7 100644 namespace fips202205 { // (References are to SP 800-52r2): -@@ -3342,7 +3354,9 @@ namespace fips202205 { +@@ -3383,7 +3395,9 @@ namespace fips202205 { // Section 3.3.1 // "The server shall be configured to only use cipher suites that are // composed entirely of NIST approved algorithms" @@ -426,10 +428,10 @@ index f64b103fb..fe5bb9bc7 100644 static const uint16_t kSigAlgs[] = { SSL_SIGN_RSA_PKCS1_SHA256, diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc -index 779a2c37a..36a0cab3b 100644 +index 49adcaf72..6427ecf5d 100644 --- a/ssl/ssl_test.cc +++ b/ssl/ssl_test.cc -@@ -506,6 +506,14 @@ static const CurveTest kCurveTests[] = { +@@ -520,6 +520,14 @@ static const CurveTest kCurveTests[] = { "MLKEM1024:X25519MLKEM768", {SSL_GROUP_MLKEM1024, SSL_GROUP_X25519_MLKEM768}, }, @@ -444,7 +446,7 @@ index 779a2c37a..36a0cab3b 100644 { "P-256:P-384:P-521:X25519", -@@ -668,7 +676,9 @@ TEST(SSLTest, CurveRules) { +@@ -681,7 +689,9 @@ TEST(SSLTest, CurveRules) { } TEST(SSLTest, DefaultCurves) { @@ -455,7 +457,7 @@ index 779a2c37a..36a0cab3b 100644 SSL_GROUP_SECP384R1}; // Test the group ID APIs. -@@ -1522,6 +1532,9 @@ static bool GetClientHello(SSL *ssl, std::vector *out) { +@@ -1641,6 +1651,9 @@ static bool GetClientHello(SSL *ssl, std::vector *out) { static size_t GetClientHelloLen(uint16_t max_version, uint16_t session_version, size_t ticket_len) { bssl::UniquePtr ctx(SSL_CTX_new(TLS_method())); @@ -465,7 +467,19 @@ index 779a2c37a..36a0cab3b 100644 bssl::UniquePtr session = CreateSessionWithTicket(session_version, ticket_len); if (!ctx || !session) { -@@ -6815,7 +6828,9 @@ TEST(SSLTest, ApplyHandoffRemovesUnsupportedCurves) { +@@ -2315,7 +2328,10 @@ TEST(SSLTest, SetGroupIdsWithEqualPreference) { + // Test that the SSL group flags are defaulted to zero when zero groups are set + // (i.e. using the default groups). + TEST(SSLTest, SetGroupIdsWithFlags_DefaultGroups) { +- const uint16_t kDefaultGroups[] = {SSL_GROUP_X25519, SSL_GROUP_SECP256R1, ++ const uint16_t kDefaultGroups[] = {SSL_GROUP_X25519_MLKEM768, ++ SSL_GROUP_P256_KYBER768_DRAFT00, ++ SSL_GROUP_X25519, ++ SSL_GROUP_SECP256R1, + SSL_GROUP_SECP384R1}; + const uint32_t kBogusFlags[] = {SSL_GROUP_FLAG_EQUAL_PREFERENCE_WITH_NEXT, + SSL_GROUP_FLAG_EQUAL_PREFERENCE_WITH_NEXT, 0}; +@@ -7052,7 +7068,9 @@ TEST(SSLTest, ApplyHandoffRemovesUnsupportedCurves) { // The default list of groups is used before applying the handoff. EXPECT_THAT(server->config->supported_group_list, @@ -477,10 +491,10 @@ index 779a2c37a..36a0cab3b 100644 ASSERT_TRUE(SSL_apply_handoff(server.get(), handoff)); EXPECT_EQ(1u, server->config->supported_group_list.size()); diff --git a/ssl/test/runner/basic_tests.go b/ssl/test/runner/basic_tests.go -index 08de8fa5f..dd945fa49 100644 +index dfd17d5f1..054241404 100644 --- a/ssl/test/runner/basic_tests.go +++ b/ssl/test/runner/basic_tests.go -@@ -129,6 +129,7 @@ read alert 1 0 +@@ -132,6 +132,7 @@ read alert 1 0 `write hs 1 read hs 3 write hs 1 @@ -488,7 +502,7 @@ index 08de8fa5f..dd945fa49 100644 read hs 2 read hs 11 read hs 12 -@@ -1956,6 +1957,7 @@ read alert 1 0 +@@ -1975,6 +1976,7 @@ read alert 1 0 write hs 2 write hs 8 write hs 11 @@ -518,10 +532,10 @@ index 6f49d12af..5e970b2b5 100644 }) } diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go -index 7dbde72c9..9d18d9d45 100644 +index 2d174832f..5056444de 100644 --- a/ssl/test/runner/common.go +++ b/ssl/test/runner/common.go -@@ -2095,7 +2095,7 @@ type ProtocolBugs struct { +@@ -2172,7 +2172,7 @@ type ProtocolBugs struct { FailIfHelloRetryRequested bool // FailIfPostQuantumOffered will cause a server to reject a ClientHello if @@ -531,10 +545,10 @@ index 7dbde72c9..9d18d9d45 100644 // ExpectKeyShares, if not nil, lists (in order) the curves that a ClientHello diff --git a/ssl/test/runner/curve_tests.go b/ssl/test/runner/curve_tests.go -index 8e7b0a45b..556bf314d 100644 +index 9bc13115e..0d11da734 100644 --- a/ssl/test/runner/curve_tests.go +++ b/ssl/test/runner/curve_tests.go -@@ -579,17 +579,6 @@ func addCurveTests() { +@@ -581,17 +581,6 @@ func addCurveTests() { }) } @@ -552,7 +566,7 @@ index 8e7b0a45b..556bf314d 100644 for _, curve := range testCurves { if !isMLKEMGroup(curve.id) { continue -@@ -679,18 +668,19 @@ func addCurveTests() { +@@ -681,18 +670,19 @@ func addCurveTests() { }) } @@ -630,7 +644,7 @@ index 2cd3c10d3..f19d8d20a 100644 clientAndServerHelloInitial += "write ccs\n" } diff --git a/ssl/test/runner/extension_tests.go b/ssl/test/runner/extension_tests.go -index d6adb7759..4eb80aa8e 100644 +index 3087efe37..61a036d8b 100644 --- a/ssl/test/runner/extension_tests.go +++ b/ssl/test/runner/extension_tests.go @@ -16,6 +16,7 @@ package runner @@ -651,7 +665,7 @@ index d6adb7759..4eb80aa8e 100644 // Test that illegal extensions in TLS 1.3 are rejected by the client if diff --git a/ssl/test/runner/key_update_tests.go b/ssl/test/runner/key_update_tests.go -index 0a9053038..5ce709589 100644 +index f98528265..2068b1102 100644 --- a/ssl/test/runner/key_update_tests.go +++ b/ssl/test/runner/key_update_tests.go @@ -14,7 +14,10 @@ @@ -675,10 +689,10 @@ index 0a9053038..5ce709589 100644 // Test that shim responds to KeyUpdate requests. diff --git a/tool/client.cc b/tool/client.cc -index 0839d4880..be9b79259 100644 +index 1653af8da..cab725d40 100644 --- a/tool/client.cc +++ b/tool/client.cc -@@ -125,6 +125,11 @@ static const struct argument kArguments[] = { +@@ -156,6 +156,11 @@ static const struct argument kArguments[] = { kBooleanArgument, "Permute extensions in handshake messages", }, @@ -688,9 +702,9 @@ index 0839d4880..be9b79259 100644 + "Do not send a second keyshare", + }, { - "-test-resumption", kBooleanArgument, - "Connect to the server twice. The first connection is closed once a " -@@ -538,6 +543,10 @@ bool Client(const std::vector &args) { + "-test-resumption", + kBooleanArgument, +@@ -637,6 +642,10 @@ bool Client(const std::vector &args) { SSL_CTX_set_permute_extensions(ctx.get(), 1); } @@ -698,9 +712,9 @@ index 0839d4880..be9b79259 100644 + SSL_CTX_use_second_keyshare(ctx.get(), 0); + } + + // Configure accepted roots. if (args_map.count("-root-certs") != 0) { if (!SSL_CTX_load_verify_locations( - ctx.get(), args_map["-root-certs"].c_str(), nullptr)) { -- -2.40.0 +2.50.1 (Apple Git-155) From 85c21125dc8d34ca63f8251ee3beb50bd97bea29 Mon Sep 17 00:00:00 2001 From: Christopher Patton Date: Wed, 22 Apr 2026 16:52:21 -0700 Subject: [PATCH 3/3] boring-sys: Update RPK patch RPK is now implemented upstream, but we need to add some additional APIs that our bindings expect. --- boring-sys/patches/rpk.patch | 1506 +++------------------------------- boring-sys/src/lib.rs | 2 +- boring/src/ssl/credential.rs | 71 +- boring/src/ssl/mod.rs | 60 +- 4 files changed, 222 insertions(+), 1417 deletions(-) diff --git a/boring-sys/patches/rpk.patch b/boring-sys/patches/rpk.patch index 512566e94..bc9801960 100644 --- a/boring-sys/patches/rpk.patch +++ b/boring-sys/patches/rpk.patch @@ -1,408 +1,110 @@ -From 9725dabfc86f57607e60e48e09e10615a05bb053 Mon Sep 17 00:00:00 2001 -From: Anthony Ramine -Date: Wed, 17 Dec 2025 13:27:50 +0100 -Subject: [PATCH] Implement support for raw public keys as server certificates - (RFC 7250) +From 4bba53440a0054bc7a0c479bce668649206d30a2 Mon Sep 17 00:00:00 2001 +From: Christopher Patton +Date: Wed, 22 Apr 2026 16:51:27 -0700 +Subject: [PATCH] Add additional methods for RPK (RFC 7250) +The new methods match the Rust bindings present in boring <= 5: + +1. Add a method for creating an initially empty an RPK credential. The + user sets the private key and the SPKI separately. + +2. Add methods for fetching the accepted certificate types list + configured on an SSL or SSL_CTX. + +3. Add a method for fetching the peer's public key that for all + certificate types (X.509 and RPK). --- - crypto/err/ssl.errordata | 1 + - include/openssl/ssl.h | 65 +++++++++++++ - include/openssl/tls1.h | 3 + - ssl/extensions.cc | 122 +++++++++++++++++++++++ - ssl/internal.h | 18 ++++ - ssl/ssl_cert.cc | 8 ++ - ssl/ssl_credential.cc | 48 +++++++++ - ssl/ssl_lib.cc | 52 +++++++++- - ssl/ssl_test.cc | 67 +++++++++++++ - ssl/test/bssl_shim.cc | 3 +- - ssl/test/runner/certificate_tests.go | 134 +++++++++++++++++++++++++- - ssl/test/runner/common.go | 19 ++++ - ssl/test/runner/handshake_client.go | 71 +++++++++++++- - ssl/test/runner/handshake_messages.go | 33 ++++++- - ssl/test/runner/handshake_server.go | 53 +++++++--- - ssl/test/runner/runner.go | 3 +- - ssl/test/test_config.cc | 92 +++++++++++++++--- - ssl/test/test_config.h | 2 + - ssl/tls13_both.cc | 98 +++++++++++++------ - ssl/tls13_server.cc | 34 ++++++- - 20 files changed, 861 insertions(+), 65 deletions(-) + include/openssl/ssl.h | 55 +++++++++++++++++++++++++++++++++++++++++++ + ssl/ssl_credential.cc | 48 +++++++++++++++++++++++++++++++++++++ + ssl/ssl_lib.cc | 28 ++++++++++++++++++++++ + 3 files changed, 131 insertions(+) -diff --git a/crypto/err/ssl.errordata b/crypto/err/ssl.errordata -index 01c4ca616..c9f4994d9 100644 ---- a/crypto/err/ssl.errordata -+++ b/crypto/err/ssl.errordata -@@ -95,6 +95,7 @@ SSL,159,INVALID_MESSAGE - SSL,320,INVALID_OUTER_EXTENSION - SSL,251,INVALID_OUTER_RECORD_TYPE - SSL,269,INVALID_SCT_LIST -+SSL,331,INVALID_SERVER_CERTIFICATE_TYPE_LIST - SSL,295,INVALID_SIGNATURE_ALGORITHM - SSL,324,INVALID_SPAKE2PLUSV1_VALUE - SSL,160,INVALID_SSL_SESSION diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h -index ff68ba69e..5a1cf42ca 100644 +index 3a2e6dc50..01443b402 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h -@@ -1781,6 +1781,10 @@ OPENSSL_EXPORT STACK_OF(X509) *SSL_get_peer_full_cert_chain(const SSL *ssl); - OPENSSL_EXPORT const STACK_OF(CRYPTO_BUFFER) *SSL_get0_peer_certificates( - const SSL *ssl); +@@ -3992,6 +3992,61 @@ OPENSSL_EXPORT int SSL_get_peer_cert_type(const SSL *ssl); + OPENSSL_EXPORT EVP_PKEY *SSL_get0_peer_rpk(const SSL *ssl); -+// SSL_get0_peer_pubkey returns the peer's public key during a handshake, or -+// NULL if unavailable. The caller does not take ownership of the result. -+OPENSSL_EXPORT const EVP_PKEY *SSL_get0_peer_pubkey(const SSL *ssl); -+ - // SSL_get0_signed_cert_timestamp_list sets |*out| and |*out_len| to point to - // |*out_len| bytes of SCT information from the server. This is only valid if - // |ssl| is a client. The SCT information is a SignedCertificateTimestampList -@@ -3406,6 +3410,49 @@ OPENSSL_EXPORT int SSL_has_application_settings(const SSL *ssl); - // codepoint. By default, the old codepoint is used. - OPENSSL_EXPORT void SSL_set_alps_use_new_codepoint(SSL *ssl, int use_new); -+// Server Certificate Type (RFC 7250). -+// -+// The Server Certificate Type extension (RFC 7301) allows negotiating -+// different server certificate types. This is used, for example, to receive -+// a raw public key instead of a full-fedged X.509 certificate from a server. -+ -+// SSL_CTX_set_server_certificate_types sets the server certificate type list -+// on |ctx| to |types|. This is the list of certificate types that the client -+// is willing to receive from the server. |types| must be an array -+// of |TLS_CERTIFICATE_TYPE_*| values. Configuring a non-empty array enables -+// the server_certificate_type extension on a client. -+OPENSSL_EXPORT int SSL_CTX_set_server_certificate_types(SSL_CTX *ctx, -+ const uint8_t *types, -+ size_t types_len); -+ -+// SSL_CTX_get0_server_certificate_types returns the server certificate type -+// list configured on |ctx|. -+OPENSSL_EXPORT void SSL_CTX_get0_server_certificate_types(const SSL_CTX *ctx, -+ const uint8_t **types, -+ size_t *types_len); ++// Cloudflare-internal APIs for Raw Public Keys (RFC 7250). + -+// SSL_set_server_certificate_types sets the server certificate type list -+// on |ssl| to |types|. This is the list of certificate types that the client -+// is willing to receive from the server. |types| must be an array -+// of |TLS_CERTIFICATE_TYPE_*| values. Configuring a non-empty array enables -+// the server_certificate_type extension on a client. -+OPENSSL_EXPORT int SSL_set_server_certificate_types(SSL *ssl, -+ const uint8_t *types, -+ size_t types_len); -+ -+// SSL_get0_server_certificate_types returns the server certificate type list -+// configured on |ssl|. -+OPENSSL_EXPORT void SSL_get0_server_certificate_types(const SSL *ssl, -+ const uint8_t **types, -+ size_t *types_len); -+ -+// SSL_get_server_certificate_type_selected gets the selected server -+// certificate type from |ssl|. -+OPENSSL_EXPORT uint8_t SSL_get_server_certificate_type_selected(const SSL *ssl); -+ -+#define TLS_CERTIFICATE_TYPE_X509 0 -+#define TLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY 2 -+ - - // Certificate compression. - // -@@ -3759,6 +3806,23 @@ OPENSSL_EXPORT SSL_CREDENTIAL *SSL_CREDENTIAL_new_delegated(void); - OPENSSL_EXPORT int SSL_CREDENTIAL_set1_delegated_credential( - SSL_CREDENTIAL *cred, CRYPTO_BUFFER *dc); - -+// Raw Public Key Credentials -+ -+// SSL_CREDENTIAL_new_raw_public_key returns a new, empty Raw Public Key ++// SSL_CREDENTIAL_new_raw_public_key_empty returns a new, empty raw public key +// credential, or NULL on error. Callers should release the result with +// |SSL_CREDENTIAL_free| when done. +// -+// Callers should configure a raw public key and a private key on the -+// credential, then add it with |SSL_CTX_add1_credential|. -+OPENSSL_EXPORT SSL_CREDENTIAL *SSL_CREDENTIAL_new_raw_public_key(void); -+ -+// SSL_CREDENTIAL_set1_spki |cred|'s raw public key from |spki|. -+// If |spki| is NULL, the public key is extracted from |cred|'s private key. -+// It returns one on success and zero on error, including if |spki| is -+// malformed or if it is NULL and |cred| has no private key. |spki| should -+// be a SubjectPublicKeyInfo structure, as described in RFC 5280. -+int SSL_CREDENTIAL_set1_spki(SSL_CREDENTIAL *cred, -+ CRYPTO_BUFFER *spki); - - // Password Authenticated Key Exchange (PAKE). - // -@@ -6569,6 +6633,7 @@ BSSL_NAMESPACE_END - #define SSL_R_INVALID_TRUST_ANCHOR_LIST 328 - #define SSL_R_INVALID_CERTIFICATE_PROPERTY_LIST 329 - #define SSL_R_DUPLICATE_GROUP 330 -+#define SSL_R_INVALID_SERVER_CERTIFICATE_TYPE_LIST 331 - #define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000 - #define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010 - #define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020 -diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h -index ea55e2a07..f55c0d441 100644 ---- a/include/openssl/tls1.h -+++ b/include/openssl/tls1.h -@@ -64,6 +64,9 @@ extern "C" { - // ExtensionType value from RFC 7301 - #define TLSEXT_TYPE_application_layer_protocol_negotiation 16 - -+// ExtensionType value from RFC 7250 -+#define TLSEXT_TYPE_server_certificate_type 20 -+ - // ExtensionType value from RFC 7685 - #define TLSEXT_TYPE_padding 21 - -diff --git a/ssl/extensions.cc b/ssl/extensions.cc -index c5f90688c..eb5ef58e8 100644 ---- a/ssl/extensions.cc -+++ b/ssl/extensions.cc -@@ -3523,6 +3523,121 @@ bool ssl_negotiate_alps(SSL_HANDSHAKE *hs, uint8_t *out_alert, - return true; - } - -+// Server Certificate Type ++// Callers should configure the credential with |SSL_CREDENTIAL_set1_spki| and ++// either |SSL_CREDENTIAL_set1_private_key| or ++// |SSL_CREDENTIAL_set_private_key_method|, then add it with ++// |SSL_CTX_add1_credential|. +// -+// https://datatracker.ietf.org/doc/html/rfc7250#section-3 -+ -+bool ssl_is_valid_certificate_type_list(Span in) { -+ CBS type_list = in; -+ if (CBS_len(&type_list) == 0) { -+ return false; -+ } -+ uint8_t type; -+ while (CBS_get_u8(&type_list, &type)) { -+ switch (type) { -+ case TLS_CERTIFICATE_TYPE_X509: -+ case TLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY: -+ break; -+ default: -+ return false; -+ } -+ } -+ return true; -+} -+ -+static bool ext_server_certificate_type_add_clienthello( -+ const SSL_HANDSHAKE *hs, CBB *out, CBB *out_compressible, -+ ssl_client_hello_type_t type) { -+ if (hs->max_version < TLS1_3_VERSION || -+ hs->config->server_certificate_type_list.empty()) { -+ return true; -+ } -+ -+ CBB contents, type_list; -+ return CBB_add_u16(out, TLSEXT_TYPE_server_certificate_type) && -+ CBB_add_u16_length_prefixed(out, &contents) && -+ CBB_add_u8_length_prefixed(&contents, &type_list) && -+ CBB_add_bytes(&type_list, -+ hs->config->server_certificate_type_list.data(), -+ hs->config->server_certificate_type_list.size()) && -+ CBB_flush(out); -+} -+ -+static bool ext_server_certificate_type_parse_serverhello(SSL_HANDSHAKE *hs, -+ uint8_t *out_alert, -+ CBS *contents) { -+ if (hs->ssl->s3->session_reused) { -+ return true; -+ } -+ -+ uint8_t cert_type = TLS_CERTIFICATE_TYPE_X509; -+ if (contents != nullptr) { -+ assert(!hs->config->server_certificate_type_list.empty()); -+ if (!CBS_get_u8(contents, &cert_type) || CBS_len(contents) != 0) { -+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); -+ goto err; -+ } -+ } -+ -+ if (!hs->config->server_certificate_type_list.empty() && -+ std::none_of( -+ hs->config->server_certificate_type_list.begin(), -+ hs->config->server_certificate_type_list.end(), -+ [cert_type](const auto &type) { return type == cert_type; })) { -+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE); -+ goto err; -+ } -+ -+ hs->server_certificate_type = cert_type; -+ return true; -+ -+err: -+ *out_alert = SSL_AD_ILLEGAL_PARAMETER; -+ return false; -+} -+ -+static bool ext_server_certificate_type_parse_clienthello(SSL_HANDSHAKE *hs, -+ uint8_t *out_alert, -+ CBS *contents) { -+ if (contents == nullptr || ssl_protocol_version(hs->ssl) < TLS1_3_VERSION) { -+ return true; -+ } -+ -+ CBS type_list; -+ if (!CBS_get_u8_length_prefixed(contents, &type_list) || -+ CBS_len(contents) != 0 || CBS_len(&type_list) == 0) { -+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); -+ *out_alert = SSL_AD_ILLEGAL_PARAMETER; -+ return false; -+ } -+ -+ if (!hs->server_certificate_type_list.CopyFrom(type_list)) { -+ *out_alert = SSL_AD_INTERNAL_ERROR; -+ return false; -+ } -+ -+ return true; -+} -+ -+static bool ext_server_certificate_type_add_serverhello(SSL_HANDSHAKE *hs, -+ CBB *out) { -+ if (hs->ssl->s3->session_reused || hs->credential == nullptr || -+ hs->credential->type == SSLCredentialType::kX509) { -+ return true; -+ } -+ -+ if (hs->credential->type != SSLCredentialType::kRawPublicKey) { -+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); -+ return false; -+ } -+ -+ CBB cert_types; -+ return CBB_add_u16(out, TLSEXT_TYPE_server_certificate_type) && -+ CBB_add_u16_length_prefixed(out, &cert_types) && -+ CBB_add_u8(&cert_types, TLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY) && -+ CBB_flush(out); -+} -+ - // kExtensions contains all the supported extensions. - static const struct tls_extension kExtensions[] = { - { -@@ -3727,6 +3842,13 @@ static const struct tls_extension kExtensions[] = { - ext_trust_anchors_parse_clienthello, - ext_trust_anchors_add_serverhello, - }, -+ { -+ TLSEXT_TYPE_server_certificate_type, -+ ext_server_certificate_type_add_clienthello, -+ ext_server_certificate_type_parse_serverhello, -+ ext_server_certificate_type_parse_clienthello, -+ ext_server_certificate_type_add_serverhello, -+ }, - }; - - #define kNumExtensions (sizeof(kExtensions) / sizeof(struct tls_extension)) -diff --git a/ssl/internal.h b/ssl/internal.h -index a69505b47..867c62bd6 100644 ---- a/ssl/internal.h -+++ b/ssl/internal.h -@@ -1408,6 +1408,7 @@ enum class SSLCredentialType { - kDelegated, - kSPAKE2PlusV1Client, - kSPAKE2PlusV1Server, -+ kRawPublicKey, - }; - - BSSL_NAMESPACE_END -@@ -2062,6 +2063,14 @@ struct SSL_HANDSHAKE { - - // pake_verifier is the PAKE context for a server. - UniquePtr pake_verifier; -+ -+ // server_certificate_type_list indicates the types of certificates -+ // the client is able to process. -+ Array server_certificate_type_list; -+ -+ // server_certificate_type indicates the type of certificates the server -+ // selected to send as the certificate payload. -+ uint8_t server_certificate_type = TLS_CERTIFICATE_TYPE_X509; - }; - - // kMaxTickets is the maximum number of tickets to send immediately after the -@@ -2256,6 +2265,10 @@ bool ssl_negotiate_alps(SSL_HANDSHAKE *hs, uint8_t *out_alert, - // identifiers list. - bool ssl_is_valid_trust_anchor_list(Span in); - -+// ssl_is_valid_certificate_type_list returns whether |in| is a valid -+// certificate type list. -+bool ssl_is_valid_certificate_type_list(Span in); -+ - struct SSLExtension { - SSLExtension(uint16_t type_arg, bool allowed_arg = true) - : type(type_arg), allowed(allowed_arg), present(false) { -@@ -3339,6 +3352,8 @@ struct SSL_CONFIG { - // negotiating a TLS 1.3 connection. - enum ssl_compliance_policy_t compliance_policy = ssl_compliance_policy_none; - -+ Array server_certificate_type_list; -+ - // verify_mode is a bitmask of |SSL_VERIFY_*| values. - uint8_t verify_mode = SSL_VERIFY_NONE; - -@@ -3988,6 +4003,9 @@ struct ssl_ctx_st : public bssl::RefCounted { - // accepted from the peer in decreasing order of preference. - bssl::Array verify_sigalgs; - -+ // For a client, this contains the list of supported server certificate types. -+ bssl::Array server_certificate_type_list; -+ - // retain_only_sha256_of_client_certs is true if we should compute the SHA256 - // hash of the peer's certificate and then discard it to save memory and - // session space. Only effective on the server side. -diff --git a/ssl/ssl_cert.cc b/ssl/ssl_cert.cc -index 72218aeea..a1a8f328b 100644 ---- a/ssl/ssl_cert.cc -+++ b/ssl/ssl_cert.cc -@@ -587,6 +587,14 @@ const STACK_OF(CRYPTO_BUFFER) *SSL_get0_peer_certificates(const SSL *ssl) { - return session->certs.get(); - } - -+const EVP_PKEY *SSL_get0_peer_pubkey(const SSL *ssl) { -+ if (ssl->s3->hs == nullptr) { -+ return nullptr; -+ } ++// Unlike |SSL_CREDENTIAL_new_raw_public_key|, this two-step constructor allows ++// the caller to set the public key later (e.g., to use with a private key ++// method or to configure the public key from an SPKI). ++OPENSSL_EXPORT SSL_CREDENTIAL *SSL_CREDENTIAL_new_raw_public_key_empty(void); ++ ++// SSL_CREDENTIAL_set1_spki sets |cred|'s raw public key from |spki|. If |spki| ++// is NULL, the public key is extracted from |cred|'s private key. |cred| must ++// have been returned by |SSL_CREDENTIAL_new_raw_public_key_empty|. It returns ++// one on success and zero on error, including if |cred| is not a raw public ++// key credential, if |spki| is malformed, if |spki| is NULL and |cred| has no ++// private key, or if |cred| has a private key that does not match the public ++// key parsed from |spki|. |spki| should be a SubjectPublicKeyInfo structure, ++// as described in RFC 5280. ++OPENSSL_EXPORT int SSL_CREDENTIAL_set1_spki(SSL_CREDENTIAL *cred, ++ CRYPTO_BUFFER *spki); ++ ++// SSL_get0_peer_pubkey returns the peer's public key during the current ++// handshake, or NULL if unavailable. This returns the public key extracted ++// from the peer's leaf certificate for X.509 credentials, or the peer's raw ++// public key for RPK credentials. The returned pointer is only valid for the ++// duration of the handshake (e.g., from within a custom verify callback); it ++// returns NULL once the handshake has completed. The caller does not take ++// ownership of the result. ++OPENSSL_EXPORT const EVP_PKEY *SSL_get0_peer_pubkey(const SSL *ssl); + -+ return ssl->s3->hs->peer_pubkey.get(); -+} ++// SSL_CTX_get0_accepted_peer_cert_types returns the list of certificate types ++// that |ctx| is willing to accept from the peer, in preference order. By ++// default, this contains only |TLSEXT_cert_type_x509|. The list can be ++// configured via |SSL_CTX_set1_accepted_peer_cert_types|. The returned pointer ++// is owned by |ctx|. ++OPENSSL_EXPORT void SSL_CTX_get0_accepted_peer_cert_types( ++ const SSL_CTX *ctx, const uint8_t **types, size_t *types_len); ++ ++// SSL_get0_accepted_peer_cert_types returns the list of certificate types ++// that |ssl| is willing to accept from the peer, in preference order. The ++// initial value is inherited from the |SSL_CTX| at the time |ssl| was ++// created; it can be overridden via |SSL_set1_accepted_peer_cert_types|. If ++// |ssl|'s configuration has been released (e.g., after the handshake has ++// completed), |*types| is set to NULL and |*types_len| to zero. The returned ++// pointer is owned by |ssl|. ++OPENSSL_EXPORT void SSL_get0_accepted_peer_cert_types(const SSL *ssl, ++ const uint8_t **types, ++ size_t *types_len); + - const STACK_OF(CRYPTO_BUFFER) *SSL_get0_server_requested_CAs(const SSL *ssl) { - if (ssl->s3->hs == nullptr) { - return nullptr; + // Password Authenticated Key Exchange (PAKE). + // + // Password Authenticated Key Exchange protocols allow client and server to diff --git a/ssl/ssl_credential.cc b/ssl/ssl_credential.cc -index bbbd76701..16a069c28 100644 +index e04482d82..512a5e00a 100644 --- a/ssl/ssl_credential.cc +++ b/ssl/ssl_credential.cc -@@ -164,6 +164,7 @@ bool ssl_credential_st::UsesX509() const { - return true; - case SSLCredentialType::kSPAKE2PlusV1Client: - case SSLCredentialType::kSPAKE2PlusV1Server: -+ case SSLCredentialType::kRawPublicKey: - return false; - } - abort(); -@@ -173,6 +174,7 @@ bool ssl_credential_st::UsesPrivateKey() const { - switch (type) { - case SSLCredentialType::kX509: - case SSLCredentialType::kDelegated: -+ case SSLCredentialType::kRawPublicKey: - return true; - case SSLCredentialType::kSPAKE2PlusV1Client: - case SSLCredentialType::kSPAKE2PlusV1Server: -@@ -335,6 +337,10 @@ SSL_CREDENTIAL *SSL_CREDENTIAL_new_delegated(void) { - return New(SSLCredentialType::kDelegated); +@@ -407,6 +407,54 @@ SSL_CREDENTIAL *SSL_CREDENTIAL_new_raw_public_key_custom( + return cred.release(); } -+SSL_CREDENTIAL *SSL_CREDENTIAL_new_raw_public_key(void) { -+ return New(SSLCredentialType::kRawPublicKey); ++SSL_CREDENTIAL *SSL_CREDENTIAL_new_raw_public_key_empty(void) { ++ UniquePtr cred = ++ MakeUnique(SSLCredentialType::kRawPublicKey); ++ if (cred == nullptr) { ++ return nullptr; ++ } ++ return cred.release(); +} + - void SSL_CREDENTIAL_up_ref(SSL_CREDENTIAL *cred) { cred->UpRefInternal(); } - - void SSL_CREDENTIAL_free(SSL_CREDENTIAL *cred) { -@@ -448,6 +454,44 @@ int SSL_CREDENTIAL_set1_delegated_credential(SSL_CREDENTIAL *cred, - return 1; - } - +int SSL_CREDENTIAL_set1_spki(SSL_CREDENTIAL *cred, CRYPTO_BUFFER *spki) { -+ if (cred->type != SSLCredentialType::kRawPublicKey) { ++ auto *cred_impl = FromOpaque(cred); ++ if (cred_impl->type != SSLCredentialType::kRawPublicKey) { + OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; + } @@ -410,13 +112,13 @@ index bbbd76701..16a069c28 100644 + ScopedCBB cbb; + CBS cbs; + if (spki == nullptr) { -+ if (cred->privkey == nullptr) { ++ if (cred_impl->privkey == nullptr) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_PRIVATE_KEY_ASSIGNED); + return 0; + } + + if (!CBB_init(cbb.get(), /*initial_capacity=*/512) || -+ !EVP_marshal_public_key(cbb.get(), cred->privkey.get())) { ++ !EVP_marshal_public_key(cbb.get(), cred_impl->privkey.get())) { + return 0; + } + CBS_init(&cbs, CBB_data(cbb.get()), CBB_len(cbb.get())); @@ -424,1044 +126,60 @@ index bbbd76701..16a069c28 100644 + CRYPTO_BUFFER_init_CBS(spki, &cbs); + } + -+ bssl::UniquePtr pubkey = -+ ssl_parse_peer_subject_public_key_info(cbs); ++ UniquePtr pubkey = ssl_parse_peer_subject_public_key_info(cbs); + if (pubkey == nullptr) { + return 0; + } + -+ if (cred->privkey != nullptr && -+ !ssl_compare_public_and_private_key(pubkey.get(), cred->privkey.get())) { ++ if (cred_impl->privkey != nullptr && ++ !ssl_compare_public_and_private_key(pubkey.get(), ++ cred_impl->privkey.get())) { + return 0; + } + -+ cred->pubkey = std::move(pubkey); ++ cred_impl->pubkey = std::move(pubkey); + return 1; +} + - int SSL_CREDENTIAL_set1_ocsp_response(SSL_CREDENTIAL *cred, - CRYPTO_BUFFER *ocsp) { - if (!cred->UsesX509()) { -@@ -611,6 +655,10 @@ void *SSL_CREDENTIAL_get_ex_data(const SSL_CREDENTIAL *cred, int idx) { - } - - void SSL_CREDENTIAL_set_must_match_issuer(SSL_CREDENTIAL *cred, int match) { -+ if (cred->type == SSLCredentialType::kRawPublicKey) { -+ return; -+ } -+ - cred->must_match_issuer = !!match; + void SSL_CREDENTIAL_up_ref(SSL_CREDENTIAL *cred) { + FromOpaque(cred)->UpRefInternal(); } - diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc -index f64b103fb..d87c3f3c4 100644 +index de4e79efc..31796871d 100644 --- a/ssl/ssl_lib.cc +++ b/ssl/ssl_lib.cc -@@ -534,7 +534,9 @@ SSL *SSL_new(SSL_CTX *ctx) { - if (!ssl->config->supported_group_list.CopyFrom(ctx->supported_group_list) || - !ssl->config->alpn_client_proto_list.CopyFrom( - ctx->alpn_client_proto_list) || -- !ssl->config->verify_sigalgs.CopyFrom(ctx->verify_sigalgs)) { -+ !ssl->config->verify_sigalgs.CopyFrom(ctx->verify_sigalgs) || -+ !ssl->config->server_certificate_type_list.CopyFrom( -+ ctx->server_certificate_type_list)) { - return nullptr; +@@ -3743,3 +3743,31 @@ EVP_PKEY *SSL_get0_peer_rpk(const SSL *ssl) { } - -@@ -3305,6 +3307,54 @@ int SSL_CTX_set_tlsext_status_arg(SSL_CTX *ctx, void *arg) { - return 1; + return nullptr; } - -+int SSL_CTX_set_server_certificate_types(SSL_CTX *ctx, const uint8_t *types, -+ size_t types_len) { -+ auto span = Span(types, types_len); -+ if (!span.empty() && !ssl_is_valid_certificate_type_list(span)) { -+ OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SERVER_CERTIFICATE_TYPE_LIST); -+ return 0; ++ ++// The following functions are compatibility shims for the Rust bindings in ++// boring-sys. They are not part of upstream BoringSSL. ++ ++const EVP_PKEY *SSL_get0_peer_pubkey(const SSL *ssl) { ++ if (ssl->s3->hs == nullptr) { ++ return nullptr; + } -+ return ctx->server_certificate_type_list.CopyFrom(span); ++ return ssl->s3->hs->peer_pubkey.get(); +} + -+void SSL_CTX_get0_server_certificate_types(const SSL_CTX *ctx, ++void SSL_CTX_get0_accepted_peer_cert_types(const SSL_CTX *ctx, + const uint8_t **types, + size_t *types_len) { -+ *types = ctx->server_certificate_type_list.data(); -+ *types_len = ctx->server_certificate_type_list.size(); -+} -+ -+int SSL_set_server_certificate_types(SSL *ssl, const uint8_t *types, -+ size_t types_len) { -+ if (ssl->server || ssl->config == nullptr) { -+ return 0; -+ } -+ auto span = Span(types, types_len); -+ if (!span.empty() && !ssl_is_valid_certificate_type_list(span)) { -+ OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SERVER_CERTIFICATE_TYPE_LIST); -+ return 0; -+ } -+ return ssl->config->server_certificate_type_list.CopyFrom(span); ++ *types = ctx->accepted_peer_cert_types.data(); ++ *types_len = ctx->accepted_peer_cert_types.size(); +} + -+void SSL_get0_server_certificate_types(const SSL *ssl, const uint8_t **types, ++void SSL_get0_accepted_peer_cert_types(const SSL *ssl, const uint8_t **types, + size_t *types_len) { -+ if (ssl->server || ssl->config == nullptr) { ++ if (ssl->config == nullptr) { + *types = nullptr; + *types_len = 0; + return; + } -+ *types = ssl->config->server_certificate_type_list.data(); -+ *types_len = ssl->config->server_certificate_type_list.size(); ++ *types = ssl->config->accepted_peer_cert_types.data(); ++ *types_len = ssl->config->accepted_peer_cert_types.size(); +} -+ -+uint8_t SSL_get_server_certificate_type_selected(const SSL *ssl) { -+ if (ssl->s3->hs == nullptr) { -+ return TLS_CERTIFICATE_TYPE_X509; -+ } -+ return ssl->s3->hs->server_certificate_type; -+} -+ - uint16_t SSL_get_curve_id(const SSL *ssl) { return SSL_get_group_id(ssl); } - - const char *SSL_get_curve_name(uint16_t curve_id) { -diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc -index 779a2c37a..85aeb817f 100644 ---- a/ssl/ssl_test.cc -+++ b/ssl/ssl_test.cc -@@ -4398,6 +4398,73 @@ TEST_P(SSLVersionTest, DefaultTicketKeyRotation) { - new_session.get(), true /* reused */)); - } - -+TEST_P(SSLVersionTest, RawPublicKeyCertificate) { -+ static const uint8_t kCertificateTypes[] = { -+ TLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY, 18}; -+ ASSERT_TRUE(SSL_CTX_set_server_certificate_types(client_ctx_.get(), -+ kCertificateTypes, 0)); -+ ASSERT_FALSE(SSL_CTX_set_server_certificate_types(client_ctx_.get(), -+ kCertificateTypes, 2)); -+ ASSERT_TRUE(SSL_CTX_set_server_certificate_types(client_ctx_.get(), -+ kCertificateTypes, 1)); -+ -+ SSL_CTX_set_custom_verify( -+ client_ctx_.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, -+ [](SSL *ssl, uint8_t *out_alert) -> ssl_verify_result_t { -+ EXPECT_EQ(SSL_get_server_certificate_type_selected(ssl), -+ TLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY); -+ -+ const EVP_PKEY *peer_pubkey = SSL_get0_peer_pubkey(ssl); -+ EXPECT_TRUE(peer_pubkey); -+ -+ if (!peer_pubkey) { -+ *out_alert = SSL_AD_CERTIFICATE_UNKNOWN; -+ return ssl_verify_invalid; -+ } -+ -+ SSL_CTX *ctx = SSL_get_SSL_CTX(ssl); -+ if (EVP_PKEY_cmp(reinterpret_cast SSL_CTX_get_app_data(ctx), -+ peer_pubkey) == 1) { -+ return ssl_verify_ok; -+ } -+ -+ *out_alert = SSL_AD_BAD_CERTIFICATE; -+ return ssl_verify_invalid; -+ }); -+ SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_CLIENT); -+ -+ // Server is not configured for raw public keys. -+ ASSERT_FALSE(Connect()); -+ -+ bssl::UniquePtr cred(SSL_CREDENTIAL_new_raw_public_key()); -+ bssl::UniquePtr key = GetECDSATestKey(); -+ ASSERT_TRUE(SSL_CREDENTIAL_set1_private_key(cred.get(), key.get())); -+ ASSERT_FALSE(SSL_CTX_add1_credential(server_ctx_.get(), cred.get())); -+ ASSERT_TRUE(SSL_CREDENTIAL_set1_spki(cred.get(), nullptr)); -+ ASSERT_TRUE(SSL_CTX_add1_credential(server_ctx_.get(), cred.get())); -+ -+ // Client is expecting |wrong_key|. -+ bssl::UniquePtr wrong_key = GetTestKey(); -+ ASSERT_TRUE(wrong_key); -+ SSL_CTX_set_app_data(client_ctx_.get(), wrong_key.get()); -+ ASSERT_FALSE(Connect()); -+ -+ if (!is_tls13()) { -+ return; -+ } -+ -+ SSL_CTX_set_app_data(client_ctx_.get(), key.get()); -+ ASSERT_TRUE(Connect()); -+ -+ bssl::UniquePtr session = -+ CreateClientSession(client_ctx_.get(), server_ctx_.get()); -+ ASSERT_TRUE(session); -+ -+ TRACED_CALL(ExpectSessionReused(client_ctx_.get(), server_ctx_.get(), -+ session.get(), -+ true /* expect session reused */)); -+} -+ - static int SwitchContext(SSL *ssl, int *out_alert, void *arg) { - SSL_CTX *ctx = reinterpret_cast(arg); - SSL_set_SSL_CTX(ssl, ctx); -diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc -index 0eba60d22..9e2fe8da9 100644 ---- a/ssl/test/bssl_shim.cc -+++ b/ssl/test/bssl_shim.cc -@@ -672,7 +672,8 @@ static bool CheckHandshakeProperties(SSL *ssl, bool is_resume, - return false; - } - } else if (!config->is_server || config->require_any_client_certificate) { -- if (SSL_get_peer_cert_chain(ssl) == nullptr) { -+ if (!config->raw_public_key_mode && -+ SSL_get_peer_cert_chain(ssl) == nullptr) { - fprintf(stderr, "Received no peer certificate but expected one.\n"); - return false; - } -diff --git a/ssl/test/runner/certificate_tests.go b/ssl/test/runner/certificate_tests.go -index 7f6c4b82d..0007f9048 100644 ---- a/ssl/test/runner/certificate_tests.go -+++ b/ssl/test/runner/certificate_tests.go -@@ -14,7 +14,11 @@ - - package runner - --import "crypto/x509" -+import ( -+ "crypto/x509" -+ "encoding/base64" -+ "strconv" -+) - - func makeCertPoolFromRoots(creds ...*Credential) *x509.CertPool { - certPool := x509.NewCertPool() -@@ -327,6 +331,134 @@ func addCertificateTests() { - } - } - -+func addRawPublicKeyCertificateTests() { -+ const decodeError = ":DECODE_ERROR:" -+ const unknownCertType = ":UNKNOWN_CERTIFICATE_TYPE:" -+ var extValueTests = []struct { -+ serverCertificateTypes []uint8 -+ expectedError string -+ }{ -+ // Explicitly requesting X.509 should be fine. -+ {[]uint8{certificateTypeX509}, ""}, -+ // ... even when mixed with unknown types. -+ {[]uint8{80, certificateTypeX509, 81, 82}, ""}, -+ // ... even when mixed with a request for raw public keys. -+ {[]uint8{certificateTypeRawPublicKey, certificateTypeX509, 81, 82}, ""}, -+ {[]uint8{certificateTypeX509, certificateTypeRawPublicKey, 81, 82}, ""}, -+ // Requesting only unknown certificate types should cause an error. -+ {[]uint8{80, 81, 82}, unknownCertType}, -+ // ... as should requesting a raw public key when the server is configured -+ // for X.509. -+ {[]uint8{certificateTypeRawPublicKey}, unknownCertType}, -+ // Listing no types is an error. -+ {[]uint8{}, decodeError}, -+ } -+ -+ for i, test := range extValueTests { -+ testCases = append(testCases, testCase{ -+ testType: serverTest, -+ name: "RawPublicKey-Server-ExtValue-" + strconv.Itoa(i), -+ config: Config{ -+ MinVersion: VersionTLS13, -+ MaxVersion: VersionTLS13, -+ Bugs: ProtocolBugs{ -+ ServerCertificateTypes: test.serverCertificateTypes, -+ }, -+ }, -+ shouldFail: len(test.expectedError) != 0, -+ expectedError: test.expectedError, -+ }) -+ } -+ -+ // An X.509 client should be rejected by a raw-public-key server. -+ testCases = append(testCases, testCase{ -+ testType: serverTest, -+ name: "RawPublicKey-Server-TLS13X509Client", -+ config: Config{ -+ MinVersion: VersionTLS13, -+ MaxVersion: VersionTLS13, -+ }, -+ flags: []string{ -+ "-raw-public-key-mode", -+ }, -+ shouldFail: true, -+ expectedError: unknownCertType, -+ }) -+ -+ testCases = append(testCases, testCase{ -+ testType: serverTest, -+ name: "RawPublicKey-Server", -+ config: Config{ -+ MinVersion: VersionTLS13, -+ MaxVersion: VersionTLS13, -+ Credential: ecdsaP384Certificate.WithSignatureAlgorithms(signatureECDSAWithP384AndSHA384), -+ useServerRawPublicKeyCertificate: true, -+ }, -+ flags: []string{ -+ "-raw-public-key-mode", -+ }, -+ shimCertificate: &ecdsaP384Certificate, -+ }) -+ -+ leaf, _ := x509.ParseCertificate(ecdsaP384Certificate.Certificate[0]) -+ base64SPKI := base64.StdEncoding.EncodeToString(leaf.RawSubjectPublicKeyInfo) -+ wrongLeaf, _ := x509.ParseCertificate(ecdsaP256Certificate.Certificate[0]) -+ wrongBase64SPKI := base64.StdEncoding.EncodeToString(wrongLeaf.RawSubjectPublicKeyInfo) -+ -+ for _, ok := range []bool{false, true} { -+ expectedSPKI, suffix, expectedError := base64SPKI, "", "" -+ if !ok { -+ expectedSPKI = wrongBase64SPKI -+ suffix = "-Mismatch" -+ expectedError = ":CERTIFICATE_VERIFY_FAILED:" -+ } -+ -+ testCases = append(testCases, testCase{ -+ testType: clientTest, -+ name: "RawPublicKey-Client" + suffix, -+ config: Config{ -+ MinVersion: VersionTLS13, -+ MaxVersion: VersionTLS13, -+ Credential: ecdsaP384Certificate.WithSignatureAlgorithms(signatureECDSAWithP384AndSHA384), -+ useServerRawPublicKeyCertificate: true, -+ }, -+ flags: []string{ -+ "-raw-public-key-mode", -+ "-verify-peer", -+ "-use-custom-verify-callback", -+ "-expect-spki", expectedSPKI, -+ }, -+ shouldFail: !ok, -+ expectedError: expectedError, -+ }) -+ } -+ -+ // Read the server's raw public key in a CompressedCertificate message. -+ testCases = append(testCases, testCase{ -+ testType: clientTest, -+ name: "RawPublicKey-Client-With-Compression", -+ config: Config{ -+ MinVersion: VersionTLS13, -+ MaxVersion: VersionTLS13, -+ Credential: ecdsaP384Certificate.WithSignatureAlgorithms(signatureECDSAWithP384AndSHA384), -+ useServerRawPublicKeyCertificate: true, -+ CertCompressionAlgs: map[uint16]CertCompressionAlg{ -+ expandingCompressionAlgID: expandingCompression, -+ }, -+ Bugs: ProtocolBugs{ -+ ExpectedCompressedCert: expandingCompressionAlgID, -+ }, -+ }, -+ flags: []string{ -+ "-raw-public-key-mode", -+ "-verify-peer", -+ "-use-custom-verify-callback", -+ "-expect-spki", base64SPKI, -+ "-install-cert-compression-algs", -+ }, -+ }) -+} -+ - func addRetainOnlySHA256ClientCertTests() { - for _, ver := range tlsVersions { - // Test that enabling -diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go -index 7dbde72c9..f1fb623ea 100644 ---- a/ssl/test/runner/common.go -+++ b/ssl/test/runner/common.go -@@ -140,6 +140,12 @@ func messageTypeToString(typ uint8) string { - return fmt.Sprintf("unknown(%d)", typ) - } - -+// TLS certificate type extension values. -+const ( -+ certificateTypeX509 uint8 = 0 -+ certificateTypeRawPublicKey uint8 = 2 -+) -+ - // TLS compression types. - const ( - compressionNone uint8 = 0 -@@ -155,6 +161,7 @@ const ( - extensionUseSRTP uint16 = 14 - extensionALPN uint16 = 16 - extensionSignedCertificateTimestamp uint16 = 18 -+ extensionServerCertificateType uint16 = 20 // RFC7250 - extensionPadding uint16 = 21 - extensionExtendedMasterSecret uint16 = 23 - extensionCompressedCertAlgs uint16 = 27 -@@ -505,6 +512,14 @@ type Config struct { - // If Time is nil, TLS uses time.Now. - Time func() time.Time - -+ // useServerRawPublicKeyCertificate indicates, for TLS 1.3 only, that raw -+ // public keys should be used. For servers, the DER-encoded X.509 -+ // SubjectPublicKeyInfo field of Certificates[0].Certificate[0] will be the -+ // CertificateEntry of Certificate messages, not including any -+ // CertificateEntry extensions. For clients, the field should be used to -+ // verify the server's Certificate message. -+ useServerRawPublicKeyCertificate bool -+ - // Credential contains the credential to present to the other side of - // the connection. Server configurations must include this field. - Credential *Credential -@@ -2172,6 +2187,10 @@ type ProtocolBugs struct { - // NewSessionTicket messages to have or not have the resumption_across_names - // flag set. - ExpectResumptionAcrossNames *bool -+ -+ // ServerCertificateTypes, if not nil, contains the contents of the server -+ // certificate types extension sent by a client, or echoed by a server. -+ ServerCertificateTypes []uint8 - } - - func (c *Config) serverInit() { -diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go -index 1c4610815..cce76f47b 100644 ---- a/ssl/test/runner/handshake_client.go -+++ b/ssl/test/runner/handshake_client.go -@@ -557,6 +557,14 @@ func (hs *clientHandshakeState) createClientHello(innerHello *clientHelloMsg, ec - hello.vers = mapClientHelloVersion(maxVersion, c.isDTLS) - } - -+ if maxVersion >= VersionTLS13 && c.config.useServerRawPublicKeyCertificate { -+ hello.serverCertificateTypes = []uint8{certificateTypeRawPublicKey} -+ } -+ -+ if c.config.Bugs.ServerCertificateTypes != nil { -+ hello.serverCertificateTypes = c.config.Bugs.ServerCertificateTypes -+ } -+ - if c.config.Bugs.SendClientVersion != 0 { - hello.vers = c.config.Bugs.SendClientVersion - } -@@ -1345,11 +1353,22 @@ func (hs *clientHandshakeState) doTLS13Handshake(msg any) error { - return errors.New("tls: server certificate unexpectedly did not match trust anchor") - } - -- if err := hs.verifyCertificates(certMsg); err != nil { -- return err -+ ex := encryptedExtensions.extensions -+ if c.config.useServerRawPublicKeyCertificate { -+ if !ex.hasServerCertificateType || ex.serverCertificateType != certificateTypeRawPublicKey { -+ c.sendAlert(alertUnsupportedCertificate) -+ return errors.New("tls: server did not support raw public keys") -+ } -+ if err := hs.verifyRawPublicKeyCertificates(certMsg); err != nil { -+ return err -+ } -+ } else { -+ if err := hs.verifyCertificates(certMsg); err != nil { -+ return err -+ } -+ c.ocspResponse = certMsg.certificates[0].ocspResponse -+ c.sctList = certMsg.certificates[0].sctList - } -- c.ocspResponse = certMsg.certificates[0].ocspResponse -- c.sctList = certMsg.certificates[0].sctList - - certVerifyMsg, err := readHandshakeType[certificateVerifyMsg](c) - if err != nil { -@@ -1854,6 +1873,50 @@ func delegatedCredentialSignedMessage(credBytes []byte, algorithm signatureAlgor - return ret - } - -+func (hs *clientHandshakeState) verifyRawPublicKeyCertificates(certMsg *certificateMsg) error { -+ c := hs.c -+ -+ if len(certMsg.certificates) != 1 { -+ c.sendAlert(alertIllegalParameter) -+ return errors.New("tls: incorrect number of certificates") -+ } -+ -+ leafSPKI := certMsg.certificates[0].data -+ -+ if !c.config.InsecureSkipVerify { -+ expectedCert, err := x509.ParseCertificate(c.config.Credential.Certificate[0]) -+ if err != nil { -+ c.sendAlert(alertInternalError) -+ return errors.New("tls: failed to parse configured certificate: " + err.Error()) -+ } -+ expectedSPKI := expectedCert.RawSubjectPublicKeyInfo -+ -+ if !bytes.Equal(expectedSPKI, leafSPKI) { -+ c.sendAlert(alertBadCertificate) -+ return errors.New("tls: raw public key verification failed") -+ } -+ } -+ -+ leafPublicKey, err := x509.ParsePKIXPublicKey(leafSPKI) -+ if err != nil { -+ c.sendAlert(alertBadCertificate) -+ return errors.New("tls: failed to parse raw public key certificate from server: " + err.Error()) -+ } -+ -+ switch leafPublicKey.(type) { -+ case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey: -+ break -+ default: -+ c.sendAlert(alertUnsupportedCertificate) -+ return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", leafPublicKey) -+ } -+ -+ c.peerCertificates = nil -+ hs.peerPublicKey = leafPublicKey -+ -+ return nil -+} -+ - func (hs *clientHandshakeState) verifyCertificates(certMsg *certificateMsg) error { - c := hs.c - -diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go -index d5097f37c..d570a76ab 100644 ---- a/ssl/test/runner/handshake_messages.go -+++ b/ssl/test/runner/handshake_messages.go -@@ -259,9 +259,10 @@ type clientHelloMsg struct { - prefixExtensions []uint16 - // The following fields are only filled in by |unmarshal| and ignored when - // marshaling a new ClientHello. -- echPayloadStart int -- echPayloadEnd int -- rawExtensions []byte -+ echPayloadStart int -+ echPayloadEnd int -+ rawExtensions []byte -+ serverCertificateTypes []uint8 - } - - func (m *clientHelloMsg) marshalKeyShares(bb *cryptobyte.Builder) { -@@ -634,6 +635,14 @@ func (m *clientHelloMsg) marshalBody(hello *cryptobyte.Builder, typ clientHelloT - body: body.BytesOrPanic(), - }) - } -+ if m.serverCertificateTypes != nil { -+ body := cryptobyte.NewBuilder(nil) -+ addUint8LengthPrefixedBytes(body, m.serverCertificateTypes) -+ extensions = append(extensions, extension{ -+ id: extensionServerCertificateType, -+ body: body.BytesOrPanic(), -+ }) -+ } - // The PSK extension must be last. See https://tools.ietf.org/html/rfc8446#section-4.2.11 - if len(m.pskIdentities) > 0 { - pskExtension := cryptobyte.NewBuilder(nil) -@@ -1144,6 +1153,10 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { - } - m.alpsProtocols = append(m.alpsProtocols, string(protocol)) - } -+ case extensionServerCertificateType: -+ if !readUint8LengthPrefixedBytes(&body, &m.serverCertificateTypes) || len(body) != 0 { -+ return false -+ } - case extensionApplicationSettingsOld: - var protocols cryptobyte.String - if !body.ReadUint16LengthPrefixed(&protocols) || len(body) != 0 { -@@ -1597,6 +1610,8 @@ type serverExtensions struct { - hasApplicationSettingsOld bool - echRetryConfigs []byte - trustAnchors [][]byte -+ hasServerCertificateType bool -+ serverCertificateType uint8 - } - - func (m *serverExtensions) marshal(extensions *cryptobyte.Builder) { -@@ -1731,6 +1746,10 @@ func (m *serverExtensions) marshal(extensions *cryptobyte.Builder) { - extensions.AddUint16(extensionEncryptedClientHello) - addUint16LengthPrefixedBytes(extensions, m.echRetryConfigs) - } -+ if m.hasServerCertificateType { -+ extensions.AddUint16(extensionServerCertificateType) -+ addUint16LengthPrefixedBytes(extensions, []byte{m.serverCertificateType}) -+ } - if len(m.trustAnchors) > 0 { - extensions.AddUint16(extensionTrustAnchors) - extensions.AddUint16LengthPrefixed(func(extension *cryptobyte.Builder) { -@@ -1797,6 +1816,14 @@ func (m *serverExtensions) unmarshal(data cryptobyte.String, version uint16) boo - return false - } - m.channelIDRequested = true -+ case extensionServerCertificateType: -+ if version < VersionTLS13 { -+ return false -+ } -+ if !body.ReadUint8(&m.serverCertificateType) || len(body) != 0 { -+ return false -+ } -+ m.hasServerCertificateType = true - case extensionExtendedMasterSecret: - if len(body) != 0 { - return false -diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go -index 4e6ae98e4..14353b5e9 100644 ---- a/ssl/test/runner/handshake_server.go -+++ b/ssl/test/runner/handshake_server.go -@@ -28,20 +28,22 @@ import ( - // serverHandshakeState contains details of a server handshake in progress. - // It's discarded once the handshake has completed. - type serverHandshakeState struct { -- c *Conn -- clientHello *clientHelloMsg -- hello *serverHelloMsg -- suite *cipherSuite -- ellipticOk bool -- ecdsaOk bool -- sessionState *sessionState -- finishedHash finishedHash -- masterSecret []byte -- certsFromClient [][]byte -- cert *Credential -- finishedBytes []byte -- echHPKEContext *hpke.Context -- echConfigID uint8 -+ c *Conn -+ clientHello *clientHelloMsg -+ hello *serverHelloMsg -+ suite *cipherSuite -+ ellipticOk bool -+ ecdsaOk bool -+ sessionState *sessionState -+ finishedHash finishedHash -+ masterSecret []byte -+ certsFromClient [][]byte -+ cert *Credential -+ finishedBytes []byte -+ echHPKEContext *hpke.Context -+ echConfigID uint8 -+ hasServerCertificateType bool -+ serverCertificateType uint8 - } - - // serverHandshake performs a TLS handshake as a server. -@@ -983,6 +985,18 @@ func (hs *serverHandshakeState) doTLS13Handshake() error { - encryptedExtensions.extensions.hasEarlyData = true - } - -+ if c.vers >= VersionTLS13 && config.useServerRawPublicKeyCertificate { -+ for _, t := range hs.clientHello.serverCertificateTypes { -+ if t != certificateTypeRawPublicKey { -+ continue -+ } -+ hs.hasServerCertificateType = true -+ hs.serverCertificateType = certificateTypeRawPublicKey -+ encryptedExtensions.extensions.hasServerCertificateType = true -+ encryptedExtensions.extensions.serverCertificateType = certificateTypeRawPublicKey -+ } -+ } -+ - // Resolve ECDHE and compute the handshake secret. - if hs.hello.hasKeyShare { - // Once a curve has been selected and a key share identified, -@@ -1184,6 +1198,17 @@ func (hs *serverHandshakeState) doTLS13Handshake() error { - } - if !config.Bugs.EmptyCertificateList { - for i, certData := range useCert.Certificate { -+ if hs.hasServerCertificateType && -+ hs.serverCertificateType == certificateTypeRawPublicKey { -+ cert, err := x509.ParseCertificate(certData) -+ if err != nil { -+ return fmt.Errorf("tls: failed to parse configured certificate: %s", err.Error()) -+ } -+ certMsg.certificates = append( -+ certMsg.certificates, -+ certificateEntry{data: cert.RawSubjectPublicKeyInfo}) -+ break -+ } - cert := certificateEntry{ - data: certData, - } -diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go -index 57f9cc410..091e44f48 100644 ---- a/ssl/test/runner/runner.go -+++ b/ssl/test/runner/runner.go -@@ -789,7 +789,7 @@ func doExchange(test *testCase, config *Config, conn net.Conn, isResume bool, tr - tlsConn = Server(conn, config) - } - } else { -- config.InsecureSkipVerify = true -+ config.InsecureSkipVerify = !config.useServerRawPublicKeyCertificate - if test.protocol == dtls { - tlsConn = DTLSClient(conn, config) - } else { -@@ -2244,6 +2244,7 @@ func main() { - addKeyUpdateTests() - addPAKETests() - addTrustAnchorTests() -+ addRawPublicKeyCertificateTests() - - toAppend, err := convertToSplitHandshakeTests(testCases) - if err != nil { -diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc -index 9a7ee6a68..5b8cd5fa6 100644 ---- a/ssl/test/test_config.cc -+++ b/ssl/test/test_config.cc -@@ -37,6 +37,8 @@ - #include - - #include "../../crypto/internal.h" -+#include "../../crypto/mem_internal.h" -+#include "../../ssl/internal.h" - #include "handshake_util.h" - #include "mock_quic_transport.h" - #include "test_state.h" -@@ -639,6 +641,8 @@ const Flag *FindFlag(const char *name) { - OptionalBoolFalseFlag("-expect-not-resumable-across-names", - &TestConfig::expect_resumable_across_names), - BoolFlag("-no-server-name-ack", &TestConfig::no_server_name_ack), -+ BoolFlag("-raw-public-key-mode", &TestConfig::raw_public_key_mode), -+ Base64Flag("-expect-spki", &TestConfig::expect_spki), - }; - std::sort(ret.begin(), ret.end(), FlagNameComparator{}); - return ret; -@@ -1145,6 +1149,17 @@ static bool CheckVerifyCallback(SSL *ssl) { - fprintf(stderr, "ECH name did not match expected value.\n"); - return false; - } -+ if (!config->expect_spki.empty()) { -+ const EVP_PKEY *pkey = SSL_get0_peer_pubkey(ssl); -+ bssl::ScopedCBB cbb; -+ if (pkey == nullptr || !CBB_init(cbb.get(), /*initial_capacity=*/512) || -+ !EVP_marshal_public_key(cbb.get(), pkey) || -+ OPENSSL_memcmp(config->expect_spki.data(), CBB_data(cbb.get()), -+ CBB_len(cbb.get())) != 0) { -+ fprintf(stderr, "Incorrect SPKI observed\n"); -+ return false; -+ } -+ } - - if (config->expect_peer_match_trust_anchor.has_value() && - !!SSL_peer_matched_trust_anchor(ssl) != -@@ -1852,17 +1867,64 @@ static bool InstallCertificate(SSL *ssl) { - return false; - } - -+ const TestConfig *config = GetTestConfig(ssl); -+ - if (pkey) { - TestState *test_state = GetTestState(ssl); -- const TestConfig *config = GetTestConfig(ssl); -- if (config->async || config->handshake_hints) { -+ // Install a custom private key if testing asynchronous callbacks, or if -+ // testing handshake hints. In the handshake hints case, we wish to check -+ // that hints only mismatch when allowed. -+ const bool use_private_key_method = -+ config->async || config->handshake_hints; -+ if (use_private_key_method) { - // Install a custom private key if testing asynchronous callbacks, or if - // testing handshake hints. In the handshake hints case, we wish to check - // that hints only mismatch when allowed. - test_state->private_key = std::move(pkey); -- SSL_set_private_key_method(ssl, &g_async_private_key_method); -- } else if (!SSL_use_PrivateKey(ssl, pkey.get())) { -- return false; -+ } -+ -+ if (config->raw_public_key_mode) { -+ bssl::UniquePtr cred(SSL_CREDENTIAL_new_raw_public_key()); -+ if (cred == nullptr) { -+ return false; -+ } -+ -+ if (use_private_key_method) { -+ SSL_CREDENTIAL_set_private_key_method(cred.get(), -+ &g_async_private_key_method); -+ -+ bssl::ScopedCBB cbb; -+ if (!CBB_init(cbb.get(), /*initial_capacity=*/512) || -+ !EVP_marshal_public_key(cbb.get(), test_state->private_key.get())) { -+ return false; -+ } -+ bssl::UniquePtr spki(CRYPTO_BUFFER_new( -+ CBB_data(cbb.get()), CBB_len(cbb.get()), /*pool=*/nullptr)); -+ if (spki == nullptr) { -+ return false; -+ } -+ if (!SSL_CREDENTIAL_set1_spki(cred.get(), spki.get())) { -+ return false; -+ } -+ } else { -+ if (!SSL_CREDENTIAL_set1_private_key(cred.get(), pkey.get())) { -+ return false; -+ } -+ if (!SSL_CREDENTIAL_set1_spki(cred.get(), nullptr)) { -+ return false; -+ } -+ } -+ -+ if (!SSL_add1_credential(ssl, cred.get())) { -+ return false; -+ } -+ return true; -+ } else { -+ if (use_private_key_method) { -+ SSL_set_private_key_method(ssl, &g_async_private_key_method); -+ } else if (!SSL_use_PrivateKey(ssl, pkey.get())) { -+ return false; -+ } - } - } - -@@ -2015,8 +2077,8 @@ bssl::UniquePtr TestConfig::SetupCtx(SSL_CTX *old_ctx) const { - } - - if (async && is_server) { -- // Disable the internal session cache. To test asynchronous session lookup, -- // we use an external session cache. -+ // Disable the internal session cache. To test asynchronous session -+ // lookup, we use an external session cache. - SSL_CTX_set_session_cache_mode( - ssl_ctx.get(), SSL_SESS_CACHE_BOTH | SSL_SESS_CACHE_NO_INTERNAL); - SSL_CTX_sess_set_get_cb(ssl_ctx.get(), GetSessionCallback); -@@ -2338,6 +2400,12 @@ bssl::UniquePtr TestConfig::NewSSL( - if (verify_peer) { - mode = SSL_VERIFY_PEER; - } -+ static const uint8_t kCertificateTypes[] = { -+ TLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY}; -+ if (!is_server && raw_public_key_mode && -+ !SSL_set_server_certificate_types(ssl.get(), kCertificateTypes, 1)) { -+ return nullptr; -+ } - if (use_custom_verify_callback) { - SSL_set_custom_verify(ssl.get(), mode, CustomVerifyCallback); - } else if (mode != SSL_VERIFY_NONE) { -@@ -2484,8 +2552,8 @@ bssl::UniquePtr TestConfig::NewSSL( - if (enable_signed_cert_timestamps) { - SSL_enable_signed_cert_timestamps(ssl.get()); - } -- // (D)TLS 1.0 and 1.1 are disabled by default, but the runner expects them to -- // be enabled. -+ // (D)TLS 1.0 and 1.1 are disabled by default, but the runner expects them -+ // to be enabled. - // TODO(davidben): Update the tests to explicitly enable the versions they - // need. - if (!SSL_set_min_proto_version( -@@ -2495,7 +2563,8 @@ bssl::UniquePtr TestConfig::NewSSL( - if (min_version != 0 && !SSL_set_min_proto_version(ssl.get(), min_version)) { - return nullptr; - } -- // TODO(crbug.com/42290594): Remove this once DTLS 1.3 is enabled by default. -+ // TODO(crbug.com/42290594): Remove this once DTLS 1.3 is enabled by -+ // default. - if (is_dtls && max_version == 0 && - !SSL_set_max_proto_version(ssl.get(), DTLS1_3_VERSION)) { - return nullptr; -@@ -2515,7 +2584,8 @@ bssl::UniquePtr TestConfig::NewSSL( - SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_once); - } - if (renegotiate_freely || forbid_renegotiation_after_handshake) { -- // |forbid_renegotiation_after_handshake| will disable renegotiation later. -+ // |forbid_renegotiation_after_handshake| will disable renegotiation -+ // later. - SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_freely); - } - if (renegotiate_ignore) { -diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h -index 9745de035..dc32f8136 100644 ---- a/ssl/test/test_config.h -+++ b/ssl/test/test_config.h -@@ -243,6 +243,8 @@ struct TestConfig { - bool resumption_across_names_enabled = false; - std::optional expect_resumable_across_names; - bool no_server_name_ack = false; -+ bool raw_public_key_mode = false; -+ std::vector expect_spki; - - std::vector handshaker_args; - -diff --git a/ssl/tls13_both.cc b/ssl/tls13_both.cc -index 257e4c997..951ab7d52 100644 ---- a/ssl/tls13_both.cc -+++ b/ssl/tls13_both.cc -@@ -198,6 +198,45 @@ bool tls13_process_certificate(SSL_HANDSHAKE *hs, const SSLMessage &msg, - } - - const bool is_leaf = sk_CRYPTO_BUFFER_num(certs.get()) == 0; -+ -+ // Parse out the extensions. -+ SSLExtension status_request( -+ TLSEXT_TYPE_status_request, -+ !ssl->server && hs->config->ocsp_stapling_enabled); -+ SSLExtension sct( -+ TLSEXT_TYPE_certificate_timestamp, -+ !ssl->server && hs->config->signed_cert_timestamps_enabled); -+ SSLExtension trust_anchors( -+ TLSEXT_TYPE_trust_anchors, -+ !ssl->server && is_leaf && -+ hs->config->requested_trust_anchors.has_value()); -+ uint8_t alert = SSL_AD_DECODE_ERROR; -+ if (!ssl_parse_extensions(&extensions, &alert, -+ {&status_request, &sct, &trust_anchors}, -+ /*ignore_unknown=*/false)) { -+ ssl_send_alert(ssl, SSL3_AL_FATAL, alert); -+ return false; -+ } -+ -+ if (!ssl->server && -+ hs->server_certificate_type == TLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY) { -+ if (pkey) { -+ // Only a single "certificate" is allowed if using raw public keys. -+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); -+ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); -+ return false; -+ } -+ -+ pkey = UniquePtr(EVP_parse_public_key(&certificate)); -+ if (!pkey || CBS_len(&certificate) != 0) { -+ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); -+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); -+ return false; -+ } -+ -+ continue; -+ } -+ - if (is_leaf) { - pkey = ssl_cert_parse_pubkey(&certificate); - if (!pkey) { -@@ -228,25 +267,6 @@ bool tls13_process_certificate(SSL_HANDSHAKE *hs, const SSLMessage &msg, - return false; - } - -- // Parse out the extensions. -- SSLExtension status_request( -- TLSEXT_TYPE_status_request, -- !ssl->server && hs->config->ocsp_stapling_enabled); -- SSLExtension sct( -- TLSEXT_TYPE_certificate_timestamp, -- !ssl->server && hs->config->signed_cert_timestamps_enabled); -- SSLExtension trust_anchors( -- TLSEXT_TYPE_trust_anchors, -- !ssl->server && is_leaf && -- hs->config->requested_trust_anchors.has_value()); -- uint8_t alert = SSL_AD_DECODE_ERROR; -- if (!ssl_parse_extensions(&extensions, &alert, -- {&status_request, &sct, &trust_anchors}, -- /*ignore_unknown=*/false)) { -- ssl_send_alert(ssl, SSL3_AL_FATAL, alert); -- return false; -- } -- - // All Certificate extensions are parsed, but only the leaf extensions are - // stored. - if (status_request.present) { -@@ -313,7 +333,16 @@ bool tls13_process_certificate(SSL_HANDSHAKE *hs, const SSLMessage &msg, - return false; - } - -- if (sk_CRYPTO_BUFFER_num(hs->new_session->certs.get()) == 0) { -+ if (!ssl->server && -+ hs->server_certificate_type == TLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY) { -+ if (!hs->peer_pubkey) { -+ OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE); -+ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_CERTIFICATE_REQUIRED); -+ return false; -+ } -+ -+ return true; -+ } else if (sk_CRYPTO_BUFFER_num(hs->new_session->certs.get()) == 0) { - if (!allow_anonymous) { - OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE); - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_CERTIFICATE_REQUIRED); -@@ -436,13 +465,28 @@ bool tls13_add_certificate(SSL_HANDSHAKE *hs) { - return ssl_add_message_cbb(ssl, cbb.get()); - } - -- assert(hs->credential->UsesX509()); -- CRYPTO_BUFFER *leaf_buf = sk_CRYPTO_BUFFER_value(cred->chain.get(), 0); -- CBB leaf, extensions; -- if (!CBB_add_u24_length_prefixed(&certificate_list, &leaf) || -- !CBB_add_bytes(&leaf, CRYPTO_BUFFER_data(leaf_buf), -- CRYPTO_BUFFER_len(leaf_buf)) || -- !CBB_add_u16_length_prefixed(&certificate_list, &extensions)) { -+ CBB leaf; -+ if (!CBB_add_u24_length_prefixed(&certificate_list, &leaf)) { -+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); -+ return false; -+ } -+ -+ if (hs->credential->type == SSLCredentialType::kRawPublicKey) { -+ if (!EVP_marshal_public_key(&leaf, cred->pubkey.get())) { -+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); -+ return false; -+ } -+ } else { -+ CRYPTO_BUFFER *leaf_buf = sk_CRYPTO_BUFFER_value(cred->chain.get(), 0); -+ if (!CBB_add_bytes(&leaf, CRYPTO_BUFFER_data(leaf_buf), -+ CRYPTO_BUFFER_len(leaf_buf))) { -+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); -+ return false; -+ } -+ } -+ -+ CBB extensions; -+ if (!CBB_add_u16_length_prefixed(&certificate_list, &extensions)) { - OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - return false; - } -diff --git a/ssl/tls13_server.cc b/ssl/tls13_server.cc -index eade4bd66..9de0bea28 100644 ---- a/ssl/tls13_server.cc -+++ b/ssl/tls13_server.cc -@@ -261,6 +261,7 @@ bool ssl_check_tls13_credential_ignoring_issuer(SSL_HANDSHAKE *hs, - uint16_t *out_sigalg) { - switch (cred->type) { - case SSLCredentialType::kX509: -+ case SSLCredentialType::kRawPublicKey: - break; - case SSLCredentialType::kDelegated: - // Check that the peer supports the signature over the delegated -@@ -284,7 +285,34 @@ bool ssl_check_tls13_credential_ignoring_issuer(SSL_HANDSHAKE *hs, - - static bool check_signature_credential(SSL_HANDSHAKE *hs, - const SSL_CREDENTIAL *cred, -- uint16_t *out_sigalg) { -+ uint16_t *out_sigalg, -+ uint8_t *cert_type) { -+ switch (cred->type) { -+ case SSLCredentialType::kDelegated: -+ case SSLCredentialType::kX509: -+ *cert_type = TLS_CERTIFICATE_TYPE_X509; -+ break; -+ case SSLCredentialType::kRawPublicKey: -+ if (hs->server_certificate_type_list.empty()) { -+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE); -+ return false; -+ } -+ *cert_type = TLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY; -+ break; -+ default: -+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE); -+ return false; -+ } -+ -+ if (!hs->server_certificate_type_list.empty() && -+ std::none_of( -+ hs->server_certificate_type_list.begin(), -+ hs->server_certificate_type_list.end(), -+ [cert_type](const auto &type) { return *cert_type == type; })) { -+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE); -+ return false; -+ } -+ - return ssl_check_tls13_credential_ignoring_issuer(hs, cred, out_sigalg) && - // Use this credential if it either matches a requested issuer, - // or does not require issuer matching. -@@ -359,9 +387,11 @@ static enum ssl_hs_wait_t do_select_parameters(SSL_HANDSHAKE *hs) { - } - } else { - uint16_t sigalg; -- if (check_signature_credential(hs, cred, &sigalg)) { -+ uint8_t cert_type; -+ if (check_signature_credential(hs, cred, &sigalg, &cert_type)) { - hs->credential = UpRef(cred); - hs->signature_algorithm = sigalg; -+ hs->server_certificate_type = cert_type; - break; - } - } -- -2.40.0 +2.50.1 (Apple Git-155) diff --git a/boring-sys/src/lib.rs b/boring-sys/src/lib.rs index 1810d174c..a16eb92b6 100644 --- a/boring-sys/src/lib.rs +++ b/boring-sys/src/lib.rs @@ -33,7 +33,7 @@ pub use generated::{FIPS_mode, SSL_CTX_set_compliance_policy}; // your include p #[cfg(feature = "mlkem")] pub use generated::{MLKEM768_encap, MLKEM768_private_key_from_seed}; // your include path is incorrect or has a version of boringssl without mlkem support #[cfg(feature = "rpk")] -pub use generated::{SSL_CREDENTIAL_new_raw_public_key, SSL_CREDENTIAL_set1_spki}; // your include path is incorrect or has a version of boringssl without rpk support +pub use generated::{SSL_CREDENTIAL_new_raw_public_key_empty, SSL_CREDENTIAL_set1_spki}; // your include path is incorrect or has a version of boringssl without rpk support pub use generated::*; diff --git a/boring/src/ssl/credential.rs b/boring/src/ssl/credential.rs index df8fb0c30..12e821a96 100644 --- a/boring/src/ssl/credential.rs +++ b/boring/src/ssl/credential.rs @@ -29,12 +29,12 @@ foreign_type_and_impl_send_sync! { impl SslCredential { /// Create a credential suitable for a handshake using a raw public key. - #[corresponds(SSL_CREDENTIAL_new_raw_public_key)] + #[corresponds(SSL_CREDENTIAL_new_raw_public_key_empty)] #[cfg(feature = "rpk")] pub fn new_raw_public_key() -> Result { unsafe { Ok(SslCredentialBuilder(Self::from_ptr(cvt_p( - ffi::SSL_CREDENTIAL_new_raw_public_key(), + ffi::SSL_CREDENTIAL_new_raw_public_key_empty(), )?))) } } @@ -172,9 +172,9 @@ impl SslCredentialBuilder { } } - // Sets the SPKI of the raw public key credential. - // - // If `spki` is `None`, the SPKI is extracted from the credential's private key. + /// Sets the SPKI of the raw public key credential. + /// + /// If `spki` is `None`, the SPKI is extracted from the credential's private key. #[corresponds(SSL_CREDENTIAL_set1_spki)] #[cfg(feature = "rpk")] pub fn set_spki_bytes(&mut self, spki: Option<&[u8]>) -> Result<(), ErrorStack> { @@ -209,3 +209,64 @@ impl SslCredentialBuilder { unsafe fn get_new_ssl_credential_idx(f: ffi::CRYPTO_EX_free) -> c_int { ffi::SSL_CREDENTIAL_get_ex_new_index(0, ptr::null_mut(), ptr::null_mut(), None, f) } + +#[cfg(all(test, feature = "rpk"))] +mod tests { + use super::*; + use crate::ec::{EcGroup, EcKey}; + use crate::nid::Nid; + use crate::pkey::PKey; + + fn generate_key() -> PKey { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let ec_key = EcKey::generate(&group).unwrap(); + PKey::from_ec_key(ec_key).unwrap() + } + + fn spki_from_pkey(pkey: &PKey) -> Vec { + pkey.public_key_to_der().unwrap() + } + + #[test] + fn set_spki_bytes_none_derives_from_private_key() { + let key = generate_key(); + + let mut cred = SslCredential::new_raw_public_key().unwrap(); + cred.set_private_key(&key).unwrap(); + // With a private key set, passing `None` should succeed and derive the + // SPKI from the private key. + cred.set_spki_bytes(None).unwrap(); + } + + #[test] + fn set_spki_bytes_none_without_private_key_fails() { + let mut cred = SslCredential::new_raw_public_key().unwrap(); + // Without a private key, passing `None` should return an error. + let err = cred.set_spki_bytes(None).unwrap_err(); + assert!(!err.errors().is_empty()); + } + + #[test] + fn set_spki_bytes_some_matching_private_key_succeeds() { + let key = generate_key(); + let spki = spki_from_pkey(&key); + + let mut cred = SslCredential::new_raw_public_key().unwrap(); + cred.set_private_key(&key).unwrap(); + cred.set_spki_bytes(Some(&spki)).unwrap(); + } + + #[test] + fn set_spki_bytes_some_mismatched_private_key_fails() { + let key = generate_key(); + let other_key = generate_key(); + let wrong_spki = spki_from_pkey(&other_key); + + let mut cred = SslCredential::new_raw_public_key().unwrap(); + cred.set_private_key(&key).unwrap(); + // The public key derived from `wrong_spki` does not match the + // configured private key, so this must fail. + let err = cred.set_spki_bytes(Some(&wrong_spki)).unwrap_err(); + assert!(!err.errors().is_empty()); + } +} diff --git a/boring/src/ssl/mod.rs b/boring/src/ssl/mod.rs index cbdfc6309..e5232fb8f 100644 --- a/boring/src/ssl/mod.rs +++ b/boring/src/ssl/mod.rs @@ -2056,14 +2056,14 @@ impl SslContextBuilder { /// Sets the list of server certificate types that clients attached to this context /// can process. - #[corresponds(SSL_CTX_set_server_certificate_types)] + #[corresponds(SSL_CTX_set1_accepted_peer_cert_types)] #[cfg(feature = "rpk")] pub fn set_server_certificate_types( &mut self, types: &[CertificateType], ) -> Result<(), ErrorStack> { unsafe { - cvt_0i(ffi::SSL_CTX_set_server_certificate_types( + cvt_0i(ffi::SSL_CTX_set1_accepted_peer_cert_types( self.as_ptr(), types.as_ptr() as *const u8, types.len(), @@ -2366,7 +2366,7 @@ impl SslContextRef { let mut types = ptr::null(); let mut types_len = 0; unsafe { - ffi::SSL_CTX_get0_server_certificate_types(self.as_ptr(), &mut types, &mut types_len); + ffi::SSL_CTX_get0_accepted_peer_cert_types(self.as_ptr(), &mut types, &mut types_len); if types_len == 0 { return None; @@ -3862,16 +3862,24 @@ impl SslRef { } } - /// Sets the list of server certificate types that clients attached to this `Ssl` - /// can process. - #[corresponds(SSL_set_server_certificate_types)] + /// Sets the list of server certificate types that this client will accept + /// from the server. + /// + /// Only valid on a client-side `Ssl`; returns an error on server-side SSLs. + #[corresponds(SSL_set1_accepted_peer_cert_types)] #[cfg(feature = "rpk")] pub fn set_server_certificate_types( &mut self, types: &[CertificateType], ) -> Result<(), ErrorStack> { + if self.is_server() { + return Err(ErrorStack::internal_error_str( + "called set_server_certificate_types as server", + )); + } + unsafe { - cvt_0i(ffi::SSL_set_server_certificate_types( + cvt_0i(ffi::SSL_set1_accepted_peer_cert_types( self.as_ptr(), types.as_ptr() as *const u8, types.len(), @@ -3880,15 +3888,22 @@ impl SslRef { } } - /// Returns the list of server certificate types. - #[corresponds(SSL_get0_server_certificate_types)] + /// Returns the list of server certificate types that this client will + /// accept from the server, or `None` if none are configured. + /// + /// Only valid on a client-side `Ssl`; returns `None` on server-side SSLs. + #[corresponds(SSL_get0_accepted_peer_cert_types)] #[must_use] #[cfg(feature = "rpk")] pub fn server_certificate_types(&self) -> Option<&[CertificateType]> { + if self.is_server() { + return None; + } + let mut types = ptr::null(); let mut types_len = 0; unsafe { - ffi::SSL_get0_server_certificate_types(self.as_ptr(), &mut types, &mut types_len); + ffi::SSL_get0_accepted_peer_cert_types(self.as_ptr(), &mut types, &mut types_len); if types_len == 0 { return None; @@ -3901,13 +3916,24 @@ impl SslRef { } } - /// Returns the server certificate type selected by the server, or `CertificateType::X509` - /// if there is no handshake. - #[corresponds(SSL_get_server_certificate_type_selected)] + /// Returns the server certificate type selected by the server during the + /// handshake. + /// + /// Only valid on a client-side `Ssl`; returns `None` on server-side SSLs + /// (a server knows its own selected credential type by other means). + #[corresponds(SSL_get_peer_cert_type)] #[must_use] #[cfg(feature = "rpk")] - pub fn selected_server_certificate_type(&self) -> CertificateType { - unsafe { CertificateType(ffi::SSL_get_server_certificate_type_selected(self.as_ptr())) } + pub fn selected_server_certificate_type(&self) -> Option { + if self.is_server() { + return None; + } + + unsafe { + Some(CertificateType( + ffi::SSL_get_peer_cert_type(self.as_ptr()) as u8 + )) + } } } @@ -4460,10 +4486,10 @@ pub struct CertificateType(u8); #[cfg(feature = "rpk")] impl CertificateType { /// A X.509 certificate. - pub const X509: Self = Self(ffi::TLS_CERTIFICATE_TYPE_X509 as u8); + pub const X509: Self = Self(ffi::TLSEXT_cert_type_x509 as u8); /// A raw public key. - pub const RAW_PUBLIC_KEY: Self = Self(ffi::TLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY as u8); + pub const RAW_PUBLIC_KEY: Self = Self(ffi::TLSEXT_cert_type_rpk as u8); } /// The result of a shutdown request.