From: Frédéric Lécaille Date: Tue, 23 Nov 2021 20:02:04 +0000 (+0100) Subject: MINOR: quic: Dynamically allocate the secrete keys X-Git-Tag: v2.6-dev1~353 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fc768ecc88824f45afe5c2d74e63b497dd42263f;p=thirdparty%2Fhaproxy.git MINOR: quic: Dynamically allocate the secrete keys This is done for any encryption level. This is to prepare the Key Update feature. --- diff --git a/include/haproxy/quic_tls-t.h b/include/haproxy/quic_tls-t.h index e2f5fb16ab..237b8c6113 100644 --- a/include/haproxy/quic_tls-t.h +++ b/include/haproxy/quic_tls-t.h @@ -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 */ diff --git a/include/haproxy/quic_tls.h b/include/haproxy/quic_tls.h index def95fe443..bf23823dd5 100644 --- a/include/haproxy/quic_tls.h +++ b/include/haproxy/quic_tls.h @@ -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 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 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 encryption level as discarded. */ +/* Flag the keys at 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 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; diff --git a/src/quic_tls.c b/src/quic_tls.c index bd45fcee98..491a1bcd79 100644 --- a/src/quic_tls.c +++ b/src/quic_tls.c @@ -15,6 +15,10 @@ #include +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, ...); diff --git a/src/xprt_quic.c b/src/xprt_quic.c index b09db06949..2f23eae4d4 100644 --- a/src/xprt_quic.c +++ b/src/xprt_quic.c @@ -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 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);