Refine fingerprints

Fixes https://github.com/XTLS/Xray-core/issues/1577
This commit is contained in:
RPRX 2023-02-01 12:58:17 +00:00 committed by GitHub
parent 74416570d4
commit dc72cf2c78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 60 additions and 50 deletions

View File

@ -379,6 +379,9 @@ func (c *TLSConfig) Build() (proto.Message, error) {
config.CipherSuites = c.CipherSuites config.CipherSuites = c.CipherSuites
config.PreferServerCipherSuites = c.PreferServerCipherSuites config.PreferServerCipherSuites = c.PreferServerCipherSuites
config.Fingerprint = strings.ToLower(c.Fingerprint) config.Fingerprint = strings.ToLower(c.Fingerprint)
if config.Fingerprint != "" && tls.GetFingerprint(config.Fingerprint) == nil {
return nil, newError(`unknown fingerprint: `, config.Fingerprint)
}
config.RejectUnknownSni = c.RejectUnknownSNI config.RejectUnknownSni = c.RejectUnknownSNI
if c.PinnedPeerCertificateChainSha256 != nil { if c.PinnedPeerCertificateChainSha256 != nil {

View File

@ -122,7 +122,7 @@ func getGrpcClient(ctx context.Context, dest net.Destination, streamSettings *in
if tlsConfig != nil { if tlsConfig != nil {
var transportCredential credentials.TransportCredentials var transportCredential credentials.TransportCredentials
if fingerprint, exists := tls.GetFingerprint(ctx, tlsConfig.Fingerprint); exists { if fingerprint := tls.GetFingerprint(tlsConfig.Fingerprint); fingerprint != nil {
transportCredential = tls.NewGrpcUtls(tlsConfig.GetTLSConfig(), fingerprint) transportCredential = tls.NewGrpcUtls(tlsConfig.GetTLSConfig(), fingerprint)
} else { // Fallback to normal gRPC TLS } else { // Fallback to normal gRPC TLS
transportCredential = credentials.NewTLS(tlsConfig.GetTLSConfig()) transportCredential = credentials.NewTLS(tlsConfig.GetTLSConfig())

View File

@ -75,7 +75,7 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
} }
var cn tls.Interface var cn tls.Interface
if fingerprint, ok := tls.GetFingerprint(ctx, tlsConfigs.Fingerprint); ok { if fingerprint := tls.GetFingerprint(tlsConfigs.Fingerprint); fingerprint != nil {
cn = tls.UClient(pconn, tlsConfig, fingerprint).(*tls.UConn) cn = tls.UClient(pconn, tlsConfig, fingerprint).(*tls.UConn)
} else { } else {
cn = tls.Client(pconn, tlsConfig).(*tls.Conn) cn = tls.Client(pconn, tlsConfig).(*tls.Conn)

View File

@ -22,7 +22,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
if config := tls.ConfigFromStreamSettings(streamSettings); config != nil { if config := tls.ConfigFromStreamSettings(streamSettings); config != nil {
tlsConfig := config.GetTLSConfig(tls.WithDestination(dest)) tlsConfig := config.GetTLSConfig(tls.WithDestination(dest))
if fingerprint, ok := tls.GetFingerprint(ctx, config.Fingerprint); ok { if fingerprint := tls.GetFingerprint(config.Fingerprint); fingerprint != nil {
conn = tls.UClient(conn, tlsConfig, fingerprint) conn = tls.UClient(conn, tlsConfig, fingerprint)
if err := conn.(*tls.UConn).Handshake(); err != nil { if err := conn.(*tls.UConn).Handshake(); err != nil {
return nil, err return nil, err

View File

@ -1,7 +1,6 @@
package tls package tls
import ( import (
"context"
"crypto/rand" "crypto/rand"
"crypto/tls" "crypto/tls"
"math/big" "math/big"
@ -9,15 +8,12 @@ import (
utls "github.com/refraction-networking/utls" utls "github.com/refraction-networking/utls"
"github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/session"
) )
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen //go:generate go run github.com/xtls/xray-core/common/errors/errorgen
var _ buf.Writer = (*Conn)(nil) var _ buf.Writer = (*Conn)(nil)
var XrayRandom *utls.ClientHelloID
type Conn struct { type Conn struct {
*tls.Conn *tls.Conn
} }
@ -118,58 +114,51 @@ func copyConfig(c *tls.Config) *utls.Config {
} }
} }
func GetFingerprint(ctx context.Context, config string) (*utls.ClientHelloID, bool) { func init() {
if XrayRandom == nil { bigInt, _ := rand.Int(rand.Reader, big.NewInt(int64(len(ModernFingerprints))))
// lazy init stopAt := int(bigInt.Int64())
for k, v := range FingerprintsForRNG { i := 0
Fingerprints[k] = v for _, v := range ModernFingerprints {
} if i == stopAt {
big, err := rand.Int(rand.Reader, big.NewInt(int64(len(FingerprintsForRNG)))) PresetFingerprints["random"] = v
if err != nil { break
newError("failed to generate xray random fingerprint").Base(err).WriteToLog(session.ExportIDToError(ctx))
}
var i = int(big.Int64())
count := 0
for k, v := range FingerprintsForRNG {
if count == i {
newError("xray random fingerprint: ", k).WriteToLog(session.ExportIDToError(ctx))
XrayRandom = v
break
}
count++
} }
i++
} }
if config == "random" {
return XrayRandom, true
}
fingerprint, ok := Fingerprints[config]
return fingerprint, ok
} }
var Fingerprints = map[string]*utls.ClientHelloID{ func GetFingerprint(name string) (fingerprint *utls.ClientHelloID) {
if name == "" {
return
}
if fingerprint = PresetFingerprints[name]; fingerprint != nil {
return
}
if fingerprint = ModernFingerprints[name]; fingerprint != nil {
return
}
if fingerprint = OtherFingerprints[name]; fingerprint != nil {
return
}
return
}
var PresetFingerprints = map[string]*utls.ClientHelloID{
// Recommended preset options in GUI clients
"chrome": &utls.HelloChrome_Auto, "chrome": &utls.HelloChrome_Auto,
"firefox": &utls.HelloFirefox_Auto, "firefox": &utls.HelloFirefox_Auto,
"safari": &utls.HelloSafari_Auto, "safari": &utls.HelloSafari_Auto,
"ios": &utls.HelloIOS_Auto,
"android": &utls.HelloAndroid_11_OkHttp,
"edge": &utls.HelloEdge_Auto,
"360": &utls.Hello360_Auto,
"qq": &utls.HelloQQ_Auto,
"random": nil,
"randomized": &utls.HelloRandomized, "randomized": &utls.HelloRandomized,
// This is a bit lame, but it seems there is no good way to reflect variables from Golang package
// We don't RNG for go, randomized, or fingerprints that is more than 4 years old
"hellogolang": &utls.HelloGolang,
"hellorandomized": &utls.HelloRandomized,
"hellorandomizedalpn": &utls.HelloRandomizedALPN,
"hellorandomizednoalpn": &utls.HelloRandomizedNoALPN,
"hellofirefox_55": &utls.HelloFirefox_55,
"hellofirefox_56": &utls.HelloFirefox_56,
"hellofirefox_63": &utls.HelloFirefox_63,
"hellofirefox_65": &utls.HelloFirefox_65,
"hellochrome_58": &utls.HelloChrome_58,
"hellochrome_62": &utls.HelloChrome_62,
"hellochrome_70": &utls.HelloChrome_70,
"hellochrome_72": &utls.HelloChrome_72,
"helloios_11_1": &utls.HelloIOS_11_1,
"hello360_7_5": &utls.Hello360_7_5,
} }
var FingerprintsForRNG = map[string]*utls.ClientHelloID{ var ModernFingerprints = map[string]*utls.ClientHelloID{
// One of these will be chosen as `random` at startup
"hellofirefox_auto": &utls.HelloFirefox_Auto, "hellofirefox_auto": &utls.HelloFirefox_Auto,
"hellofirefox_99": &utls.HelloFirefox_99, "hellofirefox_99": &utls.HelloFirefox_99,
"hellofirefox_102": &utls.HelloFirefox_102, "hellofirefox_102": &utls.HelloFirefox_102,
@ -197,6 +186,24 @@ var FingerprintsForRNG = map[string]*utls.ClientHelloID{
"helloqq_11_1": &utls.HelloQQ_11_1, "helloqq_11_1": &utls.HelloQQ_11_1,
} }
var OtherFingerprints = map[string]*utls.ClientHelloID{
// Golang, randomized, and fingerprints that are more than 4 years old
"hellogolang": &utls.HelloGolang,
"hellorandomized": &utls.HelloRandomized,
"hellorandomizedalpn": &utls.HelloRandomizedALPN,
"hellorandomizednoalpn": &utls.HelloRandomizedNoALPN,
"hellofirefox_55": &utls.HelloFirefox_55,
"hellofirefox_56": &utls.HelloFirefox_56,
"hellofirefox_63": &utls.HelloFirefox_63,
"hellofirefox_65": &utls.HelloFirefox_65,
"hellochrome_58": &utls.HelloChrome_58,
"hellochrome_62": &utls.HelloChrome_62,
"hellochrome_70": &utls.HelloChrome_70,
"hellochrome_72": &utls.HelloChrome_72,
"helloios_11_1": &utls.HelloIOS_11_1,
"hello360_7_5": &utls.Hello360_7_5,
}
type Interface interface { type Interface interface {
net.Conn net.Conn
Handshake() error Handshake() error

View File

@ -86,7 +86,7 @@ func dialWebSocket(ctx context.Context, dest net.Destination, streamSettings *in
protocol = "wss" protocol = "wss"
tlsConfig := config.GetTLSConfig(tls.WithDestination(dest), tls.WithNextProto("http/1.1")) tlsConfig := config.GetTLSConfig(tls.WithDestination(dest), tls.WithNextProto("http/1.1"))
dialer.TLSClientConfig = tlsConfig dialer.TLSClientConfig = tlsConfig
if fingerprint, exists := tls.GetFingerprint(ctx, config.Fingerprint); exists { if fingerprint := tls.GetFingerprint(config.Fingerprint); fingerprint != nil {
dialer.NetDialTLSContext = func(_ context.Context, _, addr string) (gonet.Conn, error) { dialer.NetDialTLSContext = func(_ context.Context, _, addr string) (gonet.Conn, error) {
// Like the NetDial in the dialer // Like the NetDial in the dialer
pconn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings) pconn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings)