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 }