obfuscator.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. /*
  2. * Copyright (c) 2018, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. package quic
  20. import (
  21. "crypto/sha256"
  22. "fmt"
  23. "io"
  24. "net"
  25. "sync"
  26. "sync/atomic"
  27. "time"
  28. "github.com/Psiphon-Labs/goarista/monotime"
  29. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  30. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/Yawning/chacha20"
  31. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/hkdf"
  32. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
  33. )
  34. const (
  35. MAX_QUIC_IPV4_PACKET_SIZE = 1252
  36. MAX_QUIC_IPV6_PACKET_SIZE = 1232
  37. MAX_OBFUSCATED_QUIC_IPV4_PACKET_SIZE = 1372
  38. MAX_OBFUSCATED_QUIC_IPV6_PACKET_SIZE = 1352
  39. MAX_PADDING = 64
  40. NONCE_SIZE = 12
  41. RANDOM_STREAM_LIMIT = 1<<38 - 64
  42. )
  43. // ObfuscatedPacketConn wraps a QUIC net.PacketConn with an obfuscation layer
  44. // that obscures QUIC packets, adding random padding and producing uniformly
  45. // random payload.
  46. //
  47. // The crypto performed by ObfuscatedPacketConn is purely for obfuscation to
  48. // frusctrate wire-speed DPI and does not add privacy/security. The small
  49. // nonce space and single key per server is not cryptographically secure.
  50. //
  51. // A server-side ObfuscatedPacketConn performs simple QUIC DPI to distinguish
  52. // between obfuscated and non-obfsucated peer flows and responds accordingly.
  53. //
  54. // The header and padding added by ObfuscatedPacketConn on top of the QUIC
  55. // payload will increase UDP packets beyond the QUIC max of 1280 bytes,
  56. // introducing some risk of fragmentation and/or dropped packets.
  57. type ObfuscatedPacketConn struct {
  58. net.PacketConn
  59. isServer bool
  60. isClosed int32
  61. runWaitGroup *sync.WaitGroup
  62. stopBroadcast chan struct{}
  63. obfuscationKey [32]byte
  64. peerModesMutex sync.Mutex
  65. peerModes map[string]*peerMode
  66. noncePRNG *prng.PRNG
  67. paddingPRNG *prng.PRNG
  68. }
  69. type peerMode struct {
  70. isObfuscated bool
  71. lastPacketTime monotime.Time
  72. }
  73. func (p *peerMode) isStale() bool {
  74. return monotime.Since(p.lastPacketTime) >= SERVER_IDLE_TIMEOUT
  75. }
  76. // NewObfuscatedPacketConn creates a new ObfuscatedPacketConn.
  77. func NewObfuscatedPacketConn(
  78. conn net.PacketConn,
  79. isServer bool,
  80. obfuscationKey string,
  81. paddingSeed *prng.Seed) (*ObfuscatedPacketConn, error) {
  82. // There is no replay of obfuscation "encryption", just padding.
  83. nonceSeed, err := prng.NewSeed()
  84. if err != nil {
  85. return nil, common.ContextError(err)
  86. }
  87. packetConn := &ObfuscatedPacketConn{
  88. PacketConn: conn,
  89. isServer: isServer,
  90. peerModes: make(map[string]*peerMode),
  91. noncePRNG: prng.NewPRNGWithSeed(nonceSeed),
  92. paddingPRNG: prng.NewPRNGWithSeed(paddingSeed),
  93. }
  94. secret := []byte(obfuscationKey)
  95. salt := []byte("quic-obfuscation-key")
  96. _, err = io.ReadFull(
  97. hkdf.New(sha256.New, secret, salt, nil), packetConn.obfuscationKey[:])
  98. if err != nil {
  99. return nil, common.ContextError(err)
  100. }
  101. if isServer {
  102. packetConn.runWaitGroup = new(sync.WaitGroup)
  103. packetConn.stopBroadcast = make(chan struct{}, 1)
  104. // Reap stale peer mode information to reclaim memory.
  105. packetConn.runWaitGroup.Add(1)
  106. go func() {
  107. defer packetConn.runWaitGroup.Done()
  108. ticker := time.NewTicker(SERVER_IDLE_TIMEOUT / 2)
  109. defer ticker.Stop()
  110. for {
  111. select {
  112. case <-ticker.C:
  113. packetConn.peerModesMutex.Lock()
  114. for address, mode := range packetConn.peerModes {
  115. if mode.isStale() {
  116. delete(packetConn.peerModes, address)
  117. }
  118. }
  119. packetConn.peerModesMutex.Unlock()
  120. case <-packetConn.stopBroadcast:
  121. return
  122. }
  123. }
  124. }()
  125. }
  126. return packetConn, nil
  127. }
  128. func (conn *ObfuscatedPacketConn) Close() error {
  129. // Ensure close channel only called once.
  130. if !atomic.CompareAndSwapInt32(&conn.isClosed, 0, 1) {
  131. return nil
  132. }
  133. if conn.isServer {
  134. close(conn.stopBroadcast)
  135. conn.runWaitGroup.Wait()
  136. }
  137. return conn.PacketConn.Close()
  138. }
  139. type temporaryNetError struct {
  140. err error
  141. }
  142. func newTemporaryNetError(err error) *temporaryNetError {
  143. return &temporaryNetError{err: err}
  144. }
  145. func (e *temporaryNetError) Timeout() bool {
  146. return false
  147. }
  148. func (e *temporaryNetError) Temporary() bool {
  149. return true
  150. }
  151. func (e *temporaryNetError) Error() string {
  152. return e.err.Error()
  153. }
  154. func (conn *ObfuscatedPacketConn) ReadFrom(p []byte) (int, net.Addr, error) {
  155. n, addr, err := conn.PacketConn.ReadFrom(p)
  156. // Data is processed even when err != nil, as ReadFrom may return both
  157. // a packet and an error, such as io.EOF.
  158. // See: https://golang.org/pkg/net/#PacketConn.
  159. if n > 0 {
  160. isObfuscated := true
  161. if conn.isServer {
  162. // The server handles both plain and obfuscated QUIC packets.
  163. // isQUIC performs DPI to determine whether the packet appears to
  164. // be QUIC, in which case deobfuscation is not performed. Not all
  165. // plain QUIC packets will pass the DPI test, but the initial
  166. // packet(s) in a flow are expected to match; so the server
  167. // records a peer "mode", referenced by peer address to know when
  168. // to skip deobfuscation for later packets.
  169. //
  170. // It's possible for clients to redial QUIC connections,
  171. // transitioning from obfuscated to plain, using the same source
  172. // address (IP and port). This is more likely when many clients
  173. // are behind NAT. If a packet appears to be QUIC, this will reset
  174. // any existing peer "mode" to plain. The obfuscator checks that
  175. // its obfuscated packets don't pass the QUIC DPI test.
  176. //
  177. // TODO: delete peerMode when a packet is a client connection
  178. // termination QUIC packet? Will reclaim peerMode memory faster
  179. // than relying on reaper.
  180. isQUIC := isQUIC(p[:n])
  181. // Without addr, the mode cannot be determined.
  182. if addr == nil {
  183. return n, addr, newTemporaryNetError(common.ContextError(
  184. fmt.Errorf("missing addr")))
  185. }
  186. conn.peerModesMutex.Lock()
  187. address := addr.String()
  188. mode, ok := conn.peerModes[address]
  189. if !ok {
  190. mode = &peerMode{isObfuscated: !isQUIC}
  191. conn.peerModes[address] = mode
  192. } else if mode.isStale() {
  193. mode.isObfuscated = !isQUIC
  194. } else if mode.isObfuscated && isQUIC {
  195. mode.isObfuscated = false
  196. }
  197. isObfuscated = mode.isObfuscated
  198. mode.lastPacketTime = monotime.Now()
  199. conn.peerModesMutex.Unlock()
  200. }
  201. if isObfuscated {
  202. // We can use p as a scratch buffer for deobfuscation, and this
  203. // avoids allocting a buffer.
  204. if n < (NONCE_SIZE + 1) {
  205. return n, addr, newTemporaryNetError(common.ContextError(
  206. fmt.Errorf("unexpected obfuscated QUIC packet length: %d", n)))
  207. }
  208. cipher, err := chacha20.NewCipher(conn.obfuscationKey[:], p[0:NONCE_SIZE])
  209. if err != nil {
  210. return n, addr, common.ContextError(err)
  211. }
  212. cipher.XORKeyStream(p[NONCE_SIZE:], p[NONCE_SIZE:])
  213. paddingLen := int(p[NONCE_SIZE])
  214. if paddingLen > MAX_PADDING || paddingLen > n-(NONCE_SIZE+1) {
  215. return n, addr, newTemporaryNetError(common.ContextError(
  216. fmt.Errorf("unexpected padding length: %d, %d", paddingLen, n)))
  217. }
  218. n -= (NONCE_SIZE + 1) + paddingLen
  219. copy(p[0:n], p[(NONCE_SIZE+1)+paddingLen:n+(NONCE_SIZE+1)+paddingLen])
  220. }
  221. }
  222. // Do not wrap any err returned by conn.PacketConn.ReadFrom.
  223. return n, addr, err
  224. }
  225. type obfuscatorBuffer struct {
  226. buffer [MAX_OBFUSCATED_QUIC_IPV4_PACKET_SIZE]byte
  227. }
  228. var obfuscatorBufferPool = &sync.Pool{
  229. New: func() interface{} {
  230. return new(obfuscatorBuffer)
  231. },
  232. }
  233. func getMaxPacketSizes(addr net.Addr) (int, int) {
  234. if udpAddr, ok := addr.(*net.UDPAddr); ok && udpAddr.IP.To4() == nil {
  235. return MAX_QUIC_IPV6_PACKET_SIZE, MAX_OBFUSCATED_QUIC_IPV6_PACKET_SIZE
  236. }
  237. return MAX_QUIC_IPV4_PACKET_SIZE, MAX_OBFUSCATED_QUIC_IPV4_PACKET_SIZE
  238. }
  239. func (conn *ObfuscatedPacketConn) WriteTo(p []byte, addr net.Addr) (int, error) {
  240. n := len(p)
  241. isObfuscated := true
  242. if conn.isServer {
  243. conn.peerModesMutex.Lock()
  244. address := addr.String()
  245. mode, ok := conn.peerModes[address]
  246. isObfuscated = ok && mode.isObfuscated
  247. conn.peerModesMutex.Unlock()
  248. }
  249. if isObfuscated {
  250. maxQUICPacketSize, maxObfuscatedPacketSize := getMaxPacketSizes(addr)
  251. if n > maxQUICPacketSize {
  252. return 0, newTemporaryNetError(common.ContextError(
  253. fmt.Errorf("unexpected QUIC packet length: %d", n)))
  254. }
  255. // Note: escape analysis showed a local array escaping to the heap,
  256. // so use a buffer pool instead to avoid heap allocation per packet.
  257. b := obfuscatorBufferPool.Get().(*obfuscatorBuffer)
  258. buffer := b.buffer[:]
  259. defer obfuscatorBufferPool.Put(b)
  260. for {
  261. // Note: this zero-memory pattern is compiler optimized:
  262. // https://golang.org/cl/137880043
  263. for i := range buffer {
  264. buffer[i] = 0
  265. }
  266. nonce := buffer[0:NONCE_SIZE]
  267. conn.noncePRNG.Read(nonce)
  268. // Obfuscated QUIC padding results in packets that exceed the
  269. // QUIC max packet size of 1280.
  270. maxPaddingLen := maxObfuscatedPacketSize - (n + (NONCE_SIZE + 1))
  271. if maxPaddingLen < 0 {
  272. maxPaddingLen = 0
  273. }
  274. if maxPaddingLen > MAX_PADDING {
  275. maxPaddingLen = MAX_PADDING
  276. }
  277. paddingLen := conn.paddingPRNG.Intn(maxPaddingLen + 1)
  278. buffer[NONCE_SIZE] = uint8(paddingLen)
  279. padding := buffer[(NONCE_SIZE + 1) : (NONCE_SIZE+1)+paddingLen]
  280. conn.paddingPRNG.Read(padding)
  281. copy(buffer[(NONCE_SIZE+1)+paddingLen:], p)
  282. dataLen := (NONCE_SIZE + 1) + paddingLen + n
  283. cipher, err := chacha20.NewCipher(conn.obfuscationKey[:], nonce)
  284. if err != nil {
  285. return 0, common.ContextError(err)
  286. }
  287. packet := buffer[NONCE_SIZE:dataLen]
  288. cipher.XORKeyStream(packet, packet)
  289. p = buffer[:dataLen]
  290. // Don't use obfuscation that looks like QUIC, or the
  291. // peer will not treat this packet as obfuscated.
  292. if !isQUIC(p) {
  293. break
  294. }
  295. }
  296. }
  297. _, err := conn.PacketConn.WriteTo(p, addr)
  298. // Do not wrap any err returned by conn.PacketConn.WriteTo.
  299. return n, err
  300. }
  301. func isQUIC(buffer []byte) bool {
  302. // As this function is called for every packet, it needs to be fast.
  303. //
  304. // In all currently supported versions, the first client packet contains
  305. // the "CHLO" tag at one of the following offsets. The offset can vary for
  306. // a single version.
  307. //
  308. // Note that v44 does not include the "QUIC version" header field in its
  309. // first client packet.
  310. //
  311. // As QUIC header parsing is complex, with many cases, we are not
  312. // presently doing that, although this might improve accuracy as we should
  313. // be able to identify the precise offset of "CHLO" based on header
  314. // values.
  315. if (len(buffer) >= 33 &&
  316. buffer[29] == 'C' &&
  317. buffer[30] == 'H' &&
  318. buffer[31] == 'L' &&
  319. buffer[32] == 'O') ||
  320. (len(buffer) >= 35 &&
  321. buffer[31] == 'C' &&
  322. buffer[32] == 'H' &&
  323. buffer[33] == 'L' &&
  324. buffer[34] == 'O') ||
  325. (len(buffer) >= 38 &&
  326. buffer[34] == 'C' &&
  327. buffer[35] == 'H' &&
  328. buffer[36] == 'L' &&
  329. buffer[37] == 'O') {
  330. return true
  331. }
  332. return false
  333. }