github.com/chwjbn/xclash@v0.2.0/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 }