Browse Source

Added obfuscated PSK to QUIC

Amir Khan 1 year ago
parent
commit
d6fbdf15d8

+ 1 - 1
go.mod

@@ -37,7 +37,7 @@ require (
 	github.com/Psiphon-Labs/bolt v0.0.0-20200624191537-23cedaef7ad7
 	github.com/Psiphon-Labs/bolt v0.0.0-20200624191537-23cedaef7ad7
 	github.com/Psiphon-Labs/consistent v0.0.0-20240322131436-20aaa4e05737
 	github.com/Psiphon-Labs/consistent v0.0.0-20240322131436-20aaa4e05737
 	github.com/Psiphon-Labs/goptlib v0.0.0-20200406165125-c0e32a7a3464
 	github.com/Psiphon-Labs/goptlib v0.0.0-20200406165125-c0e32a7a3464
-	github.com/Psiphon-Labs/psiphon-tls v0.0.0-20240716162946-891a0d5db073
+	github.com/Psiphon-Labs/psiphon-tls v0.0.0-20240812172553-e7a4dbd0bf2b
 	github.com/Psiphon-Labs/quic-go v0.0.0-20240424181006-45545f5e1536
 	github.com/Psiphon-Labs/quic-go v0.0.0-20240424181006-45545f5e1536
 	github.com/armon/go-proxyproto v0.0.0-20180202201750-5b7edb60ff5f
 	github.com/armon/go-proxyproto v0.0.0-20180202201750-5b7edb60ff5f
 	github.com/bifurcation/mint v0.0.0-20180306135233-198357931e61
 	github.com/bifurcation/mint v0.0.0-20180306135233-198357931e61

+ 2 - 2
go.sum

@@ -18,8 +18,8 @@ github.com/Psiphon-Labs/consistent v0.0.0-20240322131436-20aaa4e05737 h1:QTMy7Uc
 github.com/Psiphon-Labs/consistent v0.0.0-20240322131436-20aaa4e05737/go.mod h1:Enj/Gszv2zCbuRbHbabmNvfO9EM+5kmaGj8CyjwNPlY=
 github.com/Psiphon-Labs/consistent v0.0.0-20240322131436-20aaa4e05737/go.mod h1:Enj/Gszv2zCbuRbHbabmNvfO9EM+5kmaGj8CyjwNPlY=
 github.com/Psiphon-Labs/goptlib v0.0.0-20200406165125-c0e32a7a3464 h1:VmnMMMheFXwLV0noxYhbJbLmkV4iaVW3xNnj6xcCNHo=
 github.com/Psiphon-Labs/goptlib v0.0.0-20200406165125-c0e32a7a3464 h1:VmnMMMheFXwLV0noxYhbJbLmkV4iaVW3xNnj6xcCNHo=
 github.com/Psiphon-Labs/goptlib v0.0.0-20200406165125-c0e32a7a3464/go.mod h1:Pe5BqN2DdIdChorAXl6bDaQd/wghpCleJfid2NoSli0=
 github.com/Psiphon-Labs/goptlib v0.0.0-20200406165125-c0e32a7a3464/go.mod h1:Pe5BqN2DdIdChorAXl6bDaQd/wghpCleJfid2NoSli0=
-github.com/Psiphon-Labs/psiphon-tls v0.0.0-20240716162946-891a0d5db073 h1:qOr7JasrUWOR7hIgNKeDqc/0qCNNeN9I1GO9AI2ZURk=
-github.com/Psiphon-Labs/psiphon-tls v0.0.0-20240716162946-891a0d5db073/go.mod h1:AaKKoshr8RI1LZTheeNDtNuZ39qNVPWVK4uir2c2XIs=
+github.com/Psiphon-Labs/psiphon-tls v0.0.0-20240812172553-e7a4dbd0bf2b h1:OJBiXScGxzcnK+DYwWnUDWSzgZXs7VEYGiXToZHdjzk=
+github.com/Psiphon-Labs/psiphon-tls v0.0.0-20240812172553-e7a4dbd0bf2b/go.mod h1:AaKKoshr8RI1LZTheeNDtNuZ39qNVPWVK4uir2c2XIs=
 github.com/Psiphon-Labs/quic-go v0.0.0-20240424181006-45545f5e1536 h1:pM5ex1QufkHV8lDR6Tc1Crk1bW5lYZjrFIJGZNBWE9k=
 github.com/Psiphon-Labs/quic-go v0.0.0-20240424181006-45545f5e1536 h1:pM5ex1QufkHV8lDR6Tc1Crk1bW5lYZjrFIJGZNBWE9k=
 github.com/Psiphon-Labs/quic-go v0.0.0-20240424181006-45545f5e1536/go.mod h1:2MTiPsgoOqWs3Bo6Xr3ElMBX6zzfjd3YkDFpQJLwHdQ=
 github.com/Psiphon-Labs/quic-go v0.0.0-20240424181006-45545f5e1536/go.mod h1:2MTiPsgoOqWs3Bo6Xr3ElMBX6zzfjd3YkDFpQJLwHdQ=
 github.com/Psiphon-Labs/utls v1.1.1-0.20240807185429-185b54f73f17 h1:7XPQc6Izr5cMOWdggqFxXIyNQmzabPre5kwXHhV3pl4=
 github.com/Psiphon-Labs/utls v1.1.1-0.20240807185429-185b54f73f17 h1:7XPQc6Izr5cMOWdggqFxXIyNQmzabPre5kwXHhV3pl4=

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

@@ -113,6 +113,7 @@ const (
 	LimitQUICVersions                                  = "LimitQUICVersions"
 	LimitQUICVersions                                  = "LimitQUICVersions"
 	DisableFrontingProviderQUICVersions                = "DisableFrontingProviderQUICVersions"
 	DisableFrontingProviderQUICVersions                = "DisableFrontingProviderQUICVersions"
 	QUICDialEarlyProbability                           = "QUICDialEarlyProbability"
 	QUICDialEarlyProbability                           = "QUICDialEarlyProbability"
+	QUICDisableObfuscatedPSK                           = "QUICDisableObfuscatedPSK"
 	QUICDisableClientPathMTUDiscoveryProbability       = "QUICDisableClientPathMTUDiscoveryProbability"
 	QUICDisableClientPathMTUDiscoveryProbability       = "QUICDisableClientPathMTUDiscoveryProbability"
 	FragmentorProbability                              = "FragmentorProbability"
 	FragmentorProbability                              = "FragmentorProbability"
 	FragmentorLimitProtocols                           = "FragmentorLimitProtocols"
 	FragmentorLimitProtocols                           = "FragmentorLimitProtocols"
@@ -520,6 +521,7 @@ var defaultParameters = map[string]struct {
 	LimitQUICVersions:                            {value: protocol.QUICVersions{}},
 	LimitQUICVersions:                            {value: protocol.QUICVersions{}},
 	DisableFrontingProviderQUICVersions:          {value: protocol.LabeledQUICVersions{}},
 	DisableFrontingProviderQUICVersions:          {value: protocol.LabeledQUICVersions{}},
 	QUICDialEarlyProbability:                     {value: 1.0, minimum: 0.0},
 	QUICDialEarlyProbability:                     {value: 1.0, minimum: 0.0},
+	QUICDisableObfuscatedPSK:                     {value: false},
 	QUICDisableClientPathMTUDiscoveryProbability: {value: 0.0, minimum: 0.0},
 	QUICDisableClientPathMTUDiscoveryProbability: {value: 0.0, minimum: 0.0},
 
 
 	FragmentorProbability:              {value: 0.5, minimum: 0.0},
 	FragmentorProbability:              {value: 0.5, minimum: 0.0},

+ 1 - 0
psiphon/common/quic/obfuscator_test.go

@@ -133,6 +133,7 @@ func runNonceTransformer(t *testing.T, quicVersion string) {
 			},
 			},
 			false,
 			false,
 			false,
 			false,
+			true, // Disable obfuscated PSK
 			nil,
 			nil,
 		)
 		)
 
 

