github.com/metacubex/mihomo@v1.18.5/transport/vmess/conn.go (about)

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