mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-17 18:16:08 +00:00
fix(hysteria2): emit pinSHA256 as hex in subscriptions, not base64
Hysteria2 clients backed by Xray-core hex-decode the pinSHA256 URI param and crash on the base64 value the panel stores for pinnedPeerCertSha256 (xray-core native TLS format). Normalize each pin to bare lowercase hex when building the Hysteria link, accepting base64, bare hex, and colon-separated openssl fingerprints; values that are neither are passed through untouched. Applied in both the backend subscription generator and the frontend link builder. The pcs share-link and JSON-sub paths keep base64 for their consumers. Fixes #4818.
This commit is contained in:
@@ -780,6 +780,40 @@ func TestHasFinalMaskContent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHysteriaPinHex(t *testing.T) {
|
||||
const hexPin = "c847dd2395d0978c0780b8201c4b289a8b281597d47c275f2d77d3f96d8de9c4"
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
// Std base64 (xray-core's native TLS format / the panel generate button)
|
||||
// must be re-encoded to the hex form Hysteria2 clients expect (#4818).
|
||||
{"std base64", "yEfdI5XQl4wHgLggHEsomosoFZfUfCdfLXfT+W2N6cQ=", hexPin},
|
||||
// A manually pasted hex fingerprint passes through (lowercased).
|
||||
{"hex passthrough", hexPin, hexPin},
|
||||
{"uppercase hex lowercased", strings.ToUpper(hexPin), hexPin},
|
||||
// openssl x509 -fingerprint -sha256 emits colon-separated hex.
|
||||
{"colon hex stripped", "C8:47:DD:23:95:D0:97:8C:07:80:B8:20:1C:4B:28:9A:8B:28:15:97:D4:7C:27:5F:2D:77:D3:F9:6D:8D:E9:C4", hexPin},
|
||||
{"surrounding whitespace trimmed", " " + hexPin + " ", hexPin},
|
||||
// URL-safe base64 with the same 32 bytes decodes identically.
|
||||
{"url-safe base64", "yEfdI5XQl4wHgLggHEsomosoFZfUfCdfLXfT-W2N6cQ=", hexPin},
|
||||
// Garbage that is neither valid hex nor a 32-byte base64 is left as-is
|
||||
// rather than silently dropped.
|
||||
{"unrecognized passthrough", "not-a-pin", "not-a-pin"},
|
||||
{"empty", "", ""},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if got := hysteriaPinHex(tc.in); got != tc.want {
|
||||
t.Fatalf("hysteriaPinHex(%q) = %q, want %q", tc.in, got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHysteriaHopPorts(t *testing.T) {
|
||||
withHop := func(ports any) map[string]any {
|
||||
return map[string]any{
|
||||
|
||||
Reference in New Issue
Block a user