Add ALPS codepoint supports for split handshake This change adds ALPS codepoint supports on split handshake. serialize_features sends list of codepoints, handshaker will serialize the codepoint when calling SSL_serialize_handback, and send the chosen codepoint to the server, server can apply the codepoint when SSL_apply_handback. Change-Id: Id7bc985c4b9847b7c337595f1bc23b2af93d96e7 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/63265 Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/err/ssl.errordata b/crypto/err/ssl.errordata index 7e588c5..a8e6e08 100644 --- a/crypto/err/ssl.errordata +++ b/crypto/err/ssl.errordata
@@ -83,6 +83,7 @@ SSL,321,INCONSISTENT_ECH_NEGOTIATION SSL,259,INVALID_ALPN_PROTOCOL SSL,315,INVALID_ALPN_PROTOCOL_LIST +SSL,322,INVALID_ALPS_CODEPOINT SSL,314,INVALID_CLIENT_HELLO_INNER SSL,158,INVALID_COMMAND SSL,256,INVALID_COMPRESSION_LIST
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index e500dd7..003e0a5 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h
@@ -5840,6 +5840,7 @@ #define SSL_R_ECH_REJECTED 319 #define SSL_R_INVALID_OUTER_EXTENSION 320 #define SSL_R_INCONSISTENT_ECH_NEGOTIATION 321 +#define SSL_R_INVALID_ALPS_CODEPOINT 322 #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/ssl/handoff.cc b/ssl/handoff.cc index 037e070..7f78a1a 100644 --- a/ssl/handoff.cc +++ b/ssl/handoff.cc
@@ -41,7 +41,7 @@ // serialize_features adds a description of features supported by this binary to // |out|. Returns true on success and false on error. -static bool serialize_features(CBB *out, uint16_t alps_extension_type) { +static bool serialize_features(CBB *out) { CBB ciphers; if (!CBB_add_asn1(out, &ciphers, CBS_ASN1_OCTETSTRING)) { return false; @@ -68,7 +68,8 @@ // removed. CBB alps; if (!CBB_add_asn1(out, &alps, kHandoffTagALPS) || - !CBB_add_u16(&alps, alps_extension_type)) { + !CBB_add_u16(&alps, TLSEXT_TYPE_application_settings_old) || + !CBB_add_u16(&alps, TLSEXT_TYPE_application_settings)) { return false; } return CBB_flush(out); @@ -87,17 +88,13 @@ SSLMessage msg; Span<const uint8_t> transcript = s3->hs->transcript.buffer(); - uint16_t alps_extension_type = TLSEXT_TYPE_application_settings_old; - if (s3->hs->config->alps_use_new_codepoint) { - alps_extension_type = TLSEXT_TYPE_application_settings; - } if (!CBB_add_asn1(out, &seq, CBS_ASN1_SEQUENCE) || !CBB_add_asn1_uint64(&seq, kHandoffVersion) || !CBB_add_asn1_octet_string(&seq, transcript.data(), transcript.size()) || !CBB_add_asn1_octet_string(&seq, reinterpret_cast<uint8_t *>(s3->hs_buf->data), s3->hs_buf->length) || - !serialize_features(&seq, alps_extension_type) || + !serialize_features(&seq) || !CBB_flush(out) || !ssl->method->get_message(ssl, &msg) || !ssl_client_hello_init(ssl, out_hello, msg.body)) { @@ -450,6 +447,16 @@ hs->early_traffic_secret().size())) { return false; } + + if (session->has_application_settings) { + uint16_t alps_codepoint = TLSEXT_TYPE_application_settings_old; + if (hs->config->alps_use_new_codepoint) { + alps_codepoint = TLSEXT_TYPE_application_settings; + } + if (!CBB_add_asn1_uint64(&seq, alps_codepoint)) { + return false; + } + } } return CBB_flush(out); } @@ -469,7 +476,8 @@ } SSL3_STATE *const s3 = ssl->s3; - uint64_t handback_version, unused_token_binding_param, cipher, type_u64; + uint64_t handback_version, unused_token_binding_param, cipher, type_u64, + alps_codepoint; CBS seq, read_seq, write_seq, server_rand, client_rand, read_iv, write_iv, next_proto, alpn, hostname, unused_channel_id, transcript, key_share; @@ -569,6 +577,28 @@ !CBS_get_asn1(&seq, &early_traffic_secret, CBS_ASN1_OCTETSTRING)) { return false; } + + if (session->has_application_settings) { + // Making it optional to keep compatibility with older handshakers. + // Older handshakers won't send the field. + if (CBS_len(&seq) == 0) { + hs->config->alps_use_new_codepoint = false; + } else { + if (!CBS_get_asn1_uint64(&seq, &alps_codepoint)) { + return false; + } + + if (alps_codepoint == TLSEXT_TYPE_application_settings) { + hs->config->alps_use_new_codepoint = true; + } else if (alps_codepoint == TLSEXT_TYPE_application_settings_old) { + hs->config->alps_use_new_codepoint = false; + } else { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_ALPS_CODEPOINT); + return false; + } + } + } + if (ticket_age_skew > std::numeric_limits<int32_t>::max() || ticket_age_skew < std::numeric_limits<int32_t>::min()) { return false; @@ -750,13 +780,8 @@ int SSL_serialize_capabilities(const SSL *ssl, CBB *out) { CBB seq; - const SSL_HANDSHAKE *hs = ssl->s3->hs.get(); - uint16_t alps_extension_type = TLSEXT_TYPE_application_settings_old; - if (hs->config->alps_use_new_codepoint) { - alps_extension_type = TLSEXT_TYPE_application_settings; - } if (!CBB_add_asn1(out, &seq, CBS_ASN1_SEQUENCE) || - !serialize_features(&seq, alps_extension_type) || // + !serialize_features(&seq) || // !CBB_flush(out)) { return 0; }
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc index fc3d4c3..a8f4f21 100644 --- a/ssl/ssl_test.cc +++ b/ssl/ssl_test.cc
@@ -1677,6 +1677,38 @@ return std::move(g_last_session); } +static void SetUpExpectedNewCodePoint(SSL_CTX *ctx) { + SSL_CTX_set_select_certificate_cb( + ctx, + [](const SSL_CLIENT_HELLO *client_hello) -> ssl_select_cert_result_t { + const uint8_t *data; + size_t len; + if (!SSL_early_callback_ctx_extension_get( + client_hello, TLSEXT_TYPE_application_settings, &data, + &len)) { + ADD_FAILURE() << "Could not find alps new codepoint."; + return ssl_select_cert_error; + } + return ssl_select_cert_success; + }); +} + +static void SetUpExpectedOldCodePoint(SSL_CTX *ctx) { + SSL_CTX_set_select_certificate_cb( + ctx, + [](const SSL_CLIENT_HELLO *client_hello) -> ssl_select_cert_result_t { + const uint8_t *data; + size_t len; + if (!SSL_early_callback_ctx_extension_get( + client_hello, TLSEXT_TYPE_application_settings_old, &data, + &len)) { + ADD_FAILURE() << "Could not find alps old codepoint."; + return ssl_select_cert_error; + } + return ssl_select_cert_success; + }); +} + // Test that |SSL_get_client_CA_list| echoes back the configured parameter even // before configuring as a server. TEST(SSLTest, ClientCAList) { @@ -5231,7 +5263,11 @@ SSL_set0_wbio(src, nullptr); } -TEST(SSLTest, Handoff) { +void VerifyHandoff(bool use_new_alps_codepoint) { + static const uint8_t alpn[] = {0x03, 'f', 'o', 'o'}; + static const uint8_t proto[] = {'f', 'o', 'o'}; + static const uint8_t alps[] = {0x04, 'a', 'l', 'p', 's'}; + bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method())); bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method())); bssl::UniquePtr<SSL_CTX> handshaker_ctx( @@ -5240,6 +5276,12 @@ ASSERT_TRUE(server_ctx); ASSERT_TRUE(handshaker_ctx); + if (!use_new_alps_codepoint) { + SetUpExpectedOldCodePoint(server_ctx.get()); + } else { + SetUpExpectedNewCodePoint(server_ctx.get()); + } + SSL_CTX_set_session_cache_mode(client_ctx.get(), SSL_SESS_CACHE_CLIENT); SSL_CTX_sess_set_new_cb(client_ctx.get(), SaveLastSession); SSL_CTX_set_handoff_mode(server_ctx.get(), true); @@ -5255,6 +5297,12 @@ ASSERT_TRUE(CreateClientAndServer(&client, &server, client_ctx.get(), server_ctx.get())); SSL_set_early_data_enabled(client.get(), early_data); + + // Set up client ALPS settings. + SSL_set_alps_use_new_codepoint(client.get(), use_new_alps_codepoint); + ASSERT_TRUE(SSL_set_alpn_protos(client.get(), alpn, sizeof(alpn)) == 0); + ASSERT_TRUE(SSL_add_application_settings(client.get(), proto, + sizeof(proto), nullptr, 0)); if (is_resume) { ASSERT_TRUE(g_last_session); SSL_set_session(client.get(), g_last_session.get()); @@ -5295,6 +5343,23 @@ // handshake and newly-issued tickets, entirely by |handshaker|. There is // no need to call |SSL_set_early_data_enabled| on |server|. SSL_set_early_data_enabled(handshaker.get(), 1); + + // Set up handshaker ALPS settings. + SSL_set_alps_use_new_codepoint(handshaker.get(), use_new_alps_codepoint); + SSL_CTX_set_alpn_select_cb( + handshaker_ctx.get(), + [](SSL *ssl, const uint8_t **out, uint8_t *out_len, const uint8_t *in, + unsigned in_len, void *arg) -> int { + return SSL_select_next_proto( + const_cast<uint8_t **>(out), out_len, in, in_len, + alpn, sizeof(alpn)) == OPENSSL_NPN_NEGOTIATED + ? SSL_TLSEXT_ERR_OK + : SSL_TLSEXT_ERR_NOACK; + }, + nullptr); + ASSERT_TRUE(SSL_add_application_settings(handshaker.get(), proto, + sizeof(proto), alps, sizeof(alps))); + ASSERT_TRUE(SSL_apply_handoff(handshaker.get(), handoff)); MoveBIOs(handshaker.get(), server.get()); @@ -5322,6 +5387,8 @@ MoveBIOs(server2.get(), handshaker.get()); ASSERT_TRUE(CompleteHandshakes(client.get(), server2.get())); EXPECT_EQ(is_resume, SSL_session_reused(client.get())); + // Verify application settings. + ASSERT_TRUE(SSL_has_application_settings(client.get())); if (early_data && is_resume) { // In this case, one byte of early data has already been written above. @@ -5342,6 +5409,13 @@ } } +TEST(SSLTest, Handoff) { + for (bool use_new_alps_codepoint : {false, true}) { + SCOPED_TRACE(use_new_alps_codepoint); + VerifyHandoff(use_new_alps_codepoint); + } +} + TEST(SSLTest, HandoffDeclined) { bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method())); bssl::UniquePtr<SSL_CTX> server_ctx( @@ -7994,38 +8068,6 @@ ASSERT_TRUE(server_ctx_); } - void SetUpExpectedNewCodePoint() { - SSL_CTX_set_select_certificate_cb( - server_ctx_.get(), - [](const SSL_CLIENT_HELLO *client_hello) -> ssl_select_cert_result_t { - const uint8_t *data; - size_t len; - if (!SSL_early_callback_ctx_extension_get( - client_hello, TLSEXT_TYPE_application_settings, &data, - &len)) { - ADD_FAILURE() << "Could not find alps new codpoint."; - return ssl_select_cert_error; - } - return ssl_select_cert_success; - }); - } - - void SetUpExpectedOldCodePoint() { - SSL_CTX_set_select_certificate_cb( - server_ctx_.get(), - [](const SSL_CLIENT_HELLO *client_hello) -> ssl_select_cert_result_t { - const uint8_t *data; - size_t len; - if (!SSL_early_callback_ctx_extension_get( - client_hello, TLSEXT_TYPE_application_settings_old, &data, - &len)) { - ADD_FAILURE() << "Could not find alps old codpoint."; - return ssl_select_cert_error; - } - return ssl_select_cert_success; - }); - } - void SetUpApplicationSetting() { static const uint8_t alpn[] = {0x03, 'f', 'o', 'o'}; static const uint8_t proto[] = {'f', 'o', 'o'}; @@ -8058,7 +8100,7 @@ }; TEST_F(AlpsNewCodepointTest, Enabled) { - SetUpExpectedNewCodePoint(); + SetUpExpectedNewCodePoint(server_ctx_.get()); ASSERT_TRUE(CreateClientAndServer(&client_, &server_, client_ctx_.get(), server_ctx_.get())); @@ -8073,7 +8115,7 @@ TEST_F(AlpsNewCodepointTest, Disabled) { // Both client and server disable alps new codepoint. - SetUpExpectedOldCodePoint(); + SetUpExpectedOldCodePoint(server_ctx_.get()); ASSERT_TRUE(CreateClientAndServer(&client_, &server_, client_ctx_.get(), server_ctx_.get())); @@ -8088,7 +8130,7 @@ TEST_F(AlpsNewCodepointTest, ClientOnly) { // If client set new codepoint but server doesn't set, server ignores it. - SetUpExpectedNewCodePoint(); + SetUpExpectedNewCodePoint(server_ctx_.get()); ASSERT_TRUE(CreateClientAndServer(&client_, &server_, client_ctx_.get(), server_ctx_.get())); @@ -8103,7 +8145,7 @@ TEST_F(AlpsNewCodepointTest, ServerOnly) { // If client doesn't set new codepoint, while server set. - SetUpExpectedOldCodePoint(); + SetUpExpectedOldCodePoint(server_ctx_.get()); ASSERT_TRUE(CreateClientAndServer(&client_, &server_, client_ctx_.get(), server_ctx_.get()));