Add CovertDTLSConfig

This commit is contained in:
theodorsm 2024-12-18 00:09:15 +01:00
parent 5912e2892a
commit bb11646e73
11 changed files with 89 additions and 56 deletions

View file

@ -35,7 +35,7 @@ UseBridges 1
ClientTransportPlugin snowflake exec ./client -log snowflake.log ClientTransportPlugin snowflake exec ./client -log snowflake.log
Bridge snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 fingerprint=2B280B23E1107BB62ABFC40DDCC8824814F80A72 url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ fronts=foursquare.com,github.githubassets.com ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellorandomizedalpn Bridge snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 fingerprint=2B280B23E1107BB62ABFC40DDCC8824814F80A72 url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ fronts=foursquare.com,github.githubassets.com ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellorandomizedalpn covertdtls-config=randomizemimic
``` ```
`fingerprint=` is the fingerprint of bridge that the client will ultimately be connecting to. `fingerprint=` is the fingerprint of bridge that the client will ultimately be connecting to.
@ -48,6 +48,8 @@ Bridge snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 fingerpri
`utls-imitate=` configuration instructs the client to use fingerprinting resistance when connecting when rendez-vous'ing with the broker. `utls-imitate=` configuration instructs the client to use fingerprinting resistance when connecting when rendez-vous'ing with the broker.
`covertdtls-config=` configuration for randomization or mimicking (Firefox/Chrome browser) of DTLS Client Hello messages. String can be "randomize", "mimic" or "randomizemimc"
To bootstrap Tor, run: To bootstrap Tor, run:
``` ```
tor -f torrc tor -f torrc

View file

