From 987e8e8241a38cff193b9af6bd29482b6afddd9f Mon Sep 17 00:00:00 2001 From: eMeab <32988354+eMeab@users.noreply.github.com> Date: Mon, 25 Jan 2021 17:21:53 +0800 Subject: [PATCH 1/4] add uTLS --- go.mod | 1 + go.sum | 2 ++ infra/conf/transport_internet.go | 2 ++ transport/internet/tcp/dialer.go | 12 +++++++- transport/internet/tls/config.pb.go | 31 +++++++++++++------- transport/internet/tls/config.proto | 3 ++ transport/internet/tls/tls.go | 45 +++++++++++++++++++++++------ 7 files changed, 76 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index 7a54f8ffdec0..4bd97b8c0002 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/miekg/dns v1.1.35 github.com/pelletier/go-toml v1.8.1 github.com/pires/go-proxyproto v0.4.1 + github.com/refraction-networking/utls v0.0.0-20201210053706-2179f286686b github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c github.com/stretchr/testify v1.7.0 github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499 diff --git a/go.sum b/go.sum index c8a21836dd37..77f62b4b0700 100644 --- a/go.sum +++ b/go.sum @@ -137,6 +137,8 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1: github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/refraction-networking/utls v0.0.0-20201210053706-2179f286686b h1:lzo71oHzQEz0fKMSjR0BpVzuh2hOHvJTxnN3Rnikmtg= +github.com/refraction-networking/utls v0.0.0-20201210053706-2179f286686b/go.mod h1:tz9gX959MEFfFN5whTIocCLUG57WiILqtdVxI8c6Wj0= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c h1:pqy40B3MQWYrza7YZXOXgl0Nf0QGFqrOC0BKae1UNAA= github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg= diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index adaec4ed3630..6e58f9a92ec0 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -300,6 +300,7 @@ type TLSConfig struct { MaxVersion string `json:"maxVersion"` CipherSuites string `json:"cipherSuites"` PreferServerCipherSuites bool `json:"preferServerCipherSuites"` + Fingerprint string `json:"fingerprint"` } // Build implements Buildable. @@ -321,6 +322,7 @@ func (c *TLSConfig) Build() (proto.Message, error) { if c.ALPN != nil && len(*c.ALPN) > 0 { config.NextProtocol = []string(*c.ALPN) } + config.Fingerprint = c.Fingerprint config.EnableSessionResumption = c.EnableSessionResumption config.DisableSystemRoot = c.DisableSystemRoot config.MinVersion = c.MinVersion diff --git a/transport/internet/tcp/dialer.go b/transport/internet/tcp/dialer.go index c8fda92588a1..d241ec530954 100644 --- a/transport/internet/tcp/dialer.go +++ b/transport/internet/tcp/dialer.go @@ -21,6 +21,17 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me if config := tls.ConfigFromStreamSettings(streamSettings); config != nil { tlsConfig := config.GetTLSConfig(tls.WithDestination(dest)) + if config.Fingerprint != "" { + fingerprint, err := tls.GetuTLSClientHelloID(config.Fingerprint) + if err != nil { + conn = tls.Client(conn, tlsConfig) + newError("Switching to TLS.").Base(err).AtWarning().WriteToLog() + } else { + conn = tls.UClient(conn, tlsConfig, *fingerprint) + } + } else { + conn = tls.Client(conn, tlsConfig) + } /* if config.IsExperiment8357() { conn = tls.UClient(conn, tlsConfig) @@ -28,7 +39,6 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me conn = tls.Client(conn, tlsConfig) } */ - conn = tls.Client(conn, tlsConfig) } else if config := xtls.ConfigFromStreamSettings(streamSettings); config != nil { xtlsConfig := config.GetXTLSConfig(xtls.WithDestination(dest)) conn = xtls.Client(conn, xtlsConfig) diff --git a/transport/internet/tls/config.pb.go b/transport/internet/tls/config.pb.go index c2425cd10d89..3bd74f2a7d1a 100644 --- a/transport/internet/tls/config.pb.go +++ b/transport/internet/tls/config.pb.go @@ -173,6 +173,8 @@ type Config struct { CipherSuites string `protobuf:"bytes,9,opt,name=cipher_suites,json=cipherSuites,proto3" json:"cipher_suites,omitempty"` // Whether the server selects its most preferred ciphersuite. PreferServerCipherSuites bool `protobuf:"varint,10,opt,name=prefer_server_cipher_suites,json=preferServerCipherSuites,proto3" json:"prefer_server_cipher_suites,omitempty"` + // ClientHello fingerprinting resistance(utls) + Fingerprint string `protobuf:"bytes,11,opt,name=fingerprint,proto3" json:"fingerprint,omitempty"` } func (x *Config) Reset() { @@ -277,6 +279,13 @@ func (x *Config) GetPreferServerCipherSuites() bool { return false } +func (x *Config) GetFingerprint() string { + if x != nil { + return x.Fingerprint + } + return "" +} + var File_transport_internet_tls_config_proto protoreflect.FileDescriptor var file_transport_internet_tls_config_proto_rawDesc = []byte{ @@ -299,7 +308,7 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{ 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46, 0x59, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, - 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0xd3, 0x03, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, + 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0xf5, 0x03, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x63, 0x65, 0x72, @@ -328,15 +337,17 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{ 0x0a, 0x1b, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x42, 0x73, 0x0a, - 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, - 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, - 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, - 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, - 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, - 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, - 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x12, 0x20, 0x0a, + 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x0b, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x42, + 0x73, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, + 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, + 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, + 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, + 0x2e, 0x54, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/transport/internet/tls/config.proto b/transport/internet/tls/config.proto index 6905473481fd..9b79ede5d80a 100644 --- a/transport/internet/tls/config.proto +++ b/transport/internet/tls/config.proto @@ -55,4 +55,7 @@ message Config { // Whether the server selects its most preferred ciphersuite. bool prefer_server_cipher_suites = 10; + + // ClientHello fingerprinting resistance(utls) + string fingerprint = 11; } diff --git a/transport/internet/tls/tls.go b/transport/internet/tls/tls.go index 5704214bc4fe..faa49eb0de74 100644 --- a/transport/internet/tls/tls.go +++ b/transport/internet/tls/tls.go @@ -3,6 +3,7 @@ package tls import ( "crypto/tls" + utls "github.com/refraction-networking/utls" "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/net" ) @@ -41,22 +42,48 @@ func Client(c net.Conn, config *tls.Config) net.Conn { return &Conn{Conn: tlsConn} } -/* func copyConfig(c *tls.Config) *utls.Config { + //return &utls.Config{ + // NextProtos: c.NextProtos, + // ServerName: c.ServerName, + // InsecureSkipVerify: c.InsecureSkipVerify, + // MinVersion: c.MinVersion, + // MaxVersion: c.MaxVersion, + //} return &utls.Config{ - NextProtos: c.NextProtos, - ServerName: c.ServerName, - InsecureSkipVerify: c.InsecureSkipVerify, - MinVersion: utls.VersionTLS12, - MaxVersion: utls.VersionTLS12, + RootCAs: c.RootCAs, + NextProtos: c.NextProtos, + ServerName: c.ServerName, + InsecureSkipVerify: c.InsecureSkipVerify, + CipherSuites: c.CipherSuites, + PreferServerCipherSuites: c.PreferServerCipherSuites, + SessionTicketsDisabled: c.SessionTicketsDisabled, + MinVersion: c.MinVersion, + MaxVersion: c.MaxVersion, } } -func UClient(c net.Conn, config *tls.Config) net.Conn { +func GetuTLSClientHelloID(name string) (*utls.ClientHelloID, error) { + switch name { + case "randomized": + return &utls.HelloRandomized, nil + case "chrome": + return &utls.HelloChrome_Auto, nil + case "firefox": + return &utls.HelloFirefox_Auto, nil + case "ios": + return &utls.HelloIOS_Auto, nil + default: + return nil, newError("invalid fingerprint: " + name) + } +} + +func UClient(c net.Conn, config *tls.Config, clientHelloID utls.ClientHelloID) net.Conn { uConfig := copyConfig(config) - return utls.Client(c, uConfig) + conn := utls.UClient(c, uConfig, clientHelloID) + conn.Handshake() + return conn } -*/ // Server initiates a TLS server handshake on the given connection. func Server(c net.Conn, config *tls.Config) net.Conn { From 7b02a934655566be27f1232ecfa38605588baf4e Mon Sep 17 00:00:00 2001 From: eMeab <32988354+eMeab@users.noreply.github.com> Date: Thu, 28 Jan 2021 14:36:32 +0800 Subject: [PATCH 2/4] change Randomized to random --- transport/internet/tls/tls.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/internet/tls/tls.go b/transport/internet/tls/tls.go index faa49eb0de74..1f171d51bab9 100644 --- a/transport/internet/tls/tls.go +++ b/transport/internet/tls/tls.go @@ -65,7 +65,7 @@ func copyConfig(c *tls.Config) *utls.Config { func GetuTLSClientHelloID(name string) (*utls.ClientHelloID, error) { switch name { - case "randomized": + case "random": return &utls.HelloRandomized, nil case "chrome": return &utls.HelloChrome_Auto, nil From f0ceb17ea61eaad3b18e45b93eeca0f48c314da3 Mon Sep 17 00:00:00 2001 From: eMeab <32988354+eMeab@users.noreply.github.com> Date: Thu, 28 Jan 2021 21:15:58 +0800 Subject: [PATCH 3/4] optimization uTLS Log --- transport/internet/tcp/dialer.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/transport/internet/tcp/dialer.go b/transport/internet/tcp/dialer.go index d241ec530954..ae8a874220cd 100644 --- a/transport/internet/tcp/dialer.go +++ b/transport/internet/tcp/dialer.go @@ -25,9 +25,10 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me fingerprint, err := tls.GetuTLSClientHelloID(config.Fingerprint) if err != nil { conn = tls.Client(conn, tlsConfig) - newError("Switching to TLS.").Base(err).AtWarning().WriteToLog() + newError("switching to TLS.").Base(err).AtWarning().WriteToLog() } else { conn = tls.UClient(conn, tlsConfig, *fingerprint) + newError("switching to uTLS. fingerprint: " + fingerprint.Str()).AtInfo().WriteToLog() } } else { conn = tls.Client(conn, tlsConfig) From 960b575e05bd5512b2f3305d7fbb544ef771396a Mon Sep 17 00:00:00 2001 From: eMeab <32988354+eMeab@users.noreply.github.com> Date: Thu, 28 Jan 2021 22:19:41 +0800 Subject: [PATCH 4/4] optimization uTLS config --- infra/conf/transport_internet.go | 2 +- transport/internet/tls/tls.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 6e58f9a92ec0..9ba6801a0c99 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -322,7 +322,7 @@ func (c *TLSConfig) Build() (proto.Message, error) { if c.ALPN != nil && len(*c.ALPN) > 0 { config.NextProtocol = []string(*c.ALPN) } - config.Fingerprint = c.Fingerprint + config.Fingerprint = strings.ToLower(c.Fingerprint) config.EnableSessionResumption = c.EnableSessionResumption config.DisableSystemRoot = c.DisableSystemRoot config.MinVersion = c.MinVersion diff --git a/transport/internet/tls/tls.go b/transport/internet/tls/tls.go index 1f171d51bab9..4852b8be234f 100644 --- a/transport/internet/tls/tls.go +++ b/transport/internet/tls/tls.go @@ -65,14 +65,14 @@ func copyConfig(c *tls.Config) *utls.Config { func GetuTLSClientHelloID(name string) (*utls.ClientHelloID, error) { switch name { - case "random": - return &utls.HelloRandomized, nil case "chrome": return &utls.HelloChrome_Auto, nil case "firefox": return &utls.HelloFirefox_Auto, nil - case "ios": + case "safari": return &utls.HelloIOS_Auto, nil + case "randomized": + return &utls.HelloRandomized, nil default: return nil, newError("invalid fingerprint: " + name) }