From 3b449b64b36de6778465a86277c40b4aa9cbfb53 Mon Sep 17 00:00:00 2001 From: notsure2 Date: Sun, 11 Jun 2023 01:31:09 +0300 Subject: [PATCH 1/3] Support ServerName randomization (by setting ServerName=random) using the same algorithm as ProtonVPN https://github.com/ProtonVPN/wireguard-go/commit/bcf344b39b213c1f32147851af0d2a8da9266883 --- README.md | 2 +- internal/client/TLS.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 48225a7..de6dbbc 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ random-like. **You may only leave it as `plain` if you are certain that your und encryption and authentication (via AEAD or similar techniques).** `ServerName` is the domain you want to make your ISP or firewall _think_ you are visiting. Ideally it should -match `RedirAddr` in the server's configuration, a major site the censor allows, but it doesn't have to. +match `RedirAddr` in the server's configuration, a major site the censor allows, but it doesn't have to. Use `random` to randomize the server name for every connection made. `AlternativeNames` is an array used alongside `ServerName` to shuffle between different ServerNames for every new connection. **This may conflict with `CDN` Transport mode** if the CDN provider prohibits domain fronting and rejects diff --git a/internal/client/TLS.go b/internal/client/TLS.go index 6c97ab9..469ad28 100644 --- a/internal/client/TLS.go +++ b/internal/client/TLS.go @@ -1,9 +1,14 @@ package client import ( + cryptoRand "crypto/rand" utls "github.com/refraction-networking/utls" log "github.com/sirupsen/logrus" + "math/big" + "math/rand" "net" + "strings" + "time" "github.com/cbeuw/Cloak/internal/common" ) @@ -30,6 +35,33 @@ type DirectTLS struct { browser browser } +var topLevelDomains = []string{"com", "net", "org", "it", "fr", "me", "ru", "cn", "es", "tr", "top", "xyz", "info"} + +// https://github.com/ProtonVPN/wireguard-go/commit/bcf344b39b213c1f32147851af0d2a8da9266883 +func randomServerName() string { + charNum := int('z') - int('a') + 1 + size := 3 + randInt(10) + name := make([]byte, size) + for i := range name { + name[i] = byte(int('a') + randInt(charNum)) + } + return string(name) + "." + randItem(topLevelDomains) +} + +func randItem(list []string) string { + return list[randInt(len(list))] +} + +func randInt(n int) int { + size, err := cryptoRand.Int(cryptoRand.Reader, big.NewInt(int64(n))) + if err == nil { + return int(size.Int64()) + } + //goland:noinspection GoDeprecation + rand.Seed(time.Now().UnixNano()) + return rand.Intn(n) +} + func buildClientHello(browser browser, fields clientHelloFields) ([]byte, error) { // We don't use utls to handle connections (as it'll attempt a real TLS negotiation) // We only want it to build the ClientHello locally @@ -89,6 +121,10 @@ func (tls *DirectTLS) Handshake(rawConn net.Conn, authInfo AuthInfo) (sessionKey serverName: authInfo.MockDomain, } + if strings.EqualFold(fields.serverName, "random") { + fields.serverName = randomServerName() + } + var ch []byte ch, err = buildClientHello(tls.browser, fields) if err != nil { From 392fc41de86e865a16c5fa8fea20340eb673592b Mon Sep 17 00:00:00 2001 From: Andy Wang Date: Sat, 13 Apr 2024 23:08:34 +0100 Subject: [PATCH 2/3] Move random utilities to common package --- internal/client/TLS.go | 27 ++++----------------------- internal/common/crypto.go | 35 ++++++++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/internal/client/TLS.go b/internal/client/TLS.go index 469ad28..5adcf0d 100644 --- a/internal/client/TLS.go +++ b/internal/client/TLS.go @@ -1,16 +1,11 @@ package client import ( - cryptoRand "crypto/rand" + "github.com/cbeuw/Cloak/internal/common" utls "github.com/refraction-networking/utls" log "github.com/sirupsen/logrus" - "math/big" - "math/rand" "net" "strings" - "time" - - "github.com/cbeuw/Cloak/internal/common" ) const appDataMaxLength = 16401 @@ -40,26 +35,12 @@ var topLevelDomains = []string{"com", "net", "org", "it", "fr", "me", "ru", "cn" // https://github.com/ProtonVPN/wireguard-go/commit/bcf344b39b213c1f32147851af0d2a8da9266883 func randomServerName() string { charNum := int('z') - int('a') + 1 - size := 3 + randInt(10) + size := 3 + common.RandInt(10) name := make([]byte, size) for i := range name { - name[i] = byte(int('a') + randInt(charNum)) - } - return string(name) + "." + randItem(topLevelDomains) -} - -func randItem(list []string) string { - return list[randInt(len(list))] -} - -func randInt(n int) int { - size, err := cryptoRand.Int(cryptoRand.Reader, big.NewInt(int64(n))) - if err == nil { - return int(size.Int64()) + name[i] = byte(int('a') + common.RandInt(charNum)) } - //goland:noinspection GoDeprecation - rand.Seed(time.Now().UnixNano()) - return rand.Intn(n) + return string(name) + "." + common.RandItem(topLevelDomains) } func buildClientHello(browser browser, fields clientHelloFields) ([]byte, error) { diff --git a/internal/common/crypto.go b/internal/common/crypto.go index 71b3f3f..c46ba89 100644 --- a/internal/common/crypto.go +++ b/internal/common/crypto.go @@ -6,6 +6,7 @@ import ( "crypto/rand" "errors" "io" + "math/big" "time" log "github.com/sirupsen/logrus" @@ -52,8 +53,8 @@ func CryptoRandRead(buf []byte) { RandRead(rand.Reader, buf) } -func RandRead(randSource io.Reader, buf []byte) { - _, err := randSource.Read(buf) +func backoff(f func() error) { + err := f() if err == nil { return } @@ -61,12 +62,36 @@ func RandRead(randSource io.Reader, buf []byte) { 100 * time.Millisecond, 300 * time.Millisecond, 500 * time.Millisecond, 1 * time.Second, 3 * time.Second, 5 * time.Second} for i := 0; i < 10; i++ { - log.Errorf("Failed to get random bytes: %v. Retrying...", err) - _, err = randSource.Read(buf) + log.Errorf("Failed to get random: %v. Retrying...", err) + err = f() if err == nil { return } time.Sleep(waitDur[i]) } - log.Fatal("Cannot get random bytes after 10 retries") + log.Fatal("Cannot get random after 10 retries") +} + +func RandRead(randSource io.Reader, buf []byte) { + backoff(func() error { + _, err := randSource.Read(buf) + return err + }) +} + +func RandItem[T any](list []T) T { + return list[RandInt(len(list))] +} + +func RandInt(n int) int { + s := new(int) + backoff(func() error { + size, err := rand.Int(rand.Reader, big.NewInt(int64(n))) + if err != nil { + return err + } + *s = int(size.Int64()) + return nil + }) + return *s } From d5da5d049cbdb56b2c4d29d7ebe3fb038d233bb4 Mon Sep 17 00:00:00 2001 From: Andy Wang Date: Sat, 13 Apr 2024 23:10:09 +0100 Subject: [PATCH 3/3] Update copyright --- internal/client/TLS.go | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/internal/client/TLS.go b/internal/client/TLS.go index 5adcf0d..178f8fb 100644 --- a/internal/client/TLS.go +++ b/internal/client/TLS.go @@ -32,8 +32,29 @@ type DirectTLS struct { var topLevelDomains = []string{"com", "net", "org", "it", "fr", "me", "ru", "cn", "es", "tr", "top", "xyz", "info"} -// https://github.com/ProtonVPN/wireguard-go/commit/bcf344b39b213c1f32147851af0d2a8da9266883 func randomServerName() string { + /* + Copyright: Proton AG + https://github.com/ProtonVPN/wireguard-go/commit/bcf344b39b213c1f32147851af0d2a8da9266883 + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ charNum := int('z') - int('a') + 1 size := 3 + common.RandInt(10) name := make([]byte, size)