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.
This commit is contained in:
Stefan Eissing 2022-12-01 10:12:35 +01:00
parent 2d88fd3b75
commit e5cfd96609
13 changed files with 136 additions and 26 deletions

View File

@ -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.

View File

@ -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";

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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 */

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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_ */

View File

@ -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