mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-16 15:17:14 +00:00
fix(sub): enable ML-KEM for Mihomo REALITY subscriptions (#6451)
This commit is contained in:
@@ -1072,8 +1072,11 @@ func (s *SubClashService) applySecurity(proxy map[string]any, security string, s
|
||||
realityOpts["short-id"] = shortID
|
||||
}
|
||||
if len(realityOpts) > 0 {
|
||||
// Xray 26.9.8+ rejects REALITY handshakes without an ML-KEM key share.
|
||||
realityOpts["support-x25519mlkem768"] = true
|
||||
proxy["reality-opts"] = realityOpts
|
||||
}
|
||||
proxy["client-fingerprint"] = "chrome"
|
||||
if fingerprint, ok := realitySettings["fingerprint"].(string); ok && fingerprint != "" {
|
||||
proxy["client-fingerprint"] = fingerprint
|
||||
}
|
||||
|
||||
@@ -161,6 +161,51 @@ func TestBuildProxy_VLESSRealityFieldsForClash(t *testing.T) {
|
||||
if opts["short-id"] != "ab12" {
|
||||
t.Fatalf("short-id = %v, want ab12", opts["short-id"])
|
||||
}
|
||||
if opts["support-x25519mlkem768"] != true {
|
||||
t.Fatalf("ML-KEM support = %v, want true", opts["support-x25519mlkem768"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestClashRealityMLKEMAcrossSources(t *testing.T) {
|
||||
svc := NewSubClashService(false, "", &SubService{})
|
||||
for _, security := range []string{"reality", "tls", "none"} {
|
||||
for _, fingerprint := range []string{"", "chrome", "firefox"} {
|
||||
t.Run(security+"/"+fingerprint, func(t *testing.T) {
|
||||
inbound := &model.Inbound{Listen: "example.com", Port: 443, Protocol: model.VLESS, Settings: `{"encryption":"none"}`}
|
||||
client := model.Client{ID: "11111111-2222-4333-8444-555555555555"}
|
||||
stream := svc.streamData(fmt.Sprintf(`{"network":"tcp","security":%q,"realitySettings":{"serverNames":["example.com"],"shortIds":["ab12"],"settings":{"publicKey":"PBKvalue","fingerprint":%q}}}`, security, fingerprint))
|
||||
link := "vless://" + client.ID + "@example.com:443?type=tcp&security=" + security + "&sni=example.com&pbk=PBKvalue&sid=ab12&fp=" + fingerprint
|
||||
for source, proxy := range map[string]map[string]any{
|
||||
"inbound": svc.buildProxy(svc.SubService, inbound, client, stream, nil),
|
||||
"external": svc.clashProxyFromExternal(link, "external"),
|
||||
} {
|
||||
if proxy == nil {
|
||||
t.Fatalf("%s: missing proxy", source)
|
||||
}
|
||||
opts, exists := proxy["reality-opts"].(map[string]any)
|
||||
if security != "reality" {
|
||||
if exists {
|
||||
t.Fatalf("%s: REALITY options leaked into %s: %#v", source, security, opts)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if opts["support-x25519mlkem768"] != true || opts["public-key"] != "PBKvalue" || opts["short-id"] != "ab12" {
|
||||
t.Fatalf("%s: incorrect REALITY options: %#v", source, opts)
|
||||
}
|
||||
wantFingerprint := fingerprint
|
||||
if wantFingerprint == "" {
|
||||
wantFingerprint = "chrome"
|
||||
}
|
||||
if proxy["client-fingerprint"] != wantFingerprint {
|
||||
t.Fatalf("%s: fingerprint = %v, want %s", source, proxy["client-fingerprint"], wantFingerprint)
|
||||
}
|
||||
if legacyClashProxy(proxy) != nil {
|
||||
t.Fatalf("%s: REALITY must stay excluded from legacy Clash", source)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestApplyTransport_TCPHeader pins the tcp-header validation (clash_service.go ~359):
|
||||
|
||||
Reference in New Issue
Block a user