tlsDialer.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  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. Copyright (c) 2012 The Go Authors. All rights reserved.
  21. Redistribution and use in source and binary forms, with or without
  22. modification, are permitted provided that the following conditions are
  23. met:
  24. * Redistributions of source code must retain the above copyright
  25. notice, this list of conditions and the following disclaimer.
  26. * Redistributions in binary form must reproduce the above
  27. copyright notice, this list of conditions and the following disclaimer
  28. in the documentation and/or other materials provided with the
  29. distribution.
  30. * Neither the name of Google Inc. nor the names of its
  31. contributors may be used to endorse or promote products derived from
  32. this software without specific prior written permission.
  33. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  34. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  35. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  36. A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  37. OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  38. SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  39. LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  40. DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  41. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  42. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  43. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  44. */
  45. // Based on https://github.com/getlantern/tlsdialer (http://gopkg.in/getlantern/tlsdialer.v1)
  46. // which itself is a "Fork of crypto/tls.Dial and DialWithDialer"
  47. package psiphon
  48. import (
  49. "bytes"
  50. "context"
  51. "crypto/x509"
  52. "encoding/hex"
  53. "errors"
  54. "io/ioutil"
  55. "net"
  56. "time"
  57. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  58. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/parameters"
  59. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
  60. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
  61. tris "github.com/Psiphon-Labs/tls-tris"
  62. utls "github.com/refraction-networking/utls"
  63. )
  64. // CustomTLSConfig contains parameters to determine the behavior
  65. // of CustomTLSDial.
  66. type CustomTLSConfig struct {
  67. // ClientParameters is the active set of client parameters to use
  68. // for the TLS dial.
  69. ClientParameters *parameters.ClientParameters
  70. // Dial is the network connection dialer. TLS is layered on
  71. // top of a new network connection created with dialer.
  72. Dial Dialer
  73. // DialAddr overrides the "addr" input to Dial when specified
  74. DialAddr string
  75. // UseDialAddrSNI specifies whether to always use the dial "addr"
  76. // host name in the SNI server_name field. When DialAddr is set,
  77. // its host name is used.
  78. UseDialAddrSNI bool
  79. // SNIServerName specifies the value to set in the SNI
  80. // server_name field. When blank, SNI is omitted. Note that
  81. // underlying TLS code also automatically omits SNI when
  82. // the server_name is an IP address.
  83. // SNIServerName is ignored when UseDialAddrSNI is true.
  84. SNIServerName string
  85. // SkipVerify completely disables server certificate verification.
  86. SkipVerify bool
  87. // VerifyLegacyCertificate is a special case self-signed server
  88. // certificate case. Ignores IP SANs and basic constraints. No
  89. // certificate chain. Just checks that the server presented the
  90. // specified certificate. SNI is disbled when this is set.
  91. VerifyLegacyCertificate *x509.Certificate
  92. // TLSProfile specifies a particular indistinguishable TLS profile to use
  93. // for the TLS dial. When TLSProfile is "", a profile is selected at
  94. // random. Setting TLSProfile allows the caller to pin the selection so
  95. // all TLS connections in a certain context (e.g. a single meek
  96. // connection) use a consistent value. The value should be selected by
  97. // calling SelectTLSProfile, which will pick a value at random, subject to
  98. // compatibility constraints.
  99. TLSProfile string
  100. // RandomizedTLSProfileSeed specifies the PRNG seed to use when generating
  101. // a randomized TLS ClientHello, which applies to TLS profiles where
  102. // protocol.TLSProfileIsRandomized is true. The PRNG seed allows for
  103. // optional replay of a particular randomized Client Hello.
  104. RandomizedTLSProfileSeed *prng.Seed
  105. // TrustedCACertificatesFilename specifies a file containing trusted
  106. // CA certs. See Config.TrustedCACertificatesFilename.
  107. TrustedCACertificatesFilename string
  108. // ObfuscatedSessionTicketKey enables obfuscated session tickets
  109. // using the specified key.
  110. ObfuscatedSessionTicketKey string
  111. clientSessionCache utls.ClientSessionCache
  112. }
  113. // EnableClientSessionCache initializes a cache to use to persist session
  114. // tickets, enabling TLS session resumability across multiple
  115. // CustomTLSDial calls or dialers using the same CustomTLSConfig.
  116. func (config *CustomTLSConfig) EnableClientSessionCache(
  117. clientParameters *parameters.ClientParameters) {
  118. if config.clientSessionCache == nil {
  119. config.clientSessionCache = utls.NewLRUClientSessionCache(0)
  120. }
  121. }
  122. // SelectTLSProfile picks a TLS profile at random from the available candidates.
  123. func SelectTLSProfile(
  124. p *parameters.ClientParametersSnapshot) string {
  125. // Two TLS profile lists are constructed, subject to limit constraints: fixed
  126. // parrots and randomized. If one list is empty, the non-empty list is used.
  127. // Otherwise SelectRandomizedTLSProfileProbability determines which list is used.
  128. limitTLSProfiles := p.TLSProfiles(parameters.LimitTLSProfiles)
  129. randomizedTLSProfiles := make([]string, 0)
  130. parrotTLSProfiles := make([]string, 0)
  131. for _, tlsProfile := range protocol.SupportedTLSProfiles {
  132. if len(limitTLSProfiles) > 0 &&
  133. !common.Contains(limitTLSProfiles, tlsProfile) {
  134. continue
  135. }
  136. if protocol.TLSProfileIsRandomized(tlsProfile) {
  137. randomizedTLSProfiles = append(randomizedTLSProfiles, tlsProfile)
  138. } else {
  139. parrotTLSProfiles = append(parrotTLSProfiles, tlsProfile)
  140. }
  141. }
  142. if len(randomizedTLSProfiles) > 0 &&
  143. (len(parrotTLSProfiles) == 0 ||
  144. p.WeightedCoinFlip(parameters.SelectRandomizedTLSProfileProbability)) {
  145. return randomizedTLSProfiles[prng.Intn(len(randomizedTLSProfiles))]
  146. }
  147. if len(parrotTLSProfiles) == 0 {
  148. return ""
  149. }
  150. return parrotTLSProfiles[prng.Intn(len(parrotTLSProfiles))]
  151. }
  152. func getUTLSClientHelloID(tlsProfile string) utls.ClientHelloID {
  153. switch tlsProfile {
  154. case protocol.TLS_PROFILE_IOS_111:
  155. return utls.HelloIOS_11_1
  156. case protocol.TLS_PROFILE_IOS_121:
  157. return utls.HelloIOS_12_1
  158. case protocol.TLS_PROFILE_CHROME_58:
  159. return utls.HelloChrome_58
  160. case protocol.TLS_PROFILE_CHROME_62:
  161. return utls.HelloChrome_62
  162. case protocol.TLS_PROFILE_CHROME_70:
  163. return utls.HelloChrome_70
  164. case protocol.TLS_PROFILE_CHROME_72:
  165. return utls.HelloChrome_72
  166. case protocol.TLS_PROFILE_FIREFOX_55:
  167. return utls.HelloFirefox_55
  168. case protocol.TLS_PROFILE_FIREFOX_56:
  169. return utls.HelloFirefox_56
  170. case protocol.TLS_PROFILE_FIREFOX_65:
  171. return utls.HelloFirefox_65
  172. case protocol.TLS_PROFILE_RANDOMIZED:
  173. return utls.HelloRandomized
  174. default:
  175. return utls.HelloGolang
  176. }
  177. }
  178. func getClientHelloVersion(utlsClientHelloID utls.ClientHelloID) (string, error) {
  179. // Assumes utlsClientHelloID.Seed has been set; otherwise the result is
  180. // ephemeral.
  181. // As utls.HelloRandomized may be either TLS 1.2 or TLS 1.3, we cannot
  182. // perform a simple ClientHello ID check. BuildHandshakeState is run, which
  183. // constructs the entire ClientHello.
  184. //
  185. // BenchmarkRandomizedGetClientHelloVersion indicates that this operation
  186. // takes on the order of 0.05ms and allocates ~8KB for randomized client
  187. // hellos.
  188. conn := utls.UClient(
  189. nil,
  190. &utls.Config{InsecureSkipVerify: true},
  191. utlsClientHelloID)
  192. err := conn.BuildHandshakeState()
  193. if err != nil {
  194. return "", common.ContextError(err)
  195. }
  196. for _, v := range conn.HandshakeState.Hello.SupportedVersions {
  197. if v == utls.VersionTLS13 {
  198. return protocol.TLS_VERSION_13, nil
  199. }
  200. }
  201. return protocol.TLS_VERSION_12, nil
  202. }
  203. func IsTLSConnUsingHTTP2(conn net.Conn) bool {
  204. if c, ok := conn.(*utls.UConn); ok {
  205. state := c.ConnectionState()
  206. return state.NegotiatedProtocolIsMutual &&
  207. state.NegotiatedProtocol == "h2"
  208. }
  209. return false
  210. }
  211. // NewCustomTLSDialer creates a new dialer based on CustomTLSDial.
  212. func NewCustomTLSDialer(config *CustomTLSConfig) Dialer {
  213. return func(ctx context.Context, network, addr string) (net.Conn, error) {
  214. return CustomTLSDial(ctx, network, addr, config)
  215. }
  216. }
  217. // CustomTLSDial is a customized replacement for tls.Dial.
  218. // Based on tlsdialer.DialWithDialer which is based on crypto/tls.DialWithDialer.
  219. //
  220. // To ensure optimal TLS profile selection when using CustomTLSDial for tunnel
  221. // protocols, call SelectTLSProfile first and set its result into
  222. // config.TLSProfile.
  223. //
  224. // tlsdialer comment:
  225. // Note - if sendServerName is false, the VerifiedChains field on the
  226. // connection's ConnectionState will never get populated.
  227. func CustomTLSDial(
  228. ctx context.Context,
  229. network, addr string,
  230. config *CustomTLSConfig) (net.Conn, error) {
  231. dialAddr := addr
  232. if config.DialAddr != "" {
  233. dialAddr = config.DialAddr
  234. }
  235. rawConn, err := config.Dial(ctx, network, dialAddr)
  236. if err != nil {
  237. return nil, common.ContextError(err)
  238. }
  239. hostname, _, err := net.SplitHostPort(dialAddr)
  240. if err != nil {
  241. rawConn.Close()
  242. return nil, common.ContextError(err)
  243. }
  244. selectedTLSProfile := config.TLSProfile
  245. if selectedTLSProfile == "" {
  246. selectedTLSProfile = SelectTLSProfile(config.ClientParameters.Get())
  247. }
  248. tlsConfigInsecureSkipVerify := false
  249. tlsConfigServerName := ""
  250. if config.SkipVerify {
  251. tlsConfigInsecureSkipVerify = true
  252. }
  253. if config.UseDialAddrSNI {
  254. tlsConfigServerName = hostname
  255. } else if config.SNIServerName != "" && config.VerifyLegacyCertificate == nil {
  256. // Set the ServerName and rely on the usual logic in
  257. // tls.Conn.Handshake() to do its verification.
  258. // Note: Go TLS will automatically omit this ServerName when it's an IP address
  259. tlsConfigServerName = config.SNIServerName
  260. } else {
  261. // No SNI.
  262. // Disable verification in tls.Conn.Handshake(). We'll verify manually
  263. // after handshaking
  264. tlsConfigInsecureSkipVerify = true
  265. }
  266. var tlsRootCAs *x509.CertPool
  267. if !config.SkipVerify &&
  268. config.VerifyLegacyCertificate == nil &&
  269. config.TrustedCACertificatesFilename != "" {
  270. tlsRootCAs = x509.NewCertPool()
  271. certData, err := ioutil.ReadFile(config.TrustedCACertificatesFilename)
  272. if err != nil {
  273. return nil, common.ContextError(err)
  274. }
  275. tlsRootCAs.AppendCertsFromPEM(certData)
  276. }
  277. tlsConfig := &utls.Config{
  278. RootCAs: tlsRootCAs,
  279. InsecureSkipVerify: tlsConfigInsecureSkipVerify,
  280. ServerName: tlsConfigServerName,
  281. }
  282. utlsClientHelloID := getUTLSClientHelloID(selectedTLSProfile)
  283. isRandomized := protocol.TLSProfileIsRandomized(selectedTLSProfile)
  284. var randomizedTLSProfileSeed *prng.Seed
  285. if isRandomized {
  286. randomizedTLSProfileSeed = config.RandomizedTLSProfileSeed
  287. if randomizedTLSProfileSeed == nil {
  288. randomizedTLSProfileSeed, err = prng.NewSeed()
  289. if err != nil {
  290. return nil, common.ContextError(err)
  291. }
  292. }
  293. utlsClientHelloID.Seed = new(utls.PRNGSeed)
  294. *utlsClientHelloID.Seed = [32]byte(*randomizedTLSProfileSeed)
  295. }
  296. // As noted here,
  297. // https://gitlab.com/yawning/obfs4/commit/ca6765e3e3995144df2b1ca9f0e9d823a7f8a47c,
  298. // the dynamic record sizing optimization in crypto/tls is not commonly
  299. // implemented in browsers. Disable it for all non-Golang utls parrots and
  300. // select it randomly when using the randomized client hello.
  301. if isRandomized {
  302. PRNG, err := prng.NewPRNGWithSaltedSeed(randomizedTLSProfileSeed, "tls-dynamic-record-sizing")
  303. if err != nil {
  304. return nil, common.ContextError(err)
  305. }
  306. tlsConfig.DynamicRecordSizingDisabled = PRNG.FlipCoin()
  307. } else {
  308. tlsConfig.DynamicRecordSizingDisabled = (utlsClientHelloID != utls.HelloGolang)
  309. }
  310. conn := utls.UClient(rawConn, tlsConfig, utlsClientHelloID)
  311. clientSessionCache := config.clientSessionCache
  312. if clientSessionCache == nil {
  313. clientSessionCache = utls.NewLRUClientSessionCache(0)
  314. }
  315. conn.SetSessionCache(clientSessionCache)
  316. // Obfuscated session tickets are not currently supported in TLS 1.3, but we
  317. // allow UNFRONTED-MEEK-SESSION-TICKET-OSSH to use TLS 1.3 profiles for
  318. // additional diversity/capacity; TLS 1.3 encrypts the server certificate,
  319. // so the desired obfuscated session tickets property of obfuscating server
  320. // certificates is satisfied. We know that when the ClientHello offers TLS
  321. // 1.3, the Psiphon server, in these direct protocol cases, will negotiate
  322. // it.
  323. if config.ObfuscatedSessionTicketKey != "" {
  324. tlsVersion, err := getClientHelloVersion(utlsClientHelloID)
  325. if err != nil {
  326. return nil, common.ContextError(err)
  327. }
  328. if tlsVersion == protocol.TLS_VERSION_12 {
  329. var obfuscatedSessionTicketKey [32]byte
  330. key, err := hex.DecodeString(config.ObfuscatedSessionTicketKey)
  331. if err == nil && len(key) != 32 {
  332. err = errors.New("invalid obfuscated session key length")
  333. }
  334. if err != nil {
  335. return nil, common.ContextError(err)
  336. }
  337. copy(obfuscatedSessionTicketKey[:], key)
  338. obfuscatedSessionState, err := tris.NewObfuscatedClientSessionState(
  339. obfuscatedSessionTicketKey)
  340. if err != nil {
  341. return nil, common.ContextError(err)
  342. }
  343. conn.SetSessionState(
  344. utls.MakeClientSessionState(
  345. obfuscatedSessionState.SessionTicket,
  346. obfuscatedSessionState.Vers,
  347. obfuscatedSessionState.CipherSuite,
  348. obfuscatedSessionState.MasterSecret,
  349. nil,
  350. nil))
  351. // Ensure that TLS ClientHello has required session ticket extension and
  352. // obfuscated session ticket cipher suite; the latter is required by
  353. // utls/tls.Conn.loadSession. If these requirements are not met the
  354. // obfuscation session ticket would be ignored, so fail.
  355. err = conn.BuildHandshakeState()
  356. if err != nil {
  357. return nil, common.ContextError(err)
  358. }
  359. if !tris.ContainsObfuscatedSessionTicketCipherSuite(
  360. conn.HandshakeState.Hello.CipherSuites) {
  361. return nil, common.ContextError(
  362. errors.New("missing obfuscated session ticket cipher suite"))
  363. }
  364. if len(conn.HandshakeState.Hello.SessionTicket) == 0 {
  365. return nil, common.ContextError(
  366. errors.New("missing session ticket extension"))
  367. }
  368. }
  369. }
  370. resultChannel := make(chan error)
  371. go func() {
  372. resultChannel <- conn.Handshake()
  373. }()
  374. select {
  375. case err = <-resultChannel:
  376. case <-ctx.Done():
  377. err = ctx.Err()
  378. // Interrupt the goroutine
  379. rawConn.Close()
  380. <-resultChannel
  381. }
  382. if err == nil && !config.SkipVerify && tlsConfigInsecureSkipVerify {
  383. if config.VerifyLegacyCertificate != nil {
  384. err = verifyLegacyCertificate(conn, config.VerifyLegacyCertificate)
  385. } else {
  386. // Manually verify certificates
  387. err = verifyServerCerts(conn, hostname)
  388. }
  389. }
  390. if err != nil {
  391. rawConn.Close()
  392. return nil, common.ContextError(err)
  393. }
  394. return conn, nil
  395. }
  396. func verifyLegacyCertificate(conn *utls.UConn, expectedCertificate *x509.Certificate) error {
  397. certs := conn.ConnectionState().PeerCertificates
  398. if len(certs) < 1 {
  399. return common.ContextError(errors.New("no certificate to verify"))
  400. }
  401. if !bytes.Equal(certs[0].Raw, expectedCertificate.Raw) {
  402. return common.ContextError(errors.New("unexpected certificate"))
  403. }
  404. return nil
  405. }
  406. func verifyServerCerts(conn *utls.UConn, hostname string) error {
  407. certs := conn.ConnectionState().PeerCertificates
  408. opts := x509.VerifyOptions{
  409. Roots: nil, // Use host's root CAs
  410. CurrentTime: time.Now(),
  411. DNSName: hostname,
  412. Intermediates: x509.NewCertPool(),
  413. }
  414. for i, cert := range certs {
  415. if i == 0 {
  416. continue
  417. }
  418. opts.Intermediates.AddCert(cert)
  419. }
  420. _, err := certs[0].Verify(opts)
  421. if err != nil {
  422. return common.ContextError(err)
  423. }
  424. return nil
  425. }
  426. func init() {
  427. // Favor compatibility over security. CustomTLSDial is used as an obfuscation
  428. // layer; users of CustomTLSDial, including meek and remote server list
  429. // downloads, don't depend on this TLS for its security properties.
  430. utls.EnableWeakCiphers()
  431. }