serverEntry_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. /*
  2. * Copyright (c) 2015, 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 protocol
  20. import (
  21. "bytes"
  22. "encoding/hex"
  23. "encoding/json"
  24. "strconv"
  25. "testing"
  26. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  27. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
  28. )
  29. const (
  30. _VALID_NORMAL_SERVER_ENTRY = `192.168.0.1 80 <webServerSecret> <webServerCertificate> {"ipAddress":"192.168.0.1","webServerPort":"80","webServerSecret":"<webServerSecret>","webServerCertificate":"<webServerCertificate>","sshPort":22,"sshUsername":"<sshUsername>","sshPassword":"<sshPassword>","sshHostKey":"<sshHostKey>","sshObfuscatedPort":443,"sshObfuscatedKey":"<sshObfuscatedKey>","capabilities":["handshake","SSH","OSSH","VPN"],"region":"CA","meekServerPort":8080,"meekCookieEncryptionPublicKey":"<meekCookieEncryptionPublicKey>","meekObfuscatedKey":"<meekObfuscatedKey>","meekFrontingDomain":"<meekFrontingDomain>","meekFrontingHost":"<meekFrontingHost>"}`
  31. _VALID_BLANK_LEGACY_SERVER_ENTRY = ` {"ipAddress":"192.168.0.1","webServerPort":"80","webServerSecret":"<webServerSecret>","webServerCertificate":"<webServerCertificate>","sshPort":22,"sshUsername":"<sshUsername>","sshPassword":"<sshPassword>","sshHostKey":"<sshHostKey>","sshObfuscatedPort":443,"sshObfuscatedKey":"<sshObfuscatedKey>","capabilities":["handshake","SSH","OSSH","VPN"],"region":"CA","meekServerPort":8080,"meekCookieEncryptionPublicKey":"<meekCookieEncryptionPublicKey>","meekObfuscatedKey":"<meekObfuscatedKey>","meekFrontingDomain":"<meekFrontingDomain>","meekFrontingHost":"<meekFrontingHost>"}`
  32. _VALID_FUTURE_SERVER_ENTRY = `192.168.0.1 80 <webServerSecret> <webServerCertificate> {"ipAddress":"192.168.0.1","webServerPort":"80","webServerSecret":"<webServerSecret>","webServerCertificate":"<webServerCertificate>","sshPort":22,"sshUsername":"<sshUsername>","sshPassword":"<sshPassword>","sshHostKey":"<sshHostKey>","sshObfuscatedPort":443,"sshObfuscatedKey":"<sshObfuscatedKey>","capabilities":["handshake","SSH","OSSH","VPN"],"region":"CA","meekServerPort":8080,"meekCookieEncryptionPublicKey":"<meekCookieEncryptionPublicKey>","meekObfuscatedKey":"<meekObfuscatedKey>","meekFrontingDomain":"<meekFrontingDomain>","meekFrontingHost":"<meekFrontingHost>","dummyFutureField":"dummyFutureField"}`
  33. _INVALID_WINDOWS_REGISTRY_LEGACY_SERVER_ENTRY = `192.168.0.1 80 <webServerSecret> <webServerCertificate> {"sshPort":22,"sshUsername":"<sshUsername>","sshPassword":"<sshPassword>","sshHostKey":"<sshHostKey>","sshObfuscatedPort":443,"sshObfuscatedKey":"<sshObfuscatedKey>","capabilities":["handshake","SSH","OSSH","VPN"],"region":"CA","meekServerPort":8080,"meekCookieEncryptionPublicKey":"<meekCookieEncryptionPublicKey>","meekObfuscatedKey":"<meekObfuscatedKey>","meekFrontingDomain":"<meekFrontingDomain>","meekFrontingHost":"<meekFrontingHost>"}`
  34. _INVALID_MALFORMED_IP_ADDRESS_SERVER_ENTRY = `192.168.0.1 80 <webServerSecret> <webServerCertificate> {"ipAddress":"192.168.0.","webServerPort":"80","webServerSecret":"<webServerSecret>","webServerCertificate":"<webServerCertificate>","sshPort":22,"sshUsername":"<sshUsername>","sshPassword":"<sshPassword>","sshHostKey":"<sshHostKey>","sshObfuscatedPort":443,"sshObfuscatedKey":"<sshObfuscatedKey>","capabilities":["handshake","SSH","OSSH","VPN"],"region":"CA","meekServerPort":8080,"meekCookieEncryptionPublicKey":"<meekCookieEncryptionPublicKey>","meekObfuscatedKey":"<meekObfuscatedKey>","meekFrontingDomain":"<meekFrontingDomain>","meekFrontingHost":"<meekFrontingHost>"}`
  35. _EXPECTED_IP_ADDRESS = `192.168.0.1`
  36. _EXPECTED_DUMMY_FUTURE_FIELD = `dummyFutureField`
  37. )
  38. var testEncodedServerEntryList = hex.EncodeToString([]byte(_VALID_NORMAL_SERVER_ENTRY)) + "\n" +
  39. hex.EncodeToString([]byte(_VALID_BLANK_LEGACY_SERVER_ENTRY)) + "\n" +
  40. hex.EncodeToString([]byte(_VALID_FUTURE_SERVER_ENTRY)) + "\n" +
  41. hex.EncodeToString([]byte(_INVALID_WINDOWS_REGISTRY_LEGACY_SERVER_ENTRY)) + "\n" +
  42. hex.EncodeToString([]byte(_INVALID_MALFORMED_IP_ADDRESS_SERVER_ENTRY))
  43. // DecodeServerEntryList should return 3 valid decoded entries from the input list of 5
  44. func TestDecodeServerEntryList(t *testing.T) {
  45. serverEntries, err := DecodeServerEntryList(
  46. testEncodedServerEntryList, common.GetCurrentTimestamp(), SERVER_ENTRY_SOURCE_EMBEDDED)
  47. if err != nil {
  48. t.Error(err.Error())
  49. t.FailNow()
  50. }
  51. if len(serverEntries) != 3 {
  52. t.Error("unexpected number of valid server entries")
  53. }
  54. numFutureFields := 0
  55. for _, serverEntryFields := range serverEntries {
  56. if serverEntryFields.GetIPAddress() != _EXPECTED_IP_ADDRESS {
  57. t.Errorf("unexpected IP address in decoded server entry: %s", serverEntryFields.GetIPAddress())
  58. }
  59. if futureField, ok := serverEntryFields[_EXPECTED_DUMMY_FUTURE_FIELD]; ok {
  60. if futureFieldStr, ok := futureField.(string); ok && futureFieldStr == _EXPECTED_DUMMY_FUTURE_FIELD {
  61. numFutureFields += 1
  62. }
  63. }
  64. }
  65. if numFutureFields != 1 {
  66. t.Error("unexpected number of retained future fields")
  67. }
  68. }
  69. func TestStreamingServerEntryDecoder(t *testing.T) {
  70. decoder := NewStreamingServerEntryDecoder(
  71. bytes.NewReader([]byte(testEncodedServerEntryList)),
  72. common.GetCurrentTimestamp(), SERVER_ENTRY_SOURCE_EMBEDDED)
  73. serverEntries := make([]ServerEntryFields, 0)
  74. for {
  75. serverEntryFields, err := decoder.Next()
  76. if err != nil {
  77. t.Error(err.Error())
  78. t.FailNow()
  79. }
  80. if serverEntryFields == nil {
  81. break
  82. }
  83. serverEntries = append(serverEntries, serverEntryFields)
  84. }
  85. if len(serverEntries) != 3 {
  86. t.Error("unexpected number of valid server entries")
  87. }
  88. numFutureFields := 0
  89. for _, serverEntryFields := range serverEntries {
  90. if serverEntryFields.GetIPAddress() != _EXPECTED_IP_ADDRESS {
  91. t.Errorf("unexpected IP address in decoded server entry: %s", serverEntryFields.GetIPAddress())
  92. }
  93. if futureField, ok := serverEntryFields[_EXPECTED_DUMMY_FUTURE_FIELD]; ok {
  94. if futureFieldStr, ok := futureField.(string); ok && futureFieldStr == _EXPECTED_DUMMY_FUTURE_FIELD {
  95. numFutureFields += 1
  96. }
  97. }
  98. }
  99. if numFutureFields != 1 {
  100. t.Error("unexpected number of retained future fields")
  101. }
  102. }
  103. // Directly call DecodeServerEntryFields and ValidateServerEntry with invalid inputs
  104. func TestInvalidServerEntries(t *testing.T) {
  105. testCases := [2]string{_INVALID_WINDOWS_REGISTRY_LEGACY_SERVER_ENTRY, _INVALID_MALFORMED_IP_ADDRESS_SERVER_ENTRY}
  106. for _, testCase := range testCases {
  107. encodedServerEntry := hex.EncodeToString([]byte(testCase))
  108. serverEntryFields, err := DecodeServerEntryFields(
  109. encodedServerEntry, common.GetCurrentTimestamp(), SERVER_ENTRY_SOURCE_EMBEDDED)
  110. if err != nil {
  111. t.Error(err.Error())
  112. }
  113. err = ValidateServerEntryFields(serverEntryFields)
  114. if err == nil {
  115. t.Errorf("server entry should not validate: %s", testCase)
  116. }
  117. }
  118. }
  119. // Directly call DecodeServerEntry
  120. func TestDecodeServerEntryStruct(t *testing.T) {
  121. serverEntry, err := DecodeServerEntry(
  122. hex.EncodeToString([]byte(_VALID_NORMAL_SERVER_ENTRY)),
  123. common.GetCurrentTimestamp(), SERVER_ENTRY_SOURCE_EMBEDDED)
  124. if err != nil {
  125. t.Error(err.Error())
  126. t.FailNow()
  127. }
  128. if serverEntry.IpAddress != _EXPECTED_IP_ADDRESS {
  129. t.Errorf("unexpected IP address in decoded server entry: %s", serverEntry.IpAddress)
  130. }
  131. }
  132. func TestServerEntryListSignatures(t *testing.T) {
  133. testServerEntryListSignatures(t, true)
  134. testServerEntryListSignatures(t, false)
  135. }
  136. func testServerEntryListSignatures(t *testing.T, setExplicitTag bool) {
  137. publicKey, privateKey, err := NewServerEntrySignatureKeyPair()
  138. if err != nil {
  139. t.Fatalf("NewServerEntrySignatureKeyPair failed: %s", err)
  140. }
  141. n := 16
  142. serverEntry := &ServerEntry{
  143. IpAddress: prng.HexString(n),
  144. WebServerPort: strconv.Itoa(prng.Intn(n)),
  145. WebServerSecret: prng.HexString(n),
  146. WebServerCertificate: prng.HexString(n),
  147. SshPort: prng.Intn(n),
  148. SshUsername: prng.HexString(n),
  149. SshPassword: prng.HexString(n),
  150. SshHostKey: prng.HexString(n),
  151. SshObfuscatedPort: prng.Intn(n),
  152. SshObfuscatedQUICPort: prng.Intn(n),
  153. SshObfuscatedTapDancePort: prng.Intn(n),
  154. SshObfuscatedConjurePort: prng.Intn(n),
  155. SshObfuscatedKey: prng.HexString(n),
  156. Capabilities: []string{prng.HexString(n)},
  157. Region: prng.HexString(n),
  158. MeekServerPort: prng.Intn(n),
  159. MeekCookieEncryptionPublicKey: prng.HexString(n),
  160. MeekObfuscatedKey: prng.HexString(n),
  161. MeekFrontingHost: prng.HexString(n),
  162. MeekFrontingHosts: []string{prng.HexString(n)},
  163. MeekFrontingDomain: prng.HexString(n),
  164. MeekFrontingAddresses: []string{prng.HexString(n)},
  165. MeekFrontingAddressesRegex: prng.HexString(n),
  166. MeekFrontingDisableSNI: false,
  167. TacticsRequestPublicKey: prng.HexString(n),
  168. TacticsRequestObfuscatedKey: prng.HexString(n),
  169. ConfigurationVersion: 1,
  170. }
  171. if setExplicitTag {
  172. serverEntry.Tag = prng.HexString(n)
  173. }
  174. // Convert ServerEntry to ServerEntryFields
  175. marshaledServerEntry, err := json.Marshal(serverEntry)
  176. if err != nil {
  177. t.Fatalf("Marshal failed: %s", err)
  178. }
  179. var serverEntryFields ServerEntryFields
  180. err = json.Unmarshal(marshaledServerEntry, &serverEntryFields)
  181. if err != nil {
  182. t.Fatalf("Unmarshal failed: %s", err)
  183. }
  184. // Check that local fields are ignored in the signature
  185. if !setExplicitTag {
  186. serverEntryFields.SetTag(prng.HexString(n))
  187. }
  188. serverEntryFields.SetLocalSource(prng.HexString(n))
  189. serverEntryFields.SetLocalTimestamp(prng.HexString(n))
  190. // Set dummy signature to check that its overwritten
  191. serverEntryFields["signature"] = prng.HexString(n)
  192. err = serverEntryFields.AddSignature(publicKey, privateKey)
  193. if err != nil {
  194. t.Fatalf("AddSignature failed: %s", err)
  195. }
  196. err = serverEntryFields.VerifySignature(publicKey)
  197. if err != nil {
  198. t.Fatalf("VerifySignature failed: %s", err)
  199. }
  200. // A 2nd VerifySignature call checks that the first VerifySignature
  201. // call leaves the server entry fields intact
  202. err = serverEntryFields.VerifySignature(publicKey)
  203. if err != nil {
  204. t.Fatalf("VerifySignature failed: %s", err)
  205. }
  206. // Modify local local fields and check that signature remains valid
  207. if !setExplicitTag {
  208. serverEntryFields.SetTag(prng.HexString(n))
  209. }
  210. serverEntryFields.SetLocalSource(prng.HexString(n))
  211. serverEntryFields.SetLocalTimestamp(prng.HexString(n))
  212. err = serverEntryFields.VerifySignature(publicKey)
  213. if err != nil {
  214. t.Fatalf("VerifySignature failed: %s", err)
  215. }
  216. // Check that verification fails when using the wrong public key
  217. incorrectPublicKey, _, err := NewServerEntrySignatureKeyPair()
  218. if err != nil {
  219. t.Fatalf("NewServerEntrySignatureKeyPair failed: %s", err)
  220. }
  221. err = serverEntryFields.VerifySignature(incorrectPublicKey)
  222. if err == nil {
  223. t.Fatalf("VerifySignature unexpectedly succeeded")
  224. }
  225. // Check that an expected, non-local field causes verification to fail
  226. serverEntryFields[prng.HexString(n)] = prng.HexString(n)
  227. err = serverEntryFields.VerifySignature(publicKey)
  228. if err == nil {
  229. t.Fatalf("AddSignature unexpectedly succeeded")
  230. }
  231. // Check that modifying a signed field causes verification to fail
  232. fieldName := "sshObfuscatedKey"
  233. if setExplicitTag {
  234. fieldName = "tag"
  235. }
  236. serverEntryFields[fieldName] = prng.HexString(n)
  237. err = serverEntryFields.VerifySignature(publicKey)
  238. if err == nil {
  239. t.Fatalf("AddSignature unexpectedly succeeded")
  240. }
  241. }
  242. func TestIsValidInproxyDialAddress(t *testing.T) {
  243. serverEntry := &ServerEntry{
  244. IpAddress: "192.168.0.1",
  245. InproxySSHPort: 1,
  246. InproxyOSSHPort: 2,
  247. InproxyQUICPort: 3,
  248. Capabilities: []string{
  249. "handshake",
  250. "INPROXY-WEBRTC-SSH",
  251. "INPROXY-WEBRTC-OSSH",
  252. "INPROXY-WEBRTC-QUIC-OSSH",
  253. "INPROXY-WEBRTC-FRONTED-MEEK-OSSH",
  254. },
  255. MeekFrontingAddressesRegex: "[ab]+",
  256. MeekServerPort: 443,
  257. }
  258. testCases := []struct {
  259. description string
  260. networkProtocol string
  261. dialHost string
  262. dialPortNumber int
  263. isValid bool
  264. }{
  265. {
  266. "valid IP dial",
  267. "tcp", "192.168.0.1", 1,
  268. true,
  269. },
  270. {
  271. "valid domain dial",
  272. "tcp", "aaabbbaaabbb", 443,
  273. true,
  274. },
  275. {
  276. "valid UDP dial",
  277. "udp", "192.168.0.1", 3,
  278. true,
  279. },
  280. {
  281. "invalid network dial",
  282. "udp", "192.168.0.1", 1,
  283. false,
  284. },
  285. {
  286. "invalid IP dial",
  287. "tcp", "192.168.0.2", 1,
  288. false,
  289. },
  290. {
  291. "invalid domain dial",
  292. "tcp", "aaabbbcccbbb", 443,
  293. false,
  294. },
  295. {
  296. "invalid port dial",
  297. "tcp", "192.168.0.1", 4,
  298. false,
  299. },
  300. {
  301. "invalid domain port dial",
  302. "tcp", "aaabbbaaabbb", 80,
  303. false,
  304. },
  305. {
  306. "invalid domain newline dial",
  307. "tcp", "aaabbbaaabbb\nccc", 443,
  308. false,
  309. },
  310. }
  311. for _, testCase := range testCases {
  312. t.Run(testCase.description, func(t *testing.T) {
  313. if testCase.isValid != serverEntry.IsValidInproxyDialAddress(
  314. testCase.networkProtocol, testCase.dialHost, testCase.dialPortNumber) {
  315. t.Errorf("unexpected IsValidInproxyDialAddress result")
  316. }
  317. })
  318. }
  319. }