Skip to content

fix: keep private keys for secondary key shares - #416

Open
thesinakamali wants to merge 3 commits into
refraction-networking:masterfrom
thesinakamali:fix/keyshare-private-keys
Open

thesinakamali wants to merge 3 commits into
refraction-networking:masterfrom
thesinakamali:fix/keyshare-private-keys

Conversation

@thesinakamali

Copy link
Copy Markdown
Contributor

When a ClientHelloSpec offers more than one classical key share (e.g., the Firefox profiles send X25519 and P-256), ApplyPreset generates a private key for each but only keeps the first in KeyShareKeys.Ecdhe. If the server selects the second curve, clientSharedSecret runs ECDH with the wrong private key and the handshake fails with "tls: invalid server key share". Fix by storing the additional private keys in a new KeyShareKeys.EcdheFallback map keyed by CurveID, and having ecdhKeyExchange select the key matching the server's chosen group.

The first commit fixes a related issue where ApplyPreset unconditionally reset KeyShareKeys to an empty struct. When BuildHandshakeStateWithoutSession is called before BuildHandshakeState, the spec is reused and the key shares are already populated, so the second call discarded the private keys that matched the public shares already in the ClientHello. Only initialize KeyShareKeys when it is nil.

The second commit fixes the secondary key share bug and adds a test that does an in-process handshake with HelloFirefox_105/120/148 against a server with CurvePreferences = CurveP256. It fails with "invalid server key share" without the fix and negotiates P-256 with no HRR with it.

TaiLerV pushed a commit to TaiLerV/Xray-core-up that referenced this pull request Oct 13, 2026
uTLS keeps the private key of the first classical key share only. Firefox
fingerprints send key shares for X25519 and then P-256, so against a server
that selects P-256 (AWS s2n and FIPS policies, BoringSSL, Go and RHEL in FIPS
mode, P-256-first nginx configurations) the client failed with "tls: invalid
server key share", sending an illegal_parameter alert in cleartext right
after the ServerHello, where Firefox goes on (refraction-networking/utls#402).

Take uTLS from the fork github.com/Jolymmiles/utls, branch
fix/keep-secondary-key-shares: the pinned upstream commit aa6edf4b11af plus
a backport of refraction-networking/utls#416, which keeps the keys of the
later classical key shares by group and uses the one the server selected.
The ClientHello of every fingerprint is unchanged (snapshot tests), and the
REALITY client still authenticates with the first classical key. The health
test's Firefox cells against a P-256-only server now pass.

The randomized presets still send no P-256 key share; the reason is now that
Chrome sends none, not uTLS's lost key.
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.

1 participant