Add raw DTLS fingerprints

This commit is contained in:
theodorsm 2024-12-19 00:33:48 +01:00
parent fd9f08d986
commit 0976198523
6 changed files with 60 additions and 17 deletions

View file

@ -37,6 +37,7 @@ import (
"github.com/pion/ice/v4" "github.com/pion/ice/v4"
"github.com/pion/webrtc/v4" "github.com/pion/webrtc/v4"
"github.com/theodorsm/covert-dtls/pkg/fingerprints"
"github.com/xtaci/kcp-go/v5" "github.com/xtaci/kcp-go/v5"
"github.com/xtaci/smux" "github.com/xtaci/smux"
@ -118,8 +119,9 @@ type ClientConfig struct {
// connect to, as specified in the Bridge line of the torrc. // connect to, as specified in the Bridge line of the torrc.
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
CovertDTLSConfig string CovertDTLSConfig string
CovertDTLSFingerprint string
} }
// NewSnowflakeClient creates a new Snowflake transport client that can spawn multiple // NewSnowflakeClient creates a new Snowflake transport client that can spawn multiple
@ -165,9 +167,12 @@ func NewSnowflakeClient(config ClientConfig) (*Transport, error) {
eventsLogger := event.NewSnowflakeEventDispatcher() eventsLogger := event.NewSnowflakeEventDispatcher()
var transport *Transport var transport *Transport
// TODO: Add fingerprint config
if config.CovertDTLSConfig != "" { if config.CovertDTLSConfig != "" {
covertDTLSConfig := covertdtls.ParseConfigString(config.CovertDTLSConfig) covertDTLSConfig := covertdtls.ParseConfigString(config.CovertDTLSConfig)
if config.CovertDTLSFingerprint != "" {
covertDTLSConfig.Fingerprint = fingerprints.ClientHelloFingerprint(*&config.CovertDTLSFingerprint)
}
transport = &Transport{dialer: NewCovertWebRTCDialerWithEventsAndProxy(broker, iceServers, max, eventsLogger, config.CommunicationProxy, &covertDTLSConfig), eventDispatcher: eventsLogger} 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

@ -287,7 +287,17 @@ func (c *WebRTCPeer) preparePeerConnection(
s.SetNet(vnet) s.SetNet(vnet)
if covertDTLSConfig.Mimic { if covertDTLSConfig.Fingerprint != "" {
mimic := &mimicry.MimickedClientHello{}
err = mimic.LoadFingerprint(covertDTLSConfig.Fingerprint)
if err != nil {
log.Printf("NewPeerConnection ERROR: %s", err)
return err
}
profiles := utils.DefaultSRTPProtectionProfiles()
s.SetSRTPProtectionProfiles(profiles...)
s.SetDTLSClientHelloMessageHook(mimic.Hook)
} else if covertDTLSConfig.Mimic {
mimic := &mimicry.MimickedClientHello{} mimic := &mimicry.MimickedClientHello{}
if covertDTLSConfig.Randomize { if covertDTLSConfig.Randomize {
err = mimic.LoadRandomFingerprint() err = mimic.LoadRandomFingerprint()

View file

@ -126,6 +126,9 @@ func socksAcceptLoop(ln *pt.SocksListener, config sf.ClientConfig, shutdown chan
if arg, ok := conn.Req.Args.Get("covertdtls-config"); ok { if arg, ok := conn.Req.Args.Get("covertdtls-config"); ok {
config.CovertDTLSConfig = arg config.CovertDTLSConfig = arg
} }
if arg, ok := conn.Req.Args.Get("covertdtls-fingerprint"); ok {
config.CovertDTLSFingerprint = arg
}
transport, err := sf.NewSnowflakeClient(config) transport, err := sf.NewSnowflakeClient(config)
if err != nil { if err != nil {
conn.Reject() conn.Reject()
@ -177,7 +180,8 @@ 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")
covertDTLSConfig := flag.String("covertdtls-config", "", "Configuration of dtls mimicking and randomization: mimic, randomize, randomizemimic") covertDTLSConfig := flag.String("covertdtls-config", "", "Configuration of DTLS mimicking and randomization: mimic, randomize, randomizemimic")
covertDTLSfingerprint := flag.String("covertdtls-fingerprint", "", "Mimicking of a raw DTLS fingerprint")
// Deprecated // Deprecated
oldLogToStateDir := flag.Bool("logToStateDir", false, "use -log-to-state-dir instead") oldLogToStateDir := flag.Bool("logToStateDir", false, "use -log-to-state-dir instead")
@ -236,15 +240,16 @@ func main() {
} }
config := sf.ClientConfig{ config := sf.ClientConfig{
BrokerURL: *brokerURL, BrokerURL: *brokerURL,
AmpCacheURL: *ampCacheURL, AmpCacheURL: *ampCacheURL,
SQSQueueURL: *sqsQueueURL, SQSQueueURL: *sqsQueueURL,
SQSCredsStr: *sqsCredsStr, SQSCredsStr: *sqsCredsStr,
FrontDomains: frontDomains, FrontDomains: frontDomains,
ICEAddresses: iceAddresses, ICEAddresses: iceAddresses,
KeepLocalAddresses: *keepLocalAddresses || *oldKeepLocalAddresses, KeepLocalAddresses: *keepLocalAddresses || *oldKeepLocalAddresses,
Max: *max, Max: *max,
CovertDTLSConfig: *covertDTLSConfig, CovertDTLSConfig: *covertDTLSConfig,
CovertDTLSFingerprint: *covertDTLSfingerprint,
} }
// Begin goptlib client process. // Begin goptlib client process.

View file

@ -48,6 +48,8 @@ Usage of ./proxy:
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 -covertdtls-config string
Configuration of dtls mimicking and randomization: mimic, randomize, randomizemimic Configuration of dtls mimicking and randomization: mimic, randomize, randomizemimic
-covertdtls-fingerprint string
Mimicking of a raw DTLS fingerprint
-disable-stats-logger -disable-stats-logger
disable the exposing mechanism for stats using logs disable the exposing mechanism for stats using logs
-dtls-randomize -dtls-randomize

View file

@ -430,7 +430,17 @@ func (sf *SnowflakeProxy) makeWebRTCAPI() *webrtc.API {
settingsEngine.SetDTLSInsecureSkipHelloVerify(true) settingsEngine.SetDTLSInsecureSkipHelloVerify(true)
if sf.CovertDTLSConfig.Mimic { if sf.CovertDTLSConfig.Fingerprint != "" {
mimic := &mimicry.MimickedClientHello{}
err := mimic.LoadFingerprint(sf.CovertDTLSConfig.Fingerprint)
if err != nil {
log.Printf("NewPeerConnection ERROR: %s", err)
return nil
}
profiles := utils.DefaultSRTPProtectionProfiles()
settingsEngine.SetSRTPProtectionProfiles(profiles...)
settingsEngine.SetDTLSClientHelloMessageHook(mimic.Hook)
} else if sf.CovertDTLSConfig.Mimic {
mimic := &mimicry.MimickedClientHello{} mimic := &mimicry.MimickedClientHello{}
if sf.CovertDTLSConfig.Randomize { if sf.CovertDTLSConfig.Randomize {
err := mimic.LoadRandomFingerprint() err := mimic.LoadRandomFingerprint()

View file

@ -11,6 +11,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/theodorsm/covert-dtls/pkg/fingerprints"
"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/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"
@ -47,7 +48,8 @@ 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")
covertDTLSConfig := flag.String("covertdtls-config", "", "Configuration of dtls mimicking and randomization: mimic, randomize, randomizemimic") covertDTLSConfig := flag.String("covertdtls-config", "", "Configuration of DTLS mimicking and randomization: mimic, randomize, randomizemimic")
covertDTLSfingerprint := flag.String("covertdtls-fingerprint", "", "Mimicking of a raw DTLS fingerprint")
var ephemeralPortsRange []uint16 = []uint16{0, 0} var ephemeralPortsRange []uint16 = []uint16{0, 0}
@ -94,6 +96,15 @@ func main() {
} }
} }
var cDTLSconfig covertdtls.CovertDTLSConfig
if *covertDTLSConfig != "" {
cDTLSconfig = covertdtls.ParseConfigString(*covertDTLSConfig)
}
if *covertDTLSfingerprint != "" {
cDTLSconfig.Fingerprint = fingerprints.ClientHelloFingerprint(*covertDTLSfingerprint)
}
proxy := sf.SnowflakeProxy{ proxy := sf.SnowflakeProxy{
PollInterval: *pollInterval, PollInterval: *pollInterval,
Capacity: uint(*capacity), Capacity: uint(*capacity),
@ -114,7 +125,7 @@ func main() {
AllowNonTLSRelay: *allowNonTLSRelay, AllowNonTLSRelay: *allowNonTLSRelay,
SummaryInterval: *summaryInterval, SummaryInterval: *summaryInterval,
CovertDTLSConfig: covertdtls.ParseConfigString(*covertDTLSConfig), CovertDTLSConfig: cDTLSconfig,
} }
var logOutput = io.Discard var logOutput = io.Discard