Skip to content

Commit

Permalink
Merge branch 'master' into staging-server
Browse files Browse the repository at this point in the history
  • Loading branch information
rod-hynes committed May 29, 2023
2 parents 5f21116 + c4f6a59 commit 2380174
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 47 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ require (
git.torproject.org/pluggable-transports/goptlib.git v1.2.0 // indirect
github.com/AndreasBriese/bbloom v0.0.0-20170702084017-28f7e881ca57 // indirect
github.com/BurntSushi/toml v0.3.1 // indirect
github.com/Psiphon-Labs/qtls-go1-18 v0.0.0-20221014170512-3bdc7291c091 // indirect
github.com/Psiphon-Labs/qtls-go1-19 v0.0.0-20221014165721-ed28749db082 // indirect
github.com/Psiphon-Labs/qtls-go1-18 v0.0.0-20230515185031-ae6632ab97ac // indirect
github.com/Psiphon-Labs/qtls-go1-19 v0.0.0-20230515185100-099bac32c181 // indirect
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect
github.com/andybalholm/brotli v1.0.5-0.20220518190645-786ec621f618 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ github.com/Psiphon-Labs/goptlib v0.0.0-20200406165125-c0e32a7a3464 h1:VmnMMMheFX
github.com/Psiphon-Labs/goptlib v0.0.0-20200406165125-c0e32a7a3464/go.mod h1:Pe5BqN2DdIdChorAXl6bDaQd/wghpCleJfid2NoSli0=
github.com/Psiphon-Labs/qtls-go1-18 v0.0.0-20221014170512-3bdc7291c091 h1:Kv0LQQ3joUp8s2z36aigpNgNyiLiExT/OS9KOC/L/gI=
github.com/Psiphon-Labs/qtls-go1-18 v0.0.0-20221014170512-3bdc7291c091/go.mod h1:0IvfcPDkLvBkir+WGq3E0shsx+TLasdcl8ojVWWTflE=
github.com/Psiphon-Labs/qtls-go1-18 v0.0.0-20230515185031-ae6632ab97ac h1:2/n1zJIAEmpAg/IapXRdcuY29L6tud4WyKrXj8kpWSY=
github.com/Psiphon-Labs/qtls-go1-18 v0.0.0-20230515185031-ae6632ab97ac/go.mod h1:0IvfcPDkLvBkir+WGq3E0shsx+TLasdcl8ojVWWTflE=
github.com/Psiphon-Labs/qtls-go1-19 v0.0.0-20221014165721-ed28749db082 h1:arVlc3JYvckFXGyB8N30ul8AmA+rDuLolPRYMDHzgTU=
github.com/Psiphon-Labs/qtls-go1-19 v0.0.0-20221014165721-ed28749db082/go.mod h1:mHM/QFYc02W9MKJ/Ux5XGOKP4OImosPeQUO7XAaXs0E=
github.com/Psiphon-Labs/qtls-go1-19 v0.0.0-20230515185100-099bac32c181 h1:+rhvNaRVcVr6OXDPJx3lOaSccBhCxgcKlG/OVU/uvGc=
github.com/Psiphon-Labs/qtls-go1-19 v0.0.0-20230515185100-099bac32c181/go.mod h1:mHM/QFYc02W9MKJ/Ux5XGOKP4OImosPeQUO7XAaXs0E=
github.com/Psiphon-Labs/quic-go v0.0.0-20230215230806-9b1ddbf778cc h1:FUmGSvMiMbf1tFXWbK0+N7+5zBhOol8CHQdpB4ZQlDg=
github.com/Psiphon-Labs/quic-go v0.0.0-20230215230806-9b1ddbf778cc/go.mod h1:cu4yhfHkyt+uQ9FFFjTpjCjcQYf52ntEAyoV4Zg0+fg=
github.com/Psiphon-Labs/tls-tris v0.0.0-20210713133851-676a693d51ad h1:m6HS84+b5xDPLj7D/ya1CeixyaHOCZoMbBilJ48y+Ts=
Expand Down
52 changes: 47 additions & 5 deletions psiphon/common/obfuscator/passthrough.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const (
TLS_PASSTHROUGH_NONCE_SIZE = 16
TLS_PASSTHROUGH_KEY_SIZE = 32
TLS_PASSTHROUGH_TIME_PERIOD = 20 * time.Minute
TLS_PASSTHROUGH_HISTORY_TTL = TLS_PASSTHROUGH_TIME_PERIOD * 3
TLS_PASSTHROUGH_MESSAGE_SIZE = 32
)

Expand All @@ -51,7 +52,7 @@ const (
func MakeTLSPassthroughMessage(
useTimeFactor bool, obfuscatedKey string) ([]byte, error) {

passthroughKey, err := derivePassthroughKey(useTimeFactor, obfuscatedKey)
passthroughKey, err := derivePassthroughKey(useTimeFactor, 0, obfuscatedKey)
if err != nil {
return nil, errors.Trace(err)
}
Expand Down Expand Up @@ -86,9 +87,48 @@ func VerifyTLSPassthroughMessage(
message = stub[:]
}

passthroughKey, err := derivePassthroughKey(useTimeFactor, obfuscatedKey)
if useTimeFactor {

// Check three rounded time periods: the current one, the previous
// one, and the future one. Even if the client clock is ahead of the
// server clock by only a short amount, it can use the future time
// period, from the server's perspective, when the server's clock is
// close to the end of its current time period. And even if the
// client and server clocks are perfectly synchronized, the client
// may use the previous time period and then time advances to the
// next time period by the time the server receives the message.
//
// All three time periods are always checked, to avoid leaking via
// timing differences.

match := false

for _, timePeriodShift := range []int64{-1, 0, 1} {

passthroughKey, err := derivePassthroughKey(
useTimeFactor, timePeriodShift, obfuscatedKey)
if err != nil {
// derivePassthroughKey is not expected to fail.
// TODO: log error
return false
}

h := hmac.New(sha256.New, passthroughKey)
h.Write(message[0:TLS_PASSTHROUGH_NONCE_SIZE])

if 1 == subtle.ConstantTimeCompare(
message[TLS_PASSTHROUGH_NONCE_SIZE:],
h.Sum(nil)[0:TLS_PASSTHROUGH_MESSAGE_SIZE-TLS_PASSTHROUGH_NONCE_SIZE]) {

match = true
}
}

return match
}

passthroughKey, err := derivePassthroughKey(false, 0, obfuscatedKey)
if err != nil {
// TODO: log error
return false
}

Expand All @@ -106,7 +146,7 @@ func VerifyTLSPassthroughMessage(
var timePeriodSeconds = int64(TLS_PASSTHROUGH_TIME_PERIOD / time.Second)

func derivePassthroughKey(
useTimeFactor bool, obfuscatedKey string) ([]byte, error) {
useTimeFactor bool, timePeriodShift int64, obfuscatedKey string) ([]byte, error) {

secret := []byte(obfuscatedKey)

Expand All @@ -130,7 +170,9 @@ func derivePassthroughKey(
// differences at time boundaries. We assume that the server always or never
// sets useTimeFactor.

roundedTimePeriod := (time.Now().Unix() + (timePeriodSeconds / 2)) / timePeriodSeconds
roundedTimePeriod := (time.Now().Unix() +
(timePeriodSeconds / 2) +
timePeriodSeconds*timePeriodShift) / timePeriodSeconds

var timeFactor [8]byte
binary.LittleEndian.PutUint64(timeFactor[:], uint64(roundedTimePeriod))
Expand Down
21 changes: 20 additions & 1 deletion psiphon/common/obfuscator/passthrough_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func TestTLSPassthrough(t *testing.T) {

// test: valid passthrough message now invalid after time factor period

time.Sleep(time.Duration(timePeriodSeconds)*time.Second + time.Millisecond)
time.Sleep(time.Duration(timePeriodSeconds*2)*time.Second + time.Millisecond)

verified := VerifyTLSPassthroughMessage(useTimeFactor, correctMasterKey, validMessage)

Expand Down Expand Up @@ -126,6 +126,25 @@ func TestTLSPassthrough(t *testing.T) {
if timeDiff.Microseconds() > 500 {
t.Fatalf("unexpected elapsed time difference")
}

// test: cross rounded time period boundries

if useTimeFactor {

for i := 0; i < 2000; i++ {

validMessage, err := MakeTLSPassthroughMessage(useTimeFactor, correctMasterKey)
if err != nil {
t.Fatalf("MakeTLSPassthroughMessage failed: %s", err)
}

time.Sleep(10 * time.Millisecond)

if !VerifyTLSPassthroughMessage(useTimeFactor, correctMasterKey, validMessage) {
t.Fatalf("unexpected invalid passthrough message")
}
}
}
})
}
}
2 changes: 1 addition & 1 deletion psiphon/common/quic/quic.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ func Listen(
// Irregular events are logged for invalid client activity.

clientRandomHistory := obfuscator.NewSeedHistory(
&obfuscator.SeedHistoryConfig{SeedTTL: obfuscator.TLS_PASSTHROUGH_TIME_PERIOD})
&obfuscator.SeedHistoryConfig{SeedTTL: obfuscator.TLS_PASSTHROUGH_HISTORY_TTL})

verifyClientHelloRandom := func(remoteAddr net.Addr, clientHelloRandom []byte) bool {

Expand Down
37 changes: 22 additions & 15 deletions psiphon/dialParameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -675,13 +675,35 @@ func MakeDialParameters(

if !isReplay || !replayHostname {

// Any MeekHostHeader selections made here will be overridden below,
// as required, for fronting cases.

if protocol.TunnelProtocolUsesMeekHTTPS(dialParams.TunnelProtocol) ||
protocol.TunnelProtocolUsesFrontedMeekQUIC(dialParams.TunnelProtocol) {

dialParams.MeekSNIServerName = ""
hostname := ""
if p.WeightedCoinFlip(parameters.TransformHostNameProbability) {
dialParams.MeekSNIServerName = selectHostName(dialParams.TunnelProtocol, p)
hostname = dialParams.MeekSNIServerName
dialParams.MeekTransformedHostName = true
} else {

// Always select a hostname for the Host header in this case.
// Unlike HTTP, the Host header isn't plaintext on the wire,
// and so there's no anti-fingerprint benefit from presenting
// the server IP address in the Host header. Omitting the
// server IP here can prevent exposing it in certain
// scenarios where the traffic is rerouted and arrives at a
// different HTTPS server.

hostname = selectHostName(dialParams.TunnelProtocol, p)
}
if serverEntry.MeekServerPort == 443 {
dialParams.MeekHostHeader = hostname
} else {
dialParams.MeekHostHeader = net.JoinHostPort(
hostname, strconv.Itoa(serverEntry.MeekServerPort))
}

} else if protocol.TunnelProtocolUsesMeekHTTP(dialParams.TunnelProtocol) {
Expand Down Expand Up @@ -765,7 +787,6 @@ func MakeDialParameters(
dialParams.ObfuscatedQUICNonceTransformerParameters = nil
}
}

}

if !isReplay || !replayLivenessTest {
Expand Down Expand Up @@ -850,7 +871,6 @@ func MakeDialParameters(
dialParams.OSSHObfuscatorSeedTransformerParameters = nil
}
}

}

if protocol.TunnelProtocolUsesMeekHTTP(dialParams.TunnelProtocol) {
Expand Down Expand Up @@ -919,13 +939,6 @@ func MakeDialParameters(
case protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK:

dialParams.MeekDialAddress = net.JoinHostPort(serverEntry.IpAddress, dialParams.DialPortNumber)
if !dialParams.MeekTransformedHostName {
if dialPortNumber == 80 {
dialParams.MeekHostHeader = serverEntry.IpAddress
} else {
dialParams.MeekHostHeader = dialParams.MeekDialAddress
}
}

case protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS,
protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET:
Expand All @@ -935,16 +948,10 @@ func MakeDialParameters(
// Note: IP address in SNI field will be omitted.
dialParams.MeekSNIServerName = serverEntry.IpAddress
}
if dialPortNumber == 443 {
dialParams.MeekHostHeader = serverEntry.IpAddress
} else {
dialParams.MeekHostHeader = dialParams.MeekDialAddress
}

default:
return nil, errors.Tracef(
"unknown tunnel protocol: %s", dialParams.TunnelProtocol)

}

if protocol.TunnelProtocolUsesMeek(dialParams.TunnelProtocol) {
Expand Down
2 changes: 1 addition & 1 deletion psiphon/server/meek.go
Original file line number Diff line number Diff line change
Expand Up @@ -1313,7 +1313,7 @@ func (server *MeekServer) makeMeekTLSConfig(

// Use a custom, shorter TTL based on the validity period of the
// passthrough message.
TTL := obfuscator.TLS_PASSTHROUGH_TIME_PERIOD
TTL := obfuscator.TLS_PASSTHROUGH_HISTORY_TTL
if server.support.Config.LegacyPassthrough {
TTL = obfuscator.HISTORY_SEED_TTL
}
Expand Down
39 changes: 29 additions & 10 deletions vendor/github.com/Psiphon-Labs/qtls-go1-18/handshake_messages.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 29 additions & 10 deletions vendor/github.com/Psiphon-Labs/qtls-go1-19/handshake_messages.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions vendor/modules.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ github.com/Psiphon-Labs/bolt
# github.com/Psiphon-Labs/goptlib v0.0.0-20200406165125-c0e32a7a3464
## explicit
github.com/Psiphon-Labs/goptlib
# github.com/Psiphon-Labs/qtls-go1-18 v0.0.0-20221014170512-3bdc7291c091
# github.com/Psiphon-Labs/qtls-go1-18 v0.0.0-20230515185031-ae6632ab97ac
## explicit; go 1.18
github.com/Psiphon-Labs/qtls-go1-18
# github.com/Psiphon-Labs/qtls-go1-19 v0.0.0-20221014165721-ed28749db082
# github.com/Psiphon-Labs/qtls-go1-19 v0.0.0-20230515185100-099bac32c181
## explicit; go 1.18
github.com/Psiphon-Labs/qtls-go1-19
# github.com/Psiphon-Labs/quic-go v0.0.0-20230215230806-9b1ddbf778cc
Expand Down

0 comments on commit 2380174

Please sign in to comment.