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.
This commit is contained in:
William Lallemand 2024-07-10 10:28:27 +02:00
parent a6d40e09f7
commit 31c831e29b
5 changed files with 145 additions and 53 deletions

View File

@ -26,6 +26,30 @@
#include <haproxy/quic_ack-t.h>
#include <haproxy/openssl-compat.h>
/* 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;

View File

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

View File

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

View File

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

View File

@ -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 <tls_ctx> 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 <tls_ctx> 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;
int aead_nid = EVP_CIPHER_nid(aead);
#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);