+ 94 - 12
psiphon/common/quic/quic.go

@@ -43,6 +43,9 @@ package quic
 
 
 import (
 import (
 	"context"
 	"context"
+	"crypto/rand"
+	"encoding/hex"
+	std_errors "errors"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
 	"net"
 	"net"
@@ -233,6 +236,8 @@ func Listen(
 
 
 	var quicListener quicListener
 	var quicListener quicListener
 
 
+	enableGQUIC = true
+
 	if !enableGQUIC {
 	if !enableGQUIC {
 
 
 		// When gQUIC is disabled, skip the muxListener entirely. This allows
 		// When gQUIC is disabled, skip the muxListener entirely. This allows
@@ -243,8 +248,13 @@ func Listen(
 		// Skipping muxListener also avoids the additional overhead of
 		// Skipping muxListener also avoids the additional overhead of
 		// pumping read packets though mux channels.
 		// pumping read packets though mux channels.
 
 
-		tlsConfig, ietfQUICConfig := makeServerIETFConfig(
-			obfuscatedPacketConn, verifyClientHelloRandom, tlsCertificate)
+		tlsConfig, ietfQUICConfig, err := makeServerIETFConfig(
+			obfuscatedPacketConn, verifyClientHelloRandom, tlsCertificate, obfuscationKey)
+
+		if err != nil {
+			obfuscatedPacketConn.Close()
+			return nil, errors.Trace(err)
+		}
 
 
 		tr := newIETFTransport(obfuscatedPacketConn)
 		tr := newIETFTransport(obfuscatedPacketConn)
 
 
@@ -265,7 +275,7 @@ func Listen(
 		// return and caller calls Accept.
 		// return and caller calls Accept.
 
 
 		muxListener, err := newMuxListener(
 		muxListener, err := newMuxListener(
-			logger, verifyClientHelloRandom, obfuscatedPacketConn, tlsCertificate)
+			logger, verifyClientHelloRandom, obfuscatedPacketConn, tlsCertificate, obfuscationKey)
 		if err != nil {
 		if err != nil {
 			obfuscatedPacketConn.Close()
 			obfuscatedPacketConn.Close()
 			return nil, errors.Trace(err)
 			return nil, errors.Trace(err)
@@ -284,13 +294,37 @@ func Listen(
 func makeServerIETFConfig(
 func makeServerIETFConfig(
 	conn *ObfuscatedPacketConn,
 	conn *ObfuscatedPacketConn,
 	verifyClientHelloRandom func(net.Addr, []byte) bool,
 	verifyClientHelloRandom func(net.Addr, []byte) bool,
-	tlsCertificate tls.Certificate) (*tls.Config, *ietf_quic.Config) {
+	tlsCertificate tls.Certificate,
+	sharedSecret string) (*tls.Config, *ietf_quic.Config, error) {
 
 
 	tlsConfig := &tls.Config{
 	tlsConfig := &tls.Config{
 		Certificates: []tls.Certificate{tlsCertificate},
 		Certificates: []tls.Certificate{tlsCertificate},
 		NextProtos:   []string{getALPN(ietfQUIC1VersionNumber)},
 		NextProtos:   []string{getALPN(ietfQUIC1VersionNumber)},
 	}
 	}
 
 
+	if sharedSecret != "" {
+		var obfuscatedSessionTicketKey [32]byte
+		key, err := hex.DecodeString(sharedSecret)
+		if err == nil && len(key) != 32 {
+			err = std_errors.New("invalid obfuscated session key length")
+		}
+		if err != nil {
+			return nil, nil, errors.TraceNew("invalid obfuscated session key length")
+		}
+		copy(obfuscatedSessionTicketKey[:], key)
+
+		var standardSessionTicketKey [32]byte
+		_, err = rand.Read(standardSessionTicketKey[:])
+		if err != nil {
+			panic(err)
+		}
+
+		tlsConfig.SetSessionTicketKeys([][32]byte{
+			standardSessionTicketKey,
+			obfuscatedSessionTicketKey,
+		})
+	}
+
 	ietfQUICConfig := &ietf_quic.Config{
 	ietfQUICConfig := &ietf_quic.Config{
 		Allow0RTT:             true,
 		Allow0RTT:             true,
 		HandshakeIdleTimeout:  SERVER_HANDSHAKE_TIMEOUT,
 		HandshakeIdleTimeout:  SERVER_HANDSHAKE_TIMEOUT,
@@ -304,7 +338,7 @@ func makeServerIETFConfig(
 		ServerMaxPacketSizeAdjustment: conn.serverMaxPacketSizeAdjustment,
 		ServerMaxPacketSizeAdjustment: conn.serverMaxPacketSizeAdjustment,
 	}
 	}
 
 
-	return tlsConfig, ietfQUICConfig
+	return tlsConfig, ietfQUICConfig, nil
 }
 }
 
 
 func newIETFTransport(conn net.PacketConn) *ietf_quic.Transport {
 func newIETFTransport(conn net.PacketConn) *ietf_quic.Transport {
@@ -372,6 +406,7 @@ func Dial(
 	obfuscationNonceTransformerParameters *transforms.ObfuscatorSeedTransformerParameters,
 	obfuscationNonceTransformerParameters *transforms.ObfuscatorSeedTransformerParameters,
 	disablePathMTUDiscovery bool,
 	disablePathMTUDiscovery bool,
 	dialEarly bool,
 	dialEarly bool,
+	disableObfuscatedPSK bool,
 	tlsClientSessionCache *common.TLSClientSessionCacheWrapper) (net.Conn, error) {
 	tlsClientSessionCache *common.TLSClientSessionCacheWrapper) (net.Conn, error) {
 
 
 	if quicVersion == "" {
 	if quicVersion == "" {
@@ -480,6 +515,11 @@ func Dial(
 		}
 		}
 	}
 	}
 
 
+	obfuscatedSessionTicketKey := obfuscationKey
+	if disableObfuscatedPSK {
+		obfuscatedSessionTicketKey = ""
+	}
+
 	connection, err := dialQUIC(
 	connection, err := dialQUIC(
 		ctx,
 		ctx,
 		packetConn,
 		packetConn,
@@ -492,6 +532,7 @@ func Dial(
 		maxPacketSizeAdjustment,
 		maxPacketSizeAdjustment,
 		disablePathMTUDiscovery,
 		disablePathMTUDiscovery,
 		dialEarly,
 		dialEarly,
+		obfuscatedSessionTicketKey,
 		tlsClientSessionCache)
 		tlsClientSessionCache)
 
 
 	if err != nil {
 	if err != nil {
@@ -877,6 +918,7 @@ func (t *QUICTransporter) dialQUIC() (retConnection quicConnection, retErr error
 		0,
 		0,
 		t.disablePathMTUDiscovery,
 		t.disablePathMTUDiscovery,
 		t.dialEarly,
 		t.dialEarly,
+		"", // PSK ticket key is not used for fronted connections.
 		t.tlsClientSessionCache)
 		t.tlsClientSessionCache)
 
 
 	if err != nil {
 	if err != nil {
@@ -1038,8 +1080,13 @@ func dialQUIC(
 	clientMaxPacketSizeAdjustment int,
 	clientMaxPacketSizeAdjustment int,
 	disablePathMTUDiscovery bool,
 	disablePathMTUDiscovery bool,
 	dialEarly bool,
 	dialEarly bool,
+	obfuscatedSessionTicketKey string,
 	tlsClientSessionCache *common.TLSClientSessionCacheWrapper) (quicConnection, error) {
 	tlsClientSessionCache *common.TLSClientSessionCacheWrapper) (quicConnection, error) {
 
 
+	if tlsClientSessionCache == nil {
+		return nil, errors.TraceNew("missing TLS client session cache")
+	}
+
 	if isIETFVersionNumber(versionNumber) {
 	if isIETFVersionNumber(versionNumber) {
 		quicConfig := &ietf_quic.Config{
 		quicConfig := &ietf_quic.Config{
 			HandshakeIdleTimeout: time.Duration(1<<63 - 1),
 			HandshakeIdleTimeout: time.Duration(1<<63 - 1),
@@ -1068,10 +1115,41 @@ func dialQUIC(
 
 
 		var dialConnection ietf_quic.Connection
 		var dialConnection ietf_quic.Connection
 		tlsConfig := &tls.Config{
 		tlsConfig := &tls.Config{
-			InsecureSkipVerify: true,
-			NextProtos:         []string{getALPN(versionNumber)},
-			ServerName:         sni,
-			ClientSessionCache: tlsClientSessionCache,
+			InsecureSkipVerify:     true,
+			InsecureSkipTimeVerify: true,
+			NextProtos:             []string{getALPN(versionNumber)},
+			ServerName:             sni,
+			ClientSessionCache:     tlsClientSessionCache,
+		}
+
+		// Creating a session state and storing it in the TLS cache to be used
+		// for PSK (Pre-Shared Key) resumption.
+		if obfuscatedSessionTicketKey != "" {
+			var sharedSecret [32]byte
+			key, err := hex.DecodeString(obfuscatedSessionTicketKey)
+			if err == nil && len(key) != 32 {
+				err = std_errors.New("invalid obfuscated session key length")
+			}
+			if err != nil {
+				return nil, errors.Trace(err)
+			}
+			copy(sharedSecret[:], key)
+
+			obfuscatedSessionState, err := tls.NewObfuscatedClientSessionState(
+				sharedSecret, true, false)
+			if err != nil {
+				return nil, errors.Trace(err)
+			}
+			ss := tls.MakeClientSessionState(
+				obfuscatedSessionState.SessionTicket,
+				obfuscatedSessionState.Vers,
+				obfuscatedSessionState.CipherSuite,
+				obfuscatedSessionState.MasterSecret,
+				obfuscatedSessionState.CreatedAt,
+				obfuscatedSessionState.AgeAdd,
+				obfuscatedSessionState.UseBy,
+			)
+			tlsClientSessionCache.Put("", ss)
 		}
 		}
 
 
 		// Heuristic to determine if TLS dial is resuming a session.
 		// Heuristic to determine if TLS dial is resuming a session.
@@ -1253,7 +1331,8 @@ func newMuxListener(
 	logger common.Logger,
 	logger common.Logger,
 	verifyClientHelloRandom func(net.Addr, []byte) bool,
 	verifyClientHelloRandom func(net.Addr, []byte) bool,
 	conn *ObfuscatedPacketConn,
 	conn *ObfuscatedPacketConn,
-	tlsCertificate tls.Certificate) (*muxListener, error) {
+	tlsCertificate tls.Certificate,
+	sharedSecret string) (*muxListener, error) {
 
 
 	listener := &muxListener{
 	listener := &muxListener{
 		logger:              logger,
 		logger:              logger,
@@ -1271,8 +1350,11 @@ func newMuxListener(
 
 
 	listener.ietfQUICConn = newMuxPacketConn(conn.LocalAddr(), listener)
 	listener.ietfQUICConn = newMuxPacketConn(conn.LocalAddr(), listener)
 
 
-	tlsConfig, ietfQUICConfig := makeServerIETFConfig(
-		conn, verifyClientHelloRandom, tlsCertificate)
+	tlsConfig, ietfQUICConfig, err := makeServerIETFConfig(
+		conn, verifyClientHelloRandom, tlsCertificate, sharedSecret)
+	if err != nil {
+		return nil, errors.Trace(err)
+	}
 
 
 	tr := newIETFTransport(listener.ietfQUICConn)
 	tr := newIETFTransport(listener.ietfQUICConn)
 	il, err := tr.Listen(tlsConfig, ietfQUICConfig)
 	il, err := tr.Listen(tlsConfig, ietfQUICConfig)

+ 27 - 16
psiphon/common/quic/quic_test.go

@@ -33,6 +33,8 @@ import (
 	"testing"
 	"testing"
 	"time"
 	"time"
 
 
+	tls "github.com/Psiphon-Labs/psiphon-tls"
+
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
@@ -41,21 +43,26 @@ import (
 
 
 func TestQUIC(t *testing.T) {
 func TestQUIC(t *testing.T) {
 	for quicVersion := range supportedVersionNumbers {
 	for quicVersion := range supportedVersionNumbers {
-		t.Run(fmt.Sprintf("%s", quicVersion), func(t *testing.T) {
-			if isGQUIC(quicVersion) && !GQUICEnabled() {
-				t.Skipf("gQUIC is not enabled")
-			}
-			runQUIC(t, quicVersion, GQUICEnabled(), false)
-		})
-		if isIETF(quicVersion) {
-			t.Run(fmt.Sprintf("%s (invoke anti-probing)", quicVersion), func(t *testing.T) {
-				runQUIC(t, quicVersion, GQUICEnabled(), true)
-			})
-		}
-		if isIETF(quicVersion) {
-			t.Run(fmt.Sprintf("%s (disable gQUIC)", quicVersion), func(t *testing.T) {
-				runQUIC(t, quicVersion, false, false)
+
+		for _, disableObfuscatedPSK := range []bool{true, false} {
+			t.Run(fmt.Sprintf("%s|PSK disabled=%v", quicVersion, disableObfuscatedPSK), func(t *testing.T) {
+				if isGQUIC(quicVersion) && !GQUICEnabled() {
+					t.Skipf("gQUIC is not enabled")
+				}
+				runQUIC(t, quicVersion, GQUICEnabled(), disableObfuscatedPSK, false)
 			})
 			})
+			if isIETF(quicVersion) {
+				t.Run(fmt.Sprintf("%s (invoke anti-probing)|PSK disabled=%v", quicVersion, disableObfuscatedPSK), func(t *testing.T) {
+					fmt.Printf("quic version: %s\n", quicVersion)
+					runQUIC(t, quicVersion, GQUICEnabled(), true, disableObfuscatedPSK)
+				})
+			}
+			if isIETF(quicVersion) {
+				t.Run(fmt.Sprintf("%s (disable gQUIC)|PSK disabled=%v", quicVersion, disableObfuscatedPSK), func(t *testing.T) {
+					fmt.Printf("quic version: %s\n", quicVersion)
+					runQUIC(t, quicVersion, false, false, disableObfuscatedPSK)
+				})
+			}
 		}
 		}
 	}
 	}
 }
 }
@@ -64,7 +71,8 @@ func runQUIC(
 	t *testing.T,
 	t *testing.T,
 	quicVersion string,
 	quicVersion string,
 	enableGQUIC bool,
 	enableGQUIC bool,
-	invokeAntiProbing bool) {
+	invokeAntiProbing bool,
+	disableObfuscatedPSK bool) {
 
 
 	initGoroutines := getGoroutines()
 	initGoroutines := getGoroutines()
 
 
@@ -193,6 +201,8 @@ func runQUIC(
 				return errors.Trace(err)
 				return errors.Trace(err)
 			}
 			}
 
 
+			clientSessionCache := common.WrapClientSessionCache(tls.NewLRUClientSessionCache(0), "localhost")
+
 			conn, err := Dial(
 			conn, err := Dial(
 				ctx,
 				ctx,
 				packetConn,
 				packetConn,
@@ -205,7 +215,8 @@ func runQUIC(
 				nil,
 				nil,
 				disablePathMTUDiscovery,
 				disablePathMTUDiscovery,
 				true,
 				true,
-				nil)
+				disableObfuscatedPSK,
+				clientSessionCache)
 
 
 			if invokeAntiProbing {
 			if invokeAntiProbing {
 
 

+ 12 - 0
psiphon/config.go

@@ -939,6 +939,9 @@ type Config struct {
 	// QUICDialEarlyProbability is for testing purposes.
 	// QUICDialEarlyProbability is for testing purposes.
 	QUICDialEarlyProbability *float64
 	QUICDialEarlyProbability *float64
 
 
+	// QUICDisableObfuscatedPSK is for testing purposes.
+	QUICDisableObfuscatedPSK *bool
+
 	// QUICDisablePathMTUDiscoveryProbability is for testing purposes.
 	// QUICDisablePathMTUDiscoveryProbability is for testing purposes.
 	QUICDisablePathMTUDiscoveryProbability *float64
 	QUICDisablePathMTUDiscoveryProbability *float64
 
 
@@ -2214,6 +2217,10 @@ func (config *Config) makeConfigParameters() map[string]interface{} {
 		applyParameters[parameters.QUICDialEarlyProbability] = *config.QUICDialEarlyProbability
 		applyParameters[parameters.QUICDialEarlyProbability] = *config.QUICDialEarlyProbability
 	}
 	}
 
 
+	if config.QUICDisableObfuscatedPSK != nil {
+		applyParameters[parameters.QUICDisableObfuscatedPSK] = *config.QUICDisableObfuscatedPSK
+	}
+
 	if config.QUICDisablePathMTUDiscoveryProbability != nil {
 	if config.QUICDisablePathMTUDiscoveryProbability != nil {
 		applyParameters[parameters.QUICDisableClientPathMTUDiscoveryProbability] = *config.QUICDisablePathMTUDiscoveryProbability
 		applyParameters[parameters.QUICDisableClientPathMTUDiscoveryProbability] = *config.QUICDisablePathMTUDiscoveryProbability
 	}
 	}
@@ -2971,6 +2978,11 @@ func (config *Config) setDialParametersHash() {
 		binary.Write(hash, binary.LittleEndian, *config.QUICDialEarlyProbability)
 		binary.Write(hash, binary.LittleEndian, *config.QUICDialEarlyProbability)
 	}
 	}
 
 
+	if config.QUICDisableObfuscatedPSK != nil {
+		hash.Write([]byte("QUICDisableObfuscatedPSK"))
+		binary.Write(hash, binary.LittleEndian, *config.QUICDisableObfuscatedPSK)
+	}
+
 	if config.QUICDisablePathMTUDiscoveryProbability != nil {
 	if config.QUICDisablePathMTUDiscoveryProbability != nil {
 		hash.Write([]byte("QUICDisablePathMTUDiscoveryProbability"))
 		hash.Write([]byte("QUICDisablePathMTUDiscoveryProbability"))
 		binary.Write(hash, binary.LittleEndian, *config.QUICDisablePathMTUDiscoveryProbability)
 		binary.Write(hash, binary.LittleEndian, *config.QUICDisablePathMTUDiscoveryProbability)

+ 7 - 0
psiphon/dialParameters.go

@@ -133,6 +133,7 @@ type DialParameters struct {
 	ObfuscatedQUICPaddingSeed                *prng.Seed
 	ObfuscatedQUICPaddingSeed                *prng.Seed
 	ObfuscatedQUICNonceTransformerParameters *transforms.ObfuscatorSeedTransformerParameters
 	ObfuscatedQUICNonceTransformerParameters *transforms.ObfuscatorSeedTransformerParameters
 	QUICDialEarly                            bool
 	QUICDialEarly                            bool
+	QUICDisableObfuscatedPSK                 bool
 	QUICDisablePathMTUDiscovery              bool
 	QUICDisablePathMTUDiscovery              bool
 
 
 	ConjureCachedRegistrationTTL        time.Duration
 	ConjureCachedRegistrationTTL        time.Duration
@@ -866,6 +867,12 @@ func MakeDialParameters(
 			}
 			}
 		}
 		}
 
 
+		if isFronted {
+			dialParams.QUICDisableObfuscatedPSK = true
+		} else {
+			dialParams.QUICDisableObfuscatedPSK = p.Bool(parameters.QUICDisableObfuscatedPSK)
+		}
+
 		dialParams.QUICDialEarly = p.WeightedCoinFlip(parameters.QUICDialEarlyProbability)
 		dialParams.QUICDialEarly = p.WeightedCoinFlip(parameters.QUICDialEarlyProbability)
 
 
 		dialParams.QUICDisablePathMTUDiscovery =
 		dialParams.QUICDisablePathMTUDiscovery =

+ 1 - 0
psiphon/tunnel.go

@@ -935,6 +935,7 @@ func dialTunnel(
 			dialParams.ObfuscatedQUICNonceTransformerParameters,
 			dialParams.ObfuscatedQUICNonceTransformerParameters,
 			dialParams.QUICDisablePathMTUDiscovery,
 			dialParams.QUICDisablePathMTUDiscovery,
 			dialParams.QUICDialEarly,
 			dialParams.QUICDialEarly,
+			dialParams.QUICDisableObfuscatedPSK,
 			dialParams.quicTLSClientSessionCache)
 			dialParams.quicTLSClientSessionCache)
 		if err != nil {
 		if err != nil {
 			return nil, errors.Trace(err)
 			return nil, errors.Trace(err)

+ 8 - 0
vendor/github.com/Psiphon-Labs/psiphon-tls/common.go

@@ -667,6 +667,13 @@ type Config struct {
 	// testing or in combination with VerifyConnection or VerifyPeerCertificate.
 	// testing or in combination with VerifyConnection or VerifyPeerCertificate.
 	InsecureSkipVerify bool
 	InsecureSkipVerify bool
 
 
+	// [Psiphon]
+	// InsecureSkipTimeVerify controls whether a client verifies the server's
+	// certificate chain against time when loading a session.
+	// If InsecureSkipTimeVerify is true crypto/tls accepts the certificate
+	// even when it is expired.
+	InsecureSkipTimeVerify bool
+
 	// CipherSuites is a list of enabled TLS 1.0–1.2 cipher suites. The order of
 	// CipherSuites is a list of enabled TLS 1.0–1.2 cipher suites. The order of
 	// the list is ignored. Note that TLS 1.3 ciphersuites are not configurable.
 	// the list is ignored. Note that TLS 1.3 ciphersuites are not configurable.
 	//
 	//
@@ -902,6 +909,7 @@ func (c *Config) Clone() *Config {
 		ClientAuth:                  c.ClientAuth,
 		ClientAuth:                  c.ClientAuth,
 		ClientCAs:                   c.ClientCAs,
 		ClientCAs:                   c.ClientCAs,
 		InsecureSkipVerify:          c.InsecureSkipVerify,
 		InsecureSkipVerify:          c.InsecureSkipVerify,
+		InsecureSkipTimeVerify:      c.InsecureSkipTimeVerify,
 		CipherSuites:                c.CipherSuites,
 		CipherSuites:                c.CipherSuites,
 		PreferServerCipherSuites:    c.PreferServerCipherSuites,
 		PreferServerCipherSuites:    c.PreferServerCipherSuites,
 		SessionTicketsDisabled:      c.SessionTicketsDisabled,
 		SessionTicketsDisabled:      c.SessionTicketsDisabled,

+ 7 - 4
vendor/github.com/Psiphon-Labs/psiphon-tls/handshake_client.go

@@ -345,10 +345,13 @@ func (c *Conn) loadSession(hello *clientHelloMsg) (
 	// Check that the cached server certificate is not expired, and that it's
 	// Check that the cached server certificate is not expired, and that it's
 	// valid for the ServerName. This should be ensured by the cache key, but
 	// valid for the ServerName. This should be ensured by the cache key, but
 	// protect the application from a faulty ClientSessionCache implementation.
 	// protect the application from a faulty ClientSessionCache implementation.
-	if c.config.time().After(session.peerCertificates[0].NotAfter) {
-		// Expired certificate, delete the entry.
-		c.config.ClientSessionCache.Put(cacheKey, nil)
-		return nil, nil, nil, nil
+	// [Psiphon]
+	if !c.config.InsecureSkipTimeVerify {
+		if c.config.time().After(session.peerCertificates[0].NotAfter) {
+			// Expired certificate, delete the entry.
+			c.config.ClientSessionCache.Put(cacheKey, nil)
+			return nil, nil, nil, nil
+		}
 	}
 	}
 	if !c.config.InsecureSkipVerify {
 	if !c.config.InsecureSkipVerify {
 		if len(session.verifiedChains) == 0 {
 		if len(session.verifiedChains) == 0 {

+ 1 - 1
vendor/modules.txt

@@ -23,7 +23,7 @@ github.com/Psiphon-Labs/consistent
 # github.com/Psiphon-Labs/goptlib v0.0.0-20200406165125-c0e32a7a3464
 # github.com/Psiphon-Labs/goptlib v0.0.0-20200406165125-c0e32a7a3464
 ## explicit
 ## explicit
 github.com/Psiphon-Labs/goptlib
 github.com/Psiphon-Labs/goptlib
-# github.com/Psiphon-Labs/psiphon-tls v0.0.0-20240716162946-891a0d5db073
+# github.com/Psiphon-Labs/psiphon-tls v0.0.0-20240812172553-e7a4dbd0bf2b
 ## explicit; go 1.21
 ## explicit; go 1.21
 github.com/Psiphon-Labs/psiphon-tls
 github.com/Psiphon-Labs/psiphon-tls
 # github.com/Psiphon-Labs/quic-go v0.0.0-20240424181006-45545f5e1536
 # github.com/Psiphon-Labs/quic-go v0.0.0-20240424181006-45545f5e1536