github.com/psiphon-labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/obfuscator/obfuscator_test.go (about) 1 /* 2 * Copyright (c) 2016, 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 20 package obfuscator 21 22 import ( 23 "bytes" 24 "crypto/rand" 25 "crypto/rsa" 26 "errors" 27 "net" 28 "testing" 29 "time" 30 31 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common" 32 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/ssh" 33 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng" 34 ) 35 36 func TestObfuscator(t *testing.T) { 37 38 keyword := prng.HexString(32) 39 40 maxPadding := 256 41 42 paddingPRNGSeed, err := prng.NewSeed() 43 if err != nil { 44 t.Fatalf("prng.NewSeed failed: %s", err) 45 } 46 47 var irregularLogFields common.LogFields 48 49 config := &ObfuscatorConfig{ 50 Keyword: keyword, 51 MaxPadding: &maxPadding, 52 PaddingPRNGSeed: paddingPRNGSeed, 53 SeedHistory: NewSeedHistory(&SeedHistoryConfig{ClientIPTTL: 500 * time.Millisecond}), 54 IrregularLogger: func(_ string, err error, logFields common.LogFields) { 55 if logFields == nil { 56 logFields = make(common.LogFields) 57 } 58 logFields["tunnel_error"] = err.Error() 59 irregularLogFields = logFields 60 t.Logf("IrregularLogger: %+v", logFields) 61 }, 62 } 63 64 client, err := NewClientObfuscator(config) 65 if err != nil { 66 t.Fatalf("NewClientObfuscator failed: %s", err) 67 } 68 69 seedMessage := client.SendSeedMessage() 70 71 server, err := NewServerObfuscator(config, "", bytes.NewReader(seedMessage)) 72 if err != nil { 73 t.Fatalf("NewServerObfuscator failed: %s", err) 74 } 75 76 clientMessage := []byte("client hello") 77 78 b := append([]byte(nil), clientMessage...) 79 client.ObfuscateClientToServer(b) 80 server.ObfuscateClientToServer(b) 81 82 if !bytes.Equal(clientMessage, b) { 83 t.Fatalf("unexpected client message") 84 } 85 86 serverMessage := []byte("server hello") 87 88 b = append([]byte(nil), serverMessage...) 89 client.ObfuscateServerToClient(b) 90 server.ObfuscateServerToClient(b) 91 92 if !bytes.Equal(serverMessage, b) { 93 t.Fatalf("unexpected client message") 94 } 95 96 // Test: duplicate obfuscation seed cases 97 98 client, err = NewClientObfuscator(config) 99 if err != nil { 100 t.Fatalf("NewClientObfuscator failed: %s", err) 101 } 102 103 seedMessage = client.SendSeedMessage() 104 105 clientIP := "192.168.0.1" 106 107 _, err = NewServerObfuscator(config, clientIP, bytes.NewReader(seedMessage)) 108 if err != nil { 109 t.Fatalf("NewServerObfuscator failed: %s", err) 110 } 111 112 irregularLogFields = nil 113 114 _, err = NewServerObfuscator(config, clientIP, bytes.NewReader(seedMessage)) 115 if err != nil { 116 t.Fatalf("NewServerObfuscator failed: %s", err) 117 } 118 119 duplicateClientID := irregularLogFields["duplicate_client_ip"] 120 if duplicateClientID != "equal" { 121 t.Fatalf("Unexpected duplicate_client_ip: %s", duplicateClientID) 122 } 123 124 irregularLogFields = nil 125 126 _, err = NewServerObfuscator(config, "192.168.0.2", bytes.NewReader(seedMessage)) 127 if err == nil { 128 t.Fatalf("NewServerObfuscator unexpectedly succeeded") 129 } 130 131 duplicateClientID = irregularLogFields["duplicate_client_ip"] 132 if duplicateClientID != "unequal" { 133 t.Fatalf("Unexpected duplicate_client_ip: %s", duplicateClientID) 134 } 135 136 time.Sleep(600 * time.Millisecond) 137 138 irregularLogFields = nil 139 140 _, err = NewServerObfuscator(config, clientIP, bytes.NewReader(seedMessage)) 141 if err == nil { 142 t.Fatalf("NewServerObfuscator unexpectedly succeeded") 143 } 144 145 duplicateClientID = irregularLogFields["duplicate_client_ip"] 146 if duplicateClientID != "unknown" { 147 t.Fatalf("Unexpected duplicate_client_ip: %s", duplicateClientID) 148 } 149 } 150 151 func TestObfuscatedSSHConn(t *testing.T) { 152 153 keyword := prng.HexString(32) 154 155 serverAddress := "127.0.0.1:2222" 156 157 listener, err := net.Listen("tcp", serverAddress) 158 if err != nil { 159 t.Fatalf("Listen failed: %s", err) 160 } 161 162 rsaKey, err := rsa.GenerateKey(rand.Reader, 2048) 163 if err != nil { 164 t.Fatalf("GenerateKey failed: %s", err) 165 } 166 167 hostKey, err := ssh.NewSignerFromKey(rsaKey) 168 if err != nil { 169 t.Fatalf("NewSignerFromKey failed: %s", err) 170 } 171 172 sshCertChecker := &ssh.CertChecker{ 173 HostKeyFallback: func(addr string, remote net.Addr, publicKey ssh.PublicKey) error { 174 if !bytes.Equal(hostKey.PublicKey().Marshal(), publicKey.Marshal()) { 175 return errors.New("unexpected host public key") 176 } 177 return nil 178 }, 179 } 180 181 result := make(chan error, 1) 182 183 go func() { 184 185 conn, err := listener.Accept() 186 187 if err == nil { 188 conn, err = NewServerObfuscatedSSHConn( 189 conn, 190 keyword, 191 NewSeedHistory(nil), 192 func(_ string, err error, logFields common.LogFields) { 193 t.Logf("IrregularLogger: %s %+v", err, logFields) 194 }) 195 } 196 197 if err == nil { 198 config := &ssh.ServerConfig{ 199 NoClientAuth: true, 200 } 201 config.AddHostKey(hostKey) 202 203 _, _, _, err = ssh.NewServerConn(conn, config) 204 } 205 206 if err != nil { 207 select { 208 case result <- err: 209 default: 210 } 211 } 212 }() 213 214 go func() { 215 216 conn, err := net.DialTimeout("tcp", serverAddress, 5*time.Second) 217 218 var paddingPRNGSeed *prng.Seed 219 if err == nil { 220 paddingPRNGSeed, err = prng.NewSeed() 221 } 222 223 if err == nil { 224 conn, err = NewClientObfuscatedSSHConn( 225 conn, 226 keyword, 227 paddingPRNGSeed, 228 nil, nil) 229 } 230 231 var KEXPRNGSeed *prng.Seed 232 if err == nil { 233 KEXPRNGSeed, err = prng.NewSeed() 234 } 235 236 if err == nil { 237 config := &ssh.ClientConfig{ 238 HostKeyCallback: sshCertChecker.CheckHostKey, 239 } 240 config.KEXPRNGSeed = KEXPRNGSeed 241 _, _, _, err = ssh.NewClientConn(conn, "", config) 242 } 243 244 // Sends nil on success 245 select { 246 case result <- err: 247 default: 248 } 249 }() 250 251 err = <-result 252 if err != nil { 253 t.Fatalf("obfuscated SSH handshake failed: %s", err) 254 } 255 }