Просмотр исходного кода

Use resolved IP as TLS cache key for FRONTED-MEEK-HTTPS

Amir Khan 10 месяцев назад
Родитель
Сommit
85ed37aaea
3 измененных файлов с 70 добавлено и 12 удалено
  1. 53 9
      psiphon/common/tlsCache.go
  2. 11 3
      psiphon/dialParameters.go
  3. 6 0
      psiphon/tlsDialer.go

+ 53 - 9
psiphon/common/tlsCache.go

@@ -24,9 +24,10 @@ import (
 	utls "github.com/Psiphon-Labs/utls"
 )
 
+const TLS_NULL_SESSION_KEY = ""
+
 // TLSClientSessionCacheWrapper is a wrapper around tls.ClientSessionCache
 // that provides a hard-coded key for the cache.
-// It implements the TLSClientSessionCacheWrapper interface.
 type TLSClientSessionCacheWrapper struct {
 	tls.ClientSessionCache
 
@@ -34,32 +35,54 @@ type TLSClientSessionCacheWrapper struct {
 	sessionKey string
 }
 
-// WrapClientSessionCache wraps a tls.ClientSessionCache with an alternative
-// key, ignoring the SNI-based key that crypto/tls passes to Put/Get, which
-// may be incompatible with SNI obfuscation transforms.
+// WrapUtlsClientSessionCache wraps a tls.ClientSessionCache with an alternative
+// hard-coded session key, ignoring the SNI-based key that crypto/tls passes to Put/Get,
+// which may be incompatible with the SNI obfuscation transforms.
+// If the sessionKey is empty (TLS_NULL_SESSION_KEY), SetSessionKey has to be called
+// before using the cache.
 func WrapClientSessionCache(
 	cache tls.ClientSessionCache,
 	hardCodedSessionKey string,
 ) *TLSClientSessionCacheWrapper {
-
 	return &TLSClientSessionCacheWrapper{
 		ClientSessionCache: cache,
 		sessionKey:         hardCodedSessionKey,
 	}
 }
 
+// Get retrieves the session from the cache using the hard-coded session key.
 func (c *TLSClientSessionCacheWrapper) Get(_ string) (session *tls.ClientSessionState, ok bool) {
+	if c.sessionKey == "" {
+		return nil, false
+	}
 	return c.ClientSessionCache.Get(c.sessionKey)
 }
 
+// Put stores the session in the cache using the hard-coded session key.
 func (c *TLSClientSessionCacheWrapper) Put(_ string, cs *tls.ClientSessionState) {
+	if c.sessionKey == "" {
+		return
+	}
+	cs.ResumptionState()
 	c.ClientSessionCache.Put(c.sessionKey, cs)
 }
 
+// RemoveCacheEntry removes the cache entry for the hard-coded session key.
 func (c *TLSClientSessionCacheWrapper) RemoveCacheEntry() {
+	if c.sessionKey == "" {
+		return
+	}
 	c.ClientSessionCache.Put(c.sessionKey, nil)
 }
 
+// SetSessionKey sets the hard-coded session key if not already set.
+func (c *TLSClientSessionCacheWrapper) SetSessionKey(key string) {
+	if c.sessionKey != TLS_NULL_SESSION_KEY {
+		return
+	}
+	c.sessionKey = key
+}
+
 // UtlClientSessionCacheWrapper is a wrapper around utls.ClientSessionCache
 // that provides a hard-coded key for the cache.
 // It implements the TLSClientSessionCacheWrapper interface.
@@ -71,27 +94,48 @@ type UtlsClientSessionCacheWrapper struct {
 }
 
 // WrapUtlsClientSessionCache wraps a utls.ClientSessionCache with an alternative
-// key, ignoring the SNI-based key that crypto/tls passes to Put/Get, which
-// may be incompatible with SNI obfuscation transforms.
+// hard-coded session key, ignoring the SNI-based key that crypto/tls passes to Put/Get,
+// which may be incompatible with the SNI obfuscation transforms.
+// If the sessionKey is empty (TLS_NULL_SESSION_KEY), SetSessionKey has to be called
+// before using the cache.
 func WrapUtlsClientSessionCache(
 	cache utls.ClientSessionCache,
 	hardCodedSessionKey string,
 ) *UtlsClientSessionCacheWrapper {
-
 	return &UtlsClientSessionCacheWrapper{
 		ClientSessionCache: cache,
 		sessionKey:         hardCodedSessionKey,
 	}
 }
 
+// Get retrieves the session from the cache using the hard-coded session key.
 func (c *UtlsClientSessionCacheWrapper) Get(_ string) (session *utls.ClientSessionState, ok bool) {
+	if c.sessionKey == "" {
+		return nil, false
+	}
 	return c.ClientSessionCache.Get(c.sessionKey)
 }
 
+// Put stores the session in the cache using the hard-coded session key.
 func (c *UtlsClientSessionCacheWrapper) Put(_ string, cs *utls.ClientSessionState) {
+	if c.sessionKey == "" {
+		return
+	}
 	c.ClientSessionCache.Put(c.sessionKey, cs)
 }
 
+// RemoveCacheEntry removes the cache entry for the hard-coded session key.
 func (c *UtlsClientSessionCacheWrapper) RemoveCacheEntry() {
-	c.ClientSessionCache.Put(c.sessionKey, nil)
+	if c.sessionKey != "" {
+		c.ClientSessionCache.Put(c.sessionKey, nil)
+	}
+}
+
+// SetSessionKey sets the hard-coded session key if not already set.
+// If the session key is already set, it does nothing.
+func (c *UtlsClientSessionCacheWrapper) SetSessionKey(key string) {
+	if c.sessionKey != TLS_NULL_SESSION_KEY {
+		return
+	}
+	c.sessionKey = key
 }

+ 11 - 3
psiphon/dialParameters.go

@@ -759,9 +759,17 @@ func MakeDialParameters(
 		dialParams.ConjureAPIRegistration
 
 	if tlsClientSessionCache != nil && usingTLS {
-		sessionKey, err := serverEntry.GetTLSSessionCacheKeyAddress(dialParams.TunnelProtocol)
-		if err != nil {
-			return nil, errors.Trace(err)
+
+		var sessionKey string
+		if protocol.TunnelProtocolUsesFrontedMeek(dialParams.TunnelProtocol) {
+			// UsesMeekHTTPS and UsesFrontedMeek
+			// Special case: the session key is the resolved IP address of the CDN edge at dial time.
+			sessionKey = common.TLS_NULL_SESSION_KEY
+		} else {
+			sessionKey, err = serverEntry.GetTLSSessionCacheKeyAddress(dialParams.TunnelProtocol)
+			if err != nil {
+				return nil, errors.Trace(err)
+			}
 		}
 
 		dialParams.tlsClientSessionCache = common.WrapUtlsClientSessionCache(tlsClientSessionCache, sessionKey)

+ 6 - 0
psiphon/tlsDialer.go

@@ -244,6 +244,12 @@ func CustomTLSDial(
 		return nil, errors.Trace(err)
 	}
 
+	// If the hard-coded session key is not set (e.g. FRONTED-MEEK-OSSH), SetSessionKey must be called.
+	// The session key is set to the resolved IP address.
+	if wrappedCache, ok := config.ClientSessionCache.(*common.UtlsClientSessionCacheWrapper); ok {
+		wrappedCache.SetSessionKey(underlyingConn.RemoteAddr().String())
+	}
+
 	if config.FragmentClientHello {
 		underlyingConn = NewTLSFragmentorConn(underlyingConn)
 	}