Skip to content

Reject client-side receipt of server Initial packets with non-zero Token Length (RFC 9000 §17.2.2)#148

Open
tarunbtw wants to merge 1 commit into
google:mainfrom
tarunbtw:fix-client-reject-server-initial-token
Open

Reject client-side receipt of server Initial packets with non-zero Token Length (RFC 9000 §17.2.2)#148
tarunbtw wants to merge 1 commit into
google:mainfrom
tarunbtw:fix-client-reject-server-initial-token

Conversation

@tarunbtw

@tarunbtw tarunbtw commented Jul 4, 2026

Copy link
Copy Markdown

Reject client-side receipt of server Initial packets with non-zero Token Length

Fixes google/quiche#138

Problem

RFC 9000, Section 17.2.2 requires a client to discard, or MAY close the
connection on, an Initial packet from the server that carries a
non-zero-length Token field, since only clients are permitted to send
Initial tokens.

quiche parsed and stored header.retry_token but never checked for
this case on the client receive path. A server, or an on-path attacker
spoofing one, could send an Initial packet with a non-empty token and
the client would accept it.

Fix

Added a check in QuicConnection::OnUnauthenticatedPublicHeader()
that closes the connection with IETF_QUIC_PROTOCOL_VIOLATION when a
client receives a server Initial packet with a non-empty retry token.

if (perspective_ == Perspective::IS_CLIENT && header.version_flag &&
    header.long_packet_type == INITIAL && !header.retry_token.empty()) {
  CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION,
                   "Server Initial packet has non-zero token length",
                   ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
  return false;
}

Testing

Added ClientRejectsServerInitialWithNonZeroTokenLength to
quic_connection_test.cc. It constructs a server-perspective Initial
packet with a non-empty retry token, feeds it through
ProcessUdpPacket, and asserts the connection closes with
IETF_QUIC_PROTOCOL_VIOLATION.

bazel test //quiche:quic_connection_test \
  --test_filter='*ClientRejectsServerInitialWithNonZeroTokenLength*'

All 8 parameterized instances pass, including both gQUIC (short-circuited
by the existing !IsIetfQuic() check) and IETF QUIC versions.

…ken Length

RFC 9000, Section 17.2.2 requires a client to discard, or MAY close
the connection on, an Initial packet from the server that carries a
non-zero-length Token field, since only clients are permitted to send
Initial tokens. quiche parsed and stored header.retry_token but never
checked for this case on the client receive path.

Add a check in QuicConnection::OnUnauthenticatedPublicHeader() that
closes the connection with IETF_QUIC_PROTOCOL_VIOLATION when a client
receives a server Initial packet with a non-empty retry token, plus a
regression test.

Fixes google#138
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Server Initial with Non-Zero Token Length Is Accepted

1 participant