Browse Source

QUIC API support in OpenSSL compat layer, as needed by HAProxy integration.

    - adding patch for HAProxy, see dod/QUIC.md, based on current master.
      For documentaton purposes, since HAProxy does not accept PRs. To be
      removed once forwarded to the project.
Stefan Eissing 1 year ago
parent
commit
e5cfd96609
13 changed files with 136 additions and 26 deletions
  1. 0 4
      doc/QUIC.md
  2. 3 0
      src/internal.c
  3. 12 3
      src/quic.c
  4. 20 6
      src/ssl.c
  5. 5 1
      src/tls.c
  6. 6 1
      src/tls13.c
  7. 6 6
      wolfcrypt/src/evp.c
  8. 1 0
      wolfssl/error-ssl.h
  9. 5 0
      wolfssl/internal.h
  10. 5 3
      wolfssl/openssl/evp.h
  11. 66 2
      wolfssl/openssl/ssl.h
  12. 6 0
      wolfssl/openssl/tls1.h
  13. 1 0
      wolfssl/wolfcrypt/types.h

+ 0 - 4
doc/QUIC.md

@@ -117,7 +117,3 @@ and for key generation `wolfSSL_quic_hkdf_extract()`, `wolfSSL_quic_hkdf_expand(
 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.
-
-
-
-

+ 3 - 0
src/internal.c

@@ -6142,6 +6142,7 @@ int SetSSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup)
     int ret;
     byte newSSL;
 
+    WOLFSSL_ENTER("SetSSL_CTX");
     if (!ssl || !ctx)
         return BAD_FUNC_ARG;
 
@@ -23354,6 +23355,8 @@ const char* wolfSSL_ERR_reason_error_string(unsigned long e)
 #ifdef WOLFSSL_QUIC
     case QUIC_TP_MISSING_E:
         return "QUIC transport parameter not set";
+    case QUIC_WRONG_ENC_LEVEL:
+        return "QUIC data received at wrong encryption level";
 #endif
     case DTLS_CID_ERROR:
         return "DTLS ConnectionID mismatch or missing";

+ 12 - 3
src/quic.c

@@ -302,9 +302,15 @@ void wolfSSL_quic_free(WOLFSSL* ssl)
 
 static int ctx_check_quic_compat(const WOLFSSL_CTX* ctx)
 {
+    WOLFSSL_ENTER("ctx_check_quic_compat");
     if (ctx->method->version.major != SSLv3_MAJOR
         || ctx->method->version.minor != TLSv1_3_MINOR
-        || ctx->method->downgrade) {
+        || (ctx->method->downgrade && ctx->minDowngrade < TLSv1_3_MINOR)) {
+        WOLFSSL_MSG_EX("ctx not quic compatible: vmajor=%d, vminor=%d, downgrade=%d",
+                       ctx->method->version.major,
+                       ctx->method->version.minor,
+                       ctx->method->downgrade
+                      );
         return WOLFSSL_FAILURE;
     }
     return WOLFSSL_SUCCESS;
@@ -312,6 +318,7 @@ static int ctx_check_quic_compat(const WOLFSSL_CTX* ctx)
 
 static int check_method_sanity(const WOLFSSL_QUIC_METHOD* m)
 {
+    WOLFSSL_ENTER("check_method_sanity");
     if (m && m->set_encryption_secrets
         && m->add_handshake_data
         && m->flush_flight
@@ -324,6 +331,7 @@ static int check_method_sanity(const WOLFSSL_QUIC_METHOD* m)
 int wolfSSL_CTX_set_quic_method(WOLFSSL_CTX* ctx,
                                 const WOLFSSL_QUIC_METHOD* quic_method)
 {
+    WOLFSSL_ENTER("wolfSSL_CTX_set_quic_method");
     if (ctx_check_quic_compat(ctx) != WOLFSSL_SUCCESS
         || check_method_sanity(quic_method) != WOLFSSL_SUCCESS) {
         return WOLFSSL_FAILURE;
@@ -336,6 +344,7 @@ int wolfSSL_CTX_set_quic_method(WOLFSSL_CTX* ctx,
 int wolfSSL_set_quic_method(WOLFSSL* ssl,
                             const WOLFSSL_QUIC_METHOD* quic_method)
 {
+    WOLFSSL_ENTER("wolfSSL_set_quic_method");
     if (ctx_check_quic_compat(ssl->ctx) != WOLFSSL_SUCCESS
         || check_method_sanity(quic_method) != WOLFSSL_SUCCESS) {
         return WOLFSSL_FAILURE;
@@ -583,7 +592,7 @@ int wolfSSL_quic_do_handshake(WOLFSSL* ssl)
                     ret = wolfSSL_write_early_data(ssl, tmpbuffer, 0, &len);
                 }
             }
-            else if (/*disables code*/(1)) {
+            else {
                 ret = wolfSSL_read_early_data(ssl, tmpbuffer,
                                               sizeof(tmpbuffer), &len);
                 if (ret < 0 && ssl->error == ZERO_RETURN) {
@@ -598,7 +607,7 @@ int wolfSSL_quic_do_handshake(WOLFSSL* ssl)
         }
 #endif /* WOLFSSL_EARLY_DATA */
 
-        ret = wolfSSL_SSL_do_handshake(ssl);
+        ret = wolfSSL_SSL_do_handshake_internal(ssl);
         if (ret <= 0)
             goto cleanup;
     }

+ 20 - 6
src/ssl.c

@@ -23027,6 +23027,7 @@ int wolfSSL_ERR_GET_LIB(unsigned long err)
     switch (value) {
     case -SSL_R_HTTP_REQUEST:
         return ERR_LIB_SSL;
+    case -ASN_NO_PEM_HEADER:
     case PEM_R_NO_START_LINE:
     case PEM_R_PROBLEMS_GETTING_PASSWORD:
     case PEM_R_BAD_PASSWORD_READ:
@@ -23058,8 +23059,10 @@ int wolfSSL_ERR_GET_REASON(unsigned long err)
     WOLFSSL_ENTER("wolfSSL_ERR_GET_REASON");
 
 #if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY)
-    /* Nginx looks for this error to know to stop parsing certificates. */
-    if (err == ((ERR_LIB_PEM << 24) | PEM_R_NO_START_LINE))
+    /* Nginx looks for this error to know to stop parsing certificates.
+     * Same for HAProxy. */
+    if (err == ((ERR_LIB_PEM << 24) | PEM_R_NO_START_LINE)
+        || (err & 0xFFFFFFL) == -ASN_NO_PEM_HEADER)
         return PEM_R_NO_START_LINE;
     if (err == ((ERR_LIB_SSL << 24) | -SSL_R_HTTP_REQUEST))
         return SSL_R_HTTP_REQUEST;
@@ -30917,7 +30920,8 @@ unsigned long wolfSSL_ERR_peek_last_error_line(const char **file, int *line)
             WOLFSSL_MSG("Issue peeking at error node in queue");
             return 0;
         }
-    #if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX)
+    #if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) \
+        || defined(WOLFSSL_HAPROXY)
         if (ret == -ASN_NO_PEM_HEADER)
             return (ERR_LIB_PEM << 24) | PEM_R_NO_START_LINE;
     #endif
@@ -33588,10 +33592,9 @@ BIO *wolfSSL_SSL_get_wbio(const WOLFSSL *s)
 }
 #endif /* !NO_BIO */
 
-int wolfSSL_SSL_do_handshake(WOLFSSL *s)
+int wolfSSL_SSL_do_handshake_internal(WOLFSSL *s)
 {
-    WOLFSSL_ENTER("wolfSSL_SSL_do_handshake");
-
+    WOLFSSL_ENTER("wolfSSL_SSL_do_handshake_internal");
     if (s == NULL)
         return WOLFSSL_FAILURE;
 
@@ -33612,6 +33615,17 @@ int wolfSSL_SSL_do_handshake(WOLFSSL *s)
 #endif
 }
 
+int wolfSSL_SSL_do_handshake(WOLFSSL *s)
+{
+    WOLFSSL_ENTER("wolfSSL_SSL_do_handshake");
+#ifdef WOLFSSL_QUIC
+    if (WOLFSSL_IS_QUIC(s)) {
+        return wolfSSL_quic_do_handshake(s);
+    }
+#endif
+    return wolfSSL_SSL_do_handshake_internal(s);
+}
+
 #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L
 int wolfSSL_SSL_in_init(const WOLFSSL *ssl)
 #else

+ 5 - 1
src/tls.c

@@ -10783,6 +10783,7 @@ static int TLSX_EarlyData_Write(word32 maxSz, byte* output, byte msgType,
 static int TLSX_EarlyData_Parse(WOLFSSL* ssl, const byte* input, word16 length,
                                  byte msgType)
 {
+    WOLFSSL_ENTER("TLSX_EarlyData_Parse");
     if (msgType == client_hello) {
         if (length != 0)
             return BUFFER_E;
@@ -10860,7 +10861,10 @@ int TLSX_EarlyData_Use(WOLFSSL* ssl, word32 maxSz, int is_response)
     }
 
     extension->resp = is_response;
-    extension->val  = maxSz;
+    /* In QUIC, earlydata size is either 0 or 0xffffffff.
+     * Override any size between, possibly left from our intial value */
+    extension->val  = (WOLFSSL_IS_QUIC(ssl) && is_response && maxSz > 0) ?
+                       WOLFSSL_MAX_32BIT : maxSz;
 
     return 0;
 }

+ 6 - 1
src/tls13.c

@@ -12608,7 +12608,12 @@ int wolfSSL_read_early_data(WOLFSSL* ssl, void* data, int sz, int* outSz)
     if (ssl->options.handShakeState == NULL_STATE) {
         if (ssl->error != WC_PENDING_E)
             ssl->earlyData = expecting_early_data;
-        ret = wolfSSL_accept_TLSv13(ssl);
+        /* this used to be: ret = wolfSSL_accept_TLSv13(ssl);
+         * However, wolfSSL_accept_TLSv13() expects a certificate to
+         * be installed already, which is not the case in servers
+         * such as HAProxy. They do it after inspecting the ClientHello.
+         * The common wolfssl_accept() allows that. */
+        ret = wolfSSL_accept(ssl);
         if (ret <= 0)
             return WOLFSSL_FATAL_ERROR;
     }

+ 6 - 6
wolfcrypt/src/evp.c

@@ -1902,8 +1902,8 @@ int wolfSSL_EVP_PKEY_CTX_set_hkdf_md(WOLFSSL_EVP_PKEY_CTX* ctx,
     return ret;
 }
 
-int wolfSSL_EVP_PKEY_CTX_set1_hkdf_salt(WOLFSSL_EVP_PKEY_CTX* ctx, byte* salt,
-                                        int saltSz)
+int wolfSSL_EVP_PKEY_CTX_set1_hkdf_salt(WOLFSSL_EVP_PKEY_CTX* ctx,
+                                        const byte* salt, int saltSz)
 {
     int ret = WOLFSSL_SUCCESS;
 
@@ -1938,8 +1938,8 @@ int wolfSSL_EVP_PKEY_CTX_set1_hkdf_salt(WOLFSSL_EVP_PKEY_CTX* ctx, byte* salt,
     return ret;
 }
 
-int wolfSSL_EVP_PKEY_CTX_set1_hkdf_key(WOLFSSL_EVP_PKEY_CTX* ctx, byte* key,
-                                       int keySz)
+int wolfSSL_EVP_PKEY_CTX_set1_hkdf_key(WOLFSSL_EVP_PKEY_CTX* ctx,
+                                       const byte* key, int keySz)
 {
     int ret = WOLFSSL_SUCCESS;
 
@@ -1974,8 +1974,8 @@ int wolfSSL_EVP_PKEY_CTX_set1_hkdf_key(WOLFSSL_EVP_PKEY_CTX* ctx, byte* key,
     return ret;
 }
 
-int wolfSSL_EVP_PKEY_CTX_add1_hkdf_info(WOLFSSL_EVP_PKEY_CTX* ctx, byte* info,
-                                        int infoSz)
+int wolfSSL_EVP_PKEY_CTX_add1_hkdf_info(WOLFSSL_EVP_PKEY_CTX* ctx,
+                                        const byte* info, int infoSz)
 {
     int ret = WOLFSSL_SUCCESS;
 

+ 1 - 0
wolfssl/error-ssl.h

@@ -180,6 +180,7 @@ enum wolfSSL_ErrorCodes {
     DILITHIUM_KEY_SIZE_E         = -453,   /* Wrong key size for Dilithium. */
     DTLS_CID_ERROR               = -454,   /* Wrong or missing CID */
     DTLS_TOO_MANY_FRAGMENTS_E    = -455,   /* Received too many fragments */
+    QUIC_WRONG_ENC_LEVEL         = -456,   /* QUIC data received on wrong encryption level */
     /* add strings to wolfSSL_ERR_reason_error_string in internal.c !!!!! */
 
     /* begin negotiation parameter errors */

+ 5 - 0
wolfssl/internal.h

@@ -5987,6 +5987,11 @@ WOLFSSL_LOCAL int wolfSSL_RSA_To_Der(WOLFSSL_RSA* rsa, byte** outBuf,
     int publicKey, void* heap);
 #endif
 
+#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) \
+    || defined(OPENSSL_EXTRA) || defined(HAVE_LIGHTY) || defined(HAVE_SECRET_CALLBACK)
+WOLFSSL_LOCAL int wolfSSL_SSL_do_handshake_internal(WOLFSSL *s);
+#endif
+
 #ifdef WOLFSSL_QUIC
 #define WOLFSSL_IS_QUIC(s)  (((s) != NULL) && ((s)->quic.method != NULL))
 WOLFSSL_LOCAL int wolfSSL_quic_receive(WOLFSSL* ssl, byte* buf, word32 sz);

+ 5 - 3
wolfssl/openssl/evp.h

@@ -751,11 +751,13 @@ WOLFSSL_API void wolfSSL_EVP_MD_do_all(void (*fn) (const WOLFSSL_EVP_MD *md,
 WOLFSSL_API int wolfSSL_EVP_PKEY_CTX_set_hkdf_md(WOLFSSL_EVP_PKEY_CTX* ctx,
                                                  const WOLFSSL_EVP_MD* md);
 WOLFSSL_API int wolfSSL_EVP_PKEY_CTX_set1_hkdf_salt(WOLFSSL_EVP_PKEY_CTX* ctx,
-                                                    byte* salt, int saltSz);
+                                                    const byte* salt,
+                                                    int saltSz);
 WOLFSSL_API int wolfSSL_EVP_PKEY_CTX_set1_hkdf_key(WOLFSSL_EVP_PKEY_CTX* ctx,
-                                                   byte* key, int keySz);
+                                                   const byte* key, int keySz);
 WOLFSSL_API int wolfSSL_EVP_PKEY_CTX_add1_hkdf_info(WOLFSSL_EVP_PKEY_CTX* ctx,
-                                                    byte* info, int infoSz);
+                                                    const byte* info,
+                                                    int infoSz);
 WOLFSSL_API int wolfSSL_EVP_PKEY_CTX_hkdf_mode(WOLFSSL_EVP_PKEY_CTX* ctx,
                                                int mode);
 #endif

+ 66 - 2
wolfssl/openssl/ssl.h

@@ -1306,8 +1306,8 @@ typedef WOLFSSL_SRTP_PROTECTION_PROFILE      SRTP_PROTECTION_PROFILE;
 #define SSL_CONF_TYPE_FILE               WOLFSSL_CONF_TYPE_FILE
 #define SSL_CONF_TYPE_DIR                WOLFSSL_CONF_TYPE_DIR
 
-#if defined(HAVE_STUNNEL) || defined(WOLFSSL_NGINX) || defined(OPENSSL_EXTRA) \
-                                                         || defined(OPENSSL_ALL)
+#if defined(HAVE_STUNNEL) || defined(WOLFSSL_NGINX) || \
+    defined(WOLFSSL_HAPROXY) || defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)
 
 #define SSL23_ST_SR_CLNT_HELLO_A        (0x210|0x2000)
 #define SSL3_ST_SR_CLNT_HELLO_A         (0x110|0x2000)
@@ -1317,6 +1317,8 @@ typedef WOLFSSL_SRTP_PROTECTION_PROFILE      SRTP_PROTECTION_PROFILE;
 #define SSL_AD_UNRECOGNIZED_NAME         unrecognized_name
 #define SSL_AD_NO_RENEGOTIATION          no_renegotiation
 #define SSL_AD_INTERNAL_ERROR            80
+#define SSL_AD_NO_APPLICATION_PROTOCOL   no_application_protocol
+#define SSL_AD_MISSING_EXTENSION         missing_extension
 
 #define ASN1_STRFLGS_ESC_MSB             4
 
@@ -1639,6 +1641,68 @@ typedef WOLFSSL_CONF_CTX SSL_CONF_CTX;
 
 #endif /* OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */
 
+
+#ifdef WOLFSSL_QUIC
+
+#include <wolfssl/quic.h>
+
+/* Used by Chromium/QUIC - according to quictls/openssl fork */
+#define X25519_PRIVATE_KEY_LEN          32
+#define X25519_PUBLIC_VALUE_LEN         32
+
+/* TLSv1.3 cipher ids as defined in RFC 8446, returned by
+ * SSL_CIPHER_get_id(cipher)
+ * used by QUIC implementations, such as HAProxy
+ */
+#define TLS1_3_CK_AES_128_GCM_SHA256       0x1301
+#define TLS1_3_CK_AES_256_GCM_SHA384       0x1302
+#define TLS1_3_CK_CHACHA20_POLY1305_SHA256 0x1303
+#define TLS1_3_CK_AES_128_CCM_SHA256       0x1304
+#define TLS1_3_CK_AES_128_CCM_8_SHA256     0x1305
+
+#define SSL_R_MISSING_QUIC_TRANSPORT_PARAMETERS_EXTENSION   QUIC_TP_MISSING_E
+#define SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED               QUIC_WRONG_ENC_LEVEL
+
+#define ssl_quic_method_st              wolfssl_quic_method_t
+typedef WOLFSSL_QUIC_METHOD             SSL_QUIC_METHOD;
+
+#define ssl_encryption_level_t          wolfssl_encryption_level_t
+typedef WOLFSSL_ENCRYPTION_LEVEL        OSSL_ENCRYPTION_LEVEL;
+#define ssl_encryption_initial          wolfssl_encryption_initial
+#define ssl_encryption_early_data       wolfssl_encryption_early_data
+#define ssl_encryption_handshake        wolfssl_encryption_handshake
+#define ssl_encryption_application      wolfssl_encryption_application
+
+#define SSL_CTX_set_quic_method         wolfSSL_CTX_set_quic_method
+#define SSL_set_quic_method             wolfSSL_set_quic_method
+
+#define SSL_set_quic_transport_params   wolfSSL_set_quic_transport_params
+#define SSL_get_peer_quic_transport_params  wolfSSL_get_peer_quic_transport_params
+
+#define SSL_quic_max_handshake_flight_len   wolfSSL_quic_max_handshake_flight_len
+#define SSL_quic_read_level             wolfSSL_quic_read_level
+#define SSL_quic_write_level            wolfSSL_quic_write_level
+#define SSL_provide_quic_data           wolfSSL_provide_quic_data
+#define SSL_process_quic_post_handshake     wolfSSL_process_quic_post_handshake
+
+#define SSL_is_quic                     wolfSSL_is_quic
+
+#define SSL_set_quic_transport_version      wolfSSL_set_quic_transport_version
+#define SSL_get_quic_transport_version      wolfSSL_get_quic_transport_version
+#define SSL_get_peer_quic_transport_version wolfSSL_get_peer_quic_transport_version
+
+#define SSL_set_quic_early_data_enabled     wolfSSL_set_quic_early_data_enabled
+
+/* BoringSSL API - according to quictls/openssl fork */
+#define SSL_set_quic_use_legacy_codepoint   wolfSSL_set_quic_use_legacy_codepoint
+
+/* TODO: we do not have this in our QUIC api and HAProxy does not use it
+int SSL_CIPHER_get_prf_nid(const SSL_CIPHER *c);
+*/
+
+#endif /* WOLFSSL_QUIC */
+
+
 #ifdef __cplusplus
     } /* extern "C" */
 #endif

+ 6 - 0
wolfssl/openssl/tls1.h

@@ -43,4 +43,10 @@
 #define TLS_MAX_VERSION                 TLS1_3_VERSION
 #endif
 
+#ifdef WOLFSSL_QUIC
+/* from rfc9001 */
+#define TLSEXT_TYPE_quic_transport_parameters_draft   0xffa5
+#define TLSEXT_TYPE_quic_transport_parameters         0x0039
+#endif
+
 #endif /* WOLFSSL_OPENSSL_TLS1_H_ */

+ 1 - 0
wolfssl/wolfcrypt/types.h

@@ -243,6 +243,7 @@ typedef struct w64wrapper {
     };
 
     #define WOLFSSL_MAX_16BIT 0xffffU
+    #define WOLFSSL_MAX_32BIT 0xffffffffU
 
     /* use inlining if compiler allows */
     #ifndef WC_INLINE