@@ -38,7 +38,8 @@ use crate::{
3838 stats:: FrameStats ,
3939 tparams:: { TransportParameter , TransportParameterId :: * } ,
4040 tracking:: DEFAULT_LOCAL_ACK_DELAY ,
41- CloseReason , ConnectionParameters , Error , Pmtud , StreamType , Version ,
41+ CloseReason , ConnectionParameters , EmptyConnectionIdGenerator , Error , Pmtud , StreamType ,
42+ Version ,
4243} ;
4344
4445const ECH_CONFIG_ID : u8 = 7 ;
@@ -1564,3 +1565,43 @@ fn zero_rtt_with_ech() {
15641565 assert ! ( client. tls_info( ) . unwrap( ) . early_data_accepted( ) ) ;
15651566 assert ! ( server. tls_info( ) . unwrap( ) . early_data_accepted( ) ) ;
15661567}
1568+
1569+ /// RFC 9287 Section 3.1 states: "A server MUST NOT remember that a client negotiated
1570+ /// the extension in a previous connection and set the QUIC Bit to 0 based on that information."
1571+ ///
1572+ /// This test verifies that the client complies with RFC 9287 Section 3.1 by ensuring
1573+ /// it does not grease the QUIC Bit based on cached (0-RTT) transport parameters.
1574+ /// Regression test for the `handshakeloss` interop test failure, where client Initial
1575+ /// packets with the fixed bit cleared (due to cached parameters) were discarded by the server.
1576+ #[ test]
1577+ fn grease_quic_bit_respects_current_handshake ( ) {
1578+ fixture_init ( ) ;
1579+
1580+ // Create a client connection.
1581+ let client = Connection :: new_client (
1582+ test_fixture:: DEFAULT_SERVER_NAME ,
1583+ test_fixture:: DEFAULT_ALPN ,
1584+ Rc :: new ( RefCell :: new ( EmptyConnectionIdGenerator :: default ( ) ) ) ,
1585+ DEFAULT_ADDR ,
1586+ DEFAULT_ADDR ,
1587+ ConnectionParameters :: default ( ) ,
1588+ now ( ) ,
1589+ )
1590+ . unwrap ( ) ;
1591+
1592+ // Simulate having cached 0-RTT transport parameters that include grease_quic_bit.
1593+ // In reality, this would come from a previous connection's session ticket.
1594+ let mut tp = crate :: tparams:: TransportParameters :: default ( ) ;
1595+ tp. set_empty ( GreaseQuicBit ) ;
1596+ client. tps . borrow_mut ( ) . set_remote_0rtt ( Some ( tp) ) ;
1597+
1598+ // At this point:
1599+ // - We have remote_0rtt params with GreaseQuicBit
1600+ // - We do NOT have remote_handshake params (no current handshake confirmation)
1601+
1602+ // With only cached 0-RTT params, no greasing is allowed.
1603+ assert ! (
1604+ !client. can_grease_quic_bit( ) ,
1605+ "Must not grease with only cached 0-RTT params (RFC 9287 Section 3.1)"
1606+ ) ;
1607+ }
0 commit comments