padding_obfs4.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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. "io"
  20. "time"
  21. "git.schwanenlied.me/yawning/basket2.git/crypto/rand"
  22. "git.schwanenlied.me/yawning/basket2.git/framing"
  23. "git.schwanenlied.me/yawning/basket2.git/framing/tentp"
  24. "git.schwanenlied.me/yawning/basket2.git/internal/discretedist"
  25. )
  26. const (
  27. // PaddingObfs4Burst is the obfs4 style padding algorithm, approximately
  28. // equivalent to the obfs4 `iat-mode=0` configuration. No timing
  29. // obfuscation is done, and only a minimal amount of padding is
  30. // injected. on a per-burst basis.
  31. PaddingObfs4Burst PaddingMethod = 1
  32. // PaddingObfs4BurstIAT is the obfs4 style padding algorithm,
  33. // approximately equivalent to the obfs4 `iat-mode=1` configuration.
  34. // Randomized delay is inserted after each "burst" except if the padding
  35. // code thinks we are in the middle of a large burst.
  36. PaddingObfs4BurstIAT PaddingMethod = 2
  37. // PaddingObfs4PacketIAT is the obfs4 style padding algorithm
  38. // approximately equivalent to the obfs4 `iat-mode=2` configuration.
  39. // Writes are broken up into random sized packets, and randomized
  40. // delay is inserted unconditionally.
  41. PaddingObfs4PacketIAT PaddingMethod = 3
  42. // Obfs4SeedLength is the length of the randomness to provide to the obfs4
  43. // padding algoriths to parameterize the distributions.
  44. Obfs4SeedLength = 32
  45. )
  46. type obfs4Padding struct {
  47. conn *commonConn
  48. burstDist *discretedist.DiscreteDist
  49. packetDist *discretedist.DiscreteDist
  50. delayDist *discretedist.DiscreteDist
  51. method PaddingMethod
  52. recvBuf bytes.Buffer
  53. }
  54. func (p *obfs4Padding) packetWrite(b []byte) (n int, err error) {
  55. for remaining := len(b); remaining > 0; {
  56. // Sample from the packet distribution, which omits values less than
  57. // the tentp framing+payload overhead.
  58. targetLen := p.packetDist.Sample(p.conn.mRNG)
  59. wrLen := targetLen - (tentp.FramingOverhead + tentp.PayloadOverhead)
  60. padLen := 0
  61. if remaining < wrLen {
  62. padLen = wrLen - remaining
  63. wrLen = remaining
  64. }
  65. if err := p.conn.SendRawRecord(framing.CmdData, b[n:n+wrLen], padLen); err != nil {
  66. return 0, err
  67. }
  68. n += wrLen
  69. remaining -= wrLen
  70. // Always inject a delay here, since discrete packets are wanted.
  71. delay := time.Duration(p.delayDist.Sample(p.conn.mRNG)) * time.Microsecond
  72. time.Sleep(delay)
  73. }
  74. return
  75. }
  76. func (p *obfs4Padding) burstWrite(b []byte) (n int, err error) {
  77. // Because the generic io.Copy() code is used, this gets called with up to
  78. // p.conn.copyBufferSize of data (32 KiB default).
  79. //
  80. // There's an interesting problem in that it *always* will get called with
  81. // the maximum amount of data when doing bulk trasfers.
  82. //
  83. // If I could get Linux-ish TCP_INFO on all platforms, the obvious
  84. // solution would be to packetize things in userland and write based on
  85. // the available buffer size, but alas the *BSDs do not expose sufficient
  86. // information.
  87. //
  88. // TCP_CORK/TCP_NOPUSH would also be an option, but is not portable.
  89. //
  90. // The obfs4 version of this code buffered and sent everything all at
  91. // once, and I'm not sure if that's great because bulk transfers proably
  92. // stood out more (vs packetizing and writing to a connection with Nagle
  93. // enabled).
  94. remaining := len(b)
  95. isLargeWrite := remaining >= p.conn.copyBufferSize
  96. tailTargetLen := p.burstDist.Sample(p.conn.mRNG)
  97. // Write out each frame (with payload).
  98. for remaining > 0 {
  99. wrLen := p.conn.maxRecordSize
  100. padLen := 0
  101. if remaining <= wrLen {
  102. wrLen = remaining
  103. // Append the padding to the last frame.
  104. if tailTargetLen < tentp.FramingOverhead+tentp.PayloadOverhead+wrLen {
  105. // Need to also pad out to a "full" record.
  106. tailTargetLen += p.conn.maxRecordSize - remaining
  107. } else {
  108. // The tail of the burst counts towards part of the
  109. // padding.
  110. tailTargetLen -= tentp.FramingOverhead + tentp.PayloadOverhead + remaining
  111. }
  112. padLen = tailTargetLen
  113. }
  114. if err := p.conn.SendRawRecord(framing.CmdData, b[n:n+wrLen], padLen); err != nil {
  115. return 0, err
  116. }
  117. n += wrLen
  118. remaining -= wrLen
  119. }
  120. // Add a delay sampled from the IAT distribution if we do not suspect that
  121. // further data will be coming shortly.
  122. if p.method == PaddingObfs4BurstIAT && !isLargeWrite {
  123. delay := time.Duration(p.delayDist.Sample(p.conn.mRNG)) * time.Microsecond
  124. time.Sleep(delay)
  125. }
  126. return
  127. }
  128. func (p *obfs4Padding) Write(b []byte) (n int, err error) {
  129. if p.method == PaddingObfs4PacketIAT {
  130. n, err = p.packetWrite(b)
  131. } else {
  132. n, err = p.burstWrite(b)
  133. }
  134. return
  135. }
  136. func (p *obfs4Padding) Read(b []byte) (int, error) {
  137. return paddingImplGenericRead(p.conn, &p.recvBuf, b)
  138. }
  139. func (p *obfs4Padding) OnClose() {
  140. p.recvBuf.Reset()
  141. }
  142. func newObfs4Padding(conn *commonConn, m PaddingMethod, seed []byte) (paddingImpl, error) {
  143. p := new(obfs4Padding)
  144. p.conn = conn
  145. p.method = m
  146. if len(seed) != Obfs4SeedLength {
  147. return nil, ErrInvalidPadding
  148. }
  149. // Initialize the deterministic random number generator and create the
  150. // discrete distributions.
  151. //
  152. // XXX: Cache the distributions? (Should these be biased?)
  153. r := rand.NewDRBG(seed)
  154. p.burstDist = discretedist.NewUniform(r, 1, p.conn.maxRecordSize, 100, false)
  155. p.packetDist = discretedist.NewUniform(r, tentp.FramingOverhead+tentp.PayloadOverhead, p.conn.maxRecordSize, 100, false)
  156. p.delayDist = discretedist.NewUniform(r, 0, 5*1000, 100, false) // 0 to 5 ms.
  157. // Add random [0, 2 * tau) read delay to mask timings on data
  158. // fed to the upstream as well.
  159. p.conn.enableReadDelay = true
  160. if m == PaddingObfs4PacketIAT {
  161. // The packetized padding will never send large writes, and thus
  162. // record size enforcement can be enabled, and we can skip messing
  163. // with Nagle entirely.
  164. p.conn.enforceRecordSize = true
  165. } else {
  166. // There's a fundemental mismatch between what our idea of a packet
  167. // should be and what should be sent over the wire due to
  168. // unavailable/inaccurate PMTU information, and variable length TCP
  169. // headers (SACK options).
  170. //
  171. // So fuck it, enable Nagle's algorithm and hope that it helps to
  172. // mask the disconnect.
  173. conn.setNagle(true)
  174. }
  175. return p, nil
  176. }
  177. func obfs4PaddingDefaultParams(method PaddingMethod) ([]byte, error) {
  178. switch method {
  179. case PaddingObfs4Burst, PaddingObfs4BurstIAT, PaddingObfs4PacketIAT:
  180. seed := make([]byte, Obfs4SeedLength)
  181. if _, err := io.ReadFull(rand.Reader, seed); err != nil {
  182. return nil, err
  183. }
  184. return seed, nil
  185. }
  186. return nil, ErrInvalidPadding
  187. }