Skip to content

Commit

Permalink
Merge pull request #648 from mirokuratczyk/tls-proto
Browse files Browse the repository at this point in the history
Add TLS-OSSH obfuscation protocol
  • Loading branch information
rod-hynes authored Jul 26, 2023
2 parents 14bf1ee + 50cdedb commit 8d4b756
Show file tree
Hide file tree
Showing 19 changed files with 1,750 additions and 153 deletions.
15 changes: 13 additions & 2 deletions psiphon/common/parameters/parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,6 @@ const (
ReplayObfuscatorPadding = "ReplayObfuscatorPadding"
ReplayFragmentor = "ReplayFragmentor"
ReplayTLSProfile = "ReplayTLSProfile"
ReplayRandomizedTLSProfile = "ReplayRandomizedTLSProfile"
ReplayFronting = "ReplayFronting"
ReplayHostname = "ReplayHostname"
ReplayQUICVersion = "ReplayQUICVersion"
Expand Down Expand Up @@ -345,6 +344,13 @@ const (
OSSHPrefixSplitMaxDelay = "OSSHPrefixSplitMaxDelay"
OSSHPrefixEnableFragmentor = "OSSHPrefixEnableFragmentor"
ServerOSSHPrefixSpecs = "ServerOSSHPrefixSpecs"
TLSTunnelTrafficShapingProbability = "TLSTunnelTrafficShapingProbability"
TLSTunnelMinTLSPadding = "TLSTunnelMinTLSPadding"
TLSTunnelMaxTLSPadding = "TLSTunnelMaxTLSPadding"

// Retired parameters

ReplayRandomizedTLSProfile = "ReplayRandomizedTLSProfile"
)

const (
Expand Down Expand Up @@ -588,7 +594,6 @@ var defaultParameters = map[string]struct {
ReplayObfuscatorPadding: {value: true},
ReplayFragmentor: {value: true},
ReplayTLSProfile: {value: true},
ReplayRandomizedTLSProfile: {value: true},
ReplayFronting: {value: true},
ReplayHostname: {value: true},
ReplayQUICVersion: {value: true},
Expand Down Expand Up @@ -730,6 +735,12 @@ var defaultParameters = map[string]struct {
OSSHPrefixSplitMaxDelay: {value: time.Duration(0), minimum: time.Duration(0)},
OSSHPrefixEnableFragmentor: {value: false},
ServerOSSHPrefixSpecs: {value: transforms.Specs{}, flags: serverSideOnly},

// TLSTunnelMinTLSPadding/TLSTunnelMaxTLSPadding are subject to TLS server limitations.

TLSTunnelTrafficShapingProbability: {value: 1.0, minimum: 0.0},
TLSTunnelMinTLSPadding: {value: 0, minimum: 0},
TLSTunnelMaxTLSPadding: {value: 0, minimum: 0},
}

// IsServerSideOnly indicates if the parameter specified by name is used
Expand Down
18 changes: 17 additions & 1 deletion psiphon/common/protocol/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
const (
TUNNEL_PROTOCOL_SSH = "SSH"
TUNNEL_PROTOCOL_OBFUSCATED_SSH = "OSSH"
TUNNEL_PROTOCOL_TLS_OBFUSCATED_SSH = "TLS-OSSH"
TUNNEL_PROTOCOL_UNFRONTED_MEEK = "UNFRONTED-MEEK-OSSH"
TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS = "UNFRONTED-MEEK-HTTPS-OSSH"
TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET = "UNFRONTED-MEEK-SESSION-TICKET-OSSH"
Expand Down Expand Up @@ -147,6 +148,7 @@ func (labeledProtocols LabeledTunnelProtocols) PruneInvalid() LabeledTunnelProto
var SupportedTunnelProtocols = TunnelProtocols{
TUNNEL_PROTOCOL_SSH,
TUNNEL_PROTOCOL_OBFUSCATED_SSH,
TUNNEL_PROTOCOL_TLS_OBFUSCATED_SSH,
TUNNEL_PROTOCOL_UNFRONTED_MEEK,
TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS,
TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET,
Expand Down Expand Up @@ -177,6 +179,13 @@ func TunnelProtocolUsesObfuscatedSSH(protocol string) bool {
return protocol != TUNNEL_PROTOCOL_SSH
}

// NOTE: breaks the naming convention of dropping the OSSH suffix because
// UsesTLS is ambiguous by itself as there are other protocols which use
// a TLS layer, e.g. UNFRONTED-MEEK-HTTPS-OSSH.
func TunnelProtocolUsesTLSOSSH(protocol string) bool {
return protocol == TUNNEL_PROTOCOL_TLS_OBFUSCATED_SSH
}

func TunnelProtocolUsesMeek(protocol string) bool {
return TunnelProtocolUsesMeekHTTP(protocol) ||
TunnelProtocolUsesMeekHTTPS(protocol) ||
Expand Down Expand Up @@ -239,6 +248,7 @@ func TunnelProtocolIsResourceIntensive(protocol string) bool {
func TunnelProtocolIsCompatibleWithFragmentor(protocol string) bool {
return protocol == TUNNEL_PROTOCOL_SSH ||
protocol == TUNNEL_PROTOCOL_OBFUSCATED_SSH ||
protocol == TUNNEL_PROTOCOL_TLS_OBFUSCATED_SSH ||
protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK ||
protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS ||
protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET ||
Expand All @@ -251,10 +261,15 @@ func TunnelProtocolRequiresTLS12SessionTickets(protocol string) bool {
return protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET
}

func TunnelProtocolRequiresTLS13Support(protocol string) bool {
return protocol == TUNNEL_PROTOCOL_TLS_OBFUSCATED_SSH
}

func TunnelProtocolSupportsPassthrough(protocol string) bool {
return protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS ||
protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET ||
protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK
protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK ||
protocol == TUNNEL_PROTOCOL_TLS_OBFUSCATED_SSH
}

func TunnelProtocolSupportsUpstreamProxy(protocol string) bool {
Expand All @@ -264,6 +279,7 @@ func TunnelProtocolSupportsUpstreamProxy(protocol string) bool {
func TunnelProtocolMayUseServerPacketManipulation(protocol string) bool {
return protocol == TUNNEL_PROTOCOL_SSH ||
protocol == TUNNEL_PROTOCOL_OBFUSCATED_SSH ||
protocol == TUNNEL_PROTOCOL_TLS_OBFUSCATED_SSH ||
protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK ||
protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS ||
protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET
Expand Down
36 changes: 36 additions & 0 deletions psiphon/common/protocol/serverEntry.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type ServerEntry struct {
Capabilities []string `json:"capabilities"`
Region string `json:"region"`
FrontingProviderID string `json:"frontingProviderID"`
TlsOSSHPort int `json:"tlsOSSHPort"`
MeekServerPort int `json:"meekServerPort"`
MeekCookieEncryptionPublicKey string `json:"meekCookieEncryptionPublicKey"`
MeekObfuscatedKey string `json:"meekObfuscatedKey"`
Expand Down Expand Up @@ -466,6 +467,8 @@ func GetTacticsCapability(protocol string) string {
func (serverEntry *ServerEntry) hasCapability(requiredCapability string) bool {
for _, capability := range serverEntry.Capabilities {

originalCapability := capability

capability = strings.ReplaceAll(capability, "-PASSTHROUGH-v2", "")
capability = strings.ReplaceAll(capability, "-PASSTHROUGH", "")

Expand All @@ -477,6 +480,30 @@ func (serverEntry *ServerEntry) hasCapability(requiredCapability string) bool {
if capability == requiredCapability {
return true
}

// Special case: some capabilities may additionally support TLS-OSSH.
if requiredCapability == GetCapability(TUNNEL_PROTOCOL_TLS_OBFUSCATED_SSH) && capabilitySupportsTLSOSSH(originalCapability) {
return true
}
}
return false
}

// capabilitySupportsTLSOSSH returns true if and only if the given capability
// supports TLS-OSSH in addition to its primary protocol.
func capabilitySupportsTLSOSSH(capability string) bool {

tlsCapabilities := []string{
GetCapability(TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS),
GetCapability(TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET),
}

for _, tlsCapability := range tlsCapabilities {
// The TLS capability is additionally supported by UNFRONTED-MEEK-HTTPS
// and UNFRONTED-MEEK-SESSION-TICKET capabilities with passthrough.
if capability == tlsCapability+"-PASSTHROUGH-v2" {
return true
}
}
return false
}
Expand Down Expand Up @@ -617,6 +644,15 @@ func (serverEntry *ServerEntry) GetDialPortNumber(tunnelProtocol string) (int, e

switch tunnelProtocol {

case TUNNEL_PROTOCOL_TLS_OBFUSCATED_SSH:
if serverEntry.TlsOSSHPort == 0 {
// Special case: a server which supports UNFRONTED-MEEK-HTTPS-OSSH
// or UNFRONTED-MEEK-SESSION-TICKET-OSSH also supports TLS-OSSH
// over the same port.
return serverEntry.MeekServerPort, nil
}
return serverEntry.TlsOSSHPort, nil

case TUNNEL_PROTOCOL_SSH:
return serverEntry.SshPort, nil

Expand Down
34 changes: 33 additions & 1 deletion psiphon/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ type Config struct {
NetworkLatencyMultiplier float64

// LimitTunnelProtocols indicates which protocols to use. Valid values
// include: "SSH", "OSSH", "UNFRONTED-MEEK-OSSH",
// include: "SSH", "OSSH", "TLS-OSSH", "UNFRONTED-MEEK-OSSH",
// "UNFRONTED-MEEK-HTTPS-OSSH", "UNFRONTED-MEEK-SESSION-TICKET-OSSH",
// "FRONTED-MEEK-OSSH", "FRONTED-MEEK-HTTP-OSSH", "QUIC-OSSH",
// "FRONTED-MEEK-QUIC-OSSH", "TAPDANCE-OSSH", and "CONJURE-OSSH".
Expand Down Expand Up @@ -848,6 +848,11 @@ type Config struct {
OSSHPrefixSplitMaxDelayMilliseconds *int
OSSHPrefixEnableFragmentor *bool

// TLSTunnelTrafficShapingProbability and associated fields are for testing.
TLSTunnelTrafficShapingProbability *float64
TLSTunnelMinTLSPadding *int
TLSTunnelMaxTLSPadding *int

// params is the active parameters.Parameters with defaults, config values,
// and, optionally, tactics applied.
//
Expand Down Expand Up @@ -2006,6 +2011,18 @@ func (config *Config) makeConfigParameters() map[string]interface{} {
applyParameters[parameters.OSSHPrefixEnableFragmentor] = *config.OSSHPrefixEnableFragmentor
}

if config.TLSTunnelTrafficShapingProbability != nil {
applyParameters[parameters.TLSTunnelTrafficShapingProbability] = *config.TLSTunnelTrafficShapingProbability
}

if config.TLSTunnelMinTLSPadding != nil {
applyParameters[parameters.TLSTunnelMinTLSPadding] = *config.TLSTunnelMinTLSPadding
}

if config.TLSTunnelMaxTLSPadding != nil {
applyParameters[parameters.TLSTunnelMaxTLSPadding] = *config.TLSTunnelMaxTLSPadding
}

// When adding new config dial parameters that may override tactics, also
// update setDialParametersHash.

Expand Down Expand Up @@ -2522,6 +2539,21 @@ func (config *Config) setDialParametersHash() {
binary.Write(hash, binary.LittleEndian, *config.OSSHPrefixEnableFragmentor)
}

if config.TLSTunnelTrafficShapingProbability != nil {
hash.Write([]byte("TLSTunnelTrafficShapingProbability"))
binary.Write(hash, binary.LittleEndian, int64(*config.TLSTunnelTrafficShapingProbability))
}

if config.TLSTunnelMinTLSPadding != nil {
hash.Write([]byte("TLSTunnelMinTLSPadding"))
binary.Write(hash, binary.LittleEndian, int64(*config.TLSTunnelMinTLSPadding))
}

if config.TLSTunnelMaxTLSPadding != nil {
hash.Write([]byte("TLSTunnelMaxTLSPadding"))
binary.Write(hash, binary.LittleEndian, int64(*config.TLSTunnelMaxTLSPadding))
}

config.dialParametersHash = hash.Sum(nil)
}

Expand Down
20 changes: 20 additions & 0 deletions psiphon/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,26 @@ func TestObfuscatedSSH(t *testing.T) {
})
}

func TestTLS(t *testing.T) {

t.Skipf("temporarily disabled")

controllerRun(t,
&controllerRunConfig{
expectNoServerEntries: false,
protocol: protocol.TUNNEL_PROTOCOL_TLS_OBFUSCATED_SSH,
clientIsLatestVersion: false,
disableUntunneledUpgrade: true,
disableEstablishing: false,
disableApi: false,
tunnelPoolSize: 1,
useUpstreamProxy: false,
disruptNetwork: false,
transformHostNames: false,
useFragmentor: false,
})
}

func TestUnfrontedMeek(t *testing.T) {
controllerRun(t,
&controllerRunConfig{
Expand Down
Loading

0 comments on commit 8d4b756

Please sign in to comment.