padding_obfs4.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  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 (c *obfs4Padding) shortWrite(p []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. c.conn.setNagle(false)
  58. defer c.conn.setNagle(true)
  59. remaining := len(p)
  60. for remaining > 0 {
  61. // Sample from the "short" distribution, which omits values less than
  62. // the tentp framing overhead.
  63. targetLen := c.shortDist.Sample(c.conn.mRNG)
  64. wrLen := targetLen - tentp.FramingOverhead
  65. padLen := 0
  66. if remaining < wrLen {
  67. padLen = wrLen - remaining
  68. wrLen = remaining
  69. }
  70. if err := c.conn.SendRawRecord(framing.CmdData, p[n:n+wrLen], padLen); err != nil {
  71. return 0, err
  72. }
  73. n += wrLen
  74. remaining -= wrLen
  75. // Always inject a delay here, since discrete packets are wanted.
  76. delay := time.Duration(c.delayDist.Sample(c.conn.mRNG)) * time.Microsecond
  77. time.Sleep(delay)
  78. }
  79. return
  80. }
  81. func (c *obfs4Padding) largeWrite(p []byte) (n int, err error) {
  82. // Because the generic io.Copy() code is used, this gets called with up to
  83. // 32 kib of data.
  84. //
  85. // There's an interesting problem in that it *always* will get called with
  86. // 32 kib of data when doing bulk trasfers.
  87. //
  88. // If I could get Linux-ish TCP_INFO on all platforms, the obvious
  89. // solution would be to packetize things in userland and write based on
  90. // the available buffer size, but alas the *BSDs do not expose sufficient
  91. // information.
  92. //
  93. // TCP_CORK/TCP_NOPUSH would also be an option, but is not portable.
  94. //
  95. // The obfs4 version of this code buffered and sent everything all at
  96. // once, and I'm not sure if that's great because bulk transfers proably
  97. // stood out more (vs packetizing and writing to a connection with Nagel
  98. // enabled).
  99. remaining := len(p)
  100. isLargeWrite := remaining >= 32*1024 // XXX: What about CopyBuffer?
  101. tailPadLen := c.burstDist.Sample(c.conn.mRNG)
  102. // tailPadLen += c.conn.maxRecordSize * c.conn.mRNG.Intn(3)
  103. // Write out each frame (with payload).
  104. for remaining > 0 {
  105. wrLen := c.conn.maxRecordSize
  106. padLen := 0
  107. if remaining <= wrLen {
  108. // Append the padding to the last frame.
  109. if tailPadLen < tentp.FramingOverhead+wrLen {
  110. // Need to also pad out to a "full" record.
  111. tailPadLen += wrLen - remaining
  112. } else {
  113. // The tail of the burst counts towards part of the
  114. // padding.
  115. tailPadLen -= tentp.FramingOverhead + remaining
  116. }
  117. padLen = tailPadLen
  118. wrLen = remaining
  119. }
  120. if err := c.conn.SendRawRecord(framing.CmdData, p[n:n+wrLen], padLen); err != nil {
  121. return 0, err
  122. }
  123. n += wrLen
  124. remaining -= wrLen
  125. }
  126. // Add a delay sampled from the IAT distribution if we do not suspect that
  127. // further data will be coming shortly.
  128. if c.method == PaddingObfs4BurstIAT && !isLargeWrite {
  129. delay := time.Duration(c.delayDist.Sample(c.conn.mRNG)) * time.Microsecond
  130. time.Sleep(delay)
  131. }
  132. return
  133. }
  134. func (c *obfs4Padding) Write(p []byte) (n int, err error) {
  135. if len(p) > c.conn.maxRecordSize {
  136. n, err = c.shortWrite(p)
  137. } else {
  138. n, err = c.largeWrite(p)
  139. }
  140. return
  141. }
  142. func (c *obfs4Padding) Read(p []byte) (int, error) {
  143. return paddingImplGenericRead(c.conn, &c.recvBuf, p)
  144. }
  145. func (c *obfs4Padding) OnClose() {
  146. c.recvBuf.Reset()
  147. }
  148. func newObfs4Padding(conn *commonConn, m PaddingMethod, seed []byte) (paddingImpl, error) {
  149. c := new(obfs4Padding)
  150. c.conn = conn
  151. c.method = m
  152. if len(seed) != Obfs4SeedLength {
  153. return nil, ErrInvalidPadding
  154. }
  155. // Initialize the deterministic random number generator and create the
  156. // discrete distributions.
  157. //
  158. // XXX: Cache the distributions? (Should these be biased?)
  159. r := rand.NewDRBG(seed)
  160. c.burstDist = discretedist.NewUniform(r, 1, c.conn.maxRecordSize, 100, false)
  161. c.shortDist = discretedist.NewUniform(r, tentp.FramingOverhead, c.conn.maxRecordSize, 100, false)
  162. // IAT delay dist between 0 to 25 ms.
  163. // Note: This is always needed due to the short write obfsucation strategy.
  164. c.delayDist = discretedist.NewUniform(r, 0, 5*1000, 100, false)
  165. if !c.conn.isClient {
  166. // Add random [0, 2 * tau) read delay to mask timings on data
  167. // fed to the upstream as well.
  168. c.conn.enableReadDelay = true
  169. }
  170. // There's a fundemental mismatch between what our idea of a packet should
  171. // be and what should be sent over the wire due to unavailable/inaccurate
  172. // PMTU information, and variable length TCP headers (SACK options).
  173. //
  174. // So fuck it, enable Nagle's algorithm and hope that it helps to mask the
  175. // disconnect.
  176. conn.setNagle(true)
  177. return c, nil
  178. }