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

@ -17,6 +17,7 @@ import (
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/covertdtls"
"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/nat"
@ -177,10 +178,9 @@ type WebRTCDialer struct {
webrtcConfig *webrtc.Configuration
max int
eventLogger event.SnowflakeEventReceiver
proxy *url.URL
dtlsRandomize bool
dtlsMimic bool
eventLogger event.SnowflakeEventReceiver
proxy *url.URL
covertDTLSConfig *covertdtls.CovertDTLSConfig
}
// Deprecated: Use NewWebRTCDialerWithEventsAndProxy instead
@ -214,7 +214,7 @@ func NewWebRTCDialerWithEventsAndProxy(broker *BrokerChannel, iceServers []webrt
// NewWebRTCDialerWithEventsAndProxy constructs a new WebRTCDialer setting DTLS mimicking and randomization.
func NewCovertWebRTCDialerWithEventsAndProxy(broker *BrokerChannel, iceServers []webrtc.ICEServer, max int,
eventLogger event.SnowflakeEventReceiver, proxy *url.URL,
dtlsRandomize bool, dtlsMimic bool,
covertDTLSConfig *covertdtls.CovertDTLSConfig,
) *WebRTCDialer {
config := webrtc.Configuration{
ICEServers: iceServers,
@ -225,10 +225,9 @@ func NewCovertWebRTCDialerWithEventsAndProxy(broker *BrokerChannel, iceServers [
webrtcConfig: &config,
max: max,
eventLogger: eventLogger,
proxy: proxy,
dtlsRandomize: dtlsRandomize,
dtlsMimic: dtlsMimic,
eventLogger: eventLogger,
proxy: proxy,
covertDTLSConfig: covertDTLSConfig,
}
}
@ -236,8 +235,8 @@ func NewCovertWebRTCDialerWithEventsAndProxy(broker *BrokerChannel, iceServers [
func (w WebRTCDialer) Catch() (*WebRTCPeer, error) {
// TODO: [#25591] Fetch ICE server information from Broker.
// TODO: [#25596] Consider TURN servers here too.
if w.dtlsRandomize || w.dtlsMimic {
return NewCovertWebRTCPeerWithEventsAndProxy(w.webrtcConfig, w.BrokerChannel, w.eventLogger, w.proxy, w.dtlsRandomize, w.dtlsMimic)
if w.covertDTLSConfig != nil {
return NewCovertWebRTCPeerWithEventsAndProxy(w.webrtcConfig, w.BrokerChannel, w.eventLogger, w.proxy, w.covertDTLSConfig)
}
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/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/nat"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/turbotunnel"
@ -118,8 +119,7 @@ type ClientConfig struct {
BridgeFingerprint string
// CommunicationProxy is the proxy address for network communication
CommunicationProxy *url.URL
DTLSRandomize bool
DTLSMimic bool
CovertDTLSConfig string
}
// NewSnowflakeClient creates a new Snowflake transport client that can spawn multiple
@ -165,8 +165,10 @@ func NewSnowflakeClient(config ClientConfig) (*Transport, error) {
eventsLogger := event.NewSnowflakeEventDispatcher()
var transport *Transport
if config.DTLSRandomize || config.DTLSMimic {
transport = &Transport{dialer: NewCovertWebRTCDialerWithEventsAndProxy(broker, iceServers, max, eventsLogger, config.CommunicationProxy, config.DTLSRandomize, config.DTLSMimic), eventDispatcher: eventsLogger}
// TODO: Add fingerprint config
if config.CovertDTLSConfig != "" {
covertDTLSConfig := covertdtls.ParseConfigString(config.CovertDTLSConfig)
transport = &Transport{dialer: NewCovertWebRTCDialerWithEventsAndProxy(broker, iceServers, max, eventsLogger, config.CommunicationProxy, &covertDTLSConfig), eventDispatcher: eventsLogger}
} else {
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/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/proxy"
"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/util"
@ -95,7 +96,7 @@ func NewWebRTCPeerWithEventsAndProxy(
connection.eventsLogger = eventsLogger
connection.proxy = proxy
err := connection.connect(config, broker, false, false)
err := connection.connect(config, broker, nil)
if err != nil {
connection.Close()
return nil, err
@ -107,7 +108,7 @@ func NewWebRTCPeerWithEventsAndProxy(
func NewCovertWebRTCPeerWithEventsAndProxy(
config *webrtc.Configuration, broker *BrokerChannel,
eventsLogger event.SnowflakeEventReceiver, proxy *url.URL,
dtlsRandomize bool, dtlsMimic bool,
covertDTLSConfig *covertdtls.CovertDTLSConfig,
) (*WebRTCPeer, error) {
if eventsLogger == nil {
eventsLogger = event.NewSnowflakeEventDispatcher()
@ -132,7 +133,7 @@ func NewCovertWebRTCPeerWithEventsAndProxy(
connection.eventsLogger = eventsLogger
connection.proxy = proxy
err := connection.connect(config, broker, dtlsRandomize, dtlsMimic)
err := connection.connect(config, broker, covertDTLSConfig)
if err != nil {
connection.Close()
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,
// 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...")
err := c.preparePeerConnection(config, broker.keepLocalAddresses, dtlsRandomize, dtlsMimic)
err := c.preparePeerConnection(config, broker.keepLocalAddresses, covertDTLSConfig)
localDescription := c.pc.LocalDescription()
c.eventsLogger.OnNewSnowflakeEvent(event.EventOnOfferCreated{
WebRTCLocalDescription: localDescription,
@ -252,8 +253,7 @@ func (c *WebRTCPeer) connect(config *webrtc.Configuration, broker *BrokerChannel
func (c *WebRTCPeer) preparePeerConnection(
config *webrtc.Configuration,
keepLocalAddresses bool,
dtlsRandomize bool,
dtlsMimic bool,
covertDTLSConfig *covertdtls.CovertDTLSConfig,
) error {
var err error
s := webrtc.SettingEngine{}
@ -287,14 +287,21 @@ func (c *WebRTCPeer) preparePeerConnection(
s.SetNet(vnet)
if dtlsRandomize {
rand := randomize.RandomizedMessageClientHello{RandomALPN: true}
s.SetDTLSClientHelloMessageHook(rand.Hook)
} else if dtlsMimic {
if covertDTLSConfig.Mimic {
mimic := &mimicry.MimickedClientHello{}
if covertDTLSConfig.Randomize {
err = mimic.LoadRandomFingerprint()
if err != nil {
log.Printf("NewPeerConnection ERROR: %s", err)
return err
}
}
profiles := utils.DefaultSRTPProtectionProfiles()
s.SetSRTPProtectionProfiles(profiles...)
s.SetDTLSClientHelloMessageHook(mimic.Hook)
} else if covertDTLSConfig.Randomize {
rand := randomize.RandomizedMessageClientHello{RandomALPN: true}
s.SetDTLSClientHelloMessageHook(rand.Hook)
}
api := webrtc.NewAPI(webrtc.WithSettingEngine(s))