Add support for sending QUIC transport parameters
authorbeck <beck@openbsd.org>
Wed, 29 Jun 2022 17:39:20 +0000 (17:39 +0000)
committerbeck <beck@openbsd.org>
Wed, 29 Jun 2022 17:39:20 +0000 (17:39 +0000)
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
lib/libssl/ssl.h
lib/libssl/ssl_lib.c
lib/libssl/ssl_locl.h
lib/libssl/ssl_tlsext.c
lib/libssl/ssl_tlsext.h
lib/libssl/tls1.h
regress/lib/libssl/tlsext/tlsexttest.c

index 66d0eba..12eb9f4 100644 (file)
@@ -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;
index aed3fea..c733992 100644 (file)
@@ -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.
index be01f77..b959d34 100644 (file)
@@ -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;
+}
index da21758..102f7de 100644 (file)
@@ -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;
 
 /*
index 8faf90f..fc6c11d 100644 (file)
@@ -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 <jsing@openbsd.org>
  * Copyright (c) 2017 Doug Hogan <doug@openbsd.org>
@@ -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,
index 3439255..268b274 100644 (file)
@@ -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 <jsing@openbsd.org>
  * Copyright (c) 2017 Doug Hogan <doug@openbsd.org>
@@ -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);
 
index aa05f37..2f6e2e3 100644 (file)
@@ -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
 
index 297aff2..8166b88 100644 (file)
@@ -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 <jsing@openbsd.org>
  * Copyright (c) 2017 Doug Hogan <doug@openbsd.org>
@@ -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);
 }