Add post-handshake support for the QUIC API.
Change-Id: I4956efabfb33f7bd60a4743a922c29ee4de18935 Reviewed-on: https://boringssl-review.googlesource.com/c/33004 Commit-Queue: Steven Valdez <svaldez@google.com> CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org> Reviewed-by: David Benjamin <davidben@google.com>
This commit is contained in:
parent
ce45588695
commit
e6eef1ca16
@ -3047,7 +3047,10 @@ OPENSSL_EXPORT void SSL_get_peer_quic_transport_params(const SSL *ssl,
|
|||||||
// |SSL_QUIC_METHOD| to configure secrets and send data. If data is needed from
|
// |SSL_QUIC_METHOD| to configure secrets and send data. If data is needed from
|
||||||
// the peer, it will return |SSL_ERROR_WANT_READ|. When received, the caller
|
// the peer, it will return |SSL_ERROR_WANT_READ|. When received, the caller
|
||||||
// should call |SSL_provide_quic_data| and then |SSL_do_handshake| to continue
|
// should call |SSL_provide_quic_data| and then |SSL_do_handshake| to continue
|
||||||
// the handshake. It is an error to call |SSL_read| and |SSL_write| in QUIC.
|
// the handshake. After the handshake is complete, the caller should call
|
||||||
|
// |SSL_provide_quic_data| for any post-handshake data, followed by
|
||||||
|
// |SSL_process_quic_post_handshake| to process it. It is an error to call
|
||||||
|
// |SSL_read| and |SSL_write| in QUIC.
|
||||||
//
|
//
|
||||||
// Note that secrets for an encryption level may be available to QUIC before the
|
// Note that secrets for an encryption level may be available to QUIC before the
|
||||||
// level is active in TLS. Callers should use |SSL_quic_read_level| to determine
|
// level is active in TLS. Callers should use |SSL_quic_read_level| to determine
|
||||||
@ -3064,8 +3067,7 @@ OPENSSL_EXPORT void SSL_get_peer_quic_transport_params(const SSL *ssl,
|
|||||||
// |SSL_quic_max_handshake_flight_len| to get the maximum buffer length at each
|
// |SSL_quic_max_handshake_flight_len| to get the maximum buffer length at each
|
||||||
// encryption level.
|
// encryption level.
|
||||||
//
|
//
|
||||||
// Note: 0-RTT and post-handshake tickets are not currently supported via this
|
// Note: 0-RTT is not currently supported via this API.
|
||||||
// API.
|
|
||||||
|
|
||||||
// ssl_encryption_level_t represents a specific QUIC encryption level used to
|
// ssl_encryption_level_t represents a specific QUIC encryption level used to
|
||||||
// transmit handshake messages.
|
// transmit handshake messages.
|
||||||
@ -3139,6 +3141,11 @@ OPENSSL_EXPORT int SSL_provide_quic_data(SSL *ssl,
|
|||||||
const uint8_t *data, size_t len);
|
const uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
|
||||||
|
// SSL_process_quic_post_handshake processes any data that QUIC has provided
|
||||||
|
// after the handshake has completed. This includes NewSessionTicket messages
|
||||||
|
// sent by the server. It returns one on success and zero on error.
|
||||||
|
OPENSSL_EXPORT int SSL_process_quic_post_handshake(SSL *ssl);
|
||||||
|
|
||||||
// SSL_CTX_set_quic_method configures the QUIC hooks. This should only be
|
// SSL_CTX_set_quic_method configures the QUIC hooks. This should only be
|
||||||
// configured with a minimum version of TLS 1.3. |quic_method| must remain valid
|
// configured with a minimum version of TLS 1.3. |quic_method| must remain valid
|
||||||
// for the lifetime of |ctx|. It returns one on success and zero on error.
|
// for the lifetime of |ctx|. It returns one on success and zero on error.
|
||||||
|
@ -948,6 +948,33 @@ static int ssl_do_post_handshake(SSL *ssl, const SSLMessage &msg) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SSL_process_quic_post_handshake(SSL *ssl) {
|
||||||
|
ssl_reset_error_state(ssl);
|
||||||
|
|
||||||
|
if (SSL_in_init(ssl)) {
|
||||||
|
OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replay post-handshake message errors.
|
||||||
|
if (!check_read_error(ssl)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process any buffered post-handshake messages.
|
||||||
|
SSLMessage msg;
|
||||||
|
while (ssl->method->get_message(ssl, &msg)) {
|
||||||
|
// Handle the post-handshake message and try again.
|
||||||
|
if (!ssl_do_post_handshake(ssl, msg)) {
|
||||||
|
ssl_set_read_error(ssl);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ssl->method->next_message(ssl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int ssl_read_impl(SSL *ssl) {
|
static int ssl_read_impl(SSL *ssl) {
|
||||||
ssl_reset_error_state(ssl);
|
ssl_reset_error_state(ssl);
|
||||||
|
|
||||||
|
114
ssl/ssl_test.cc
114
ssl/ssl_test.cc
@ -4727,6 +4727,21 @@ class QUICMethodTest : public testing::Test {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CreateSecondClientAndServer() {
|
||||||
|
client_.reset(SSL_new(client_ctx_.get()));
|
||||||
|
server_.reset(SSL_new(server_ctx_.get()));
|
||||||
|
if (!client_ || !server_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SSL_set_connect_state(client_.get());
|
||||||
|
SSL_set_accept_state(server_.get());
|
||||||
|
|
||||||
|
ex_data_.Set(client_.get(), second_transport_.client());
|
||||||
|
ex_data_.Set(server_.get(), second_transport_.server());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// The following functions may be configured on an |SSL_QUIC_METHOD| as
|
// The following functions may be configured on an |SSL_QUIC_METHOD| as
|
||||||
// default implementations.
|
// default implementations.
|
||||||
|
|
||||||
@ -4760,6 +4775,7 @@ class QUICMethodTest : public testing::Test {
|
|||||||
|
|
||||||
static UnownedSSLExData<MockQUICTransport> ex_data_;
|
static UnownedSSLExData<MockQUICTransport> ex_data_;
|
||||||
MockQUICTransportPair transport_;
|
MockQUICTransportPair transport_;
|
||||||
|
MockQUICTransportPair second_transport_;
|
||||||
|
|
||||||
bssl::UniquePtr<SSL> client_;
|
bssl::UniquePtr<SSL> client_;
|
||||||
bssl::UniquePtr<SSL> server_;
|
bssl::UniquePtr<SSL> server_;
|
||||||
@ -4776,6 +4792,10 @@ TEST_F(QUICMethodTest, Basic) {
|
|||||||
SendAlertCallback,
|
SendAlertCallback,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
g_last_session = nullptr;
|
||||||
|
|
||||||
|
SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH);
|
||||||
|
SSL_CTX_sess_set_new_cb(client_ctx_.get(), SaveLastSession);
|
||||||
ASSERT_TRUE(SSL_CTX_set_quic_method(client_ctx_.get(), &quic_method));
|
ASSERT_TRUE(SSL_CTX_set_quic_method(client_ctx_.get(), &quic_method));
|
||||||
ASSERT_TRUE(SSL_CTX_set_quic_method(server_ctx_.get(), &quic_method));
|
ASSERT_TRUE(SSL_CTX_set_quic_method(server_ctx_.get(), &quic_method));
|
||||||
ASSERT_TRUE(CreateClientAndServer());
|
ASSERT_TRUE(CreateClientAndServer());
|
||||||
@ -4807,13 +4827,43 @@ TEST_F(QUICMethodTest, Basic) {
|
|||||||
EXPECT_FALSE(transport_.server()->has_alert());
|
EXPECT_FALSE(transport_.server()->has_alert());
|
||||||
|
|
||||||
// The server sent NewSessionTicket messages in the handshake.
|
// The server sent NewSessionTicket messages in the handshake.
|
||||||
//
|
EXPECT_FALSE(g_last_session);
|
||||||
// TODO(davidben,svaldez): Add an API for the client to consume post-handshake
|
ASSERT_TRUE(ProvideHandshakeData(client_.get()));
|
||||||
// messages and update these tests.
|
EXPECT_EQ(SSL_process_quic_post_handshake(client_.get()), 1);
|
||||||
std::vector<uint8_t> new_session_ticket;
|
EXPECT_TRUE(g_last_session);
|
||||||
ASSERT_TRUE(transport_.client()->ReadHandshakeData(
|
|
||||||
&new_session_ticket, ssl_encryption_application));
|
// Create a second connection to verify resumption works.
|
||||||
EXPECT_FALSE(new_session_ticket.empty());
|
ASSERT_TRUE(CreateSecondClientAndServer());
|
||||||
|
bssl::UniquePtr<SSL_SESSION> session = std::move(g_last_session);
|
||||||
|
SSL_set_session(client_.get(), session.get());
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
ASSERT_TRUE(ProvideHandshakeData(client_.get()));
|
||||||
|
int client_ret = SSL_do_handshake(client_.get());
|
||||||
|
if (client_ret != 1) {
|
||||||
|
ASSERT_EQ(client_ret, -1);
|
||||||
|
ASSERT_EQ(SSL_get_error(client_.get(), client_ret), SSL_ERROR_WANT_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_TRUE(ProvideHandshakeData(server_.get()));
|
||||||
|
int server_ret = SSL_do_handshake(server_.get());
|
||||||
|
if (server_ret != 1) {
|
||||||
|
ASSERT_EQ(server_ret, -1);
|
||||||
|
ASSERT_EQ(SSL_get_error(server_.get(), server_ret), SSL_ERROR_WANT_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client_ret == 1 && server_ret == 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(SSL_do_handshake(client_.get()), 1);
|
||||||
|
EXPECT_EQ(SSL_do_handshake(server_.get()), 1);
|
||||||
|
EXPECT_TRUE(transport_.SecretsMatch(ssl_encryption_application));
|
||||||
|
EXPECT_FALSE(transport_.client()->has_alert());
|
||||||
|
EXPECT_FALSE(transport_.server()->has_alert());
|
||||||
|
EXPECT_TRUE(SSL_session_reused(client_.get()));
|
||||||
|
EXPECT_TRUE(SSL_session_reused(server_.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test only releasing data to QUIC one byte at a time on request, to maximize
|
// Test only releasing data to QUIC one byte at a time on request, to maximize
|
||||||
@ -5073,6 +5123,56 @@ TEST_F(QUICMethodTest, TooMuchData) {
|
|||||||
SSL_provide_quic_data(client_.get(), ssl_encryption_initial, &b, 1));
|
SSL_provide_quic_data(client_.get(), ssl_encryption_initial, &b, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Provide invalid post-handshake data.
|
||||||
|
TEST_F(QUICMethodTest, BadPostHandshake) {
|
||||||
|
const SSL_QUIC_METHOD quic_method = {
|
||||||
|
SetEncryptionSecretsCallback,
|
||||||
|
AddHandshakeDataCallback,
|
||||||
|
FlushFlightCallback,
|
||||||
|
SendAlertCallback,
|
||||||
|
};
|
||||||
|
|
||||||
|
g_last_session = nullptr;
|
||||||
|
|
||||||
|
SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH);
|
||||||
|
SSL_CTX_sess_set_new_cb(client_ctx_.get(), SaveLastSession);
|
||||||
|
ASSERT_TRUE(SSL_CTX_set_quic_method(client_ctx_.get(), &quic_method));
|
||||||
|
ASSERT_TRUE(SSL_CTX_set_quic_method(server_ctx_.get(), &quic_method));
|
||||||
|
ASSERT_TRUE(CreateClientAndServer());
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
ASSERT_TRUE(ProvideHandshakeData(client_.get()));
|
||||||
|
int client_ret = SSL_do_handshake(client_.get());
|
||||||
|
if (client_ret != 1) {
|
||||||
|
ASSERT_EQ(client_ret, -1);
|
||||||
|
ASSERT_EQ(SSL_get_error(client_.get(), client_ret), SSL_ERROR_WANT_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_TRUE(ProvideHandshakeData(server_.get()));
|
||||||
|
int server_ret = SSL_do_handshake(server_.get());
|
||||||
|
if (server_ret != 1) {
|
||||||
|
ASSERT_EQ(server_ret, -1);
|
||||||
|
ASSERT_EQ(SSL_get_error(server_.get(), server_ret), SSL_ERROR_WANT_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client_ret == 1 && server_ret == 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(SSL_do_handshake(client_.get()), 1);
|
||||||
|
EXPECT_EQ(SSL_do_handshake(server_.get()), 1);
|
||||||
|
EXPECT_TRUE(transport_.SecretsMatch(ssl_encryption_application));
|
||||||
|
EXPECT_FALSE(transport_.client()->has_alert());
|
||||||
|
EXPECT_FALSE(transport_.server()->has_alert());
|
||||||
|
|
||||||
|
// Junk sent as part of post-handshake data should cause an error.
|
||||||
|
uint8_t kJunk[] = {0x17, 0x0, 0x0, 0x4, 0xB, 0xE, 0xE, 0xF};
|
||||||
|
ASSERT_TRUE(SSL_provide_quic_data(client_.get(), ssl_encryption_application,
|
||||||
|
kJunk, sizeof(kJunk)));
|
||||||
|
EXPECT_EQ(SSL_process_quic_post_handshake(client_.get()), 0);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(davidben): Convert this file to GTest properly.
|
// TODO(davidben): Convert this file to GTest properly.
|
||||||
TEST(SSLTest, AllTests) {
|
TEST(SSLTest, AllTests) {
|
||||||
if (!TestSSL_SESSIONEncoding(kOpenSSLSession) ||
|
if (!TestSSL_SESSIONEncoding(kOpenSSLSession) ||
|
||||||
|
Loading…
Reference in New Issue
Block a user