|
@@ -21,8 +21,10 @@ package server
|
|
|
|
|
|
|
|
import (
|
|
import (
|
|
|
"bytes"
|
|
"bytes"
|
|
|
- "crypto/tls"
|
|
|
|
|
|
|
+ "crypto/rand"
|
|
|
|
|
+ golangtls "crypto/tls"
|
|
|
"encoding/base64"
|
|
"encoding/base64"
|
|
|
|
|
+ "encoding/hex"
|
|
|
"encoding/json"
|
|
"encoding/json"
|
|
|
"errors"
|
|
"errors"
|
|
|
"io"
|
|
"io"
|
|
@@ -36,6 +38,7 @@ import (
|
|
|
"github.com/Psiphon-Inc/crypto/nacl/box"
|
|
"github.com/Psiphon-Inc/crypto/nacl/box"
|
|
|
"github.com/Psiphon-Inc/goarista/monotime"
|
|
"github.com/Psiphon-Inc/goarista/monotime"
|
|
|
"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/tls"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
// MeekServer is based on meek-server.go from Tor and Psiphon:
|
|
// MeekServer is based on meek-server.go from Tor and Psiphon:
|
|
@@ -93,7 +96,7 @@ type MeekServer struct {
|
|
|
func NewMeekServer(
|
|
func NewMeekServer(
|
|
|
support *SupportServices,
|
|
support *SupportServices,
|
|
|
listener net.Listener,
|
|
listener net.Listener,
|
|
|
- useTLS bool,
|
|
|
|
|
|
|
+ useTLS, useObfuscatedSessionTickets bool,
|
|
|
clientHandler func(clientConn net.Conn),
|
|
clientHandler func(clientConn net.Conn),
|
|
|
stopBroadcast <-chan struct{}) (*MeekServer, error) {
|
|
stopBroadcast <-chan struct{}) (*MeekServer, error) {
|
|
|
|
|
|
|
@@ -107,7 +110,8 @@ func NewMeekServer(
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if useTLS {
|
|
if useTLS {
|
|
|
- tlsConfig, err := makeMeekTLSConfig(support)
|
|
|
|
|
|
|
+ tlsConfig, err := makeMeekTLSConfig(
|
|
|
|
|
+ support, useObfuscatedSessionTickets)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return nil, common.ContextError(err)
|
|
return nil, common.ContextError(err)
|
|
|
}
|
|
}
|
|
@@ -162,15 +166,14 @@ func (server *MeekServer) Run() error {
|
|
|
ConnState: server.httpConnStateCallback,
|
|
ConnState: server.httpConnStateCallback,
|
|
|
|
|
|
|
|
// Disable auto HTTP/2 (https://golang.org/doc/go1.6)
|
|
// Disable auto HTTP/2 (https://golang.org/doc/go1.6)
|
|
|
- TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
|
|
|
|
|
|
|
+ TLSNextProto: make(map[string]func(*http.Server, *golangtls.Conn, http.Handler)),
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Note: Serve() will be interrupted by listener.Close() call
|
|
// Note: Serve() will be interrupted by listener.Close() call
|
|
|
var err error
|
|
var err error
|
|
|
if server.tlsConfig != nil {
|
|
if server.tlsConfig != nil {
|
|
|
- httpServer.TLSConfig = server.tlsConfig
|
|
|
|
|
httpsServer := HTTPSServer{Server: *httpServer}
|
|
httpsServer := HTTPSServer{Server: *httpServer}
|
|
|
- err = httpsServer.ServeTLS(server.listener)
|
|
|
|
|
|
|
+ err = httpsServer.ServeTLS(server.listener, server.tlsConfig)
|
|
|
} else {
|
|
} else {
|
|
|
err = httpServer.Serve(server.listener)
|
|
err = httpServer.Serve(server.listener)
|
|
|
}
|
|
}
|
|
@@ -467,7 +470,9 @@ func (session *meekSession) expired() bool {
|
|
|
// Currently, this config is optimized for fronted meek where the nature
|
|
// Currently, this config is optimized for fronted meek where the nature
|
|
|
// of the connection is non-circumvention; it's optimized for performance
|
|
// of the connection is non-circumvention; it's optimized for performance
|
|
|
// assuming the peer is an uncensored CDN.
|
|
// assuming the peer is an uncensored CDN.
|
|
|
-func makeMeekTLSConfig(support *SupportServices) (*tls.Config, error) {
|
|
|
|
|
|
|
+func makeMeekTLSConfig(
|
|
|
|
|
+ support *SupportServices,
|
|
|
|
|
+ useObfuscatedSessionTickets bool) (*tls.Config, error) {
|
|
|
|
|
|
|
|
certificate, privateKey, err := GenerateWebServerCertificate(
|
|
certificate, privateKey, err := GenerateWebServerCertificate(
|
|
|
support.Config.MeekCertificateCommonName)
|
|
support.Config.MeekCertificateCommonName)
|
|
@@ -481,7 +486,7 @@ func makeMeekTLSConfig(support *SupportServices) (*tls.Config, error) {
|
|
|
return nil, common.ContextError(err)
|
|
return nil, common.ContextError(err)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return &tls.Config{
|
|
|
|
|
|
|
+ config := &tls.Config{
|
|
|
Certificates: []tls.Certificate{tlsCertificate},
|
|
Certificates: []tls.Certificate{tlsCertificate},
|
|
|
NextProtos: []string{"http/1.1"},
|
|
NextProtos: []string{"http/1.1"},
|
|
|
MinVersion: tls.VersionTLS10,
|
|
MinVersion: tls.VersionTLS10,
|
|
@@ -512,7 +517,39 @@ func makeMeekTLSConfig(support *SupportServices) (*tls.Config, error) {
|
|
|
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
|
},
|
|
},
|
|
|
PreferServerCipherSuites: true,
|
|
PreferServerCipherSuites: true,
|
|
|
- }, nil
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if useObfuscatedSessionTickets {
|
|
|
|
|
+
|
|
|
|
|
+ // See obfuscated session ticket overview
|
|
|
|
|
+ // in tls.NewObfuscatedClientSessionCache
|
|
|
|
|
+
|
|
|
|
|
+ var obfuscatedSessionTicketKey [32]byte
|
|
|
|
|
+ key, err := hex.DecodeString(support.Config.MeekObfuscatedKey)
|
|
|
|
|
+ if err == nil && len(key) != 32 {
|
|
|
|
|
+ err = errors.New("invalid obfuscated session key length")
|
|
|
|
|
+ }
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, common.ContextError(err)
|
|
|
|
|
+ }
|
|
|
|
|
+ copy(obfuscatedSessionTicketKey[:], key)
|
|
|
|
|
+
|
|
|
|
|
+ var standardSessionTicketKey [32]byte
|
|
|
|
|
+ _, err = rand.Read(standardSessionTicketKey[:])
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, common.ContextError(err)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Note: SessionTicketKey needs to be set, or else, it appears,
|
|
|
|
|
+ // tls.Config.serverInit() will clobber the value set by
|
|
|
|
|
+ // SetSessionTicketKeys.
|
|
|
|
|
+ config.SessionTicketKey = obfuscatedSessionTicketKey
|
|
|
|
|
+ config.SetSessionTicketKeys([][32]byte{
|
|
|
|
|
+ standardSessionTicketKey,
|
|
|
|
|
+ obfuscatedSessionTicketKey})
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return config, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// getMeekCookiePayload extracts the payload from a meek cookie. The cookie
|
|
// getMeekCookiePayload extracts the payload from a meek cookie. The cookie
|