Selaa lähdekoodia

Add UseOnlyCustomTLSProfiles

- UseOnlyCustomTLSProfiles enables using
  only CustomTLSProfiles without setting
  LimitTLSProfiles.

- Enable setting new TLS tactics via
  config file for testing.

- Fix: additional custom TLS profile name
  validation.
Rod Hynes 6 vuotta sitten
vanhempi
sitoutus
be1f6bb7af

+ 2 - 0
psiphon/common/parameters/clientParameters.go

@@ -93,6 +93,7 @@ const (
 	LimitTunnelProtocols                             = "LimitTunnelProtocols"
 	LimitTLSProfilesProbability                      = "LimitTLSProfilesProbability"
 	LimitTLSProfiles                                 = "LimitTLSProfiles"
+	UseOnlyCustomTLSProfiles                         = "UseOnlyCustomTLSProfiles"
 	CustomTLSProfiles                                = "CustomTLSProfiles"
 	SelectRandomizedTLSProfileProbability            = "SelectRandomizedTLSProfileProbability"
 	NoDefaultTLSSessionIDProbability                 = "NoDefaultTLSSessionIDProbability"
@@ -278,6 +279,7 @@ var defaultClientParameters = map[string]struct {
 
 	LimitTLSProfilesProbability:           {value: 1.0, minimum: 0.0},
 	LimitTLSProfiles:                      {value: protocol.TLSProfiles{}},
+	UseOnlyCustomTLSProfiles:              {value: false},
 	CustomTLSProfiles:                     {value: protocol.CustomTLSProfiles{}},
 	SelectRandomizedTLSProfileProbability: {value: 0.25, minimum: 0.0},
 	NoDefaultTLSSessionIDProbability:      {value: 0.5, minimum: 0.0},

+ 2 - 2
psiphon/common/parameters/clientParameters_test.go

@@ -277,8 +277,8 @@ func TestCustomTLSProfiles(t *testing.T) {
 	}
 
 	customTLSProfiles := protocol.CustomTLSProfiles{
-		&protocol.CustomTLSProfile{Name: "Profile1"},
-		&protocol.CustomTLSProfile{Name: "Profile2"},
+		&protocol.CustomTLSProfile{Name: "Profile1", UTLSSpec: &protocol.UTLSSpec{}},
+		&protocol.CustomTLSProfile{Name: "Profile2", UTLSSpec: &protocol.UTLSSpec{}},
 	}
 
 	applyParameters := map[string]interface{}{

+ 12 - 4
psiphon/common/protocol/customTLSProfiles.go

@@ -37,15 +37,23 @@ type CustomTLSProfile struct {
 
 type CustomTLSProfiles []*CustomTLSProfile
 
-// Validate checks that the profiles in CustomTLSProfiles have no name conflicts.
+// Validate checks that the profiles in CustomTLSProfiles are initialized and
+// have no name conflicts.
 func (profiles CustomTLSProfiles) Validate() error {
 	names := make(map[string]bool)
 	for _, p := range profiles {
-		if p.Name == "" || common.Contains(SupportedTLSProfiles, p.Name) {
-			return common.ContextError(fmt.Errorf("invalid custom TLS profile: %s", p.Name))
+		if p.Name == "" {
+			return common.ContextError(fmt.Errorf("custom TLS profile missing name: %s", p.Name))
+		}
+		if p.UTLSSpec == nil {
+			return common.ContextError(fmt.Errorf("custom TLS profile missing utls spec: %s", p.Name))
+		}
+		if common.Contains(SupportedTLSProfiles, p.Name) ||
+			common.Contains(legacyTLSProfiles, p.Name) {
+			return common.ContextError(fmt.Errorf("invalid custom TLS profile name: %s", p.Name))
 		}
 		if _, ok := names[p.Name]; ok {
-			return common.ContextError(fmt.Errorf("duplicate custom TLS profile: %s", p.Name))
+			return common.ContextError(fmt.Errorf("duplicate custom TLS profile name: %s", p.Name))
 		}
 		names[p.Name] = true
 	}

+ 1 - 1
psiphon/common/protocol/customTLSProfiles_test.go

@@ -132,7 +132,7 @@ func TestCustomTLSProfiles(t *testing.T) {
             {"Name": "CertCompressionAlgs", "Data": {"Methods": [2]}},
             {"Name": "ChannelID"},
             {"Name": "RecordSizeLimit", "Data": {"Limit": 9999}}],
-          "GetSessionID" : "SHA-256"
+          "GetSessionID": "SHA-256"
         }
       }
     ]`)

+ 9 - 9
psiphon/common/protocol/protocol.go

@@ -237,6 +237,15 @@ var SupportedTLSProfiles = TLSProfiles{
 	TLS_PROFILE_RANDOMIZED,
 }
 
+var legacyTLSProfiles = TLSProfiles{
+	"iOS-Safari-11.3.1",
+	"Android-6.0",
+	"Android-5.1",
+	"Chrome-57",
+	"Randomized",
+	"TLS-1.3-Randomized",
+}
+
 func TLSProfileIsRandomized(tlsProfile string) bool {
 	return tlsProfile == TLS_PROFILE_RANDOMIZED
 }
@@ -245,15 +254,6 @@ type TLSProfiles []string
 
 func (profiles TLSProfiles) Validate() error {
 
-	legacyTLSProfiles := TLSProfiles{
-		"iOS-Safari-11.3.1",
-		"Android-6.0",
-		"Android-5.1",
-		"Chrome-57",
-		"Randomized",
-		"TLS-1.3-Randomized",
-	}
-
 	for _, p := range profiles {
 		if !common.Contains(SupportedTLSProfiles, p) && !common.Contains(legacyTLSProfiles, p) {
 			return common.ContextError(fmt.Errorf("invalid TLS profile: %s", p))

+ 23 - 0
psiphon/config.go

@@ -548,6 +548,13 @@ type Config struct {
 	ReplayLaterRoundMoveToFrontProbability *float64
 	ReplayRetainFailedProbability          *float64
 
+	// UseOnlyCustomTLSProfiles and other TLS configuration fields are for
+	// testing purposes.
+	UseOnlyCustomTLSProfiles              *bool
+	CustomTLSProfiles                     protocol.CustomTLSProfiles
+	SelectRandomizedTLSProfileProbability *float64
+	NoDefaultTLSSessionIDProbability      *float64
+
 	// clientParameters is the active ClientParameters with defaults, config
 	// values, and, optionally, tactics applied.
 	//
@@ -1076,6 +1083,22 @@ func (config *Config) makeConfigParameters() map[string]interface{} {
 		applyParameters[parameters.ReplayRetainFailedProbability] = *config.ReplayRetainFailedProbability
 	}
 
+	if config.UseOnlyCustomTLSProfiles != nil {
+		applyParameters[parameters.UseOnlyCustomTLSProfiles] = *config.UseOnlyCustomTLSProfiles
+	}
+
+	if config.CustomTLSProfiles != nil {
+		applyParameters[parameters.CustomTLSProfiles] = config.CustomTLSProfiles
+	}
+
+	if config.SelectRandomizedTLSProfileProbability != nil {
+		applyParameters[parameters.SelectRandomizedTLSProfileProbability] = *config.SelectRandomizedTLSProfileProbability
+	}
+
+	if config.NoDefaultTLSSessionIDProbability != nil {
+		applyParameters[parameters.NoDefaultTLSSessionIDProbability] = *config.NoDefaultTLSSessionIDProbability
+	}
+
 	return applyParameters
 }
 

+ 20 - 9
psiphon/tlsDialer.go

@@ -162,23 +162,34 @@ func SelectTLSProfile(
 	// Note that LimitTLSProfiles is not applied to CustomTLSProfiles; the
 	// presence of a candidate in CustomTLSProfiles is treated as explicit
 	// enabling.
+	//
+	// UseOnlyCustomTLSProfiles may be used to disable all stock TLS profiles and
+	// use only CustomTLSProfiles; UseOnlyCustomTLSProfiles is ignored if
+	// CustomTLSProfiles is empty.
 
 	limitTLSProfiles := p.TLSProfiles(parameters.LimitTLSProfiles)
 
 	randomizedTLSProfiles := make([]string, 0)
 	parrotTLSProfiles := p.CustomTLSProfileNames()
 
-	for _, tlsProfile := range protocol.SupportedTLSProfiles {
+	useOnlyCustomTLSProfiles := p.Bool(parameters.UseOnlyCustomTLSProfiles)
+	if useOnlyCustomTLSProfiles && len(parrotTLSProfiles) == 0 {
+		useOnlyCustomTLSProfiles = false
+	}
 
-		if len(limitTLSProfiles) > 0 &&
-			!common.Contains(limitTLSProfiles, tlsProfile) {
-			continue
-		}
+	if !useOnlyCustomTLSProfiles {
+		for _, tlsProfile := range protocol.SupportedTLSProfiles {
 
-		if protocol.TLSProfileIsRandomized(tlsProfile) {
-			randomizedTLSProfiles = append(randomizedTLSProfiles, tlsProfile)
-		} else {
-			parrotTLSProfiles = append(parrotTLSProfiles, tlsProfile)
+			if len(limitTLSProfiles) > 0 &&
+				!common.Contains(limitTLSProfiles, tlsProfile) {
+				continue
+			}
+
+			if protocol.TLSProfileIsRandomized(tlsProfile) {
+				randomizedTLSProfiles = append(randomizedTLSProfiles, tlsProfile)
+			} else {
+				parrotTLSProfiles = append(parrotTLSProfiles, tlsProfile)
+			}
 		}
 	}
 

+ 23 - 4
psiphon/tlsDialer_test.go

@@ -120,7 +120,7 @@ func testTLSDialerCompatibility(t *testing.T, address string) {
 		return d.DialContext(ctx, network, address)
 	}
 
-	clientParameters := makeCustomTLSProfilesClientParameters(t)
+	clientParameters := makeCustomTLSProfilesClientParameters(t, false)
 
 	profiles := append([]string(nil), protocol.SupportedTLSProfiles...)
 	profiles = append(profiles, clientParameters.Get().CustomTLSProfileNames()...)
@@ -170,7 +170,7 @@ func testTLSDialerCompatibility(t *testing.T, address string) {
 
 func TestSelectTLSProfile(t *testing.T) {
 
-	clientParameters := makeCustomTLSProfilesClientParameters(t)
+	clientParameters := makeCustomTLSProfilesClientParameters(t, false)
 
 	profiles := append([]string(nil), protocol.SupportedTLSProfiles...)
 	profiles = append(profiles, clientParameters.Get().CustomTLSProfileNames()...)
@@ -192,6 +192,12 @@ func TestSelectTLSProfile(t *testing.T) {
 		}
 	}
 
+	// Only expected profiles should be selected
+
+	if len(selected) != len(profiles) {
+		t.Errorf("unexpected TLS profile selected")
+	}
+
 	// Randomized TLS profiles should be selected with expected probability.
 
 	numRandomized := 0
@@ -246,6 +252,18 @@ func TestSelectTLSProfile(t *testing.T) {
 			t.Errorf("Unexpected ClientHelloSpec for TLS profile %s", profile)
 		}
 	}
+
+	// Only custom TLS profiles should be selected
+
+	clientParameters = makeCustomTLSProfilesClientParameters(t, true)
+	customTLSProfileNames := clientParameters.Get().CustomTLSProfileNames()
+
+	for i := 0; i < numSelections; i++ {
+		profile := SelectTLSProfile(clientParameters.Get())
+		if !common.Contains(customTLSProfileNames, profile) {
+			t.Errorf("unexpected non-custom TLS profile selected")
+		}
+	}
 }
 
 func BenchmarkRandomizedGetClientHelloVersion(b *testing.B) {
@@ -257,7 +275,7 @@ func BenchmarkRandomizedGetClientHelloVersion(b *testing.B) {
 }
 
 func makeCustomTLSProfilesClientParameters(
-	t *testing.T) *parameters.ClientParameters {
+	t *testing.T, useOnlyCustomTLSProfiles bool) *parameters.ClientParameters {
 
 	clientParameters, err := parameters.NewClientParameters(nil)
 	if err != nil {
@@ -288,7 +306,7 @@ func makeCustomTLSProfilesClientParameters(
             {"Name": "SupportedCurves", "Data": {"Curves": [2570, 29, 23, 24]}},
             {"Name": "BoringPadding"},
             {"Name": "GREASE"}],
-          "GetSessionID" : "SHA-256"
+          "GetSessionID": "SHA-256"
         }
       }
     ]`)
@@ -302,6 +320,7 @@ func makeCustomTLSProfilesClientParameters(
 
 	applyParameters := make(map[string]interface{})
 
+	applyParameters[parameters.UseOnlyCustomTLSProfiles] = useOnlyCustomTLSProfiles
 	applyParameters[parameters.CustomTLSProfiles] = customTLSProfiles
 
 	_, err = clientParameters.Set("", false, applyParameters)