Browse Source

add QUIC support.

Stefan Eissing 1 year ago
parent
commit
4431438fb2
25 changed files with 4173 additions and 46 deletions
  1. 2 0
      .gitignore
  2. 7 0
      cmake/functions.cmake
  3. 49 1
      configure.ac
  4. 99 0
      doc/QUIC.md
  5. 606 0
      doc/dox_comments/header_files/quic.h
  6. 15 0
      doc/dox_comments/header_files/ssl.h
  7. 3 1
      doc/include.am
  8. 1 0
      rpm/spec.in
  9. 4 0
      src/include.am
  10. 47 3
      src/internal.c
  11. 5 0
      src/keys.c
  12. 1262 0
      src/quic.c
  13. 25 1
      src/ssl.c
  14. 148 3
      src/tls.c
  15. 108 32
      src/tls13.c
  16. 1 0
      tests/include.am
  17. 1393 0
      tests/quic.c
  18. 7 0
      tests/unit.c
  19. 1 0
      tests/unit.h
  20. 1 0
      wolfssl/error-ssl.h
  21. 1 0
      wolfssl/include.am
  22. 87 4
      wolfssl/internal.h
  23. 1 0
      wolfssl/openssl/ssl.h
  24. 294 0
      wolfssl/quic.h
  25. 6 1
      wolfssl/ssl.h

+ 2 - 0
.gitignore

@@ -390,3 +390,5 @@ cmake_install.cmake
 # GDB Settings
 \.gdbinit
 
+# Pycharm and other IDEs
+\.idea

+ 7 - 0
cmake/functions.cmake

@@ -44,6 +44,9 @@ function(generate_build_flags)
     if(WOLFSSL_DTLS13 OR WOLFSSL_USER_SETTINGS)
         set(BUILD_DTLS13 "yes" PARENT_SCOPE)
     endif()
+    if(WOLFSSL_QUIC)
+        set(BUILD_QUIC "yes" PARENT_SCOPE)
+    endif()
     if(WOLFSSL_RNG OR WOLFSSL_USER_SETTINGS)
         set(BUILD_RNG "yes" PARENT_SCOPE)
     endif()
@@ -818,6 +821,10 @@ function(generate_lib_src_list LIB_SOURCES)
                    list(APPEND LIB_SOURCES src/dtls13.c)
               endif()
 
+              if(BUILD_QUIC)
+                   list(APPEND LIB_SOURCES src/quic.c)
+              endif()
+
               if(BUILD_OCSP)
                    list(APPEND LIB_SOURCES src/ocsp.c)
               endif()

+ 49 - 1
configure.ac

@@ -664,6 +664,7 @@ then
         test "$enable_tls13" = "" && enable_tls13=yes
         test "$enable_rsapss" = "" && enable_rsapss=yes
     fi
+    test "$enable_quic" = "" && enable_quic=yes
 
     # this set is also enabled by enable-all-crypto:
     test "$enable_atomicuser" = "" && enable_atomicuser=yes
@@ -1037,6 +1038,25 @@ then
     ENABLED_TLS13="no"
 fi
 
