From: William Lallemand Date: Thu, 25 Jul 2024 08:54:10 +0000 (+0200) Subject: MEDIUM: quic: implement CHACHA20_POLY1305 for AWS-LC X-Git-Tag: v3.1-dev5~84 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=28cb01f8e8580e5c3936b227e897b64a9e4c5c7e;p=thirdparty%2Fhaproxy.git MEDIUM: quic: implement CHACHA20_POLY1305 for AWS-LC With AWS-LC, the aead part is covered by the EVP_AEAD API which provides the correct EVP_aead_chacha20_poly1305(), however for header protection it does not provides an EVP_CIPHER for chacha20. This patch implements exceptions in the header protection code and use EVP_CIPHER_CHACHA20 and EVP_CIPHER_CTX_CHACHA20 placeholders so we can use the CRYPTO_chacha_20() primitive manually instead of the EVP_CIPHER API. This requires to check if we are using EVP_CIPHER_CTX_CHACHA20 when doing EVP_CIPHER_CTX_free(). --- diff --git a/include/haproxy/quic_tls-t.h b/include/haproxy/quic_tls-t.h index b6e3286c85..9eb97529eb 100644 --- a/include/haproxy/quic_tls-t.h +++ b/include/haproxy/quic_tls-t.h @@ -17,6 +17,9 @@ #error "Must define USE_OPENSSL" #endif +#if defined(USE_OPENSSL_AWSLC) +#include +#endif #include #include @@ -38,6 +41,9 @@ # define QUIC_AEAD_key_length EVP_AEAD_key_length # define QUIC_AEAD_iv_length EVP_AEAD_nonce_length +# define EVP_CIPHER_CTX_CHACHA20 ((EVP_CIPHER_CTX *)EVP_aead_chacha20_poly1305()) +# define EVP_CIPHER_CHACHA20 ((EVP_CIPHER*)EVP_aead_chacha20_poly1305()) + #else # define QUIC_AEAD EVP_CIPHER diff --git a/include/haproxy/quic_tls.h b/include/haproxy/quic_tls.h index 9cc8f10f1a..757b78fb46 100644 --- a/include/haproxy/quic_tls.h +++ b/include/haproxy/quic_tls.h @@ -150,7 +150,7 @@ static inline const QUIC_AEAD *tls_aead(const SSL_CIPHER *cipher) return EVP_aes_256_gcm(); #endif -#if !defined(OPENSSL_IS_AWSLC) && (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x4000000fL) +#if (!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 * into next major version. Given that on machines without AES-NI @@ -160,7 +160,11 @@ static inline const QUIC_AEAD *tls_aead(const SSL_CIPHER *cipher) * Thanks to Theo Buehler for his help! */ case TLS1_3_CK_CHACHA20_POLY1305_SHA256: +# ifdef QUIC_AEAD_API + return EVP_aead_chacha20_poly1305(); +# else return EVP_chacha20_poly1305(); +# endif #endif #if !defined(USE_OPENSSL_WOLFSSL) && !defined(OPENSSL_IS_AWSLC) case TLS1_3_CK_AES_128_CCM_SHA256: @@ -188,8 +192,10 @@ static inline const EVP_MD *tls_md(const SSL_CIPHER *cipher) static inline const EVP_CIPHER *tls_hp(const SSL_CIPHER *cipher) { switch (SSL_CIPHER_get_id(cipher)) { -#if !defined(OPENSSL_IS_AWSLC) case TLS1_3_CK_CHACHA20_POLY1305_SHA256: +#ifdef QUIC_AEAD_API + return EVP_CIPHER_CHACHA20; +#else return EVP_chacha20(); #endif case TLS1_3_CK_AES_128_CCM_SHA256: @@ -754,14 +760,24 @@ static inline void quic_tls_ctx_secs_free(struct quic_tls_ctx *ctx) } /* RX HP protection */ +#ifdef QUIC_AEAD_API + if (ctx->rx.hp_ctx != EVP_CIPHER_CTX_CHACHA20) + EVP_CIPHER_CTX_free(ctx->rx.hp_ctx); +#else EVP_CIPHER_CTX_free(ctx->rx.hp_ctx); +#endif /* RX AEAD decryption */ 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 */ +#ifdef QUIC_AEAD_API + if (ctx->tx.hp_ctx != EVP_CIPHER_CTX_CHACHA20) + EVP_CIPHER_CTX_free(ctx->tx.hp_ctx); +#else EVP_CIPHER_CTX_free(ctx->tx.hp_ctx); +#endif /* TX AEAD encryption */ QUIC_AEAD_CTX_free(ctx->tx.ctx); pool_free(pool_head_quic_tls_iv, ctx->tx.iv); diff --git a/src/quic_tls.c b/src/quic_tls.c index 566584f789..5174c2dc9e 100644 --- a/src/quic_tls.c +++ b/src/quic_tls.c @@ -454,7 +454,16 @@ int quic_tls_derive_keys(const QUIC_AEAD *aead, const EVP_CIPHER *hp, { size_t aead_keylen = (size_t)QUIC_AEAD_key_length(aead); size_t aead_ivlen = (size_t)QUIC_AEAD_iv_length(aead); +#ifdef QUIC_AEAD_API + size_t hp_len = 0; + + if (hp == EVP_CIPHER_CHACHA20) + hp_len = 32; + else if (hp) + hp_len = (size_t)EVP_CIPHER_key_length(hp); +#else size_t hp_len = hp ? (size_t)EVP_CIPHER_key_length(hp) : 0; +#endif if (aead_keylen > keylen || aead_ivlen > ivlen || hp_len > hp_keylen) return 0; @@ -599,6 +608,14 @@ int quic_tls_enc_hp_ctx_init(EVP_CIPHER_CTX **hp_ctx, { EVP_CIPHER_CTX *ctx; +#ifdef QUIC_AEAD_API + + if (hp == EVP_CIPHER_CHACHA20) { + *hp_ctx = EVP_CIPHER_CTX_CHACHA20; + return 1; + } +#endif + ctx = EVP_CIPHER_CTX_new(); if (!ctx) return 0; @@ -625,6 +642,25 @@ int quic_tls_hp_encrypt(unsigned char *out, { int ret = 0; +#ifdef QUIC_AEAD_API + + if (ctx == EVP_CIPHER_CTX_CHACHA20) { + uint32_t counter; + /* According to RFC 9001, 5.4.4. ChaCha20-Based Header Protection: + * The first 4 bytes of the sampled ciphertext are the block counter. + * The remaining 12 bytes are used as the nonce. + */ +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + counter = (uint32_t)in[0] + (uint32_t)(in[1] << 8) + (uint32_t)(in[2] << 16) + (uint32_t)(in[3] << 24); +#else + memcpy(&counter, in, sizeof(counter)); +#endif + CRYPTO_chacha_20(out, out, inlen, key, in + sizeof(counter), counter); + return 1; + } + +#endif + if (!EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, in) || !EVP_EncryptUpdate(ctx, out, &ret, out, inlen) || !EVP_EncryptFinal_ex(ctx, out, &ret)) @@ -639,6 +675,14 @@ int quic_tls_dec_hp_ctx_init(EVP_CIPHER_CTX **hp_ctx, { EVP_CIPHER_CTX *ctx; +#ifdef QUIC_AEAD_API + + if (hp == EVP_CIPHER_CHACHA20) { + *hp_ctx = EVP_CIPHER_CTX_CHACHA20; + return 1; + } +#endif + ctx = EVP_CIPHER_CTX_new(); if (!ctx) return 0; @@ -665,6 +709,25 @@ int quic_tls_hp_decrypt(unsigned char *out, { int ret = 0; +#ifdef QUIC_AEAD_API + if (ctx == EVP_CIPHER_CTX_CHACHA20) { + uint32_t counter; + + /* According to RFC 9001, 5.4.4. ChaCha20-Based Header Protection: + * The first 4 bytes of the sampled ciphertext are the block counter. + * The remaining 12 bytes are used as the nonce. + */ +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + counter = (uint32_t)in[0] + (uint32_t)(in[1] << 8) + (uint32_t)(in[2] << 16) + (uint32_t)(in[3] << 24); +#else + memcpy(&counter, in, sizeof(counter)); +#endif + CRYPTO_chacha_20(out, out, inlen, key, in + sizeof(counter), counter); + return 1; + } + +#endif + if (!EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, in) || !EVP_DecryptUpdate(ctx, out, &ret, out, inlen) || !EVP_DecryptFinal_ex(ctx, out, &ret))