Skip to content

Commit 604bb09

Browse files
cherylsyclaude
andcommitted
[+] #756 remove §6.2 pre-decrypt consecutive key update detection
The pre-decrypt detection (key_phase != next_in_key_phase && key_update_initiator) causes false positives on the initiator side: after flipping next_in_key_phase, any old-phase packet still in flight from peer will trigger KEY_UPDATE_ERROR. This breaks "key update" and "key update 0RTT" integration tests. RFC 9001 §6.2 consecutive detection is MAY (not MUST) and is a responder-side concern. Pre-decrypt checks cannot distinguish legitimate old packets from actual violations—only post-decrypt §6.4 detection (pkt_num > first_recv_pktno during retention window) can reliably do so, and that check remains. Co-Authored-By: Claude (claude-opus-4-6) <noreply@anthropic.com>
1 parent a1cd72a commit 604bb09

5 files changed

Lines changed: 1 addition & 125 deletions

File tree

src/transport/xqc_conn.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ typedef struct {
293293
xqc_usec_t initiate_time_guard; /* time limit for initiating next key update */
294294

295295
/* §6.1: TRUE if we initiated this key update. Blocks next initiation
296-
* until peer ACKs new-phase packet. Also gates §6.2 consecutive detect. */
296+
* until peer ACKs new-phase packet (xqc_key_update_acked). */
297297
xqc_bool_t key_update_initiator;
298298

299299
} xqc_key_update_ctx_t;

src/transport/xqc_packet_parser.c

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -766,26 +766,6 @@ xqc_packet_decrypt(xqc_connection_t *conn, xqc_packet_in_t *packet_in)
766766
/* check key phase, determine weather to update read keys */
767767
xqc_uint_t key_phase = XQC_PACKET_SHORT_HEADER_KEY_PHASE(header);
768768

769-
/*
770-
* §6.2: if we initiated a key update (key_update_initiator) and the peer
771-
* changes key_phase again before ACKing ours, the peer skipped a phase.
772-
* Cannot gate on pkt_num > first_recv_pktno: confirm resets it to MAX,
773-
* making that comparison always false.
774-
*/
775-
if (packet_in->pi_pkt.pkt_type == XQC_PTYPE_SHORT_HEADER && level == XQC_ENC_LEV_1RTT
776-
&& key_phase != conn->key_update_ctx.next_in_key_phase
777-
&& conn->key_update_ctx.key_update_initiator)
778-
{
779-
xqc_log(conn->log, XQC_LOG_ERROR,
780-
"|consecutive key update from peer|pkt_num:%ui|key_phase:%ui|"
781-
"expected:%ui|first_recv_pktno:%ui|",
782-
packet_in->pi_pkt.pkt_num, key_phase,
783-
conn->key_update_ctx.next_in_key_phase,
784-
conn->key_update_ctx.first_recv_pktno);
785-
XQC_CONN_ERR(conn, TRA_KEY_UPDATE_ERROR);
786-
return -XQC_EPROTO;
787-
}
788-
789769
/*
790770
* RX key derivation for a peer-initiated key update.
791771
* pkt_num > first_recv_pktno: skip reordered late arrivals.

tests/unittest/main.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,6 @@ main()
108108
/* issue #823: key update initiator confirmation (RFC 9001 §6.1) */
109109
|| !CU_add_test(pSuite, "xqc_test_key_update_initiator_confirmation",
110110
xqc_test_key_update_initiator_confirmation)
111-
/* issue #756 BUG2: consecutive key update detection (RFC 9001 §6.2) */
112-
|| !CU_add_test(pSuite, "xqc_test_consecutive_key_update_detection",
113-
xqc_test_consecutive_key_update_detection)
114111
/* issue #756 BUG3: old-key high pkt_num detection (RFC 9001 §6.4) */
115112
|| !CU_add_test(pSuite, "xqc_test_old_key_high_pktnum_detection",
116113
xqc_test_old_key_high_pktnum_detection)

tests/unittest/xqc_send_ctl_test.c

Lines changed: 0 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -675,100 +675,6 @@ xqc_test_key_update_initiator_confirmation(void)
675675
}
676676

677677

