Skip to content

feat: add Firefox 158 parrot - #427

Open
SaamoCha wants to merge 1 commit into
refraction-networking:masterfrom
SaamoCha:firefox-158
Open

SaamoCha wants to merge 1 commit into
refraction-networking:masterfrom
SaamoCha:firefox-158

Conversation

@SaamoCha

@SaamoCha SaamoCha commented Oct 19, 2026 •

Copy link
Copy Markdown

This adds HelloFirefox_158 and points HelloFirefox_Auto at it.

Compared with HelloFirefox_148, Firefox 158 no longer offers:

  • TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA and TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
  • ffdhe2048 and ffdhe3072 in supported_groups

The new parrot also keeps session_ticket and psk_key_exchange_modes. As discussed in #390, Firefox only leaves these two out in private browsing, and HelloFirefox_148 matches the private browsing hello. The older Firefox parrots (63 to 120) all follow normal browsing, so 158 does too. I did not change 148.

I noticed this drift with Parroteer, an automated pipeline I set up on GitHub Actions. Every day it captures ClientHellos from real browsers (Chrome, Edge, Firefox, Safari and Android OkHttp), diffs them against the parrots on the latest uTLS master, and opens an issue when one falls behind. It has been running since April, and I hope it can be useful for keeping the parrots up to date here too.

I then checked the drift with a raw capture:

Data Collection:

  • Firefox: 158.0 (official build)
  • OS: Ubuntu 25.10
  • Fresh profile, normal browsing, HTTP/3 off, first visit to www.example.com
  • Full TLS 1.3 handshake checked with SSLKEYLOGFILE

The raw ClientHello and the capture details are in testdata/firefox158_clienthello.{hex,md}.

Testing:

  • go test ./... passes, and 158 is added to the existing hybrid/classical key share reuse test
  • A generated hello has the same cipher suites, extension order and extension bodies as the capture (GREASE ECH aside)
  • Handshakes work against www.example.com, www.cloudflare.com, www.google.com, www.mozilla.org, github.com and cards-na.klarna.com

One note: in my Firefox 158 captures the GREASE ECH payload was always 240 bytes, but Firefox 148 sent both 239 and 240, so I kept the 239-byte setting from 148.

Closes #428

Add HelloFirefox_158 and point HelloFirefox_Auto at it. The spec follows a
fresh Firefox 158.0 connection in normal browsing mode, like the Firefox
parrots before 148. Compared with HelloFirefox_148 it:

- drops TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA and
  TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
- drops ffdhe2048 and ffdhe3072 from supported_groups
- adds session_ticket and psk_key_exchange_modes, which Firefox sends
  outside private browsing

HelloFirefox_148 is unchanged; it matches Firefox 148 in private browsing
mode, as noted in the README. The raw ClientHello and how it was captured
are in testdata/firefox158_clienthello.{hex,md}.
@fabifont

Copy link
Copy Markdown

The GREASE ECH payload should be 240 bytes for Firefox 158, so CandidatePayloadLens: []uint16{224}, // +16: 240, not 223. Your own capture agrees: testdata/firefox158_clienthello.hex carries a 240-byte payload.

The 239 comes from an NSS bug. Before NSS 3.127, tls13_PadChInner omitted the "- 1" of the RFC 9849 section 6.1.3 rounding term and padded the fake inner hello to 32k + 31, which with the 16-byte AEAD tag gives 239 (NSS bug 2060720, fixed 2026-08-13; NSS_3_127_RTM is the first tag with the corrected formula, NSS_3_126_RTM still has the old one). Firefox 155 is the first release shipping NSS 3.127.

Measured on official Linux builds from archive.mozilla.org, driven headless against a listener: Firefox 148.0 (bundled NSS 3.120.1) and 154.0 (NSS 3.126) send 239 on every hello; 155.0 (NSS 3.127) and 158.0 (NSS 3.130) send 240 on every hello, with hostnames of 10 bytes. With a 126-byte hostname 148.0 sends 271 and 155.0 sends 272, the missing byte of the old rounding term. The AEAD alternates between AES-128-GCM and ChaCha20 in all of them, as the parrot already models. A 240 from a Firefox 148 can therefore only come from a build linked against a newer system NSS, as distribution packages are.

So HelloFirefox_148 is right to keep 223, and this parrot should use 224; any parrot of Firefox 155 or later needs 224.

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.

HelloFirefox_Auto (148) no longer matches current Firefox

2 participants