@ -17,6 +17,7 @@ import (
utls "github.com/refraction-networking/utls" utls "github.com/refraction-networking/utls"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/certs" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/certs"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/covertdtls"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/event" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/event"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/messages" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/messages"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/nat" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/nat"
@ -177,10 +178,9 @@ type WebRTCDialer struct {
webrtcConfig *webrtc.Configuration webrtcConfig *webrtc.Configuration
max int max int
eventLogger event.SnowflakeEventReceiver eventLogger event.SnowflakeEventReceiver
proxy *url.URL proxy *url.URL
dtlsRandomize bool covertDTLSConfig *covertdtls.CovertDTLSConfig
dtlsMimic bool
} }
// Deprecated: Use NewWebRTCDialerWithEventsAndProxy instead // Deprecated: Use NewWebRTCDialerWithEventsAndProxy instead
@ -214,7 +214,7 @@ func NewWebRTCDialerWithEventsAndProxy(broker *BrokerChannel, iceServers []webrt
// NewWebRTCDialerWithEventsAndProxy constructs a new WebRTCDialer setting DTLS mimicking and randomization. // NewWebRTCDialerWithEventsAndProxy constructs a new WebRTCDialer setting DTLS mimicking and randomization.
func NewCovertWebRTCDialerWithEventsAndProxy(broker *BrokerChannel, iceServers []webrtc.ICEServer, max int, func NewCovertWebRTCDialerWithEventsAndProxy(broker *BrokerChannel, iceServers []webrtc.ICEServer, max int,
eventLogger event.SnowflakeEventReceiver, proxy *url.URL, eventLogger event.SnowflakeEventReceiver, proxy *url.URL,
dtlsRandomize bool, dtlsMimic bool, covertDTLSConfig *covertdtls.CovertDTLSConfig,
) *WebRTCDialer { ) *WebRTCDialer {
config := webrtc.Configuration{ config := webrtc.Configuration{
ICEServers: iceServers, ICEServers: iceServers,
@ -225,10 +225,9 @@ func NewCovertWebRTCDialerWithEventsAndProxy(broker *BrokerChannel, iceServers [
webrtcConfig: &config, webrtcConfig: &config,
max: max, max: max,
eventLogger: eventLogger, eventLogger: eventLogger,
proxy: proxy, proxy: proxy,
dtlsRandomize: dtlsRandomize, covertDTLSConfig: covertDTLSConfig,
dtlsMimic: dtlsMimic,
} }
} }
@ -236,8 +235,8 @@ func NewCovertWebRTCDialerWithEventsAndProxy(broker *BrokerChannel, iceServers [
func (w WebRTCDialer) Catch() (*WebRTCPeer, error) { func (w WebRTCDialer) Catch() (*WebRTCPeer, error) {
// TODO: [#25591] Fetch ICE server information from Broker. // TODO: [#25591] Fetch ICE server information from Broker.
// TODO: [#25596] Consider TURN servers here too. // TODO: [#25596] Consider TURN servers here too.
if w.dtlsRandomize || w.dtlsMimic { if w.covertDTLSConfig != nil {
return NewCovertWebRTCPeerWithEventsAndProxy(w.webrtcConfig, w.BrokerChannel, w.eventLogger, w.proxy, w.dtlsRandomize, w.dtlsMimic) return NewCovertWebRTCPeerWithEventsAndProxy(w.webrtcConfig, w.BrokerChannel, w.eventLogger, w.proxy, w.covertDTLSConfig)
} }
return NewWebRTCPeerWithEventsAndProxy(w.webrtcConfig, w.BrokerChannel, w.eventLogger, w.proxy) return NewWebRTCPeerWithEventsAndProxy(w.webrtcConfig, w.BrokerChannel, w.eventLogger, w.proxy)
} }

View file

@ -40,6 +40,7 @@ import (
"github.com/xtaci/kcp-go/v5" "github.com/xtaci/kcp-go/v5"
"github.com/xtaci/smux" "github.com/xtaci/smux"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/covertdtls"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/event" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/event"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/nat" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/nat"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/turbotunnel" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/turbotunnel"
@ -118,8 +119,7 @@ type ClientConfig struct {
BridgeFingerprint string BridgeFingerprint string
// CommunicationProxy is the proxy address for network communication // CommunicationProxy is the proxy address for network communication
CommunicationProxy *url.URL CommunicationProxy *url.URL
DTLSRandomize bool CovertDTLSConfig string
DTLSMimic bool
} }
// NewSnowflakeClient creates a new Snowflake transport client that can spawn multiple // NewSnowflakeClient creates a new Snowflake transport client that can spawn multiple
@ -165,8 +165,10 @@ func NewSnowflakeClient(config ClientConfig) (*Transport, error) {
eventsLogger := event.NewSnowflakeEventDispatcher() eventsLogger := event.NewSnowflakeEventDispatcher()
var transport *Transport var transport *Transport
if config.DTLSRandomize || config.DTLSMimic { // TODO: Add fingerprint config
transport = &Transport{dialer: NewCovertWebRTCDialerWithEventsAndProxy(broker, iceServers, max, eventsLogger, config.CommunicationProxy, config.DTLSRandomize, config.DTLSMimic), eventDispatcher: eventsLogger} if config.CovertDTLSConfig != "" {
covertDTLSConfig := covertdtls.ParseConfigString(config.CovertDTLSConfig)
transport = &Transport{dialer: NewCovertWebRTCDialerWithEventsAndProxy(broker, iceServers, max, eventsLogger, config.CommunicationProxy, &covertDTLSConfig), eventDispatcher: eventsLogger}
} else { } else {
transport = &Transport{dialer: NewWebRTCDialerWithEventsAndProxy(broker, iceServers, max, eventsLogger, config.CommunicationProxy), eventDispatcher: eventsLogger} transport = &Transport{dialer: NewWebRTCDialerWithEventsAndProxy(broker, iceServers, max, eventsLogger, config.CommunicationProxy), eventDispatcher: eventsLogger}
} }

View file

@ -19,6 +19,7 @@ import (
"github.com/theodorsm/covert-dtls/pkg/randomize" "github.com/theodorsm/covert-dtls/pkg/randomize"
"github.com/theodorsm/covert-dtls/pkg/utils" "github.com/theodorsm/covert-dtls/pkg/utils"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/covertdtls"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/event" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/event"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/proxy" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/proxy"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/util" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/util"
@ -95,7 +96,7 @@ func NewWebRTCPeerWithEventsAndProxy(
connection.eventsLogger = eventsLogger connection.eventsLogger = eventsLogger
connection.proxy = proxy connection.proxy = proxy
err := connection.connect(config, broker, false, false) err := connection.connect(config, broker, nil)
if err != nil { if err != nil {
connection.Close() connection.Close()
return nil, err return nil, err
@ -107,7 +108,7 @@ func NewWebRTCPeerWithEventsAndProxy(
func NewCovertWebRTCPeerWithEventsAndProxy( func NewCovertWebRTCPeerWithEventsAndProxy(
config *webrtc.Configuration, broker *BrokerChannel, config *webrtc.Configuration, broker *BrokerChannel,
eventsLogger event.SnowflakeEventReceiver, proxy *url.URL, eventsLogger event.SnowflakeEventReceiver, proxy *url.URL,
dtlsRandomize bool, dtlsMimic bool, covertDTLSConfig *covertdtls.CovertDTLSConfig,
) (*WebRTCPeer, error) { ) (*WebRTCPeer, error) {
if eventsLogger == nil { if eventsLogger == nil {
eventsLogger = event.NewSnowflakeEventDispatcher() eventsLogger = event.NewSnowflakeEventDispatcher()
@ -132,7 +133,7 @@ func NewCovertWebRTCPeerWithEventsAndProxy(
connection.eventsLogger = eventsLogger connection.eventsLogger = eventsLogger
connection.proxy = proxy connection.proxy = proxy
err := connection.connect(config, broker, dtlsRandomize, dtlsMimic) err := connection.connect(config, broker, covertDTLSConfig)
if err != nil { if err != nil {
connection.Close() connection.Close()
return nil, err return nil, err
@ -206,9 +207,9 @@ func (c *WebRTCPeer) checkForStaleness(timeout time.Duration) {
// connect does the bulk of the work: gather ICE candidates, send the SDP offer to broker, // connect does the bulk of the work: gather ICE candidates, send the SDP offer to broker,
// receive an answer from broker, and wait for data channel to open // receive an answer from broker, and wait for data channel to open
func (c *WebRTCPeer) connect(config *webrtc.Configuration, broker *BrokerChannel, dtlsRandomize bool, dtlsMimic bool) error { func (c *WebRTCPeer) connect(config *webrtc.Configuration, broker *BrokerChannel, covertDTLSConfig *covertdtls.CovertDTLSConfig) error {
log.Println(c.id, " connecting...") log.Println(c.id, " connecting...")
err := c.preparePeerConnection(config, broker.keepLocalAddresses, dtlsRandomize, dtlsMimic) err := c.preparePeerConnection(config, broker.keepLocalAddresses, covertDTLSConfig)
localDescription := c.pc.LocalDescription() localDescription := c.pc.LocalDescription()
c.eventsLogger.OnNewSnowflakeEvent(event.EventOnOfferCreated{ c.eventsLogger.OnNewSnowflakeEvent(event.EventOnOfferCreated{
WebRTCLocalDescription: localDescription, WebRTCLocalDescription: localDescription,
@ -252,8 +253,7 @@ func (c *WebRTCPeer) connect(config *webrtc.Configuration, broker *BrokerChannel
func (c *WebRTCPeer) preparePeerConnection( func (c *WebRTCPeer) preparePeerConnection(
config *webrtc.Configuration, config *webrtc.Configuration,
keepLocalAddresses bool, keepLocalAddresses bool,
dtlsRandomize bool, covertDTLSConfig *covertdtls.CovertDTLSConfig,
dtlsMimic bool,
) error { ) error {
var err error var err error
s := webrtc.SettingEngine{} s := webrtc.SettingEngine{}
@ -287,14 +287,21 @@ func (c *WebRTCPeer) preparePeerConnection(
s.SetNet(vnet) s.SetNet(vnet)
if dtlsRandomize { if covertDTLSConfig.Mimic {
rand := randomize.RandomizedMessageClientHello{RandomALPN: true}
s.SetDTLSClientHelloMessageHook(rand.Hook)
} else if dtlsMimic {
mimic := &mimicry.MimickedClientHello{} mimic := &mimicry.MimickedClientHello{}
if covertDTLSConfig.Randomize {
err = mimic.LoadRandomFingerprint()
if err != nil {
log.Printf("NewPeerConnection ERROR: %s", err)
return err
}
}
profiles := utils.DefaultSRTPProtectionProfiles() profiles := utils.DefaultSRTPProtectionProfiles()
s.SetSRTPProtectionProfiles(profiles...) s.SetSRTPProtectionProfiles(profiles...)
s.SetDTLSClientHelloMessageHook(mimic.Hook) s.SetDTLSClientHelloMessageHook(mimic.Hook)
} else if covertDTLSConfig.Randomize {
rand := randomize.RandomizedMessageClientHello{RandomALPN: true}
s.SetDTLSClientHelloMessageHook(rand.Hook)
} }
api := webrtc.NewAPI(webrtc.WithSettingEngine(s)) api := webrtc.NewAPI(webrtc.WithSettingEngine(s))

View file

@ -123,6 +123,9 @@ func socksAcceptLoop(ln *pt.SocksListener, config sf.ClientConfig, shutdown chan
if arg, ok := conn.Req.Args.Get("fingerprint"); ok { if arg, ok := conn.Req.Args.Get("fingerprint"); ok {
config.BridgeFingerprint = arg config.BridgeFingerprint = arg
} }
if arg, ok := conn.Req.Args.Get("covertdtls-config"); ok {
config.CovertDTLSConfig = arg
}
transport, err := sf.NewSnowflakeClient(config) transport, err := sf.NewSnowflakeClient(config)
if err != nil { if err != nil {
conn.Reject() conn.Reject()
@ -174,8 +177,7 @@ func main() {
max := flag.Int("max", DefaultSnowflakeCapacity, max := flag.Int("max", DefaultSnowflakeCapacity,
"capacity for number of multiplexed WebRTC peers") "capacity for number of multiplexed WebRTC peers")
versionFlag := flag.Bool("version", false, "display version info to stderr and quit") versionFlag := flag.Bool("version", false, "display version info to stderr and quit")
dtlsRandomize := flag.Bool("dtls-randomize", false, "randomize DTLS client hello") covertDTLSConfig := flag.String("covertdtls-config", "", "Configuration of dtls mimicking and randomization: mimic, randomize, randomizemimic")
dtlsMimic := flag.Bool("dtls-mimic", false, "mimic DTLS client hello of Chrome and Firefox")
// Deprecated // Deprecated
oldLogToStateDir := flag.Bool("logToStateDir", false, "use -log-to-state-dir instead") oldLogToStateDir := flag.Bool("logToStateDir", false, "use -log-to-state-dir instead")
@ -188,10 +190,6 @@ func main() {
os.Exit(0) os.Exit(0)
} }
if *dtlsMimic && *dtlsRandomize {
log.Fatal("Cannot both Randomize and Mimic DTLS client hello")
}
log.SetFlags(log.LstdFlags | log.LUTC) log.SetFlags(log.LstdFlags | log.LUTC)
// Don't write to stderr; versions of tor earlier than about 0.3.5.6 do // Don't write to stderr; versions of tor earlier than about 0.3.5.6 do
@ -246,8 +244,7 @@ func main() {
ICEAddresses: iceAddresses, ICEAddresses: iceAddresses,
KeepLocalAddresses: *keepLocalAddresses || *oldKeepLocalAddresses, KeepLocalAddresses: *keepLocalAddresses || *oldKeepLocalAddresses,
Max: *max, Max: *max,
DTLSRandomize: *dtlsRandomize, CovertDTLSConfig: *covertDTLSConfig,
DTLSMimic: *dtlsMimic,
} }
// Begin goptlib client process. // Begin goptlib client process.

View file

@ -0,0 +1,24 @@
package covertdtls
import (
"github.com/theodorsm/covert-dtls/pkg/fingerprints"
"strings"
)
type CovertDTLSConfig struct {
Randomize bool
Mimic bool
Fingerprint fingerprints.ClientHelloFingerprint
}
func ParseConfigString(str string) CovertDTLSConfig {
config := CovertDTLSConfig{}
str = strings.ToLower(str)
if strings.Contains(str, "random") {
config.Randomize = true
}
if strings.Contains(str, "mimic") {
config.Mimic = true
}
return config
}

4
go.mod
View file

@ -10,7 +10,6 @@ require (
github.com/golang/mock v1.6.0 github.com/golang/mock v1.6.0
github.com/gorilla/websocket v1.5.3 github.com/gorilla/websocket v1.5.3
github.com/miekg/dns v1.1.62 github.com/miekg/dns v1.1.62
github.com/pion/dtls/v3 v3.0.4
github.com/pion/ice/v4 v4.0.3 github.com/pion/ice/v4 v4.0.3
github.com/pion/sdp/v3 v3.0.9 github.com/pion/sdp/v3 v3.0.9
github.com/pion/stun/v3 v3.0.0 github.com/pion/stun/v3 v3.0.0
@ -21,7 +20,7 @@ require (
github.com/refraction-networking/utls v1.6.7 github.com/refraction-networking/utls v1.6.7
github.com/smartystreets/goconvey v1.8.1 github.com/smartystreets/goconvey v1.8.1
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/theodorsm/covert-dtls v0.0.2-0.20241215210721-995fe9f65413 github.com/theodorsm/covert-dtls v0.0.2-0.20241217214738-5d8466562b2b
github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301
github.com/xtaci/kcp-go/v5 v5.6.8 github.com/xtaci/kcp-go/v5 v5.6.8
github.com/xtaci/smux v1.5.31 github.com/xtaci/smux v1.5.31
@ -58,6 +57,7 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pion/datachannel v1.5.9 // indirect github.com/pion/datachannel v1.5.9 // indirect
github.com/pion/dtls/v3 v3.0.4 // indirect
github.com/pion/interceptor v0.1.37 // indirect github.com/pion/interceptor v0.1.37 // indirect
github.com/pion/logging v0.2.2 // indirect github.com/pion/logging v0.2.2 // indirect
github.com/pion/mdns/v2 v2.0.7 // indirect github.com/pion/mdns/v2 v2.0.7 // indirect

4
go.sum
View file

@ -163,8 +163,8 @@ github.com/templexxx/cpu v0.1.0 h1:wVM+WIJP2nYaxVxqgHPD4wGA2aJ9rvrQRV8CvFzNb40=
github.com/templexxx/cpu v0.1.0/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk= github.com/templexxx/cpu v0.1.0/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/xorsimd v0.4.2 h1:ocZZ+Nvu65LGHmCLZ7OoCtg8Fx8jnHKK37SjvngUoVI= github.com/templexxx/xorsimd v0.4.2 h1:ocZZ+Nvu65LGHmCLZ7OoCtg8Fx8jnHKK37SjvngUoVI=
github.com/templexxx/xorsimd v0.4.2/go.mod h1:HgwaPoDREdi6OnULpSfxhzaiiSUY4Fi3JPn1wpt28NI= github.com/templexxx/xorsimd v0.4.2/go.mod h1:HgwaPoDREdi6OnULpSfxhzaiiSUY4Fi3JPn1wpt28NI=
github.com/theodorsm/covert-dtls v0.0.2-0.20241215210721-995fe9f65413 h1:gR1xoHiOzqQ4bm5EPFk1YVVYNJlPSrz5zu+/yVwNV0A= github.com/theodorsm/covert-dtls v0.0.2-0.20241217214738-5d8466562b2b h1:bp8+LC6XFBGUEonmXOIQXW9WjQrgGEZ/68ZrKFO0xdQ=
github.com/theodorsm/covert-dtls v0.0.2-0.20241215210721-995fe9f65413/go.mod h1:0Gj7OgRe9suVAMrNuuxMczZWVpa4LLuRjduo9d5g6Tc= github.com/theodorsm/covert-dtls v0.0.2-0.20241217214738-5d8466562b2b/go.mod h1:0Gj7OgRe9suVAMrNuuxMczZWVpa4LLuRjduo9d5g6Tc=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf h1:7PflaKRtU4np/epFxRXlFhlzLXZzKFrH5/I4so5Ove0= github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf h1:7PflaKRtU4np/epFxRXlFhlzLXZzKFrH5/I4so5Ove0=

View file

@ -46,10 +46,10 @@ Usage of ./proxy:
The URL of the broker server that the proxy will be using to find clients (default "https://snowflake-broker.torproject.net/") The URL of the broker server that the proxy will be using to find clients (default "https://snowflake-broker.torproject.net/")
-capacity uint -capacity uint
maximum concurrent clients (default is to accept an unlimited number of clients) maximum concurrent clients (default is to accept an unlimited number of clients)
-covertdtls-config string
Configuration of dtls mimicking and randomization: mimic, randomize, randomizemimic
-disable-stats-logger -disable-stats-logger
disable the exposing mechanism for stats using logs disable the exposing mechanism for stats using logs
-dtls-mimic
mimic DTLS client hello of Chrome and Firefox
-dtls-randomize -dtls-randomize
randomize DTLS client hello randomize DTLS client hello
-ephemeral-ports-range range -ephemeral-ports-range range

View file

@ -48,6 +48,7 @@ import (
"github.com/theodorsm/covert-dtls/pkg/randomize" "github.com/theodorsm/covert-dtls/pkg/randomize"
"github.com/theodorsm/covert-dtls/pkg/utils" "github.com/theodorsm/covert-dtls/pkg/utils"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/covertdtls"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/event" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/event"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/messages" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/messages"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/namematcher" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/namematcher"
@ -170,8 +171,7 @@ type SnowflakeProxy struct {
periodicProxyStats *periodicProxyStats periodicProxyStats *periodicProxyStats
bytesLogger bytesLogger bytesLogger bytesLogger
DTLSRandomize bool CovertDTLSConfig covertdtls.CovertDTLSConfig
DTLSMimic bool
} }
// Checks whether an IP address is a remote address for the client // Checks whether an IP address is a remote address for the client
@ -430,14 +430,21 @@ func (sf *SnowflakeProxy) makeWebRTCAPI() *webrtc.API {
settingsEngine.SetDTLSInsecureSkipHelloVerify(true) settingsEngine.SetDTLSInsecureSkipHelloVerify(true)
if sf.DTLSRandomize { if sf.CovertDTLSConfig.Mimic {
rand := randomize.RandomizedMessageClientHello{RandomALPN: true}
settingsEngine.SetDTLSClientHelloMessageHook(rand.Hook)
} else if sf.DTLSMimic {
mimic := &mimicry.MimickedClientHello{} mimic := &mimicry.MimickedClientHello{}
if sf.CovertDTLSConfig.Randomize {
err := mimic.LoadRandomFingerprint()
if err != nil {
log.Printf("makeWebRTCAPI ERROR: %s", err)
return nil
}
}
profiles := utils.DefaultSRTPProtectionProfiles() profiles := utils.DefaultSRTPProtectionProfiles()
settingsEngine.SetSRTPProtectionProfiles(profiles...) settingsEngine.SetSRTPProtectionProfiles(profiles...)
settingsEngine.SetDTLSClientHelloMessageHook(mimic.Hook) settingsEngine.SetDTLSClientHelloMessageHook(mimic.Hook)
} else if sf.CovertDTLSConfig.Randomize {
rand := randomize.RandomizedMessageClientHello{RandomALPN: true}
settingsEngine.SetDTLSClientHelloMessageHook(rand.Hook)
} }
return webrtc.NewAPI(webrtc.WithSettingEngine(settingsEngine)) return webrtc.NewAPI(webrtc.WithSettingEngine(settingsEngine))

View file

@ -12,6 +12,7 @@ import (
"time" "time"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/ptutil/safelog" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/ptutil/safelog"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/covertdtls"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/event" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/event"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/version" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/version"
sf "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/proxy/lib" sf "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/proxy/lib"
@ -46,8 +47,7 @@ func main() {
verboseLogging := flag.Bool("verbose", false, "increase log verbosity") verboseLogging := flag.Bool("verbose", false, "increase log verbosity")
ephemeralPortsRangeFlag := flag.String("ephemeral-ports-range", "", "Set the `range` of ports used for client connections (format:\"<min>:<max>\").\nIf omitted, the ports will be chosen automatically.") ephemeralPortsRangeFlag := flag.String("ephemeral-ports-range", "", "Set the `range` of ports used for client connections (format:\"<min>:<max>\").\nIf omitted, the ports will be chosen automatically.")
versionFlag := flag.Bool("version", false, "display version info to stderr and quit") versionFlag := flag.Bool("version", false, "display version info to stderr and quit")
dtlsRandomize := flag.Bool("dtls-randomize", false, "randomize DTLS client hello") covertDTLSConfig := flag.String("covertdtls-config", "", "Configuration of dtls mimicking and randomization: mimic, randomize, randomizemimic")
dtlsMimic := flag.Bool("dtls-mimic", false, "mimic DTLS client hello of Chrome and Firefox")
var ephemeralPortsRange []uint16 = []uint16{0, 0} var ephemeralPortsRange []uint16 = []uint16{0, 0}
@ -66,10 +66,6 @@ func main() {
log.Fatal("Cannot keep local address candidates when outbound address is specified") log.Fatal("Cannot keep local address candidates when outbound address is specified")
} }
if *dtlsMimic && *dtlsRandomize {
log.Fatal("Cannot both Randomize and Mimic DTLS client hello")
}
eventLogger := event.NewSnowflakeEventDispatcher() eventLogger := event.NewSnowflakeEventDispatcher()
if *ephemeralPortsRangeFlag != "" { if *ephemeralPortsRangeFlag != "" {
@ -117,9 +113,8 @@ func main() {
AllowProxyingToPrivateAddresses: *allowProxyingToPrivateAddresses, AllowProxyingToPrivateAddresses: *allowProxyingToPrivateAddresses,
AllowNonTLSRelay: *allowNonTLSRelay, AllowNonTLSRelay: *allowNonTLSRelay,
SummaryInterval: *summaryInterval, SummaryInterval: *summaryInterval,
DTLSRandomize: *dtlsRandomize, CovertDTLSConfig: covertdtls.ParseConfigString(*covertDTLSConfig),
DTLSMimic: *dtlsMimic,
} }
var logOutput = io.Discard var logOutput = io.Discard