| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- package reality
- import (
- "bytes"
- "context"
- "crypto/ecdh"
- "crypto/ed25519"
- "crypto/hmac"
- "crypto/sha256"
- "crypto/sha512"
- gotls "crypto/tls"
- "crypto/x509"
- "encoding/binary"
- "fmt"
- "io"
- "net/http"
- "reflect"
- "regexp"
- "strings"
- "sync"
- "time"
- "unsafe"
- "github.com/cloudflare/circl/sign/mldsa/mldsa65"
- utls "github.com/refraction-networking/utls"
- "github.com/xtls/reality"
- "github.com/xtls/xray-core/common/crypto"
- "github.com/xtls/xray-core/common/errors"
- "github.com/xtls/xray-core/common/net"
- "github.com/xtls/xray-core/core"
- "github.com/xtls/xray-core/transport/internet/tls"
- "golang.org/x/crypto/hkdf"
- "golang.org/x/net/http2"
- )
- type Conn struct {
- *reality.Conn
- }
- func (c *Conn) HandshakeAddress() net.Address {
- if err := c.Handshake(); err != nil {
- return nil
- }
- state := c.ConnectionState()
- if state.ServerName == "" {
- return nil
- }
- return net.ParseAddress(state.ServerName)
- }
- func Server(c net.Conn, config *reality.Config) (net.Conn, error) {
- realityConn, err := reality.Server(context.Background(), c, config)
- return &Conn{Conn: realityConn}, err
- }
- type UConn struct {
- *utls.UConn
- Config *Config
- ServerName string
- AuthKey []byte
- Verified bool
- }
- func (c *UConn) HandshakeAddress() net.Address {
- if err := c.Handshake(); err != nil {
- return nil
- }
- state := c.ConnectionState()
- if state.ServerName == "" {
- return nil
- }
- return net.ParseAddress(state.ServerName)
- }
- func (c *UConn) VerifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
- if c.Config.Show {
- localAddr := c.LocalAddr().String()
- fmt.Printf("REALITY localAddr: %v\tis using X25519MLKEM768 for TLS' communication: %v\n", localAddr, c.HandshakeState.ServerHello.ServerShare.Group == utls.X25519MLKEM768)
- fmt.Printf("REALITY localAddr: %v\tis using ML-DSA-65 for cert's extra verification: %v\n", localAddr, len(c.Config.Mldsa65Verify) > 0)
- }
- 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) {
- if len(c.Config.Mldsa65Verify) > 0 {
- if len(certs[0].Extensions) > 0 {
- h.Write(c.HandshakeState.Hello.Raw)
- h.Write(c.HandshakeState.ServerHello.Raw)
- verify, _ := mldsa65.Scheme().UnmarshalBinaryPublicKey(c.Config.Mldsa65Verify)
- if mldsa65.Verify(verify.(*mldsa65.PublicKey), h.Sum(nil), nil, certs[0].Extensions[0].Value) {
- c.Verified = true
- return nil
- }
- }
- } else {
- 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
- }
- func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destination) (net.Conn, error) {
- localAddr := c.LocalAddr().String()
- uConn := &UConn{
- Config: config,
- }
- utlsConfig := &utls.Config{
- VerifyPeerCertificate: uConn.VerifyPeerCertificate,
- ServerName: config.ServerName,
- InsecureSkipVerify: true,
- SessionTicketsDisabled: true,
- KeyLogWriter: KeyLogWriterFromConfig(config),
- }
- if utlsConfig.ServerName == "" {
- utlsConfig.ServerName = dest.Address.String()
- }
- uConn.ServerName = utlsConfig.ServerName
- fingerprint := tls.GetFingerprint(config.Fingerprint)
- if fingerprint == nil {
- return nil, errors.New("REALITY: failed to get fingerprint").AtError()
- }
- uConn.UConn = utls.UClient(c, utlsConfig, *fingerprint)
- {
- uConn.BuildHandshakeState()
- hello := uConn.HandshakeState.Hello
- hello.SessionId = make([]byte, 32)
- copy(hello.Raw[39:], hello.SessionId) // the fixed location of `Session ID`
- hello.SessionId[0] = core.Version_x
- hello.SessionId[1] = core.Version_y
- hello.SessionId[2] = core.Version_z
- hello.SessionId[3] = 0 // reserved
- binary.BigEndian.PutUint32(hello.SessionId[4:], uint32(time.Now().Unix()))
- copy(hello.SessionId[8:], config.ShortId)
- if config.Show {
- fmt.Printf("REALITY localAddr: %v\thello.SessionId[:16]: %v\n", localAddr, hello.SessionId[:16])
- }
- publicKey, err := ecdh.X25519().NewPublicKey(config.PublicKey)
- if err != nil {
- return nil, errors.New("REALITY: publicKey == nil")
- }
- ecdhe := uConn.HandshakeState.State13.KeyShareKeys.Ecdhe
- if ecdhe == nil {
- ecdhe = uConn.HandshakeState.State13.KeyShareKeys.MlkemEcdhe
- }
- if ecdhe == nil {
- return nil, errors.New("Current fingerprint ", uConn.ClientHelloID.Client, uConn.ClientHelloID.Version, " does not support TLS 1.3, REALITY handshake cannot establish.")
- }
- uConn.AuthKey, _ = ecdhe.ECDH(publicKey)
- if uConn.AuthKey == nil {
- return nil, errors.New("REALITY: SharedKey == nil")
- }
- if _, err := hkdf.New(sha256.New, uConn.AuthKey, hello.Random[:20], []byte("REALITY")).Read(uConn.AuthKey); err != nil {
- return nil, err
- }
- aead := crypto.NewAesGcm(uConn.AuthKey)
- if config.Show {
- fmt.Printf("REALITY localAddr: %v\tuConn.AuthKey[:16]: %v\tAEAD: %T\n", localAddr, uConn.AuthKey[:16], aead)
- }
- aead.Seal(hello.SessionId[:0], hello.Random[20:], hello.SessionId[:16], hello.Raw)
- copy(hello.Raw[39:], hello.SessionId)
- }
- if err := uConn.HandshakeContext(ctx); err != nil {
- return nil, err
- }
- if config.Show {
- fmt.Printf("REALITY localAddr: %v\tuConn.Verified: %v\n", localAddr, uConn.Verified)
- }
- if !uConn.Verified {
- go func() {
- client := &http.Client{
- Transport: &http2.Transport{
- DialTLSContext: func(ctx context.Context, network, addr string, cfg *gotls.Config) (net.Conn, error) {
- fmt.Printf("REALITY localAddr: %v\tDialTLSContext\n", localAddr)
- return uConn, nil
- },
- },
- }
- prefix := []byte("https://" + uConn.ServerName)
- maps.Lock()
- if maps.maps == nil {
- maps.maps = make(map[string]map[string]struct{})
- }
- paths := maps.maps[uConn.ServerName]
- if paths == nil {
- paths = make(map[string]struct{})
- paths[config.SpiderX] = struct{}{}
- maps.maps[uConn.ServerName] = paths
- }
- firstURL := string(prefix) + getPathLocked(paths)
- maps.Unlock()
- get := func(first bool) {
- var (
- req *http.Request
- resp *http.Response
- err error
- body []byte
- )
- if first {
- req, _ = http.NewRequest("GET", firstURL, nil)
- } else {
- maps.Lock()
- req, _ = http.NewRequest("GET", string(prefix)+getPathLocked(paths), nil)
- maps.Unlock()
- }
- if req == nil {
- return
- }
- req.Header.Set("User-Agent", fingerprint.Client) // TODO: User-Agent map
- if first && config.Show {
- fmt.Printf("REALITY localAddr: %v\treq.UserAgent(): %v\n", localAddr, req.UserAgent())
- }
- times := 1
- if !first {
- times = int(crypto.RandBetween(config.SpiderY[4], config.SpiderY[5]))
- }
- for j := 0; j < times; j++ {
- if !first && j == 0 {
- req.Header.Set("Referer", firstURL)
- }
- req.AddCookie(&http.Cookie{Name: "padding", Value: strings.Repeat("0", int(crypto.RandBetween(config.SpiderY[0], config.SpiderY[1])))})
- if resp, err = client.Do(req); err != nil {
- break
- }
- defer resp.Body.Close()
- req.Header.Set("Referer", req.URL.String())
- if body, err = io.ReadAll(resp.Body); err != nil {
- break
- }
- maps.Lock()
- for _, m := range href.FindAllSubmatch(body, -1) {
- m[1] = bytes.TrimPrefix(m[1], prefix)
- if !bytes.Contains(m[1], dot) {
- paths[string(m[1])] = struct{}{}
- }
- }
- req.URL.Path = getPathLocked(paths)
- if config.Show {
- fmt.Printf("REALITY localAddr: %v\treq.Referer(): %v\n", localAddr, req.Referer())
- fmt.Printf("REALITY localAddr: %v\tlen(body): %v\n", localAddr, len(body))
- fmt.Printf("REALITY localAddr: %v\tlen(paths): %v\n", localAddr, len(paths))
- }
- maps.Unlock()
- if !first {
- time.Sleep(time.Duration(crypto.RandBetween(config.SpiderY[6], config.SpiderY[7])) * time.Millisecond) // interval
- }
- }
- }
- get(true)
- concurrency := int(crypto.RandBetween(config.SpiderY[2], config.SpiderY[3]))
- for i := 0; i < concurrency; i++ {
- go get(false)
- }
- // Do not close the connection
- }()
- time.Sleep(time.Duration(crypto.RandBetween(config.SpiderY[8], config.SpiderY[9])) * time.Millisecond) // return
- return nil, errors.New("REALITY: processed invalid connection").AtWarning()
- }
- return uConn, nil
- }
- var (
- href = regexp.MustCompile(`href="([/h].*?)"`)
- dot = []byte(".")
- )
- var maps struct {
- sync.Mutex
- maps map[string]map[string]struct{}
- }
- func getPathLocked(paths map[string]struct{}) string {
- stopAt := int(crypto.RandBetween(0, int64(len(paths)-1)))
- i := 0
- for s := range paths {
- if i == stopAt {
- return s
- }
- i++
- }
- return "/"
- }
|