padding_obfs4.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. // padding_obfs4.go - Obfs4 padding implementation.
  2. // Copyright (C) 2016 Yawning Angel.
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Affero General Public License as
  6. // published by the Free Software Foundation, either version 3 of the
  7. // License, or (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Affero General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Affero General Public License
  15. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. package basket2
  17. import (
  18. "bytes"
  19. "time"
  20. "git.schwanenlied.me/yawning/basket2.git/crypto/rand"
  21. "git.schwanenlied.me/yawning/basket2.git/framing"
  22. "git.schwanenlied.me/yawning/basket2.git/framing/tentp"
  23. "git.schwanenlied.me/yawning/basket2.git/internal/discretedist"
  24. )
  25. const (
  26. // PaddingObfs4Burst is the obfs4 style padding algorithm, approximately
  27. // equivalent to the obfs4 `iat-mode=0` configuration. No timing
  28. // obfuscation is done, and only a minimal amount of padding is
  29. // injected. on a per-burst basis.
  30. PaddingObfs4Burst PaddingMethod = 1
  31. // PaddingObfs4BurstIAT is the obfs4 style padding algorithm,
  32. // approximately equivalent to the obfs4 `iat-mode=1` configuration.
  33. // Randomized delay is inserted after each "burst" except if the padding
  34. // code thinks we are in the middle of a large burst.
  35. PaddingObfs4BurstIAT PaddingMethod = 2
  36. // Obfs4SeedLength is the length of the randomness to provide to the obfs4
  37. // padding algoriths to parameterize the distributions.
  38. Obfs4SeedLength = 32
  39. )
  40. type obfs4Padding struct {
  41. conn *commonConn
  42. burstDist *discretedist.DiscreteDist
  43. shortDist *discretedist.DiscreteDist
  44. delayDist *discretedist.DiscreteDist
  45. method PaddingMethod
  46. recvBuf bytes.Buffer
  47. }
  48. func (p *obfs4Padding) shortWrite(b []byte) (n int, err error) {
  49. // Special case len(p) being "short".
  50. //
  51. // This is kind of annoying to obfuscate, since sending 2 segments isn't
  52. // that different from sending 1 segment, and I assume the forces of evil
  53. // know how to count.
  54. //
  55. // So, attempt to be somewhat clever by disabling Nagle and sending short
  56. // records sized to something from the distribution.
  57. p.conn.setNagle(false)
  58. defer p.conn.setNagle(true)
  59. for remaining := len(b); remaining > 0; {
  60. // Sample from the "short" distribution, which omits values less than
  61. // the tentp framing+payload overhead.
  62. targetLen := p.shortDist.Sample(p.conn.mRNG)
  63. wrLen := targetLen - (tentp.FramingOverhead + tentp.PayloadOverhead)
  64. padLen := 0
  65. if remaining < wrLen {
  66. padLen = wrLen - remaining
  67. wrLen = remaining
  68. }
  69. if err := p.conn.SendRawRecord(framing.CmdData, b[n:n+wrLen], padLen); err != nil {
  70. return 0, err
  71. }
  72. n += wrLen
  73. remaining -= wrLen
  74. // Always inject a delay here, since discrete packets are wanted.
  75. delay := time.Duration(p.delayDist.Sample(p.conn.mRNG)) * time.Microsecond
  76. time.Sleep(delay)
  77. }
  78. return
  79. }
  80. func (p *obfs4Padding) largeWrite(b []byte) (n int, err error) {
  81. // Because the generic io.Copy() code is used, this gets called with up to
  82. // 32 kib of data.
  83. //
  84. // There's an interesting problem in that it *always* will get called with
  85. // 32 kib of data when doing bulk trasfers.
  86. //
  87. // If I could get Linux-ish TCP_INFO on all platforms, the obvious
  88. // solution would be to packetize things in userland and write based on
  89. // the available buffer size, but alas the *BSDs do not expose sufficient
  90. // information.
  91. //
  92. // TCP_CORK/TCP_NOPUSH would also be an option, but is not portable.
  93. //
  94. // The obfs4 version of this code buffered and sent everything all at
  95. // once, and I'm not sure if that's great because bulk transfers proably
  96. // stood out more (vs packetizing and writing to a connection with Nagel
  97. // enabled).
  98. remaining := len(b)
  99. isLargeWrite := remaining >= p.conn.copyBufferSize
  100. tailPadLen := p.burstDist.Sample(p.conn.mRNG)
  101. // tailPadLen += c.conn.maxRecordSize * c.conn.mRNG.Intn(3)
  102. // Write out each frame (with payload).
  103. for remaining > 0 {
  104. wrLen := p.conn.maxRecordSize
  105. padLen := 0
  106. if remaining <= wrLen {
  107. // Append the padding to the last frame.
  108. if tailPadLen < tentp.FramingOverhead+tentp.PayloadOverhead+wrLen {
  109. // Need to also pad out to a "full" record.
  110. tailPadLen += wrLen - remaining
  111. } else {
  112. // The tail of the burst counts towards part of the
  113. // padding.
  114. tailPadLen -= tentp.FramingOverhead + tentp.PayloadOverhead + remaining
  115. }
  116. padLen = tailPadLen
  117. wrLen = remaining
  118. }
  119. if err := p.conn.SendRawRecord(framing.CmdData, b[n:n+wrLen], padLen); err != nil {
  120. return 0, err
  121. }
  122. n += wrLen
  123. remaining -= wrLen
  124. }
  125. // Add a delay sampled from the IAT distribution if we do not suspect that
  126. // further data will be coming shortly.
  127. if p.method == PaddingObfs4BurstIAT && !isLargeWrite {
  128. delay := time.Duration(p.delayDist.Sample(p.conn.mRNG)) * time.Microsecond
  129. time.Sleep(delay)
  130. }
  131. return
  132. }
  133. func (p *obfs4Padding) Write(b []byte) (n int, err error) {
  134. if len(b) > p.conn.maxRecordSize {
  135. n, err = p.shortWrite(b)
  136. } else {
  137. n, err = p.largeWrite(b)
  138. }
  139. return
  140. }
  141. func (p *obfs4Padding) Read(b []byte) (int, error) {
  142. return paddingImplGenericRead(p.conn, &p.recvBuf, b)
  143. }
  144. func (p *obfs4Padding) OnClose() {
  145. p.recvBuf.Reset()
  146. }
  147. func newObfs4Padding(conn *commonConn, m PaddingMethod, seed []byte) (paddingImpl, error) {
  148. p := new(obfs4Padding)
  149. p.conn = conn
  150. p.method = m
  151. if len(seed) != Obfs4SeedLength {
  152. return nil, ErrInvalidPadding
  153. }
  154. // Initialize the deterministic random number generator and create the
  155. // discrete distributions.
  156. //
  157. // XXX: Cache the distributions? (Should these be biased?)
  158. r := rand.NewDRBG(seed)
  159. p.burstDist = discretedist.NewUniform(r, 1, p.conn.maxRecordSize, 100, false)
  160. p.shortDist = discretedist.NewUniform(r, tentp.FramingOverhead+tentp.PayloadOverhead, p.conn.maxRecordSize, 100, false)
  161. // IAT delay dist between 0 to 25 ms.
  162. // Note: This is always needed due to the short write obfsucation strategy.
  163. p.delayDist = discretedist.NewUniform(r, 0, 5*1000, 100, false)
  164. if !p.conn.isClient {
  165. // Add random [0, 2 * tau) read delay to mask timings on data
  166. // fed to the upstream as well.
  167. p.conn.enableReadDelay = true
  168. }
  169. // There's a fundemental mismatch between what our idea of a packet should
  170. // be and what should be sent over the wire due to unavailable/inaccurate
  171. // PMTU information, and variable length TCP headers (SACK options).
  172. //
  173. // So fuck it, enable Nagle's algorithm and hope that it helps to mask the
  174. // disconnect.
  175. conn.setNagle(true)
  176. return p, nil
  177. }