]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: quic: implement CHACHA20_POLY1305 for AWS-LC
authorWilliam Lallemand <wlallemand@haproxy.com>
Thu, 25 Jul 2024 08:54:10 +0000 (10:54 +0200)
committerWilliam Lallemand <wlallemand@haproxy.com>
Thu, 25 Jul 2024 11:45:39 +0000 (13:45 +0200)
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().

include/haproxy/quic_tls-t.h
include/haproxy/quic_tls.h
src/quic_tls.c

index b6e3286c853203e830224e1d5e4e4727cc5ff7ff..9eb97529eb5f63888855e7415ab87410d07640aa 100644 (file)
@@ -17,6 +17,9 @@
 #error "Must define USE_OPENSSL"
 #endif
 
+#if defined(USE_OPENSSL_AWSLC)
+#include <openssl/chacha.h>
+#endif
 #include <openssl/evp.h>
 
 #include <import/ebtree.h>
@@ -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
index 9cc8f10f1a6ff0a89d641e2b2d814315d1f259bf..757b78fb46d2e9659c20edc950594cd99119fb1e 100644 (file)
@@ -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);
index 566584f789f7cd0b842422b9eed30c87c646d285..5174c2dc9e64c0b68ee00c6540c3c2e609b94e14 100644 (file)
@@ -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))