github.com/Psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/protocol/serverEntry_test.go (about)

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