Provide SSL_QUIC_METHOD.
authorjsing <jsing@openbsd.org>
Sun, 21 Aug 2022 19:32:38 +0000 (19:32 +0000)
committerjsing <jsing@openbsd.org>
Sun, 21 Aug 2022 19:32:38 +0000 (19:32 +0000)
This provides SSL_QUIC_METHOD (aka ssl_quic_method_st), which allows for
QUIC callback hooks to be passed to an SSL_CTX or SSL. This is largely
ported/adapted from BoringSSL.

It is worth noting that this struct is not opaque and the original
interface exposed by BoringSSL differs to the one they now use. The
original interface was copied by quictls and it appears that this API
will not be updated to match BoringSSL.

To make things even more challenging, at least one consumer does not use
named initialisers, making code completely dependent on the order in
which the function pointers are defined as struct members. In order to
try to support both variants, the set_read_secret/set_write_secret
functions are included, however they have to go at the end.

ok tb@

lib/libssl/ssl.h
lib/libssl/ssl_lib.c
lib/libssl/ssl_locl.h

index 359b554..be116de 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl.h,v 1.225 2022/08/21 19:18:57 jsing Exp $ */
+/* $OpenBSD: ssl.h,v 1.226 2022/08/21 19:32:38 jsing Exp $ */
 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
@@ -361,6 +361,10 @@ typedef struct ssl_method_st SSL_METHOD;
 typedef struct ssl_cipher_st SSL_CIPHER;
 typedef struct ssl_session_st SSL_SESSION;
 
+#if defined(LIBRESSL_HAS_QUIC) || defined(LIBRESSL_INTERNAL)
+typedef struct ssl_quic_method_st SSL_QUIC_METHOD;
+#endif
+
 DECLARE_STACK_OF(SSL_CIPHER)
 
 /* SRTP protection profiles for use with the use_srtp extension (RFC 5764)*/
@@ -1590,6 +1594,36 @@ void SSL_CTX_set_security_level(SSL_CTX *ctx, int level);
 int SSL_CTX_get_security_level(const SSL_CTX *ctx);
 
 #if defined(LIBRESSL_HAS_QUIC) || defined(LIBRESSL_INTERNAL)