678-
/*
679-
* Issue #756 BUG2 regression test (RFC 9001 §6.2).
680-
*
681-
* When a key update is in progress (key_update_initiator == TRUE),
682-
* a second key-phase change from the peer (different from next_in_key_phase)
683-
* must be detected as a consecutive key update violation and treated as
684-
* KEY_UPDATE_ERROR.
685-
*
686-
* The detection condition must NOT include pkt_num > first_recv_pktno,
687-
* because xqc_conn_confirm_key_update() resets first_recv_pktno to
688-
* XQC_MAX_UINT64_VALUE, which would make the comparison always false
689-
* and the detection dead code.
690-
*/
691-
void
692-
xqc_test_consecutive_key_update_detection(void)
693-
{
694-
xqc_connection_t *conn = test_engine_connect();
695-
CU_ASSERT_FATAL(conn != NULL);
696-
697-
/*
698-
* Setup: simulate a key update in progress.
699-
* next_in_key_phase = 0 (expecting key_phase 0 for current epoch)
700-
* key_update_initiator = TRUE (we initiated, awaiting ACK confirmation)
701-
* first_recv_pktno = XQC_MAX_UINT64_VALUE (reset by confirm_key_update)
702-
*/
703-
conn->key_update_ctx.next_in_key_phase = 0;
704-
conn->key_update_ctx.first_recv_pktno = XQC_MAX_UINT64_VALUE;
705-
conn->key_update_ctx.key_update_initiator = XQC_TRUE;
706-
707-
/*
708-
* Case 1: Packet with same key_phase as expected.
709-
* This is a normal packet, NOT a consecutive key update.
710-
* key_phase == next_in_key_phase → condition does not fire.
711-
*/
712-
xqc_uint_t key_phase = 0; /* matches next_in_key_phase */
713-
xqc_bool_t detected = XQC_FALSE;
714-
715-
if (key_phase != conn->key_update_ctx.next_in_key_phase
716-
&& conn->key_update_ctx.key_update_initiator)
717-
{
718-
detected = XQC_TRUE;
719-
}
720-
CU_ASSERT_EQUAL(detected, XQC_FALSE);
721-
722-
/*
723-
* Case 2: Packet with different key_phase,
724-
* but key_update_initiator is FALSE → no detection (normal RX key update).
725-
*/
726-
conn->key_update_ctx.key_update_initiator = XQC_FALSE;
727-
key_phase = 1;
728-
detected = XQC_FALSE;
729-
730-
if (key_phase != conn->key_update_ctx.next_in_key_phase
731-
&& conn->key_update_ctx.key_update_initiator)
732-
{
733-
detected = XQC_TRUE;
734-
}
735-
CU_ASSERT_EQUAL(detected, XQC_FALSE);
736-
737-
/*
738-
* Case 3: All conditions met → consecutive key update DETECTED.
739-
* key_phase != next_in_key_phase, initiator = TRUE.
740-
*/
741-
conn->key_update_ctx.key_update_initiator = XQC_TRUE;
742-
key_phase = 1;
743-
detected = XQC_FALSE;
744-
745-
if (key_phase != conn->key_update_ctx.next_in_key_phase
746-
&& conn->key_update_ctx.key_update_initiator)
747-
{
748-
detected = XQC_TRUE;
749-
}
750-
CU_ASSERT_EQUAL(detected, XQC_TRUE);
751-
752-
/*
753-
* Case 4: first_recv_pktno = XQC_MAX_UINT64_VALUE (post-confirm reset).
754-
* This is the typical scenario after initiator calls confirm_key_update().
755-
* The detection must still fire — this was the original bug where
756-
* pkt_num > XQC_MAX_UINT64_VALUE was always false.
757-
*/
758-
conn->key_update_ctx.first_recv_pktno = XQC_MAX_UINT64_VALUE;
759-
conn->key_update_ctx.key_update_initiator = XQC_TRUE;
760-
key_phase = 1;
761-
detected = XQC_FALSE;
762-
763-
if (key_phase != conn->key_update_ctx.next_in_key_phase
764-
&& conn->key_update_ctx.key_update_initiator)
765-
{
766-
detected = XQC_TRUE;
767-
}
768-
CU_ASSERT_EQUAL(detected, XQC_TRUE);
769-
770-
xqc_engine_destroy(conn->engine);
771-
}
772678

773679

774680
/*

tests/unittest/xqc_send_ctl_test.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,6 @@ void xqc_test_send_ctl_persistent_congestion_no_rtt_sample_early_return(void);
5050
*/
5151
void xqc_test_key_update_initiator_confirmation(void);
5252

53-
/*
54-
* Regression test for issue #756 BUG2 (RFC 9001 §6.2):
55-
* Consecutive key update from peer must be detected and treated as
56-
* KEY_UPDATE_ERROR when key_update_initiator is TRUE.
57-
*/
58-
void xqc_test_consecutive_key_update_detection(void);
59-
6053
/*
6154
* Regression test for issue #756 BUG3 (RFC 9001 §6.4):
6255
* Old-key packet with pkt_num higher than any new-key packet must be

0 commit comments

Comments
 (0)