github.com/astaguna/popon-core@v0.0.0-20231019235610-96e42d76a5ff/psiphon/tlsTunnelConn.go (about) 1 /* 2 * Copyright (c) 2023, 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 psiphon 21 22 import ( 23 "context" 24 "net" 25 26 "github.com/astaguna/popon-core/psiphon/common" 27 "github.com/astaguna/popon-core/psiphon/common/errors" 28 "github.com/astaguna/popon-core/psiphon/common/obfuscator" 29 "github.com/astaguna/popon-core/psiphon/common/prng" 30 ) 31 32 // TLSTunnelConfig specifies the behavior of a TLSTunnelConn. 33 type TLSTunnelConfig struct { 34 35 // CustomTLSConfig is the parameters that will be used to esablish a new 36 // TLS connection with CustomTLSDial. 37 CustomTLSConfig *CustomTLSConfig 38 39 // UseObfuscatedSessionTickets indicates whether to use obfuscated session 40 // tickets. 41 UseObfuscatedSessionTickets bool 42 43 // The following values are used to create the TLS passthrough message. 44 45 ObfuscatedKey string 46 ObfuscatorPaddingSeed *prng.Seed 47 } 48 49 // TLSTunnelConn is a network connection that tunnels net.Conn flows over TLS. 50 type TLSTunnelConn struct { 51 net.Conn 52 tlsPadding int 53 } 54 55 // DialTLSTunnel returns an initialized tls-tunnel connection. 56 func DialTLSTunnel( 57 ctx context.Context, 58 tlsTunnelConfig *TLSTunnelConfig, 59 dialConfig *DialConfig, 60 tlsOSSHApplyTrafficShaping bool, 61 tlsOSSHMinTLSPadding, 62 tlsOSSHMaxTLSPadding int, 63 ) (*TLSTunnelConn, error) { 64 65 tlsPadding, 66 err := 67 tlsTunnelTLSPadding( 68 tlsOSSHApplyTrafficShaping, 69 tlsOSSHMinTLSPadding, 70 tlsOSSHMaxTLSPadding, 71 tlsTunnelConfig.ObfuscatorPaddingSeed) 72 if err != nil { 73 return nil, errors.Trace(err) 74 } 75 76 tlsConfig := &CustomTLSConfig{ 77 Parameters: tlsTunnelConfig.CustomTLSConfig.Parameters, 78 Dial: NewTCPDialer(dialConfig), 79 DialAddr: tlsTunnelConfig.CustomTLSConfig.DialAddr, 80 SNIServerName: tlsTunnelConfig.CustomTLSConfig.SNIServerName, 81 VerifyServerName: tlsTunnelConfig.CustomTLSConfig.VerifyServerName, 82 VerifyPins: tlsTunnelConfig.CustomTLSConfig.VerifyPins, 83 SkipVerify: tlsTunnelConfig.CustomTLSConfig.SkipVerify, 84 TLSProfile: tlsTunnelConfig.CustomTLSConfig.TLSProfile, 85 NoDefaultTLSSessionID: tlsTunnelConfig.CustomTLSConfig.NoDefaultTLSSessionID, 86 RandomizedTLSProfileSeed: tlsTunnelConfig.CustomTLSConfig.RandomizedTLSProfileSeed, 87 TLSPadding: tlsPadding, 88 TrustedCACertificatesFilename: dialConfig.TrustedCACertificatesFilename, 89 } 90 tlsConfig.EnableClientSessionCache() 91 92 if tlsTunnelConfig.UseObfuscatedSessionTickets { 93 tlsConfig.ObfuscatedSessionTicketKey = tlsTunnelConfig.ObfuscatedKey 94 } 95 96 // As the passthrough message is unique and indistinguishable from a normal 97 // TLS client random value, we set it unconditionally and not just for 98 // protocols which may support passthrough (even for those protocols, 99 // clients don't know which servers are configured to use it). 100 101 passthroughMessage, err := obfuscator.MakeTLSPassthroughMessage( 102 true, 103 tlsTunnelConfig.ObfuscatedKey) 104 if err != nil { 105 return nil, errors.Trace(err) 106 } 107 tlsConfig.PassthroughMessage = passthroughMessage 108 109 tlsDialer := NewCustomTLSDialer(tlsConfig) 110 111 // As DialAddr is set in the CustomTLSConfig, no address is required here. 112 conn, err := tlsDialer(ctx, "tcp", "") 113 if err != nil { 114 return nil, errors.Trace(err) 115 } 116 117 return &TLSTunnelConn{ 118 Conn: conn, 119 tlsPadding: tlsPadding, 120 }, nil 121 } 122 123 // tlsTunnelTLSPadding returns the padding length to apply with the TLS padding 124 // extension to the TLS conn established with NewCustomTLSDialer. See 125 // CustomTLSConfig.TLSPadding for details. 126 func tlsTunnelTLSPadding( 127 tlsOSSHApplyTrafficShaping bool, 128 tlsOSSHMinTLSPadding int, 129 tlsOSSHMaxTLSPadding int, 130 obfuscatorPaddingPRNGSeed *prng.Seed, 131 ) (tlsPadding int, 132 err error) { 133 134 tlsPadding = 0 135 136 if tlsOSSHApplyTrafficShaping { 137 138 minPadding := tlsOSSHMinTLSPadding 139 maxPadding := tlsOSSHMaxTLSPadding 140 141 // Maximum padding size per RFC 7685 142 if maxPadding > 65535 { 143 maxPadding = 65535 144 } 145 146 if maxPadding > 0 { 147 tlsPaddingPRNG, err := prng.NewPRNGWithSaltedSeed(obfuscatorPaddingPRNGSeed, "tls-padding") 148 if err != nil { 149 return 0, errors.Trace(err) 150 } 151 152 tlsPadding = tlsPaddingPRNG.Range(minPadding, maxPadding) 153 } 154 } 155 156 return tlsPadding, nil 157 } 158 159 func (conn *TLSTunnelConn) GetMetrics() common.LogFields { 160 logFields := make(common.LogFields) 161 162 logFields["tls_padding"] = conn.tlsPadding 163 164 // Include metrics, such as fragmentor metrics, from the underlying dial 165 // conn. Properties of subsequent underlying dial conns are not reflected 166 // in these metrics; we assume that the first dial conn, which most likely 167 // transits the various protocol handshakes, is most significant. 168 underlyingMetrics, ok := conn.Conn.(common.MetricsSource) 169 if ok { 170 logFields.Add(underlyingMetrics.GetMetrics()) 171 } 172 return logFields 173 } 174 175 func (conn *TLSTunnelConn) IsClosed() bool { 176 closer, ok := conn.Conn.(common.Closer) 177 if !ok { 178 return false 179 } 180 return closer.IsClosed() 181 }