diff --git a/psiphon/common/quic/obfuscator.go b/psiphon/common/quic/obfuscator.go index 63bca5b40..4fabdd060 100644 --- a/psiphon/common/quic/obfuscator.go +++ b/psiphon/common/quic/obfuscator.go @@ -42,7 +42,7 @@ const ( MAX_OBFUSCATED_QUIC_IPV6_PACKET_SIZE = 1352 MAX_PADDING = 64 NONCE_SIZE = 12 - RANDOM_STREAM_LIMIT = 1 << 38 + RANDOM_STREAM_LIMIT = 1<<38 - 64 ) // ObfuscatedPacketConn wraps a QUIC net.PacketConn with an obfuscation layer @@ -83,7 +83,7 @@ func (p *peerMode) isStale() bool { return monotime.Since(p.lastPacketTime) >= SERVER_IDLE_TIMEOUT } -// NewObfuscatedPacketConnPacketConn creates a new ObfuscatedPacketConn. +// NewObfuscatedPacketConn creates a new ObfuscatedPacketConn. func NewObfuscatedPacketConn( conn net.PacketConn, isServer bool, @@ -368,7 +368,7 @@ func (conn *ObfuscatedPacketConn) getRandomBytes(b []byte) error { if conn.randomStreamCount+int64(len(b)) >= RANDOM_STREAM_LIMIT { - // Re-key before reaching the 2^38 chacha20 key stream limit. + // Re-key before reaching the 2^38-64 chacha20 key stream limit. var randomStreamKey [32]byte _, err := rand.Read(randomStreamKey[:]) diff --git a/psiphon/common/quic/obfuscator_test.go b/psiphon/common/quic/obfuscator_test.go index a170f3437..7baec6dae 100644 --- a/psiphon/common/quic/obfuscator_test.go +++ b/psiphon/common/quic/obfuscator_test.go @@ -68,19 +68,36 @@ func Disabled_TestPaddingLenLimit(t *testing.T) { t.Fatalf("NewObfuscatedPacketConn failed: %s", err) } + // Use large blocks to get close to the key stream limit. + var b [2 * 1024 * 1024 * 1024]byte n := int64(0) - for { + for i := 0; i < 127; i++ { err := c.getRandomBytes(b[:]) if err != nil { t.Fatalf("getRandomBytes failed: %s", err) } n += int64(len(b)) - if n > (1<<38)+1 { - // We're past the chacha20 key stream limit. - break + } + + // Stop using large blocks 64 bytes short of the limit, 2^38-64. + + err = c.getRandomBytes(b[0 : len(b)-128]) + if err != nil { + t.Fatalf("getRandomBytes failed: %s", err) + } + n += int64(len(b) - 128) + + // Invoke byte at a time across the limit boundary to ensure we + // don't jump over the limit case. + + for i := 0; i < 192; i++ { + err := c.getRandomBytes(b[0:1]) + if err != nil { + t.Fatalf("getRandomBytes failed: %s", err) } + n += int64(1) } }