From: William Lallemand Date: Wed, 10 Jul 2024 08:28:27 +0000 (+0200) Subject: MEDIUM: ssl/quic: implement quic crypto with EVP_AEAD X-Git-Tag: v3.1-dev5~87 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=31c831e29b432f0a9958be63948e8f4cb278e9f8;p=thirdparty%2Fhaproxy.git MEDIUM: ssl/quic: implement quic crypto with EVP_AEAD The QUIC crypto is using the EVP_CIPHER API in order to achieve authenticated encryption, this was the API which was used with OpenSSL. With libraries that inspires from BoringSSL (libreSSL and AWS-LC), the AEAD algorithms are implemented using the EVP_AEAD API. This patch converts the call to the EVP_CIPHER API when called in the contex of AEAD cryptography for QUIC. The patch defines some QUIC_AEAD macros that can be either EVP_CIPHER or EVP_AEAD depending on the library. This was mainly done for AWS-LC but this could be useful for other libraries. This should finally allow to use CHACHA20_POLY1305 with AWS-LC. This patch allows to use the following ciphers with the EVP_AEAD API: - TLS1_3_CK_AES_128_GCM_SHA256 - TLS1_3_CK_AES_256_GCM_SHA384 AWS-LC does not implement TLS1_3_CK_AES_128_CCM_SHA256 and TLS1_3_CK_CHACHA20_POLY1305_SHA256 requires some hack for headers protection which will come in another patch. --- diff --git a/include/haproxy/quic_tls-t.h b/include/haproxy/quic_tls-t.h index 7f90d9a54b..b6e3286c85 100644 --- a/include/haproxy/quic_tls-t.h +++ b/include/haproxy/quic_tls-t.h @@ -26,6 +26,30 @@ #include #include +/* Use EVP_CIPHER or EVP_AEAD API depending on the library */ +#if defined(USE_OPENSSL_AWSLC) + +# define QUIC_AEAD_API + +# define QUIC_AEAD EVP_AEAD +# define QUIC_AEAD_CTX EVP_AEAD_CTX + +# define QUIC_AEAD_CTX_free EVP_AEAD_CTX_free +# define QUIC_AEAD_key_length EVP_AEAD_key_length +# define QUIC_AEAD_iv_length EVP_AEAD_nonce_length + +#else + +# define QUIC_AEAD EVP_CIPHER +# define QUIC_AEAD_CTX EVP_CIPHER_CTX + +# define QUIC_AEAD_CTX_free EVP_CIPHER_CTX_free +# define QUIC_AEAD_key_length EVP_CIPHER_key_length +# define QUIC_AEAD_iv_length EVP_CIPHER_iv_length + +#endif + + /* It seems TLS 1.3 ciphersuites macros differ between openssl and boringssl */ #if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) @@ -162,7 +186,7 @@ struct quic_pktns { /* Key phase used for Key Update */ struct quic_tls_kp { - EVP_CIPHER_CTX *ctx; + QUIC_AEAD_CTX *ctx; unsigned char *secret; size_t secretlen; unsigned char *iv; @@ -178,8 +202,8 @@ struct quic_tls_kp { #define QUIC_FL_TLS_KP_BIT_SET (1 << 0) struct quic_tls_secrets { - EVP_CIPHER_CTX *ctx; - const EVP_CIPHER *aead; + QUIC_AEAD_CTX *ctx; + const QUIC_AEAD *aead; const EVP_MD *md; EVP_CIPHER_CTX *hp_ctx; const EVP_CIPHER *hp; diff --git a/include/haproxy/quic_tls.h b/include/haproxy/quic_tls.h index 67c25aad2f..69194148d1 100644 --- a/include/haproxy/quic_tls.h +++ b/include/haproxy/quic_tls.h @@ -59,25 +59,25 @@ int quic_tls_derive_initial_secrets(const EVP_MD *md, int quic_tls_encrypt(unsigned char *buf, size_t len, const unsigned char *aad, size_t aad_len, - EVP_CIPHER_CTX *ctx, const EVP_CIPHER *aead, + QUIC_AEAD_CTX *ctx, const QUIC_AEAD *aead, const unsigned char *iv); int quic_tls_decrypt2(unsigned char *out, unsigned char *in, size_t ilen, unsigned char *aad, size_t aad_len, - EVP_CIPHER_CTX *ctx, const EVP_CIPHER *aead, + QUIC_AEAD_CTX *ctx, const QUIC_AEAD *aead, const unsigned char *key, const unsigned char *iv); int quic_tls_decrypt(unsigned char *buf, size_t len, unsigned char *aad, size_t aad_len, - EVP_CIPHER_CTX *tls_ctx, const EVP_CIPHER *aead, + QUIC_AEAD_CTX *tls_ctx, const QUIC_AEAD *aead, const unsigned char *key, const unsigned char *iv); int quic_tls_generate_retry_integrity_tag(unsigned char *odcid, unsigned char odcid_len, unsigned char *buf, size_t len, const struct quic_version *qv); -int quic_tls_derive_keys(const EVP_CIPHER *aead, const EVP_CIPHER *hp, +int quic_tls_derive_keys(const QUIC_AEAD *aead, const EVP_CIPHER *hp, const EVP_MD *md, const struct quic_version *qv, unsigned char *key, size_t keylen, unsigned char *iv, size_t ivlen, @@ -106,10 +106,10 @@ int quic_hkdf_extract_and_expand(const EVP_MD *md, const unsigned char *salt, size_t saltlen, const unsigned char *label, size_t labellen); -int quic_tls_rx_ctx_init(EVP_CIPHER_CTX **rx_ctx, - const EVP_CIPHER *aead, unsigned char *key); -int quic_tls_tx_ctx_init(EVP_CIPHER_CTX **tx_ctx, - const EVP_CIPHER *aead, unsigned char *key); +int quic_tls_rx_ctx_init(QUIC_AEAD_CTX **rx_ctx, + const QUIC_AEAD *aead, unsigned char *key); +int quic_tls_tx_ctx_init(QUIC_AEAD_CTX **tx_ctx, + const QUIC_AEAD *aead, unsigned char *key); int quic_tls_sec_update(const EVP_MD *md, const struct quic_version *qv, unsigned char *new_sec, size_t new_seclen, @@ -133,13 +133,23 @@ int quic_tls_aes_encrypt(unsigned char *out, int quic_tls_key_update(struct quic_conn *qc); void quic_tls_rotate_keys(struct quic_conn *qc); -static inline const EVP_CIPHER *tls_aead(const SSL_CIPHER *cipher) +static inline const QUIC_AEAD *tls_aead(const SSL_CIPHER *cipher) { switch (SSL_CIPHER_get_id(cipher)) { case TLS1_3_CK_AES_128_GCM_SHA256: +#ifdef QUIC_AEAD_API + return EVP_aead_aes_128_gcm(); +#else return EVP_aes_128_gcm(); +#endif + case TLS1_3_CK_AES_256_GCM_SHA384: +#ifdef QUIC_AEAD_API + return EVP_aead_aes_256_gcm(); +#else return EVP_aes_256_gcm(); +#endif + #if !defined(OPENSSL_IS_AWSLC) && (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x4000000fL) /* WT: LibreSSL has an issue with CHACHA20 running in-place till 3.9.2 * included, but the fix is already identified and will be merged @@ -746,14 +756,14 @@ static inline void quic_tls_ctx_secs_free(struct quic_tls_ctx *ctx) /* RX HP protection */ EVP_CIPHER_CTX_free(ctx->rx.hp_ctx); /* RX AEAD decryption */ - EVP_CIPHER_CTX_free(ctx->rx.ctx); + QUIC_AEAD_CTX_free(ctx->rx.ctx); pool_free(pool_head_quic_tls_iv, ctx->rx.iv); pool_free(pool_head_quic_tls_key, ctx->rx.key); /* TX HP protection */ EVP_CIPHER_CTX_free(ctx->tx.hp_ctx); /* TX AEAD encryption */ - EVP_CIPHER_CTX_free(ctx->tx.ctx); + QUIC_AEAD_CTX_free(ctx->tx.ctx); pool_free(pool_head_quic_tls_iv, ctx->tx.iv); pool_free(pool_head_quic_tls_key, ctx->tx.key); @@ -806,7 +816,7 @@ static inline void quic_tls_secrets_keys_free(struct quic_tls_secrets *secs) /* HP protection */ EVP_CIPHER_CTX_free(secs->hp_ctx); /* AEAD decryption */ - EVP_CIPHER_CTX_free(secs->ctx); + QUIC_AEAD_CTX_free(secs->ctx); pool_free(pool_head_quic_tls_iv, secs->iv); pool_free(pool_head_quic_tls_key, secs->key); @@ -845,7 +855,11 @@ static inline void quic_nictx_free(struct quic_conn *qc) /* Initialize a TLS cryptographic context for the Initial encryption level. */ static inline int quic_initial_tls_ctx_init(struct quic_tls_ctx *ctx) { +#ifdef QUIC_AEAD_API + ctx->rx.aead = ctx->tx.aead = EVP_aead_aes_128_gcm(); +#else ctx->rx.aead = ctx->tx.aead = EVP_aes_128_gcm(); +#endif ctx->rx.md = ctx->tx.md = EVP_sha256(); ctx->rx.hp = ctx->tx.hp = EVP_aes_128_ctr(); @@ -985,17 +999,17 @@ static inline void quic_tls_ku_reset(struct quic_tls_kp *tls_kp) */ static inline void quic_tls_ku_free(struct quic_conn *qc) { - EVP_CIPHER_CTX_free(qc->ku.prv_rx.ctx); + QUIC_AEAD_CTX_free(qc->ku.prv_rx.ctx); pool_free(pool_head_quic_tls_secret, qc->ku.prv_rx.secret); pool_free(pool_head_quic_tls_iv, qc->ku.prv_rx.iv); pool_free(pool_head_quic_tls_key, qc->ku.prv_rx.key); quic_tls_ku_reset(&qc->ku.prv_rx); - EVP_CIPHER_CTX_free(qc->ku.nxt_rx.ctx); + QUIC_AEAD_CTX_free(qc->ku.nxt_rx.ctx); pool_free(pool_head_quic_tls_secret, qc->ku.nxt_rx.secret); pool_free(pool_head_quic_tls_iv, qc->ku.nxt_rx.iv); pool_free(pool_head_quic_tls_key, qc->ku.nxt_rx.key); quic_tls_ku_reset(&qc->ku.nxt_rx); - EVP_CIPHER_CTX_free(qc->ku.nxt_tx.ctx); + QUIC_AEAD_CTX_free(qc->ku.nxt_tx.ctx); pool_free(pool_head_quic_tls_secret, qc->ku.nxt_tx.secret); pool_free(pool_head_quic_tls_iv, qc->ku.nxt_tx.iv); pool_free(pool_head_quic_tls_key, qc->ku.nxt_tx.key); diff --git a/src/quic_retry.c b/src/quic_retry.c index f1d55b8781..2d6ea31ac4 100644 --- a/src/quic_retry.c +++ b/src/quic_retry.c @@ -92,8 +92,12 @@ int quic_generate_retry_token(unsigned char *token, size_t len, unsigned char iv[QUIC_TLS_IV_LEN]; const unsigned char *sec = global.cluster_secret; size_t seclen = sizeof global.cluster_secret; - EVP_CIPHER_CTX *ctx = NULL; - const EVP_CIPHER *aead = EVP_aes_128_gcm(); + QUIC_AEAD_CTX *ctx = NULL; +#ifdef QUIC_AEAD_API + const QUIC_AEAD *aead = EVP_aead_aes_128_gcm(); +#else + const QUIC_AEAD *aead = EVP_aes_128_gcm(); +#endif uint32_t timestamp = (uint32_t)date.tv_sec; TRACE_ENTER(QUIC_EV_CONN_TXPKT); @@ -141,7 +145,7 @@ int quic_generate_retry_token(unsigned char *token, size_t len, p += QUIC_TLS_TAG_LEN; memcpy(p, salt, sizeof salt); p += sizeof salt; - EVP_CIPHER_CTX_free(ctx); + QUIC_AEAD_CTX_free(ctx); ret = p - token; leave: @@ -150,7 +154,7 @@ int quic_generate_retry_token(unsigned char *token, size_t len, err: if (ctx) - EVP_CIPHER_CTX_free(ctx); + QUIC_AEAD_CTX_free(ctx); goto leave; } @@ -242,10 +246,14 @@ int quic_retry_token_check(struct quic_rx_packet *pkt, unsigned char iv[QUIC_TLS_IV_LEN]; const unsigned char *sec = global.cluster_secret; size_t seclen = sizeof global.cluster_secret; - EVP_CIPHER_CTX *ctx = NULL; - const EVP_CIPHER *aead = EVP_aes_128_gcm(); + QUIC_AEAD_CTX *ctx = NULL; const struct quic_version *qv = qc ? qc->original_version : pkt->version; +#ifdef QUIC_AEAD_API + const QUIC_AEAD *aead = EVP_aead_aes_128_gcm(); +#else + const QUIC_AEAD *aead = EVP_aes_128_gcm(); +#endif TRACE_ENTER(QUIC_EV_CONN_LPKT, qc); @@ -301,7 +309,7 @@ int quic_retry_token_check(struct quic_rx_packet *pkt, goto err; } - EVP_CIPHER_CTX_free(ctx); + QUIC_AEAD_CTX_free(ctx); ret = 1; HA_ATOMIC_INC(&prx_counters->retry_validated); @@ -313,7 +321,7 @@ int quic_retry_token_check(struct quic_rx_packet *pkt, err: HA_ATOMIC_INC(&prx_counters->retry_error); if (ctx) - EVP_CIPHER_CTX_free(ctx); + QUIC_AEAD_CTX_free(ctx); goto leave; } diff --git a/src/quic_rx.c b/src/quic_rx.c index 036e5d30f2..083454272c 100644 --- a/src/quic_rx.c +++ b/src/quic_rx.c @@ -126,7 +126,7 @@ static int qc_pkt_decrypt(struct quic_conn *qc, struct quic_enc_level *qel, unsigned char iv[QUIC_TLS_IV_LEN]; struct quic_tls_ctx *tls_ctx = qc_select_tls_ctx(qc, qel, pkt->type, pkt->version); - EVP_CIPHER_CTX *rx_ctx = tls_ctx->rx.ctx; + QUIC_AEAD_CTX *rx_ctx = tls_ctx->rx.ctx; unsigned char *rx_iv = tls_ctx->rx.iv; size_t rx_iv_sz = tls_ctx->rx.ivlen; unsigned char *rx_key = tls_ctx->rx.key; diff --git a/src/quic_tls.c b/src/quic_tls.c index 885df6f68c..94d9e7f82b 100644 --- a/src/quic_tls.c +++ b/src/quic_tls.c @@ -57,9 +57,8 @@ void quic_tls_keys_hexdump(struct buffer *buf, if (!secs->aead || !secs->hp) return; - - aead_keylen = (size_t)EVP_CIPHER_key_length(secs->aead); - aead_ivlen = (size_t)EVP_CIPHER_iv_length(secs->aead); + aead_keylen = (size_t)QUIC_AEAD_key_length(secs->aead); + aead_ivlen = (size_t)QUIC_AEAD_iv_length(secs->aead); hp_len = (size_t)EVP_CIPHER_key_length(secs->hp); chunk_appendf(buf, "\n key="); @@ -446,15 +445,15 @@ int quic_hkdf_expand_label(const EVP_MD *md, * ->hp_key is the key to be derived for header protection. * Obviouly these keys have the same size becaused derived with the same TLS cryptographic context. */ -int quic_tls_derive_keys(const EVP_CIPHER *aead, const EVP_CIPHER *hp, +int quic_tls_derive_keys(const QUIC_AEAD *aead, const EVP_CIPHER *hp, const EVP_MD *md, const struct quic_version *qv, unsigned char *key, size_t keylen, unsigned char *iv, size_t ivlen, unsigned char *hp_key, size_t hp_keylen, const unsigned char *secret, size_t secretlen) { - size_t aead_keylen = (size_t)EVP_CIPHER_key_length(aead); - size_t aead_ivlen = (size_t)EVP_CIPHER_iv_length(aead); + size_t aead_keylen = (size_t)QUIC_AEAD_key_length(aead); + size_t aead_ivlen = (size_t)QUIC_AEAD_iv_length(aead); size_t hp_len = hp ? (size_t)EVP_CIPHER_key_length(hp) : 0; if (aead_keylen > keylen || aead_ivlen > ivlen || hp_len > hp_keylen) @@ -563,13 +562,18 @@ void quic_aead_iv_build(unsigned char *iv, size_t ivlen, /* Initialize the cipher context for RX part of QUIC TLS context. * Return 1 if succeeded, 0 if not. */ -int quic_tls_rx_ctx_init(EVP_CIPHER_CTX **rx_ctx, - const EVP_CIPHER *aead, unsigned char *key) +int quic_tls_rx_ctx_init(QUIC_AEAD_CTX **rx_ctx, + const QUIC_AEAD *aead, unsigned char *key) { - EVP_CIPHER_CTX *ctx; - int aead_nid = EVP_CIPHER_nid(aead); - ctx = EVP_CIPHER_CTX_new(); +#ifdef QUIC_AEAD_API + QUIC_AEAD_CTX *ctx = EVP_AEAD_CTX_new(aead, key, EVP_AEAD_key_length(aead), EVP_AEAD_DEFAULT_TAG_LENGTH); + if (!ctx) + return 0; + +#else + int aead_nid = EVP_CIPHER_nid(aead); + QUIC_AEAD_CTX *ctx = EVP_CIPHER_CTX_new(); if (!ctx) return 0; @@ -580,12 +584,12 @@ int quic_tls_rx_ctx_init(EVP_CIPHER_CTX **rx_ctx, !EVP_DecryptInit_ex(ctx, NULL, NULL, key, NULL)) goto err; +#endif *rx_ctx = ctx; - return 1; err: - EVP_CIPHER_CTX_free(ctx); + QUIC_AEAD_CTX_free(ctx); return 0; } @@ -672,13 +676,18 @@ int quic_tls_aes_decrypt(unsigned char *out, /* Initialize the cipher context for TX part of QUIC TLS context. * Return 1 if succeeded, 0 if not. */ -int quic_tls_tx_ctx_init(EVP_CIPHER_CTX **tx_ctx, - const EVP_CIPHER *aead, unsigned char *key) +int quic_tls_tx_ctx_init(QUIC_AEAD_CTX **tx_ctx, + const QUIC_AEAD *aead, unsigned char *key) { - EVP_CIPHER_CTX *ctx; +#ifdef QUIC_AEAD_API + QUIC_AEAD_CTX *ctx = EVP_AEAD_CTX_new(aead, key, EVP_AEAD_key_length(aead), EVP_AEAD_DEFAULT_TAG_LENGTH); + if (!ctx) + return 0; + +#else int aead_nid = EVP_CIPHER_nid(aead); + QUIC_AEAD_CTX *ctx = EVP_CIPHER_CTX_new(); - ctx = EVP_CIPHER_CTX_new(); if (!ctx) return 0; @@ -688,13 +697,13 @@ int quic_tls_tx_ctx_init(EVP_CIPHER_CTX **tx_ctx, !EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, QUIC_TLS_TAG_LEN, NULL)) || !EVP_EncryptInit_ex(ctx, NULL, NULL, key, NULL)) goto err; +#endif *tx_ctx = ctx; - return 1; err: - EVP_CIPHER_CTX_free(ctx); + QUIC_AEAD_CTX_free(ctx); return 0; } @@ -740,9 +749,18 @@ int quic_tls_tx_ctx_init(EVP_CIPHER_CTX **tx_ctx, */ int quic_tls_encrypt(unsigned char *buf, size_t len, const unsigned char *aad, size_t aad_len, - EVP_CIPHER_CTX *ctx, const EVP_CIPHER *aead, + QUIC_AEAD_CTX *ctx, const QUIC_AEAD *aead, const unsigned char *iv) { +#ifdef QUIC_AEAD_API + size_t outlen; + + if (!EVP_AEAD_CTX_seal(ctx, buf, &outlen, len + EVP_AEAD_max_overhead(aead), + iv, QUIC_TLS_IV_LEN, + buf, len, + aad, aad_len)) + return 0; +#else int outlen; int aead_nid = EVP_CIPHER_nid(aead); @@ -755,6 +773,9 @@ int quic_tls_encrypt(unsigned char *buf, size_t len, !EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, QUIC_TLS_TAG_LEN, buf + len)) return 0; + +#endif + return 1; } @@ -769,9 +790,20 @@ int quic_tls_encrypt(unsigned char *buf, size_t len, */ int quic_tls_decrypt(unsigned char *buf, size_t len, unsigned char *aad, size_t aad_len, - EVP_CIPHER_CTX *ctx, const EVP_CIPHER *aead, + QUIC_AEAD_CTX *ctx, const QUIC_AEAD *aead, const unsigned char *key, const unsigned char *iv) { +#ifdef QUIC_AEAD_API + size_t outlen; + + if (!EVP_AEAD_CTX_open(ctx, buf, &outlen, len, + iv, QUIC_TLS_IV_LEN, + buf, len, + aad, aad_len)) + return 0; + +#else + int outlen; int aead_nid = EVP_CIPHER_nid(aead); @@ -786,6 +818,8 @@ int quic_tls_decrypt(unsigned char *buf, size_t len, !EVP_DecryptFinal_ex(ctx, buf + outlen, &outlen))) return 0; +#endif + return 1; } @@ -805,9 +839,20 @@ int quic_tls_decrypt(unsigned char *buf, size_t len, int quic_tls_decrypt2(unsigned char *out, unsigned char *in, size_t len, unsigned char *aad, size_t aad_len, - EVP_CIPHER_CTX *ctx, const EVP_CIPHER *aead, + QUIC_AEAD_CTX *ctx, const QUIC_AEAD *aead, const unsigned char *key, const unsigned char *iv) { +#ifdef QUIC_AEAD_API + size_t outlen; + + if (!EVP_AEAD_CTX_open(ctx, out, &outlen, len, + iv, QUIC_TLS_IV_LEN, + in, len, + aad, aad_len)) + return 0; + +#else + int outlen; int aead_nid = EVP_CIPHER_nid(aead); @@ -821,6 +866,7 @@ int quic_tls_decrypt2(unsigned char *out, (aead_nid != NID_aes_128_ccm && !EVP_DecryptFinal_ex(ctx, out + outlen, &outlen))) return 0; +#endif return 1; } @@ -961,7 +1007,7 @@ int quic_tls_key_update(struct quic_conn *qc) kp_trace.tx = nxt_tx; if (nxt_rx->ctx) { - EVP_CIPHER_CTX_free(nxt_rx->ctx); + QUIC_AEAD_CTX_free(nxt_rx->ctx); nxt_rx->ctx = NULL; } @@ -971,7 +1017,7 @@ int quic_tls_key_update(struct quic_conn *qc) } if (nxt_tx->ctx) { - EVP_CIPHER_CTX_free(nxt_tx->ctx); + QUIC_AEAD_CTX_free(nxt_tx->ctx); nxt_tx->ctx = NULL; } @@ -995,7 +1041,7 @@ void quic_tls_rotate_keys(struct quic_conn *qc) { struct quic_tls_ctx *tls_ctx = &qc->ael->tls_ctx; unsigned char *curr_secret, *curr_iv, *curr_key; - EVP_CIPHER_CTX *curr_ctx; + QUIC_AEAD_CTX *curr_ctx; TRACE_ENTER(QUIC_EV_CONN_RXPKT, qc);