+/*
+ * QUIC integration.
+ *
+ * QUIC acts as an underlying transport for the TLS 1.3 handshake. The following
+ * functions allow a QUIC implementation to serve as the underlying transport as
+ * described in RFC 9001.
+ *
+ * When configured for QUIC, |SSL_do_handshake| will drive the handshake as
+ * before, but it will not use the configured |BIO|. It will call functions on
+ * |SSL_QUIC_METHOD| to configure secrets and send data. If data is needed from
+ * the peer, it will return |SSL_ERROR_WANT_READ|. As the caller receives data
+ * it can decrypt, it calls |SSL_provide_quic_data|. Subsequent
+ * |SSL_do_handshake| calls will then consume that data and progress the
+ * handshake. After the handshake is complete, the caller should continue to
+ * 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_peek|, |SSL_read| and |SSL_write| in QUIC.
+ *
+ * To avoid DoS attacks, the QUIC implementation must limit the amount of data
+ * being queued up. The implementation can call
+ * |SSL_quic_max_handshake_flight_len| to get the maximum buffer length at each
+ * encryption level.
+ *
+ * QUIC implementations must additionally configure transport parameters with
+ * |SSL_set_quic_transport_params|. |SSL_get_peer_quic_transport_params| may be
+ * used to query the value received from the peer. This extension is handled
+ * as an opaque byte string, which the caller is responsible for serializing
+ * and parsing. See RFC 9000 section 7.4 for further details.
+ */
+
 /*
  * ssl_encryption_level_t specifies the QUIC encryption level used to transmit
  * handshake messages.
@@ -1601,16 +1635,120 @@ typedef enum ssl_encryption_level_t {
        ssl_encryption_application,
 } OSSL_ENCRYPTION_LEVEL;
 
+/*
+ * ssl_quic_method_st (aka |SSL_QUIC_METHOD|) describes custom QUIC hooks.
+ *
+ * Note that we provide both the new (BoringSSL) secrets interface
+ * (set_read_secret/set_write_secret) along with the old interface
+ * (set_encryption_secrets), which quictls is still using.
+ *
+ * Since some consumers fail to use named initialisers, the order of these
+ * functions is important. Hopefully all of these consumers use the old version.
+ */
+struct ssl_quic_method_st {
+       /*
+        * set_encryption_secrets configures the read and write secrets for the
+        * given encryption level. This function will always be called before an
+        * encryption level other than |ssl_encryption_initial| is used.
+        *
+        * When reading packets at a given level, the QUIC implementation must
+        * send ACKs at the same level, so this function provides read and write
+        * secrets together. The exception is |ssl_encryption_early_data|, where
+        * secrets are only available in the client to server direction. The
+        * other secret will be NULL. The server acknowledges such data at
+        * |ssl_encryption_application|, which will be configured in the same
+        * |SSL_do_handshake| call.
+        *
+        * This function should use |SSL_get_current_cipher| to determine the TLS
+        * cipher suite.
+        */
+       int (*set_encryption_secrets)(SSL *ssl, enum ssl_encryption_level_t level,
+           const uint8_t *read_secret, const uint8_t *write_secret,
+           size_t secret_len);
+
+       /*
+        * add_handshake_data adds handshake data to the current flight at the
+        * given encryption level. It returns one on success and zero on error.
+        * Callers should defer writing data to the network until |flush_flight|
+        * to better pack QUIC packets into transport datagrams.
+        *
+        * If |level| is not |ssl_encryption_initial|, this function will not be
+        * called before |level| is initialized with |set_write_secret|.
+        */
+       int (*add_handshake_data)(SSL *ssl, enum ssl_encryption_level_t level,
+           const uint8_t *data, size_t len);
+
+       /*
+        * flush_flight is called when the current flight is complete and should
+        * be written to the transport. Note a flight may contain data at
+        * several encryption levels. It returns one on success and zero on
+        * error.
+        */
+       int (*flush_flight)(SSL *ssl);
+
+       /*
+        * send_alert sends a fatal alert at the specified encryption level. It
+        * returns one on success and zero on error.
+        *
+        * If |level| is not |ssl_encryption_initial|, this function will not be
+        * called before |level| is initialized with |set_write_secret|.
+        */
+       int (*send_alert)(SSL *ssl, enum ssl_encryption_level_t level,
+           uint8_t alert);
+
+       /*
+        * set_read_secret configures the read secret and cipher suite for the
+        * given encryption level. It returns one on success and zero to
+        * terminate the handshake with an error. It will be called at most once
+        * per encryption level.
+        *
+        * Read keys will not be released before QUIC may use them. Once a level
+        * has been initialized, QUIC may begin processing data from it.
+        * Handshake data should be passed to |SSL_provide_quic_data| and
+        * application data (if |level| is |ssl_encryption_early_data| or
+        * |ssl_encryption_application|) may be processed according to the rules
+        * of the QUIC protocol.
+        */
+       int (*set_read_secret)(SSL *ssl, enum ssl_encryption_level_t level,
+           const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len);
+
+       /*
+        * set_write_secret behaves like |set_read_secret| but configures the
+        * write secret and cipher suite for the given encryption level. It will
+        * be called at most once per encryption level.
+        *
+        * Write keys will not be released before QUIC may use them. If |level|
+        * is |ssl_encryption_early_data| or |ssl_encryption_application|, QUIC
+        * may begin sending application data at |level|.
+        */
+       int (*set_write_secret)(SSL *ssl, enum ssl_encryption_level_t level,
+           const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len);
+};
+
+/*
+ * 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
+ * for the lifetime of |ctx|. It returns one on success and zero on error.
+ */
+int SSL_CTX_set_quic_method(SSL_CTX *ctx, const SSL_QUIC_METHOD *quic_method);
+
+/*
+ * SSL_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
+ * for the lifetime of |ssl|. It returns one on success and zero on error.
+ */
+int SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method);
+
+/* SSL_is_quic returns true if an SSL has been configured for use with QUIC. */
 int SSL_is_quic(const SSL *ssl);
 
 /*
  * 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.
+ * ClientHello or EncryptedExtensions handshake message. It is an error to set
+ * transport parameters if |ssl| is not configured for QUIC. 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);
@@ -1624,6 +1762,7 @@ int SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params,
  */
 void SSL_get_peer_quic_transport_params(const SSL *ssl,
     const uint8_t **out_params, size_t *out_params_len);
+
 #endif
 
 void ERR_load_SSL_strings(void);
index 515065d..f0f0150 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl_lib.c,v 1.302 2022/08/21 18:17:11 jsing Exp $ */
+/* $OpenBSD: ssl_lib.c,v 1.303 2022/08/21 19:32:38 jsing Exp $ */
 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
@@ -339,6 +339,7 @@ SSL_new(SSL_CTX *ctx)
        s->verify_result = X509_V_OK;
 
        s->method = ctx->method;
+       s->quic_method = ctx->quic_method;
 
        if (!s->method->ssl_new(s))
                goto err;
@@ -2584,6 +2585,28 @@ SSL_get_error(const SSL *s, int i)
        return (SSL_ERROR_SYSCALL);
 }
 
+int
+SSL_CTX_set_quic_method(SSL_CTX *ctx, const SSL_QUIC_METHOD *quic_method)
+{
+       if (ctx->method->dtls)
+               return 0;
+
+       ctx->quic_method = quic_method;
+
+       return 1;
+}
+
+int
+SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method)
+{
+       if (ssl->method->dtls)
+               return 0;
+
+       ssl->quic_method = quic_method;
+
+       return 1;
+}
+
 int
 SSL_do_handshake(SSL *s)
 {
index 6a4f14d..d45983a 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl_locl.h,v 1.422 2022/08/18 07:00:59 tb Exp $ */
+/* $OpenBSD: ssl_locl.h,v 1.423 2022/08/21 19:32:38 jsing Exp $ */
 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
@@ -882,6 +882,7 @@ typedef struct ssl_ctx_internal_st {
 
 struct ssl_ctx_st {
        const SSL_METHOD *method;
+       const SSL_QUIC_METHOD *quic_method;
 
        STACK_OF(SSL_CIPHER) *cipher_list;
 
@@ -1073,7 +1074,7 @@ struct ssl_st {
        int version;
 
        const SSL_METHOD *method;
-       const void *quic_method; /* XXX */
+       const SSL_QUIC_METHOD *quic_method;
 
        /* There are 2 BIO's even though they are normally both the
         * same.  This is so data can be read and written to different