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 }