| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 | //go:build with_utlspackage tlsimport (	"bytes"	"context"	"crypto/aes"	"crypto/cipher"	"crypto/ecdh"	"crypto/ed25519"	"crypto/hmac"	"crypto/sha256"	"crypto/sha512"	"crypto/tls"	"crypto/x509"	"encoding/base64"	"encoding/binary"	"encoding/hex"	"fmt"	"io"	mRand "math/rand"	"net"	"net/http"	"reflect"	"strings"	"time"	"unsafe"	"github.com/sagernet/sing-box/option"	"github.com/sagernet/sing/common/debug"	E "github.com/sagernet/sing/common/exceptions"	aTLS "github.com/sagernet/sing/common/tls"	utls "github.com/sagernet/utls"	"golang.org/x/crypto/hkdf"	"golang.org/x/net/http2")var _ ConfigCompat = (*RealityClientConfig)(nil)type RealityClientConfig struct {	uClient   *UTLSClientConfig	publicKey []byte	shortID   [8]byte}func NewRealityClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (*RealityClientConfig, error) {	if options.UTLS == nil || !options.UTLS.Enabled {		return nil, E.New("uTLS is required by reality client")	}	uClient, err := NewUTLSClient(ctx, serverAddress, options)	if err != nil {		return nil, err	}	publicKey, err := base64.RawURLEncoding.DecodeString(options.Reality.PublicKey)	if err != nil {		return nil, E.Cause(err, "decode public_key")	}	if len(publicKey) != 32 {		return nil, E.New("invalid public_key")	}	var shortID [8]byte	decodedLen, err := hex.Decode(shortID[:], []byte(options.Reality.ShortID))	if err != nil {		return nil, E.Cause(err, "decode short_id")	}	if decodedLen > 8 {		return nil, E.New("invalid short_id")	}	return &RealityClientConfig{uClient, publicKey, shortID}, nil}func (e *RealityClientConfig) ServerName() string {	return e.uClient.ServerName()}func (e *RealityClientConfig) SetServerName(serverName string) {	e.uClient.SetServerName(serverName)}func (e *RealityClientConfig) NextProtos() []string {	return e.uClient.NextProtos()}func (e *RealityClientConfig) SetNextProtos(nextProto []string) {	e.uClient.SetNextProtos(nextProto)}func (e *RealityClientConfig) Config() (*STDConfig, error) {	return nil, E.New("unsupported usage for reality")}func (e *RealityClientConfig) Client(conn net.Conn) (Conn, error) {	return ClientHandshake(context.Background(), conn, e)}func (e *RealityClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {	verifier := &realityVerifier{		serverName: e.uClient.ServerName(),	}	uConfig := e.uClient.config.Clone()	uConfig.InsecureSkipVerify = true	uConfig.SessionTicketsDisabled = true	uConfig.VerifyPeerCertificate = verifier.VerifyPeerCertificate	uConn := utls.UClient(conn, uConfig, e.uClient.id)	verifier.UConn = uConn	err := uConn.BuildHandshakeState()	if err != nil {		return nil, err	}	if len(uConfig.NextProtos) > 0 {		for _, extension := range uConn.Extensions {			if alpnExtension, isALPN := extension.(*utls.ALPNExtension); isALPN {				alpnExtension.AlpnProtocols = uConfig.NextProtos				break			}		}	}	hello := uConn.HandshakeState.Hello	hello.SessionId = make([]byte, 32)	copy(hello.Raw[39:], hello.SessionId)	var nowTime time.Time	if uConfig.Time != nil {		nowTime = uConfig.Time()	} else {		nowTime = time.Now()	}	binary.BigEndian.PutUint64(hello.SessionId, uint64(nowTime.Unix()))	hello.SessionId[0] = 1	hello.SessionId[1] = 8	hello.SessionId[2] = 1	binary.BigEndian.PutUint32(hello.SessionId[4:], uint32(time.Now().Unix()))	copy(hello.SessionId[8:], e.shortID[:])	if debug.Enabled {		fmt.Printf("REALITY hello.sessionId[:16]: %v\n", hello.SessionId[:16])	}	publicKey, err := ecdh.X25519().NewPublicKey(e.publicKey)	if err != nil {		return nil, err	}	ecdheKey := uConn.HandshakeState.State13.EcdheKey	if ecdheKey == nil {		return nil, E.New("nil ecdhe_key")	}	authKey, err := ecdheKey.ECDH(publicKey)	if err != nil {		return nil, err	}	if authKey == nil {		return nil, E.New("nil auth_key")	}	verifier.authKey = authKey	_, err = hkdf.New(sha256.New, authKey, hello.Random[:20], []byte("REALITY")).Read(authKey)	if err != nil {		return nil, err	}	aesBlock, _ := aes.NewCipher(authKey)	aesGcmCipher, _ := cipher.NewGCM(aesBlock)	aesGcmCipher.Seal(hello.SessionId[:0], hello.Random[20:], hello.SessionId[:16], hello.Raw)	copy(hello.Raw[39:], hello.SessionId)	if debug.Enabled {		fmt.Printf("REALITY hello.sessionId: %v\n", hello.SessionId)		fmt.Printf("REALITY uConn.AuthKey: %v\n", authKey)	}	err = uConn.HandshakeContext(ctx)	if err != nil {		return nil, err	}	if debug.Enabled {		fmt.Printf("REALITY Conn.Verified: %v\n", verifier.verified)	}	if !verifier.verified {		go realityClientFallback(uConn, e.uClient.ServerName(), e.uClient.id)		return nil, E.New("reality verification failed")	}	return &realityClientConnWrapper{uConn}, nil}func realityClientFallback(uConn net.Conn, serverName string, fingerprint utls.ClientHelloID) {	defer uConn.Close()	client := &http.Client{		Transport: &http2.Transport{			DialTLSContext: func(ctx context.Context, network, addr string, config *tls.Config) (net.Conn, error) {				return uConn, nil			},		},	}	request, _ := http.NewRequest("GET", "https://"+serverName, nil)	request.Header.Set("User-Agent", fingerprint.Client)	request.AddCookie(&http.Cookie{Name: "padding", Value: strings.Repeat("0", mRand.Intn(32)+30)})	response, err := client.Do(request)	if err != nil {		return	}	_, _ = io.Copy(io.Discard, response.Body)	response.Body.Close()}func (e *RealityClientConfig) SetSessionIDGenerator(generator func(clientHello []byte, sessionID []byte) error) {	e.uClient.config.SessionIDGenerator = generator}func (e *RealityClientConfig) Clone() Config {	return &RealityClientConfig{		e.uClient.Clone().(*UTLSClientConfig),		e.publicKey,		e.shortID,	}}type realityVerifier struct {	*utls.UConn	serverName string	authKey    []byte	verified   bool}func (c *realityVerifier) VerifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {	p, _ := reflect.TypeOf(c.Conn).Elem().FieldByName("peerCertificates")	certs := *(*([]*x509.Certificate))(unsafe.Pointer(uintptr(unsafe.Pointer(c.Conn)) + p.Offset))	if pub, ok := certs[0].PublicKey.(ed25519.PublicKey); ok {		h := hmac.New(sha512.New, c.authKey)		h.Write(pub)		if bytes.Equal(h.Sum(nil), certs[0].Signature) {			c.verified = true			return nil		}	}	opts := x509.VerifyOptions{		DNSName:       c.serverName,		Intermediates: x509.NewCertPool(),	}	for _, cert := range certs[1:] {		opts.Intermediates.AddCert(cert)	}	if _, err := certs[0].Verify(opts); err != nil {		return err	}	return nil}type realityClientConnWrapper struct {	*utls.UConn}func (c *realityClientConnWrapper) ConnectionState() tls.ConnectionState {	state := c.Conn.ConnectionState()	//nolint:staticcheck	return tls.ConnectionState{		Version:                     state.Version,		HandshakeComplete:           state.HandshakeComplete,		DidResume:                   state.DidResume,		CipherSuite:                 state.CipherSuite,		NegotiatedProtocol:          state.NegotiatedProtocol,		NegotiatedProtocolIsMutual:  state.NegotiatedProtocolIsMutual,		ServerName:                  state.ServerName,		PeerCertificates:            state.PeerCertificates,		VerifiedChains:              state.VerifiedChains,		SignedCertificateTimestamps: state.SignedCertificateTimestamps,		OCSPResponse:                state.OCSPResponse,		TLSUnique:                   state.TLSUnique,	}}func (c *realityClientConnWrapper) Upstream() any {	return c.UConn}// Due to low implementation quality, the reality server intercepted half close and caused memory leaks.// We fixed it by calling Close() directly.func (c *realityClientConnWrapper) CloseWrite() error {	return c.Close()}
 |