MINOR: quic: Add traces about QUIC TLS key update
Dump the secret used to derive the next one during a key update initiated by the
client and dump the resulted new secret and the new key and iv to be used to
decryption Application level packets.
Also add a trace when the key update is supposed to be initiated on haproxy side.
This has already helped in diagnosing an issue evealed by the key update interop
test with xquic as client.
Must be backported to 2.7.
diff --git a/include/haproxy/quic_conn-t.h b/include/haproxy/quic_conn-t.h
index ebe3dfd..9c3d4f4 100644
--- a/include/haproxy/quic_conn-t.h
+++ b/include/haproxy/quic_conn-t.h
@@ -225,6 +225,7 @@
#define QUIC_EV_CONN_ELEVELSEL (1ULL << 47)
#define QUIC_EV_CONN_RCV (1ULL << 48)
#define QUIC_EV_CONN_KILL (1ULL << 49)
+#define QUIC_EV_CONN_KP (1ULL << 50)
/* Similar to kernel min()/max() definitions. */
#define QUIC_MIN(a, b) ({ \
diff --git a/include/haproxy/quic_tls.h b/include/haproxy/quic_tls.h
index 1687d3c..a2eb223 100644
--- a/include/haproxy/quic_tls.h
+++ b/include/haproxy/quic_tls.h
@@ -31,6 +31,8 @@
void quic_tls_keys_hexdump(struct buffer *buf,
const struct quic_tls_secrets *secs);
+void quic_tls_kp_keys_hexdump(struct buffer *buf,
+ const struct quic_tls_kp *kp);
void quic_tls_secret_hexdump(struct buffer *buf,
const unsigned char *secret, size_t secret_len);
diff --git a/src/quic_conn.c b/src/quic_conn.c
index d65c3f1..5587bae 100644
--- a/src/quic_conn.c
+++ b/src/quic_conn.c
@@ -253,6 +253,16 @@
edi->pn = pn;
}
+/* Used only for QUIC TLS key phase traces */
+struct quic_kp_trace {
+ const unsigned char *rx_sec;
+ size_t rx_seclen;
+ const struct quic_tls_kp *rx;
+ const unsigned char *tx_sec;
+ size_t tx_seclen;
+ const struct quic_tls_kp *tx;
+};
+
/* Trace callback for QUIC.
* These traces always expect that arg1, if non-null, is of type connection.
*/
@@ -310,6 +320,27 @@
quic_tls_secret_hexdump(&trace_buf, tx_sec, 32);
quic_tls_keys_hexdump(&trace_buf, &tls_ctx->tx);
}
+
+ if ((mask & QUIC_EV_CONN_KP) && qc) {
+ /* Initial read & write secrets. */
+ const struct quic_kp_trace *kp = a2;
+
+ if (kp) {
+ if (kp->rx) {
+ chunk_appendf(&trace_buf, "\n RX kp");
+ if (kp->rx_sec)
+ quic_tls_secret_hexdump(&trace_buf, kp->rx_sec, kp->rx_seclen);
+ quic_tls_kp_keys_hexdump(&trace_buf, kp->rx);
+ }
+ if (kp->tx) {
+ chunk_appendf(&trace_buf, "\n TX kp");
+ if (kp->tx_sec)
+ quic_tls_secret_hexdump(&trace_buf, kp->tx_sec, kp->tx_seclen);
+ quic_tls_kp_keys_hexdump(&trace_buf, kp->tx);
+ }
+ }
+ }
+
if (mask & (QUIC_EV_CONN_RSEC|QUIC_EV_CONN_RWSEC)) {
const enum ssl_encryption_level_t *level = a2;
@@ -819,25 +850,34 @@
static int quic_tls_key_update(struct quic_conn *qc)
{
struct quic_tls_ctx *tls_ctx = &qc->els[QUIC_TLS_ENC_LEVEL_APP].tls_ctx;
- struct quic_tls_secrets *rx, *tx;
+ struct quic_tls_secrets *rx = &tls_ctx->rx;
+ struct quic_tls_secrets *tx = &tls_ctx->tx;
+ /* Used only for the traces */
+ struct quic_kp_trace kp_trace = {
+ .rx_sec = rx->secret,
+ .rx_seclen = rx->secretlen,
+ .tx_sec = tx->secret,
+ .tx_seclen = tx->secretlen,
+ };
+ /* The next key phase secrets to be derived */
struct quic_tls_kp *nxt_rx = &qc->ku.nxt_rx;
struct quic_tls_kp *nxt_tx = &qc->ku.nxt_tx;
const struct quic_version *ver =
qc->negotiated_version ? qc->negotiated_version : qc->original_version;
int ret = 0;
- TRACE_ENTER(QUIC_EV_CONN_RWSEC, qc);
+ TRACE_ENTER(QUIC_EV_CONN_KP, qc);
- tls_ctx = &qc->els[QUIC_TLS_ENC_LEVEL_APP].tls_ctx;
- rx = &tls_ctx->rx;
- tx = &tls_ctx->tx;
nxt_rx = &qc->ku.nxt_rx;
nxt_tx = &qc->ku.nxt_tx;
+ TRACE_PRINTF(TRACE_LEVEL_DEVELOPER, QUIC_EV_CONN_SPPKTS, qc, 0, 0, 0,
+ "nxt_rx->secretlen=%llu rx->secretlen=%llu",
+ (ull)nxt_rx->secretlen, (ull)rx->secretlen);
/* Prepare new RX secrets */
if (!quic_tls_sec_update(rx->md, ver, nxt_rx->secret, nxt_rx->secretlen,
rx->secret, rx->secretlen)) {
- TRACE_ERROR("New RX secret update failed", QUIC_EV_CONN_RWSEC, qc);
+ TRACE_ERROR("New RX secret update failed", QUIC_EV_CONN_KP, qc);
goto leave;
}
@@ -845,14 +885,15 @@
nxt_rx->key, nxt_rx->keylen,
nxt_rx->iv, nxt_rx->ivlen, NULL, 0,
nxt_rx->secret, nxt_rx->secretlen)) {
- TRACE_ERROR("New RX key derivation failed", QUIC_EV_CONN_RWSEC, qc);
+ TRACE_ERROR("New RX key derivation failed", QUIC_EV_CONN_KP, qc);
goto leave;
}
+ kp_trace.rx = nxt_rx;
/* Prepare new TX secrets */
if (!quic_tls_sec_update(tx->md, ver, nxt_tx->secret, nxt_tx->secretlen,
tx->secret, tx->secretlen)) {
- TRACE_ERROR("New TX secret update failed", QUIC_EV_CONN_RWSEC, qc);
+ TRACE_ERROR("New TX secret update failed", QUIC_EV_CONN_KP, qc);
goto leave;
}
@@ -860,17 +901,18 @@
nxt_tx->key, nxt_tx->keylen,
nxt_tx->iv, nxt_tx->ivlen, NULL, 0,
nxt_tx->secret, nxt_tx->secretlen)) {
- TRACE_ERROR("New TX key derivation failed", QUIC_EV_CONN_RWSEC, qc);
+ TRACE_ERROR("New TX key derivation failed", QUIC_EV_CONN_KP, qc);
goto leave;
}
+ kp_trace.tx = nxt_tx;
if (nxt_rx->ctx) {
EVP_CIPHER_CTX_free(nxt_rx->ctx);
nxt_rx->ctx = NULL;
}
if (!quic_tls_rx_ctx_init(&nxt_rx->ctx, tls_ctx->rx.aead, nxt_rx->key)) {
- TRACE_ERROR("could not initial RX TLS cipher context", QUIC_EV_CONN_RWSEC, qc);
+ TRACE_ERROR("could not initial RX TLS cipher context", QUIC_EV_CONN_KP, qc);
goto leave;
}
@@ -880,13 +922,13 @@
}
if (!quic_tls_rx_ctx_init(&nxt_tx->ctx, tls_ctx->tx.aead, nxt_tx->key)) {
- TRACE_ERROR("could not initial RX TLS cipher context", QUIC_EV_CONN_RWSEC, qc);
+ TRACE_ERROR("could not initial RX TLS cipher context", QUIC_EV_CONN_KP, qc);
goto leave;
}
ret = 1;
leave:
- TRACE_LEAVE(QUIC_EV_CONN_RWSEC, qc);
+ TRACE_LEAVE(QUIC_EV_CONN_KP, qc, &kp_trace);
return ret;
}
@@ -1543,6 +1585,7 @@
}
else if (pkt->pn > qel->pktns->rx.largest_pn) {
/* Next key phase */
+ TRACE_PROTO("Key phase changed", QUIC_EV_CONN_RXPKT, qc);
kp_changed = 1;
rx_ctx = qc->ku.nxt_rx.ctx;
rx_iv = qc->ku.nxt_rx.iv;
diff --git a/src/quic_tls.c b/src/quic_tls.c
index 199e55c..edd45e7 100644
--- a/src/quic_tls.c
+++ b/src/quic_tls.c
@@ -57,6 +57,23 @@
chunk_appendf(buf, "%02x", secs->hp_key[i]);
}
+/* Dump the RX/TX secrets of <kp> QUIC TLS key phase */
+void quic_tls_kp_keys_hexdump(struct buffer *buf,
+ const struct quic_tls_kp *kp)
+{
+ int i;
+
+ chunk_appendf(buf, "\n secret=");
+ for (i = 0; i < kp->secretlen; i++)
+ chunk_appendf(buf, "%02x", kp->secret[i]);
+ chunk_appendf(buf, "\n key=");
+ for (i = 0; i < kp->keylen; i++)
+ chunk_appendf(buf, "%02x", kp->key[i]);
+ chunk_appendf(buf, "\n iv=");
+ for (i = 0; i < kp->ivlen; i++)
+ chunk_appendf(buf, "%02x", kp->iv[i]);
+}
+
/* Dump <secret> TLS secret. */
void quic_tls_secret_hexdump(struct buffer *buf,
const unsigned char *secret, size_t secret_len)