obfuscation_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  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. t.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 = 2
  77. defer func() {
  78. antiReplayTimeFactorPeriodSeconds = originalAntiReplayTimeFactorPeriodSeconds
  79. }()
  80. rootSecret, err := GenerateRootObfuscationSecret()
  81. if err != nil {
  82. return errors.Trace(err)
  83. }
  84. initiatorSendSecret, initiatorReceiveSecret, err :=
  85. deriveSessionPacketObfuscationSecrets(rootSecret, true)
  86. if err != nil {
  87. return errors.Trace(err)
  88. }
  89. responderSendSecret, responderReceiveSecret, err :=
  90. deriveSessionPacketObfuscationSecrets(rootSecret, false)
  91. if err != nil {
  92. return errors.Trace(err)
  93. }
  94. replayHistory := newObfuscationReplayHistory()
  95. // Test: obfuscate/deobfuscate initiator -> responder
  96. packet := prng.Bytes(1000)
  97. minPadding := 1
  98. maxPadding := 1000
  99. obfuscatedPacket1, err := obfuscateSessionPacket(
  100. initiatorSendSecret, true, packet, minPadding, maxPadding)
  101. if err != nil {
  102. return errors.Trace(err)
  103. }
  104. packet1, err := deobfuscateSessionPacket(
  105. responderReceiveSecret, false, replayHistory, obfuscatedPacket1)
  106. if err != nil {
  107. return errors.Trace(err)
  108. }
  109. if !bytes.Equal(packet1, packet) {
  110. return errors.TraceNew("unexpected deobfuscated packet")
  111. }
  112. // Test: replay packet
  113. _, err = deobfuscateSessionPacket(
  114. responderReceiveSecret, false, replayHistory, obfuscatedPacket1)
  115. if err == nil {
  116. return errors.TraceNew("unexpected replay success")
  117. }
  118. // Test: replay packet after time factor period
  119. time.Sleep(time.Duration(antiReplayTimeFactorPeriodSeconds) * time.Second)
  120. _, err = deobfuscateSessionPacket(
  121. responderReceiveSecret, false, replayHistory, obfuscatedPacket1)
  122. if err == nil {
  123. return errors.TraceNew("unexpected replay success")
  124. }
  125. // Test: different packet sizes (due to padding)
  126. n := 10
  127. for i := 0; i < n; i++ {
  128. obfuscatedPacket2, err := obfuscateSessionPacket(
  129. initiatorSendSecret, true, packet, minPadding, maxPadding)
  130. if err != nil {
  131. return errors.Trace(err)
  132. }
  133. if len(obfuscatedPacket1) != len(obfuscatedPacket2) {
  134. break
  135. }
  136. if i == n-1 {
  137. return errors.TraceNew("unexpected same size")
  138. }
  139. }
  140. // Test: obfuscate/deobfuscate responder -> initiator
  141. obfuscatedPacket2, err := obfuscateSessionPacket(
  142. responderSendSecret, false, packet, minPadding, maxPadding)
  143. if err != nil {
  144. return errors.Trace(err)
  145. }
  146. packet2, err := deobfuscateSessionPacket(
  147. initiatorReceiveSecret, true, nil, obfuscatedPacket2)
  148. if err != nil {
  149. return errors.Trace(err)
  150. }
  151. if !bytes.Equal(packet2, packet) {
  152. return errors.TraceNew("unexpected deobfuscated packet")
  153. }
  154. // Test: initiator -> initiator
  155. obfuscatedPacket1, err = obfuscateSessionPacket(
  156. initiatorSendSecret, true, packet, minPadding, maxPadding)
  157. if err != nil {
  158. return errors.Trace(err)
  159. }
  160. _, err = deobfuscateSessionPacket(
  161. initiatorReceiveSecret, true, nil, obfuscatedPacket1)
  162. if err == nil {
  163. return errors.TraceNew("unexpected initiator -> initiator success")
  164. }
  165. // Test: responder -> responder
  166. obfuscatedPacket2, err = obfuscateSessionPacket(
  167. responderSendSecret, false, packet, minPadding, maxPadding)
  168. if err != nil {
  169. return errors.Trace(err)
  170. }
  171. _, err = deobfuscateSessionPacket(
  172. responderReceiveSecret, false, newObfuscationReplayHistory(), obfuscatedPacket2)
  173. if err == nil {
  174. return errors.TraceNew("unexpected responder -> responder success")
  175. }
  176. // Test: distinct keys derived for each direction
  177. isInitiator := true
  178. secret1, err := deriveSessionPacketObfuscationSecret(
  179. rootSecret, isInitiator, true)
  180. if err != nil {
  181. return errors.Trace(err)
  182. }
  183. isInitiator = false
  184. secret2, err := deriveSessionPacketObfuscationSecret(
  185. rootSecret, isInitiator, true)
  186. if err != nil {
  187. return errors.Trace(err)
  188. }
  189. err = testMostlyDifferent(secret1[:], secret2[:])
  190. if err != nil {
  191. return errors.Trace(err)
  192. }
  193. // Test: for identical packet with same padding and derived key, most
  194. // bytes different (due to nonce)
  195. padding := 100
  196. obfuscatedPacket1, err = obfuscateSessionPacket(
  197. initiatorSendSecret, true, packet, padding, padding)
  198. if err != nil {
  199. return errors.Trace(err)
  200. }
  201. obfuscatedPacket2, err = obfuscateSessionPacket(
  202. initiatorSendSecret, true, packet, padding, padding)
  203. if err != nil {
  204. return errors.Trace(err)
  205. }
  206. err = testMostlyDifferent(obfuscatedPacket1, obfuscatedPacket2)
  207. if err != nil {
  208. return errors.Trace(err)
  209. }
  210. // Test: uniformly random
  211. for _, isInitiator := range []bool{true, false} {
  212. err = testEntropy(func() ([]byte, error) {
  213. secret := initiatorSendSecret
  214. if !isInitiator {
  215. secret = responderSendSecret
  216. }
  217. obfuscatedPacket, err := obfuscateSessionPacket(
  218. secret, isInitiator, packet, padding, padding)
  219. if err != nil {
  220. return nil, errors.Trace(err)
  221. }
  222. return obfuscatedPacket, nil
  223. })
  224. if err != nil {
  225. return errors.Trace(err)
  226. }
  227. }
  228. // Test: wrong obfuscation secret
  229. wrongRootSecret, err := GenerateRootObfuscationSecret()
  230. if err != nil {
  231. return errors.Trace(err)
  232. }
  233. wrongInitiatorSendSecret, _, err :=
  234. deriveSessionPacketObfuscationSecrets(wrongRootSecret, true)
  235. if err != nil {
  236. return errors.Trace(err)
  237. }
  238. obfuscatedPacket1, err = obfuscateSessionPacket(
  239. wrongInitiatorSendSecret, true, packet, minPadding, maxPadding)
  240. if err != nil {
  241. return errors.Trace(err)
  242. }
  243. _, err = deobfuscateSessionPacket(
  244. responderReceiveSecret, false, newObfuscationReplayHistory(), obfuscatedPacket1)
  245. if err == nil {
  246. return errors.TraceNew("unexpected wrong secret success")
  247. }
  248. // Test: truncated obfuscated packet
  249. obfuscatedPacket1, err = obfuscateSessionPacket(
  250. initiatorSendSecret, true, packet, minPadding, maxPadding)
  251. if err != nil {
  252. return errors.Trace(err)
  253. }
  254. obfuscatedPacket1 = obfuscatedPacket1[:len(obfuscatedPacket1)-1]
  255. _, err = deobfuscateSessionPacket(
  256. responderReceiveSecret, false, newObfuscationReplayHistory(), obfuscatedPacket1)
  257. if err == nil {
  258. return errors.TraceNew("unexpected truncated packet success")
  259. }
  260. // Test: flip byte
  261. obfuscatedPacket1, err = obfuscateSessionPacket(
  262. initiatorSendSecret, true, packet, minPadding, maxPadding)
  263. if err != nil {
  264. return errors.Trace(err)
  265. }
  266. obfuscatedPacket1[len(obfuscatedPacket1)-1] ^= 1
  267. _, err = deobfuscateSessionPacket(
  268. responderReceiveSecret, false, newObfuscationReplayHistory(), obfuscatedPacket1)
  269. if err == nil {
  270. return errors.TraceNew("unexpected modified packet success")
  271. }
  272. return nil
  273. }
  274. func TestObfuscationReplayHistory(t *testing.T) {
  275. err := runTestObfuscationReplayHistory()
  276. if err != nil {
  277. t.Errorf(errors.Trace(err).Error())
  278. }
  279. }
  280. func runTestObfuscationReplayHistory() error {
  281. replayHistory := newObfuscationReplayHistory()
  282. size := obfuscationSessionPacketNonceSize
  283. count := int(obfuscationAntiReplayHistorySize / 100)
  284. // Test: values found as expected; no false positives
  285. for i := 0; i < count; i++ {
  286. value := prng.Bytes(size)
  287. if replayHistory.Lookup(value) {
  288. return errors.Tracef("value found on iteration %d", i)
  289. }
  290. replayHistory.Insert(value)
  291. if !replayHistory.Lookup(value) {
  292. return errors.Tracef("value not found on iteration %d", i)
  293. }
  294. }
  295. return nil
  296. }
  297. func testMostlyDifferent(a, b []byte) error {
  298. if len(a) != len(b) {
  299. return errors.TraceNew("unexpected different size")
  300. }
  301. equalBytes := 0
  302. for i := 0; i < len(a); i++ {
  303. if a[i] == b[i] {
  304. equalBytes += 1
  305. }
  306. }
  307. // TODO: use a stricter threshold?
  308. if equalBytes > len(a)/10 {
  309. return errors.Tracef("unexpected similar bytes: %d/%d", equalBytes, len(a))
  310. }
  311. return nil
  312. }
  313. func testEntropy(f func() ([]byte, error)) error {
  314. bitCount := make(map[int]int)
  315. n := 10000
  316. for i := 0; i < n; i++ {
  317. value, err := f()
  318. if err != nil {
  319. return errors.Trace(err)
  320. }
  321. for j := 0; j < len(value); j++ {
  322. for k := 0; k < 8; k++ {
  323. bit := (uint8(value[j]) >> k) & 0x1
  324. bitCount[(j*8)+k] += int(bit)
  325. }
  326. }
  327. }
  328. // TODO: use a stricter threshold?
  329. for index, count := range bitCount {
  330. if count < n/3 || count > 2*n/3 {
  331. return errors.Tracef("unexpected entropy at %d: %v", index, bitCount)
  332. }
  333. }
  334. return nil
  335. }