// +build android windows /* * Copyright (c) 2015, Psiphon Inc. * All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ package psiphon import ( "errors" "net" "strings" "github.com/Psiphon-Inc/openssl" "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common" ) // newOpenSSLConn wraps a connection with TLS which mimicks stock Android TLS. // This facility is used as a circumvention measure to ensure Psiphon client // TLS ClientHello messages match common TLS ClientHellos vs. the more // distinguishable (blockable) Go TLS ClientHello. func newOpenSSLConn(rawConn net.Conn, hostname string, config *CustomTLSConfig) (handshakeConn, error) { ctx, err := openssl.NewCtx() if err != nil { return nil, common.ContextError(err) } if !config.SkipVerify { ctx.SetVerifyMode(openssl.VerifyPeer) if config.VerifyLegacyCertificate != nil { // TODO: verify with VerifyLegacyCertificate return nil, errors.New("newOpenSSLConn does not support VerifyLegacyCertificate") } else { if config.TrustedCACertificatesFilename == "" { return nil, errors.New("newOpenSSLConn cannot verify without TrustedCACertificatesFilename") } err = ctx.LoadVerifyLocations(config.TrustedCACertificatesFilename, "") if err != nil { return nil, common.ContextError(err) } } } else { ctx.SetVerifyMode(openssl.VerifyNone) } // Use the same cipher suites, in the same priority order, as stock Android TLS. // Based on: https://android.googlesource.com/platform/external/conscrypt/+/master/src/main/java/org/conscrypt/NativeCrypto.java // This list includes include recently retired DSS suites: https://android.googlesource.com/platform/external/conscrypt/+/e53baea9221be7f9828d0f338ede284e22f55722%5E!/#F0, // as those are still commonly deployed. ciphersuites := []string{ "ECDHE-ECDSA-AES128-GCM-SHA256", "ECDHE-ECDSA-AES256-GCM-SHA384", "ECDHE-RSA-AES128-GCM-SHA256", "ECDHE-RSA-AES256-GCM-SHA384", "DHE-RSA-AES128-GCM-SHA256", "DHE-RSA-AES256-GCM-SHA384", "ECDHE-ECDSA-AES128-SHA", "ECDHE-ECDSA-AES256-SHA", "ECDHE-RSA-AES128-SHA", "ECDHE-RSA-AES256-SHA", "DHE-RSA-AES128-SHA", "DHE-RSA-AES256-SHA", "DHE-DSS-AES128-SHA", "DHE-DSS-AES256-SHA", "ECDHE-ECDSA-RC4-SHA", "ECDHE-RSA-RC4-SHA", "AES128-GCM-SHA256", "AES256-GCM-SHA384", "AES128-SHA", "AES256-SHA", "RC4-SHA", } ctx.SetCipherList(strings.Join(ciphersuites, ":")) // Mimic extensions used by stock Android. // NOTE: Heartbeat extension is disabled at compile time. ctx.SetOptions(openssl.NoSessionResumptionOrRenegotiation | openssl.NoTicket) conn, err := openssl.Client(rawConn, ctx) if err != nil { return nil, common.ContextError(err) } if config.SNIServerName != "" { // Explicitly exclude IPs: // - "Literal IPv4 and IPv6 addresses are not permitted": https://tools.ietf.org/html/rfc6066#page-6. // - OpenSSL does not appear to enforce this rule itself. if net.ParseIP(config.SNIServerName) == nil { err = conn.SetTlsExtHostName(config.SNIServerName) if err != nil { return nil, common.ContextError(err) } } } return conn, nil }