MINOR: quic: Enable the Key Update process
This patch modifies ha_quic_set_encryption_secrets() to store the
secrets received by the TLS stack and prepare the information for the
next key update thanks to quic_tls_key_update().
qc_pkt_decrypt() is modified to check if we must used the next or the
previous key phase information to decrypt a short packet.
The information are rotated if the packet could be decrypted with the
next key phase information. Then new secrets, keys and IVs are updated
calling quic_tls_key_update() to prepare the next key phase.
quic_build_packet_short_header() is also modified to handle the key phase
bit from the current key phase information.
diff --git a/src/xprt_quic.c b/src/xprt_quic.c
index f006c1a..9732ec1 100644
--- a/src/xprt_quic.c
+++ b/src/xprt_quic.c
@@ -823,6 +823,29 @@
if (!quic_transport_params_store(conn->qc, 1, buf, buf + buflen))
goto err;
}
+
+ if (level == ssl_encryption_application) {
+ struct quic_conn *qc = conn->qc;
+ struct quic_tls_kp *prv_rx = &qc->ku.prv_rx;
+ struct quic_tls_kp *nxt_rx = &qc->ku.nxt_rx;
+ struct quic_tls_kp *nxt_tx = &qc->ku.nxt_tx;
+
+ if (!(rx->secret = pool_alloc(pool_head_quic_tls_secret)) ||
+ !(tx->secret = pool_alloc(pool_head_quic_tls_secret))) {
+ TRACE_DEVEL("Could not allocate secrete keys", QUIC_EV_CONN_RWSEC, conn);
+ goto err;
+ }
+
+ memcpy(rx->secret, read_secret, secret_len);
+ rx->secretlen = secret_len;
+ memcpy(tx->secret, write_secret, secret_len);
+ tx->secretlen = secret_len;
+ /* Initialize all the secret keys lengths */
+ prv_rx->secretlen = nxt_rx->secretlen = nxt_tx->secretlen = secret_len;
+ /* Prepare the next key update */
+ if (!quic_tls_key_update(qc))
+ goto err;
+ }
out:
TRACE_LEAVE(QUIC_EV_CONN_RWSEC, conn, &level);
return 1;
@@ -1294,22 +1317,62 @@
/* Decrypt <pkt> QUIC packet with <tls_ctx> as QUIC TLS cryptographic context.
* Returns 1 if succeeded, 0 if not.
*/
-static int qc_pkt_decrypt(struct quic_rx_packet *pkt, struct quic_tls_ctx *tls_ctx)
+static int qc_pkt_decrypt(struct quic_rx_packet *pkt, struct quic_enc_level *qel)
{
- int ret;
+ int ret, kp_changed;
unsigned char iv[12];
+ struct quic_tls_ctx *tls_ctx = &qel->tls_ctx;
unsigned char *rx_iv = tls_ctx->rx.iv;
- size_t rx_iv_sz = sizeof tls_ctx->rx.iv;
+ size_t rx_iv_sz = tls_ctx->rx.ivlen;
+ unsigned char *rx_key = tls_ctx->rx.key;
+
+ kp_changed = 0;
+ if (pkt->type == QUIC_PACKET_TYPE_SHORT) {
+ /* The two tested bits are not at the same position,
+ * this is why they are first both inversed.
+ */
+ if (!(*pkt->data & QUIC_PACKET_KEY_PHASE_BIT) ^ !(tls_ctx->flags & QUIC_FL_TLS_KP_BIT_SET)) {
+ if (pkt->pn < tls_ctx->rx.pn) {
+ /* The lowest packet number of a previous key phase
+ * cannot be null if it really stores previous key phase
+ * secrets.
+ */
+ if (!pkt->qc->ku.prv_rx.pn)
+ return 0;
+
+ rx_iv = pkt->qc->ku.prv_rx.iv;
+ rx_key = pkt->qc->ku.prv_rx.key;
+ }
+ else if (pkt->pn > qel->pktns->rx.largest_pn) {
+ /* Next key phase */
+ kp_changed = 1;
+ rx_iv = pkt->qc->ku.nxt_rx.iv;
+ rx_key = pkt->qc->ku.nxt_rx.key;
+ }
+ }
+ }
if (!quic_aead_iv_build(iv, sizeof iv, rx_iv, rx_iv_sz, pkt->pn))
return 0;
ret = quic_tls_decrypt(pkt->data + pkt->aad_len, pkt->len - pkt->aad_len,
pkt->data, pkt->aad_len,
- tls_ctx->rx.aead, tls_ctx->rx.key, iv);
+ tls_ctx->rx.aead, rx_key, iv);
if (!ret)
return 0;
+ /* Update the keys only if the packet decryption succeeded. */
+ if (kp_changed) {
+ quic_tls_rotate_keys(pkt->qc);
+ /* Toggle the Key Phase bit */
+ tls_ctx->flags ^= QUIC_FL_TLS_KP_BIT_SET;
+ /* Store the lowest packet number received for the current key phase */
+ tls_ctx->rx.pn = pkt->pn;
+ /* Prepare the next key update */
+ if (!quic_tls_key_update(pkt->qc))
+ return 0;
+ }
+
/* Update the packet length (required to parse the frames). */
pkt->len = pkt->aad_len + ret;
@@ -2912,7 +2975,7 @@
pkt = eb64_entry(&node->node, struct quic_rx_packet, pn_node);
TRACE_PROTO("new packet", QUIC_EV_CONN_ELRXPKTS,
ctx->conn, pkt, NULL, ctx->ssl);
- if (!qc_pkt_decrypt(pkt, &qel->tls_ctx)) {
+ if (!qc_pkt_decrypt(pkt, qel)) {
/* Drop the packet */
TRACE_PROTO("packet decryption failed -> dropped",
QUIC_EV_CONN_ELRXPKTS, ctx->conn, pkt);
@@ -4088,23 +4151,22 @@
return 1;
}
-/* This function builds into <buf> buffer a QUIC long packet header whose size may be computed
+/* This function builds into <buf> buffer a QUIC short packet header whose size may be computed
* in advance. This is the reponsability of the caller to check there is enough room in this
- * buffer to build a long header.
- * Returns 0 if <type> QUIC packet type is not supported by long header, or 1 if succeeded.
+ * buffer to build a short header.
*/
-static int quic_build_packet_short_header(unsigned char **buf, const unsigned char *end,
- size_t pn_len, struct quic_conn *conn)
+static void quic_build_packet_short_header(unsigned char **buf, const unsigned char *end,
+ size_t pn_len, struct quic_conn *conn,
+ unsigned char tls_flags)
{
/* #0 byte flags */
- *(*buf)++ = QUIC_PACKET_FIXED_BIT | (pn_len - 1);
+ *(*buf)++ = QUIC_PACKET_FIXED_BIT |
+ ((tls_flags & QUIC_FL_TLS_KP_BIT_SET) ? QUIC_PACKET_KEY_PHASE_BIT : 0) | (pn_len - 1);
/* Destination connection ID */
if (conn->dcid.len) {
memcpy(*buf, conn->dcid.data, conn->dcid.len);
*buf += conn->dcid.len;
}
-
- return 1;
}
/* Apply QUIC header protection to the packet with <buf> as first byte address,
@@ -4485,7 +4547,7 @@
*pn_len = quic_packet_number_length(pn, largest_acked_pn);
/* Build the header */
if (pkt->type == QUIC_PACKET_TYPE_SHORT)
- quic_build_packet_short_header(&pos, end, *pn_len, conn);
+ quic_build_packet_short_header(&pos, end, *pn_len, conn, qel->tls_ctx.flags);
else
quic_build_packet_long_header(&pos, end, pkt->type, *pn_len, conn);
/* XXX FIXME XXX Encode the token length (0) for an Initial packet. */