github.com/kelleygo/clashcore@v1.0.2/component/tls/reality.go (about)

     1  package tls
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/aes"
     7  	"crypto/cipher"
     8  	"crypto/ecdh"
     9  	"crypto/ed25519"
    10  	"crypto/hmac"
    11  	"crypto/sha256"
    12  	"crypto/sha512"
    13  	"crypto/tls"
    14  	"crypto/x509"
    15  	"encoding/binary"
    16  	"errors"
    17  	"net"
    18  	"net/http"
    19  	"reflect"
    20  	"strings"
    21  	"time"
    22  	"unsafe"
    23  
    24  	"github.com/kelleygo/clashcore/common/utils"
    25  	"github.com/kelleygo/clashcore/log"
    26  	"github.com/kelleygo/clashcore/ntp"
    27  
    28  	utls "github.com/sagernet/utls"
    29  	"github.com/zhangyunhao116/fastrand"
    30  	"golang.org/x/crypto/chacha20poly1305"
    31  	"golang.org/x/crypto/hkdf"
    32  	"golang.org/x/net/http2"
    33  )
    34  
    35  const RealityMaxShortIDLen = 8
    36  
    37  type RealityConfig struct {
    38  	PublicKey *ecdh.PublicKey
    39  	ShortID   [RealityMaxShortIDLen]byte
    40  }
    41  
    42  //go:linkname aesgcmPreferred crypto/tls.aesgcmPreferred
    43  func aesgcmPreferred(ciphers []uint16) bool
    44  
    45  func GetRealityConn(ctx context.Context, conn net.Conn, ClientFingerprint string, tlsConfig *tls.Config, realityConfig *RealityConfig) (net.Conn, error) {
    46  	retry := 0
    47  	for fingerprint, exists := GetFingerprint(ClientFingerprint); exists; retry++ {
    48  		verifier := &realityVerifier{
    49  			serverName: tlsConfig.ServerName,
    50  		}
    51  		uConfig := &utls.Config{
    52  			ServerName:             tlsConfig.ServerName,
    53  			InsecureSkipVerify:     true,
    54  			SessionTicketsDisabled: true,
    55  			VerifyPeerCertificate:  verifier.VerifyPeerCertificate,
    56  		}
    57  		clientID := utls.ClientHelloID{
    58  			Client:  fingerprint.Client,
    59  			Version: fingerprint.Version,
    60  			Seed:    fingerprint.Seed,
    61  		}
    62  		uConn := utls.UClient(conn, uConfig, clientID)
    63  		verifier.UConn = uConn
    64  		err := uConn.BuildHandshakeState()
    65  		if err != nil {
    66  			return nil, err
    67  		}
    68  
    69  		hello := uConn.HandshakeState.Hello
    70  		rawSessionID := hello.Raw[39 : 39+32] // the location of session ID
    71  		for i := range rawSessionID {         // https://github.com/golang/go/issues/5373
    72  			rawSessionID[i] = 0
    73  		}
    74  
    75  		binary.BigEndian.PutUint64(hello.SessionId, uint64(ntp.Now().Unix()))
    76  
    77  		copy(hello.SessionId[8:], realityConfig.ShortID[:])
    78  		hello.SessionId[0] = 1
    79  		hello.SessionId[1] = 8
    80  		hello.SessionId[2] = 2
    81  
    82  		//log.Debugln("REALITY hello.sessionId[:16]: %v", hello.SessionId[:16])
    83  
    84  		ecdheKey := uConn.HandshakeState.State13.EcdheKey
    85  		if ecdheKey == nil {
    86  			// WTF???
    87  			if retry > 2 {
    88  				return nil, errors.New("nil ecdheKey")
    89  			}
    90  			continue // retry
    91  		}
    92  		authKey, err := ecdheKey.ECDH(realityConfig.PublicKey)
    93  		if err != nil {
    94  			return nil, err
    95  		}
    96  		if authKey == nil {
    97  			return nil, errors.New("nil auth_key")
    98  		}
    99  		verifier.authKey = authKey
   100  		_, err = hkdf.New(sha256.New, authKey, hello.Random[:20], []byte("REALITY")).Read(authKey)
   101  		if err != nil {
   102  			return nil, err
   103  		}
   104  		var aeadCipher cipher.AEAD
   105  		if aesgcmPreferred(hello.CipherSuites) {
   106  			aesBlock, _ := aes.NewCipher(authKey)
   107  			aeadCipher, _ = cipher.NewGCM(aesBlock)
   108  		} else {
   109  			aeadCipher, _ = chacha20poly1305.New(authKey)
   110  		}
   111  		aeadCipher.Seal(hello.SessionId[:0], hello.Random[20:], hello.SessionId[:16], hello.Raw)
   112  		copy(hello.Raw[39:], hello.SessionId)
   113  		//log.Debugln("REALITY hello.sessionId: %v", hello.SessionId)
   114  		//log.Debugln("REALITY uConn.AuthKey: %v", authKey)
   115  
   116  		err = uConn.HandshakeContext(ctx)
   117  		if err != nil {
   118  			return nil, err
   119  		}
   120  
   121  		log.Debugln("REALITY Authentication: %v, AEAD: %T", verifier.verified, aeadCipher)
   122  
   123  		if !verifier.verified {
   124  			go realityClientFallback(uConn, uConfig.ServerName, clientID)
   125  			return nil, errors.New("REALITY authentication failed")
   126  		}
   127  
   128  		return uConn, nil
   129  	}
   130  	return nil, errors.New("unknown uTLS fingerprint")
   131  }
   132  
   133  func realityClientFallback(uConn net.Conn, serverName string, fingerprint utls.ClientHelloID) {
   134  	defer uConn.Close()
   135  	client := http.Client{
   136  		Transport: &http2.Transport{
   137  			DialTLSContext: func(ctx context.Context, network, addr string, config *tls.Config) (net.Conn, error) {
   138  				return uConn, nil
   139  			},
   140  		},
   141  	}
   142  	request, _ := http.NewRequest("GET", "https://"+serverName, nil)
   143  	request.Header.Set("User-Agent", fingerprint.Client)
   144  	request.AddCookie(&http.Cookie{Name: "padding", Value: strings.Repeat("0", fastrand.Intn(32)+30)})
   145  	response, err := client.Do(request)
   146  	if err != nil {
   147  		return
   148  	}
   149  	//_, _ = io.Copy(io.Discard, response.Body)
   150  	time.Sleep(time.Duration(5+fastrand.Int63n(10)) * time.Second)
   151  	response.Body.Close()
   152  	client.CloseIdleConnections()
   153  }
   154  
   155  type realityVerifier struct {
   156  	*utls.UConn
   157  	serverName string
   158  	authKey    []byte
   159  	verified   bool
   160  }
   161  
   162  var pOffset = utils.MustOK(reflect.TypeOf((*utls.Conn)(nil)).Elem().FieldByName("peerCertificates")).Offset
   163  
   164  func (c *realityVerifier) VerifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
   165  	//p, _ := reflect.TypeOf(c.Conn).Elem().FieldByName("peerCertificates")
   166  	certs := *(*[]*x509.Certificate)(unsafe.Add(unsafe.Pointer(c.Conn), pOffset))
   167  	if pub, ok := certs[0].PublicKey.(ed25519.PublicKey); ok {
   168  		h := hmac.New(sha512.New, c.authKey)
   169  		h.Write(pub)
   170  		if bytes.Equal(h.Sum(nil), certs[0].Signature) {
   171  			c.verified = true
   172  			return nil
   173  		}
   174  	}
   175  	opts := x509.VerifyOptions{
   176  		DNSName:       c.serverName,
   177  		Intermediates: x509.NewCertPool(),
   178  	}
   179  	for _, cert := range certs[1:] {
   180  		opts.Intermediates.AddCert(cert)
   181  	}
   182  	if _, err := certs[0].Verify(opts); err != nil {
   183  		return err
   184  	}
   185  	return nil
   186  }