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  }