This is an automated email from the git hooks/post-receive script.
meskio pushed a change to branch main in repository pluggable-transports/obfs4.
from cbf3f3c Bump the version to 0.0.13-dev add d5a5161 doc: Correct why the obfs4 change is backward compatible (NFC) add 83f01d5 transports/meek_lite: Remove utls support add 77af0cb build: Do the release ritual for obfs4proxy-0.0.13 add f63befd build: Bump the version to 0.0.14-dev add 586fbf4 Test that public keys are not always on the prime-order subgroup. add 5fcbb0e Do not clamp the private key before Elligator inverse map. add ef83204 doc: Add a changelog entry and clarified a comment (NFC) add 336a71d build: Do the release ritual for obfs4proxy-0.0.14 new 68b1705 Cherry-pick meek uTLS support new ed46c23 Update upstream fingerprints new b9e04fd Use HelloChrome_Auto uTLS by default in meek
The 3 revisions listed above as "new" are entirely new to this repository and will be described in separate emails. The revisions listed as "add" were already present in the repository and have only been added to this reference.
Summary of changes: ChangeLog | 8 ++- common/ntor/ntor.go | 3 - common/ntor/ntor_test.go | 136 ++++++++++++++++++++++++++++++++++++++ go.mod | 6 +- go.sum | 35 +++++----- internal/README.md | 7 +- internal/x25519ell2/x25519ell2.go | 4 ++ obfs4proxy/obfs4proxy.go | 2 +- transports/meeklite/base.go | 6 +- transports/meeklite/hpkp_lite.go | 124 ---------------------------------- transports/meeklite/meek.go | 28 +++----- transports/meeklite/transport.go | 64 +++++++++--------- 12 files changed, 217 insertions(+), 206 deletions(-) delete mode 100644 transports/meeklite/hpkp_lite.go
This is an automated email from the git hooks/post-receive script.
meskio pushed a commit to branch main in repository pluggable-transports/obfs4.
commit 68b17054153565c6fd938aecbbd0132e643207bb Author: meskio meskio@torproject.org AuthorDate: Wed Oct 19 13:02:45 2022 +0200
Cherry-pick meek uTLS support
Mostly from f01e92dd. --- go.mod | 5 +- go.sum | 18 ++++ transports/meeklite/meek.go | 29 ++++- transports/meeklite/transport.go | 222 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 267 insertions(+), 7 deletions(-)
diff --git a/go.mod b/go.mod index 49e491b..0dea33e 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,10 @@ require ( filippo.io/edwards25519 v1.0.0-rc.1.0.20210721174708-390f27c3be20 git.torproject.org/pluggable-transports/goptlib.git v1.0.0 github.com/dchest/siphash v1.2.1 + github.com/refraction-networking/utls v1.1.5 gitlab.com/yawning/edwards25519-extra.git v0.0.0-20211229043746-2f91fcc9fbdb - golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 - golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 + golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 + golang.org/x/net v0.0.0-20220909164309-bea034e7d591 )
go 1.16 diff --git a/go.sum b/go.sum index b542a6b..dd093aa 100644 --- a/go.sum +++ b/go.sum @@ -2,17 +2,35 @@ filippo.io/edwards25519 v1.0.0-rc.1.0.20210721174708-390f27c3be20 h1:iJoUgXvhags filippo.io/edwards25519 v1.0.0-rc.1.0.20210721174708-390f27c3be20/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= git.torproject.org/pluggable-transports/goptlib.git v1.0.0 h1:ElTwFFPKf/tA6x5nuIk9g49JZzS4T5WN+eTQTjqd00A= git.torproject.org/pluggable-transports/goptlib.git v1.0.0/go.mod h1:YT4XMSkuEXbtqlydr9+OxqFAyspUv0Gr9qhM3B++o/Q= +github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/dchest/siphash v1.2.1 h1:4cLinnzVJDKxTCl9B01807Yiy+W7ZzVHj/KIroQRvT4= github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4= +github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/refraction-networking/utls v1.1.5 h1:JtrojoNhbUQkBqEg05sP3gDgDj6hIEAAVKbI9lx4n6w= +github.com/refraction-networking/utls v1.1.5/go.mod h1:jRQxtYi7nkq1p28HF2lwOH5zQm9aC8rpK0O9lIIzGh8= gitlab.com/yawning/edwards25519-extra.git v0.0.0-20211229043746-2f91fcc9fbdb h1:qRSZHsODmAP5qDvb3YsO7Qnf3TRiVbGxNG/WYnlM4/o= gitlab.com/yawning/edwards25519-extra.git v0.0.0-20211229043746-2f91fcc9fbdb/go.mod h1:gvdJuZuO/tPZyhEV8K3Hmoxv/DWud5L4qEQxfYjEUTo= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/transports/meeklite/meek.go b/transports/meeklite/meek.go index 17c7a67..8f9fe35 100644 --- a/transports/meeklite/meek.go +++ b/transports/meeklite/meek.go @@ -44,6 +44,8 @@ import ( "sync" "time"
+ utls "github.com/refraction-networking/utls" + "git.torproject.org/pluggable-transports/goptlib.git" "gitlab.com/yawning/obfs4.git/transports/base" ) @@ -51,6 +53,7 @@ import ( const ( urlArg = "url" frontArg = "front" + utlsArg = "utls"
maxChanBacklog = 16
@@ -73,6 +76,8 @@ var ( type meekClientArgs struct { url *gourl.URL front string + + utls *utls.ClientHelloID }
func (ca *meekClientArgs) Network() string { @@ -104,13 +109,19 @@ func newClientArgs(args *pt.Args) (ca *meekClientArgs, err error) { // Parse the (optional) front argument. ca.front, _ = args.Get(frontArg)
+ // Parse the (optional) utls argument. + utlsOpt, _ := args.Get(utlsArg) + if ca.utls, err = parseClientHelloID(utlsOpt); err != nil { + return nil, err + } + return ca, nil }
type meekConn struct { - args *meekClientArgs - sessionID string - transport *http.Transport + args *meekClientArgs + sessionID string + roundTripper http.RoundTripper
closeOnce sync.Once workerWrChan chan []byte @@ -242,7 +253,7 @@ func (c *meekConn) roundTrip(sndBuf []byte) (recvBuf []byte, err error) { req.Header.Set("X-Session-Id", c.sessionID) req.Header.Set("User-Agent", "")
- resp, err = c.transport.RoundTrip(req) + resp, err = c.roundTripper.RoundTrip(req) if err != nil { return nil, err } @@ -343,10 +354,18 @@ func newMeekConn(network, addr string, dialFn base.DialFunc, ca *meekClientArgs) return nil, err }
+ var rt http.RoundTripper + switch ca.utls { + case nil: + rt = &http.Transport{Dial: dialFn} + default: + rt = newRoundTripper(dialFn, ca.utls) + } + conn := &meekConn{ args: ca, sessionID: id, - transport: &http.Transport{Dial: dialFn}, + roundTripper: rt, workerWrChan: make(chan []byte, maxChanBacklog), workerRdChan: make(chan []byte, maxChanBacklog), workerCloseChan: make(chan struct{}), diff --git a/transports/meeklite/transport.go b/transports/meeklite/transport.go new file mode 100644 index 0000000..82ae9fe --- /dev/null +++ b/transports/meeklite/transport.go @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2019 Yawning Angel <yawning at schwanenlied dot me> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ + +package meeklite + +import ( + "crypto/tls" + "errors" + "fmt" + "net" + "net/http" + "net/url" + "strconv" + "strings" + "sync" + + utls "github.com/refraction-networking/utls" + "gitlab.com/yawning/obfs4.git/transports/base" + "golang.org/x/net/http2" +) + +var ( + errProtocolNegotiated = errors.New("meek_lite: protocol negotiated") + + // This should be kept in sync with what is available in utls. + clientHelloIDMap = map[string]*utls.ClientHelloID{ + "hellogolang": nil, // Don't bother with utls. + "hellorandomized": &utls.HelloRandomized, + "hellorandomizedalpn": &utls.HelloRandomizedALPN, + "hellorandomizednoalpn": &utls.HelloRandomizedNoALPN, + "hellofirefox_auto": &utls.HelloFirefox_Auto, + "hellofirefox_55": &utls.HelloFirefox_55, + "hellofirefox_56": &utls.HelloFirefox_56, + "hellofirefox_63": &utls.HelloFirefox_63, + "hellochrome_auto": &utls.HelloChrome_Auto, + "hellochrome_58": &utls.HelloChrome_58, + "hellochrome_62": &utls.HelloChrome_62, + "hellochrome_70": &utls.HelloChrome_70, + "helloios_auto": &utls.HelloIOS_Auto, + "helloios_11_1": &utls.HelloIOS_11_1, + } + defaultClientHello = &utls.HelloFirefox_Auto +) + +type roundTripper struct { + sync.Mutex + + clientHelloID *utls.ClientHelloID + dialFn base.DialFunc + transport http.RoundTripper + + initConn net.Conn +} + +func (rt *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + // Note: This isn't protected with a lock, since the meeklite ioWorker + // serializes RoundTripper requests. + // + // This also assumes that req.URL.Host will remain constant for the + // lifetime of the roundTripper, which is a valid assumption for meeklite. + if rt.transport == nil { + if err := rt.getTransport(req); err != nil { + return nil, err + } + } + return rt.transport.RoundTrip(req) +} + +func (rt *roundTripper) getTransport(req *http.Request) error { + switch strings.ToLower(req.URL.Scheme) { + case "http": + rt.transport = newHTTPTransport(rt.dialFn, nil) + return nil + case "https": + default: + return fmt.Errorf("meek_lite: invalid URL scheme: '%v'", req.URL.Scheme) + } + + _, err := rt.dialTLS("tcp", getDialTLSAddr(req.URL)) + switch err { + case errProtocolNegotiated: + case nil: + // Should never happen. + panic("meek_lite: dialTLS returned no error when determining transport") + default: + return err + } + + return nil +} + +func (rt *roundTripper) dialTLS(network, addr string) (net.Conn, error) { + // Unlike rt.transport, this is protected by a critical section + // since past the initial manual call from getTransport, the HTTP + // client will be the caller. + rt.Lock() + defer rt.Unlock() + + // If we have the connection from when we determined the HTTPS + // transport to use, return that. + if conn := rt.initConn; conn != nil { + rt.initConn = nil + return conn, nil + } + + rawConn, err := rt.dialFn(network, addr) + if err != nil { + return nil, err + } + + var host string + if host, _, err = net.SplitHostPort(addr); err != nil { + host = addr + } + + conn := utls.UClient(rawConn, &utls.Config{ + ServerName: host, + + // `crypto/tls` gradually ramps up the record size. While this is + // a good optimization and is a relatively common server feature, + // neither Firefox nor Chromium appear to use such optimizations. + DynamicRecordSizingDisabled: true, + }, *rt.clientHelloID) + + if err = conn.Handshake(); err != nil { + conn.Close() + return nil, err + } + + if rt.transport != nil { + return conn, nil + } + + // No http.Transport constructed yet, create one based on the results + // of ALPN. + switch conn.ConnectionState().NegotiatedProtocol { + case http2.NextProtoTLS: + // The remote peer is speaking HTTP 2 + TLS. + rt.transport = &http2.Transport{DialTLS: rt.dialTLSHTTP2} + default: + // Assume the remote peer is speaking HTTP 1.x + TLS. + rt.transport = newHTTPTransport(nil, rt.dialTLS) + } + + // Stash the connection just established for use servicing the + // actual request (should be near-immediate). + rt.initConn = conn + + return nil, errProtocolNegotiated +} + +func (rt *roundTripper) dialTLSHTTP2(network, addr string, cfg *tls.Config) (net.Conn, error) { + return rt.dialTLS(network, addr) +} + +func getDialTLSAddr(u *url.URL) string { + host, port, err := net.SplitHostPort(u.Host) + if err == nil { + return net.JoinHostPort(host, port) + } + pInt, _ := net.LookupPort("tcp", u.Scheme) + + return net.JoinHostPort(u.Host, strconv.Itoa(pInt)) +} + +func newRoundTripper(dialFn base.DialFunc, clientHelloID *utls.ClientHelloID) http.RoundTripper { + return &roundTripper{ + clientHelloID: clientHelloID, + dialFn: dialFn, + } +} + +func parseClientHelloID(s string) (*utls.ClientHelloID, error) { + s = strings.ToLower(s) + switch s { + case "none": + return nil, nil + case "": + return defaultClientHello, nil + default: + if ret := clientHelloIDMap[s]; ret != nil { + return ret, nil + } + } + return nil, fmt.Errorf("invalid ClientHelloID: '%v'", s) +} + +func newHTTPTransport(dialFn, dialTLSFn base.DialFunc) *http.Transport { + base := (http.DefaultTransport).(*http.Transport) + + return &http.Transport{ + Dial: dialFn, + DialTLS: dialTLSFn, + + // Use default configuration values, taken from the runtime. + MaxIdleConns: base.MaxIdleConns, + IdleConnTimeout: base.IdleConnTimeout, + TLSHandshakeTimeout: base.TLSHandshakeTimeout, + ExpectContinueTimeout: base.ExpectContinueTimeout, + } +} + +func init() { + // Attempt to increase compatibility, there's an encrypted link + // underneath, and this doesn't (shouldn't) affect the external + // fingerprint. + utls.EnableWeakCiphers() +}
This is an automated email from the git hooks/post-receive script.
meskio pushed a commit to branch main in repository pluggable-transports/obfs4.
commit ed46c23917b55c4b274d6986daeaf6bec7963115 Author: meskio meskio@torproject.org AuthorDate: Wed Oct 19 13:26:32 2022 +0200
Update upstream fingerprints --- transports/meeklite/transport.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
diff --git a/transports/meeklite/transport.go b/transports/meeklite/transport.go index 82ae9fe..31dec27 100644 --- a/transports/meeklite/transport.go +++ b/transports/meeklite/transport.go @@ -46,12 +46,36 @@ var ( "hellofirefox_55": &utls.HelloFirefox_55, "hellofirefox_56": &utls.HelloFirefox_56, "hellofirefox_63": &utls.HelloFirefox_63, + "hellofirefox_65": &utls.HelloFirefox_65, + "hellofirefox_99": &utls.HelloFirefox_99, + "hellofirefox_102": &utls.HelloFirefox_102, + "hellofirefox_105": &utls.HelloFirefox_105, "hellochrome_auto": &utls.HelloChrome_Auto, "hellochrome_58": &utls.HelloChrome_58, "hellochrome_62": &utls.HelloChrome_62, "hellochrome_70": &utls.HelloChrome_70, + "hellochrome_72": &utls.HelloChrome_72, + "hellochrome_83": &utls.HelloChrome_83, + "hellochrome_87": &utls.HelloChrome_87, + "hellochrome_96": &utls.HelloChrome_96, + "hellochrome_100": &utls.HelloChrome_100, + "hellochrome_102": &utls.HelloChrome_102, "helloios_auto": &utls.HelloIOS_Auto, "helloios_11_1": &utls.HelloIOS_11_1, + "helloios_12_1": &utls.HelloIOS_12_1, + "helloios_13": &utls.HelloIOS_13, + "helloios_14": &utls.HelloIOS_14, + "helloandroid_11": &utls.HelloAndroid_11_OkHttp, + "helloedge_auto": &utls.HelloEdge_Auto, + "helloedge_85": &utls.HelloEdge_85, + "helloedge_106": &utls.HelloEdge_106, + "hellosafari_auto": &utls.HelloSafari_Auto, + "hellosafari_16_0": &utls.HelloSafari_16_0, + "hello360_auto": &utls.Hello360_Auto, + "hello360_7_5": &utls.Hello360_7_5, + "hello360_11_0": &utls.Hello360_11_0, + "helloqq_auto": &utls.HelloQQ_Auto, + "helloqq_11_1": &utls.HelloQQ_11_1, } defaultClientHello = &utls.HelloFirefox_Auto )
This is an automated email from the git hooks/post-receive script.
meskio pushed a commit to branch main in repository pluggable-transports/obfs4.
commit b9e04fd9e241b0530e05511ec6895e698a25575f Author: meskio meskio@torproject.org AuthorDate: Tue Oct 25 12:20:27 2022 +0200
Use HelloChrome_Auto uTLS by default in meek --- transports/meeklite/transport.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/transports/meeklite/transport.go b/transports/meeklite/transport.go index 31dec27..28b5894 100644 --- a/transports/meeklite/transport.go +++ b/transports/meeklite/transport.go @@ -77,7 +77,7 @@ var ( "helloqq_auto": &utls.HelloQQ_Auto, "helloqq_11_1": &utls.HelloQQ_11_1, } - defaultClientHello = &utls.HelloFirefox_Auto + defaultClientHello = &utls.HelloChrome_Auto )
type roundTripper struct {
tor-commits@lists.torproject.org