Merge branch 'covertdtls' into 'main'

Add covert-dtls to proxy and client

See merge request tpo/anti-censorship/pluggable-transports/snowflake!448
This commit is contained in:
theodorsm 2024-12-05 14:34:43 +00:00
commit aa5c034ba2
9 changed files with 92 additions and 16 deletions

View file

@ -177,23 +177,26 @@ type WebRTCDialer struct {
webrtcConfig *webrtc.Configuration
max int
eventLogger event.SnowflakeEventReceiver
proxy *url.URL
eventLogger event.SnowflakeEventReceiver
proxy *url.URL
dtlsRandomize bool
dtlsMimic bool
}
// Deprecated: Use NewWebRTCDialerWithEventsAndProxy instead
func NewWebRTCDialer(broker *BrokerChannel, iceServers []webrtc.ICEServer, max int) *WebRTCDialer {
return NewWebRTCDialerWithEventsAndProxy(broker, iceServers, max, nil, nil)
return NewWebRTCDialerWithEventsAndProxy(broker, iceServers, max, nil, nil, false, false)
}
// Deprecated: Use NewWebRTCDialerWithEventsAndProxy instead
func NewWebRTCDialerWithEvents(broker *BrokerChannel, iceServers []webrtc.ICEServer, max int, eventLogger event.SnowflakeEventReceiver) *WebRTCDialer {
return NewWebRTCDialerWithEventsAndProxy(broker, iceServers, max, eventLogger, nil)
return NewWebRTCDialerWithEventsAndProxy(broker, iceServers, max, eventLogger, nil, false, false)
}
// NewWebRTCDialerWithEventsAndProxy constructs a new WebRTCDialer.
func NewWebRTCDialerWithEventsAndProxy(broker *BrokerChannel, iceServers []webrtc.ICEServer, max int,
eventLogger event.SnowflakeEventReceiver, proxy *url.URL,
dtlsRandomize bool, dtlsMimic bool,
) *WebRTCDialer {
config := webrtc.Configuration{
ICEServers: iceServers,
@ -204,8 +207,10 @@ func NewWebRTCDialerWithEventsAndProxy(broker *BrokerChannel, iceServers []webrt
webrtcConfig: &config,
max: max,
eventLogger: eventLogger,
proxy: proxy,
eventLogger: eventLogger,
proxy: proxy,
dtlsRandomize: dtlsRandomize,
dtlsMimic: dtlsMimic,
}
}
@ -213,7 +218,7 @@ func NewWebRTCDialerWithEventsAndProxy(broker *BrokerChannel, iceServers []webrt
func (w WebRTCDialer) Catch() (*WebRTCPeer, error) {
// TODO: [#25591] Fetch ICE server information from Broker.
// TODO: [#25596] Consider TURN servers here too.
return NewWebRTCPeerWithEventsAndProxy(w.webrtcConfig, w.BrokerChannel, w.eventLogger, w.proxy)
return NewWebRTCPeerWithEventsAndProxy(w.webrtcConfig, w.BrokerChannel, w.eventLogger, w.proxy, w.dtlsRandomize, w.dtlsMimic)
}
// GetMax returns the maximum number of snowflakes to collect.

View file

@ -118,6 +118,8 @@ type ClientConfig struct {
BridgeFingerprint string
// CommunicationProxy is the proxy address for network communication
CommunicationProxy *url.URL
DTLSRandomize bool
DTLSMimic bool
}
// NewSnowflakeClient creates a new Snowflake transport client that can spawn multiple
@ -161,7 +163,7 @@ func NewSnowflakeClient(config ClientConfig) (*Transport, error) {
max = config.Max
}
eventsLogger := event.NewSnowflakeEventDispatcher()
transport := &Transport{dialer: NewWebRTCDialerWithEventsAndProxy(broker, iceServers, max, eventsLogger, config.CommunicationProxy), eventDispatcher: eventsLogger}
transport := &Transport{dialer: NewWebRTCDialerWithEventsAndProxy(broker, iceServers, max, eventsLogger, config.CommunicationProxy, config.DTLSRandomize, config.DTLSMimic), eventDispatcher: eventsLogger}
return transport, nil
}

View file

@ -11,10 +11,13 @@ import (
"sync"
"time"
"github.com/pion/dtls/v3"
"github.com/pion/ice/v4"
"github.com/pion/transport/v3"
"github.com/pion/transport/v3/stdnet"
"github.com/pion/webrtc/v4"
"github.com/theodorsm/covert-dtls/pkg/mimicry"
"github.com/theodorsm/covert-dtls/pkg/randomize"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/event"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/proxy"
@ -49,7 +52,7 @@ type WebRTCPeer struct {
func NewWebRTCPeer(
config *webrtc.Configuration, broker *BrokerChannel,
) (*WebRTCPeer, error) {
return NewWebRTCPeerWithEventsAndProxy(config, broker, nil, nil)
return NewWebRTCPeerWithEventsAndProxy(config, broker, nil, nil, false, false)
}
// Deprecated: Use NewWebRTCPeerWithEventsAndProxy Instead.
@ -57,7 +60,7 @@ func NewWebRTCPeerWithEvents(
config *webrtc.Configuration, broker *BrokerChannel,
eventsLogger event.SnowflakeEventReceiver,
) (*WebRTCPeer, error) {
return NewWebRTCPeerWithEventsAndProxy(config, broker, eventsLogger, nil)
return NewWebRTCPeerWithEventsAndProxy(config, broker, eventsLogger, nil, false, false)
}
// NewWebRTCPeerWithEventsAndProxy constructs a WebRTC PeerConnection to a snowflake proxy.
@ -68,6 +71,7 @@ func NewWebRTCPeerWithEvents(
func NewWebRTCPeerWithEventsAndProxy(
config *webrtc.Configuration, broker *BrokerChannel,
eventsLogger event.SnowflakeEventReceiver, proxy *url.URL,
dtlsRandomize bool, dtlsMimic bool,
) (*WebRTCPeer, error) {
if eventsLogger == nil {
eventsLogger = event.NewSnowflakeEventDispatcher()
@ -92,7 +96,7 @@ func NewWebRTCPeerWithEventsAndProxy(
connection.eventsLogger = eventsLogger
connection.proxy = proxy
err := connection.connect(config, broker)
err := connection.connect(config, broker, dtlsRandomize, dtlsMimic)
if err != nil {
connection.Close()
return nil, err
@ -166,10 +170,9 @@ func (c *WebRTCPeer) checkForStaleness(timeout time.Duration) {
// 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
func (c *WebRTCPeer) connect(config *webrtc.Configuration, broker *BrokerChannel) error {
func (c *WebRTCPeer) connect(config *webrtc.Configuration, broker *BrokerChannel, dtlsRandomize bool, dtlsMimic bool) error {
log.Println(c.id, " connecting...")
err := c.preparePeerConnection(config, broker.keepLocalAddresses)
err := c.preparePeerConnection(config, broker.keepLocalAddresses, dtlsRandomize, dtlsMimic)
localDescription := c.pc.LocalDescription()
c.eventsLogger.OnNewSnowflakeEvent(event.EventOnOfferCreated{
WebRTCLocalDescription: localDescription,
@ -213,6 +216,8 @@ func (c *WebRTCPeer) connect(config *webrtc.Configuration, broker *BrokerChannel
func (c *WebRTCPeer) preparePeerConnection(
config *webrtc.Configuration,
keepLocalAddresses bool,
dtlsRandomize bool,
dtlsMimic bool,
) error {
var err error
s := webrtc.SettingEngine{}
@ -245,6 +250,24 @@ func (c *WebRTCPeer) preparePeerConnection(
}
s.SetNet(vnet)
if dtlsRandomize {
rand := randomize.RandomizedMessageClientHello{RandomALPN: true}
s.SetDTLSClientHelloMessageHook(rand.Hook)
} else if dtlsMimic {
mimic := &mimicry.MimickedClientHello{}
profiles := []dtls.SRTPProtectionProfile{
dtls.SRTP_AES128_CM_HMAC_SHA1_80,
dtls.SRTP_AES128_CM_HMAC_SHA1_32,
dtls.SRTP_AEAD_AES_128_GCM,
dtls.SRTP_AEAD_AES_256_GCM,
dtls.SRTP_AES256_CM_SHA1_32,
dtls.SRTP_AES256_CM_SHA1_80,
}
s.SetSRTPProtectionProfiles(profiles...)
s.SetDTLSClientHelloMessageHook(mimic.Hook)
}
api := webrtc.NewAPI(webrtc.WithSettingEngine(s))
c.pc, err = api.NewPeerConnection(*config)
if err != nil {

View file

@ -174,6 +174,8 @@ func main() {
max := flag.Int("max", DefaultSnowflakeCapacity,
"capacity for number of multiplexed WebRTC peers")
versionFlag := flag.Bool("version", false, "display version info to stderr and quit")
dtlsRandomize := flag.Bool("dtls-randomize", false, "randomize DTLS client hello")
dtlsMimic := flag.Bool("dtls-mimic", false, "mimic DTLS client hello of Chrome and Firefox")
// Deprecated
oldLogToStateDir := flag.Bool("logToStateDir", false, "use -log-to-state-dir instead")
@ -186,6 +188,10 @@ func main() {
os.Exit(0)
}
if *dtlsMimic && *dtlsRandomize {
log.Fatal("Cannot both Randomize and Mimic DTLS client hello")
}
log.SetFlags(log.LstdFlags | log.LUTC)
// Don't write to stderr; versions of tor earlier than about 0.3.5.6 do
@ -240,6 +246,8 @@ func main() {
ICEAddresses: iceAddresses,
KeepLocalAddresses: *keepLocalAddresses || *oldKeepLocalAddresses,
Max: *max,
DTLSRandomize: *dtlsRandomize,
DTLSMimic: *dtlsMimic,
}
// Begin goptlib client process.