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