From 8385bbf85755d2e62d5c1523f3b7390fce3f14b5 Mon Sep 17 00:00:00 2001 From: beck Date: Wed, 29 Jun 2022 17:39:20 +0000 Subject: [PATCH] Add support for sending QUIC transport parameters This is the start of adding the boringssl API for QUIC support, and the TLS extensions necessary to send and receive QUIC transport data. Inspired by boringssl's https://boringssl-review.googlesource.com/24464 ok jsing@ tb@ --- lib/libssl/s3_lib.c | 10 +- lib/libssl/ssl.h | 26 ++- lib/libssl/ssl_lib.c | 30 ++- lib/libssl/ssl_locl.h | 10 +- lib/libssl/ssl_tlsext.c | 122 +++++++++++- lib/libssl/ssl_tlsext.h | 13 +- lib/libssl/tls1.h | 5 +- regress/lib/libssl/tlsext/tlsexttest.c | 258 ++++++++++++++++++++++++- 8 files changed, 466 insertions(+), 8 deletions(-) diff --git a/lib/libssl/s3_lib.c b/lib/libssl/s3_lib.c index 66d0eba9a08..12eb9f4af45 100644 --- a/lib/libssl/s3_lib.c +++ b/lib/libssl/s3_lib.c @@ -1,4 +1,4 @@ -/* $OpenBSD: s3_lib.c,v 1.230 2022/06/29 08:37:18 tb Exp $ */ +/* $OpenBSD: s3_lib.c,v 1.231 2022/06/29 17:39:20 beck Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -1575,6 +1575,9 @@ ssl3_free(SSL *s) free(s->s3->alpn_selected); + freezero(s->s3->peer_quic_transport_params, + s->s3->peer_quic_transport_params_len); + freezero(s->s3, sizeof(*s->s3)); s->s3 = NULL; @@ -1619,6 +1622,11 @@ ssl3_clear(SSL *s) s->s3->alpn_selected = NULL; s->s3->alpn_selected_len = 0; + freezero(s->s3->peer_quic_transport_params, + s->s3->peer_quic_transport_params_len); + s->s3->peer_quic_transport_params = NULL; + s->s3->peer_quic_transport_params_len = 0; + memset(s->s3, 0, sizeof(*s->s3)); s->s3->rbuf.buf = rp; diff --git a/lib/libssl/ssl.h b/lib/libssl/ssl.h index aed3fea1d0c..c7339928489 100644 --- a/lib/libssl/ssl.h +++ b/lib/libssl/ssl.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssl.h,v 1.217 2022/06/28 20:57:33 tb Exp $ */ +/* $OpenBSD: ssl.h,v 1.218 2022/06/29 17:39:20 beck Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -1578,6 +1578,30 @@ void SSL_CTX_set_security_level(SSL_CTX *ctx, int level); int SSL_CTX_get_security_level(const SSL_CTX *ctx); #endif /* LIBRESSL_INTERNAL */ +#ifdef LIBRESSL_INTERNAL +/* + * SSL_set_quic_transport_params configures |ssl| to send |params| (of length + * |params_len|) in the quic_transport_parameters extension in either the + * ClientHello or EncryptedExtensions handshake message. This extension will + * only be sent if the TLS version is at least 1.3, and for a server, only if + * the client sent the extension. The buffer pointed to by |params| only need be + * valid for the duration of the call to this function. This function returns 1 + *on success and 0 on failure. + */ +int SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params, + size_t params_len); + +/* + * SSL_get_peer_quic_transport_params provides the caller with the value of the + * quic_transport_parameters extension sent by the peer. A pointer to the buffer + * containing the TransportParameters will be put in |*out_params|, and its + * length in |*params_len|. This buffer will be valid for the lifetime of the + * |SSL|. If no params were received from the peer, |*out_params_len| will be 0. + */ +void SSL_get_peer_quic_transport_params(const SSL *ssl, + const uint8_t **out_params, size_t *out_params_len); +#endif + /* BEGIN ERROR CODES */ /* The following lines are auto generated by the script mkerr.pl. Any changes * made after this point may be overwritten when the script is next run. diff --git a/lib/libssl/ssl_lib.c b/lib/libssl/ssl_lib.c index be01f771e08..b959d3428f7 100644 --- a/lib/libssl/ssl_lib.c +++ b/lib/libssl/ssl_lib.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssl_lib.c,v 1.292 2022/06/29 08:39:08 tb Exp $ */ +/* $OpenBSD: ssl_lib.c,v 1.293 2022/06/29 17:39:20 beck Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -573,6 +573,8 @@ SSL_free(SSL *s) free(s->internal->alpn_client_proto_list); + free(s->internal->quic_transport_params); + #ifndef OPENSSL_NO_SRTP sk_SRTP_PROTECTION_PROFILE_free(s->internal->srtp_profiles); #endif @@ -3312,3 +3314,29 @@ OBJ_bsearch_ssl_cipher_id(SSL_CIPHER *key, SSL_CIPHER const *base, int num) return (SSL_CIPHER *)OBJ_bsearch_(key, base, num, sizeof(SSL_CIPHER), ssl_cipher_id_cmp_BSEARCH_CMP_FN); } + +int +SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params, + size_t params_len) +{ + freezero(ssl->internal->quic_transport_params, + ssl->internal->quic_transport_params_len); + ssl->internal->quic_transport_params = NULL; + ssl->internal->quic_transport_params_len = 0; + + if ((ssl->internal->quic_transport_params = malloc(params_len)) == NULL) + return 0; + + memcpy(ssl->internal->quic_transport_params, params, params_len); + ssl->internal->quic_transport_params_len = params_len; + + return 1; +} + +void +SSL_get_peer_quic_transport_params(const SSL *ssl, const uint8_t **out_params, + size_t *out_params_len) +{ + *out_params = ssl->s3->peer_quic_transport_params; + *out_params_len = ssl->s3->peer_quic_transport_params_len; +} diff --git a/lib/libssl/ssl_locl.h b/lib/libssl/ssl_locl.h index da21758815d..102f7deaf52 100644 --- a/lib/libssl/ssl_locl.h +++ b/lib/libssl/ssl_locl.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssl_locl.h,v 1.401 2022/06/29 12:03:38 tb Exp $ */ +/* $OpenBSD: ssl_locl.h,v 1.402 2022/06/29 17:39:20 beck Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -932,6 +932,10 @@ typedef struct ssl_internal_st { unsigned char *alpn_client_proto_list; unsigned int alpn_client_proto_list_len; + /* QUIC transport params we will send */ + uint8_t *quic_transport_params; + size_t quic_transport_params_len; + /* XXX Callbacks */ /* true when we are actually in SSL_accept() or SSL_connect() */ @@ -1218,6 +1222,10 @@ typedef struct ssl3_state_st { */ unsigned char *alpn_selected; size_t alpn_selected_len; + + /* Contains the QUIC transport params received from our peer. */ + uint8_t *peer_quic_transport_params; + size_t peer_quic_transport_params_len; } SSL3_STATE; /* diff --git a/lib/libssl/ssl_tlsext.c b/lib/libssl/ssl_tlsext.c index 8faf90fde0e..fc6c11daa62 100644 --- a/lib/libssl/ssl_tlsext.c +++ b/lib/libssl/ssl_tlsext.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssl_tlsext.c,v 1.114 2022/06/29 07:53:58 tb Exp $ */ +/* $OpenBSD: ssl_tlsext.c,v 1.115 2022/06/29 17:39:20 beck Exp $ */ /* * Copyright (c) 2016, 2017, 2019 Joel Sing * Copyright (c) 2017 Doug Hogan @@ -1943,6 +1943,112 @@ tlsext_psk_server_parse(SSL *s, uint16_t msg_type, CBS *cbs, int *alert) return CBS_skip(cbs, CBS_len(cbs)); } +/* + * QUIC transport parameters extension. + */ + +int +tlsext_quic_transport_parameters_client_needs(SSL *s, uint16_t msg_type) +{ + return (s->internal->quic_transport_params_len > 0 && + s->s3->hs.our_max_tls_version >= TLS1_3_VERSION); +} + +int +tlsext_quic_transport_parameters_client_build(SSL *s, uint16_t msg_type, + CBB *cbb) +{ + CBB contents; + + if (!CBB_add_u16_length_prefixed(cbb, &contents)) + return 0; + + if (!CBB_add_bytes(&contents, s->internal->quic_transport_params, + s->internal->quic_transport_params_len)) + return 0; + + if (!CBB_flush(cbb)) + return 0; + + return 1; +} + +int +tlsext_quic_transport_parameters_client_parse(SSL *s, uint16_t msg_type, + CBS *cbs, int *alert) +{ + CBS transport_data; + + /* QUIC requires TLS 1.3. */ + if (ssl_effective_tls_version(s) < TLS1_3_VERSION) { + *alert = SSL_AD_UNSUPPORTED_EXTENSION; + return 0; + } + + if (!CBS_get_u16_length_prefixed(cbs, &transport_data)) + return 0; + + if (!CBS_stow(&transport_data, &s->s3->peer_quic_transport_params, + &s->s3->peer_quic_transport_params_len)) + return 0; + + return 1; +} + +int +tlsext_quic_transport_parameters_server_needs(SSL *s, uint16_t msg_type) +{ + return s->internal->quic_transport_params_len > 0; +} + +int +tlsext_quic_transport_parameters_server_build(SSL *s, uint16_t msg_type, + CBB *cbb) +{ + CBB contents; + + if (!CBB_add_u16_length_prefixed(cbb, &contents)) + return 0; + + if (!CBB_add_bytes(&contents, s->internal->quic_transport_params, + s->internal->quic_transport_params_len)) + return 0; + + if (!CBB_flush(cbb)) + return 0; + + return 1; +} + +int +tlsext_quic_transport_parameters_server_parse(SSL *s, uint16_t msg_type, + CBS *cbs, int *alert) +{ + CBS transport_data; + + /* + * Ignore this extension if we don't have configured quic transport data + * or if we are not TLS 1.3. + */ + if (s->internal->quic_transport_params_len == 0 || + ssl_effective_tls_version(s) < TLS1_3_VERSION) { + if (!CBS_skip(cbs, CBS_len(cbs))) { + *alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + return 1; + } + + if (!CBS_get_u16_length_prefixed(cbs, &transport_data)) + return 0; + + if (!CBS_stow(&transport_data, &s->s3->peer_quic_transport_params, + &s->s3->peer_quic_transport_params_len)) + return 0; + + return 1; +} + struct tls_extension_funcs { int (*needs)(SSL *s, uint16_t msg_type); int (*build)(SSL *s, uint16_t msg_type, CBB *cbb); @@ -2131,6 +2237,20 @@ static const struct tls_extension tls_extensions[] = { }, }, #endif /* OPENSSL_NO_SRTP */ + { + .type = TLSEXT_TYPE_quic_transport_parameters, + .messages = SSL_TLSEXT_MSG_CH | SSL_TLSEXT_MSG_SH, + .client = { + .needs = tlsext_quic_transport_parameters_client_needs, + .build = tlsext_quic_transport_parameters_client_build, + .parse = tlsext_quic_transport_parameters_client_parse, + }, + .server = { + .needs = tlsext_quic_transport_parameters_server_needs, + .build = tlsext_quic_transport_parameters_server_build, + .parse = tlsext_quic_transport_parameters_server_parse, + }, + }, { .type = TLSEXT_TYPE_psk_key_exchange_modes, .messages = SSL_TLSEXT_MSG_CH, diff --git a/lib/libssl/ssl_tlsext.h b/lib/libssl/ssl_tlsext.h index 3439255fd6e..268b2749483 100644 --- a/lib/libssl/ssl_tlsext.h +++ b/lib/libssl/ssl_tlsext.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssl_tlsext.h,v 1.29 2022/06/03 13:31:49 tb Exp $ */ +/* $OpenBSD: ssl_tlsext.h,v 1.30 2022/06/29 17:39:20 beck Exp $ */ /* * Copyright (c) 2016, 2017 Joel Sing * Copyright (c) 2017 Doug Hogan @@ -144,6 +144,17 @@ int tlsext_srtp_server_build(SSL *s, uint16_t msg_type, CBB *cbb); int tlsext_srtp_server_parse(SSL *s, uint16_t msg_type, CBS *cbs, int *alert); #endif +int tlsext_quic_transport_parameters_client_needs(SSL *s, uint16_t msg_type); +int tlsext_quic_transport_parameters_client_build(SSL *s, uint16_t msg_type, + CBB *cbb); +int tlsext_quic_transport_parameters_client_parse(SSL *s, uint16_t msg_type, + CBS *cbs, int *alert); +int tlsext_quic_transport_parameters_server_needs(SSL *s, uint16_t msg_type); +int tlsext_quic_transport_parameters_server_build(SSL *s, uint16_t msg_type, + CBB *cbb); +int tlsext_quic_transport_parameters_server_parse(SSL *s, uint16_t msg_type, + CBS *cbs, int *alert); + int tlsext_client_build(SSL *s, uint16_t msg_type, CBB *cbb); int tlsext_client_parse(SSL *s, uint16_t msg_type, CBS *cbs, int *alert); diff --git a/lib/libssl/tls1.h b/lib/libssl/tls1.h index aa05f37cc8c..2f6e2e3bd07 100644 --- a/lib/libssl/tls1.h +++ b/lib/libssl/tls1.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tls1.h,v 1.52 2022/06/28 20:36:55 tb Exp $ */ +/* $OpenBSD: tls1.h,v 1.53 2022/06/29 17:39:20 beck Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -259,6 +259,9 @@ extern "C" { /* ExtensionType value from RFC 7685. */ #define TLSEXT_TYPE_padding 21 +/* ExtensionType value from draft-ietf-quic-tls */ +#define TLSEXT_TYPE_quic_transport_parameters 26 + /* ExtensionType value from RFC 4507. */ #define TLSEXT_TYPE_session_ticket 35 diff --git a/regress/lib/libssl/tlsext/tlsexttest.c b/regress/lib/libssl/tlsext/tlsexttest.c index 297aff2441a..8166b883b87 100644 --- a/regress/lib/libssl/tlsext/tlsexttest.c +++ b/regress/lib/libssl/tlsext/tlsexttest.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tlsexttest.c,v 1.63 2022/06/06 06:11:04 tb Exp $ */ +/* $OpenBSD: tlsexttest.c,v 1.64 2022/06/29 17:39:21 beck Exp $ */ /* * Copyright (c) 2017 Joel Sing * Copyright (c) 2017 Doug Hogan @@ -1896,6 +1896,259 @@ test_tlsext_sni_server(void) return (failure); } + +/* + * QUIC transport parameters extenstion - RFC 90210 :) + */ + +#define TEST_QUIC_TRANSPORT_DATA "0123456789abcdef" + +static unsigned char tlsext_quic_transport_data[] = { + 0x00, 0x10, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, + 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, + 0x65, 0x66, +}; + +static int +test_tlsext_quic_transport_parameters_client(void) +{ + unsigned char *data = NULL; + SSL_CTX *ssl_ctx = NULL; + SSL *ssl = NULL; + int failure; + size_t dlen; + CBB cbb; + CBS cbs; + int alert; + const uint8_t *out_bytes; + size_t out_bytes_len; + + failure = 1; + + if ((ssl_ctx = SSL_CTX_new(TLS_client_method())) == NULL) + errx(1, "failed to create SSL_CTX"); + if ((ssl = SSL_new(ssl_ctx)) == NULL) + errx(1, "failed to create SSL"); + + CBB_init(&cbb, 0); + + if (tlsext_quic_transport_parameters_client_needs(ssl, + SSL_TLSEXT_MSG_CH)) { + FAIL("client should not need QUIC\n"); + goto err; + } + + if (!SSL_set_quic_transport_params(ssl, + TEST_QUIC_TRANSPORT_DATA, strlen(TEST_QUIC_TRANSPORT_DATA))) { + FAIL("client failed to set QUIC parametes\n"); + goto err; + } + + if (tlsext_quic_transport_parameters_client_needs(ssl, + SSL_TLSEXT_MSG_CH)) { + FAIL("client should not need QUIC\n"); + goto err; + } + + ssl->s3->hs.our_max_tls_version = TLS1_3_VERSION; + ssl->s3->hs.negotiated_tls_version = TLS1_3_VERSION; + + if (!tlsext_quic_transport_parameters_client_needs(ssl, + SSL_TLSEXT_MSG_CH)) { + FAIL("client should not need QUIC\n"); + goto err; + } + + if (!tlsext_quic_transport_parameters_client_build(ssl, + SSL_TLSEXT_MSG_CH, &cbb)) { + FAIL("client failed to build QUIC\n"); + goto err; + } + + if (!CBB_finish(&cbb, &data, &dlen)) { + FAIL("failed to finish CBB"); + goto err; + } + + if (dlen != sizeof(tlsext_quic_transport_data)) { + FAIL("got client QUIC with length %zu, " + "want length %zu\n", dlen, + sizeof(tlsext_quic_transport_data)); + goto err; + } + + if (memcmp(data, tlsext_quic_transport_data, dlen) != 0) { + FAIL("client QUIC differs:\n"); + fprintf(stderr, "received:\n"); + hexdump(data, dlen); + fprintf(stderr, "test data:\n"); + hexdump(tlsext_quic_transport_data, + sizeof(tlsext_quic_transport_data)); + goto err; + } + + CBS_init(&cbs, tlsext_quic_transport_data, + sizeof(tlsext_quic_transport_data)); + + if (!tlsext_quic_transport_parameters_server_parse(ssl, + SSL_TLSEXT_MSG_SH, &cbs, &alert)) { + FAIL("server_parse of QUIC from server failed\n"); + goto err; + } + if (CBS_len(&cbs) != 0) { + FAIL("extension data remaining\n"); + goto err; + } + + SSL_get_peer_quic_transport_params(ssl, &out_bytes, &out_bytes_len); + + if (out_bytes_len != strlen(TEST_QUIC_TRANSPORT_DATA)) { + FAIL("server_parse QUIC length differs, got %zu want %zu\n", + out_bytes_len, + sizeof(tlsext_quic_transport_data)); + goto err; + } + + if (memcmp(out_bytes, TEST_QUIC_TRANSPORT_DATA, + out_bytes_len) != 0) { + FAIL("server_parse QUIC differs from sent:\n"); + fprintf(stderr, "received:\n"); + hexdump(data, dlen); + fprintf(stderr, "test data:\n"); + hexdump(tlsext_quic_transport_data, + sizeof(tlsext_quic_transport_data)); + goto err; + } + + failure = 0; + + err: + CBB_cleanup(&cbb); + SSL_CTX_free(ssl_ctx); + SSL_free(ssl); + free(data); + + return (failure); +} + +static int +test_tlsext_quic_transport_parameters_server(void) +{ + unsigned char *data = NULL; + SSL_CTX *ssl_ctx = NULL; + SSL *ssl = NULL; + int failure; + size_t dlen; + int alert; + CBB cbb; + CBS cbs; + const uint8_t *out_bytes; + size_t out_bytes_len; + + failure = 1; + + CBB_init(&cbb, 0); + + if ((ssl_ctx = SSL_CTX_new(TLS_server_method())) == NULL) + errx(1, "failed to create SSL_CTX"); + if ((ssl = SSL_new(ssl_ctx)) == NULL) + errx(1, "failed to create SSL"); + + if (tlsext_quic_transport_parameters_server_needs(ssl, SSL_TLSEXT_MSG_SH)) { + FAIL("server should not need QUIC\n"); + goto err; + } + + if (!SSL_set_quic_transport_params(ssl, + TEST_QUIC_TRANSPORT_DATA, strlen(TEST_QUIC_TRANSPORT_DATA))) { + FAIL("server failed to set QUIC parametes\n"); + goto err; + } + + if (!tlsext_quic_transport_parameters_server_needs(ssl, SSL_TLSEXT_MSG_SH)) { + FAIL("server should need QUIC\n"); + goto err; + } + + if (!tlsext_quic_transport_parameters_server_build(ssl, + SSL_TLSEXT_MSG_SH, &cbb)) { + FAIL("server failed to build QUIC\n"); + goto err; + } + + if (!CBB_finish(&cbb, &data, &dlen)) + errx(1, "failed to finish CBB"); + + if (dlen != sizeof(tlsext_quic_transport_data)) { + FAIL("got server QUIC with length %zu, " + "want length %zu\n", dlen, + sizeof(tlsext_quic_transport_data)); + goto err; + } + + if (memcmp(data, tlsext_quic_transport_data, dlen) != 0) { + FAIL("saved server QUIC differs:\n"); + fprintf(stderr, "received:\n"); + hexdump(data, dlen); + fprintf(stderr, "test data:\n"); + hexdump(tlsext_quic_transport_data, + sizeof(tlsext_quic_transport_data)); + goto err; + } + + CBS_init(&cbs, tlsext_quic_transport_data, + sizeof(tlsext_quic_transport_data)); + + if (tlsext_quic_transport_parameters_client_parse(ssl, + SSL_TLSEXT_MSG_SH, &cbs, &alert)) { + FAIL("QUIC parse should have failed!\n"); + goto err; + } + + ssl->s3->hs.our_max_tls_version = TLS1_3_VERSION; + ssl->s3->hs.negotiated_tls_version = TLS1_3_VERSION; + + if (!tlsext_quic_transport_parameters_client_parse(ssl, + SSL_TLSEXT_MSG_SH, &cbs, &alert)) { + FAIL("client_parse of QUIC from server failed\n"); + goto err; + } + if (CBS_len(&cbs) != 0) { + FAIL("extension data remaining\n"); + goto err; + } + + SSL_get_peer_quic_transport_params(ssl, &out_bytes, &out_bytes_len); + + if (out_bytes_len != strlen(TEST_QUIC_TRANSPORT_DATA)) { + FAIL("client QUIC length differs, got %zu want %zu\n", + out_bytes_len, + sizeof(tlsext_quic_transport_data)); + goto err; + } + + if (memcmp(out_bytes, TEST_QUIC_TRANSPORT_DATA, + out_bytes_len) != 0) { + FAIL("client QUIC differs from sent:\n"); + fprintf(stderr, "received:\n"); + hexdump(data, dlen); + fprintf(stderr, "test data:\n"); + hexdump(tlsext_quic_transport_data, + sizeof(tlsext_quic_transport_data)); + goto err; + } + + failure = 0; + + err: + CBB_cleanup(&cbb); + SSL_CTX_free(ssl_ctx); + SSL_free(ssl); + free(data); + + return (failure); +} + static unsigned char tls_ocsp_client_default[] = { 0x01, 0x00, 0x00, 0x00, 0x00 }; @@ -3991,5 +4244,8 @@ main(int argc, char **argv) failed |= test_tlsext_valid_hostnames(); + failed |= test_tlsext_quic_transport_parameters_client(); + failed |= test_tlsext_quic_transport_parameters_server(); + return (failed); } -- 2.20.1