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.
legiz-ru added a commit to legiz-ru/prizrak-utls that referenced this pull request Nov 1, 2026
ApplyPreset kept the private key of the first classical key share only.
Firefox fingerprints send key shares for X25519 and then P-256, so a
server that selected P-256 failed the handshake with "tls: invalid server
key share" (refraction-networking#402), where Firefox completes it.
Keep the private keys of the classical key shares after the first, by
group, and derive the shared secret with the one the server selected.
The ClientHello is unchanged, and KeySharePrivateKeys.Ecdhe still holds
the first classical key.

Backport of refraction-networking#416, as in Jolymmiles/utls.
legiz-ru added a commit to legiz-ru/Prizrak-Core that referenced this pull request Nov 1, 2026
…-256

Firefox fingerprints send key shares for X25519 and then P-256, and uTLS
kept the private key of the first only: a server that selected P-256
failed the handshake with "tls: invalid server key share" right after
its ServerHello, where Firefox goes on (refraction-networking/utls#402).
prizrak-utls now keeps the keys of every classical key share and uses the
one the server selected (backport of refraction-networking/utls#416, as
in Jolymmiles/utls). The ClientHello is unchanged.
legiz-ru added a commit to legiz-ru/Prizrak-Core that referenced this pull request Nov 2, 2026
…-256

Firefox fingerprints send key shares for X25519 and then P-256, and uTLS
kept the private key of the first only: a server that selected P-256
failed the handshake with "tls: invalid server key share" right after
its ServerHello, where Firefox goes on (refraction-networking/utls#402).
prizrak-utls now keeps the keys of every classical key share and uses the
one the server selected (backport of refraction-networking/utls#416, as
in Jolymmiles/utls). The ClientHello is unchanged.
legiz-ru added a commit to legiz-ru/Prizrak-Core that referenced this pull request Nov 2, 2026
…-256

Firefox fingerprints send key shares for X25519 and then P-256, and uTLS
kept the private key of the first only: a server that selected P-256
failed the handshake with "tls: invalid server key share" right after
its ServerHello, where Firefox goes on (refraction-networking/utls#402).
prizrak-utls now keeps the keys of every classical key share and uses the
one the server selected (backport of refraction-networking/utls#416, as
in Jolymmiles/utls). The ClientHello is unchanged.
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