This appears related to #4974 and #5031, which report the same tls: CurvePreferences includes unsupported curve symptom.
This report provides a packet-level reproduction of one concrete mechanism that triggers this error on Xray-core v26.7.28 with REALITY.
Environment
- Client: Xray-core v26.7.28
- Server: Xray-core v26.7.28
- Protocol/transport/security: VLESS + XHTTP + REALITY
- Client fingerprint:
randomized
Only the client fingerprint was changed during the comparison.
The same client profile and server work consistently with fingerprint: firefox, including dozens of consecutive successful connections.
Reproduction
-
Configure an Xray-core v26.7.28 client to use VLESS + XHTTP + REALITY with:
"fingerprint": "randomized"
-
Start the client process and attempt to connect.
-
Restart the client process between test runs so that Xray generates different randomized seeds.
-
With some generated randomized specifications, REALITY handshakes repeatedly fail for the lifetime of that client process.
-
Change only the fingerprint to:
-
Connections then complete successfully using the same server and the same remaining client settings.
Observed behavior
A failing randomized ClientHello advertises X25519MLKEM768 in supported_groups, but does not provide an initial key share for that group:
supported_groups:
X25519MLKEM768 (0x11ec)
X25519 (0x001d)
secp256r1 (0x0017)
secp384r1 (0x0018)
key_share:
X25519 (0x001d)
0x11ec is the registered X25519MLKEM768 TLS Supported Group:
https://www.rfc-editor.org/rfc/rfc10024.html#section-7.1
The REALITY server responds with a TLS 1.3 HelloRetryRequest.
The ServerHello Random contains the standard TLS 1.3 HRR value:
cf 21 ad 74 e5 9a 61 11
be 1d 8c 02 1e 65 b8 91
c2 a2 11 16 7a bb 8c 5e
07 9e 09 e2 c8 a8 33 9c
The HRR selects X25519MLKEM768:
key_share selected_group:
00 33 00 02 11 ec
The client then sends:
14 03 03 00 01 01
15 03 03 00 02 02 50
These records decode as:
ChangeCipherSpec
fatal internal_error (80)
The TLS connection is then closed before any VLESS or XHTTP application data is exchanged. At the application level, this appears as a connected VPN tunnel with traffic stalling or pages loading indefinitely.
Protocol analysis
The initial ClientHello is not invalid merely because it advertises X25519MLKEM768 without providing an initial key share for that group.
The current TLS 1.3 specification states that key_share may contain shares for some or all groups listed in supported_groups.
If the server selects a mutually supported group for which the initial ClientHello does not contain a compatible key share, the server must respond with a HelloRetryRequest:
https://www.rfc-editor.org/rfc/rfc9846.html#section-4.2.1
After receiving an HRR containing a key_share selection, the client must send a second ClientHello whose key-share list contains a single entry for the selected group:
https://www.rfc-editor.org/rfc/rfc9846.html#section-4.2.2
Therefore, the server's HRR selecting X25519MLKEM768 is valid for this ClientHello.
Root cause
Xray-core v26.7.28 pins the following uTLS revision:
github.com/refraction-networking/utls
v1.8.3-0.20260301010127-aa6edf4b11af
In this revision, the randomized ClientHello generator independently decides whether to:
- include
X25519MLKEM768 in supported_groups;
- include an initial
X25519MLKEM768 key share.
The two independent generator paths are shown here:
The generator can therefore produce a ClientHello that advertises X25519MLKEM768, while its initial key_share contains only X25519.
In the same uTLS revision, the TLS 1.3 HRR handler explicitly does not support selecting X25519MLKEM768 through an HRR:
// Note: we don't support selecting X25519MLKEM768 in a HRR, because it
// is currently first in preference order, so if it's enabled we'll
// always send a key share for it.
However, this assumption does not hold for every ClientHello produced by the randomized generator.
When the HRR selects X25519MLKEM768, the client executes:
c.sendAlert(alertInternalError)
return errors.New("tls: CurvePreferences includes unsupported curve")
Relevant HRR handler code:
https://github.com/refraction-networking/utls/blob/aa6edf4b11af82e110eea845bb2983d30138d651/handshake_client_tls13.go#L325-L346
The observed fatal internal_error (80) therefore matches this source-code path.
REALITY server behavior
The REALITY server prioritizes a mutually supported post-quantum key-exchange group.
When the selected group has no corresponding initial client key share, the server calls doHelloRetryRequest(selectedGroup):
https://github.com/XTLS/REALITY/blob/9234c772ba8f181f31c3e81dc2b4177322e5a9a9/handshake_server_tls13.go#L319-L335
Consequently, when the randomized ClientHello advertises X25519MLKEM768 without its key share, REALITY selects that group and sends the HRR that exposes the unsupported uTLS client path.
Xray seed persistence
Xray creates one PRNG seed for the randomized fingerprint during package initialization and reuses that seeded ClientHelloID:
|
weights := utls.DefaultWeights |
|
weights.TLSVersMax_Set_VersionTLS13 = 1 |
|
weights.FirstKeyShare_Set_CurveP256 = 0 |
|
randomized := utls.HelloRandomizedALPN |
|
randomized.Seed, _ = utls.NewPRNGSeed() |
|
randomized.Weights = &weights |
|
randomizednoalpn := utls.HelloRandomizedNoALPN |
|
randomizednoalpn.Seed, _ = utls.NewPRNGSeed() |
|
randomizednoalpn.Weights = &weights |
|
PresetFingerprints["randomized"] = &randomized |
|
PresetFingerprints["randomizednoalpn"] = &randomizednoalpn |
Xray reuses the same seed for the lifetime of the client process, deterministically producing the same randomized specification. Therefore, if that specification advertises X25519MLKEM768 without the corresponding initial key share, connections to a server that selects this group through HRR continue to fail until the client process is restarted.
Restarting the client process generates a fresh randomized seed and may temporarily avoid the problem.
Expected behavior
fingerprint: randomized should not produce a ClientHello that advertises a key-exchange group which the embedded TLS implementation cannot provide after a valid HelloRetryRequest.
The handshake should not abort with fatal internal_error in response to a valid HRR selecting a group advertised by the client.
Possible implementation directions
Possible solutions include:
- ensure that randomized always includes an initial
X25519MLKEM768 key share whenever it advertises that group;
- exclude
X25519MLKEM768 from randomized supported_groups until its HRR path is supported;
- implement
X25519MLKEM768 HRR support in uTLS.
Related issues
This appears related to #4974 and #5031, which report the same
tls: CurvePreferences includes unsupported curvesymptom.This report provides a packet-level reproduction of one concrete mechanism that triggers this error on Xray-core v26.7.28 with REALITY.
Environment
randomizedOnly the client fingerprint was changed during the comparison.
The same client profile and server work consistently with
fingerprint: firefox, including dozens of consecutive successful connections.Reproduction
Configure an Xray-core v26.7.28 client to use VLESS + XHTTP + REALITY with:
Start the client process and attempt to connect.
Restart the client process between test runs so that Xray generates different randomized seeds.
With some generated randomized specifications, REALITY handshakes repeatedly fail for the lifetime of that client process.
Change only the fingerprint to:
Connections then complete successfully using the same server and the same remaining client settings.
Observed behavior
A failing randomized ClientHello advertises
X25519MLKEM768insupported_groups, but does not provide an initial key share for that group:0x11ecis the registeredX25519MLKEM768TLS Supported Group:https://www.rfc-editor.org/rfc/rfc10024.html#section-7.1
The REALITY server responds with a TLS 1.3 HelloRetryRequest.
The ServerHello Random contains the standard TLS 1.3 HRR value:
The HRR selects
X25519MLKEM768:The client then sends:
These records decode as:
The TLS connection is then closed before any VLESS or XHTTP application data is exchanged. At the application level, this appears as a connected VPN tunnel with traffic stalling or pages loading indefinitely.
Protocol analysis
The initial ClientHello is not invalid merely because it advertises
X25519MLKEM768without providing an initial key share for that group.The current TLS 1.3 specification states that
key_sharemay contain shares for some or all groups listed insupported_groups.If the server selects a mutually supported group for which the initial ClientHello does not contain a compatible key share, the server must respond with a HelloRetryRequest:
https://www.rfc-editor.org/rfc/rfc9846.html#section-4.2.1
After receiving an HRR containing a
key_shareselection, the client must send a second ClientHello whose key-share list contains a single entry for the selected group:https://www.rfc-editor.org/rfc/rfc9846.html#section-4.2.2
Therefore, the server's HRR selecting
X25519MLKEM768is valid for this ClientHello.Root cause
Xray-core v26.7.28 pins the following uTLS revision:
In this revision, the randomized ClientHello generator independently decides whether to:
X25519MLKEM768insupported_groups;X25519MLKEM768key share.The two independent generator paths are shown here:
supported_groupsgeneration:https://github.com/refraction-networking/utls/blob/aa6edf4b11af82e110eea845bb2983d30138d651/u_parrots.go#L3341-L3353
Initial
key_sharegeneration:https://github.com/refraction-networking/utls/blob/aa6edf4b11af82e110eea845bb2983d30138d651/u_parrots.go#L3392-L3404
The generator can therefore produce a ClientHello that advertises
X25519MLKEM768, while its initialkey_sharecontains onlyX25519.In the same uTLS revision, the TLS 1.3 HRR handler explicitly does not support selecting
X25519MLKEM768through an HRR:However, this assumption does not hold for every ClientHello produced by the randomized generator.
When the HRR selects
X25519MLKEM768, the client executes:Relevant HRR handler code:
https://github.com/refraction-networking/utls/blob/aa6edf4b11af82e110eea845bb2983d30138d651/handshake_client_tls13.go#L325-L346
The observed
fatal internal_error (80)therefore matches this source-code path.REALITY server behavior
The REALITY server prioritizes a mutually supported post-quantum key-exchange group.
When the selected group has no corresponding initial client key share, the server calls
doHelloRetryRequest(selectedGroup):https://github.com/XTLS/REALITY/blob/9234c772ba8f181f31c3e81dc2b4177322e5a9a9/handshake_server_tls13.go#L319-L335
Consequently, when the randomized ClientHello advertises
X25519MLKEM768without its key share, REALITY selects that group and sends the HRR that exposes the unsupported uTLS client path.Xray seed persistence
Xray creates one PRNG seed for the randomized fingerprint during package initialization and reuses that seeded
ClientHelloID:Xray-core/transport/internet/tls/tls.go
Lines 174 to 184 in 5ca6f4b
Xray reuses the same seed for the lifetime of the client process, deterministically producing the same randomized specification. Therefore, if that specification advertises
X25519MLKEM768without the corresponding initial key share, connections to a server that selects this group through HRR continue to fail until the client process is restarted.Restarting the client process generates a fresh randomized seed and may temporarily avoid the problem.
Expected behavior
fingerprint: randomizedshould not produce a ClientHello that advertises a key-exchange group which the embedded TLS implementation cannot provide after a valid HelloRetryRequest.The handshake should not abort with
fatal internal_errorin response to a valid HRR selecting a group advertised by the client.Possible implementation directions
Possible solutions include:
X25519MLKEM768key share whenever it advertises that group;X25519MLKEM768from randomizedsupported_groupsuntil its HRR path is supported;X25519MLKEM768HRR support in uTLS.Related issues
randomizedfingerprint problem: CurvePreferences includes unsupported curve refraction-networking/utls#366