Files
sing-box-extended-mirror/common/tls/std_client.go
T

241 lines
7.2 KiB
Go
Raw Normal View History

2022-09-09 18:45:10 +08:00
package tls
2022-07-18 20:40:14 +08:00
import (
"bytes"
2023-08-29 13:43:42 +08:00
"context"
"crypto/sha256"
2022-07-18 20:40:14 +08:00
"crypto/tls"
"crypto/x509"
"encoding/base64"
2022-07-18 20:40:14 +08:00
"net"
"os"
2023-09-19 19:59:07 +08:00
"strings"
2025-06-12 08:58:07 +08:00
"time"
2022-07-18 20:40:14 +08:00
2025-01-08 10:34:45 +08:00
"github.com/sagernet/sing-box/adapter"
2025-06-12 08:58:07 +08:00
"github.com/sagernet/sing-box/common/tlsfragment"
2025-09-07 21:03:32 +08:00
C "github.com/sagernet/sing-box/constant"
2022-07-18 20:40:14 +08:00
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
2025-09-07 21:03:32 +08:00
"github.com/sagernet/sing/common/logger"
2023-08-29 13:43:42 +08:00
"github.com/sagernet/sing/common/ntp"
2022-07-18 20:40:14 +08:00
)
2022-11-09 11:43:03 +08:00
type STDClientConfig struct {
2025-06-12 08:58:07 +08:00
ctx context.Context
config *tls.Config
fragment bool
fragmentFallbackDelay time.Duration
recordFragment bool
2022-07-18 20:40:14 +08:00
}
2025-06-12 09:13:23 +08:00
func (c *STDClientConfig) ServerName() string {
return c.config.ServerName
2022-11-09 11:43:03 +08:00
}
2025-06-12 09:13:23 +08:00
func (c *STDClientConfig) SetServerName(serverName string) {
c.config.ServerName = serverName
2022-11-09 11:43:03 +08:00
}
2025-06-12 09:13:23 +08:00
func (c *STDClientConfig) NextProtos() []string {
return c.config.NextProtos
2022-11-09 11:43:03 +08:00
}
2025-06-12 09:13:23 +08:00
func (c *STDClientConfig) SetNextProtos(nextProto []string) {
c.config.NextProtos = nextProto
2022-11-09 11:43:03 +08:00
}
2025-09-07 21:03:32 +08:00
func (c *STDClientConfig) STDConfig() (*STDConfig, error) {
2025-06-12 09:13:23 +08:00
return c.config, nil
2022-11-09 11:43:03 +08:00
}
2025-06-12 09:13:23 +08:00
func (c *STDClientConfig) Client(conn net.Conn) (Conn, error) {
if c.recordFragment {
conn = tf.NewConn(conn, c.ctx, c.fragment, c.recordFragment, c.fragmentFallbackDelay)
2025-06-12 08:58:07 +08:00
}
2025-06-12 09:13:23 +08:00
return tls.Client(conn, c.config), nil
2022-11-09 11:43:03 +08:00
}
2025-06-12 09:13:23 +08:00
func (c *STDClientConfig) Clone() Config {
2025-09-07 21:03:32 +08:00
return &STDClientConfig{
ctx: c.ctx,
config: c.config.Clone(),
fragment: c.fragment,
fragmentFallbackDelay: c.fragmentFallbackDelay,
recordFragment: c.recordFragment,
}
2025-06-12 09:13:23 +08:00
}
func (c *STDClientConfig) ECHConfigList() []byte {
return c.config.EncryptedClientHelloConfigList
}
func (c *STDClientConfig) SetECHConfigList(EncryptedClientHelloConfigList []byte) {
c.config.EncryptedClientHelloConfigList = EncryptedClientHelloConfigList
2022-11-09 11:43:03 +08:00
}
2025-09-07 21:03:32 +08:00
func NewSTDClient(ctx context.Context, logger logger.ContextLogger, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
2022-07-18 20:40:14 +08:00
var serverName string
if options.ServerName != "" {
serverName = options.ServerName
} else if serverAddress != "" {
2025-03-16 14:50:44 +08:00
serverName = serverAddress
2022-07-18 20:40:14 +08:00
}
2022-08-24 16:11:41 +08:00
if serverName == "" && !options.Insecure {
2022-07-18 20:40:14 +08:00
return nil, E.New("missing server_name or insecure=true")
}
var tlsConfig tls.Config
2023-08-29 13:43:42 +08:00
tlsConfig.Time = ntp.TimeFuncFromContext(ctx)
2025-01-08 10:34:45 +08:00
tlsConfig.RootCAs = adapter.RootPoolFromContext(ctx)
2025-06-12 09:13:23 +08:00
if !options.DisableSNI {
2022-07-18 20:40:14 +08:00
tlsConfig.ServerName = serverName
}
if options.Insecure {
tlsConfig.InsecureSkipVerify = options.Insecure
} else if options.DisableSNI {
tlsConfig.InsecureSkipVerify = true
tlsConfig.VerifyConnection = func(state tls.ConnectionState) error {
verifyOptions := x509.VerifyOptions{
Roots: tlsConfig.RootCAs,
2022-07-18 20:40:14 +08:00
DNSName: serverName,
Intermediates: x509.NewCertPool(),
}
for _, cert := range state.PeerCertificates[1:] {
verifyOptions.Intermediates.AddCert(cert)
}
2025-07-23 17:51:39 +08:00
if tlsConfig.Time != nil {
verifyOptions.CurrentTime = tlsConfig.Time()
}
2022-07-18 20:40:14 +08:00
_, err := state.PeerCertificates[0].Verify(verifyOptions)
return err
}
}
if len(options.CertificatePublicKeySHA256) > 0 {
if len(options.Certificate) > 0 || options.CertificatePath != "" {
return nil, E.New("certificate_public_key_sha256 is conflict with certificate or certificate_path")
}
tlsConfig.InsecureSkipVerify = true
tlsConfig.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
return verifyPublicKeySHA256(options.CertificatePublicKeySHA256, rawCerts, tlsConfig.Time)
}
}
2022-07-25 08:14:09 +08:00
if len(options.ALPN) > 0 {
tlsConfig.NextProtos = options.ALPN
}
if options.MinVersion != "" {
2022-09-09 18:45:10 +08:00
minVersion, err := ParseTLSVersion(options.MinVersion)
2022-07-25 08:14:09 +08:00
if err != nil {
return nil, E.Cause(err, "parse min_version")
}
tlsConfig.MinVersion = minVersion
}
if options.MaxVersion != "" {
2022-09-09 18:45:10 +08:00
maxVersion, err := ParseTLSVersion(options.MaxVersion)
2022-07-25 08:14:09 +08:00
if err != nil {
return nil, E.Cause(err, "parse max_version")
}
tlsConfig.MaxVersion = maxVersion
}
if options.CipherSuites != nil {
find:
for _, cipherSuite := range options.CipherSuites {
for _, tlsCipherSuite := range tls.CipherSuites() {
if cipherSuite == tlsCipherSuite.Name {
tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, tlsCipherSuite.ID)
continue find
}
}
return nil, E.New("unknown cipher_suite: ", cipherSuite)
}
}
for _, curve := range options.CurvePreferences {
tlsConfig.CurvePreferences = append(tlsConfig.CurvePreferences, tls.CurveID(curve))
}
2022-07-25 08:14:09 +08:00
var certificate []byte
2023-09-19 19:59:07 +08:00
if len(options.Certificate) > 0 {
certificate = []byte(strings.Join(options.Certificate, "\n"))
2022-07-25 08:14:09 +08:00
} else if options.CertificatePath != "" {
content, err := os.ReadFile(options.CertificatePath)
if err != nil {
return nil, E.Cause(err, "read certificate")
}
certificate = content
}
if len(certificate) > 0 {
2022-07-27 12:03:07 +08:00
certPool := x509.NewCertPool()
2022-08-08 08:56:04 +08:00
if !certPool.AppendCertsFromPEM(certificate) {
2026-07-25 22:57:06 +08:00
return nil, E.New("failed to parse certificate:\n\n", string(certificate))
2022-07-25 08:14:09 +08:00
}
tlsConfig.RootCAs = certPool
}
2025-10-09 23:10:34 +08:00
var clientCertificate []byte
if len(options.ClientCertificate) > 0 {
clientCertificate = []byte(strings.Join(options.ClientCertificate, "\n"))
} else if options.ClientCertificatePath != "" {
content, err := os.ReadFile(options.ClientCertificatePath)
if err != nil {
return nil, E.Cause(err, "read client certificate")
}
clientCertificate = content
}
var clientKey []byte
if len(options.ClientKey) > 0 {
clientKey = []byte(strings.Join(options.ClientKey, "\n"))
} else if options.ClientKeyPath != "" {
content, err := os.ReadFile(options.ClientKeyPath)
if err != nil {
return nil, E.Cause(err, "read client key")
}
clientKey = content
}
if len(clientCertificate) > 0 && len(clientKey) > 0 {
keyPair, err := tls.X509KeyPair(clientCertificate, clientKey)
if err != nil {
return nil, E.Cause(err, "parse client x509 key pair")
}
tlsConfig.Certificates = []tls.Certificate{keyPair}
} else if len(clientCertificate) > 0 || len(clientKey) > 0 {
return nil, E.New("client certificate and client key must be provided together")
}
2025-09-07 21:03:32 +08:00
var config Config = &STDClientConfig{ctx, &tlsConfig, options.Fragment, time.Duration(options.FragmentFallbackDelay), options.RecordFragment}
2025-02-22 08:00:59 +08:00
if options.ECH != nil && options.ECH.Enabled {
2025-09-07 21:03:32 +08:00
var err error
config, err = parseECHClientConfig(ctx, config.(ECHCapableConfig), options)
if err != nil {
return nil, err
}
}
if options.KernelRx || options.KernelTx {
if !C.IsLinux {
return nil, E.New("kTLS is only supported on Linux")
}
config = &KTLSClientConfig{
Config: config,
logger: logger,
kernelTx: options.KernelTx,
kernelRx: options.KernelRx,
}
2025-02-22 08:00:59 +08:00
}
2025-09-07 21:03:32 +08:00
return config, nil
2022-08-22 18:53:47 +08:00
}
func verifyPublicKeySHA256(knownHashValues [][]byte, rawCerts [][]byte, timeFunc func() time.Time) error {
leafCertificate, err := x509.ParseCertificate(rawCerts[0])
if err != nil {
return E.Cause(err, "failed to parse leaf certificate")
}
pubKeyBytes, err := x509.MarshalPKIXPublicKey(leafCertificate.PublicKey)
if err != nil {
return E.Cause(err, "failed to marshal public key")
}
hashValue := sha256.Sum256(pubKeyBytes)
for _, value := range knownHashValues {
if bytes.Equal(value, hashValue[:]) {
return nil
}
}
return E.New("unrecognized remote public key: ", base64.StdEncoding.EncodeToString(hashValue[:]))
}