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().
This commit is contained in:
William Lallemand 2024-07-25 10:54:10 +02:00
parent 177c84808c
commit 28cb01f8e8
3 changed files with 87 additions and 2 deletions

View 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

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

View 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))