obfuscation_test.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. /*
  2. * Copyright (c) 2023, 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 inproxy
  20. import (
  21. "bytes"
  22. "testing"
  23. "time"
  24. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
  25. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
  26. )
  27. func FuzzSessionPacketDeobfuscation(f *testing.F) {
  28. packet := prng.Padding(100, 1000)
  29. minPadding := 1
  30. maxPadding := 1000
  31. rootSecret, err := GenerateRootObfuscationSecret()
  32. if err != nil {
  33. f.Fatalf(errors.Trace(err).Error())
  34. }
  35. n := 10
  36. originals := make([][]byte, n)
  37. for i := 0; i < n; i++ {
  38. obfuscatedPacket, err := obfuscateSessionPacket(
  39. rootSecret, true, packet, minPadding, maxPadding)
  40. if err != nil {
  41. f.Fatalf(errors.Trace(err).Error())
  42. }
  43. originals[i] = obfuscatedPacket
  44. f.Add(obfuscatedPacket)
  45. }
  46. f.Fuzz(func(t *testing.T, obfuscatedPacket []byte) {
  47. // Make a new history each time to bypass the replay check and focus
  48. // on fuzzing the parsing code.
  49. _, err := deobfuscateSessionPacket(
  50. rootSecret,
  51. false,
  52. newObfuscationReplayHistory(),
  53. obfuscatedPacket)
  54. // Only the original, valid messages should successfully deobfuscate.
  55. inOriginals := false
  56. for i := 0; i < n; i++ {
  57. if bytes.Equal(originals[i], obfuscatedPacket) {
  58. inOriginals = true
  59. break
  60. }
  61. }
  62. if (err == nil) != inOriginals {
  63. f.Errorf("unexpected deobfuscation result")
  64. }
  65. })
  66. }
  67. func TestSessionPacketObfuscation(t *testing.T) {
  68. err := runTestSessionPacketObfuscation()
  69. if err != nil {
  70. t.Errorf(errors.Trace(err).Error())
  71. }
  72. }
  73. func runTestSessionPacketObfuscation() error {
  74. // Use a replay time period factor more suitable for test runs.
  75. originalAntiReplayTimeFactorPeriodSeconds := antiReplayTimeFactorPeriodSeconds
  76. antiReplayTimeFactorPeriodSeconds = 1
  77. defer func() {
  78. antiReplayTimeFactorPeriodSeconds = originalAntiReplayTimeFactorPeriodSeconds
  79. }()
  80. rootSecret, err := GenerateRootObfuscationSecret()
  81. if err != nil {
  82. return errors.Trace(err)
  83. }
  84. replayHistory := newObfuscationReplayHistory()
  85. // Test: obfuscate/deobfuscate initiator -> responder
  86. packet := prng.Bytes(1000)
  87. minPadding := 1
  88. maxPadding := 1000
  89. obfuscatedPacket1, err := obfuscateSessionPacket(
  90. rootSecret, true, packet, minPadding, maxPadding)
  91. if err != nil {
  92. return errors.Trace(err)
  93. }
  94. packet1, err := deobfuscateSessionPacket(
  95. rootSecret, false, replayHistory, obfuscatedPacket1)
  96. if err != nil {
  97. return errors.Trace(err)
  98. }
  99. if !bytes.Equal(packet1, packet) {
  100. return errors.TraceNew("unexpected deobfuscated packet")
  101. }
  102. // Test: replay packet
  103. _, err = deobfuscateSessionPacket(
  104. rootSecret, false, replayHistory, obfuscatedPacket1)
  105. if err == nil {
  106. return errors.TraceNew("unexpected replay success")
  107. }
  108. // Test: replay packet after time factor period
  109. time.Sleep(1 * time.Second)
  110. _, err = deobfuscateSessionPacket(
  111. rootSecret, false, replayHistory, obfuscatedPacket1)
  112. if err == nil {
  113. return errors.TraceNew("unexpected replay success")
  114. }
  115. // Test: different packet sizes (due to padding)
  116. n := 10
  117. for i := 0; i < n; i++ {
  118. obfuscatedPacket2, err := obfuscateSessionPacket(
  119. rootSecret, true, packet, minPadding, maxPadding)
  120. if err != nil {
  121. return errors.Trace(err)
  122. }
  123. if len(obfuscatedPacket1) != len(obfuscatedPacket2) {
  124. break
  125. }
  126. if i == n-1 {
  127. return errors.TraceNew("unexpected same size")
  128. }
  129. }
  130. // Test: obfuscate/deobfuscate responder -> initiator
  131. obfuscatedPacket2, err := obfuscateSessionPacket(
  132. rootSecret, false, packet, minPadding, maxPadding)
  133. if err != nil {
  134. return errors.Trace(err)
  135. }
  136. packet2, err := deobfuscateSessionPacket(
  137. rootSecret, true, nil, obfuscatedPacket2)
  138. if err != nil {
  139. return errors.Trace(err)
  140. }
  141. if !bytes.Equal(packet2, packet) {
  142. return errors.TraceNew("unexpected deobfuscated packet")
  143. }
  144. // Test: initiator -> initiator
  145. obfuscatedPacket1, err = obfuscateSessionPacket(
  146. rootSecret, true, packet, minPadding, maxPadding)
  147. if err != nil {
  148. return errors.Trace(err)
  149. }
  150. _, err = deobfuscateSessionPacket(
  151. rootSecret, true, nil, obfuscatedPacket1)
  152. if err == nil {
  153. return errors.TraceNew("unexpected initiator -> initiator success")
  154. }
  155. // Test: responder -> responder
  156. obfuscatedPacket2, err = obfuscateSessionPacket(
  157. rootSecret, false, packet, minPadding, maxPadding)
  158. if err != nil {
  159. return errors.Trace(err)
  160. }
  161. _, err = deobfuscateSessionPacket(
  162. rootSecret, false, newObfuscationReplayHistory(), obfuscatedPacket2)
  163. if err == nil {
  164. return errors.TraceNew("unexpected initiator -> initiator success")
  165. }
  166. // Test: distinct keys derived for each direction
  167. isInitiator := true
  168. secret1, err := deriveSessionPacketObfuscationSecret(
  169. rootSecret, isInitiator, true)
  170. if err != nil {
  171. return errors.Trace(err)
  172. }
  173. isInitiator = false
  174. secret2, err := deriveSessionPacketObfuscationSecret(
  175. rootSecret, isInitiator, true)
  176. if err != nil {
  177. return errors.Trace(err)
  178. }
  179. err = testMostlyDifferent(secret1[:], secret2[:])
  180. if err != nil {
  181. return errors.Trace(err)
  182. }
  183. // Test: for identical packet with same padding and derived key, most
  184. // bytes different (due to nonce)
  185. padding := 100
  186. obfuscatedPacket1, err = obfuscateSessionPacket(
  187. rootSecret, true, packet, padding, padding)
  188. if err != nil {
  189. return errors.Trace(err)
  190. }
  191. obfuscatedPacket2, err = obfuscateSessionPacket(
  192. rootSecret, false, packet, padding, padding)
  193. if err != nil {
  194. return errors.Trace(err)
  195. }
  196. err = testMostlyDifferent(obfuscatedPacket1, obfuscatedPacket2)
  197. if err != nil {
  198. return errors.Trace(err)
  199. }
  200. // Test: uniformly random
  201. for _, isInitiator := range []bool{true, false} {
  202. err = testEntropy(func() ([]byte, error) {
  203. obfuscatedPacket, err := obfuscateSessionPacket(
  204. rootSecret, isInitiator, packet, padding, padding)
  205. if err != nil {
  206. return nil, errors.Trace(err)
  207. }
  208. return obfuscatedPacket, nil
  209. })
  210. if err != nil {
  211. return errors.Trace(err)
  212. }
  213. }
  214. // Test: wrong obfuscation secret
  215. wrongRootSecret, err := GenerateRootObfuscationSecret()
  216. if err != nil {
  217. return errors.Trace(err)
  218. }
  219. obfuscatedPacket1, err = obfuscateSessionPacket(
  220. wrongRootSecret, true, packet, minPadding, maxPadding)
  221. if err != nil {
  222. return errors.Trace(err)
  223. }
  224. _, err = deobfuscateSessionPacket(
  225. rootSecret, false, newObfuscationReplayHistory(), obfuscatedPacket1)
  226. if err == nil {
  227. return errors.TraceNew("unexpected wrong secret success")
  228. }
  229. // Test: truncated obfuscated packet
  230. obfuscatedPacket1, err = obfuscateSessionPacket(
  231. rootSecret, true, packet, minPadding, maxPadding)
  232. if err != nil {
  233. return errors.Trace(err)
  234. }
  235. obfuscatedPacket1 = obfuscatedPacket1[:len(obfuscatedPacket1)-1]
  236. _, err = deobfuscateSessionPacket(
  237. rootSecret, false, newObfuscationReplayHistory(), obfuscatedPacket1)
  238. if err == nil {
  239. return errors.TraceNew("unexpected truncated packet success")
  240. }
  241. // Test: flip byte
  242. obfuscatedPacket1, err = obfuscateSessionPacket(
  243. rootSecret, true, packet, minPadding, maxPadding)
  244. if err != nil {
  245. return errors.Trace(err)
  246. }
  247. obfuscatedPacket1[len(obfuscatedPacket1)-1] ^= 1
  248. _, err = deobfuscateSessionPacket(
  249. rootSecret, false, newObfuscationReplayHistory(), obfuscatedPacket1)
  250. if err == nil {
  251. return errors.TraceNew("unexpected modified packet success")
  252. }
  253. return nil
  254. }
  255. func TestObfuscationReplayHistory(t *testing.T) {
  256. err := runTestObfuscationReplayHistory()
  257. if err != nil {
  258. t.Errorf(errors.Trace(err).Error())
  259. }
  260. }
  261. func runTestObfuscationReplayHistory() error {
  262. replayHistory := newObfuscationReplayHistory()
  263. size := obfuscationSessionPacketNonceSize
  264. count := int(obfuscationAntiReplayHistorySize / 100)
  265. // Test: values found as expected; no false positives
  266. for i := 0; i < count; i++ {
  267. value := prng.Bytes(size)
  268. if replayHistory.Lookup(value) {
  269. return errors.Tracef("value found on iteration %d", i)
  270. }
  271. err := replayHistory.Insert(value)
  272. if err != nil {
  273. return errors.Trace(err)
  274. }
  275. if !replayHistory.Lookup(value) {
  276. return errors.Tracef("value not found on iteration %d", i)
  277. }
  278. }
  279. return nil
  280. }
  281. func testMostlyDifferent(a, b []byte) error {
  282. if len(a) != len(b) {
  283. return errors.TraceNew("unexpected different size")
  284. }
  285. equalBytes := 0
  286. for i := 0; i < len(a); i++ {
  287. if a[i] == b[i] {
  288. equalBytes += 1
  289. }
  290. }
  291. // TODO: use a stricter threshold?
  292. if equalBytes > len(a)/10 {
  293. return errors.Tracef("unexpected similar bytes: %d/%d", equalBytes, len(a))
  294. }
  295. return nil
  296. }
  297. func testEntropy(f func() ([]byte, error)) error {
  298. bitCount := make(map[int]int)
  299. n := 10000
  300. for i := 0; i < n; i++ {
  301. value, err := f()
  302. if err != nil {
  303. return errors.Trace(err)
  304. }
  305. for j := 0; j < len(value); j++ {
  306. for k := 0; k < 8; k++ {
  307. bit := (uint8(value[j]) >> k) & 0x1
  308. bitCount[(j*8)+k] += int(bit)
  309. }
  310. }
  311. }
  312. // TODO: use a stricter threshold?
  313. for index, count := range bitCount {
  314. if count < n/3 || count > 2*n/3 {
  315. return errors.Tracef("unexpected entropy at %d: %v", index, bitCount)
  316. }
  317. }
  318. return nil
  319. }