MINOR: quic: Dynamically allocate the secrete keys

This is done for any encryption level. This is to prepare the Key Update feature.
This commit is contained in:
Frédéric Lécaille 2021-11-23 21:02:04 +01:00
parent d77c50b6d6
commit fc768ecc88
4 changed files with 125 additions and 32 deletions

View File

@ -37,10 +37,19 @@
#endif
#endif
/* AEAD iv and secrete key lengths */
#define QUIC_TLS_IV_LEN 12 /* bytes */
#define QUIC_TLS_KEY_LEN 32 /* bytes */
#define QUIC_TLS_SECRET_LEN 64 /* bytes */
/* The TLS extensions for QUIC transport parameters */
#define TLS_EXTENSION_QUIC_TRANSPORT_PARAMETERS 0x0039
#define TLS_EXTENSION_QUIC_TRANSPORT_PARAMETERS_DRAFT 0xffa5
extern struct pool_head *pool_head_quic_tls_secret;
extern struct pool_head *pool_head_quic_tls_iv;
extern struct pool_head *pool_head_quic_tls_key;
/* QUIC handshake states for both clients and servers. */
enum quic_handshake_state {
QUIC_HS_ST_CLIENT_HANDSHAKE_FAILED,
@ -91,20 +100,26 @@ struct quic_tls_secrets {
const EVP_CIPHER *aead;
const EVP_MD *md;
const EVP_CIPHER *hp;
unsigned char key[32];
unsigned char iv[12];
unsigned char *secret;
size_t secretlen;
/* Header protection key.
* Note: the header protection is applied after packet protection.
* As the header belong to the data, its protection must be removed before removing
* the packet protection.
*/
unsigned char hp_key[32];
unsigned char *iv;
size_t ivlen;
unsigned char *key;
size_t keylen;
int64_t pn;
char flags;
};
struct quic_tls_ctx {
struct quic_tls_secrets rx;
struct quic_tls_secrets tx;
unsigned char flags;
};
#endif /* USE_QUIC */

View File

@ -340,12 +340,64 @@ static inline enum quic_tls_pktns quic_tls_pktns(enum quic_tls_enc_level level)
}
}
/* Erase and free the secrets for a QUIC encryption level with <ctx> as
* context.
* Always succeeds.
*/
static inline void quic_tls_ctx_secs_free(struct quic_tls_ctx *ctx)
{
if (ctx->rx.iv) {
memset(ctx->rx.iv, 0, ctx->rx.ivlen);
ctx->rx.ivlen = 0;
}
if (ctx->rx.key) {
memset(ctx->rx.key, 0, ctx->rx.keylen);
ctx->rx.keylen = 0;
}
if (ctx->tx.iv) {
memset(ctx->tx.iv, 0, ctx->tx.ivlen);
ctx->tx.ivlen = 0;
}
if (ctx->tx.key) {
memset(ctx->tx.key, 0, ctx->tx.keylen);
ctx->tx.keylen = 0;
}
pool_free(pool_head_quic_tls_iv, ctx->rx.iv);
pool_free(pool_head_quic_tls_key, ctx->rx.key);
pool_free(pool_head_quic_tls_iv, ctx->tx.iv);
pool_free(pool_head_quic_tls_key, ctx->tx.key);
ctx->rx.iv = ctx->tx.iv = NULL;
ctx->rx.key = ctx->tx.key = NULL;
}
/* Allocate the secrete keys for a QUIC encryption level with <ctx> as context.
* Returns 1 if succeeded, 0 if not.
*/
static inline int quic_tls_ctx_keys_alloc(struct quic_tls_ctx *ctx)
{
if (!(ctx->rx.iv = pool_alloc(pool_head_quic_tls_iv)) ||
!(ctx->rx.key = pool_alloc(pool_head_quic_tls_key)) ||
!(ctx->tx.iv = pool_alloc(pool_head_quic_tls_iv)) ||
!(ctx->tx.key = pool_alloc(pool_head_quic_tls_key)))
goto err;
ctx->rx.ivlen = ctx->tx.ivlen = QUIC_TLS_IV_LEN;
ctx->rx.keylen = ctx->tx.keylen = QUIC_TLS_KEY_LEN;
return 1;
err:
quic_tls_ctx_secs_free(ctx);
return 0;
}
/* Initialize a TLS cryptographic context for the Initial encryption level. */
static inline void quic_initial_tls_ctx_init(struct quic_tls_ctx *ctx)
static inline int quic_initial_tls_ctx_init(struct quic_tls_ctx *ctx)
{
ctx->rx.aead = ctx->tx.aead = EVP_aes_128_gcm();
ctx->rx.md = ctx->tx.md = EVP_sha256();
ctx->rx.hp = ctx->tx.hp = EVP_aes_128_ctr();
return quic_tls_ctx_keys_alloc(ctx);
}
static inline int quic_tls_level_pkt_type(enum quic_tls_enc_level level)
@ -392,11 +444,14 @@ static inline int quic_get_tls_enc_levels(enum quic_tls_enc_level *level,
return 1;
}
/* Flag the keys at <qel> encryption level as discarded. */
/* Flag the keys at <qel> encryption level as discarded.
* Note that this function is called only for Initial or Handshake encryption levels.
*/
static inline void quic_tls_discard_keys(struct quic_enc_level *qel)
{
qel->tls_ctx.rx.flags |= QUIC_FL_TLS_SECRETS_DCD;
qel->tls_ctx.tx.flags |= QUIC_FL_TLS_SECRETS_DCD;
quic_tls_ctx_secs_free(&qel->tls_ctx);
}
/* Derive the initial secrets with <ctx> as QUIC TLS context which is the
@ -419,7 +474,9 @@ static inline int qc_new_isecs(struct quic_conn *qc,
TRACE_ENTER(QUIC_EV_CONN_ISEC);
ctx = &qc->els[QUIC_TLS_ENC_LEVEL_INITIAL].tls_ctx;
quic_initial_tls_ctx_init(ctx);
if (!quic_initial_tls_ctx_init(ctx))
goto err;
if (!quic_derive_initial_secret(ctx->rx.md,
salt, salt_len,
initial_secret, sizeof initial_secret,
@ -435,16 +492,16 @@ static inline int qc_new_isecs(struct quic_conn *qc,
rx_ctx = &ctx->rx;
tx_ctx = &ctx->tx;
if (!quic_tls_derive_keys(ctx->rx.aead, ctx->rx.hp, ctx->rx.md,
rx_ctx->key, sizeof rx_ctx->key,
rx_ctx->iv, sizeof rx_ctx->iv,
rx_ctx->key, rx_ctx->keylen,
rx_ctx->iv, rx_ctx->ivlen,
rx_ctx->hp_key, sizeof rx_ctx->hp_key,
rx_init_sec, sizeof rx_init_sec))
goto err;
rx_ctx->flags |= QUIC_FL_TLS_SECRETS_SET;
if (!quic_tls_derive_keys(ctx->tx.aead, ctx->tx.hp, ctx->tx.md,
tx_ctx->key, sizeof tx_ctx->key,
tx_ctx->iv, sizeof tx_ctx->iv,
tx_ctx->key, tx_ctx->keylen,
tx_ctx->iv, tx_ctx->ivlen,
tx_ctx->hp_key, sizeof tx_ctx->hp_key,
tx_init_sec, sizeof tx_init_sec))
goto err;

View File

@ -15,6 +15,10 @@
#include <haproxy/xprt_quic.h>
DECLARE_POOL(pool_head_quic_tls_secret, "quic_tls_secret", QUIC_TLS_SECRET_LEN);
DECLARE_POOL(pool_head_quic_tls_iv, "quic_tls_iv", QUIC_TLS_IV_LEN);
DECLARE_POOL(pool_head_quic_tls_key, "quic_tls_key", QUIC_TLS_KEY_LEN);
__attribute__((format (printf, 3, 4)))
void hexdump(const void *buf, size_t buflen, const char *title_fmt, ...);

View File

@ -684,51 +684,61 @@ int ha_quic_set_encryption_secrets(SSL *ssl, enum ssl_encryption_level_t level,
struct quic_tls_ctx *tls_ctx =
&conn->qc->els[ssl_to_quic_enc_level(level)].tls_ctx;
const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
struct quic_tls_secrets *rx, *tx;
TRACE_ENTER(QUIC_EV_CONN_RWSEC, conn);
BUG_ON(secret_len > QUIC_TLS_SECRET_LEN);
if (HA_ATOMIC_LOAD(&conn->qc->flags) & QUIC_FL_CONN_IMMEDIATE_CLOSE) {
TRACE_PROTO("CC required", QUIC_EV_CONN_RWSEC, conn);
goto out;
}
tls_ctx->rx.aead = tls_ctx->tx.aead = tls_aead(cipher);
tls_ctx->rx.md = tls_ctx->tx.md = tls_md(cipher);
tls_ctx->rx.hp = tls_ctx->tx.hp = tls_hp(cipher);
if (!quic_tls_derive_keys(tls_ctx->rx.aead, tls_ctx->rx.hp, tls_ctx->rx.md,
tls_ctx->rx.key, sizeof tls_ctx->rx.key,
tls_ctx->rx.iv, sizeof tls_ctx->rx.iv,
tls_ctx->rx.hp_key, sizeof tls_ctx->rx.hp_key,
if (!quic_tls_ctx_keys_alloc(tls_ctx)) {
TRACE_DEVEL("keys allocation failed", QUIC_EV_CONN_RWSEC, conn);
goto err;
}
rx = &tls_ctx->rx;
tx = &tls_ctx->tx;
rx->aead = tx->aead = tls_aead(cipher);
rx->md = tx->md = tls_md(cipher);
rx->hp = tx->hp = tls_hp(cipher);
if (!quic_tls_derive_keys(rx->aead, rx->hp, rx->md, rx->key, rx->keylen,
rx->iv, rx->ivlen, rx->hp_key, sizeof rx->hp_key,
read_secret, secret_len)) {
TRACE_DEVEL("RX key derivation failed", QUIC_EV_CONN_RWSEC, conn);
return 0;
goto err;
}
tls_ctx->rx.flags |= QUIC_FL_TLS_SECRETS_SET;
if (!quic_tls_derive_keys(tls_ctx->tx.aead, tls_ctx->tx.hp, tls_ctx->tx.md,
tls_ctx->tx.key, sizeof tls_ctx->tx.key,
tls_ctx->tx.iv, sizeof tls_ctx->tx.iv,
tls_ctx->tx.hp_key, sizeof tls_ctx->tx.hp_key,
rx->flags |= QUIC_FL_TLS_SECRETS_SET;
if (!quic_tls_derive_keys(tx->aead, tx->hp, tx->md, tx->key, tx->keylen,
tx->iv, tx->ivlen, tx->hp_key, sizeof tx->hp_key,
write_secret, secret_len)) {
TRACE_DEVEL("TX key derivation failed", QUIC_EV_CONN_RWSEC, conn);
return 0;
goto err;
}
tls_ctx->tx.flags |= QUIC_FL_TLS_SECRETS_SET;
tx->flags |= QUIC_FL_TLS_SECRETS_SET;
if (objt_server(conn->target) && level == ssl_encryption_application) {
const unsigned char *buf;
size_t buflen;
SSL_get_peer_quic_transport_params(ssl, &buf, &buflen);
if (!buflen)
return 0;
goto err;
if (!quic_transport_params_store(conn->qc, 1, buf, buf + buflen))
return 0;
goto err;
}
out:
TRACE_LEAVE(QUIC_EV_CONN_RWSEC, conn, &level);
return 1;
err:
TRACE_DEVEL("leaving in error", QUIC_EV_CONN_RWSEC, conn);
return 0;
}
#else
/* ->set_read_secret callback to derive the RX secrets at <level> encryption
@ -753,9 +763,12 @@ int ha_set_rsec(SSL *ssl, enum ssl_encryption_level_t level,
tls_ctx->rx.md = tls_md(cipher);
tls_ctx->rx.hp = tls_hp(cipher);
if (!(ctx->rx.key = pool_alloc(pool_head_quic_tls_key)))
goto err;
if (!quic_tls_derive_keys(tls_ctx->rx.aead, tls_ctx->rx.hp, tls_ctx->rx.md,
tls_ctx->rx.key, sizeof tls_ctx->rx.key,
tls_ctx->rx.iv, sizeof tls_ctx->rx.iv,
tls_ctx->rx.key, tls_ctx->rx.keylen,
tls_ctx->rx.iv, tls_ctx->rx.ivlen,
tls_ctx->rx.hp_key, sizeof tls_ctx->rx.hp_key,
secret, secret_len)) {
TRACE_DEVEL("RX key derivation failed", QUIC_EV_CONN_RSEC, conn);
@ -803,13 +816,16 @@ int ha_set_wsec(SSL *ssl, enum ssl_encryption_level_t level,
goto out;
}
if (!(ctx->tx.key = pool_alloc(pool_head_quic_tls_key)))
goto err;
tls_ctx->tx.aead = tls_aead(cipher);
tls_ctx->tx.md = tls_md(cipher);
tls_ctx->tx.hp = tls_hp(cipher);
if (!quic_tls_derive_keys(tls_ctx->tx.aead, tls_ctx->tx.hp, tls_ctx->tx.md,
tls_ctx->tx.key, sizeof tls_ctx->tx.key,
tls_ctx->tx.iv, sizeof tls_ctx->tx.iv,
tls_ctx->tx.key, tls_ctx->tx.keylen,
tls_ctx->tx.iv, tls_ctx->tx.ivlen,
tls_ctx->tx.hp_key, sizeof tls_ctx->tx.hp_key,
secret, secret_len)) {
TRACE_DEVEL("TX key derivation failed", QUIC_EV_CONN_WSEC, conn);
@ -1162,7 +1178,7 @@ static int quic_packet_encrypt(unsigned char *payload, size_t payload_len,
{
unsigned char iv[12];
unsigned char *tx_iv = tls_ctx->tx.iv;
size_t tx_iv_sz = sizeof tls_ctx->tx.iv;
size_t tx_iv_sz = tls_ctx->tx.ivlen;
struct enc_debug_info edi;
if (!quic_aead_iv_build(iv, sizeof iv, tx_iv, tx_iv_sz, pn)) {
@ -2974,6 +2990,7 @@ static int quic_conn_enc_level_init(struct quic_conn *qc,
qel->tls_ctx.rx.hp = qel->tls_ctx.tx.hp = NULL;
qel->tls_ctx.rx.flags = 0;
qel->tls_ctx.tx.flags = 0;
qel->tls_ctx.flags = 0;
qel->rx.pkts = EB_ROOT;
HA_RWLOCK_INIT(&qel->rx.pkts_rwlock);