github.com/yaling888/clash@v1.53.0/transport/vmess/conn.go (about)

     1  package vmess
     2  
     3  import (
     4  	"crypto/aes"
     5  	"crypto/cipher"
     6  	"crypto/hmac"
     7  	"crypto/md5"
     8  	"crypto/rand"
     9  	"crypto/sha256"
    10  	"encoding/binary"
    11  	"errors"
    12  	"hash/fnv"
    13  	"io"
    14  	R "math/rand/v2"
    15  	"net"
    16  	"time"
    17  
    18  	"golang.org/x/crypto/chacha20poly1305"
    19  
    20  	"github.com/yaling888/clash/common/pool"
    21  )
    22  
    23  // Conn wrapper a net.Conn with vmess protocol
    24  type Conn struct {
    25  	net.Conn
    26  	reader      io.Reader
    27  	writer      io.Writer
    28  	dst         *DstAddr
    29  	id          *ID
    30  	reqBodyIV   []byte
    31  	reqBodyKey  []byte
    32  	respBodyIV  []byte
    33  	respBodyKey []byte
    34  	respV       byte
    35  	security    byte
    36  	option      byte
    37  	isAead      bool
    38  
    39  	received bool
    40  }
    41  
    42  func (vc *Conn) Write(b []byte) (int, error) {
    43  	return vc.writer.Write(b)
    44  }
    45  
    46  func (vc *Conn) Read(b []byte) (int, error) {
    47  	if vc.received {
    48  		return vc.reader.Read(b)
    49  	}
    50  
    51  	if err := vc.recvResponse(); err != nil {
    52  		return 0, err
    53  	}
    54  	vc.received = true
    55  	return vc.reader.Read(b)
    56  }
    57  
    58  func (vc *Conn) sendRequest() error {
    59  	timestamp := time.Now()
    60  
    61  	mBuf := pool.BufferWriter{}
    62  
    63  	if !vc.isAead {
    64  		h := hmac.New(md5.New, vc.id.UUID.Bytes())
    65  		_ = binary.Write(h, binary.BigEndian, uint64(timestamp.Unix()))
    66  		mBuf.PutSlice(h.Sum(nil))
    67  	}
    68  
    69  	buf := pool.BufferWriter{}
    70  
    71  	// Ver IV Key V Opt
    72  	buf.PutUint8(Version)
    73  	buf.PutSlice(vc.reqBodyIV[:])
    74  	buf.PutSlice(vc.reqBodyKey[:])
    75  	buf.PutUint8(vc.respV)
    76  	buf.PutUint8(vc.option)
    77  
    78  	p := R.IntN(16)
    79  	// P Sec Reserve Cmd
    80  	buf.PutUint8(byte(p<<4) | vc.security)
    81  	buf.PutUint8(0)
    82  	if vc.dst.UDP {
    83  		buf.PutUint8(CommandUDP)
    84  	} else {
    85  		buf.PutUint8(CommandTCP)
    86  	}
    87  
    88  	// Port AddrType Addr
    89  	buf.PutUint16be(uint16(vc.dst.Port))
    90  	buf.PutUint8(vc.dst.AddrType)
    91  	buf.PutSlice(vc.dst.Addr)
    92  
    93  	// padding
    94  	if p > 0 {
    95  		_ = buf.ReadFull(rand.Reader, p)
    96  	}
    97  
    98  	fnv1a := fnv.New32a()
    99  	_, _ = fnv1a.Write(buf.Bytes())
   100  	buf.PutSlice(fnv1a.Sum(nil))
   101  
   102  	if !vc.isAead {
   103  		block, err := aes.NewCipher(vc.id.CmdKey)
   104  		if err != nil {
   105  			return err
   106  		}
   107  
   108  		stream := cipher.NewCFBEncrypter(block, hashTimestamp(timestamp))
   109  		stream.XORKeyStream(buf.Bytes(), buf.Bytes())
   110  		mBuf.PutSlice(buf.Bytes())
   111  		_, err = vc.Conn.Write(mBuf.Bytes())
   112  		return err
   113  	}
   114  
   115  	var fixedLengthCmdKey [16]byte
   116  	copy(fixedLengthCmdKey[:], vc.id.CmdKey)
   117  	vmessout := sealVMessAEADHeader(fixedLengthCmdKey, buf.Bytes(), timestamp)
   118  	_, err := vc.Conn.Write(vmessout)
   119  	return err
   120  }
   121  
   122  func (vc *Conn) recvResponse() error {
   123  	var buf []byte
   124  	if !vc.isAead {
   125  		block, err := aes.NewCipher(vc.respBodyKey[:])
   126  		if err != nil {
   127  			return err
   128  		}
   129  
   130  		stream := cipher.NewCFBDecrypter(block, vc.respBodyIV[:])
   131  		buf = make([]byte, 4)
   132  		_, err = io.ReadFull(vc.Conn, buf)
   133  		if err != nil {
   134  			return err
   135  		}
   136  		stream.XORKeyStream(buf, buf)
   137  	} else {
   138  		aeadResponseHeaderLengthEncryptionKey := kdf(vc.respBodyKey[:], kdfSaltConstAEADRespHeaderLenKey)[:16]
   139  		aeadResponseHeaderLengthEncryptionIV := kdf(vc.respBodyIV[:], kdfSaltConstAEADRespHeaderLenIV)[:12]
   140  
   141  		aeadResponseHeaderLengthEncryptionKeyAESBlock, _ := aes.NewCipher(aeadResponseHeaderLengthEncryptionKey)
   142  		aeadResponseHeaderLengthEncryptionAEAD, _ := cipher.NewGCM(aeadResponseHeaderLengthEncryptionKeyAESBlock)
   143  
   144  		aeadEncryptedResponseHeaderLength := make([]byte, 18)
   145  		if _, err := io.ReadFull(vc.Conn, aeadEncryptedResponseHeaderLength); err != nil {
   146  			return err
   147  		}
   148  
   149  		decryptedResponseHeaderLengthBinaryBuffer, err := aeadResponseHeaderLengthEncryptionAEAD.Open(nil, aeadResponseHeaderLengthEncryptionIV, aeadEncryptedResponseHeaderLength[:], nil)
   150  		if err != nil {
   151  			return err
   152  		}
   153  
   154  		decryptedResponseHeaderLength := binary.BigEndian.Uint16(decryptedResponseHeaderLengthBinaryBuffer)
   155  		aeadResponseHeaderPayloadEncryptionKey := kdf(vc.respBodyKey[:], kdfSaltConstAEADRespHeaderPayloadKey)[:16]
   156  		aeadResponseHeaderPayloadEncryptionIV := kdf(vc.respBodyIV[:], kdfSaltConstAEADRespHeaderPayloadIV)[:12]
   157  		aeadResponseHeaderPayloadEncryptionKeyAESBlock, _ := aes.NewCipher(aeadResponseHeaderPayloadEncryptionKey)
   158  		aeadResponseHeaderPayloadEncryptionAEAD, _ := cipher.NewGCM(aeadResponseHeaderPayloadEncryptionKeyAESBlock)
   159  
   160  		encryptedResponseHeaderBuffer := make([]byte, decryptedResponseHeaderLength+16)
   161  		if _, err := io.ReadFull(vc.Conn, encryptedResponseHeaderBuffer); err != nil {
   162  			return err
   163  		}
   164  
   165  		buf, err = aeadResponseHeaderPayloadEncryptionAEAD.Open(nil, aeadResponseHeaderPayloadEncryptionIV, encryptedResponseHeaderBuffer, nil)
   166  		if err != nil {
   167  			return err
   168  		}
   169  
   170  		if len(buf) < 4 {
   171  			return errors.New("unexpected buffer length")
   172  		}
   173  	}
   174  
   175  	if buf[0] != vc.respV {
   176  		return errors.New("unexpected response header")
   177  	}
   178  
   179  	if buf[2] != 0 {
   180  		return errors.New("dynamic port is not supported now")
   181  	}
   182  
   183  	return nil
   184  }
   185  
   186  func hashTimestamp(t time.Time) []byte {
   187  	md5hash := md5.New()
   188  	ts := make([]byte, 8)
   189  	binary.BigEndian.PutUint64(ts, uint64(t.Unix()))
   190  	md5hash.Write(ts)
   191  	md5hash.Write(ts)
   192  	md5hash.Write(ts)
   193  	md5hash.Write(ts)
   194  	return md5hash.Sum(nil)
   195  }
   196  
   197  // newConn return a Conn instance
   198  func newConn(conn net.Conn, id *ID, dst *DstAddr, security Security, isAead bool) (*Conn, error) {
   199  	randBytes := make([]byte, 33)
   200  	_, _ = rand.Read(randBytes)
   201  	reqBodyIV := make([]byte, 16)
   202  	reqBodyKey := make([]byte, 16)
   203  	copy(reqBodyIV[:], randBytes[:16])
   204  	copy(reqBodyKey[:], randBytes[16:32])
   205  	respV := randBytes[32]
   206  	option := OptionChunkStream
   207  
   208  	var (
   209  		respBodyKey []byte
   210  		respBodyIV  []byte
   211  	)
   212  
   213  	if isAead {
   214  		bodyKey := sha256.Sum256(reqBodyKey)
   215  		bodyIV := sha256.Sum256(reqBodyIV)
   216  		respBodyKey = bodyKey[:16]
   217  		respBodyIV = bodyIV[:16]
   218  	} else {
   219  		bodyKey := md5.Sum(reqBodyKey)
   220  		bodyIV := md5.Sum(reqBodyIV)
   221  		respBodyKey = bodyKey[:]
   222  		respBodyIV = bodyIV[:]
   223  	}
   224  
   225  	var writer io.Writer
   226  	var reader io.Reader
   227  	switch security {
   228  	case SecurityZero:
   229  		security = SecurityNone
   230  		if !dst.UDP {
   231  			reader = conn
   232  			writer = conn
   233  			option = 0
   234  		} else {
   235  			reader = newChunkReader(conn)
   236  			writer = newChunkWriter(conn)
   237  		}
   238  	case SecurityNone:
   239  		reader = newChunkReader(conn)
   240  		writer = newChunkWriter(conn)
   241  	case SecurityAES128GCM:
   242  		block, _ := aes.NewCipher(reqBodyKey[:])
   243  		aead, _ := cipher.NewGCM(block)
   244  		writer = newAEADWriter(conn, aead, reqBodyIV[:])
   245  
   246  		block, _ = aes.NewCipher(respBodyKey[:])
   247  		aead, _ = cipher.NewGCM(block)
   248  		reader = newAEADReader(conn, aead, respBodyIV[:])
   249  	case SecurityCHACHA20POLY1305:
   250  		key := make([]byte, 32)
   251  		t := md5.Sum(reqBodyKey[:])
   252  		copy(key, t[:])
   253  		t = md5.Sum(key[:16])
   254  		copy(key[16:], t[:])
   255  		aead, _ := chacha20poly1305.New(key)
   256  		writer = newAEADWriter(conn, aead, reqBodyIV[:])
   257  
   258  		t = md5.Sum(respBodyKey[:])
   259  		copy(key, t[:])
   260  		t = md5.Sum(key[:16])
   261  		copy(key[16:], t[:])
   262  		aead, _ = chacha20poly1305.New(key)
   263  		reader = newAEADReader(conn, aead, respBodyIV[:])
   264  	}
   265  
   266  	c := &Conn{
   267  		Conn:        conn,
   268  		id:          id,
   269  		dst:         dst,
   270  		reqBodyIV:   reqBodyIV,
   271  		reqBodyKey:  reqBodyKey,
   272  		respV:       respV,
   273  		respBodyIV:  respBodyIV[:],
   274  		respBodyKey: respBodyKey[:],
   275  		reader:      reader,
   276  		writer:      writer,
   277  		security:    security,
   278  		option:      option,
   279  		isAead:      isAead,
   280  	}
   281  	if err := c.sendRequest(); err != nil {
   282  		return nil, err
   283  	}
   284  	return c, nil
   285  }