github.com/Psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/protocol/customTLSProfiles_test.go (about) 1 /* 2 * Copyright (c) 2019, 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 "crypto/sha256" 25 "encoding/json" 26 "testing" 27 28 utls "github.com/Psiphon-Labs/utls" 29 ) 30 31 func TestCustomTLSProfiles(t *testing.T) { 32 33 // Based on utls.HelloChrome_62. Some attributes have been removed to 34 // eliminate randomness; and additional extensions have been added for extra 35 // test coverage. 36 37 utlsClientHelloSpec := &utls.ClientHelloSpec{ 38 TLSVersMax: utls.VersionTLS12, 39 TLSVersMin: utls.VersionTLS10, 40 CipherSuites: []uint16{ 41 utls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 42 utls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 43 utls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 44 utls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 45 utls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 46 utls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 47 utls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 48 utls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 49 utls.TLS_RSA_WITH_AES_128_GCM_SHA256, 50 utls.TLS_RSA_WITH_AES_256_GCM_SHA384, 51 utls.TLS_RSA_WITH_AES_128_CBC_SHA, 52 utls.TLS_RSA_WITH_AES_256_CBC_SHA, 53 utls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, 54 }, 55 CompressionMethods: []byte{0}, 56 Extensions: []utls.TLSExtension{ 57 &utls.RenegotiationInfoExtension{Renegotiation: utls.RenegotiateOnceAsClient}, 58 &utls.SNIExtension{}, 59 &utls.UtlsExtendedMasterSecretExtension{}, 60 &utls.SessionTicketExtension{}, 61 &utls.SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []utls.SignatureScheme{ 62 utls.ECDSAWithP256AndSHA256, 63 utls.PSSWithSHA256, 64 utls.PKCS1WithSHA256, 65 utls.ECDSAWithP384AndSHA384, 66 utls.PSSWithSHA384, 67 utls.PKCS1WithSHA384, 68 utls.PSSWithSHA512, 69 utls.PKCS1WithSHA512, 70 utls.PKCS1WithSHA1}, 71 }, 72 &utls.StatusRequestExtension{}, 73 &utls.SCTExtension{}, 74 &utls.ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}}, 75 &utls.FakeChannelIDExtension{}, 76 &utls.SupportedPointsExtension{SupportedPoints: []byte{0}}, 77 &utls.SupportedCurvesExtension{[]utls.CurveID{ 78 utls.X25519, utls.CurveP256, utls.CurveP384}}, 79 &utls.UtlsPaddingExtension{GetPaddingLen: utls.BoringPaddingStyle}, 80 81 // Additional extensions for test coverage 82 &utls.NPNExtension{NextProtos: []string{"http/1.1"}}, 83 &utls.GenericExtension{Id: 9999, Data: []byte("generic extension")}, 84 &utls.KeyShareExtension{[]utls.KeyShare{ 85 {Group: utls.X25519, Data: []byte{9, 9, 9, 9}}, 86 }}, 87 &utls.PSKKeyExchangeModesExtension{[]uint8{ 88 utls.PskModeDHE, 89 }}, 90 &utls.SupportedVersionsExtension{[]uint16{ 91 utls.VersionTLS13, 92 utls.VersionTLS12, 93 utls.VersionTLS11, 94 utls.VersionTLS10, 95 }}, 96 &utls.UtlsCompressCertExtension{[]utls.CertCompressionAlgo{ 97 utls.CertCompressionBrotli, 98 }}, 99 &utls.FakeChannelIDExtension{}, 100 &utls.FakeRecordSizeLimitExtension{Limit: 9999}, 101 }, 102 GetSessionID: sha256.Sum256, 103 } 104 105 customTLSProfilesJSON := []byte(` 106 [ 107 { 108 "Name": "CustomProfile", 109 "UTLSSpec": { 110 "TLSVersMax": 771, 111 "TLSVersMin": 769, 112 "CipherSuites": [49195, 49199, 49196, 49200, 52393, 52392, 49171, 49172, 156, 157, 47, 53, 10], 113 "CompressionMethods": [0], 114 "Extensions" : [ 115 {"Name": "RenegotiationInfo", "Data": {"Renegotiation": 1}}, 116 {"Name": "SNI"}, 117 {"Name": "ExtendedMasterSecret"}, 118 {"Name": "SessionTicket"}, 119 {"Name": "SignatureAlgorithms", "Data": {"SupportedSignatureAlgorithms": [1027, 2052, 1025, 1283, 2053, 1281, 2054, 1537, 513]}}, 120 {"Name": "StatusRequest"}, 121 {"Name": "SCT"}, 122 {"Name": "ALPN", "Data": {"AlpnProtocols": ["h2", "http/1.1"]}}, 123 {"Name": "ChannelID"}, 124 {"Name": "SupportedPoints", "Data": {"SupportedPoints": [0]}}, 125 {"Name": "SupportedCurves", "Data": {"Curves": [29, 23, 24]}}, 126 {"Name": "BoringPadding"}, 127 {"Name": "NPN", "Data": {"NextProtos": ["h2", "http/1.1"]}}, 128 {"Name": "Generic", "Data": {"Id": 9999, "Data": [103, 101, 110, 101, 114, 105, 99, 32, 101, 120, 116, 101, 110, 115, 105, 111, 110]}}, 129 {"Name": "KeyShare", "Data": {"KeyShares": [{"Group": 29, "Data": [9, 9, 9, 9]}]}}, 130 {"Name": "PSKKeyExchangeModes", "Data": {"Modes": [1]}}, 131 {"Name": "SupportedVersions", "Data": {"Versions": [772, 771, 770, 769]}}, 132 {"Name": "CertCompressionAlgs", "Data": {"Algorithms": [2]}}, 133 {"Name": "ChannelID"}, 134 {"Name": "RecordSizeLimit", "Data": {"Limit": 9999}}], 135 "GetSessionID": "SHA-256" 136 } 137 } 138 ]`) 139 140 var customTLSProfiles CustomTLSProfiles 141 142 err := json.Unmarshal(customTLSProfilesJSON, &customTLSProfiles) 143 if err != nil { 144 t.Fatalf("Unmarshal failed: %s", err) 145 } 146 147 err = customTLSProfiles.Validate() 148 if err != nil { 149 t.Fatalf("Validate failed: %s", err) 150 } 151 152 profile := customTLSProfiles[0] 153 profileClientHelloSpec, err := profile.GetClientHelloSpec() 154 if err != nil { 155 t.Fatalf("GetClientHelloSpec failed: %s", err) 156 } 157 158 zeroes := make([]byte, 32) 159 160 conn1 := utls.UClient(nil, &utls.Config{InsecureSkipVerify: true}, utls.HelloCustom) 161 conn1.ApplyPreset(utlsClientHelloSpec) 162 conn1.SetClientRandom(zeroes) 163 conn1.HandshakeState.Hello.SessionId = zeroes 164 err = conn1.BuildHandshakeState() 165 if err != nil { 166 t.Fatalf("BuildHandshakeState failed: %s", err) 167 } 168 169 conn2 := utls.UClient(nil, &utls.Config{InsecureSkipVerify: true}, utls.HelloCustom) 170 conn2.ApplyPreset(profileClientHelloSpec) 171 conn2.SetClientRandom(zeroes) 172 conn2.HandshakeState.Hello.SessionId = zeroes 173 err = conn2.BuildHandshakeState() 174 if err != nil { 175 t.Fatalf("BuildHandshakeState failed: %s", err) 176 } 177 178 if len(conn1.HandshakeState.Hello.Raw) == 0 { 179 t.Fatalf("Missing raw ClientHello") 180 } 181 182 if len(conn2.HandshakeState.Hello.Raw) == 0 { 183 t.Fatalf("Missing raw ClientHello") 184 } 185 186 if !bytes.Equal(conn1.HandshakeState.Hello.Raw, conn2.HandshakeState.Hello.Raw) { 187 t.Fatalf("Unidentical raw ClientHellos") 188 } 189 }