summaryrefslogtreecommitdiffstats
path: root/doc/designs
diff options
context:
space:
mode:
authorMatt Caswell <matt@openssl.org>2022-11-14 16:41:17 +0000
committerMatt Caswell <matt@openssl.org>2022-11-21 09:53:22 +0000
commit88113f5dc6828694820d39612c3a760e386a0aa5 (patch)
treea0d93ea0b000d3cb7345eff1c27459a34523a14a /doc/designs
parent83c1220ad137bb4b651478444c3666c66ec9d640 (diff)
Design document for the QUIC-TLS integration
Reviewed-by: Hugo Landau <hlandau@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/19683)
Diffstat (limited to 'doc/designs')
-rw-r--r--doc/designs/quic-design/quic-tls.md261
1 files changed, 261 insertions, 0 deletions
diff --git a/doc/designs/quic-design/quic-tls.md b/doc/designs/quic-design/quic-tls.md
new file mode 100644
index 0000000000..122e3deb9d
--- /dev/null
+++ b/doc/designs/quic-design/quic-tls.md
@@ -0,0 +1,261 @@
+QUIC-TLS Handshake Integration
+==============================
+
+QUIC reuses the TLS handshake for the establishment of keys. It does not use
+the standard TLS record layer and instead assumes responsibility for the
+confidentiality and integrity of QUIC packets itself. Only the TLS handshake is
+used. Application data is entirely protected by QUIC.
+
+QUIC_TLS Object
+---------------
+
+A QUIC-TLS handshake is managed by a QUIC_TLS object. This object provides
+3 core functions to the rest of the QUIC implementation:
+
+```c
+QUIC_TLS *ossl_quic_tls_new(const QUIC_TLS_ARGS *args);
+```
+
+The `ossl_quic_tls_new` function instantiates a new `QUIC_TLS` object associated
+with the QUIC Connection and initialises it with a set of callbacks and other
+arguments provided in the `args` parameter. These callbacks are called at
+various key points during the handshake lifecycle such as when new keys are
+established, crypto frame data is ready to be sent or consumed, or when the
+handshake is complete.
+
+A key field of the `args` structure is the `SSL` object (`s`). This "inner"
+`SSL` object is initialised with an `SSL_CONNECTION` to represent the TLS
+handshake state. This is a different `SSL` object to the "user" visible `SSL`
+object which contains a `QUIC_CONNECTION`, i.e. the user visible `SSL` object
+contains a `QUIC_CONNECTION` which contains the inner `SSL` object which
+contains an `SSL_CONNECTION`.
+
+```c
+void ossl_quic_tls_free(QUIC_TLS *qtls);
+```
+
+When the QUIC Connection no longer needs the handshake object it can be freed
+via the `ossl_quic_tls_free` function.
+
+```c
+int ossl_quic_tls_tick(QUIC_TLS *qtls);
+```
+
+Finally the `ossl_quic_tls_tick` function is responsible for advancing the
+state of the QUIC-TLS handshake. On each call to `ossl_quic_tls_tick` newly
+received crypto frame data may be consumed, or new crypto frame data may be
+queued for sending, or one or more of the various callbacks may be invoked.
+
+QUIC_TLS_ARGS
+-------------
+
+A `QUIC_TLS_ARGS` object is passed to the `ossl_quic_tls_new` function by the
+OpenSSL QUIC implementation to supply a set of callbacks and other essential
+parameters. The `QUIC_TLS_ARGS` structure is as follows:
+
+```c
+typedef struct quic_tls_args_st {
+ /*
+ * The "inner" SSL object for the QUIC Connection. Contains an
+ * SSL_CONNECTION
+ */
+ SSL *s;
+
+ /*
+ * Called to send data on the crypto stream. We use a callback rather than
+ * passing the crypto stream QUIC_SSTREAM directly because this lets the CSM
+ * dynamically select the correct outgoing crypto stream based on the
+ * current EL.
+ */
+ int (*crypto_send_cb)(const unsigned char *buf, size_t buf_len,
+ size_t *consumed, void *arg);
+ void *crypto_send_cb_arg;
+ int (*crypto_recv_cb)(unsigned char *buf, size_t buf_len,
+ size_t *bytes_read, void *arg);
+ void *crypto_recv_cb_arg;
+
+ /* Called when a traffic secret is available for a given encryption level. */
+ int (*yield_secret_cb)(uint32_t enc_level, int direction /* 0=RX, 1=TX */,
+ uint32_t suite_id, EVP_MD *md,
+ const unsigned char *secret, size_t secret_len,
+ void *arg);
+ void *yield_secret_cb_arg;
+
+ /*
+ * Called when we receive transport parameters from the peer.
+ *
+ * Note: These parameters are not authenticated until the handshake is
+ * marked as completed.
+ */
+ int (*got_transport_params_cb)(const unsigned char *params,
+ size_t params_len,
+ void *arg);
+ void *got_transport_params_cb_arg;
+
+ /*
+ * Called when the handshake has been completed as far as the handshake
+ * protocol is concerned, meaning that the connection has been
+ * authenticated.
+ */
+ int (*handshake_complete_cb)(void *arg);
+ void *handshake_complete_cb_arg;
+
+ /*
+ * Called when something has gone wrong with the connection as far as the
+ * handshake layer is concerned, meaning that it should be immediately torn
+ * down. Note that this may happen at any time, including after a connection
+ * has been fully established.
+ */
+ int (*alert_cb)(void *arg, unsigned char alert_code);
+ void *alert_cb_arg;
+
+ /*
+ * Transport parameters which client should send. Buffer lifetime must
+ * exceed the lifetime of the QUIC_TLS object.
+ */
+ const unsigned char *transport_params;
+ size_t transport_params_len;
+} QUIC_TLS_ARGS;
+```
+
+The `crypto_send_cb` and `crypto_recv_cb` callbacks will be called by the
+QUIC-TLS handshake when there is new CRYPTO frame data to be sent, or when it
+wants to consume queued CRYPTO frame data from the peer.
+
+When the TLS handshake generates secrets they will be communicated to the
+OpenSSL QUIC implementation via the `yield_secret_cb`, and when the handshake
+has successfully completed this will be communicated via `handshake_complete_cb`.
+
+In the event that an error occurs a normal TLS handshake would send a TLS alert
+record. QUIC handles this differently and so the QUIC_TLS object will intercept
+attempts to send an alert and will communicate this via the `alert_cb` callback.
+
+QUIC requires the use of a TLS extension in order to send and receive "transport
+parameters". These transport parameters are opaque to the `QUIC_TLS` object. It
+does not need to use them directly but instead simply includes them in an
+extension to be sent in the ClientHello and receives them back from the peer in
+the EncryptedExtensions message. The data to be sent is provided in the
+`transport_params` argument. When the peer's parameters are received the
+`got_transport_params_cb` callback is invoked.
+
+QUIC_TLS Implementation
+-----------------------
+
+The `QUIC_TLS` object utilises two main mechanisms for fulfilling its functions:
+
+* It registers itself as a custom TLS record layer
+* It supplies callbacks to register a custom TLS extension
+
+### Custom TLS Record Layer
+
+A TLS record layer is defined via an `OSSL_RECORD_METHOD` object. This object
+consists of a set of function pointers which need to be implemented by any
+record layer. Existing record layers include one for TLS, one for DTLS and one
+for KTLS.
+
+`QUIC_TLS` registers itself as a custom TLS record layer. A new internal
+function is used to provide the custom record method data and associate it with
+an `SSL_CONNECTION`:
+
+```C
+void ossl_ssl_set_custom_record_layer(SSL_CONNECTION *s,
+ const OSSL_RECORD_METHOD *meth,
+ void *rlarg);
+```
+
+The internal function `ssl_select_next_record_layer` which is used in the TLS
+implementation to work out which record method should be used next is modified
+to first check whether a custom record method has been specified and always use
+that one if so.
+
+The TLS record layer code is further modified to provide the following
+capabilities which are needed in order to support QUIC.
+
+The custom record layer will need a record layer specific argument (`rlarg`
+above). This is passed as part of a modified `new_record_layer` call.
+
+Existing TLS record layers use TLS keys and IVs that are calculated using a
+KDF from a higher level secret. Instead of this QUIC needs direct access to the
+higher level secret as well as the digest to be used in the KDF - so these
+values are now also passed through as part of the `new_record_layer` call.
+
+The most important function pointers in the `OSSL_RECORD_METHOD` for the
+`QUIC_TLS` object are:
+
+* `new_record_layer`
+
+Invoked every time a new record layer object is created by the TLS
+implementation. This occurs every time new keys are provisioned (once for the
+"read" side and once for the "write" side). This function is responsible for
+invoking the `yield_secret_cb` callback.
+
+* `write_records`
+
+Invoked every time the TLS implementation wants to send TLS handshake data. This
+is responsible for calling the `crypto_send_cb` callback. It also includes
+special processing in the event that the TLS implementation wants to send an
+alert. This manifests itself as a call to `write_records` indicating a type of
+`SSL3_RT_ALERT`. The `QUIC_TLS` implementation of `write_records` must parse the
+alert data supplied by the TLS implementation (always a 2 byte record payload)
+and pull out the alert description (a one byte integer) and invoke the
+`alert_cb` callback. Note that while the TLS RFC strictly allows the 2 byte
+alert record to be fragmented across two 1 byte records this is never done in
+practice by OpenSSL's TLS stack and the `write_records` implementation can make
+the optimising assumption that both bytes of an alert are always sent together.
+
+* `quic_read_record`
+
+Invoked when the TLS implementation wants to read more handshake data. This
+results in a call to `crypto_recv_cb`.
+
+This design does introduce an extra "copy" in the process when `crypto_recv_cb`
+is invoked. CRYPTO frame data will be queued within internal QUIC "Stream
+Receive Buffers" when it is received by the peer. However the TLS implementation
+expects to request data from the record layer, get a handle on that data, and
+then inform the record layer when it has finished using that data. The current
+design of the Stream Receive Buffers does not allow for this model. Therefore
+when `crypto_recv_cb` is invoked the data is copied into a QUIC_TLS object
+managed buffer. This is inefficient, so it is expected that a later phase of
+development will resolve this problem.
+
+### Custom TLS extension
+
+Libssl already has the ability for an application to supply a custom extension
+via the `SSL_CTX_add_custom_ext()` API. There is no equivalent
+`SSL_add_custom_ext()` and therefore an internal API is used to do this. This
+mechanism is used for supporting QUIC transport parameters. An extension
+type `TLSEXT_TYPE_quic_transport_parameters` with value 57 is used for this
+purpose.
+
+The custom extension API enables the caller to supply `add`, `free` and `parse`
+callbacks. The `add` callback simply adds the `transport_params` data from
+`QUIC_TLS_ARGS`. The `parse` callback invokes the `got_transport_params_cb`
+callback when the transport parameters have been received from the peer.
+
+### ALPN
+
+QUIC requires the use of ALPN (Application-Layer Protocol Negotiation). This is
+normally optional in OpenSSL but is mandatory for QUIC connections. Therefore
+a QUIC client must call one of `SSL_CTX_set_alpn_protos` or
+`SSL_set_alpn_protos` prior to initiating the handshake. If the ALPN data has
+not been set then the `QUIC_TLS` object immediately fails.
+
+### Other Implementation Details
+
+The `SSL_CONNECTION` used for the TLS handshake is held alongside the QUIC
+related data in the `SSL` object. Public API functions that are only relevant to
+TLS will modify this internal `SSL_CONNECTION` as appropriate. This enables the
+end application to configure the TLS connection parameters as it sees fit (e.g.
+setting ciphersuites, providing client certificates, etc). However there are
+certain settings that may be optional in a normal TLS connection but are
+mandatory for QUIC. Where possible these settings will be automatically
+configured just before the handshake starts.
+
+One of these settings is the minimum TLS protocol version. QUIC requires that
+TLSv1.3 is used as a minimum. Therefore the `QUIC_TLS` object automatically
+calls `SSL_set_min_proto_version()` and specifies `TLS1_3_VERSION` as the
+minimum version.
+
+Secondly, QUIC enforces that the TLS "middlebox" mode must not be used. For
+normal TLS this is "on" by default. Therefore the `QUIC_TLS` object will
+automatically clear the `SSL_OP_ENABLE_MIDDLEBOX_COMPAT` option if it is set.