diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 03cdf87..99258f6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: '^1.15' # The Go version to download (if necessary) and use. + go-version: '^1.17' # The Go version to download (if necessary) and use. - run: go test -race -coverprofile coverage.txt -coverpkg ./... -covermode atomic ./... - uses: codecov/codecov-action@v1 with: diff --git a/.gitignore b/.gitignore index a82796c..9d8deca 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ corpus/ suppressions/ crashers/ -*.zip \ No newline at end of file +*.zip +.idea/ +build/ \ No newline at end of file diff --git a/README.md b/README.md index f380dac..e2dff6a 100644 --- a/README.md +++ b/README.md @@ -3,25 +3,30 @@ [![Go Report Card](https://goreportcard.com/badge/github.com/cbeuw/Cloak)](https://goreportcard.com/report/github.com/cbeuw/Cloak) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=SAUYKGSREP8GL&source=url) -![logo](https://user-images.githubusercontent.com/7034308/96387206-3e214100-1198-11eb-8917-689d7c56e0cd.png) +

+ + +

-![diagram](https://user-images.githubusercontent.com/7034308/65385852-7eab5280-dd2b-11e9-8887-db449b250e2a.png) +

+ +

-Cloak is a [pluggable transport](https://www.ietf.org/proceedings/103/slides/slides-103-pearg-pt-slides-01) that works -alongside traditional proxy tools like OpenVPN to evade deep-packet-inspection based censorship. +Cloak is a [pluggable transport](https://www.ietf.org/proceedings/103/slides/slides-103-pearg-pt-slides-01) that enhances +traditional proxy tools like OpenVPN to evade [sophisticated censorship](https://en.wikipedia.org/wiki/Deep_packet_inspection) and [data discrimination](https://en.wikipedia.org/wiki/Net_bias). -Cloak is not a standalone proxy program. Rather, it works by masquerading proxy tool's traffic as normal web browsing -traffic. In contrast to traditional tools which have very prominent traffic "fingerprints", it's very difficult to -precisely target Cloak with little false positives. This increases the collateral damage to censorship actions as +Cloak is not a standalone proxy program. Rather, it works by masquerading proxied traffic as normal web browsing +activities. In contrast to traditional tools which have very prominent traffic fingerprints and can be blocked by simple filtering rules, +it's very difficult to precisely target Cloak with little false positives. This increases the collateral damage to censorship actions as attempts to block Cloak could also damage services the censor state relies on. -To a third party observer, a host running Cloak server is indistinguishable from an innocent web server. Both while +To any third party observer, a host running Cloak server is indistinguishable from an innocent web server. Both while passively observing traffic flow to and from the server, as well as while actively probing the behaviours of a Cloak server. This is achieved through the use a series -of [cryptographic stegnatography techniques](https://github.com/cbeuw/Cloak/wiki/Steganography-and-encryption). +of [cryptographic steganography techniques](https://github.com/cbeuw/Cloak/wiki/Steganography-and-encryption). -Since Cloak is transparent, it can be used in conjunction with any proxy software that tunnels traffic through TCP or -UDP, such as Shadowsocks, OpenVPN and Tor. Multiple proxy servers can be running on the same server host machine and +Cloak can be used in conjunction with any proxy program that tunnels traffic through TCP or +UDP, such as Shadowsocks, OpenVPN and Tor. Multiple proxy servers can be running on the same server host and Cloak server will act as a reverse proxy, bridging clients with their desired proxy end. Cloak multiplexes traffic through multiple underlying TCP connections which reduces head-of-line blocking and eliminates @@ -31,9 +36,8 @@ Cloak provides multi-user support, allowing multiple clients to connect to the p default). It also provides traffic management features such as usage credit and bandwidth control. This allows a proxy server to serve multiple users even if the underlying proxy software wasn't designed for multiple users -Cloak has two modes of [_Transport_](https://github.com/cbeuw/Cloak/wiki/CDN-mode): `direct` and `CDN`. Clients can -either connect to the host running Cloak server directly, or it can instead connect to a CDN edge server, which may be -used by many other websites as well, thus further increases the collateral damage to censorship. +Cloak also supports tunneling through an intermediary CDN server such as Amazon Cloudfront. Such services are so widely used, +attempts to disrupt traffic to them can lead to very high collateral damage for the censor. ## Quick Start diff --git a/go.mod b/go.mod index 3b69708..04cad73 100644 --- a/go.mod +++ b/go.mod @@ -3,22 +3,16 @@ module github.com/cbeuw/Cloak go 1.14 require ( - github.com/cbeuw/connutil v0.0.0-20200411160121-c5a5c4a9de14 - github.com/dvyukov/go-fuzz v0.0.0-20201003075337-90825f39c90b // indirect - github.com/elazarl/go-bindata-assetfs v1.0.1 // indirect - github.com/gorilla/mux v1.7.3 - github.com/gorilla/websocket v1.4.1 + github.com/cbeuw/connutil v0.0.0-20200411215123-966bfaa51ee3 + github.com/gorilla/mux v1.8.0 + github.com/gorilla/websocket v1.4.2 github.com/juju/ratelimit v1.0.1 - github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/kr/pretty v0.1.0 // indirect - github.com/mitchellh/gox v1.0.1 // indirect - github.com/sirupsen/logrus v1.5.0 - github.com/stephens2424/writerset v1.0.2 // indirect + github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.6.1 gitlab.com/yawning/utls.git v0.0.12-1 - go.etcd.io/bbolt v1.3.4 - golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 - golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 // indirect - golang.org/x/tools v0.0.0-20201015182029-a5d9e455e9c4 // indirect + go.etcd.io/bbolt v1.3.6 + golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 + golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect ) diff --git a/go.sum b/go.sum index 7fc2281..052333a 100644 --- a/go.sum +++ b/go.sum @@ -1,96 +1,58 @@ -github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74= -github.com/cbeuw/connutil v0.0.0-20200411160121-c5a5c4a9de14 h1:bWJKlzTJR7C9DX0l1qhkTaP1lTEBWVDKhg8C/tNJqKg= -github.com/cbeuw/connutil v0.0.0-20200411160121-c5a5c4a9de14/go.mod h1:6jR2SzckGv8hIIS9zWJ160mzGVVOYp4AXZMDtacL6LE= +github.com/cbeuw/connutil v0.0.0-20200411215123-966bfaa51ee3 h1:LRxW8pdmWmyhoNh+TxUjxsAinGtCsVGjsl3xg6zoRSs= +github.com/cbeuw/connutil v0.0.0-20200411215123-966bfaa51ee3/go.mod h1:6jR2SzckGv8hIIS9zWJ160mzGVVOYp4AXZMDtacL6LE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= -github.com/dvyukov/go-fuzz v0.0.0-20201003075337-90825f39c90b h1:CXfDl9Y3NKuhOSxF9kXhiLmuYCdufQDrLY2fO1BzqBU= -github.com/dvyukov/go-fuzz v0.0.0-20201003075337-90825f39c90b/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= -github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw= -github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= -github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8= -github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/juju/ratelimit v1.0.1 h1:+7AIFJVQ0EQgq/K9+0Krm7m530Du7tIz0METWzN0RgY= github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI= -github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4= -github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/refraction-networking/utls v0.0.0-20190909200633-43c36d3c1f57 h1:SL1K0QAuC1b54KoY1pjPWe6kSlsFHwK9/oC960fKrTY= -github.com/refraction-networking/utls v0.0.0-20190909200633-43c36d3c1f57/go.mod h1:tz9gX959MEFfFN5whTIocCLUG57WiILqtdVxI8c6Wj0= -github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s= -github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q= -github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= -github.com/stephens2424/writerset v1.0.2 h1:znRLgU6g8RS5euYRcy004XeE4W+Tu44kALzy7ghPif8= -github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ= gitlab.com/yawning/utls.git v0.0.12-1 h1:RL6O0MP2YI0KghuEU/uGN6+8b4183eqNWoYgx7CXD0U= gitlab.com/yawning/utls.git v0.0.12-1/go.mod h1:3ONKiSFR9Im/c3t5RKmMJTVdmZN496FNyk3mjrY1dyo= -go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg= -go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= +go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8= -golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 h1:71vQrMauZZhcTVK6KdYM+rklehEEwb3E+ZhaE5jrPrE= +golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20190328230028-74de082e2cca/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HXxFpW2sdCiP8MJSKLQY= -golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27 h1:XDXtA5hveEEV8JB2l7nhMTp3t3cHp9ZpwcdjqyEWLlo= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20201015182029-a5d9e455e9c4 h1:rQWkJiVIyJ3PgiSHL+RXc8xbrK8duU6jG5eeZ9G7nk8= -golang.org/x/tools v0.0.0-20201015182029-a5d9e455e9c4/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/client/TLS.go b/internal/client/TLS.go index 39e8bb3..7e21724 100644 --- a/internal/client/TLS.go +++ b/internal/client/TLS.go @@ -13,14 +13,14 @@ type clientHelloFields struct { random []byte sessionId []byte x25519KeyShare []byte - sni []byte + serverName string } type browser interface { composeClientHello(clientHelloFields) []byte } -func makeServerName(serverName string) []byte { +func generateSNI(serverName string) []byte { serverNameListLength := make([]byte, 2) binary.BigEndian.PutUint16(serverNameListLength, uint16(len(serverName)+3)) serverNameType := []byte{0x00} // host_name @@ -45,16 +45,6 @@ func addExtRec(typ []byte, data []byte) []byte { return ret } -func genStegClientHello(ai authenticationPayload, serverName string) (ret clientHelloFields) { - // random is marshalled ephemeral pub key 32 bytes - // The authentication ciphertext and its tag are then distributed among SessionId and X25519KeyShare - ret.random = ai.randPubKey[:] - ret.sessionId = ai.ciphertextWithTag[0:32] - ret.x25519KeyShare = ai.ciphertextWithTag[32:64] - ret.sni = makeServerName(serverName) - return -} - type DirectTLS struct { *common.TLSConn browser browser @@ -64,7 +54,16 @@ type DirectTLS struct { // if the server proceed with Cloak authentication func (tls *DirectTLS) Handshake(rawConn net.Conn, authInfo AuthInfo) (sessionKey [32]byte, err error) { payload, sharedSecret := makeAuthenticationPayload(authInfo) - chOnly := tls.browser.composeClientHello(genStegClientHello(payload, authInfo.MockDomain)) + + // random is marshalled ephemeral pub key 32 bytes + // The authentication ciphertext and its tag are then distributed among SessionId and X25519KeyShare + fields := clientHelloFields{ + random: payload.randPubKey[:], + sessionId: payload.ciphertextWithTag[0:32], + x25519KeyShare: payload.ciphertextWithTag[32:64], + serverName: authInfo.MockDomain, + } + chOnly := tls.browser.composeClientHello(fields) chWithRecordLayer := common.AddRecordLayer(chOnly, common.Handshake, common.VersionTLS11) _, err = rawConn.Write(chWithRecordLayer) if err != nil { diff --git a/internal/client/TLS_test.go b/internal/client/TLS_test.go index b8bdd81..bd3b051 100644 --- a/internal/client/TLS_test.go +++ b/internal/client/TLS_test.go @@ -33,6 +33,6 @@ func TestMakeServerName(t *testing.T) { } for _, p := range pairs { - assert.Equal(t, p.target, makeServerName(p.serverName)) + assert.Equal(t, p.target, generateSNI(p.serverName)) } } diff --git a/internal/client/chrome.go b/internal/client/chrome.go index 34e394f..042c6f0 100644 --- a/internal/client/chrome.go +++ b/internal/client/chrome.go @@ -1,4 +1,4 @@ -// Fingerprint of Chrome 90 +// Fingerprint of Chrome 97 package client @@ -21,7 +21,7 @@ func makeGREASE() []byte { return doubleGREASE } -func (c *Chrome) composeExtensions(sni []byte, keyShare []byte) []byte { +func (c *Chrome) composeExtensions(serverName string, keyShare []byte) []byte { makeSupportedGroups := func() []byte { suppGroupListLen := []byte{0x00, 0x08} @@ -47,13 +47,13 @@ func (c *Chrome) composeExtensions(sni []byte, keyShare []byte) []byte { // extension length is always 403, and server name length is variable var ext [17][]byte - ext[0] = addExtRec(makeGREASE(), nil) // First GREASE - ext[1] = addExtRec([]byte{0x00, 0x00}, sni) // server name indication - ext[2] = addExtRec([]byte{0x00, 0x17}, nil) // extended_master_secret - ext[3] = addExtRec([]byte{0xff, 0x01}, []byte{0x00}) // renegotiation_info - ext[4] = addExtRec([]byte{0x00, 0x0a}, makeSupportedGroups()) // supported groups - ext[5] = addExtRec([]byte{0x00, 0x0b}, []byte{0x01, 0x00}) // ec point formats - ext[6] = addExtRec([]byte{0x00, 0x23}, nil) // Session tickets + ext[0] = addExtRec(makeGREASE(), nil) // First GREASE + ext[1] = addExtRec([]byte{0x00, 0x00}, generateSNI(serverName)) // server name indication + ext[2] = addExtRec([]byte{0x00, 0x17}, nil) // extended_master_secret + ext[3] = addExtRec([]byte{0xff, 0x01}, []byte{0x00}) // renegotiation_info + ext[4] = addExtRec([]byte{0x00, 0x0a}, makeSupportedGroups()) // supported groups + ext[5] = addExtRec([]byte{0x00, 0x0b}, []byte{0x01, 0x00}) // ec point formats + ext[6] = addExtRec([]byte{0x00, 0x23}, nil) // Session tickets ALPN, _ := hex.DecodeString("000c02683208687474702f312e31") ext[7] = addExtRec([]byte{0x00, 0x10}, ALPN) // app layer proto negotiation ext[8] = addExtRec([]byte{0x00, 0x05}, []byte{0x01, 0x00, 0x00, 0x00, 0x00}) // status request @@ -87,12 +87,12 @@ func (c *Chrome) composeClientHello(hd clientHelloFields) (ch []byte) { clientHello[3] = hd.random // random clientHello[4] = []byte{0x20} // session id length 32 clientHello[5] = hd.sessionId // session id - clientHello[6] = []byte{0x00, 0x20} // cipher suites length 34 + clientHello[6] = []byte{0x00, 0x20} // cipher suites length 32 cipherSuites, _ := hex.DecodeString("130113021303c02bc02fc02cc030cca9cca8c013c014009c009d002f0035") clientHello[7] = append(makeGREASE(), cipherSuites...) // cipher suites clientHello[8] = []byte{0x01} // compression methods length 1 clientHello[9] = []byte{0x00} // compression methods - clientHello[11] = c.composeExtensions(hd.sni, hd.x25519KeyShare) + clientHello[11] = c.composeExtensions(hd.serverName, hd.x25519KeyShare) clientHello[10] = []byte{0x00, 0x00} // extensions length 403 binary.BigEndian.PutUint16(clientHello[10], uint16(len(clientHello[11]))) var ret []byte diff --git a/internal/client/chrome_test.go b/internal/client/chrome_test.go index 8ddb6f8..629e23c 100644 --- a/internal/client/chrome_test.go +++ b/internal/client/chrome_test.go @@ -30,9 +30,7 @@ func TestComposeExtension(t *testing.T) { serverName := "github.com" keyShare, _ := hex.DecodeString("690f074f5c01756982269b66d58c90c47dc0f281d654c7b2c16f63c9033f5604") - sni := makeServerName(serverName) - - result := (&Chrome{}).composeExtensions(sni, keyShare) + result := (&Chrome{}).composeExtensions(serverName, keyShare) target, _ := hex.DecodeString("8a8a00000000000f000d00000a6769746875622e636f6d00170000ff01000100000a000a00088a8a001d00170018000b00020100002300000010000e000c02683208687474702f312e31000500050100000000000d0012001004030804040105030805050108060601001200000033002b00298a8a000100001d0020690f074f5c01756982269b66d58c90c47dc0f281d654c7b2c16f63c9033f5604002d00020101002b000b0a3a3a0304030303020301001b00030200024a4a000100001500d2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") for p := 0; p < len(result); p++ { if result[p] != target[p] { diff --git a/internal/client/firefox.go b/internal/client/firefox.go index 89631ee..10c48b6 100644 --- a/internal/client/firefox.go +++ b/internal/client/firefox.go @@ -1,4 +1,4 @@ -// Fingerprint of Firefox 88 +// Fingerprint of Firefox 96 package client @@ -10,7 +10,7 @@ import ( type Firefox struct{} -func (f *Firefox) composeExtensions(SNI []byte, keyShare []byte) []byte { +func (f *Firefox) composeExtensions(serverName string, keyShare []byte) []byte { composeKeyShare := func(hidden []byte) []byte { ret := make([]byte, 107) ret[0], ret[1] = 0x00, 0x69 // length 105 @@ -24,9 +24,9 @@ func (f *Firefox) composeExtensions(SNI []byte, keyShare []byte) []byte { } // extension length is always 399, and server name length is variable var ext [12][]byte - ext[0] = addExtRec([]byte{0x00, 0x00}, SNI) // server name indication - ext[1] = addExtRec([]byte{0x00, 0x17}, nil) // extended_master_secret - ext[2] = addExtRec([]byte{0xff, 0x01}, []byte{0x00}) // renegotiation_info + ext[0] = addExtRec([]byte{0x00, 0x00}, generateSNI(serverName)) // server name indication + ext[1] = addExtRec([]byte{0x00, 0x17}, nil) // extended_master_secret + ext[2] = addExtRec([]byte{0xff, 0x01}, []byte{0x00}) // renegotiation_info suppGroup, _ := hex.DecodeString("000c001d00170018001901000101") ext[3] = addExtRec([]byte{0x00, 0x0a}, suppGroup) // supported groups ext[4] = addExtRec([]byte{0x00, 0x0b}, []byte{0x01, 0x00}) // ec point formats @@ -63,7 +63,7 @@ func (f *Firefox) composeClientHello(hd clientHelloFields) (ch []byte) { clientHello[8] = []byte{0x01} // compression methods length 1 clientHello[9] = []byte{0x00} // compression methods - clientHello[11] = f.composeExtensions(hd.sni, hd.x25519KeyShare) + clientHello[11] = f.composeExtensions(hd.serverName, hd.x25519KeyShare) clientHello[10] = []byte{0x00, 0x00} // extensions length binary.BigEndian.PutUint16(clientHello[10], uint16(len(clientHello[11]))) diff --git a/internal/client/firefox_test.go b/internal/client/firefox_test.go index fd04fda..6b6a338 100644 --- a/internal/client/firefox_test.go +++ b/internal/client/firefox_test.go @@ -11,8 +11,8 @@ func TestComposeExtensions(t *testing.T) { serverName := "consent.google.com" keyShare, _ := hex.DecodeString("8d8ea1b80430b7710b65f0d89b0144a5eeb218709ce6613d4fc8bfb117657c15") - sni := makeServerName(serverName) - result := (&Firefox{}).composeExtensions(sni, keyShare) + + result := (&Firefox{}).composeExtensions(serverName, keyShare) // skip random secp256r1 if !bytes.Equal(result[:133], target[:133]) || !bytes.Equal(result[198:], target[198:]) { t.Errorf("got %x", result) diff --git a/internal/client/state.go b/internal/client/state.go index b9f8125..550f002 100644 --- a/internal/client/state.go +++ b/internal/client/state.go @@ -238,7 +238,9 @@ func (raw *RawConfig) ProcessRawConfig(worldState common.WorldState) (local Loca case "firefox": browser = &Firefox{} case "chrome": - fallthrough + browser = &Chrome{} + case "steam": + browser = &Steam{} default: browser = &Chrome{} } diff --git a/internal/client/steam.go b/internal/client/steam.go new file mode 100644 index 0000000..b022f2e --- /dev/null +++ b/internal/client/steam.go @@ -0,0 +1,91 @@ +// Fingerprint of Steam Client + +package client + +import ( + "encoding/binary" + "encoding/hex" +) + +type Steam struct{} + +func (c *Steam) composeExtensions(serverName string, keyShare []byte) []byte { + + makeSupportedGroups := func() []byte { + suppGroupListLen := []byte{0x00, 0x08} + ret := make([]byte, 2+8) + copy(ret[0:2], suppGroupListLen) + copy(ret[2:4], makeGREASE()) + copy(ret[4:], []byte{0x00, 0x1d, 0x00, 0x17, 0x00, 0x18}) + return ret + } + + makeKeyShare := func(hidden []byte) []byte { + ret := make([]byte, 43) + ret[0], ret[1] = 0x00, 0x29 // length 41 + copy(ret[2:4], makeGREASE()) + ret[4], ret[5] = 0x00, 0x01 // length 1 + ret[6] = 0x00 + ret[7], ret[8] = 0x00, 0x1d // group x25519 + ret[9], ret[10] = 0x00, 0x20 // length 32 + copy(ret[11:43], hidden) + return ret + } + + // extension length is always 403, and server name length is variable + + var ext [17][]byte + ext[0] = addExtRec(makeGREASE(), nil) // First GREASE + ext[1] = addExtRec([]byte{0x00, 0x00}, generateSNI(serverName)) // server name indication + ext[2] = addExtRec([]byte{0x00, 0x17}, nil) // extended_master_secret + ext[3] = addExtRec([]byte{0xff, 0x01}, []byte{0x00}) // renegotiation_info + ext[4] = addExtRec([]byte{0x00, 0x0a}, makeSupportedGroups()) // supported groups + ext[5] = addExtRec([]byte{0x00, 0x0b}, []byte{0x01, 0x00}) // ec point formats + ext[6] = addExtRec([]byte{0x00, 0x23}, nil) // Session tickets + ALPN, _ := hex.DecodeString("000c02683208687474702f312e31") + ext[7] = addExtRec([]byte{0x00, 0x10}, ALPN) // app layer proto negotiation + ext[8] = addExtRec([]byte{0x00, 0x05}, []byte{0x01, 0x00, 0x00, 0x00, 0x00}) // status request + sigAlgo, _ := hex.DecodeString("001004030804040105030805050108060601") + ext[9] = addExtRec([]byte{0x00, 0x0d}, sigAlgo) // Signature Algorithms + ext[10] = addExtRec([]byte{0x00, 0x12}, nil) // signed cert timestamp + ext[11] = addExtRec([]byte{0x00, 0x33}, makeKeyShare(keyShare)) // key share + ext[12] = addExtRec([]byte{0x00, 0x2d}, []byte{0x01, 0x01}) // psk key exchange modes + suppVersions, _ := hex.DecodeString("0a9A9A0304030303020301") // 9A9A needs to be a GREASE + copy(suppVersions[1:3], makeGREASE()) + ext[13] = addExtRec([]byte{0x00, 0x2b}, suppVersions) // supported versions + ext[14] = addExtRec([]byte{0x00, 0x1b}, []byte{0x02, 0x00, 0x02}) // compress certificate + ext[15] = addExtRec(makeGREASE(), []byte{0x00}) // Last GREASE + // len(ext[1]) + 170 + len(ext[16]) = 403 + // len(ext[16]) = 233 - len(ext[1]) + // 2+2+len(padding) = 233 - len(ext[1]) + // len(padding) = 229 - len(ext[1]) + ext[16] = addExtRec([]byte{0x00, 0x15}, make([]byte, 229-len(ext[1]))) // padding + var ret []byte + for _, e := range ext { + ret = append(ret, e...) + } + return ret +} + +func (c *Steam) composeClientHello(hd clientHelloFields) (ch []byte) { + var clientHello [12][]byte + clientHello[0] = []byte{0x01} // handshake type + clientHello[1] = []byte{0x00, 0x01, 0xfc} // length 508 + clientHello[2] = []byte{0x03, 0x03} // client version + clientHello[3] = hd.random // random + clientHello[4] = []byte{0x20} // session id length 32 + clientHello[5] = hd.sessionId // session id + clientHello[6] = []byte{0x00, 0x20} // cipher suites length 32 + cipherSuites, _ := hex.DecodeString("130313011302cca9cca8c02bc02fc02cc030c013c014009c009d002f0035") + clientHello[7] = append(makeGREASE(), cipherSuites...) // cipher suites + clientHello[8] = []byte{0x01} // compression methods length 1 + clientHello[9] = []byte{0x00} // compression methods + clientHello[11] = c.composeExtensions(hd.serverName, hd.x25519KeyShare) + clientHello[10] = []byte{0x00, 0x00} // extensions length 403 + binary.BigEndian.PutUint16(clientHello[10], uint16(len(clientHello[11]))) + var ret []byte + for _, c := range clientHello { + ret = append(ret, c...) + } + return ret +} diff --git a/internal/server/auth.go b/internal/server/auth.go index 1ce79ff..c7e6d1b 100644 --- a/internal/server/auth.go +++ b/internal/server/auth.go @@ -83,10 +83,6 @@ func AuthFirstPacket(firstPacket []byte, transport Transport, sta *State) (info err = fmt.Errorf("%w: %v", ErrBadDecryption, err) return } - if _, ok := sta.ProxyBook[info.ProxyMethod]; !ok { - err = ErrBadProxyMethod - return - } info.Transport = transport return } diff --git a/internal/server/dispatcher.go b/internal/server/dispatcher.go index 287e363..20eae5f 100644 --- a/internal/server/dispatcher.go +++ b/internal/server/dispatcher.go @@ -213,6 +213,18 @@ func dispatchConnection(conn net.Conn, sta *State) { return } + if _, ok := sta.ProxyBook[ci.ProxyMethod]; !ok { + log.WithFields(log.Fields{ + "remoteAddr": conn.RemoteAddr(), + "UID": b64(ci.UID), + "sessionId": ci.SessionId, + "proxyMethod": ci.ProxyMethod, + "encryptionMethod": ci.EncryptionMethod, + }).Error(ErrBadProxyMethod) + goWeb() + return + } + var user *ActiveUser if sta.IsBypass(ci.UID) { user, err = sta.Panel.GetBypassUser(ci.UID) diff --git a/internal/server/usermanager/localmanager.go b/internal/server/usermanager/localmanager.go index d60f62e..4b3096b 100644 --- a/internal/server/usermanager/localmanager.go +++ b/internal/server/usermanager/localmanager.go @@ -191,6 +191,9 @@ func (manager *localManager) ListAllUsers() (infos []UserInfo, err error) { }) return err }) + if infos == nil { + infos = []UserInfo{} + } return } diff --git a/internal/server/usermanager/voidmanager.go b/internal/server/usermanager/voidmanager.go index a20ab3c..d317353 100644 --- a/internal/server/usermanager/voidmanager.go +++ b/internal/server/usermanager/voidmanager.go @@ -15,7 +15,7 @@ func (v *Voidmanager) UploadStatus(updates []StatusUpdate) ([]StatusResponse, er } func (v *Voidmanager) ListAllUsers() ([]UserInfo, error) { - return nil, ErrMangerIsVoid + return []UserInfo{}, ErrMangerIsVoid } func (v *Voidmanager) GetUserInfo(UID []byte) (UserInfo, error) {