+# QUIC support
+AC_ARG_ENABLE([quic],
+    [AS_HELP_STRING([--enable-quic],[Enable QUIC API with wolfSSL TLS v1.3 (default: disabled)])],
+    [ ENABLED_QUIC=$enableval ],
+    [ ENABLED_QUIC=no ]
+    )
+
+if test "$ENABLED_QUIC" = "yes"
+then
+    if test "x$ENABLED_TLS13" = "xno"
+    then
+        AC_MSG_ERROR([TLS 1.3 is disabled - necessary for QUIC])
+    fi
+    AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_QUIC"
+    # QUIC proto handlers need app_data at WOLFSSL*
+    AM_CFLAGS="$AM_CFLAGS -DHAVE_EX_DATA"
+fi
+
+
 # Post-handshake Authentication
 AC_ARG_ENABLE([postauth],
     [AS_HELP_STRING([--enable-postauth],[Enable wolfSSL Post-handshake Authentication (default: disabled)])],
@@ -1477,6 +1497,12 @@ AC_ARG_ENABLE([opensslextra],
     [ ENABLED_OPENSSLEXTRA=no ]
     )
 
+if test "$ENABLED_QUIC" = "yes"
+then
+    ENABLED_OPENSSLEXTRA="yes"
+fi
+
+
 # One Error Queue per Thread
 AC_ARG_ENABLE([error-queue-per-thread],
 [AS_HELP_STRING([--enable-error-queue-per-thread],[Enable one error queue per thread. Requires thread local storage. (default: disabled)])],
@@ -1943,6 +1969,11 @@ then
     ENABLED_AESCTR=yes
 fi
 
+if test "$ENABLED_QUIC" = "yes"
+then
+    ENABLED_AESCTR=yes
+fi
+
 # AES-OFB
 AC_ARG_ENABLE([aesofb],
     [AS_HELP_STRING([--enable-aesofb],[Enable wolfSSL AES-OFB support (default: disabled)])],
@@ -4550,6 +4581,11 @@ then
     ENABLED_SNI="yes"
 fi
 
+if test "$ENABLED_QUIC" = "yes"
+then
+    ENABLED_SNI=yes
+fi
+
 if test "x$ENABLED_SNI" = "xyes"
 then
     AM_CFLAGS="$AM_CFLAGS -DHAVE_TLS_EXTENSIONS -DHAVE_SNI"
@@ -4574,6 +4610,11 @@ then
     ENABLED_ALPN=yes
 fi
 
+if test "$ENABLED_QUIC" = "yes"
+then
+    ENABLED_ALPN=yes
+fi
+
 if test "x$ENABLED_ALPN" = "xyes"
 then
     AM_CFLAGS="$AM_CFLAGS -DHAVE_TLS_EXTENSIONS -DHAVE_ALPN"
@@ -4833,7 +4874,6 @@ then
     AM_CFLAGS="$AM_CFLAGS -DNO_SESSION_CACHE"
 fi
 
-
 # PKCS7
 AC_ARG_ENABLE([pkcs7],
     [AS_HELP_STRING([--enable-pkcs7],[Enable PKCS7 (default: disabled)])],
@@ -5669,6 +5709,12 @@ then
     # FTPS server requires pointer to session cache
     AM_CFLAGS="$AM_CFLAGS -DNO_SESSION_CACHE_REF"
 
+    if test "x$ENABLED_QUIC" = "xno"
+    then
+        ENABLED_QUIC="yes"
+        AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_QUIC"
+    fi
+
     AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_DES_ECB"
 fi
 
@@ -8033,6 +8079,7 @@ AM_CONDITIONAL([BUILD_ERROR_STRINGS],[test "x$ENABLED_ERROR_STRINGS" = "xyes"])
 AM_CONDITIONAL([BUILD_DO178],[test "x$ENABLED_DO178" = "xyes"])
 AM_CONDITIONAL([BUILD_PSA],[test "x$ENABLED_PSA" = "xyes"])
 AM_CONDITIONAL([BUILD_DTLS13],[test "x$ENABLED_DTLS13" = "xyes"])
+AM_CONDITIONAL([BUILD_QUIC],[test "x$ENABLED_QUIC" = "xyes"])
 
 if test "$ENABLED_REPRODUCIBLE_BUILD" != "yes" &&
    (test "$ax_enable_debug" = "yes" ||
@@ -8381,6 +8428,7 @@ echo "   * TLS v1.2:                   $ENABLED_TLSV12"
 echo "   * TLS v1.3:                   $ENABLED_TLS13"
 echo "   * Post-handshake Auth:        $ENABLED_TLS13_POST_AUTH"
 echo "   * Early Data:                 $ENABLED_TLS13_EARLY_DATA"
+echo "   * QUIC:                       $ENABLED_QUIC"
 echo "   * Send State in HRR Cookie:   $ENABLED_SEND_HRR_COOKIE"
 echo "   * OCSP:                       $ENABLED_OCSP"
 echo "   * OCSP Stapling:              $ENABLED_CERTIFICATE_STATUS_REQUEST"

+ 99 - 0
doc/QUIC.md

@@ -0,0 +1,99 @@
+# QUIC in wolfSSL
+
+This is an intermediate documentation about the added support for the QUIC protocol in wolfSSL. 
+It is intended as a guide to reviewing the changes and a base for future update of the standard
+wolfSSL documentation, once the changes have been reviewed.
+
+## Configuration
+
+QUIC support is enabled in the common autoconf style via:
+
+```
+> ./configure --enable-quic
+```
+
+this drags in some very basic features. To have everything necessary for a QUIC protocol implementation like ngtcpo2, one would do:
+
+```
+ ./configure --enable-quic --enable-session-ticket --enable-earlydata --enable-psk --enable-sni --enable-alpn
+```
+
+CMake support files have been updated as well, but not tested.
+
+## Structure of Changes
+
+The following files carry the main code added:
+
+```
+src/quic.c
+wolfssl/quic.h
+tests/quic.c
+```
+
+Additions to other files were necessary, protected by `#ifdef WOLFSSL_QUIC`, those cover:
+
+* additions to `SSL_new()`, `SSL_free()`, `SSL_clear()`
+* a new TLSX extension for QUIC transport parameters in QUICv1 and draft-27 versions. A new error code for when this is missing.
+* a new `ssl->options` field to en-/disable TLSv1.3 MiddleBox Compat support, since this feature may not be active on a TLS13 session that is used by QUIC.
+* handling of EarlyData without `EndOfEarlyData` messages being exchanged, since QUIC does not use those.
+
+## API
+
+The exposed API carries all methods that the [quictls/openssl](https://github.com/quictls/openssl) introduces. This seems to become the standard, since other *SLL libraries have picked those up or are about to. The methods are all in the `wolfSSL_` prefix. There are some additional methods, which are covered below.
+
+### Core Interworking
+
+At the base is the struct `WOLFSSL_QUIC_METHOD` which carries four callbacks:
+
+* `set_encryption_secrets()`: to forward generated secrets.
+* `add_handshake_data()`: to forward Handshake messages.
+* `flush_flight()`: to tell the QUIC protocol handler to flush any buffered data.
+* `send_alert()`: to forward SSL alerts.
+
+A QUIC protocol handler installs these via `wolfSSL_CTX_set_quic_method()` or `wolfSSL_set_quic_method()`. When CRYPTO messages arrive from the peer, those are added via `wolfSSL_provide_quic_data()` to the `WOLFSSL*` instance:
+
+```
+  DATA ---recv+decrypt---+
+                         v 
+            wolfSSL_provide_quic_data(ssl, ...)
+            wolfSSL_do_handshake(ssl);
+                  +-> add_handshake_data_callback(REPLY)
+                          |
+  REPLY <--encrypt+send---+
+```
+
+The wolfSSL instance performs the common TLSv1.3 handshake processing with the significant change that it does not encrypt or decrypt messages itself. It computes all the secrets and MACs as usual, however.
+
+Encryption and Decryption is done by the QUIC protocol handler. Which is why it gets access to the secrets
+at the different encryption levels: `initial`(no encryption), `handshake`, `application` and `earlydata`.
+
+For sending data, the level to use for encryption is a call parameter in `add_handshake_data()`. For received data, the level to use for decryption can be interrogated via `wolfSSL_quic_read_level()`.
+
+When the handshake is done, any additional CRYPTO messages are received in the same way, only `wolfSSL_process_quic_post_handshake()` is invoked to process them.
+
+### Crypto Support
+
+At the basic level, there are:
+
+* `wolfSSL_quic_get_aead()`: to get the AEAD cipher negotiated
+* `wolfSSL_quic_get_md()`:  to get the MD negotiated
+* `wolfSSL_quic_get_hp()`: to get the EVP_CIPHER for header protection
+* `wolfSSL_quic_get_aead_tag_len()`: the get the tag length of the negotiated AEAD cipher
+
+In addition to that, the wolfSSL QUIC API offers the following functions:
+
+* `wolfSSL_quic_crypt_new()`: to setup a `WOLFSSL_EVP_CIPHER_CTX` for en- or decryption with AEAD cipher, key and iv.
+* `wolfSSL_quic_aead_encrypt()`: to encrypt with such a context and params
+* `wolfSSL_quic_aead_decrypt()`: to decrypt with such a context and params
+
+and for key generation `wolfSSL_quic_hkdf_extract()`, `wolfSSL_quic_hkdf_expand()` and `wolfSSL_quic_hkdf()`.
+
+## Tests
+
+Tests have been added in `tests/quic.c` to run as part of `unit.tests`. Those go from basic checks on providing data and receiving secrets to complete handshakes between SSL client and server instances. These handshakes are done plain, with session resumption and with early data.
+
+These tests exchange the handshake messages between the SSL instances unencrypted, verifying their sequence and contents. They also verify that client and sever did indeed generate identical secrets for the different encryption levels.
+
+
+
+

+ 606 - 0
doc/dox_comments/header_files/quic.h

@@ -0,0 +1,606 @@
+/*!
+    \ingroup QUIC
+
+    \brief Callback invoked when secrets are generated during a handshake.
+    Since QUIC protocol handlers perform the en-/decryption of packets, they
+    need the negotiated secrets for the levels early_data/handshake/application.
+
+    The callback will be invoked several times during a handshake. Either both
+    or only the read or write secret might be provided. This does not mean the
+    given encryption level is already in effect.
+
+    \return 1 on success, 0 on failure.
+
+    \param ssl - a pointer to a WOLFSSL structure, created using wolfSSL_new().
+    \param level - the encryption level the secrets are for
+    \param read_secret - the secret used in decryption at the given level, may be NULL.
+    \param write_secret - the secret used in encryption at the given level, may be NULL.
+    \param secret_len - the length of the secret
+
+    \sa wolfSSL_set_quic_method
+*/
+int (*set_encryption_secrets)(WOLFSSL *ssl, WOLFSSL_ENCRYPTION_LEVEL level,
+                              const uint8_t *read_secret,
+                              const uint8_t *write_secret, size_t secret_len);
+
+/*!
+    \ingroup QUIC
+
+    \brief Callback invoked for forwarding handshake CRYPTO data to peer.
+    The data forwarded this way is not encrypted. It is the job of the QUIC
+    protocol implementation to do this. Which secrets are to be used
+    is determined by the encryption level specified.
+
+    This callback may be invoked several times during handshake or post handshake
+    processing. The data may cover a complete CRYPTO record, but may also
+    be partial. However, the callback will have received all records data before
+    using another encryption level.
+
+    \return 1 on success, 0 on failure.
+
+    \param ssl - a pointer to a WOLFSSL structure, created using wolfSSL_new().
+    \param level - the encryption level to use for encrypting the data
+    \param data - the data itself
+    \param len - the length of the data
+
+    \sa wolfSSL_set_quic_method
+*/
+int (*add_handshake_data)(WOLFSSL *ssl, WOLFSSL_ENCRYPTION_LEVEL level,
+                          const uint8_t *data, size_t len);
+
+/*!
+    \ingroup QUIC
+
+    \brief Callback invoked for advisory flushing of the data to send.
+
+    \return 1 on success, 0 on failure.
+
+    \param ssl - a pointer to a WOLFSSL structure, created using wolfSSL_new().
+
+    \sa wolfSSL_set_quic_method
+*/
+int (*flush_flight)(WOLFSSL *ssl);
+
+/*!
+    \ingroup QUIC
+
+    \brief Callback invoked when an SSL alert happened during processing.
+
+    \return 1 on success, 0 on failure.
+
+    \param ssl - a pointer to a WOLFSSL structure, created using wolfSSL_new().
+    \param level - the encryption level in effect when the alert happened
+    \param alert - the error
+
+    \sa wolfSSL_set_quic_method
+*/
+int (*send_alert)(WOLFSSL *ssl, WOLFSSL_ENCRYPTION_LEVEL level, uint8_t alert);
+
+/*!
+    \ingroup QUIC
+
+    \brief Activate QUIC protocol for a WOLFSSL_CTX and all derived WOLFSSL instances
+    by providing the four callbacks required. The CTX needs to be a TLSv1.3 one.
+
+    The passed quic_method needs to have a lifetime outlasting the SSL instances.
+    It is not copied. All callbacks need to be provided.
+
+    \return WOLFSSL_SUCCESS If successful.
+
+    \param ctx - a pointer to a WOLFSSL_CTX structure, created using wolfSSL_CTX_new().
+    \param quic_method - the callback structure
+
+    \sa wolfSSL_is_quic
+    \sa wolfSSL_set_quic_method
+*/
+int wolfSSL_CTX_set_quic_method(WOLFSSL_CTX *ctx, const WOLFSSL_QUIC_METHOD *quic_method);
+
+/*!
+    \ingroup QUIC
+
+    \brief Activate QUIC protocol for a WOLFSSL instance  by providing the
+    four callbacks required. The WOLFSSL needs to be a TLSv1.3 one.
+
+    The passed quic_method needs to have a lifetime outlasting the SSL instance.
+    It is not copied. All callbacks need to be provided.
+
+    \return WOLFSSL_SUCCESS If successful.
+
+    \param ssl - a pointer to a WOLFSSL structure, created using wolfSSL_new().
+    \param quic_method - the callback structure
+
+    \sa wolfSSL_is_quic
+    \sa wolfSSL_CTX_set_quic_method
+*/
+int wolfSSL_set_quic_method(WOLFSSL *ssl, const WOLFSSL_QUIC_METHOD *quic_method);
+
+/*!
+    \ingroup QUIC
+
+    \brief Check if QUIC has been activated in a WOLFSSL instance.
+
+    \return 1 if WOLFSSL is using QUIC.
+
+    \param ssl - a pointer to a WOLFSSL structure, created using wolfSSL_new().
+
+    \sa wolfSSL_CTX_quic_method
+    \sa wolfSSL_CTX_set_quic_method
+*/
+int wolfSSL_is_quic(WOLFSSL *ssl);
+
+/*!
+    \ingroup QUIC
+
+    \brief Determine the encryption level for reads currently in use. Meaningful only when
+    the WOLFSSL instance is using QUIC.
+
+    Note that the effective level is always a parameter when passing data back and
+    forth. Data from a peer might arrive at other levels than reported via this
+    function.
+
+    \return encryption level.
+
+    \param ssl - a pointer to a WOLFSSL structure, created using wolfSSL_new().
+
+    \sa wolfSSL_quic_write_level
+*/
+WOLFSSL_ENCRYPTION_LEVEL wolfSSL_quic_read_level(const WOLFSSL *ssl);
+
+/*!
+    \ingroup QUIC
+
+    \brief Determine the encryption level for writes currently in use. Meaningful only when
+    the WOLFSSL instance is using QUIC.
+
+    Note that the effective level is always a parameter when passing data back and
+    forth. Data from a peer might arrive at other levels than reported via this
+    function.
+
+    \return encryption level.
+
+    \param ssl - a pointer to a WOLFSSL structure, created using wolfSSL_new().
+
+    \sa wolfSSL_quic_read_level
+*/
+WOLFSSL_ENCRYPTION_LEVEL wolfSSL_quic_write_level(const WOLFSSL *ssl);
+
+
+/*!
+    \ingroup QUIC
+
+    \brief Configure which QUIC version shall be used. Without calling this,
+    the WOLFSSL will offer both (draft-27 and v1) to a server, resp. accept
+    both from a client and negotiate the most recent one.
+
+    \return WOLFSSL_SUCCESS If successful.
+
+    \param ssl - a pointer to a WOLFSSL structure, created using wolfSSL_new().
+    \param use_legacy - true if draft-27 shall be used, 0 if only QUICv1 is used.
+
+    \sa wolfSSL_set_quic_transport_version
+*/
+void wolfSSL_set_quic_use_legacy_codepoint(WOLFSSL *ssl, int use_legacy);
+
+/*!
+    \ingroup QUIC
+
+    \brief Configure which QUIC version shall be used.
+
+    \return WOLFSSL_SUCCESS If successful.
+
+    \param ssl - a pointer to a WOLFSSL structure, created using wolfSSL_new().
+    \param version - the TLS Extension defined for the QUIC version.
+
+    \sa wolfSSL_set_quic_use_legacy_codepoint
+*/
+void wolfSSL_set_quic_transport_version(WOLFSSL *ssl, int version);
+
+/*!
+    \ingroup QUIC
+
+    \brief Get the configured QUIC version.
+
+    \return TLS Extension of configured version.
+
+    \param ssl - a pointer to a WOLFSSL structure, created using wolfSSL_new().
+
+    \sa wolfSSL_set_quic_use_legacy_codepoint
+    \sa wolfSSL_set_quic_transport_version
+*/
+int wolfSSL_get_quic_transport_version(const WOLFSSL *ssl);
+
+/*!
+    \ingroup QUIC
+
+    \brief Set the QUIC transport parameters to use.
+
+    \return WOLFSSL_SUCCESS If successful.
+
+    \param ssl - a pointer to a WOLFSSL structure, created using wolfSSL_new().
+    \param params - the parameter bytes to use
+    ·param params_len - the length of the parameters
+
+    \sa wolfSSL_set_quic_use_legacy_codepoint
+    \sa wolfSSL_set_quic_transport_version
+*/
+int wolfSSL_set_quic_transport_params(WOLFSSL *ssl, const uint8_t *params, size_t params_len);
+
+/*!
+    \ingroup QUIC
+
+    \brief Get the negotiated QUIC transport version. This will only give
+    meaningful results when called after the respective TLS extensions have
+    been seen from the peer.
+
+    \return the negotiated version or -1.
+
+    \param ssl - a pointer to a WOLFSSL structure, created using wolfSSL_new().
+
+    \sa wolfSSL_set_quic_use_legacy_codepoint
+    \sa wolfSSL_set_quic_transport_version
+*/
+int wolfSSL_get_peer_quic_transport_version(const WOLFSSL *ssl);
+
+/*!
+    \ingroup QUIC
+
+    \brief Get the negotiated QUIC transport parameters. This will only give
+    meaningful results when called after the respective TLS extensions have
+    been seen from the peer.
+
+    \param ssl - a pointer to a WOLFSSL structure, created using wolfSSL_new().
+    \param out_params - the parameters sent be the peer, set to NULL if not available.
+    \param out_params_len - the length of the parameters sent be the peer, set to 0 if not available
+
+    \sa wolfSSL_get_peer_quic_transport_version
+*/
+void wolfSSL_get_peer_quic_transport_params(const WOLFSSL *ssl, const uint8_t **out_params, size_t *out_params_len);
+
+
+/*!
+    \ingroup QUIC
+
+    \brief Configure if Early Data is enabled. Intended for servers to signal
+    this to clients.
+
+    \param ssl - a pointer to a WOLFSSL structure, created using wolfSSL_new().
+    \param enabled - != 0 iff early data is enabled
+
+*/
+void wolfSSL_set_quic_early_data_enabled(WOLFSSL *ssl, int enabled);
+
+/*!
+    \ingroup QUIC
+
+    \brief Get advice on the amount of data that shall be "in flight", e.g. unacknowledged
+    at the given encryption level. This is the amount of data the WOLFSSL instance
+    is prepared to buffer.
+
+    \return the recommend max data in flight
+
+    \param ssl - a pointer to a WOLFSSL structure, created using wolfSSL_new().
+    \param level - the encryption level to inquire about
+
+*/
+size_t wolfSSL_quic_max_handshake_flight_len(const WOLFSSL *ssl, WOLFSSL_ENCRYPTION_LEVEL level);
+
+
+/*!
+    \ingroup QUIC
+
+    \brief Pass decrypted CRYPTO data to the WOLFSSL instance for further processing.
+    The encryption level between calls is only every allowed to increase and it is
+    also checked that data records are complete before a change in encryption
+    level is accepted.
+
+    \return WOLFSSL_SUCCESS If successful.
+
+    \param ssl - a pointer to a WOLFSSL structure, created using wolfSSL_new().
+    \param level - the level the data was encrypted at
+    \param data - the data itself
+    \param len - the length of the data
+
+    \sa wolfSSL_process_quic_post_handshake
+    \sa wolfSSL_quic_read_write
+    \sa wolfSSL_accept
+    \sa wolfSSL_connect
+*/
+int wolfSSL_provide_quic_data(WOLFSSL *ssl, WOLFSSL_ENCRYPTION_LEVEL level, const uint8_t *data, size_t len);
+
+/*!
+    \ingroup QUIC
+
+    \brief Process any CRYPTO records that have been provided after the handshake
+    has completed. Will fail if called before that.
+
+    \return WOLFSSL_SUCCESS If successful.
+
+    \param ssl - a pointer to a WOLFSSL structure, created using wolfSSL_new().
+
+    \sa wolfSSL_provide_quic_data
+    \sa wolfSSL_quic_read_write
+    \sa wolfSSL_accept
+    \sa wolfSSL_connect
+*/
+WOLFSSL_API int wolfSSL_process_quic_post_handshake(WOLFSSL *ssl);
+
+/*!
+    \ingroup QUIC
+
+    \brief Process any CRYPTO records that have been provided during or after the handshake.
+    Will progress the handshake if not already complete and otherwise work like
+    wolfSSL_process_quic_post_handshake().
+
+    \return WOLFSSL_SUCCESS If successful.
+
+    \param ssl - a pointer to a WOLFSSL structure, created using wolfSSL_new().
+
+    \sa wolfSSL_provide_quic_data
+    \sa wolfSSL_quic_read_write
+    \sa wolfSSL_accept
+    \sa wolfSSL_connect
+*/
+int wolfSSL_quic_read_write(WOLFSSL *ssl);
+
+/*!
+    \ingroup QUIC
+
+    \brief Get the AEAD cipher negotiated in the TLS handshake.
+
+    \return negotiated cipher or NULL if not determined.
+
+    \param ssl - a pointer to a WOLFSSL structure, created using wolfSSL_new().
+
+    \sa wolfSSL_quic_aead_is_gcm
+    \sa wolfSSL_quic_aead_is_ccm
+    \sa wolfSSL_quic_aead_is_chacha20
+    \sa wolfSSL_quic_get_aead_tag_len
+    \sa wolfSSL_quic_get_md
+    \sa wolfSSL_quic_get_hp
+    \sa wolfSSL_quic_crypt_new
+    \sa wolfSSL_quic_aead_encrypt
+    \sa wolfSSL_quic_aead_decrypt
+*/
+const WOLFSSL_EVP_CIPHER *wolfSSL_quic_get_aead(WOLFSSL *ssl);
+
+/*!
+    \ingroup QUIC
+
+    \brief Check if the AEAD cipher is GCM.
+
+    \return != 0 iff the AEAD cipher is GCM.
+
+    \param cipher - the cipher
+
+    \sa wolfSSL_quic_get_aead
+    \sa wolfSSL_quic_aead_is_ccm
+    \sa wolfSSL_quic_aead_is_chacha20
+    \sa wolfSSL_quic_get_aead_tag_len
+    \sa wolfSSL_quic_get_md
+    \sa wolfSSL_quic_get_hp
+    \sa wolfSSL_quic_crypt_new
+    \sa wolfSSL_quic_aead_encrypt
+    \sa wolfSSL_quic_aead_decrypt
+*/
+int wolfSSL_quic_aead_is_gcm(const WOLFSSL_EVP_CIPHER *aead_cipher);
+
+/*!
+    \ingroup QUIC
+
+    \brief Check if the AEAD cipher is CCM.
+
+    \return != 0 iff the AEAD cipher is CCM.
+
+    \param cipher - the cipher
+
+    \sa wolfSSL_quic_get_aead
+    \sa wolfSSL_quic_aead_is_gcm
+    \sa wolfSSL_quic_aead_is_chacha20
+    \sa wolfSSL_quic_get_aead_tag_len
+    \sa wolfSSL_quic_get_md
+    \sa wolfSSL_quic_get_hp
+    \sa wolfSSL_quic_crypt_new
+    \sa wolfSSL_quic_aead_encrypt
+    \sa wolfSSL_quic_aead_decrypt
+*/
+int wolfSSL_quic_aead_is_ccm(const WOLFSSL_EVP_CIPHER *aead_cipher);
+
+/*!
+    \ingroup QUIC
+
+    \brief Check if the AEAD cipher is CHACHA20.
+
+    \return != 0 iff the AEAD cipher is CHACHA20.
+
+    \param cipher - the cipher
+
+    \sa wolfSSL_quic_get_aead
+    \sa wolfSSL_quic_aead_is_ccm
+    \sa wolfSSL_quic_aead_is_gcm
+    \sa wolfSSL_quic_get_aead_tag_len
+    \sa wolfSSL_quic_get_md
+    \sa wolfSSL_quic_get_hp
+    \sa wolfSSL_quic_crypt_new
+    \sa wolfSSL_quic_aead_encrypt
+    \sa wolfSSL_quic_aead_decrypt
+*/
+int wolfSSL_quic_aead_is_chacha20(const WOLFSSL_EVP_CIPHER *aead_cipher);
+
+/*!
+    \ingroup QUIC
+
+    \brief Determine the tag length for the AEAD cipher.
+
+    \return tag length of AEAD cipher.
+
+    \param cipher - the cipher
+
+    \sa wolfSSL_quic_get_aead
+*/
+WOLFSSL_API size_t wolfSSL_quic_get_aead_tag_len(const WOLFSSL_EVP_CIPHER *aead_cipher);
+
+/*!
+    \ingroup QUIC
+
+    \brief Determine the message digest negotiated in the TLS handshake.
+
+    \return the message digest negotiated in the TLS handshake
+
+    \param ssl - a pointer to a WOLFSSL structure, created using wolfSSL_new().
+
+    \sa wolfSSL_quic_get_aead
+    \sa wolfSSL_quic_get_hp
+*/
+WOLFSSL_API const WOLFSSL_EVP_MD *wolfSSL_quic_get_md(WOLFSSL *ssl);
+
+/*!
+    \ingroup QUIC
+
+    \brief Determine the header protection cipher negotiated in the TLS handshake.
+
+    \return the header protection cipher negotiated in the TLS handshake
+
+    \param ssl - a pointer to a WOLFSSL structure, created using wolfSSL_new().
+
+    \sa wolfSSL_quic_get_aead
+    \sa wolfSSL_quic_get_md
+*/
+const WOLFSSL_EVP_CIPHER *wolfSSL_quic_get_hp(WOLFSSL *ssl);
+
+/*!
+    \ingroup QUIC
+
+    \brief Create a cipher context for en-/decryption.
+
+    \return the created context or NULL in case of errors.
+
+    \param cipher - the cipher to use in the context.
+    \param key - the key to use in the context.
+    \param iv - the iv to use in the context.
+    \param encrypt - != 0 if for encryption, otherwise decryption
+
+    \sa wolfSSL_quic_get_aead
+    \sa wolfSSL_quic_get_hp
+    \sa wolfSSL_quic_aead_encrypt
+    \sa wolfSSL_quic_aead_decrypt
+*/
+WOLFSSL_EVP_CIPHER_CTX *wolfSSL_quic_crypt_new(const WOLFSSL_EVP_CIPHER *cipher,
+                                               const uint8_t *key, const uint8_t *iv, int encrypt);
+
+/*!
+    \ingroup QUIC
+
+    \brief Encrypt the plain text in the given context.
+
+    \return WOLFSSL_SUCCESS If successful.
+
+    \param dest - destination where encrypted data is to be written
+    \param aead_ctx - the cipher context to use
+    \param plain - the plain data to encrypt
+    \param plainlen - the length of the plain data
+    \param iv - the iv to use
+    \param aad - the add to use
+    \param aadlen - the length of the aad
+
+    \sa wolfSSL_quic_get_aead
+    \sa wolfSSL_quic_get_hp
+    \sa wolfSSL_quic_crypt_new
+    \sa wolfSSL_quic_aead_decrypt
+*/
+int wolfSSL_quic_aead_encrypt(uint8_t *dest, WOLFSSL_EVP_CIPHER_CTX *aead_ctx,
+                              const uint8_t *plain, size_t plainlen,
+                              const uint8_t *iv, const uint8_t *aad, size_t aadlen);
+
+/*!
+    \ingroup QUIC
+
+    \brief Decrypt the cipher text in the given context.
+
+    \return WOLFSSL_SUCCESS If successful.
+
+    \param dest - destination where plain text is to be written
+    \param ctx - the cipher context to use
+    \param enc - the encrypted data to decrypt
+    \param envlen - the length of the encrypted data
+    \param iv - the iv to use
+    \param aad - the add to use
+    \param aadlen - the length of the aad
+
+    \sa wolfSSL_quic_get_aead
+    \sa wolfSSL_quic_get_hp
+    \sa wolfSSL_quic_crypt_new
+    \sa wolfSSL_quic_aead_encrypt
+*/
+int wolfSSL_quic_aead_decrypt(uint8_t *dest, WOLFSSL_EVP_CIPHER_CTX *ctx,
+                              const uint8_t *enc, size_t enclen,
+                              const uint8_t *iv, const uint8_t *aad, size_t aadlen);
+
+/*!
+    \ingroup QUIC
+
+    \brief Extract a pseudo random key.
+
+    \return WOLFSSL_SUCCESS If successful.
+
+    \param dest - destination where key is to be written
+    \param md - message digest to use
+    \param secret - the secret to use
+    \param secretlen - the length of the secret
+    \param salt - the salt to use
+    \param saltlen - the length of the salt
+
+    \sa wolfSSL_quic_hkdf_expand
+    \sa wolfSSL_quic_hkdf
+*/
+int wolfSSL_quic_hkdf_extract(uint8_t *dest, const WOLFSSL_EVP_MD *md,
+                              const uint8_t *secret, size_t secretlen,
+                              const uint8_t *salt, size_t saltlen);
+
+/*!
+    \ingroup QUIC
+
+    \brief Expand a pseudo random key into a new key.
+
+    \return WOLFSSL_SUCCESS If successful.
+
+    \param dest - destination where key is to be written
+    \param destlen - length of the key to expand
+    \param md - message digest to use
+    \param secret - the secret to use
+    \param secretlen - the length of the secret
+    \param info - the info to use
+    \param infolen - the length of the info
+
+    \sa wolfSSL_quic_hkdf_extract
+    \sa wolfSSL_quic_hkdf
+*/
+int wolfSSL_quic_hkdf_expand(uint8_t *dest, size_t destlen,
+                             const WOLFSSL_EVP_MD *md,
+                             const uint8_t *secret, size_t secretlen,
+                             const uint8_t *info, size_t infolen);
+
+/*!
+    \ingroup QUIC
+
+    \brief Expand and Extract a pseudo random key.
+
+    \return WOLFSSL_SUCCESS If successful.
+
+    \param dest - destination where key is to be written
+    \param destlen - length of the key
+    \param md - message digest to use
+    \param secret - the secret to use
+    \param secretlen - the length of the secret
+    \param salt - the salt to use
+    \param saltlen - the length of the salt
+    \param info - the info to use
+    \param infolen - the length of the info
+
+    \sa wolfSSL_quic_hkdf_extract
+    \sa wolfSSL_quic_hkdf_expand
+*/
+int wolfSSL_quic_hkdf(uint8_t *dest, size_t destlen,
+                      const WOLFSSL_EVP_MD *md,
+                      const uint8_t *secret, size_t secretlen,
+                      const uint8_t *salt, size_t saltlen,
+                      const uint8_t *info, size_t infolen);

+ 15 - 0
doc/dox_comments/header_files/ssl.h

@@ -14332,3 +14332,18 @@ int wolfSSL_RSA_sign_generic_padding(int type, const unsigned char* m,
  \param ssl A WOLFSSL object pointer
 */
 int wolfSSL_dtls13_has_pending_msg(WOLFSSL *ssl);
+
+/*!
+    \ingroup SSL
+    \brief Get the maximum size of Early Data from a session.
+
+    \param [in] s  the WOLFSSL_SESSION instance.
+
+    \return the value of max_early_data that was configured in the WOLFSSL* the session
+    was derived from.
+
+    \sa wolfSSL_set_max_early_data
+    \sa wolfSSL_write_early_data
+    \sa wolfSSL_read_early_data
+ */
+unsigned int wolfSSL_SESSION_get_max_early_data(const WOLFSSL_SESSION *s);

+ 3 - 1
doc/include.am

@@ -2,7 +2,9 @@
 # included from Top Level Makefile.am
 # All paths should be given relative to the root
 
-dist_doc_DATA+= doc/README.txt
+dist_doc_DATA+= doc/README.txt \
+	doc/QUIC.md
+
 
 dox-pdf:
 	echo "Generating PDF"

+ 1 - 0
rpm/spec.in

@@ -234,6 +234,7 @@ mkdir -p $RPM_BUILD_ROOT/
 %{_includedir}/wolfssl/openssl/x509_vfy.h
 %{_includedir}/wolfssl/openssl/x509v3.h
 %{_includedir}/wolfssl/options.h
+%{_includedir}/wolfssl/quic.h
 %{_includedir}/wolfssl/sniffer.h
 %{_includedir}/wolfssl/sniffer_error.h
 %{_includedir}/wolfssl/ssl.h

+ 4 - 0
src/include.am

@@ -699,6 +699,10 @@ if BUILD_DTLS13
 src_libwolfssl_la_SOURCES += src/dtls13.c
 endif
 
+if BUILD_QUIC
+src_libwolfssl_la_SOURCES += src/quic.c
+endif
+
 endif !BUILD_CRYPTONLY
 
 

+ 47 - 3
src/internal.c

@@ -533,7 +533,11 @@ static WC_INLINE int IsEncryptionOn(WOLFSSL* ssl, int isSend)
 
     }
     #endif /* WOLFSSL_DTLS */
-
+    #ifdef WOLFSSL_QUIC
+        if (WOLFSSL_IS_QUIC(ssl) && IsAtLeastTLSv1_3(ssl->version)) {
+            return 0;
+        }
+    #endif
     return ssl->keys.encryptionOn &&
         (isSend ? ssl->encrypt.setup : ssl->decrypt.setup);
 }
@@ -6871,6 +6875,13 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup)
     ssl->dtls13Rtx.rtxRecordTailPtr = &ssl->dtls13Rtx.rtxRecords;
 #endif /* WOLFSSL_DTLS13 */
 
+#ifdef WOLFSSL_QUIC
+    if (ctx->quic.method) {
+        ret = wolfSSL_set_quic_method(ssl, ctx->quic.method);
+        if (ret != WOLFSSL_SUCCESS)
+            return ret;
+    }
+#endif
     return 0;
 }
 
@@ -7538,6 +7549,9 @@ void SSL_ResourceFree(WOLFSSL* ssl)
 #ifdef WOLFSSL_DTLS13
     Dtls13FreeFsmResources(ssl);
 #endif /* WOLFSSL_DTLS13 */
+#ifdef WOLFSSL_QUIC
+    wolfSSL_quic_free(ssl);
+#endif
 }
 
 /* Free any handshake resources no longer needed */
@@ -9268,6 +9282,15 @@ static int wolfSSLReceive(WOLFSSL* ssl, byte* buf, word32 sz)
     int recvd;
     int retryLimit = WOLFSSL_MODE_AUTO_RETRY_ATTEMPTS;
 
+#ifdef WOLFSSL_QUIC
+    if (WOLFSSL_IS_QUIC(ssl)) {
+        /* QUIC only "reads" from data provided by the application
+         * via wolfSSL_provide_quic_data(). Transfer from there
+         * into the inputBuffer. */
+        return wolfSSL_quic_receive(ssl, buf, sz);
+    }
+#endif
+
     if (ssl->CBIORecv == NULL) {
         WOLFSSL_MSG("Your IO Recv callback is null, please set");
         return -1;
@@ -9423,7 +9446,7 @@ void ShrinkInputBuffer(WOLFSSL* ssl, int forcedFree)
 
 int SendBuffered(WOLFSSL* ssl)
 {
-    if (ssl->CBIOSend == NULL) {
+    if (ssl->CBIOSend == NULL && !WOLFSSL_IS_QUIC(ssl)) {
         WOLFSSL_MSG("Your IO Send callback is null, please set");
         return SOCKET_ERROR_E;
     }
@@ -9436,6 +9459,12 @@ int SendBuffered(WOLFSSL* ssl)
     }
 #endif
 
+#ifdef WOLFSSL_QUIC
+    if (WOLFSSL_IS_QUIC(ssl)) {
+        return wolfSSL_quic_send(ssl);
+    }
+#endif
+
     while (ssl->buffers.outputBuffer.length > 0) {
         int sent = ssl->CBIOSend(ssl,
                                       (char*)ssl->buffers.outputBuffer.buffer +
@@ -17644,7 +17673,7 @@ int TimingPadVerify(WOLFSSL* ssl, const byte* input, int padLen, int macSz,
 
 int DoApplicationData(WOLFSSL* ssl, byte* input, word32* inOutIdx, int sniff)
 {
-    word32 msgSz   = ssl->keys.encryptSz;
+    word32 msgSz   = WOLFSSL_IS_QUIC(ssl)? ssl->curSize : ssl->keys.encryptSz;
     word32 idx     = *inOutIdx;
     int    dataSz;
     int    ivExtra = 0;
@@ -21876,6 +21905,16 @@ static int SendAlert_ex(WOLFSSL* ssl, int severity, int type)
 
     WOLFSSL_ENTER("SendAlert");
 
+#ifdef WOLFSSL_QUIC
+    if (WOLFSSL_IS_QUIC(ssl)) {
+        ret = !ssl->quic.method->send_alert(ssl, ssl->quic.enc_level_write, (uint8_t)type);
+        if (ret) {
+            WOLFSSL_MSG("QUIC send_alert callback error");
+        }
+        return ret;
+    }
+#endif
+
 #ifdef HAVE_WRITE_DUP
     if (ssl->dupWrite && ssl->dupSide == READ_DUP_SIDE) {
         int notifyErr = 0;
@@ -22522,6 +22561,11 @@ const char* wolfSSL_ERR_reason_error_string(unsigned long e)
     case FALCON_KEY_SIZE_E:
         return "Wrong key size for Falcon.";
 
+#ifdef WOLFSSL_QUIC
+    case QUIC_TP_MISSING_E:
+        return "QUIC transport parameter not set";
+#endif
+
     default :
         return "unknown error number";
     }

+ 5 - 0
src/keys.c

@@ -2988,6 +2988,11 @@ int SetKeysSide(WOLFSSL* ssl, enum encrypt_side side)
     if (ret == 0 && ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version))
         ret = Dtls13SetRecordNumberKeys(ssl, side);
 #endif /* WOLFSSL_DTLS13 */
+#ifdef WOLFSSL_QUIC
+    if (ret == 0 && WOLFSSL_IS_QUIC(ssl)) {
+        ret = wolfSSL_quic_keys_active(ssl, side);
+    }
+#endif /* WOLFSSL_QUIC */
 
 #ifdef HAVE_SECURE_RENEGOTIATION
 #ifdef WOLFSSL_DTLS

+ 1262 - 0
src/quic.c

@@ -0,0 +1,1262 @@
+/* quic.c
+ *
+ * Copyright (C) 2006-2022 wolfSSL Inc.
+ *
+ * This file is part of wolfSSL.
+ *
+ * wolfSSL is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+ */
+
+
+  /* Name change compatibility layer no longer needs to be included here */
+
+#ifdef HAVE_CONFIG_H
+    #include <config.h>
+#endif
+
+#include <wolfssl/wolfcrypt/settings.h>
+#ifdef NO_INLINE
+    #include <wolfssl/wolfcrypt/misc.h>
+#else
+    #define WOLFSSL_MISC_INCLUDED
+    #include <wolfcrypt/src/misc.c>
+#endif
+
+#ifndef WOLFCRYPT_ONLY
+#ifdef WOLFSSL_QUIC
+
+#include <wolfssl/error-ssl.h>
+#include <wolfssl/ssl.h>
+#include <wolfssl/internal.h>
+
+#include <wolfssl/openssl/buffer.h>
+#include <wolfssl/openssl/ecdsa.h>
+#include <wolfssl/openssl/evp.h>
+#include <wolfssl/openssl/kdf.h>
+
+
+static int qr_length(const uint8_t *data, size_t len)
+{
+    word32 rlen;
+    if (len < 4) {
+        return 0;
+    }
+    c24to32(&data[1], &rlen);
+    return (int)rlen + 4;
+}
+
+static void quic_record_free(WOLFSSL *ssl, QuicRecord *r)
+{
+    (void)ssl;
+    if (r->data) {
+        ForceZero(r->data, r->capacity);
+        XFREE(r->data, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
+    }
+    XFREE(r, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
+}
+
+
+static QuicRecord *quic_record_make(WOLFSSL *ssl,
+                                    WOLFSSL_ENCRYPTION_LEVEL level,
+                                    const uint8_t *data, size_t len)
+{
+    QuicRecord *qr;
+
+    qr = (QuicRecord*)XMALLOC(sizeof(*qr), ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
+    if (qr) {
+        memset(qr, 0, sizeof(*qr));
+        qr->level = level;
+        if (level == wolfssl_encryption_early_data) {
+            qr->capacity = qr->len = (word32)len;
+        }
+        else {
+            qr->capacity = qr->len = qr_length(data, len);
+        }
+        if (qr->capacity == 0) {
+            qr->capacity = 2*1024;
+        }
+        qr->data = (uint8_t*)XMALLOC(qr->capacity, ssl->heap,
+                                     DYNAMIC_TYPE_TMP_BUFFER);
+        if (!qr->data) {
+            quic_record_free(ssl, qr);
+            return NULL;
+        }
+    }
+    return qr;
+}
+
+static int quic_record_complete(QuicRecord *r)
+{
+    return r->len && r->end >= r->len;
+}
+
+static int quic_record_done(QuicRecord *r)
+{
+    return r->len && r->end >= r->len && r->start >= r->end;
+}
+
+static int quic_record_append(WOLFSSL *ssl, QuicRecord *qr, const uint8_t *data,
+                              size_t len, size_t *pconsumed)
+{
+    size_t missing, consumed = 0;
+    int ret = WOLFSSL_SUCCESS;
+
+    (void)ssl;
+    if (!qr->len && len) {
+        missing = 4 - qr->end;
+        if (len < missing) {
+            XMEMCPY(qr->data + qr->end, data, len);
+            qr->end += len;
+            consumed = len;
+            goto cleanup; /* len consumed, but qr->len still unkown */
+        }
+        XMEMCPY(qr->data + qr->end, data, missing);
+        qr->end += missing;
+        len -= missing;
+        data += missing;
+        consumed = missing;
+
+        qr->len = qr_length(qr->data, qr->end);
+        if (qr->len > qr->capacity) {
+            uint8_t *ndata = (uint8_t*)XREALLOC(qr->data, qr->len, ssl->head,
+                                                DYNAMIC_TYPE_TMP_BUFFER);
+            if (!ndata) {
+                ret = WOLFSSL_FAILURE;
+                goto cleanup;
+            }
+            qr->data = ndata;
+            qr->capacity = qr->len;
+        }
+    }
+
+    if (quic_record_complete(qr) || len == 0) {
+        return 0;
+    }
+
+    missing = qr->len - qr->end;
+    if (len > missing) {
+        len = missing;
+    }
+    XMEMCPY(qr->data + qr->end, data, len);
+    qr->end += len;
+    consumed += len;
+
+cleanup:
+    *pconsumed = (ret == WOLFSSL_SUCCESS) ? consumed : 0;
+    return ret;
+}
+
+
+static word32 add_rec_header(byte* output, word32 length, int type)
+{
+    RecordLayerHeader* rl;
+
+    /* record layer header */
+    rl = (RecordLayerHeader*)output;
+    if (rl == NULL) {
+        return 0;
+    }
+    rl->type    = type;
+    rl->pvMajor = SSLv3_MAJOR;
+    rl->pvMinor = TLSv1_2_MINOR;
+    c16toa((word16)length, rl->length);
+    return RECORD_HEADER_SZ;
+}
+
+static word32 quic_record_transfer(QuicRecord* qr, byte* buf, word32 sz)
+{
+    word32 len = qr->end - qr->start;
+    word32 offset = 0;
+    word16 rlen;
+
+    if (len <= 0) {
+        return 0;
+    }
+    if (qr->rec_hdr_remain == 0) {
+        /* start a new TLS record */
+        rlen = (qr->len <= (word32)MAX_RECORD_SIZE) ?
+                qr->len : (word32)MAX_RECORD_SIZE;
+        offset += add_rec_header(buf, rlen,
+                                 (qr->level == wolfssl_encryption_early_data) ?
+                                  application_data : handshake);
+        qr->rec_hdr_remain = rlen;
+        sz -= offset;
+    }
+    if (len > qr->rec_hdr_remain) {
+        len = qr->rec_hdr_remain;
+    }
+    if (len > sz) {
+        len = sz;
+    }
+    if (len > 0) {
+        XMEMCPY(buf + offset, qr->data + qr->start, len);
+        qr->start += len;
+        qr->rec_hdr_remain -= len;
+    }
+    return len + offset;
+}
+
+
+const QuicTransportParam* QuicTransportParam_new(const uint8_t* data,
+                                                 size_t len, void* heap)
+{
+    QuicTransportParam* tp;
+
+    if (len > 65353) return NULL;
+    tp = (QuicTransportParam*)XMALLOC(sizeof(*tp), heap, DYNAMIC_TYPE_TLSX);
+    if (!tp) return NULL;
+    tp->data = (uint8_t*)XMALLOC(len, heap, DYNAMIC_TYPE_TLSX);
+    if (!tp->data) {
+        XFREE(tp, heap, DYNAMIC_TYPE_TLSX);
+        return NULL;
+    }
+    XMEMCPY((uint8_t*)tp->data, data, len);
+    tp->len = len;
+    return tp;
+}
+
+const QuicTransportParam* QuicTransportParam_dup(const QuicTransportParam* tp,
+                                                 void* heap)
+{
+    QuicTransportParam* tp2;
+    tp2 = (QuicTransportParam*)XMALLOC(sizeof(*tp2), heap, DYNAMIC_TYPE_TLSX);
+    if (!tp2) return NULL;
+    tp2->data = (uint8_t*)XMALLOC(tp->len, heap, DYNAMIC_TYPE_TLSX);
+    if (!tp2->data) {
+        XFREE(tp2, heap, DYNAMIC_TYPE_TLSX);
+        return NULL;
+    }
+    XMEMCPY((uint8_t*)tp2->data, tp->data, tp->len);
+    tp2->len = tp->len;
+    return tp2;
+}
+
+void QuicTransportParam_free(const QuicTransportParam* tp, void* heap)
+{
+    (void)heap;
+    if (tp) {
+        if (tp->data) XFREE((uint8_t*)tp->data, heap, DYNAMIC_TYPE_TLSX);
+        XFREE((void*)tp, heap, DYNAMIC_TYPE_TLSX);
+    }
+}
+
+
+void wolfSSL_quic_clear(WOLFSSL* ssl)
+{
+    QuicEncData* qd;
+
+    /* keep
+     * - ssl->quic.transport_local
+     * - ssl->quic.method
+     * - ssl->quic.transport_version
+     * reset/free everything else
+     */
+    if (ssl->quic.transport_peer) {
+        QTP_FREE(ssl->quic.transport_peer, ssl->heap);
+        ssl->quic.transport_peer = NULL;
+    }
+    if (ssl->quic.transport_peer_draft) {
+        QTP_FREE(ssl->quic.transport_peer_draft, ssl->heap);
+        ssl->quic.transport_peer_draft = NULL;
+    }
+    ssl->quic.enc_level_write = wolfssl_encryption_initial;
+    ssl->quic.enc_level_latest_recvd = wolfssl_encryption_initial;
+
+    while ((qd = ssl->quic.input_head)) {
+        ssl->quic.input_head = qd->next;
+        quic_record_free(ssl, qd);
+    }
+    ssl->quic.input_tail = NULL;
+    ssl->quic.output_rec_remain = 0;
+
+    if (ssl->quic.scratch) {
+        quic_record_free(ssl, ssl->quic.scratch);
+        ssl->quic.scratch = NULL;
+    }
+}
+
+
+void wolfSSL_quic_free(WOLFSSL* ssl)
+{
+    wolfSSL_quic_clear(ssl);
+    if (ssl->quic.transport_local) {
+        QTP_FREE(ssl->quic.transport_local, ssl->heap);
+        ssl->quic.transport_local = NULL;
+    }
+
+    ssl->quic.method = NULL;
+}
+
+
+static int ctx_check_quic_compat(const WOLFSSL_CTX* ctx)
+{
+    if (ctx->method->version.major != SSLv3_MAJOR
+        || ctx->method->version.minor != TLSv1_3_MINOR
+        || ctx->method->downgrade) {
+        return WOLFSSL_FAILURE;
+    }
+    return WOLFSSL_SUCCESS;
+}
+
+static int check_method_sanity(const WOLFSSL_QUIC_METHOD* m)
+{
+    if (m && m->set_encryption_secrets
+        && m->add_handshake_data
+        && m->flush_flight
+        && m->send_alert) {
+        return WOLFSSL_SUCCESS;
+    }
+    return WOLFSSL_FAILURE;
+}
+
+int wolfSSL_CTX_set_quic_method(WOLFSSL_CTX* ctx,
+                                const WOLFSSL_QUIC_METHOD* quic_method)
+{
+    if (ctx_check_quic_compat(ctx) != WOLFSSL_SUCCESS
+        || check_method_sanity(quic_method) != WOLFSSL_SUCCESS) {
+        return WOLFSSL_FAILURE;
+    }
+    ctx->quic.method = quic_method;
+    return WOLFSSL_SUCCESS;
+}
+
+
+int wolfSSL_set_quic_method(WOLFSSL* ssl,
+                            const WOLFSSL_QUIC_METHOD* quic_method)
+{
+    if (ctx_check_quic_compat(ssl->ctx) != WOLFSSL_SUCCESS
+        || check_method_sanity(quic_method) != WOLFSSL_SUCCESS) {
+        return WOLFSSL_FAILURE;
+    }
+    ssl->quic.method = quic_method;
+    return WOLFSSL_SUCCESS;
+}
+
+
+int wolfSSL_is_quic(WOLFSSL* ssl)
+{
+    return WOLFSSL_IS_QUIC(ssl);
+}
+
+
+WOLFSSL_ENCRYPTION_LEVEL wolfSSL_quic_read_level(const WOLFSSL* ssl)
+{
+    return ssl->quic.enc_level_read;
+}
+
+
+WOLFSSL_ENCRYPTION_LEVEL wolfSSL_quic_write_level(const WOLFSSL* ssl)
+{
+    return ssl->quic.enc_level_write;
+}
+
+
+int wolfSSL_set_quic_transport_params(WOLFSSL* ssl,
+                                      const uint8_t* params,
+                                      size_t params_len)
+{
+    const QuicTransportParam* tp;
+    int ret = WOLFSSL_SUCCESS;
+
+    WOLFSSL_ENTER("SSL_set_quic_transport_params");
+
+    if (!params || params_len == 0) {
+        tp = NULL;
+    }
+    else {
+        tp = QuicTransportParam_new(params, params_len, ssl->heap);
+        if (!tp) {
+            ret = WOLFSSL_FAILURE;
+            goto cleanup;
+        }
+    }
+    if (ssl->quic.transport_local)
+        QTP_FREE(ssl->quic.transport_local, ssl->heap);
+    ssl->quic.transport_local = tp;
+
+cleanup:
+    WOLFSSL_LEAVE("SSL_set_quic_transport_params", ret);
+    return ret;
+}
+
+
+void wolfSSL_get_peer_quic_transport_params(const WOLFSSL* ssl,
+                                            const uint8_t** out_params,
+                                            size_t* out_params_len)
+{
+    const QuicTransportParam* tp = ssl->quic.transport_peer ?
+        ssl->quic.transport_peer : ssl->quic.transport_peer_draft;
+
+    *out_params = tp ? tp->data : NULL;
+    *out_params_len = tp ? tp->len : 0;
+}
+
+
+int wolfSSL_get_peer_quic_transport_version(const WOLFSSL* ssl)
+{
+    return ssl->quic.transport_peer ?
+        TLSX_KEY_QUIC_TP_PARAMS : (ssl->quic.transport_peer_draft ?
+        TLSX_KEY_QUIC_TP_PARAMS : -1);
+}
+
+
+void wolfSSL_set_quic_use_legacy_codepoint(WOLFSSL* ssl, int use_legacy)
+{
+    ssl->quic.transport_version = use_legacy ? TLSX_KEY_QUIC_TP_PARAMS_DRAFT
+        : TLSX_KEY_QUIC_TP_PARAMS;
+}
+
+void wolfSSL_set_quic_transport_version(WOLFSSL* ssl, int version)
+{
+    if (version == TLSX_KEY_QUIC_TP_PARAMS
+        || version == TLSX_KEY_QUIC_TP_PARAMS_DRAFT
+        || !version) {
+        ssl->quic.transport_version = version;
+    }
+    else {
+        WOLFSSL_MSG("wolfSSL_set_quic_transport_version: invalid version");
+    }
+}
+
+
+int wolfSSL_get_quic_transport_version(const WOLFSSL* ssl)
+{
+    return ssl->quic.transport_version;
+}
+
+
+int wolfSSL_quic_add_transport_extensions(WOLFSSL* ssl, int msg_type)
+{
+    /* RFC 9001, ch. 8.2: "The quic_transport_parameters extension is carried
+     * in the ClientHello and the EncryptedExtensions messages during the
+     * handshake. Endpoints MUST send the quic_transport_parameters extension;"
+     * Which means, at least one. There can be more to signal compatibility to
+     * older/newer versions.
+     */
+    int ret = 0, is_resp = (msg_type == encrypted_extensions);
+
+    if (ssl->quic.transport_local == NULL) {
+        return QUIC_TP_MISSING_E;
+    }
+
+    if (is_resp) {
+        /* server response: time to decide which version to use */
+        if (ssl->quic.transport_peer && ssl->quic.transport_peer_draft) {
+            if (ssl->quic.transport_version == TLSX_KEY_QUIC_TP_PARAMS_DRAFT) {
+                ret = TLSX_QuicTP_Use(ssl,
+                                      TLSX_KEY_QUIC_TP_PARAMS_DRAFT, is_resp);
+                QTP_FREE(ssl->quic.transport_peer, ssl->heap);
+                ssl->quic.transport_peer = NULL;
+            }
+            else {
+                ret = TLSX_QuicTP_Use(ssl, TLSX_KEY_QUIC_TP_PARAMS, is_resp);
+                QTP_FREE(ssl->quic.transport_peer_draft,
+                                        ssl->heap);
+                ssl->quic.transport_peer_draft = NULL;
+            }
+        }
+        else {
+            if (ssl->quic.transport_version == TLSX_KEY_QUIC_TP_PARAMS_DRAFT
+                && ssl->quic.transport_peer_draft) {
+                ret = TLSX_QuicTP_Use(ssl, TLSX_KEY_QUIC_TP_PARAMS_DRAFT,
+                                      is_resp);
+            }
+            else if (ssl->quic.transport_peer) {
+                ret = TLSX_QuicTP_Use(ssl, TLSX_KEY_QUIC_TP_PARAMS, is_resp);
+            }
+            else {
+                /* no match, send none, will let the client fail */
+            }
+        }
+    }
+    else {
+        /* client hello */
+        if (ssl->quic.transport_version == 0) {
+            /* not being set to a particular id, we send both draft+v1 */
+            ret = TLSX_QuicTP_Use(ssl, TLSX_KEY_QUIC_TP_PARAMS, is_resp)
+                || TLSX_QuicTP_Use(ssl, TLSX_KEY_QUIC_TP_PARAMS_DRAFT, is_resp);
+        }
+        else {
+            /* otherwise, send the version configured */
+            ret = TLSX_QuicTP_Use(ssl, (TLSX_Type)ssl->quic.transport_version,
+                                  is_resp);
+        }
+    }
+    return ret;
+}
+
+
+#define QUIC_HS_FLIGHT_LIMIT_DEFAULT      (16*  1024)
+
+size_t wolfSSL_quic_max_handshake_flight_len(const WOLFSSL* ssl,
+                                             WOLFSSL_ENCRYPTION_LEVEL level)
+{
+    switch (level) {
+        case wolfssl_encryption_initial:
+        case wolfssl_encryption_application:
+                return QUIC_HS_FLIGHT_LIMIT_DEFAULT;
+        case wolfssl_encryption_early_data:
+            return 0; /* QUIC does not send at this level */
+        case wolfssl_encryption_handshake:
+            /* during handshake itself, certificates may be exchanged which
+             * exceed our default limit, advise a higher limit one.
+             */
+            if (ssl->options.side == WOLFSSL_SERVER_END) {
+                if (ssl->options.verifyPeer
+                    && MAX_CERTIFICATE_SZ > QUIC_HS_FLIGHT_LIMIT_DEFAULT)
+                    return MAX_CERTIFICATE_SZ;
+            }
+            else {
+                /* clients may receive the server cert chain
+                 */
+                if (2*MAX_CERTIFICATE_SZ > QUIC_HS_FLIGHT_LIMIT_DEFAULT)
+                    return 2*MAX_CERTIFICATE_SZ;
+            }
+            return QUIC_HS_FLIGHT_LIMIT_DEFAULT;
+    }
+    return 0;
+}
+
+
+#ifdef WOLFSSL_EARLY_DATA
+void wolfSSL_set_quic_early_data_enabled(WOLFSSL* ssl, int enabled)
+{
+    /* This only has effect on server and when the handshake has
+     * not started yet.
+     * This function is part of the quictls/openssl API and does
+     * not return any error, sadly. So we just ignore any
+     * unsuccessful use. But we can produce some warnings.
+     */
+    if (!WOLFSSL_IS_QUIC(ssl)) {
+        WOLFSSL_MSG("wolfSSL_set_quic_early_data_enabled: not a QUIC SSL");
+    }
+    else if (ssl->options.handShakeState != NULL_STATE) {
+        WOLFSSL_MSG("wolfSSL_set_quic_early_data_enabled: handshake started");
+    }
+    else if (ssl->options.side == WOLFSSL_CLIENT_END) {
+        WOLFSSL_MSG("wolfSSL_set_quic_early_data_enabled: on client side");
+    }
+    else {
+        wolfSSL_set_max_early_data(ssl, enabled ? UINT32_MAX : 0);
+    }
+}
+#endif /* WOLFSSL_EARLY_DATA */
+
+
+int wolfSSL_quic_read_write(WOLFSSL* ssl)
+{
+    int ret = WOLFSSL_SUCCESS, nret;
+
+    WOLFSSL_ENTER("wolfSSL_quic_read_write");
+
+    if (!wolfSSL_is_quic(ssl)) {
+        WOLFSSL_MSG("WOLFSSL_QUIC_READ_WRITE not a QUIC SSL");
+        ret = WOLFSSL_FAILURE;
+        goto cleanup;
+    }
+
+    if (ssl->options.handShakeState != HANDSHAKE_DONE) {
+        ret = wolfSSL_SSL_do_handshake(ssl);
+        if (ret != WOLFSSL_SUCCESS)
+            goto cleanup;
+    }
+
+    while (ssl->quic.input_head != NULL
+           || ssl->buffers.inputBuffer.length > 0) {
+        if ((nret = ProcessReply(ssl)) < 0) {
+            ret = nret;
+            goto cleanup;
+        }
+    }
+    while (ssl->buffers.outputBuffer.length > 0) {
+        SendBuffered(ssl);
+    }
+
+cleanup:
+    WOLFSSL_LEAVE("wolfSSL_quic_read_write", ret);
+    return ret;
+}
+
+int wolfSSL_process_quic_post_handshake(WOLFSSL* ssl)
+{
+    int ret = WOLFSSL_SUCCESS, nret;
+
+    WOLFSSL_ENTER("wolfSSL_process_quic_post_handshake");
+
+    if (!wolfSSL_is_quic(ssl)) {
+        WOLFSSL_MSG("WOLFSSL_QUIC_POST_HS not a QUIC SSL");
+        ret = WOLFSSL_FAILURE;
+        goto cleanup;
+    }
+
+    if (ssl->options.handShakeState != HANDSHAKE_DONE) {
+        WOLFSSL_MSG("WOLFSSL_QUIC_POST_HS handshake is not done yet");
+        ret = WOLFSSL_FAILURE;
+        goto cleanup;
+    }
+
+    while (ssl->quic.input_head != NULL
+           || ssl->buffers.inputBuffer.length > 0) {
+        if ((nret = ProcessReply(ssl)) < 0) {
+            ret = nret;
+            goto cleanup;
+        }
+    }
+    while (ssl->buffers.outputBuffer.length > 0) {
+        SendBuffered(ssl);
+    }
+
+cleanup:
+    WOLFSSL_LEAVE("wolfSSL_process_quic_post_handshake", ret);
+    return ret;
+}
+
+
+int wolfSSL_provide_quic_data(WOLFSSL* ssl, WOLFSSL_ENCRYPTION_LEVEL level,
+                              const uint8_t* data, size_t len)
+{
+    int ret = WOLFSSL_SUCCESS;
+    size_t l;
+
+    WOLFSSL_ENTER("wolfSSL_provide_quic_data");
+    if (!wolfSSL_is_quic(ssl)) {
+        WOLFSSL_MSG("WOLFSSL_QUIC_PROVIDE_DATA not a QUIC SSL");
+        ret = WOLFSSL_FAILURE;
+        goto cleanup;
+    }
+
+    if (level < wolfSSL_quic_read_level(ssl)
+        || (ssl->quic.input_tail && level < ssl->quic.input_tail->level)
+        || level < ssl->quic.enc_level_latest_recvd) {
+        WOLFSSL_MSG("WOLFSSL_QUIC_PROVIDE_DATA wrong encryption level");
+        ret = WOLFSSL_FAILURE;
+        goto cleanup;
+    }
+
+    while (len > 0) {
+        if (ssl->quic.scratch) {
+            if (ssl->quic.scratch->level != level) {
+                WOLFSSL_MSG("WOLFSSL_QUIC_PROVIDE_DATA wrong encryption level");
+                ret = WOLFSSL_FAILURE;
+                goto cleanup;
+            }
+
+            ret = quic_record_append(ssl, ssl->quic.scratch, data, len, &l);
+            if (ret != WOLFSSL_SUCCESS) {
+                goto cleanup;
+            }
+            data += l;
+            len -= l;
+            if (quic_record_complete(ssl->quic.scratch)) {
+                if (ssl->quic.input_tail) {
+                    ssl->quic.input_tail->next = ssl->quic.scratch;
+                    ssl->quic.input_tail = ssl->quic.scratch;
+                }
+                else {
+                    ssl->quic.input_head = ssl->quic.input_tail =
+                        ssl->quic.scratch;
+                }
+                ssl->quic.scratch = NULL;
+            }
+        }
+        else {
+            /* start of next record with all bytes for the header */
+            ssl->quic.scratch = quic_record_make(ssl, level, data, len);
+            if (!ssl->quic.scratch) {
+                ret = WOLFSSL_FAILURE;
+                goto cleanup;
+            }
+        }
+    }
+
+    ssl->quic.enc_level_latest_recvd = level;
+
+cleanup:
+    WOLFSSL_LEAVE("wolfSSL_provide_quic_data", ret);
+    return ret;
+}
+
+
+/* Called internally when SSL wants a certain amount of input. */
+int wolfSSL_quic_receive(WOLFSSL* ssl, byte* buf, word32 sz)
+{
+    word32 n, transferred = 0;
+
+    WOLFSSL_ENTER("wolfSSL_quic_receive");
+    while (sz > 0) {
+        n = 0;
+        if (ssl->quic.input_head) {
+            n = quic_record_transfer(ssl->quic.input_head, buf, sz);
+            if (quic_record_done(ssl->quic.input_head)) {
+                QuicRecord* qr = ssl->quic.input_head;
+                ssl->quic.input_head = qr->next;
+                if (!qr->next) {
+                    ssl->quic.input_tail = NULL;
+                }
+                quic_record_free(ssl, qr);
+            }
+        }
+
+        if (n == 0) {
+            if (transferred > 0) {
+                goto cleanup;
+            }
+            ssl->error = transferred = WANT_READ;
+            goto cleanup;
+        }
+        sz -= n;
+        buf += n;
+        transferred += n;
+    }
+cleanup:
+    WOLFSSL_LEAVE("wolfSSL_quic_receive", transferred);
+    return transferred;
+}
+
+/**
+ * We need to forward the HANDSHAKE messages to the QUIC protocol stack
+ * via ssl->quic.method->add_handshake_data().
+ * The messages in the output buffer are unencrypted TLS records. We need
+ * to forward the content of those records.
+ */
+static int wolfSSL_quic_send_internal(WOLFSSL* ssl)
+{
+    int ret = 0, aret;
+    size_t len;
+    RecordLayerHeader* rl;
+    word16 rlen;
+    word32 idx, length;
+    byte* output;
+
+    WOLFSSL_ENTER("wolfSSL_quic_send");
+
+    idx = ssl->buffers.outputBuffer.idx;
+    length = ssl->buffers.outputBuffer.length;
+    output = ssl->buffers.outputBuffer.buffer + idx;
+    while (length > 0) {
+        if (ssl->quic.output_rec_remain > 0) {
+            len = ssl->quic.output_rec_remain;
+            if (len > length) {
+                len = length;
+            }
+
+            aret = ssl->quic.method->add_handshake_data(ssl,
+                ssl->quic.output_rec_level, (const uint8_t*)output, len);
+            if (aret != 1) {
+                /* The application has an error. General disaster. */
+                WOLFSSL_MSG("WOLFSSL_QUIC_SEND application failed");
+                ret = FWRITE_ERROR;
+                goto cleanup;
+            }
+            output += len;
+            length -= len;
+            ssl->quic.output_rec_remain -= len;
+        }
+        else {
+            /* at start of a TLS Record */
+            rl = (RecordLayerHeader*)output;
+            ato16(rl->length, &rlen);
+            output += RECORD_HEADER_SZ;
+            length -= RECORD_HEADER_SZ;
+            ssl->quic.output_rec_remain = rlen;
+            ssl->quic.output_rec_level = ssl->quic.enc_level_write;
+            if (rl->type == application_data) {
+                if (ssl->options.handShakeState != HANDSHAKE_DONE) {
+                    ssl->quic.output_rec_level = wolfssl_encryption_early_data;
+                }
+                else {
+                    WOLFSSL_MSG("WOLFSSL_QUIC_SEND app data after handshake");
+                    ret = FWRITE_ERROR;
+                    goto cleanup;
+                }
+            }
+        }
+    }
+
+    ssl->buffers.outputBuffer.idx = 0;
+    ssl->buffers.outputBuffer.length = 0;
+
+cleanup:
+    WOLFSSL_LEAVE("wolfSSL_quic_send", ret);
+    return ret;
+}
+
+int wolfSSL_quic_send(WOLFSSL* ssl)
+{
+    return wolfSSL_quic_send_internal(ssl);
+}
+
+int wolfSSL_quic_forward_secrets(WOLFSSL* ssl, int ktype, int side)
+{
+    const uint8_t* rx_secret = NULL, *tx_secret = NULL;
+    WOLFSSL_ENCRYPTION_LEVEL level;
+    int ret = 0;
+
+    WOLFSSL_ENTER("wolfSSL_quic_forward_secrets");
+    switch (ktype) {
+        case early_data_key:
+            level = wolfssl_encryption_early_data;
+            break;
+        case handshake_key:
+            level = wolfssl_encryption_handshake;
+            break;
+        case traffic_key:
+            FALL_THROUGH;
+        case update_traffic_key:
+            level = wolfssl_encryption_application;
+            break;
+        case no_key:
+            FALL_THROUGH;
+        default:
+            /* ignore */
+            goto cleanup;
+    }
+
+    if (side == ENCRYPT_AND_DECRYPT_SIDE || side == ENCRYPT_SIDE_ONLY) {
+        tx_secret = (ssl->options.side == WOLFSSL_CLIENT_END) ?
+            ssl->clientSecret : ssl->serverSecret;
+    }
+    if (side == ENCRYPT_AND_DECRYPT_SIDE || side == DECRYPT_SIDE_ONLY) {
+        rx_secret = (ssl->options.side == WOLFSSL_CLIENT_END) ?
+            ssl->serverSecret : ssl->clientSecret;
+    }
+
+    if (!tx_secret && !rx_secret) {
+        WOLFSSL_MSG("WOLFSSL_QUIC_FORWARD_SECRETS neither "
+                    "enc- nor decrypt specified");
+        goto cleanup;
+    }
+
+    ret = !ssl->quic.method->set_encryption_secrets(
+        ssl, level, rx_secret, tx_secret, ssl->specs.hash_size);
+
+    /* Having installed the secrets, any future read/write will happen
+     * at the level. Except early data, which is detected on the record
+     * type and the handshake state. */
+     if (ktype == early_data_key) {
+        goto cleanup;
+     }
+
+     if (tx_secret && ssl->quic.enc_level_write != level) {
+        ssl->quic.enc_level_write_next = level;
+     }
+     if (rx_secret && ssl->quic.enc_level_read != level) {
+        ssl->quic.enc_level_read_next = level;
+     }
+
+cleanup:
+    WOLFSSL_LEAVE("wolfSSL_quic_forward_secrets", ret);
+    return ret;
+}
+
+int wolfSSL_quic_keys_active(WOLFSSL* ssl, enum encrypt_side side)
+{
+    int ret = 0;
+
+    WOLFSSL_ENTER("wolfSSL_quic_keys_active");
+    /* Keys derived from recent secrets have been activated */
+    if (side == ENCRYPT_AND_DECRYPT_SIDE || side == ENCRYPT_SIDE_ONLY) {
+        /* If there is data in the output buffers, it was supposed to be
+         * encrypted at the previous level. We need to remember that when
+         * forwarding this data to the QUIC protocol application. */
+        if (ssl->buffers.outputBuffer.length > 0) {
+            ret = wolfSSL_quic_send_internal(ssl);
+            if (ret)
+                goto cleanup;
+        }
+        ssl->quic.enc_level_write = ssl->quic.enc_level_write_next;
+    }
+    if (side == ENCRYPT_AND_DECRYPT_SIDE || side == DECRYPT_SIDE_ONLY) {
+        ssl->quic.enc_level_read = ssl->quic.enc_level_read_next;
+    }
+cleanup:
+    WOLFSSL_LEAVE("wolfSSL_quic_keys_active", ret);
+    return ret;
+}
+
+const WOLFSSL_EVP_CIPHER* wolfSSL_quic_get_aead(WOLFSSL* ssl)
+{
+    WOLFSSL_CIPHER* cipher = wolfSSL_get_current_cipher(ssl);
+    const WOLFSSL_EVP_CIPHER* evp_cipher;
+
+    switch (cipher->cipherSuite) {
+#if !defined(NO_AES) && defined(HAVE_AESGCM)
+        case TLS_AES_128_GCM_SHA256:
+            evp_cipher = wolfSSL_EVP_aes_128_gcm();
+            break;
+        case TLS_AES_256_GCM_SHA384:
+            evp_cipher = wolfSSL_EVP_aes_256_gcm();
+            break;
+#endif
+#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
+        case TLS_CHACHA20_POLY1305_SHA256:
+            evp_cipher = wolfSSL_EVP_chacha20_poly1305();
+            break;
+#endif
+#if defined(WOLFSSL_AES_COUNTER) && defined(WOLFSSL_AES_128)
+        case TLS_AES_128_CCM_SHA256:
+            FALL_THROUGH;
+        case TLS_AES_128_CCM_8_SHA256:
+            evp_cipher = wolfSSL_EVP_aes_128_ctr();
+            break;
+#endif
+
+        default:
+            evp_cipher = NULL;
+            break;
+    }
+
+    if (!evp_cipher) {
+        /* should not happen, as SSL* should not have negotiated it? */
+        WOLFSSL_MSG("wolfSSL_quic_get_aead: current cipher not supported");
+        return NULL;
+    }
+    return evp_cipher;
+}
+
+static int evp_cipher_eq(const WOLFSSL_EVP_CIPHER* c1,
+                         const WOLFSSL_EVP_CIPHER* c2)
+{
+    /* We could check on nid equality, but we seem to have singulars */
+    return c1 == c2;
+}
+
+const WOLFSSL_EVP_CIPHER* wolfSSL_quic_get_hp(WOLFSSL* ssl)
+{
+    WOLFSSL_CIPHER* cipher = wolfSSL_get_current_cipher(ssl);
+    const WOLFSSL_EVP_CIPHER* evp_cipher;
+
+    switch (cipher->cipherSuite) {
+#if !defined(NO_AES) && defined(HAVE_AESGCM)
+        case TLS_AES_128_GCM_SHA256:
+            evp_cipher = wolfSSL_EVP_aes_128_ctr();
+            break;
+        case TLS_AES_256_GCM_SHA384:
+            evp_cipher = wolfSSL_EVP_aes_256_ctr();
+            break;
+#endif
+#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
+        case TLS_CHACHA20_POLY1305_SHA256:
+            evp_cipher = wolfSSL_EVP_chacha20_poly1305();
+            break;
+#endif
+#if defined(WOLFSSL_AES_COUNTER) && defined(WOLFSSL_AES_128)
+        case TLS_AES_128_CCM_SHA256:
+            FALL_THROUGH;
+        case TLS_AES_128_CCM_8_SHA256:
+            evp_cipher = wolfSSL_EVP_aes_128_ctr();
+            break;
+#endif
+
+        default:
+            evp_cipher = NULL;
+            break;
+    }
+
+    if (!evp_cipher) {
+        /* should not happen, as SSL* should not have negotiated it? */
+        WOLFSSL_MSG("wolfSSL_quic_get_hp: current cipher not supported");
+        return NULL;
+    }
+    return evp_cipher;
+}
+
+size_t wolfSSL_quic_get_aead_tag_len(const WOLFSSL_EVP_CIPHER* aead_cipher)
+{
+    WOLFSSL_EVP_CIPHER_CTX ctx;
+
+    XMEMSET(&ctx, 0, sizeof(ctx));
+    if (wolfSSL_EVP_CipherInit(&ctx, aead_cipher, NULL, NULL, 0)
+        != WOLFSSL_SUCCESS) {
+        return 0;
+    }
+    return ctx.authTagSz;
+}
+
+int wolfSSL_quic_aead_is_gcm(const WOLFSSL_EVP_CIPHER* aead_cipher)
+{
+#if !defined(NO_AES) && defined(HAVE_AESGCM)
+    if (evp_cipher_eq(aead_cipher, wolfSSL_EVP_aes_128_gcm())
+#ifdef WOLFSSL_AES_256
+        || evp_cipher_eq(aead_cipher, wolfSSL_EVP_aes_256_gcm())
+#endif
+    ) {
+        return 1;
+    }
+#endif
+    return 0;
+}
+
+int wolfSSL_quic_aead_is_ccm(const WOLFSSL_EVP_CIPHER* aead_cipher)
+{
+#if defined(WOLFSSL_AES_COUNTER) && defined(WOLFSSL_AES_128)
+    if (evp_cipher_eq(aead_cipher, wolfSSL_EVP_aes_128_ctr())) {
+        return 1;
+    }
+#endif
+    return 0;
+}
+
+int wolfSSL_quic_aead_is_chacha20(const WOLFSSL_EVP_CIPHER* aead_cipher)
+{
+    return evp_cipher_eq(aead_cipher, wolfSSL_EVP_chacha20_poly1305());
+}
+
+const WOLFSSL_EVP_MD* wolfSSL_quic_get_md(WOLFSSL* ssl)
+{
+    /* a copy from the handshake md setup */
+    switch(ssl->specs.mac_algorithm) {
+        case no_mac:
+    #ifndef NO_MD5
+        case md5_mac:
+            return wolfSSL_EVP_md5();
+    #endif
+    #ifndef NO_SHA
+        case sha_mac:
+            return wolfSSL_EVP_sha1();
+    #endif
+    #ifdef WOLFSSL_SHA224
+        case sha224_mac:
+            return wolfSSL_EVP_sha224();
+    #endif
+        case sha256_mac:
+            return wolfSSL_EVP_sha256();
+    #ifdef WOLFSSL_SHA384
+        case sha384_mac:
+            return wolfSSL_EVP_sha384();
+    #endif
+    #ifdef WOLFSSL_SHA512
+        case sha512_mac:
+            return wolfSSL_EVP_sha512();
+    #endif
+        case rmd_mac:
+        case blake2b_mac:
+            WOLFSSL_MSG("no suitable EVP_MD");
+            return NULL;
+        default:
+            WOLFSSL_MSG("Unknown mac algorithm");
+            return NULL;
+    }
+}
+
+#ifdef OPENSSL_EXTRA
+
+int wolfSSL_quic_hkdf_extract(uint8_t* dest, const WOLFSSL_EVP_MD* md,
+                              const uint8_t* secret, size_t secretlen,
+                              const uint8_t* salt, size_t saltlen)
+{
+    WOLFSSL_EVP_PKEY_CTX* pctx = NULL;
+    size_t destlen = (size_t)wolfSSL_EVP_MD_size(md);
+    int ret = WOLFSSL_SUCCESS;
+
+    WOLFSSL_ENTER("wolfSSL_quic_hkdf_extract");
+
+    pctx = wolfSSL_EVP_PKEY_CTX_new_id(NID_hkdf, NULL);
+    if (pctx == NULL) {
+        ret = WOLFSSL_FAILURE;
+        goto cleanup;
+    }
+
+    if (wolfSSL_EVP_PKEY_derive_init(pctx) != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_PKEY_CTX_hkdf_mode(
+                pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY) != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_PKEY_CTX_set_hkdf_md(pctx, md) != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_PKEY_CTX_set1_hkdf_salt(
+                pctx, (byte*)salt, (int)saltlen) != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_PKEY_CTX_set1_hkdf_key(
+                pctx, (byte*)secret, (int)secretlen) != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_PKEY_derive(pctx, dest, &destlen) != WOLFSSL_SUCCESS) {
+        ret = WOLFSSL_FAILURE;
+        goto cleanup;
+    }
+
+cleanup:
+    if (pctx)
+        wolfSSL_EVP_PKEY_CTX_free(pctx);
+    WOLFSSL_LEAVE("wolfSSL_quic_hkdf_extract", ret);
+    return ret;
+}
+
+
+int wolfSSL_quic_hkdf_expand(uint8_t* dest, size_t destlen,
+                             const WOLFSSL_EVP_MD* md,
+                             const uint8_t* secret, size_t secretlen,
+                             const uint8_t* info, size_t infolen)
+{
+    WOLFSSL_EVP_PKEY_CTX* pctx = NULL;
+    int ret = WOLFSSL_SUCCESS;
+
+    WOLFSSL_ENTER("wolfSSL_quic_hkdf_expand");
+
+    pctx = wolfSSL_EVP_PKEY_CTX_new_id(NID_hkdf, NULL);
+    if (pctx == NULL) {
+        ret = WOLFSSL_FAILURE;
+        goto cleanup;
+    }
+
+    if (wolfSSL_EVP_PKEY_derive_init(pctx) != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_PKEY_CTX_hkdf_mode(
+                pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_PKEY_CTX_set_hkdf_md(pctx, md) != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_PKEY_CTX_set1_hkdf_salt(
+                pctx, (byte*)"", 0) != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_PKEY_CTX_set1_hkdf_key(
+                pctx, (byte*)secret, (int)secretlen) != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_PKEY_CTX_add1_hkdf_info(
+                pctx, (byte*)info, (int)infolen) != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_PKEY_derive(pctx, dest, &destlen) != WOLFSSL_SUCCESS) {
+        ret = WOLFSSL_FAILURE;
+        goto cleanup;
+    }
+
+cleanup:
+    if (pctx)
+        EVP_PKEY_CTX_free(pctx);
+    WOLFSSL_LEAVE("wolfSSL_quic_hkdf_expand", ret);
+    return ret;
+}
+
+
+int wolfSSL_quic_hkdf(uint8_t* dest, size_t destlen,
+                      const WOLFSSL_EVP_MD* md,
+                      const uint8_t* secret, size_t secretlen,
+                      const uint8_t* salt, size_t saltlen,
+                      const uint8_t* info, size_t infolen)
+{
+    WOLFSSL_EVP_PKEY_CTX* pctx = NULL;
+    int ret = WOLFSSL_SUCCESS;
+
+    WOLFSSL_ENTER("wolfSSL_quic_hkdf");
+
+    pctx = wolfSSL_EVP_PKEY_CTX_new_id(NID_hkdf, NULL);
+    if (pctx == NULL) {
+        ret = WOLFSSL_FAILURE;
+        goto cleanup;
+    }
+
+    if (wolfSSL_EVP_PKEY_derive_init(pctx) != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_PKEY_CTX_hkdf_mode(
+                pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND) != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_PKEY_CTX_set_hkdf_md(pctx, md) != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_PKEY_CTX_set1_hkdf_salt(
+                pctx, (byte*)salt, (int)saltlen) != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_PKEY_CTX_set1_hkdf_key(
+                pctx, (byte*)secret, (int)secretlen) != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_PKEY_CTX_add1_hkdf_info(
+                pctx, (byte*)info, (int)infolen) != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_PKEY_derive(pctx, dest, &destlen) != WOLFSSL_SUCCESS) {
+        ret = WOLFSSL_FAILURE;
+        goto cleanup;
+    }
+
+cleanup:
+    if (pctx)
+        EVP_PKEY_CTX_free(pctx);
+    WOLFSSL_LEAVE("wolfSSL_quic_hkdf", ret);
+    return ret;
+}
+
+#endif /* OPENSSL_EXTRA */
+
+
+WOLFSSL_EVP_CIPHER_CTX* wolfSSL_quic_crypt_new(const WOLFSSL_EVP_CIPHER* cipher,
+                                               const uint8_t* key,
+                                               const uint8_t* iv,
+                                               int encrypt)
+{
+    WOLFSSL_EVP_CIPHER_CTX* ctx;
+
+    ctx = wolfSSL_EVP_CIPHER_CTX_new();
+    if (ctx == NULL) {
+        return NULL;
+    }
+
+    if (wolfSSL_EVP_CipherInit(ctx, cipher, key, iv, encrypt)
+            != WOLFSSL_SUCCESS) {
+        wolfSSL_EVP_CIPHER_CTX_free(ctx);
+        return NULL;
+    }
+
+    return ctx;
+}
+
+
+int wolfSSL_quic_aead_encrypt(uint8_t* dest, WOLFSSL_EVP_CIPHER_CTX* ctx,
+                              const uint8_t* plain, size_t plainlen,
+                              const uint8_t* iv, const uint8_t* aad,
+                              size_t aadlen)
+{
+    int len;
+
+    /* A case can be made if this really should be a function in wolfSSL, since
+     * the same should be doable from the API by a QUIC protocol stack.
+     * What speaks for this:
+     * - it gives us a decent testing point
+     * - API users do not have to re-invent (it fits into ngtcp2 use).
+     *   picotls offers a similar abstraction level for AEAD.
+     * TODO: there is some fiddling in OpenSSL+quic in regard to CCM ciphers
+     *       which we need to check.
+     */
+    if (wolfSSL_EVP_CipherInit(ctx, NULL, NULL, iv, 1) != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_CipherUpdate(
+                ctx, NULL, &len, aad, (int)aadlen) != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_CipherUpdate(
+                ctx, dest, &len, plain, (int)plainlen) != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_CipherFinal(ctx, dest + len, &len) != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_CIPHER_CTX_ctrl(
+                ctx, EVP_CTRL_AEAD_GET_TAG, ctx->authTagSz, dest + plainlen)
+           != WOLFSSL_SUCCESS) {
+        return WOLFSSL_FAILURE;
+    }
+
+    return WOLFSSL_SUCCESS;
+}
+
+
+int wolfSSL_quic_aead_decrypt(uint8_t* dest, WOLFSSL_EVP_CIPHER_CTX* ctx,
+                              const uint8_t* enc, size_t enclen,
+                              const uint8_t* iv, const uint8_t* aad,
+                              size_t aadlen)
+{
+    int len;
+    const uint8_t* tag;
+
+    /* See rationale for wolfSSL_quic_aead_encrypt() on why this is here */
+    if (enclen > INT_MAX || ctx->authTagSz > (int)enclen) {
+        return WOLFSSL_FAILURE;
+    }
+
+    enclen -= ctx->authTagSz;
+    tag = enc + enclen;
+
+    if (wolfSSL_EVP_CipherInit(ctx, NULL, NULL, iv, 0) != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_CIPHER_CTX_ctrl(
+                ctx, EVP_CTRL_AEAD_SET_TAG, ctx->authTagSz, (uint8_t*)tag)
+            != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_CipherUpdate(ctx, NULL, &len, aad, (int)aadlen)
+            != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_CipherUpdate(ctx, dest, &len, enc, (int)enclen)
+            != WOLFSSL_SUCCESS
+        || wolfSSL_EVP_CipherFinal(ctx, dest, &len) != WOLFSSL_SUCCESS) {
+        return WOLFSSL_FAILURE;
+    }
+
+    return WOLFSSL_SUCCESS;
+}
+
+
+#endif /* WOLFSSL_QUIC */
+#endif /* WOLFCRYPT_ONLY */
+

+ 25 - 1
src/ssl.c

@@ -2338,6 +2338,12 @@ int wolfSSL_write(WOLFSSL* ssl, const void* data, int sz)
     if (ssl == NULL || data == NULL || sz < 0)
         return BAD_FUNC_ARG;
 
+#ifdef WOLFSSL_QUIC
+    if (WOLFSSL_IS_QUIC(ssl)) {
+        WOLFSSL_MSG("SSL_write() on QUIC not allowed");
+        return BAD_FUNC_ARG;
+    }
+#endif
 #ifdef WOLFSSL_EARLY_DATA
     if (ssl->earlyData != no_early_data && (ret = wolfSSL_negotiate(ssl)) < 0) {
         ssl->error = ret;
@@ -2405,6 +2411,12 @@ static int wolfSSL_read_internal(WOLFSSL* ssl, void* data, int sz, int peek)
     if (ssl == NULL || data == NULL || sz < 0)
         return BAD_FUNC_ARG;
 
+#ifdef WOLFSSL_QUIC
+    if (WOLFSSL_IS_QUIC(ssl)) {
+        WOLFSSL_MSG("SSL_read() on QUIC not allowed");
+        return BAD_FUNC_ARG;
+    }
+#endif
 #if defined(WOLFSSL_ERROR_CODE_OPENSSL) && defined(OPENSSL_EXTRA)
     /* This additional logic is meant to simulate following openSSL behavior:
      * After bidirectional SSL_shutdown complete, SSL_read returns 0 and
@@ -9792,6 +9804,13 @@ int wolfSSL_SESSION_get_master_key_length(const WOLFSSL_SESSION* ses)
     return SECRET_LEN;
 }
 
+#ifdef WOLFSSL_EARLY_DATA
+unsigned int wolfSSL_SESSION_get_max_early_data(const WOLFSSL_SESSION *session)
+{
+    return session->maxEarlyDataSz;
+}
+#endif /* WOLFSSL_EARLY_DATA */
+
 #endif /* OPENSSL_EXTRA */
 
 typedef struct {
@@ -18253,6 +18272,10 @@ size_t wolfSSL_get_client_random(const WOLFSSL* ssl, unsigned char* out,
         InitX509(&ssl->peerCert, 0, ssl->heap);
 #endif
 
+#ifdef WOLFSSL_QUIC
+        wolfSSL_quic_clear(ssl);
+#endif
+
         return WOLFSSL_SUCCESS;
     }
 
@@ -33310,7 +33333,8 @@ int wolfSSL_sk_WOLFSSL_STRING_num(WOLF_STACK_OF(WOLFSSL_STRING)* strings)
 #endif /* WOLFSSL_NGINX || WOLFSSL_HAPROXY || OPENSSL_EXTRA || OPENSSL_ALL */
 
 #if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || \
-    defined(WOLFSSL_HAPROXY) || defined(HAVE_LIGHTY)
+    defined(WOLFSSL_HAPROXY) || defined(HAVE_LIGHTY) || \
+    defined(WOLFSSL_QUIC)
 #ifdef HAVE_ALPN
 void wolfSSL_get0_alpn_selected(const WOLFSSL *ssl, const unsigned char **data,
                                 unsigned int *len)

+ 148 - 3
src/tls.c

@@ -1226,8 +1226,8 @@ int TLS_hmac(WOLFSSL* ssl, byte* digest, const byte* in, word32 sz, int padSz,
  * from one peer to another.
  */
 
-/** Supports up to 64 flags. Increase as needed. */
-#define SEMAPHORE_SIZE 8
+/** Supports up to 72 flags. Increase as needed. */
+#define SEMAPHORE_SIZE 9
 
 /**
  * Converts the extension type (id) to an index in the semaphore.
@@ -1254,7 +1254,10 @@ static WC_INLINE word16 TLSX_ToSemaphore(word16 type)
 
         case TLSX_RENEGOTIATION_INFO: /* 0xFF01 */
             return 63;
-
+#ifdef WOLFSSL_QUIC
+        case TLSX_KEY_QUIC_TP_PARAMS_DRAFT: /* 0xffa5 */
+            return 64;
+#endif
         default:
             if (type > 62) {
                 /* This message SHOULD only happens during the adding of
@@ -10394,6 +10397,95 @@ int TLSX_EarlyData_Use(WOLFSSL* ssl, word32 maxSz)
 
 #endif
 
+/******************************************************************************/
+/* QUIC transport parameter extension                                         */
+/******************************************************************************/
+#ifdef WOLFSSL_QUIC
+
+static word16 TLSX_QuicTP_GetSize(TLSX* extension)
+{
+    const QuicTransportParam *tp = (QuicTransportParam*)extension->data;
+
+    return tp ? tp->len : 0;
+}
+
+int TLSX_QuicTP_Use(WOLFSSL* ssl, TLSX_Type ext_type, int is_response)
+{
+    int ret = 0;
+    TLSX* extension;
+
+    WOLFSSL_ENTER("TLSX_QuicTP_Use");
+    if (ssl->quic.transport_local == NULL) {
+        /* RFC9000, ch 7.3: "An endpoint MUST treat the absence of [...]
+         *     from either endpoint [...] as a connection error of type
+         *     TRANSPORT_PARAMETER_ERROR."
+         */
+        ret = QUIC_TP_MISSING_E;
+        goto cleanup;
+    }
+
+    extension = TLSX_Find(ssl->extensions, ext_type);
+    if (extension == NULL) {
+        ret = TLSX_Push(&ssl->extensions, ext_type, NULL, ssl->heap);
+        if (ret != 0)
+            goto cleanup;
+
+        extension = TLSX_Find(ssl->extensions, ext_type);
+        if (extension == NULL) {
+            ret = MEMORY_E;
+            goto cleanup;
+        }
+    }
+    extension->resp = is_response;
+    extension->data = (void*)QuicTransportParam_dup(ssl->quic.transport_local, ssl->heap);
+    if (!extension->data) {
+        ret = MEMORY_E;
+        goto cleanup;
+    }
+
+cleanup:
+    WOLFSSL_LEAVE("TLSX_QuicTP_Use", ret);
+    return ret;
+}
+
+static word16 TLSX_QuicTP_Write(QuicTransportParam *tp, byte* output)
+{
+    word16 len = 0;
+
+    WOLFSSL_ENTER("TLSX_QuicTP_Write");
+    if (tp && tp->len) {
+        XMEMCPY(output, tp->data, tp->len);
+        len = tp->len;
+    }
+    WOLFSSL_LEAVE("TLSX_QuicTP_Write", len);
+    return len;
+}
+
+static int TLSX_QuicTP_Parse(WOLFSSL *ssl, const byte *input, size_t len, int ext_type, int msgType)
+{
+    const QuicTransportParam *tp, **ptp;
+
+    (void)msgType;
+    tp = QuicTransportParam_new(input, len, ssl->heap);
+    if (!tp) {
+        return MEMORY_E;
+    }
+    ptp = (ext_type == TLSX_KEY_QUIC_TP_PARAMS_DRAFT) ?
+        &ssl->quic.transport_peer_draft : &ssl->quic.transport_peer;
+    if (*ptp) {
+        QTP_FREE(*ptp, ssl->heap);
+    }
+    *ptp = tp;
+    return 0;
+}
+
+#define QTP_GET_SIZE    TLSX_QuicTP_GetSize
+#define QTP_USE         TLSX_QuicTP_Use
+#define QTP_WRITE       TLSX_QuicTP_Write
+#define QTP_PARSE       TLSX_QuicTP_Parse
+
+#endif /* WOLFSSL_QUIC */
+
 /******************************************************************************/
 /* TLS Extensions Framework                                                   */
 /******************************************************************************/
@@ -10536,6 +10628,14 @@ void TLSX_FreeAll(TLSX* list, void* heap)
                 break;
 #endif
 
+    #ifdef WOLFSSL_QUIC
+            case TLSX_KEY_QUIC_TP_PARAMS:
+                FALL_THROUGH;
+            case TLSX_KEY_QUIC_TP_PARAMS_DRAFT:
+                QTP_FREE((QuicTransportParam*)extension->data, heap);
+                break;
+    #endif
+
             default:
                 break;
         }
@@ -10691,6 +10791,14 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType,
                 length += SRTP_GET_SIZE((TlsxSrtp*)extension->data);
                 break;
 #endif
+
+#ifdef WOLFSSL_QUIC
+            case TLSX_KEY_QUIC_TP_PARAMS:
+                FALL_THROUGH; /* followed by */
+            case TLSX_KEY_QUIC_TP_PARAMS_DRAFT:
+                length += QTP_GET_SIZE(extension);
+                break;
+#endif
             default:
                 break;
         }
@@ -10878,6 +10986,15 @@ static int TLSX_Write(TLSX* list, byte* output, byte* semaphore,
             case TLSX_USE_SRTP:
                 offset += SRTP_WRITE((TlsxSrtp*)extension->data, output+offset);
                 break;
+#endif
+#ifdef WOLFSSL_QUIC
+            case TLSX_KEY_QUIC_TP_PARAMS:
+                FALL_THROUGH;
+            case TLSX_KEY_QUIC_TP_PARAMS_DRAFT:
+                WOLFSSL_MSG("QUIC transport parameter to write");
+                offset += QTP_WRITE((QuicTransportParam*)extension->data,
+                                    output + offset);
+                break;
 #endif
             default:
                 break;
@@ -12536,6 +12653,34 @@ int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, byte msgType,
                 ret = SRTP_PARSE(ssl, input + offset, size, isRequest);
                 break;
 #endif
+#ifdef WOLFSSL_QUIC
+            case TLSX_KEY_QUIC_TP_PARAMS:
+                FALL_THROUGH;
+            case TLSX_KEY_QUIC_TP_PARAMS_DRAFT:
+                WOLFSSL_MSG("QUIC transport parameter received");
+            #ifdef WOLFSSL_DEBUG_TLS
+                WOLFSSL_BUFFER(input + offset, size);
+            #endif
+
+                if (IsAtLeastTLSv1_3(ssl->version) &&
+                        msgType != client_hello &&
+                        msgType != server_hello &&
+                        msgType != encrypted_extensions) {
+                    return EXT_NOT_ALLOWED;
+                }
+                else if (!IsAtLeastTLSv1_3(ssl->version) &&
+                         msgType == encrypted_extensions) {
+                    return EXT_NOT_ALLOWED;
+                }
+                else if (WOLFSSL_IS_QUIC(ssl)) {
+                    ret = QTP_PARSE(ssl, input + offset, size, type, msgType);
+                }
+                else {
+                    WOLFSSL_MSG("QUIC transport param TLS extension type, but no QUIC");
+                    return EXT_NOT_ALLOWED; /* be safe, this should not happen */
+                }
+                break;
+#endif /* WOLFSSL_QUIC */
             default:
                 WOLFSSL_MSG("Unknown TLS extension type");
         }

+ 108 - 32
src/tls13.c

@@ -1280,6 +1280,14 @@ int DeriveTls13Keys(WOLFSSL* ssl, int secret, int side, int store)
             break;
     }
 
+#ifdef WOLFSSL_QUIC
+    if (WOLFSSL_IS_QUIC(ssl)) {
+        ret = wolfSSL_quic_forward_secrets(ssl, secret, side);
+        if (ret != 0)
+            goto end;
+    }
+#endif /* WOLFSSL_QUIC */
+
     if (!store)
         goto end;
 
@@ -2662,6 +2670,16 @@ int BuildTls13Message(WOLFSSL* ssl, byte* output, int outSz, const byte* input,
 
         case BUILD_MSG_ENCRYPT:
         {
+#ifdef WOLFSSL_QUIC
+            if (WOLFSSL_IS_QUIC(ssl)) {
+                /* QUIC does not use encryption of the TLS Record Layer.
+                 * Return the original length + added headers
+                 * and restore it in the record header. */
+                AddTls13RecordHeader(output, inSz, type, ssl);
+                ret = args->headerSz + inSz;
+                goto exit_buildmsg;
+            }
+#endif
         #ifdef ATOMIC_USER
             if (ssl->ctx->MacEncryptCb) {
                 /* User Record Layer Callback handling */
@@ -3440,8 +3458,19 @@ int SendTls13ClientHello(WOLFSSL* ssl)
     /* Version | Random | Session Id | Cipher Suites | Compression */
     args->length = VERSION_SZ + RAN_LEN + ENUM_LEN + ssl->suites->suiteSz +
             SUITE_LEN + COMP_LEN + ENUM_LEN;
+#ifdef WOLFSSL_QUIC
+    if (WOLFSSL_IS_QUIC(ssl)) {
+        /* RFC 9001 ch. 8.4 sessionID in ClientHello MUST be 0 length */
+        ssl->session->sessionIDSz = 0;
+        ssl->options.tls13MiddleBoxCompat = 0;
+    }
+    else
+#endif
 #if defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT)
-    args->length += ID_LEN;
+    {
+        args->length += ID_LEN;
+        ssl->options.tls13MiddleBoxCompat = 1;
+    }
 #else
     if (ssl->session->sessionIDSz > 0)
         args->length += ssl->session->sessionIDSz;
@@ -3494,6 +3523,13 @@ int SendTls13ClientHello(WOLFSSL* ssl)
                                        (ret = TLSX_EarlyData_Use(ssl, 0)) < 0) {
         return ret;
     }
+#endif
+#ifdef WOLFSSL_QUIC
+    if (WOLFSSL_IS_QUIC(ssl) && IsAtLeastTLSv1_3(ssl->version)) {
+        ret = wolfSSL_quic_add_transport_extensions(ssl, client_hello);
+        if (ret != 0)
+            return ret;
+    }
 #endif
     /* Include length of TLS extensions. */
     ret = TLSX_GetRequestSize(ssl, client_hello, &args->length);
@@ -3551,13 +3587,17 @@ int SendTls13ClientHello(WOLFSSL* ssl)
     }
     else {
     #ifdef WOLFSSL_TLS13_MIDDLEBOX_COMPAT
-        args->output[args->idx++] = ID_LEN;
-        XMEMCPY(args->output + args->idx, ssl->arrays->clientRandom, ID_LEN);
-        args->idx += ID_LEN;
-    #else
-        /* TLS v1.3 does not use session id - 0 length. */
-        args->output[args->idx++] = 0;
+        if (ssl->options.tls13MiddleBoxCompat) {
+            args->output[args->idx++] = ID_LEN;
+            XMEMCPY(args->output + args->idx, ssl->arrays->clientRandom, ID_LEN);
+            args->idx += ID_LEN;
+        }
+        else
     #endif /* WOLFSSL_TLS13_MIDDLEBOX_COMPAT */
+        {
+            /* TLS v1.3 does not use session id - 0 length. */
+            args->output[args->idx++] = 0;
+        }
     }
 
 #ifdef WOLFSSL_DTLS13
@@ -3658,7 +3698,9 @@ int SendTls13ClientHello(WOLFSSL* ssl)
 
     case TLS_ASYNC_END:
 #ifdef WOLFSSL_EARLY_DATA_GROUP
-    if (ssl->earlyData == no_early_data)
+    /* QUIC needs to forward records at their encryption level
+     * and is therefore unable to group here */
+    if (ssl->earlyData == no_early_data || WOLFSSL_IS_QUIC(ssl))
 #endif
         ret = SendBuffered(ssl);
 
@@ -4097,27 +4139,39 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
     case TLS_ASYNC_FINALIZE:
     {
 #ifdef WOLFSSL_TLS13_MIDDLEBOX_COMPAT
-    if (args->sessIdSz == 0) {
-        WOLFSSL_MSG("args->sessIdSz == 0");
-        WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER);
-        return INVALID_PARAMETER;
-    }
-    if (ssl->session->sessionIDSz != 0) {
-        if (ssl->session->sessionIDSz != args->sessIdSz ||
-            XMEMCMP(ssl->session->sessionID, args->sessId,
-                args->sessIdSz) != 0) {
-            WOLFSSL_MSG("session id doesn't match");
+    if (ssl->options.tls13MiddleBoxCompat) {
+        if (args->sessIdSz == 0) {
+            WOLFSSL_MSG("args->sessIdSz == 0");
             WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER);
             return INVALID_PARAMETER;
         }
+        if (ssl->session->sessionIDSz != 0) {
+            if (ssl->session->sessionIDSz != args->sessIdSz ||
+                XMEMCMP(ssl->session->sessionID, args->sessId,
+                    args->sessIdSz) != 0) {
+                WOLFSSL_MSG("session id doesn't match");
+                WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER);
+                return INVALID_PARAMETER;
+            }
+        }
+        else if (XMEMCMP(ssl->arrays->clientRandom, args->sessId,
+                args->sessIdSz) != 0) {
+            WOLFSSL_MSG("session id doesn't match client random");
+            WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER);
+            return INVALID_PARAMETER;
     }
-    else if (XMEMCMP(ssl->arrays->clientRandom, args->sessId,
-            args->sessIdSz) != 0) {
-        WOLFSSL_MSG("session id doesn't match client random");
-        WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER);
-        return INVALID_PARAMETER;
+    else
+#endif /* WOLFSSL_TLS13_MIDDLEBOX_COMPAT */
+#ifdef WOLFSSL_QUIC
+    if (WOLFSSL_IS_QUIC(ssl)) {
+        if (args->sessIdSz != 0) {
+            WOLFSSL_MSG("args->sessIdSz != 0");
+            WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER);
+            return INVALID_PARAMETER;
+        }
     }
-#else
+    else
+#endif /* WOLFSSL_QUIC */
     if (args->sessIdSz != ssl->session->sessionIDSz || (args->sessIdSz > 0 &&
         XMEMCMP(ssl->session->sessionID, args->sessId, args->sessIdSz) != 0))
     {
@@ -4125,7 +4179,6 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
         WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER);
         return INVALID_PARAMETER;
     }
-#endif /* WOLFSSL_TLS13_MIDDLEBOX_COMPAT */
 
     ret = SetCipherSpecs(ssl);
     if (ret != 0)
@@ -5867,6 +5920,13 @@ static int SendTls13EncryptedExtensions(WOLFSSL* ssl)
     if ((ret = SetKeysSide(ssl, ENCRYPT_AND_DECRYPT_SIDE)) != 0)
         return ret;
 #endif
+#ifdef WOLFSSL_QUIC
+    if (IsAtLeastTLSv1_3(ssl->version) && WOLFSSL_IS_QUIC(ssl)) {
+        ret = wolfSSL_quic_add_transport_extensions(ssl, encrypted_extensions);
+        if (ret != 0)
+            return ret;
+    }
+#endif
 
 #ifdef WOLFSSL_DTLS13
     if (ssl->options.dtls) {
@@ -8201,6 +8261,7 @@ static int SendTls13Finished(WOLFSSL* ssl)
 
         }
 #endif /* WOLFSSL_DTLS13 */
+
     }
 
     if (ssl->options.side == WOLFSSL_CLIENT_END &&
@@ -9319,7 +9380,9 @@ static int SanityCheckTls13MsgReceived(WOLFSSL* ssl, byte type)
             #ifdef WOLFSSL_EARLY_DATA
                 if (ssl->earlyData == process_early_data &&
                     /* early data may be lost when using DTLS */
-                    !ssl->options.dtls) {
+                    !ssl->options.dtls
+                    /* QUIC does not use EndOfEarlyData records */
+                    && !WOLFSSL_IS_QUIC(ssl)) {
                     WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                     return OUT_OF_ORDER_E;
                 }
@@ -9681,6 +9744,15 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx,
                 ForceZero(ssl->arrays->preMasterSecret,
                     ssl->arrays->preMasterSz);
         #ifdef WOLFSSL_EARLY_DATA
+        #ifdef WOLFSSL_QUIC
+                if (WOLFSSL_IS_QUIC(ssl) && ssl->earlyData != no_early_data) {
+                    /* QUIC never sends/receives EndOfEarlyData, but having
+                     * early data means the last encrpytion keys had not been
+                     * set yet. */
+                    if ((ret = SetKeysSide(ssl, ENCRYPT_SIDE_ONLY)) != 0)
+                        return ret;
+                }
+        #endif
                 if ((ret = DeriveTls13Keys(ssl, traffic_key,
                                     ENCRYPT_AND_DECRYPT_SIDE,
                                     ssl->earlyData == no_early_data)) != 0) {
@@ -9992,7 +10064,7 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl)
     #ifdef WOLFSSL_EARLY_DATA
             if (ssl->earlyData != no_early_data) {
         #if defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT)
-                if (!ssl->options.dtls) {
+                if (!ssl->options.dtls && ssl->options.tls13MiddleBoxCompat) {
                     if ((ssl->error = SendChangeCipher(ssl)) != 0) {
                         WOLFSSL_ERROR(ssl->error);
                         return WOLFSSL_FATAL_ERROR;
@@ -10046,7 +10118,8 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl)
             if (ssl->options.serverState ==
                                           SERVER_HELLO_RETRY_REQUEST_COMPLETE) {
         #if defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT)
-                if (!ssl->options.dtls && !ssl->options.sentChangeCipher) {
+                if (!ssl->options.dtls && !ssl->options.sentChangeCipher
+                    && ssl->options.tls13MiddleBoxCompat) {
                     if ((ssl->error = SendChangeCipher(ssl)) != 0) {
                         WOLFSSL_ERROR(ssl->error);
                         return WOLFSSL_FATAL_ERROR;
@@ -10089,7 +10162,8 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl)
 
         case FIRST_REPLY_DONE:
         #ifdef WOLFSSL_EARLY_DATA
-            if (!ssl->options.dtls && ssl->earlyData != no_early_data) {
+            if (!ssl->options.dtls && ssl->earlyData != no_early_data
+                && !WOLFSSL_IS_QUIC(ssl)) {
                 if ((ssl->error = SendTls13EndOfEarlyData(ssl)) != 0) {
                     WOLFSSL_ERROR(ssl->error);
                     return WOLFSSL_FATAL_ERROR;
@@ -10104,7 +10178,8 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl)
 
         case FIRST_REPLY_FIRST:
         #if defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT)
-            if (!ssl->options.sentChangeCipher && !ssl->options.dtls) {
+            if (!ssl->options.sentChangeCipher && !ssl->options.dtls
+                && ssl->options.tls13MiddleBoxCompat) {
                 if ((ssl->error = SendChangeCipher(ssl)) != 0) {
                     WOLFSSL_ERROR(ssl->error);
                     return WOLFSSL_FATAL_ERROR;
@@ -11156,7 +11231,8 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl)
 
         case TLS13_ACCEPT_HELLO_RETRY_REQUEST_DONE :
     #ifdef WOLFSSL_TLS13_MIDDLEBOX_COMPAT
-            if (!ssl->options.dtls && ssl->options.serverState ==
+            if (!ssl->options.dtls && ssl->options.tls13MiddleBoxCompat
+                && ssl->options.serverState ==
                                           SERVER_HELLO_RETRY_REQUEST_COMPLETE) {
                 if ((ssl->error = SendChangeCipher(ssl)) != 0) {
                     WOLFSSL_ERROR(ssl->error);
@@ -11218,7 +11294,7 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl)
 
         case TLS13_SERVER_HELLO_SENT :
     #if defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT)
-            if (!ssl->options.dtls
+            if (!ssl->options.dtls && ssl->options.tls13MiddleBoxCompat
                           && !ssl->options.sentChangeCipher && !ssl->options.dtls) {
                 if ((ssl->error = SendChangeCipher(ssl)) != 0) {
                     WOLFSSL_ERROR(ssl->error);

+ 1 - 0
tests/include.am

@@ -12,6 +12,7 @@ tests_unit_test_SOURCES = \
                   tests/hash.c \
                   tests/w64wrapper.c \
                   tests/srp.c \
+                  tests/quic.c \
                   examples/client/client.c \
                   examples/server/server.c
 tests_unit_test_CFLAGS       = -DNO_MAIN_DRIVER $(AM_CFLAGS) $(WOLFSENTRY_INCLUDE)

+ 1393 - 0
tests/quic.c

@@ -0,0 +1,1393 @@
+/* quic.c QUIC unit tests
+ *
+ * Copyright (C) 2006-2022 wolfSSL Inc.
+ *
+ * This file is part of wolfSSL.
+ *
+ * wolfSSL is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+ */
+
+
+#ifdef HAVE_CONFIG_H
+    #include <config.h>
+#endif
+
+#include <wolfssl/wolfcrypt/settings.h>
+
+#include <tests/unit.h>
+
+#ifdef WOLFSSL_QUIC
+
+#include <wolfssl/ssl.h>
+#include <wolfssl/quic.h>
+#ifdef NO_INLINE
+    #include <wolfssl/wolfcrypt/misc.h>
+#else
+    #define WOLFSSL_MISC_INCLUDED
+    #include <wolfcrypt/src/misc.c>
+#endif
+#include <wolfssl/error-ssl.h>
+#include <wolfssl/internal.h>
+
+
+#define testingFmt "   %s:"
+#define resultFmt  " %s\n"
+static const char* passed = "passed";
+static const char* failed = "failed";
+
+typedef struct {
+    const char *name;
+    WOLFSSL_METHOD *method;
+    int is_server;
+} ctx_setups;
+
+static int dummy_set_encryption_secrets(WOLFSSL *ssl, WOLFSSL_ENCRYPTION_LEVEL level,
+                                         const uint8_t *read_secret,
+                                         const uint8_t *write_secret, size_t secret_len)
+{
+    (void)ssl;
+    printf("QUIC_set_encryption_secrets(level=%d, length=%d, rx=%s, tx=%s)\n",
+           level, (int)secret_len, read_secret? "yes" : "no", write_secret? "yes" : "no");
+    return 1;
+}
+
+static int dummy_add_handshake_data(WOLFSSL *ssl, WOLFSSL_ENCRYPTION_LEVEL level,
+                                    const uint8_t *data, size_t len)
+{
+    (void)ssl;
+    (void)data;
+    printf("QUIC_add_handshake_data(level=%d, length=%d)\n", level, (int)len);
+    return 1;
+}
+
+static int dummy_flush_flight(WOLFSSL *ssl)
+{
+    (void)ssl;
+    printf("QUIC_flush_flight()\n");
+    return 1;
+}
+
+static int dummy_send_alert(WOLFSSL *ssl, WOLFSSL_ENCRYPTION_LEVEL level, uint8_t err)
+{
+    (void)ssl;
+    printf("QUIC_send_alert(level=%d, err=%d)\n", level, err);
+    return 1;
+}
+
+static WOLFSSL_QUIC_METHOD dummy_method = {
+    dummy_set_encryption_secrets,
+    dummy_add_handshake_data,
+    dummy_flush_flight,
+    dummy_send_alert,
+};
+
+static WOLFSSL_QUIC_METHOD null_method = {
+    NULL, NULL, NULL, NULL
+};
+
+static int test_set_quic_method(void) {
+    WOLFSSL_CTX *ctx;
+    WOLFSSL *ssl;
+    int ret = 0, i;
+    const uint8_t *data;
+    size_t data_len;
+    ctx_setups valids[] = {
+        { "TLSv1.3 server", wolfTLSv1_3_server_method(), 1},
+        { "TLSv1.3 client", wolfTLSv1_3_client_method(), 0},
+    };
+    ctx_setups invalids[] = {
+        { "TLSv1.2 server", wolfTLSv1_2_server_method(), 1},
+        { "TLSv1.2 client", wolfTLSv1_2_client_method(), 0},
+        { "TLSv1.1 server", wolfTLSv1_1_server_method(), 1},
+        { "TLSv1.1 client", wolfTLSv1_1_client_method(), 0},
+    };
+
+    for (i = 0; i < (int)(sizeof(valids)/sizeof(valids[0])); ++i) {
+        AssertNotNull(ctx = wolfSSL_CTX_new(valids[i].method));
+        if (valids[i].is_server) {
+            AssertTrue(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
+                                                        WOLFSSL_FILETYPE_PEM));
+            AssertTrue(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
+                                                       WOLFSSL_FILETYPE_PEM));
+        }
+        /* ctx does not have quic enabled, so will SSL* derived from it */
+        AssertNotNull(ssl = wolfSSL_new(ctx));
+        AssertFalse(wolfSSL_is_quic(ssl));
+        /* Enable quic on the SSL* */
+        AssertFalse(wolfSSL_set_quic_method(ssl, &null_method) == WOLFSSL_SUCCESS);
+        AssertTrue(wolfSSL_set_quic_method(ssl, &dummy_method) == WOLFSSL_SUCCESS);
+        AssertTrue(wolfSSL_is_quic(ssl));
+        /* Check some default, initial behaviour */
+        AssertTrue(wolfSSL_set_quic_transport_params(ssl, NULL, 0) == WOLFSSL_SUCCESS);
+        wolfSSL_get_peer_quic_transport_params(ssl, &data, &data_len);
+        AssertNull(data);
+        AssertTrue(data_len == 0);
+        AssertTrue(wolfSSL_quic_read_level(ssl) == wolfssl_encryption_initial);
+        AssertTrue(wolfSSL_quic_write_level(ssl) == wolfssl_encryption_initial);
+        AssertTrue(wolfSSL_get_quic_transport_version(ssl) == 0);
+        wolfSSL_set_quic_transport_version(ssl, TLSX_KEY_QUIC_TP_PARAMS);
+        AssertTrue(wolfSSL_get_quic_transport_version(ssl) == TLSX_KEY_QUIC_TP_PARAMS);
+        wolfSSL_set_quic_use_legacy_codepoint(ssl, 1);
+        AssertTrue(wolfSSL_get_quic_transport_version(ssl) == TLSX_KEY_QUIC_TP_PARAMS_DRAFT);
+        wolfSSL_set_quic_use_legacy_codepoint(ssl, 0);
+        AssertTrue(wolfSSL_get_quic_transport_version(ssl) == TLSX_KEY_QUIC_TP_PARAMS);
+        /* max flight len during stages of handhshake, we us 16k initial and on
+         * app data, and during handshake allow larger for cert exchange. This is
+         * more advisory for the network code. ngtcp2 has its own ideas, for example.
+         */
+        data_len = wolfSSL_quic_max_handshake_flight_len(ssl, wolfssl_encryption_initial);
+        AssertTrue(data_len == 16*1024);
+        data_len = wolfSSL_quic_max_handshake_flight_len(ssl, wolfssl_encryption_early_data);
+        AssertTrue(data_len == 0);
+        data_len = wolfSSL_quic_max_handshake_flight_len(ssl, wolfssl_encryption_handshake);
+        AssertTrue(data_len >= 16*1024);
+        data_len = wolfSSL_quic_max_handshake_flight_len(ssl, wolfssl_encryption_application);
+        AssertTrue(data_len == 16*1024);
+        wolfSSL_free(ssl);
+        /* Enabled quic on the ctx */
+        AssertTrue(wolfSSL_CTX_set_quic_method(ctx, &dummy_method) == WOLFSSL_SUCCESS);
+        /* It will be enabled on the SSL* */
+        AssertNotNull(ssl = wolfSSL_new(ctx));
+        AssertTrue(wolfSSL_is_quic(ssl));
+        wolfSSL_free(ssl);
+
+        wolfSSL_CTX_free(ctx);
+    }
+
+    for (i = 0; i < (int)(sizeof(invalids)/sizeof(invalids[0])); ++i) {
+
+        AssertNotNull(ctx = wolfSSL_CTX_new(invalids[i].method));
+        AssertTrue(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
+                                                    WOLFSSL_FILETYPE_PEM));
+        AssertTrue(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
+                                                   WOLFSSL_FILETYPE_PEM));
+        AssertFalse(wolfSSL_CTX_set_quic_method(ctx, &dummy_method) == WOLFSSL_SUCCESS);
+        AssertNotNull(ssl = wolfSSL_new(ctx));
+        AssertFalse(wolfSSL_set_quic_method(ssl, &dummy_method) == WOLFSSL_SUCCESS);
+        AssertFalse(wolfSSL_is_quic(ssl));
+        /* even though not quic, this is the only level we can return */
+        AssertTrue(wolfSSL_quic_read_level(ssl) == wolfssl_encryption_initial);
+        AssertTrue(wolfSSL_quic_write_level(ssl) == wolfssl_encryption_initial);
+        wolfSSL_free(ssl);
+        wolfSSL_CTX_free(ctx);
+    }
+
+    printf("    test_set_quic_method: %s\n", (ret == 0)? passed : failed);
+    return ret;
+}
+
+static size_t fake_record(byte rtype, word32 rlen, uint8_t *rec)
+{
+    rec[0] = (uint8_t)rtype;
+    c32to24(rlen, rec+1);
+    return rlen + 4;
+}
+
+static size_t shift_record(uint8_t *rec, size_t len, size_t written)
+{
+    len -= written;
+    XMEMMOVE(rec, rec+written, len);
+    return len;
+}
+
+static void dump_buffer(const char *name, const byte *p, size_t len, int indent)
+{
+    size_t i = 0;
+
+    printf("%s[%d] = {", name, (int)len);
+    while((p != NULL) && (i < len)) {
+        if((i % 0x10) == 0) {
+            printf("\n");
+            printf("%*s  %04X - ", indent, " ", (int)(i / 0x10));
+        }
+        else if((i % 0x08) == 0) {
+            printf("  ");
+        }
+        printf("%02X ", p[i]);
+        i++;
+    }
+    printf("\n%*s};\n", indent, " ");
+}
+
+static void dump_ssl_buffers(WOLFSSL *ssl, FILE *fp)
+{
+    QuicRecord *qr = ssl->quic.input_head;
+
+    fprintf(fp, "SSL quic data buffered: \n");
+    while (qr) {
+        fprintf(fp, "  - %d-%d/%d (cap %d, level=%d)\n",
+                qr->start, qr->end, qr->len, qr->capacity, qr->level);
+        qr = qr->next;
+    }
+    if ((qr = ssl->quic.scratch)) {
+        fprintf(fp, "  scratch: %d-%d/%d (cap %d, level=%d)\n",
+                qr->start, qr->end, qr->len, qr->capacity, qr->level);
+    }
+    else {
+        fprintf(fp, "  scratch: -\n");
+    }
+}
+
+static int provide_data(WOLFSSL *ssl, WOLFSSL_ENCRYPTION_LEVEL level,
+                        const uint8_t *data, size_t len, int excpect_fail)
+{
+    int ret;
+
+    ret = (wolfSSL_provide_quic_data(ssl, level, data, len) == WOLFSSL_SUCCESS);
+    if (!!ret != !excpect_fail) {
+        dump_ssl_buffers(ssl, stdout);
+        return 0;
+    }
+    return 1;
+}
+
+static int test_provide_quic_data(void) {
+    WOLFSSL_CTX *ctx;
+    WOLFSSL *ssl;
+    uint8_t lbuffer[16*1024];
+    size_t len;
+    int ret = 0;
+
+    AssertNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
+    AssertTrue(wolfSSL_CTX_set_quic_method(ctx, &dummy_method) == WOLFSSL_SUCCESS);
+    /* provide_quic_data() feeds CRYPTO packets inside a QUIC Frame into
+    * the TLSv1.3 state machine.
+     * The data fed is not the QUIC frame, but the TLS record inside it.
+     * This may be called several times before SSL_do_handshake() is invoked
+     * to process them.
+     * During buffering this data, the code checks that:
+     * - encryption level only ever increases for subsequent TLS records
+     * - a TLS record is received complete before the encryption level increases
+     */
+    AssertNotNull(ssl = wolfSSL_new(ctx));
+    len = fake_record(1, 100, lbuffer);
+    AssertTrue(provide_data(ssl, wolfssl_encryption_initial, lbuffer, len, 0));
+    len = fake_record(2, 1523, lbuffer);
+    AssertTrue(provide_data(ssl, wolfssl_encryption_handshake, lbuffer, len, 0));
+    len = fake_record(2, 1, lbuffer);
+    len += fake_record(3, 190, lbuffer+len);
+    AssertTrue(provide_data(ssl, wolfssl_encryption_handshake, lbuffer, len, 0));
+    len = fake_record(5, 2049, lbuffer);
+    AssertTrue(provide_data(ssl, wolfssl_encryption_application, lbuffer, len, 0));
+    /* adding another record with decreased level must fail */
+    len = fake_record(1, 100, lbuffer);
+    AssertTrue(provide_data(ssl, wolfssl_encryption_initial, lbuffer, len, 1));
+    wolfSSL_free(ssl);
+
+    AssertNotNull(ssl = wolfSSL_new(ctx));
+    len = fake_record(1, 100, lbuffer);
+    AssertTrue(provide_data(ssl, wolfssl_encryption_initial, lbuffer, 24, 0));
+    len = shift_record(lbuffer, len, 24);
+    len += fake_record(2, 4000, lbuffer+len);
+    AssertTrue(provide_data(ssl, wolfssl_encryption_initial, lbuffer, len - 99, 0));
+    len = shift_record(lbuffer, len, len - 99);
+    len += fake_record(5, 2049, lbuffer+len);
+    AssertTrue(provide_data(ssl, wolfssl_encryption_initial, lbuffer, len, 0));
+    /* should be recognized as complete and level increase needs to be accepted */
+    len = fake_record(2, 1, lbuffer);
+    len += fake_record(3, 190, lbuffer+len);
+    AssertTrue(provide_data(ssl, wolfssl_encryption_handshake, lbuffer, len - 10, 0));
+    len = shift_record(lbuffer, len, len - 10);
+    /* Change level with incomplete record in lbuffer, needs to fail */
+    len += fake_record(5, 8102, lbuffer+len);
+    AssertTrue(provide_data(ssl, wolfssl_encryption_application, lbuffer, len - 10, 1));
+    wolfSSL_free(ssl);
+
+    wolfSSL_CTX_free(ctx);
+
+    printf("    test_provide_quic_data: %s\n", (ret == 0)? passed : failed);
+    return 0;
+}
+
+
+static int test_quic_crypt(void) {
+    WOLFSSL_CTX *ctx;
+    WOLFSSL *ssl;
+    const WOLFSSL_EVP_CIPHER *aead_cipher;
+    int ret = 0;
+
+    AssertNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
+    AssertTrue(wolfSSL_CTX_set_quic_method(ctx, &dummy_method) == WOLFSSL_SUCCESS);
+    AssertNotNull(ssl = wolfSSL_new(ctx));
+
+    /* don't have an AEAD cipher selected before start */
+    AssertTrue(wolfSSL_CIPHER_get_id(wolfSSL_get_current_cipher(ssl)) == 0);
+    AssertNotNull(aead_cipher = wolfSSL_EVP_aes_128_gcm());
+    AssertTrue(wolfSSL_quic_aead_is_gcm(aead_cipher) != 0);
+    AssertTrue(wolfSSL_quic_aead_is_ccm(aead_cipher) == 0);
+    AssertTrue(wolfSSL_quic_aead_is_chacha20(aead_cipher) == 0);
+
+    if (1) {
+        /* check that our enc-/decrypt support in quic rount-trips */
+        static const uint8_t key[16] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+                                        0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
+        static const uint8_t aad[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
+        static const uint8_t iv[] = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
+        static const uint8_t plaintext[] = "hello world\nhello world\nhello world\nhello world\nhello world\nhello world\nhello world\n";
+        static const uint8_t expected[] = {0xd3, 0xa8, 0x1d, 0x96, 0x4c, 0x9b, 0x02, 0xd7, 0x9a, 0xb0, 0x41, 0x07, 0x4c, 0x8c, 0xe2,
+                                           0xe0, 0x2e, 0x83, 0x54, 0x52, 0x45, 0xcb, 0xd4, 0x68, 0xc8, 0x43, 0x45, 0xca, 0x91, 0xfb,
+                                           0xa3, 0x7a, 0x67, 0xed, 0xe8, 0xd7, 0x5e, 0xe2, 0x33, 0xd1, 0x3e, 0xbf, 0x50, 0xc2, 0x4b,
+                                           0x86, 0x83, 0x55, 0x11, 0xbb, 0x17, 0x4f, 0xf5, 0x78, 0xb8, 0x65, 0xeb, 0x9a, 0x2b, 0x8f,
+                                           0x77, 0x08, 0xa9, 0x60, 0x17, 0x73, 0xc5, 0x07, 0xf3, 0x04, 0xc9, 0x3f, 0x67, 0x4d, 0x12,
+                                           0xa1, 0x02, 0x93, 0xc2, 0x3c, 0xd3, 0xf8, 0x59, 0x33, 0xd5, 0x01, 0xc3, 0xbb, 0xaa, 0xe6,
+                                           0x3f, 0xbb, 0x23, 0x66, 0x94, 0x26, 0x28, 0x43, 0xa5, 0xfd, 0x2f};
+        WOLFSSL_EVP_CIPHER_CTX *enc_ctx, *dec_ctx;
+        uint8_t *encrypted, *decrypted;
+        size_t tag_len, enc_len, dec_len;
+
+        AssertTrue((tag_len = wolfSSL_quic_get_aead_tag_len(aead_cipher)) == 16);
+        dec_len = sizeof(plaintext);
+        enc_len = dec_len + tag_len;
+        encrypted = (uint8_t*)XMALLOC(enc_len, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        AssertNotNull(encrypted);
+        decrypted = (uint8_t*)XMALLOC(dec_len, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        AssertNotNull(decrypted);
+
+        AssertNotNull(enc_ctx = wolfSSL_quic_crypt_new(aead_cipher, key, iv, 1));
+        AssertTrue(wolfSSL_quic_aead_encrypt(encrypted, enc_ctx,
+                                             plaintext, sizeof(plaintext),
+                                             NULL, aad, sizeof(aad)) == WOLFSSL_SUCCESS);
+        AssertTrue(memcmp(expected, encrypted, dec_len) == 0);
+        AssertTrue(memcmp(expected+dec_len, encrypted+dec_len, tag_len) == 0);
+
+        AssertNotNull(dec_ctx = wolfSSL_quic_crypt_new(aead_cipher, key, iv, 0));
+        AssertTrue(wolfSSL_quic_aead_decrypt(decrypted, dec_ctx,
+                                             encrypted, enc_len,
+                                             NULL, aad, sizeof(aad)) == WOLFSSL_SUCCESS);
+        AssertTrue(memcmp(plaintext, decrypted, dec_len) == 0);
+
+        XFREE(encrypted, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        XFREE(decrypted, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        wolfSSL_EVP_CIPHER_CTX_free(enc_ctx);
+        wolfSSL_EVP_CIPHER_CTX_free(dec_ctx);
+    }
+
+    wolfSSL_free(ssl);
+    wolfSSL_CTX_free(ctx);
+
+    printf("    test_quic_crypt: %s\n", (ret == 0)? passed : failed);
+    return ret;
+}
+
+typedef struct OutputBuffer {
+    byte data[64*1024];
+    size_t len;
+    WOLFSSL_ENCRYPTION_LEVEL level;
+    struct OutputBuffer *next;
+} OutputBuffer;
+
+typedef struct {
+    const char *name;
+    WOLFSSL *ssl;
+    OutputBuffer output;
+    byte rx_secret[4][1024];
+    size_t rx_secret_len[4];
+    byte tx_secret[4][1024];
+    size_t tx_secret_len[4];
+    int handshake_done;
+    int alert_level;
+    int alert;
+    int flushed;
+    int verbose;
+    byte ticket[16*1024];
+    word32 ticket_len;
+    byte session[16*1024];
+    word32 session_len;
+} QuicTestContext;
+
+static int ctx_set_encryption_secrets(WOLFSSL *ssl, WOLFSSL_ENCRYPTION_LEVEL level,
+                                      const uint8_t *read_secret,
+                                      const uint8_t *write_secret, size_t secret_len);
+static int ctx_add_handshake_data(WOLFSSL *ssl, WOLFSSL_ENCRYPTION_LEVEL level,
+                                  const uint8_t *data, size_t len);
+static int ctx_flush_flight(WOLFSSL *ssl);
+static int ctx_send_alert(WOLFSSL *ssl, WOLFSSL_ENCRYPTION_LEVEL level, uint8_t alert);
+#ifdef HAVE_SESSION_TICKET
+static int ctx_session_ticket_cb(WOLFSSL* ssl,
+                                 const unsigned char* ticket, int ticketSz,
+                                 void* cb_ctx);
+#endif
+
+static WOLFSSL_QUIC_METHOD ctx_method = {
+    ctx_set_encryption_secrets,
+    ctx_add_handshake_data,
+    ctx_flush_flight,
+    ctx_send_alert,
+};
+
+static void QuicTestContext_init(QuicTestContext *tctx, WOLFSSL_CTX *ctx,
+                                 const char *name, int verbose)
+{
+    static const byte tp_params_c[] = {0, 1, 2, 3, 4, 5, 6, 7};
+    static const byte tp_params_s[] = {7, 6, 5, 4, 3, 2, 1, 0, 1};
+
+    AssertNotNull(tctx);
+    memset(tctx, 0, sizeof(*tctx));
+    tctx->name = name;
+    AssertNotNull((tctx->ssl = wolfSSL_new(ctx)));
+    tctx->verbose = verbose;
+    wolfSSL_set_app_data(tctx->ssl, tctx);
+    AssertTrue(wolfSSL_set_quic_method(tctx->ssl, &ctx_method) == WOLFSSL_SUCCESS);
+    wolfSSL_set_verify(tctx->ssl, SSL_VERIFY_NONE, 0);
+#ifdef HAVE_SESSION_TICKET
+    wolfSSL_UseSessionTicket(tctx->ssl);
+    wolfSSL_set_SessionTicket_cb(tctx->ssl, ctx_session_ticket_cb, NULL);
+#endif
+    if (wolfSSL_is_server(tctx->ssl)) {
+        wolfSSL_set_quic_transport_version(tctx->ssl, 0);
+        wolfSSL_set_quic_transport_params(tctx->ssl, tp_params_s, sizeof(tp_params_s));
+    }
+    else {
+        wolfSSL_set_quic_transport_version(tctx->ssl, 0);
+        wolfSSL_set_quic_transport_params(tctx->ssl, tp_params_c, sizeof(tp_params_c));
+    }
+}
+
+static void QuicTestContext_free(QuicTestContext *tctx)
+{
+    OutputBuffer *out, *n;
+
+    if (tctx->ssl) {
+        wolfSSL_free(tctx->ssl);
+        tctx->ssl = NULL;
+    }
+    out = tctx->output.next;
+    while (out) {
+        n = out->next;
+        free(out);
+        out = n;
+    }
+}
+
+static int ctx_set_encryption_secrets(WOLFSSL *ssl, WOLFSSL_ENCRYPTION_LEVEL level,
+                                      const uint8_t *read_secret,
+                                      const uint8_t *write_secret, size_t secret_len)
+{
+    QuicTestContext *ctx = (QuicTestContext*)wolfSSL_get_app_data(ssl);
+
+    AssertNotNull(ctx);
+    AssertTrue(secret_len <= sizeof(ctx->rx_secret[0]));
+    if (read_secret) {
+        memcpy(ctx->rx_secret[level], read_secret, secret_len);
+        ctx->rx_secret_len[level] = secret_len;
+    }
+    if (write_secret) {
+        memcpy(ctx->tx_secret[level], write_secret, secret_len);
+        ctx->tx_secret_len[level] = secret_len;
+    }
+    AssertNotNull(ctx);
+    return 1;
+}
+
+static int ctx_add_handshake_data(WOLFSSL *ssl, WOLFSSL_ENCRYPTION_LEVEL level,
+                                  const uint8_t *data, size_t len)
+{
+    QuicTestContext *ctx = (QuicTestContext*)wolfSSL_get_app_data(ssl);
+    OutputBuffer *out;
+
+    AssertNotNull(ctx);
+    out = &ctx->output;
+    while (out->next) {
+        out = out->next;
+    }
+    if (out->level != level) {
+        if (out->len > 0) {
+            out->next = (OutputBuffer*)calloc(1, sizeof(OutputBuffer));
+            out = out->next;
+            AssertNotNull(out);
+        }
+        out->level = level;
+    }
+    if (ctx->verbose) {
+        printf("[%s] add_handshake[enc_level=%d]: %d bytes\n", ctx->name, level, (int)len);
+        /* dump_buffer("add", data, len, 0); */
+    }
+    if (len > 0) {
+        AssertTrue(out->len + len < sizeof(out->data));
+        memcpy(out->data + out->len, data, len);
+        out->len += len;
+    }
+    return 1;
+}
+
+static int ctx_flush_flight(WOLFSSL *ssl)
+{
+    QuicTestContext *ctx = (QuicTestContext*)wolfSSL_get_app_data(ssl);
+
+    AssertNotNull(ctx);
+    ctx->flushed = 1;
+    return 1;
+}
+
+static int ctx_send_alert(WOLFSSL *ssl, WOLFSSL_ENCRYPTION_LEVEL level, uint8_t err)
+{
+    QuicTestContext *ctx = (QuicTestContext*)wolfSSL_get_app_data(ssl);
+
+    if (ctx->verbose) {
+        printf("[%s] send_alert: level=%d, err=%d\n", ctx->name, level, err);
+    }
+    AssertNotNull(ctx);
+    ctx->alert_level = level;
+    ctx->alert = alert;
+    return 1;
+}
+
+#ifdef HAVE_SESSION_TICKET
+static int ctx_session_ticket_cb(WOLFSSL* ssl,
+                                 const unsigned char* ticket, int ticketSz,
+                                 void* cb_ctx)
+{
+    QuicTestContext *ctx = (QuicTestContext*)wolfSSL_get_app_data(ssl);
+
+    (void)cb_ctx;
+    if (ticketSz < 0 || (size_t)ticketSz > sizeof(ctx->ticket)) {
+        printf("SESSION TICKET callback: ticket given is too large: %d bytes\n", ticketSz);
+        return 1;
+    }
+    memset(ctx->ticket, 0, sizeof(ctx->ticket));
+    ctx->ticket_len = (word32)ticketSz;
+    memcpy(ctx->ticket, ticket, ticketSz);
+    if (ctx->verbose) {
+        printf("Session Ticket[%s]: ", ctx->name);
+        dump_buffer("", ticket, ticketSz, 4);
+    }
+    return 0;
+}
+#endif
+
+static void ctx_dump_output(QuicTestContext *ctx)
+{
+    dump_buffer("Output", ctx->output.data, ctx->output.len, 0);
+}
+
+static void check_handshake_record(const byte *data, size_t data_len, int *ptype, size_t *prlen)
+{
+    word32 rlen;
+    AssertTrue(data_len >= HANDSHAKE_HEADER_SZ);
+    *ptype = data[0];
+    c24to32(&data[1], &rlen);
+    *prlen = rlen + HANDSHAKE_HEADER_SZ;
+}
+
+static void ext_dump(const byte *data, size_t data_len, int indent)
+{
+    size_t idx = 0;
+    word16 len16, etype, i;
+
+    printf("%*sextensions:\n", indent, " ");
+    while (idx < data_len) {
+        ato16(&data[idx], &etype); /* extension type */
+        ato16(&data[idx+2], &len16); /* extension length */
+        printf("  extension: %04x [", etype);
+        for (i = 0; i < len16; ++i) {
+            printf("%s0x%02x", (i? ", ": ""), data[idx+4+i]);
+        }
+        printf("]\n");
+        idx += 2 + 2 + len16;
+    }
+}
+
+static const byte *ext_find(const byte *data, size_t data_len, int ext_type)
+{
+    size_t idx = 0;
+    word16 len16, etype;
+
+    while (idx < data_len) {
+        ato16(&data[idx], &etype); /* extension type */
+        if (etype == ext_type) {
+            return data + idx;
+        }
+        ato16(&data[idx+2], &len16); /* extension length */
+        idx += 2 + 2 + len16;
+    }
+    return NULL;
+}
+
+static int ext_has(const byte *data, size_t data_len, int ext_type)
+{
+    return ext_find(data, data_len,ext_type) != NULL;
+}
+
+static void ext_equals(const byte *data, size_t data_len, int ext_type,
+                       const byte *exp_data, size_t exp_len)
+{
+    const byte *ext;
+    word16 len16;
+
+    AssertNotNull(ext = ext_find(data, data_len, ext_type));
+    ato16(&ext[2], &len16);
+    AssertTrue(len16 == exp_len);
+    AssertTrue(memcmp(ext + 4, exp_data, exp_len) == 0);
+}
+
+static void check_quic_client_hello(const byte *data, size_t data_len, int verbose, int indent)
+{
+    size_t idx;
+    word16 len16;
+    const byte *exts;
+    size_t exts_len, rec_len;
+    int rec_type;
+    static byte ext_sup_version[3] = {0x02, 0x03, 0x04};
+
+    check_handshake_record(data, data_len, &rec_type, &rec_len);
+    AssertIntEQ(rec_type, client_hello);
+    idx = HANDSHAKE_HEADER_SZ;
+    /* the client hello arrives alone */
+    AssertIntEQ(rec_len, data_len);
+    AssertTrue(data[idx++] == SSLv3_MAJOR);
+    AssertTrue(data[idx++] == TLSv1_2_MINOR);
+    idx += 32; /* 32 bytes RANDOM */
+    AssertIntEQ(data[idx], 0);  /* session id length MUST be 0, RFC9001 ch. 8.4 */
+    idx += 1 + data[idx];
+    ato16(&data[idx], &len16); /* ciphers length */
+    AssertTrue(len16 > 0);
+    idx += 2 + len16;
+    AssertTrue(data[idx] == 1);   /* compressions */
+    AssertTrue(data[idx+1] == 0);   /* no compression */
+    idx += 2;
+    ato16(&data[idx], &len16); /* extensions length */
+    AssertTrue(len16 > 0);
+    exts_len = len16;
+    idx += 2;
+    exts = &data[idx];
+    idx += exts_len;
+    AssertTrue(idx <= rec_len); /* should fit */
+    for (; idx < rec_len; ++idx) {
+        AssertTrue(data[idx] == 0); /* padding */
+    }
+    ext_equals(exts, exts_len, TLSX_SUPPORTED_VERSIONS,
+               ext_sup_version, sizeof(ext_sup_version));
+    if (verbose) {
+        ext_dump(exts, exts_len, indent);
+        dump_buffer("", data, data_len, indent);
+    }
+}
+
+static void check_quic_client_hello_tp(OutputBuffer *out, int tp_v1, int tp_draft)
+{
+    size_t idx;
+    word16 len16;
+    const byte *exts;
+    size_t exts_len, rec_len;
+    int rec_type;
+
+    check_handshake_record(out->data, out->len, &rec_type, &rec_len);
+    AssertIntEQ(rec_type, client_hello);
+    idx = HANDSHAKE_HEADER_SZ;
+    idx += 2; /* old version */
+    idx += 32; /* 32 bytes RANDOM */
+    idx += 1 + out->data[idx]; /* session id */
+    ato16(&out->data[idx], &len16); /* ciphers length */
+    idx += 2 + len16;
+    idx += 2; /* compression */
+    ato16(&out->data[idx], &len16); /* extensions length */
+    AssertTrue(len16 > 0);
+    exts_len = len16;
+    idx += 2;
+    exts = &out->data[idx];
+
+    AssertTrue(!ext_has(exts, exts_len, TLSX_KEY_QUIC_TP_PARAMS) == !tp_v1);
+    AssertTrue(!ext_has(exts, exts_len, TLSX_KEY_QUIC_TP_PARAMS_DRAFT) == !tp_draft);
+}
+
+static void check_secrets(QuicTestContext *ctx, WOLFSSL_ENCRYPTION_LEVEL level, size_t rx_len, size_t tx_len)
+{
+    int idx = (int)level;
+    AssertTrue(idx < 4);
+    AssertIntEQ(ctx->rx_secret_len[idx], rx_len);
+    AssertIntEQ(ctx->tx_secret_len[idx], tx_len);
+}
+
+static void assert_secrets_EQ(QuicTestContext *ctx1, QuicTestContext *ctx2,
+                              WOLFSSL_ENCRYPTION_LEVEL level)
+{
+    int idx = (int)level;
+    /* rx secrets are the other ones tx secrets */
+    AssertIntEQ(ctx1->rx_secret_len[idx], ctx2->tx_secret_len[idx]);
+    AssertIntEQ(ctx1->tx_secret_len[idx], ctx2->rx_secret_len[idx]);
+    AssertIntEQ(memcmp(ctx1->rx_secret[idx], ctx2->tx_secret[idx], ctx1->rx_secret_len[idx]), 0);
+    AssertIntEQ(memcmp(ctx1->tx_secret[idx], ctx2->rx_secret[idx], ctx1->tx_secret_len[idx]), 0);
+}
+
+static void check_ee(const byte *data, size_t data_len, int verbose, int indent)
+{
+    size_t rec_len, exts_len, idx;
+    word16 len16;
+    const byte *exts;
+    int rec_type;
+
+    check_handshake_record(data, data_len, &rec_type, &rec_len);
+    AssertIntEQ(rec_type, encrypted_extensions);
+    idx = HANDSHAKE_HEADER_SZ;
+    ato16(&data[idx], &len16); /* extensions length */
+    AssertTrue(len16 > 0);
+    exts_len = len16;
+    idx += 2;
+    exts = &data[idx];
+    if (verbose) {
+        ext_dump(exts, exts_len, indent);
+        dump_buffer("", data, data_len, indent);
+    }
+}
+
+static void check_quic_server_hello(const byte *data, size_t data_len, int verbose, int indent)
+{
+    size_t idx;
+    word16 len16, cipher;
+    const byte *exts;
+    size_t exts_len, rec_len;
+    static byte ext_sup_version[2] = {0x03, 0x04};
+    int rec_type;
+
+    check_handshake_record(data, data_len, &rec_type, &rec_len);
+    AssertIntEQ(rec_type, server_hello);
+    idx = HANDSHAKE_HEADER_SZ;
+    AssertTrue(data[idx++] == SSLv3_MAJOR);
+    AssertTrue(data[idx++] == TLSv1_2_MINOR);
+    idx += 32; /* 32 bytes RANDOM */
+    /* AssertIntEQ(data[idx], 0);  session id of len 0 */
+    idx += 1 + data[idx];
+    ato16(&data[idx], &cipher); /* cipher selected */
+    AssertTrue(cipher != 0);
+    idx += 2;
+    AssertTrue(data[idx] == 0);   /* null compression */
+    idx += 1;
+    ato16(&data[idx], &len16); /* extensions length */
+    AssertTrue(len16 > 0);
+    exts_len = len16;
+    idx += 2;
+    exts = &data[idx];
+    idx += exts_len;
+    AssertTrue(idx <= rec_len); /* should fit */
+    for (; idx < rec_len; ++idx) {
+        AssertTrue(data[idx] == 0); /* padding */
+    }
+    if (verbose) {
+        ext_dump(exts, exts_len, indent);
+        dump_buffer("", data, rec_len, indent);
+    }
+    ext_equals(exts, exts_len, TLSX_SUPPORTED_VERSIONS,
+               ext_sup_version, sizeof(ext_sup_version));
+}
+
+static void check_crypto_rec(const byte *data, size_t data_len, int verbose, int indent)
+{
+    size_t rec_len;
+    int rec_type;
+
+    check_handshake_record(data, data_len, &rec_type, &rec_len);
+    if (verbose) {
+        dump_buffer("", data, rec_len, indent);
+    }
+}
+
+static void check_crypto_records(QuicTestContext *from, OutputBuffer *out, int indent, char *rec_log)
+{
+    const byte *data = out->data;
+    size_t data_len = out->len;
+    size_t rec_len;
+    int rec_type;
+    const char *rec_name;
+    char lbuffer[128];
+    void (*check_rec) (const byte *d, size_t l, int v, int indent);
+
+    while (data_len > 0) {
+        check_handshake_record(data, data_len, &rec_type, &rec_len);
+        if (rec_len > data_len) {
+            printf("%*sINCOMPLETE CRYPTO?: ", indent, " ");
+            dump_buffer("", data, data_len, indent);
+        }
+        AssertTrue(rec_len <= data_len);
+        check_rec = check_crypto_rec;
+        switch (rec_type) {
+            case client_hello:
+                rec_name = "ClientHello";
+                check_rec = check_quic_client_hello;
+                break;
+            case server_hello:
+                rec_name = "ServerHello";
+                check_rec = check_quic_server_hello;
+                break;
+            case session_ticket:
+                rec_name = "SessionTicket";
+                break;
+            case encrypted_extensions:
+                rec_name = "EncryptedExtension";
+                check_rec = check_ee;
+                break;
+            case certificate:
+                rec_name = "Certificate";
+                break;
+            case certificate_verify:
+                rec_name = "CertificateVerify";
+                break;
+            case finished:
+                rec_name = "Finished";
+                break;
+            default:
+                sprintf(lbuffer, "%d", rec_type);
+                rec_name = lbuffer;
+                break;
+        }
+
+        if (rec_log) {
+            if (*rec_log) strcat(rec_log, ":");
+            strcat(rec_log, rec_name);
+        }
+        if (from->verbose) printf("%*sCRYPTO[%s]: ", indent, " ", rec_name);
+        check_rec(data, rec_len, from->verbose, indent);
+        if (from->verbose) printf("\n");
+        data += rec_len;
+        data_len -= rec_len;
+    }
+}
+
+static void QuicTestContext_forward(QuicTestContext *from, QuicTestContext *to, char *rec_log)
+{
+    int ret;
+    OutputBuffer *out, *old;
+
+    out = &from->output;
+    while (out->len > 0) {
+        if (from->verbose) {
+            printf("[%s -> %s] forward %d bytes at level %d\n",
+                   from->name, to->name, (int)out->len, out->level);
+        }
+        if (out->level == wolfssl_encryption_early_data) {
+            if (from->verbose) dump_buffer("EarlyData", out->data, out->len, 4);
+        }
+        else {
+            check_crypto_records(from, out, 4, rec_log);
+        }
+        ret = wolfSSL_provide_quic_data(to->ssl, out->level, out->data, out->len);
+        out->len = 0;
+        AssertIntEQ(ret, WOLFSSL_SUCCESS);
+        if (out->next) {
+            old = out->next;
+            memcpy(out, out->next, sizeof(*out));
+            free(old);
+        }
+    }
+}
+
+typedef struct {
+    QuicTestContext *client;
+    QuicTestContext *server;
+    int started;
+    int verbose;
+    char rec_log[16*1024];
+    int sent_early_data;
+    int accept_early_data;
+    char early_data[16*1024];
+    size_t early_data_len;
+} QuicConversation;
+
+static void QuicConversation_init(QuicConversation *conv,
+                                  QuicTestContext *tclient, QuicTestContext *tserver)
+{
+    memset(conv, 0, sizeof(*conv));
+    conv->client = tclient;
+    conv->server = tserver;
+    conv->verbose = tclient->verbose && tserver->verbose;
+}
+
+static int QuicConversation_start(QuicConversation *conv, const byte *data,
+                                  size_t data_len, size_t *pwritten)
+{
+    int ret;
+
+    AssertFalse(conv->started);
+
+    if (conv->verbose) {
+        printf("[%s <-> %s] starting\n", conv->client->name, conv->server->name);
+    }
+    if (data && data_len > 0) {
+#ifdef WOLFSSL_EARLY_DATA
+        int written;
+        ret = wolfSSL_write_early_data(conv->client->ssl, data, (int)data_len, &written);
+        if (ret < 0) {
+            int err = wolfSSL_get_error(conv->client->ssl, ret);
+            char lbuffer[1024];
+            printf("EARLY DATA ret = %d, error = %d, %s\n", ret, err, wolfSSL_ERR_error_string(err, lbuffer));
+            AssertTrue(0);
+        }
+        *pwritten = (size_t)written;
+        conv->sent_early_data = 1;
+#else
+        fprintf(stderr, "Cannot send EARLY DATA without feature enabled!\n");
+        AssertTrue(0);
+#endif
+    }
+    else {
+        ret = wolfSSL_connect(conv->client->ssl);
+        if (ret != WOLFSSL_SUCCESS) {
+            AssertIntEQ(wolfSSL_get_error(conv->client->ssl, 0), SSL_ERROR_WANT_READ);
+        }
+        if (pwritten) *pwritten = 0;
+    }
+    conv->started = 1;
+    return ret;
+}
+
+static int QuicConversation_step(QuicConversation *conv)
+{
+    int n;
+
+    if (!conv->started) {
+        AssertTrue(wolfSSL_connect(conv->client->ssl) != WOLFSSL_SUCCESS);
+        AssertIntEQ(SSL_ERROR_WANT_READ, wolfSSL_get_error(conv->client->ssl, 0));
+        conv->started = 1;
+    }
+    if (conv->server->output.len > 0) {
+        QuicTestContext_forward(conv->server, conv->client, conv->rec_log);
+        n = wolfSSL_quic_read_write(conv->client->ssl);
+        if (n != WOLFSSL_SUCCESS) {
+            AssertIntEQ(wolfSSL_get_error(conv->client->ssl, 0), SSL_ERROR_WANT_READ);
+        }
+        return 1;
+    }
+    else if (conv->client->output.len > 0) {
+        QuicTestContext_forward(conv->client, conv->server, conv->rec_log);
+#ifdef WOLFSSL_EARLY_DATA
+        if (conv->accept_early_data) {
+            int written;
+            n = wolfSSL_read_early_data(conv->server->ssl,
+                                        conv->early_data + conv->early_data_len,
+                                        (int)(sizeof(conv->early_data) - conv->early_data_len),
+                                        &written);
+            if (n < 0) {
+                AssertIntEQ(wolfSSL_get_error(conv->server->ssl, 0), SSL_ERROR_WANT_READ);
+            }
+            else if (n > 0) {
+                conv->early_data_len += n;
+                if (conv->verbose)
+                    printf("RECVed early data, len now=%d\n", (int)conv->early_data_len);
+            }
+        }
+        else
+ #endif /* WOLFSSL_EARLY_DATA */
+        {
+            n = wolfSSL_quic_read_write(conv->server->ssl);
+            if (n != WOLFSSL_SUCCESS) {
+                AssertIntEQ(wolfSSL_get_error(conv->server->ssl, 0), SSL_ERROR_WANT_READ);
+            }
+        }
+        return 1;
+    }
+    return 0;
+}
+
+static void QuicConversation_do(QuicConversation *conv)
+{
+    if (!conv->started) {
+        QuicConversation_start(conv, NULL, 0, NULL);
+    }
+
+    while (1) {
+        if (!QuicConversation_step(conv)) {
+            int c_err = wolfSSL_get_error(conv->client->ssl, 0);
+            int s_err = wolfSSL_get_error(conv->server->ssl, 0);
+            if (c_err == 0
+                && (s_err == 0
+                    || (conv->sent_early_data && s_err == SSL_ERROR_WANT_READ))) {
+                /* Since QUIC does not use EndOfEarlyData messages, we may
+                 * encounter WANT_READ on the server side. QUIC protocol stacks
+                 * detect EOF here differently, so this should be fine. */
+                break;  /* handshake done */
+            }
+            printf("Neither tclient nor server have anything to send, "
+                   "but client_error=%d, server_error=%d\n",
+                   c_err, s_err);
+            AssertFalse(1);
+        }
+    }
+}
+
+static int test_quic_client_hello(int verbose) {
+    WOLFSSL_CTX *ctx;
+    int ret = 0;
+    QuicTestContext tctx;
+
+    (void)ctx_dump_output;
+
+    AssertNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
+
+    QuicTestContext_init(&tctx, ctx, "client", verbose);
+    /* Without any QUIC transport params, this needs to fail */
+    AssertTrue(wolfSSL_set_quic_transport_params(tctx.ssl, NULL, 0) == WOLFSSL_SUCCESS);
+    AssertTrue(wolfSSL_quic_read_write(tctx.ssl) != 0);
+    AssertIntEQ(wolfSSL_get_error(tctx.ssl, 0), QUIC_TP_MISSING_E);
+    QuicTestContext_free(&tctx);
+
+    /* Set transport params, expect both extensions */
+    QuicTestContext_init(&tctx, ctx, "client", verbose);
+#ifdef HAVE_SNI
+    wolfSSL_UseSNI(tctx.ssl, WOLFSSL_SNI_HOST_NAME, "wolfssl.com", sizeof("wolfssl.com")-1);
+#endif
+    AssertTrue(wolfSSL_connect(tctx.ssl) != 0);
+    AssertIntEQ(wolfSSL_get_error(tctx.ssl, 0), SSL_ERROR_WANT_READ);
+    check_quic_client_hello_tp(&tctx.output, 1, 1);
+    QuicTestContext_free(&tctx);
+
+    /* Set transport params v1, expect v1 extension */
+    QuicTestContext_init(&tctx, ctx, "client", verbose);
+    wolfSSL_set_quic_transport_version(tctx.ssl, TLSX_KEY_QUIC_TP_PARAMS);
+    AssertTrue(wolfSSL_connect(tctx.ssl) != 0);
+    check_quic_client_hello_tp(&tctx.output, 1, 0);
+    QuicTestContext_free(&tctx);
+
+    /* Set transport params draft, expect draft extension */
+    QuicTestContext_init(&tctx, ctx, "client", verbose);
+    wolfSSL_set_quic_transport_version(tctx.ssl, TLSX_KEY_QUIC_TP_PARAMS_DRAFT);
+    AssertTrue(wolfSSL_connect(tctx.ssl) != 0);
+    check_quic_client_hello_tp(&tctx.output, 0, 1);
+    QuicTestContext_free(&tctx);
+
+    /* Set transport params 0, expect both extension */
+    QuicTestContext_init(&tctx, ctx, "client", verbose);
+    wolfSSL_set_quic_transport_version(tctx.ssl, 0);
+    AssertTrue(wolfSSL_connect(tctx.ssl) != 0);
+    check_quic_client_hello_tp(&tctx.output, 1, 1);
+    QuicTestContext_free(&tctx);
+
+    wolfSSL_CTX_free(ctx);
+    printf("    test_quic_client_hello: %s\n", (ret == 0)? passed : failed);
+
+    return ret;
+}
+
+static int test_quic_server_hello(int verbose) {
+    WOLFSSL_CTX *ctx_c, *ctx_s;
+    int ret = 0;
+    QuicTestContext tclient, tserver;
+    QuicConversation conv;
+
+    AssertNotNull(ctx_c = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
+    AssertNotNull(ctx_s = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
+    AssertTrue(wolfSSL_CTX_use_certificate_file(ctx_s, svrCertFile, WOLFSSL_FILETYPE_PEM));
+    AssertTrue(wolfSSL_CTX_use_PrivateKey_file(ctx_s, svrKeyFile, WOLFSSL_FILETYPE_PEM));
+
+    /* setup ssls */
+    QuicTestContext_init(&tclient, ctx_c, "client", verbose);
+    QuicTestContext_init(&tserver, ctx_s, "server", verbose);
+
+    /* connect */
+    QuicConversation_init(&conv, &tclient, &tserver);
+    QuicConversation_step(&conv);
+    /* check established/missing secrets */
+    check_secrets(&tserver, wolfssl_encryption_initial, 0, 0);
+    check_secrets(&tserver, wolfssl_encryption_handshake, 32, 32);
+    check_secrets(&tserver, wolfssl_encryption_application, 32, 32);
+    check_secrets(&tclient, wolfssl_encryption_handshake, 0, 0);
+    /* feed the server data to the client */
+    QuicConversation_step(&conv);
+    /* client has generated handshake secret */
+    check_secrets(&tclient, wolfssl_encryption_handshake, 32, 32);
+    /* continue the handshake till done */
+    conv.started = 1;
+    /* run till end */
+    QuicConversation_do(&conv);
+    AssertIntEQ(tclient.output.len, 0);
+    AssertIntEQ(tserver.output.len, 0);
+    /* what have we seen? */
+#ifdef HAVE_SESSION_TICKET
+    AssertStrEQ(conv.rec_log, "ClientHello:ServerHello:EncryptedExtension:Certificate:CertificateVerify:Finished:Finished:SessionTicket");
+#else
+    AssertStrEQ(conv.rec_log, "ClientHello:ServerHello:EncryptedExtension:Certificate:CertificateVerify:Finished:Finished");
+#endif
+    /* we are at application encryption level */
+    AssertTrue(wolfSSL_quic_read_level(tclient.ssl) == wolfssl_encryption_application);
+    AssertTrue(wolfSSL_quic_write_level(tclient.ssl) == wolfssl_encryption_application);
+    AssertTrue(wolfSSL_quic_read_level(tserver.ssl) == wolfssl_encryption_application);
+    AssertTrue(wolfSSL_quic_write_level(tserver.ssl) == wolfssl_encryption_application);
+    /* the last client write (FINISHED) was at handshake level */
+    AssertTrue(tclient.output.level == wolfssl_encryption_handshake);
+    /* we have the app secrets */
+    check_secrets(&tclient, wolfssl_encryption_application, 32, 32);
+    check_secrets(&tserver, wolfssl_encryption_application, 32, 32);
+    /* verify client and server have the same secrets establishd */
+    assert_secrets_EQ(&tclient, &tserver, wolfssl_encryption_handshake);
+    assert_secrets_EQ(&tclient, &tserver, wolfssl_encryption_application);
+    /* AEAD cipher should be known */
+    AssertNotNull(wolfSSL_quic_get_aead(tclient.ssl));
+    AssertNotNull(wolfSSL_quic_get_aead(tserver.ssl));
+    /* What was negiotiated and is it the same? */
+    AssertIntEQ(wolfSSL_get_peer_quic_transport_version(tclient.ssl),
+                wolfSSL_get_peer_quic_transport_version(tserver.ssl));
+
+    QuicTestContext_free(&tclient);
+    QuicTestContext_free(&tserver);
+
+    wolfSSL_CTX_free(ctx_c);
+    wolfSSL_CTX_free(ctx_s);
+    printf("    test_quic_server_hello: %s\n", (ret == 0)? passed : failed);
+
+    return ret;
+}
+
+#ifdef HAVE_SESSION_TICKET
+
+static int test_quic_resumption(int verbose) {
+    WOLFSSL_CTX *ctx_c, *ctx_s;
+    WOLFSSL_SESSION *session;
+    int ret = 0;
+    QuicTestContext tclient, tserver;
+    QuicConversation conv;
+
+    AssertNotNull(ctx_c = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
+    AssertNotNull(ctx_s = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
+    AssertTrue(wolfSSL_CTX_use_certificate_file(ctx_s, svrCertFile, WOLFSSL_FILETYPE_PEM));
+    AssertTrue(wolfSSL_CTX_use_PrivateKey_file(ctx_s, svrKeyFile, WOLFSSL_FILETYPE_PEM));
+
+    /* setup ssls */
+    QuicTestContext_init(&tclient, ctx_c, "client", verbose);
+    QuicTestContext_init(&tserver, ctx_s, "server", verbose);
+
+    QuicConversation_init(&conv, &tclient, &tserver);
+    /* run till end */
+    QuicConversation_do(&conv);
+    /* what have we seen? */
+    AssertStrEQ(conv.rec_log, "ClientHello:ServerHello:EncryptedExtension:Certificate:CertificateVerify:Finished:Finished:SessionTicket");
+
+    /* Should have received a session ticket, save the session */
+    AssertTrue(tclient.ticket_len > 0);
+    AssertNotNull(session = wolfSSL_get1_session(tclient.ssl));
+    QuicTestContext_free(&tserver);
+    QuicTestContext_free(&tclient);
+
+    /* Do a Session resumption with the ticket */
+    QuicTestContext_init(&tserver, ctx_s, "server", verbose);
+    QuicTestContext_init(&tclient, ctx_c, "client_resume", verbose);
+    AssertIntEQ(wolfSSL_set_session(tclient.ssl, session), WOLFSSL_SUCCESS);
+    /* let them talk */
+    QuicConversation_init(&conv, &tclient, &tserver);
+    QuicConversation_do(&conv);
+    /* this is what should happen. Look Ma, no certificate! */
+    AssertStrEQ(conv.rec_log, "ClientHello:ServerHello:EncryptedExtension:Finished:Finished:SessionTicket");
+
+    QuicTestContext_free(&tclient);
+    QuicTestContext_free(&tserver);
+
+    wolfSSL_SESSION_free(session);
+    wolfSSL_CTX_free(ctx_c);
+    wolfSSL_CTX_free(ctx_s);
+
+    printf("    test_quic_resumption: %s\n", (ret == 0)? passed : failed);
+    return ret;
+}
+
+#ifdef WOLFSSL_EARLY_DATA
+static int test_quic_early_data(int verbose) {
+    WOLFSSL_CTX *ctx_c, *ctx_s;
+    int ret = 0;
+    QuicTestContext tclient, tserver;
+    QuicConversation conv;
+    const byte early_data[] = "Nulla dies sine linea!";
+    size_t ed_written;
+    WOLFSSL_SESSION *session;
+    unsigned int max_early_sz;
+
+    AssertNotNull(ctx_c = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
+    wolfSSL_CTX_UseSessionTicket(ctx_c);
+
+    AssertNotNull(ctx_s = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
+    AssertTrue(wolfSSL_CTX_use_certificate_file(ctx_s, svrCertFile, WOLFSSL_FILETYPE_PEM));
+    AssertTrue(wolfSSL_CTX_use_PrivateKey_file(ctx_s, svrKeyFile, WOLFSSL_FILETYPE_PEM));
+
+    /* setup ssls */
+    QuicTestContext_init(&tclient, ctx_c, "client", verbose);
+    QuicTestContext_init(&tserver, ctx_s, "server", verbose);
+    wolfSSL_set_quic_early_data_enabled(tserver.ssl, 1);
+    /* QUIC only allows 0xffffffff or 0x0 as values */
+    AssertIntEQ(wolfSSL_get_max_early_data(tserver.ssl), UINT32_MAX);
+
+    QuicConversation_init(&conv, &tclient, &tserver);
+    /* run till end */
+    QuicConversation_do(&conv);
+    /* what have we seen? */
+    AssertStrEQ(conv.rec_log, "ClientHello:ServerHello:EncryptedExtension:Certificate:CertificateVerify:Finished:Finished:SessionTicket");
+
+    /* Should have received a session ticket, save the session */
+    AssertTrue(tclient.ticket_len > 0);
+    AssertNotNull(session = wolfSSL_get1_session(tclient.ssl));
+    QuicTestContext_free(&tclient);
+    QuicTestContext_free(&tserver);
+
+    /* QUIC requires 0 or 0xffffffff as only allowed values.
+     * Since we enabled early data in the server that created the session,
+     * we need to see it here. */
+    max_early_sz = wolfSSL_SESSION_get_max_early_data(session);
+    AssertIntEQ(max_early_sz, UINT32_MAX);
+
+    /* Do a Session resumption with the ticket */
+    QuicTestContext_init(&tserver, ctx_s, "server", verbose);
+    QuicTestContext_init(&tclient, ctx_c, "client", verbose);
+    AssertIntEQ(wolfSSL_set_session(tclient.ssl, session), WOLFSSL_SUCCESS);
+    /* enable early data -*/
+    wolfSSL_set_quic_early_data_enabled(tserver.ssl, 1);
+    /* client will send, but server will not receive, since
+     * QuicConversation_do() uses wolfSSL_accept() */
+    QuicConversation_init(&conv, &tclient, &tserver);
+    QuicConversation_start(&conv, early_data, sizeof(early_data), &ed_written);
+    QuicConversation_do(&conv);
+    AssertIntEQ(wolfSSL_get_early_data_status(tclient.ssl), WOLFSSL_EARLY_DATA_REJECTED);
+
+    QuicTestContext_free(&tclient);
+    QuicTestContext_free(&tserver);
+
+    QuicTestContext_init(&tserver, ctx_s, "server", verbose);
+    QuicTestContext_init(&tclient, ctx_c, "client", verbose);
+    AssertIntEQ(wolfSSL_set_session(tclient.ssl, session), WOLFSSL_SUCCESS);
+    /* client will send, and server will receive */
+    QuicConversation_init(&conv, &tclient, &tserver);
+    /* make QuicConversation_do() use wolfSSL_read_early_data() */
+    conv.accept_early_data = 1;
+    QuicConversation_start(&conv, early_data, sizeof(early_data), &ed_written);
+    QuicConversation_do(&conv);
+    AssertIntEQ(wolfSSL_get_early_data_status(tclient.ssl), WOLFSSL_EARLY_DATA_ACCEPTED);
+    AssertIntEQ(conv.early_data_len, sizeof(early_data));
+    AssertStrEQ(conv.early_data, (const char*)early_data);
+
+    QuicTestContext_free(&tclient);
+    QuicTestContext_free(&tserver);
+
+    wolfSSL_SESSION_free(session);
+    wolfSSL_CTX_free(ctx_c);
+    wolfSSL_CTX_free(ctx_s);
+    printf("    test_quic_early_data: %s\n", (ret == 0)? passed : failed);
+
+    return ret;
+}
+#endif /* WOLFSSL_EARLY_DATA */
+
+static int new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session)
+{
+    QuicTestContext *ctx = (QuicTestContext*)wolfSSL_get_app_data(ssl);
+    byte *data;
+    int ret = 0;
+    int sz;
+
+    sz = wolfSSL_i2d_SSL_SESSION(session, NULL);
+    if (sz <= 0) {
+        printf("[%s] session serialization error: %d <- ", ctx->name, sz);
+        return sz;
+    }
+    if ((size_t)sz > sizeof(ctx->session)) {
+        printf("[%s] session serialization too large: %d <- ", ctx->name, sz);
+        return -1;
+    }
+    data = ctx->session;
+    ctx->session_len = wolfSSL_i2d_SSL_SESSION(session, &data);
+    if (ctx->verbose) {
+        printf("[%s]", ctx->name);
+        dump_buffer(" new SESSION", ctx->session, ctx->session_len, 4);
+    }
+    return ret;
+}
+
+static int test_quic_session_export(int verbose)
+{
+    WOLFSSL_CTX *ctx_c, *ctx_s;
+    WOLFSSL_SESSION *session = NULL;
+    int ret = 0;
+    QuicTestContext tclient, tserver;
+    QuicConversation conv;
+    byte session_data[16*1024];
+    const byte *bp;
+    word32 session_len;
+
+    AssertNotNull(ctx_c = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
+    AssertNotNull(ctx_s = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
+    AssertTrue(wolfSSL_CTX_use_certificate_file(ctx_s, svrCertFile, WOLFSSL_FILETYPE_PEM));
+    AssertTrue(wolfSSL_CTX_use_PrivateKey_file(ctx_s, svrKeyFile, WOLFSSL_FILETYPE_PEM));
+
+    /* Uses CTX session callback for new sessions */
+    wolfSSL_CTX_sess_set_new_cb(ctx_c, new_session_cb);
+
+    /* setup ssls */
+    QuicTestContext_init(&tclient, ctx_c, "client", verbose);
+    QuicTestContext_init(&tserver, ctx_s, "server", verbose);
+
+    QuicConversation_init(&conv, &tclient, &tserver);
+    /* run till end */
+    QuicConversation_do(&conv);
+    /* what have we seen? */
+    AssertStrEQ(conv.rec_log, "ClientHello:ServerHello:EncryptedExtension:Certificate:CertificateVerify:Finished:Finished:SessionTicket");
+
+    /* Should have received a session, save it */
+    AssertTrue(tclient.session_len > 0);
+    memcpy(session_data, tclient.session, tclient.session_len);
+    session_len = tclient.session_len;
+    if (verbose)
+        dump_buffer("copied SESSION", session_data, session_len, 0);
+
+    QuicTestContext_free(&tserver);
+    QuicTestContext_free(&tclient);
+
+    /* Do a Session resumption with the ticket */
+    QuicTestContext_init(&tserver, ctx_s, "server", verbose);
+    QuicTestContext_init(&tclient, ctx_c, "client_resume", verbose);
+    bp = session_data;
+    AssertNotNull(session = wolfSSL_d2i_SSL_SESSION(NULL, &bp, session_len));
+    AssertIntEQ(wolfSSL_set_session(tclient.ssl, session), WOLFSSL_SUCCESS);
+    wolfSSL_SESSION_free(session);
+
+    /* let them talk */
+    QuicConversation_init(&conv, &tclient, &tserver);
+    QuicConversation_do(&conv);
+    /* this is what should happen. Look Ma, no certificate! */
+    AssertStrEQ(conv.rec_log, "ClientHello:ServerHello:EncryptedExtension:Finished:Finished:SessionTicket");
+
+    QuicTestContext_free(&tclient);
+    QuicTestContext_free(&tserver);
+
+    wolfSSL_CTX_free(ctx_c);
+    wolfSSL_CTX_free(ctx_s);
+
+    printf("    test_quic_session_export: %s\n", (ret == 0)? passed : failed);
+    return ret;
+}
+#endif /* WOLFSSL_SESSION_EXPORT */
+
+#endif /* WOLFSSL_QUIC */
+
+
+int QuicTest(void)
+{
+    int ret = 0;
+#ifdef WOLFSSL_QUIC
+    int verbose = 0;
+    printf(" Begin QUIC Tests\n");
+
+    if ((ret = test_set_quic_method()) != 0) goto leave;
+    if ((ret = test_provide_quic_data()) != 0) goto leave;
+    if ((ret = test_quic_crypt()) != 0) goto leave;
+    if ((ret = test_quic_client_hello(verbose)) != 0) goto leave;
+    if ((ret = test_quic_server_hello(verbose)) != 0) goto leave;
+#ifdef HAVE_SESSION_TICKET
+    if ((ret = test_quic_resumption(verbose)) != 0) goto leave;
+#ifdef WOLFSSL_EARLY_DATA
+    if ((ret = test_quic_early_data(verbose)) != 0) goto leave;
+#endif /* WOLFSSL_EARLY_DATA */
+    if ((ret = test_quic_session_export(verbose)) != 0) goto leave;
+#endif /* HAVE_SESSION_TICKET */
+
+leave:
+    if (ret != 0)
+        printf("  FAILED: some tests did not pass.\n");
+    printf(" End QUIC Tests\n");
+#endif
+    return ret;
+}

+ 7 - 0
tests/unit.c

@@ -185,6 +185,13 @@ int unit_test(int argc, char** argv)
 #endif
 #endif /* NO_WOLFSSL_CIPHER_SUITE_TEST */
 
+#ifdef WOLFSSL_QUIC
+    if ( (ret = QuicTest()) != 0){
+        printf("quic test failed with %d\n", ret);
+        goto exit;
+    }
+#endif
+
     SrpTest();
 
 exit:

+ 1 - 0
tests/unit.h

@@ -118,6 +118,7 @@ int  SuiteTest(int argc, char** argv);
 int  HashTest(void);
 void SrpTest(void);
 int w64wrapper_test(void);
+int QuicTest(void);
 
 
 #endif /* CyaSSL_UNIT_H */

+ 1 - 0
wolfssl/error-ssl.h

@@ -176,6 +176,7 @@ enum wolfSSL_ErrorCodes {
     HTTP_APPSTR_ERR              = -449,   /* HTTP Application string error */
     UNSUPPORTED_PROTO_VERSION    = -450,   /* bad/unsupported protocol version*/
     FALCON_KEY_SIZE_E            = -451,   /* Wrong key size for Falcon. */
+    QUIC_TP_MISSING_E            = -452,   /* QUIC transport parameter missing */
 
     /* add strings to wolfSSL_ERR_reason_error_string in internal.c !!!!! */
 

+ 1 - 0
wolfssl/include.am

@@ -17,6 +17,7 @@ nobase_include_HEADERS+= \
                          wolfssl/test.h \
                          wolfssl/version.h \
                          wolfssl/ocsp.h \
+                         wolfssl/quic.h \
                          wolfssl/crl.h \
                          wolfssl/wolfio.h
 

+ 87 - 4
wolfssl/internal.h

@@ -74,6 +74,9 @@
 #ifdef HAVE_OCSP
     #include <wolfssl/ocsp.h>
 #endif
+#ifdef WOLFSSL_QUIC
+    #include <wolfssl/quic.h>
+#endif
 #ifdef WOLFSSL_SHA384
     #include <wolfssl/wolfcrypt/sha512.h>
 #endif
@@ -2026,7 +2029,7 @@ WOLFSSL_LOCAL int  SetSuitesHashSigAlgo(Suites* suites, const char* list);
 #if defined(WOLFSSL_DTLS) && defined(WOLFSSL_SESSION_EXPORT) && \
    !defined(WOLFSSL_DTLS_EXPORT_TYPES)
     typedef int (*wc_dtls_export)(WOLFSSL* ssl,
-                   unsigned char* exportBuffer, unsigned int sz, void* userCtx);
+
 #define WOLFSSL_DTLS_EXPORT_TYPES
 #endif /* WOLFSSL_DTLS_EXPORT_TYPES */
 
@@ -2423,8 +2426,14 @@ typedef enum {
     TLSX_SIGNATURE_ALGORITHMS_CERT  = 0x0032,
     #endif
     TLSX_KEY_SHARE                  = 0x0033,
+    #ifdef WOLFSSL_QUIC
+    TLSX_KEY_QUIC_TP_PARAMS         = 0x0039, /* RFC 9001, ch. 8.2 */
+    #endif
+#endif
+    TLSX_RENEGOTIATION_INFO         = 0xff01,
+#ifdef WOLFSSL_QUIC
+    TLSX_KEY_QUIC_TP_PARAMS_DRAFT   = 0xffa5, /* from draft-ietf-quic-tls-27 */
 #endif
-    TLSX_RENEGOTIATION_INFO         = 0xff01
 } TLSX_Type;
 
 typedef struct TLSX {
@@ -3084,7 +3093,8 @@ struct WOLFSSL_CTX {
 #ifdef HAVE_EX_DATA
     WOLFSSL_CRYPTO_EX_DATA ex_data;
 #endif
-#if defined(HAVE_ALPN) && (defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) || defined(HAVE_LIGHTY))
+#if defined(HAVE_ALPN) && (defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || \
+    defined(WOLFSSL_HAPROXY) || defined(HAVE_LIGHTY) || defined(WOLFSSL_QUIC))
     CallbackALPNSelect alpnSelect;
     void*              alpnSelectArg;
 #endif
@@ -3219,6 +3229,11 @@ struct WOLFSSL_CTX {
     wolfSSL_Mutex staticKELock;
     #endif
 #endif
+#ifdef WOLFSSL_QUIC
+    struct {
+        const WOLFSSL_QUIC_METHOD *method;
+    } quic;
+#endif
 };
 
 WOLFSSL_LOCAL
@@ -3482,6 +3497,35 @@ typedef enum WOLFSSL_SESSION_TYPE {
     WOLFSSL_SESSION_TYPE_HEAP    /* allocated from heap SESSION_new */
 } WOLFSSL_SESSION_TYPE;
 
+#ifdef WOLFSSL_QUIC
+typedef struct QuicRecord QuicRecord;
+typedef struct QuicRecord {
+    struct QuicRecord *next;
+    uint8_t *data;
+    word32 capacity;
+    word32 len;
+    word32 start;
+    word32 end;
+    WOLFSSL_ENCRYPTION_LEVEL level;
+    word32 rec_hdr_remain;
+} QuicEncData;
+
+typedef struct QuicTransportParam QuicTransportParam;
+struct QuicTransportParam {
+    const uint8_t *data;
+    word16 len;
+};
+
+WOLFSSL_LOCAL const QuicTransportParam *QuicTransportParam_new(const uint8_t *data, size_t len, void *heap);
+WOLFSSL_LOCAL const QuicTransportParam *QuicTransportParam_dup(const QuicTransportParam *tp, void *heap);
+WOLFSSL_LOCAL void QuicTransportParam_free(const QuicTransportParam *tp, void *heap);
+WOLFSSL_LOCAL int TLSX_QuicTP_Use(WOLFSSL* ssl, TLSX_Type ext_type, int is_response);
+WOLFSSL_LOCAL int wolfSSL_quic_add_transport_extensions(WOLFSSL *ssl, int msg_type);
+
+#define QTP_FREE     QuicTransportParam_free
+
+#endif /* WOLFSSL_QUIC */
+
 /* wolfSSL session type */
 struct WOLFSSL_SESSION {
     /* WARNING Do not add fields here. They will be ignored in
@@ -3918,6 +3962,9 @@ typedef struct Options {
     word16            dtls13SendMoreAcks:1;  /* Send more acks during the
                                               * handshake process */
 #endif
+#ifdef WOLFSSL_TLS13
+    word16            tls13MiddleBoxCompat:1; /* TLSv1.3 middlebox compatibility */
+#endif
 
     /* need full byte values for this section */
     byte            processReply;           /* nonblocking resume */
@@ -4780,7 +4827,8 @@ struct WOLFSSL {
     #endif                                         /* user turned on */
     #ifdef HAVE_ALPN
         char*   alpn_client_list;  /* keep the client's list */
-        #if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX)  || defined(WOLFSSL_HAPROXY)
+        #if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX)  || \
+            defined(WOLFSSL_HAPROXY) || defined(WOLFSSL_QUIC)
             CallbackALPNSelect alpnSelect;
             void*              alpnSelectArg;
         #endif
@@ -4924,6 +4972,27 @@ struct WOLFSSL {
 #ifdef WOLFSSL_LWIP_NATIVE
     WOLFSSL_LWIP_NATIVE_STATE      lwipCtx; /* LwIP native socket IO Context */
 #endif
+#ifdef WOLFSSL_QUIC
+    struct {
+        const WOLFSSL_QUIC_METHOD* method;
+        WOLFSSL_ENCRYPTION_LEVEL enc_level_read;
+        WOLFSSL_ENCRYPTION_LEVEL enc_level_read_next;
+        WOLFSSL_ENCRYPTION_LEVEL enc_level_latest_recvd;
+        WOLFSSL_ENCRYPTION_LEVEL enc_level_write;
+        WOLFSSL_ENCRYPTION_LEVEL enc_level_write_next;
+        int transport_version;
+        const QuicTransportParam* transport_local;
+        const QuicTransportParam* transport_peer;
+        const QuicTransportParam* transport_peer_draft;
+        QuicRecord* input_head;          /* we own, data for handshake */
+        QuicRecord* input_tail;          /* points to last element for append */
+        QuicRecord* scratch;             /* we own, record construction */
+        enum wolfssl_encryption_level_t output_rec_level;
+                                         /* encryption level of current output record */
+        word32 output_rec_remain;        /* how many bytes of output TLS record
+                                          * content have not been handled yet by quic */
+    } quic;
+#endif /* WOLFSSL_QUIC */
 };
 
 /*
@@ -5562,6 +5631,20 @@ WOLFSSL_LOCAL int wolfSSL_RSA_To_Der(WOLFSSL_RSA* rsa, byte** outBuf,
     int publicKey, void* heap);
 #endif
 
+#ifdef WOLFSSL_QUIC
+#define WOLFSSL_IS_QUIC(s)  (s && s->quic.method != NULL)
+WOLFSSL_LOCAL int wolfSSL_quic_receive(WOLFSSL* ssl, byte* buf, word32 sz);
+WOLFSSL_LOCAL int wolfSSL_quic_send(WOLFSSL* ssl);
+WOLFSSL_LOCAL void wolfSSL_quic_clear(WOLFSSL* ssl);
+WOLFSSL_LOCAL void wolfSSL_quic_free(WOLFSSL* ssl);
+WOLFSSL_LOCAL int wolfSSL_quic_forward_secrets(WOLFSSL *ssl,
+                                               int ktype, int side);
+WOLFSSL_LOCAL int wolfSSL_quic_keys_active(WOLFSSL* ssl, enum encrypt_side side);
+
+#else
+#define WOLFSSL_IS_QUIC(s) 0
+#endif /* WOLFSSL_QUIC (else) */
+
 #ifdef __cplusplus
     }  /* extern "C" */
 #endif

+ 1 - 0
wolfssl/openssl/ssl.h

@@ -384,6 +384,7 @@ typedef STACK_OF(ACCESS_DESCRIPTION) AUTHORITY_INFO_ACCESS;
 #define SSL_get_keys                    wolfSSL_get_keys
 #define SSL_SESSION_get_master_key      wolfSSL_SESSION_get_master_key
 #define SSL_SESSION_get_master_key_length wolfSSL_SESSION_get_master_key_length
+#define SSL_SESSION_get_max_early_data  wolfSSL_SESSION_get_max_early_data
 
 #if defined(WOLFSSL_QT) || defined(OPENSSL_ALL)
     #define SSL_MODE_RELEASE_BUFFERS    0x00000010U

+ 294 - 0
wolfssl/quic.h

@@ -0,0 +1,294 @@
+/* quic.h
+ *
+ * Copyright (C) 2006-2021 wolfSSL Inc.
+ *
+ * This file is part of wolfSSL.
+ *
+ * wolfSSL is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+ */
+
+
+
+/* wolfSSL QUIC API */
+
+#ifndef WOLFSSL_QUIC_H
+#define WOLFSSL_QUIC_H
+
+#ifdef __cplusplus
+    extern "C" {
+#endif
+
+#ifdef WOLFSSL_QUIC
+
+/* QUIC operates on three encryption levels which determine
+ * which keys/algos are used for de-/encryption. These are
+ * kept separately for incoming and outgoing data and.
+ * Due to the nature of UDP, more than one might be in use
+ * at the same time due to resends or out-of-order arrivals.
+ */
+typedef enum wolfssl_encryption_level_t {
+    wolfssl_encryption_initial = 0,
+    wolfssl_encryption_early_data,
+    wolfssl_encryption_handshake,
+    wolfssl_encryption_application
+} WOLFSSL_ENCRYPTION_LEVEL;
+
+
+/* All QUIC related callbacks to the application.
+ */
+typedef struct wolfssl_quic_method_t WOLFSSL_QUIC_METHOD;
+
+struct wolfssl_quic_method_t {
+    /**
+     * Provide secrets to the QUIC stack when they becaome available in the SSL
+     * instance during handshake processing. read/write secrets have the same
+     * length. A call may only provide one, passing NULL as the other.
+     */
+    int (*set_encryption_secrets)(WOLFSSL* ssl, WOLFSSL_ENCRYPTION_LEVEL level,
+                                  const uint8_t* read_secret,
+                                  const uint8_t* write_secret,
+                                  size_t secret_len);
+    /**
+     * Provide handshake packets to the QUIC stack to send to the peer. The
+     * QUIC stack will wrap these and take care of re-transmissions.
+     */
+    int (*add_handshake_data)(WOLFSSL* ssl, WOLFSSL_ENCRYPTION_LEVEL level,
+                              const uint8_t* data, size_t len);
+    /**
+     * Flush any buffered packets during handshake.
+     */
+    int (*flush_flight)(WOLFSSL* ssl);
+    /**
+     * Send a TLS alert that happend during handshake. In QUIC, such alerts
+     * lead to connection shutdown.
+     */
+    int (*send_alert)(WOLFSSL* ssl, WOLFSSL_ENCRYPTION_LEVEL level,
+                      uint8_t alert);
+};
+
+
+/**
+ * Mark the given SSL context for QUIC protocol handling. Meaning all
+ * SSL instances derived from it will inherit this. Provides all callbacks
+ * to the QUIC application the SSL stack needs.
+ */
+WOLFSSL_API
+int wolfSSL_CTX_set_quic_method(WOLFSSL_CTX* ctx,
+                                const WOLFSSL_QUIC_METHOD* quic_method);
+/**
+ * Mark extactly this SSL instance for QUIC protocol handling.
+ * Provides all callbacks to the QUIC application the SSL stack needs.
+ */
+WOLFSSL_API
+int wolfSSL_set_quic_method(WOLFSSL* ssl,
+                            const WOLFSSL_QUIC_METHOD* quic_method);
+
+/**
+ * Check if QUIC handling has been installed on the given SSL instance.
+ */
+WOLFSSL_API int wolfSSL_is_quic(WOLFSSL* ssl);
+
+/**
+ * Return the current encryption level of the SSL instance for READs.
+ */
+WOLFSSL_API
+WOLFSSL_ENCRYPTION_LEVEL  wolfSSL_quic_read_level(const WOLFSSL* ssl);
+
+/**
+ * Return the current encryption level of the SSL instance for WRITEs.
+ */
+WOLFSSL_API
+WOLFSSL_ENCRYPTION_LEVEL wolfSSL_quic_write_level(const WOLFSSL* ssl);
+
+
+/**
+ * Configure the QUIC transport version to use. On `use_legacy` != 0,
+ * selects TLSX_KEY_QUIC_TP_PARAMS_DRAFT, otherwise TLSX_KEY_QUIC_TP_PARAMS.
+ * This method is part of the BoringSSL API and replicated here for app
+ * portability (as in quictls/openssl).
+ */
+WOLFSSL_API
+void wolfSSL_set_quic_use_legacy_codepoint(WOLFSSL* ssl, int use_legacy);
+
+/**
+ * Set the TLS extension for the transport parameter version to announce
+ * to the peer. Known values are TLSX_KEY_QUIC_TP_PARAMS (V1) and
+ * TLSX_KEY_QUIC_TP_PARAMS_DRAFT.
+ * Setting it to 0 will announce both V1 and draft versions to a server.
+ * Servers will, on 0, select the latest version seen from the client.
+ * Default is 0.
+ */
+WOLFSSL_API
+void wolfSSL_set_quic_transport_version(WOLFSSL* ssl, int version);
+
+/**
+ * Get the configured transport version.
+ */
+WOLFSSL_API int wolfSSL_get_quic_transport_version(const WOLFSSL* ssl);
+
+/**
+ * Set the raw QUIC transport parameter that will be sent in the TLS extension
+ * to the peer, using the configured transport version(s).
+ */
+WOLFSSL_API int wolfSSL_set_quic_transport_params(WOLFSSL* ssl,
+                                                  const uint8_t* params,
+                                                  size_t params_len);
+/**
+ * Get the raw QUIC transport parameter as retrieved via TLS Extension
+ * from the peer. If the peer announced several versions,
+ * return the latest one.
+ * If the extension has not arrived yet, initializes out parameter to
+ * NULL, resp. 0.
+ */
+WOLFSSL_API
+void wolfSSL_get_peer_quic_transport_params(const WOLFSSL* ssl,
+                                            const uint8_t* *out_params,
+                                            size_t* out_params_len);
+
+/**
+ * Get the QUIC version negotiated with the peer during the handshake.
+ */
+WOLFSSL_API int wolfSSL_get_peer_quic_transport_version(const WOLFSSL* ssl);
+
+#ifdef WOLFSSL_EARLY_DATA
+WOLFSSL_API void wolfSSL_set_quic_early_data_enabled(WOLFSSL* ssl, int enabled);
+#endif
+
+/**
+ * Advisory amount of the maximum data a QUIC protocol handler should have
+ * in flight. This varies during handshake processing, for example certficate
+ * exchange will increase the limit.
+ */
+WOLFSSL_API
+size_t wolfSSL_quic_max_handshake_flight_len(const WOLFSSL* ssl,
+                                             WOLFSSL_ENCRYPTION_LEVEL level);
+
+
+/**
+ * The QUIC protocol handler provides peer TLS records to the SSL instance
+ * during handshake to progress it. The SSL instance will use the registered
+ * callbacks to send packets to the peer.
+ * Encryption level is provided to indicate how to decrypt the data. Data may
+ * be added for levels not yet reached by the SSL instance. However, data
+ * may only be added in ever increasing levels and levels may only increase
+ * at TLS record boundaries. Any violation will make this function fail.
+ */
+WOLFSSL_API
+int wolfSSL_provide_quic_data(WOLFSSL* ssl, WOLFSSL_ENCRYPTION_LEVEL level,
+                              const uint8_t* data, size_t len);
+
+/**
+ * Process any CRYPTO data added post-handshake.
+ */
+WOLFSSL_API int wolfSSL_process_quic_post_handshake(WOLFSSL* ssl);
+
+/**
+ * Process any pending input and flush all output. Can be invoked
+ * during and/or after handshake processing.
+ */
+WOLFSSL_API int wolfSSL_quic_read_write(WOLFSSL* ssl);
+
+/**
+ * Get the AEAD cipher that is currently selected in the SSL instance.
+ * Will return NULL if none has been selected so far. This is used by the
+ * QUIC stack to encrypt/decrypt packets after the handshake.
+ */
+WOLFSSL_API const WOLFSSL_EVP_CIPHER* wolfSSL_quic_get_aead(WOLFSSL* ssl);
+
+/**
+ * Use to classify the AEAD cipher for key reuse limits.
+ */
+WOLFSSL_API int wolfSSL_quic_aead_is_gcm(const WOLFSSL_EVP_CIPHER* aead_cipher);
+WOLFSSL_API int wolfSSL_quic_aead_is_ccm(const WOLFSSL_EVP_CIPHER* aead_cipher);
+WOLFSSL_API
+int wolfSSL_quic_aead_is_chacha20(const WOLFSSL_EVP_CIPHER* aead_cipher);
+
+/**
+ * Get the 'tag' length used by the AEAD cipher. Encryption buffer lengths
+ * are plaintext length plus this tag length.
+ */
+WOLFSSL_API
+size_t wolfSSL_quic_get_aead_tag_len(const WOLFSSL_EVP_CIPHER* aead_cipher);
+
+/**
+ * The message digest currently selected in the SSL instance.
+ */
+WOLFSSL_API const WOLFSSL_EVP_MD* wolfSSL_quic_get_md(WOLFSSL* ssl);
+
+/**
+ * The QUIC header protection cipher matching the AEAD cipher currently
+ * selected in the SSL instance.
+ */
+WOLFSSL_API const WOLFSSL_EVP_CIPHER* wolfSSL_quic_get_hp(WOLFSSL* ssl);
+
+/**
+ * Create and initialize a cipher context for use in en- or decryption.
+ */
+WOLFSSL_API WOLFSSL_EVP_CIPHER_CTX*
+wolfSSL_quic_crypt_new(const WOLFSSL_EVP_CIPHER* cipher,
+                       const uint8_t* key, const uint8_t* iv, int encrypt);
+
+/**
+ * Use a previously created cipher context to encrypt the given plain text.
+ */
+WOLFSSL_API
+int wolfSSL_quic_aead_encrypt(uint8_t* dest, WOLFSSL_EVP_CIPHER_CTX* aead_ctx,
+                              const uint8_t* plain, size_t plainlen,
+                              const uint8_t* iv, const uint8_t* aad,
+                              size_t aadlen);
+/**
+ * Use a previously created cipher context to decrypt the given encoded text.
+ */
+WOLFSSL_API
+int wolfSSL_quic_aead_decrypt(uint8_t* dest, WOLFSSL_EVP_CIPHER_CTX* ctx,
+                              const uint8_t* enc, size_t enclen,
+                              const uint8_t* iv, const uint8_t* aad,
+                              size_t aadlen);
+
+/**
+ * Extract a pseudo-random key, using the given message digest, a secret
+ * and a salt. The key size is the size of the digest.
+ */
+WOLFSSL_API
+int wolfSSL_quic_hkdf_extract(uint8_t* dest, const WOLFSSL_EVP_MD* md,
+                              const uint8_t* secret, size_t secretlen,
+                              const uint8_t* salt, size_t saltlen);
+/**
+ * Expand a pseudo-random key (secret) into a new key, using the mesasge
+ * digest and the info bytes.
+ */
+WOLFSSL_API
+int wolfSSL_quic_hkdf_expand(uint8_t* dest, size_t destlen,
+                             const WOLFSSL_EVP_MD* md,
+                             const uint8_t* secret, size_t secretlen,
+                             const uint8_t* info, size_t infolen);
+
+/**
+ * Extract and extpand secret, salt and info into a new key.
+ */
+WOLFSSL_API
+int wolfSSL_quic_hkdf(uint8_t* dest, size_t destlen,
+                      const WOLFSSL_EVP_MD* md,
+                      const uint8_t* secret, size_t secretlen,
+                      const uint8_t* salt, size_t saltlen,
+                      const uint8_t* info, size_t infolen);
+
+#endif /* WOLFSSL_QUIC */
+
+#ifdef __cplusplus
+    }  /* extern "C" */
+#endif
+
+#endif /* WOLFSSL_QUIC_H */

+ 6 - 1
wolfssl/ssl.h

@@ -1121,6 +1121,9 @@ WOLFSSL_API int  wolfSSL_write_early_data(WOLFSSL* ssl, const void* data,
 WOLFSSL_API int  wolfSSL_read_early_data(WOLFSSL* ssl, void* data, int sz,
                                          int* outSz);
 WOLFSSL_API int  wolfSSL_get_early_data_status(const WOLFSSL* ssl);
+#ifdef OPENSSL_EXTRA
+WOLFSSL_API unsigned int wolfSSL_SESSION_get_max_early_data(const WOLFSSL_SESSION *s);
+#endif /* OPENSSL_EXTRA */
 #endif /* WOLFSSL_EARLY_DATA */
 #endif /* WOLFSSL_TLS13 */
 WOLFSSL_ABI WOLFSSL_API void wolfSSL_CTX_free(WOLFSSL_CTX* ctx);
@@ -3629,7 +3632,9 @@ enum {
     WOLFSSL_MAX_ALPN_NUMBER = 257
 };
 
-#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) || defined(HAVE_LIGHTY)
+#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || \
+    defined(WOLFSSL_HAPROXY) || defined(HAVE_LIGHTY) || \
+    defined(WOLFSSL_QUIC)
 typedef int (*CallbackALPNSelect)(WOLFSSL* ssl, const unsigned char** out,
     unsigned char* outLen, const unsigned char* in, unsigned int inLen,
     void *arg);