github.com/eagleql/xray-core@v1.4.4/proxy/vmess/encoding/server.go (about) 1 package encoding 2 3 import ( 4 "bytes" 5 "crypto/aes" 6 "crypto/cipher" 7 "crypto/md5" 8 "crypto/sha256" 9 "encoding/binary" 10 "hash/fnv" 11 "io" 12 "io/ioutil" 13 "sync" 14 "time" 15 16 "github.com/eagleql/xray-core/common" 17 "github.com/eagleql/xray-core/common/bitmask" 18 "github.com/eagleql/xray-core/common/buf" 19 "github.com/eagleql/xray-core/common/crypto" 20 "github.com/eagleql/xray-core/common/dice" 21 "github.com/eagleql/xray-core/common/net" 22 "github.com/eagleql/xray-core/common/protocol" 23 "github.com/eagleql/xray-core/common/task" 24 "github.com/eagleql/xray-core/proxy/vmess" 25 vmessaead "github.com/eagleql/xray-core/proxy/vmess/aead" 26 "golang.org/x/crypto/chacha20poly1305" 27 ) 28 29 type sessionID struct { 30 user [16]byte 31 key [16]byte 32 nonce [16]byte 33 } 34 35 // SessionHistory keeps track of historical session ids, to prevent replay attacks. 36 type SessionHistory struct { 37 sync.RWMutex 38 cache map[sessionID]time.Time 39 task *task.Periodic 40 } 41 42 // NewSessionHistory creates a new SessionHistory object. 43 func NewSessionHistory() *SessionHistory { 44 h := &SessionHistory{ 45 cache: make(map[sessionID]time.Time, 128), 46 } 47 h.task = &task.Periodic{ 48 Interval: time.Second * 30, 49 Execute: h.removeExpiredEntries, 50 } 51 return h 52 } 53 54 // Close implements common.Closable. 55 func (h *SessionHistory) Close() error { 56 return h.task.Close() 57 } 58 59 func (h *SessionHistory) addIfNotExits(session sessionID) bool { 60 h.Lock() 61 62 if expire, found := h.cache[session]; found && expire.After(time.Now()) { 63 h.Unlock() 64 return false 65 } 66 67 h.cache[session] = time.Now().Add(time.Minute * 3) 68 h.Unlock() 69 common.Must(h.task.Start()) 70 return true 71 } 72 73 func (h *SessionHistory) removeExpiredEntries() error { 74 now := time.Now() 75 76 h.Lock() 77 defer h.Unlock() 78 79 if len(h.cache) == 0 { 80 return newError("nothing to do") 81 } 82 83 for session, expire := range h.cache { 84 if expire.Before(now) { 85 delete(h.cache, session) 86 } 87 } 88 89 if len(h.cache) == 0 { 90 h.cache = make(map[sessionID]time.Time, 128) 91 } 92 93 return nil 94 } 95 96 // ServerSession keeps information for a session in VMess server. 97 type ServerSession struct { 98 userValidator *vmess.TimedUserValidator 99 sessionHistory *SessionHistory 100 requestBodyKey [16]byte 101 requestBodyIV [16]byte 102 responseBodyKey [16]byte 103 responseBodyIV [16]byte 104 responseWriter io.Writer 105 responseHeader byte 106 107 isAEADRequest bool 108 109 isAEADForced bool 110 } 111 112 // NewServerSession creates a new ServerSession, using the given UserValidator. 113 // The ServerSession instance doesn't take ownership of the validator. 114 func NewServerSession(validator *vmess.TimedUserValidator, sessionHistory *SessionHistory) *ServerSession { 115 return &ServerSession{ 116 userValidator: validator, 117 sessionHistory: sessionHistory, 118 } 119 } 120 121 // SetAEADForced sets isAEADForced for a ServerSession. 122 func (s *ServerSession) SetAEADForced(isAEADForced bool) { 123 s.isAEADForced = isAEADForced 124 } 125 126 func parseSecurityType(b byte) protocol.SecurityType { 127 if _, f := protocol.SecurityType_name[int32(b)]; f { 128 st := protocol.SecurityType(b) 129 // For backward compatibility. 130 if st == protocol.SecurityType_UNKNOWN { 131 st = protocol.SecurityType_LEGACY 132 } 133 return st 134 } 135 return protocol.SecurityType_UNKNOWN 136 } 137 138 // DecodeRequestHeader decodes and returns (if successful) a RequestHeader from an input stream. 139 func (s *ServerSession) DecodeRequestHeader(reader io.Reader, isDrain bool) (*protocol.RequestHeader, error) { 140 buffer := buf.New() 141 behaviorRand := dice.NewDeterministicDice(int64(s.userValidator.GetBehaviorSeed())) 142 BaseDrainSize := behaviorRand.Roll(3266) 143 RandDrainMax := behaviorRand.Roll(64) + 1 144 RandDrainRolled := dice.Roll(RandDrainMax) 145 DrainSize := BaseDrainSize + 16 + 38 + RandDrainRolled 146 readSizeRemain := DrainSize 147 148 drainConnection := func(e error) error { 149 // We read a deterministic generated length of data before closing the connection to offset padding read pattern 150 readSizeRemain -= int(buffer.Len()) 151 if readSizeRemain > 0 && isDrain { 152 err := s.DrainConnN(reader, readSizeRemain) 153 if err != nil { 154 return newError("failed to drain connection DrainSize = ", BaseDrainSize, " ", RandDrainMax, " ", RandDrainRolled).Base(err).Base(e) 155 } 156 return newError("connection drained DrainSize = ", BaseDrainSize, " ", RandDrainMax, " ", RandDrainRolled).Base(e) 157 } 158 return e 159 } 160 161 defer func() { 162 buffer.Release() 163 }() 164 165 if _, err := buffer.ReadFullFrom(reader, protocol.IDBytesLen); err != nil { 166 return nil, newError("failed to read request header").Base(err) 167 } 168 169 var decryptor io.Reader 170 var vmessAccount *vmess.MemoryAccount 171 172 user, foundAEAD, errorAEAD := s.userValidator.GetAEAD(buffer.Bytes()) 173 174 var fixedSizeAuthID [16]byte 175 copy(fixedSizeAuthID[:], buffer.Bytes()) 176 177 switch { 178 case foundAEAD: 179 vmessAccount = user.Account.(*vmess.MemoryAccount) 180 var fixedSizeCmdKey [16]byte 181 copy(fixedSizeCmdKey[:], vmessAccount.ID.CmdKey()) 182 aeadData, shouldDrain, bytesRead, errorReason := vmessaead.OpenVMessAEADHeader(fixedSizeCmdKey, fixedSizeAuthID, reader) 183 if errorReason != nil { 184 if shouldDrain { 185 readSizeRemain -= bytesRead 186 return nil, drainConnection(newError("AEAD read failed").Base(errorReason)) 187 } else { 188 return nil, drainConnection(newError("AEAD read failed, drain skipped").Base(errorReason)) 189 } 190 } 191 decryptor = bytes.NewReader(aeadData) 192 s.isAEADRequest = true 193 194 case !s.isAEADForced && errorAEAD == vmessaead.ErrNotFound: 195 userLegacy, timestamp, valid, userValidationError := s.userValidator.Get(buffer.Bytes()) 196 if !valid || userValidationError != nil { 197 return nil, drainConnection(newError("invalid user").Base(userValidationError)) 198 } 199 user = userLegacy 200 iv := hashTimestamp(md5.New(), timestamp) 201 vmessAccount = userLegacy.Account.(*vmess.MemoryAccount) 202 203 aesStream := crypto.NewAesDecryptionStream(vmessAccount.ID.CmdKey(), iv) 204 decryptor = crypto.NewCryptionReader(aesStream, reader) 205 206 default: 207 return nil, drainConnection(newError("invalid user").Base(errorAEAD)) 208 } 209 210 readSizeRemain -= int(buffer.Len()) 211 buffer.Clear() 212 if _, err := buffer.ReadFullFrom(decryptor, 38); err != nil { 213 return nil, newError("failed to read request header").Base(err) 214 } 215 216 request := &protocol.RequestHeader{ 217 User: user, 218 Version: buffer.Byte(0), 219 } 220 221 copy(s.requestBodyIV[:], buffer.BytesRange(1, 17)) // 16 bytes 222 copy(s.requestBodyKey[:], buffer.BytesRange(17, 33)) // 16 bytes 223 var sid sessionID 224 copy(sid.user[:], vmessAccount.ID.Bytes()) 225 sid.key = s.requestBodyKey 226 sid.nonce = s.requestBodyIV 227 if !s.sessionHistory.addIfNotExits(sid) { 228 if !s.isAEADRequest { 229 drainErr := s.userValidator.BurnTaintFuse(fixedSizeAuthID[:]) 230 if drainErr != nil { 231 return nil, drainConnection(newError("duplicated session id, possibly under replay attack, and failed to taint userHash").Base(drainErr)) 232 } 233 return nil, drainConnection(newError("duplicated session id, possibly under replay attack, userHash tainted")) 234 } else { 235 return nil, newError("duplicated session id, possibly under replay attack, but this is a AEAD request") 236 } 237 } 238 239 s.responseHeader = buffer.Byte(33) // 1 byte 240 request.Option = bitmask.Byte(buffer.Byte(34)) // 1 byte 241 paddingLen := int(buffer.Byte(35) >> 4) 242 request.Security = parseSecurityType(buffer.Byte(35) & 0x0F) 243 // 1 bytes reserved 244 request.Command = protocol.RequestCommand(buffer.Byte(37)) 245 246 switch request.Command { 247 case protocol.RequestCommandMux: 248 request.Address = net.DomainAddress("v1.mux.cool") 249 request.Port = 0 250 251 case protocol.RequestCommandTCP, protocol.RequestCommandUDP: 252 if addr, port, err := addrParser.ReadAddressPort(buffer, decryptor); err == nil { 253 request.Address = addr 254 request.Port = port 255 } 256 } 257 258 if paddingLen > 0 { 259 if _, err := buffer.ReadFullFrom(decryptor, int32(paddingLen)); err != nil { 260 if !s.isAEADRequest { 261 burnErr := s.userValidator.BurnTaintFuse(fixedSizeAuthID[:]) 262 if burnErr != nil { 263 return nil, newError("failed to read padding, failed to taint userHash").Base(burnErr).Base(err) 264 } 265 return nil, newError("failed to read padding, userHash tainted").Base(err) 266 } 267 return nil, newError("failed to read padding").Base(err) 268 } 269 } 270 271 if _, err := buffer.ReadFullFrom(decryptor, 4); err != nil { 272 if !s.isAEADRequest { 273 burnErr := s.userValidator.BurnTaintFuse(fixedSizeAuthID[:]) 274 if burnErr != nil { 275 return nil, newError("failed to read checksum, failed to taint userHash").Base(burnErr).Base(err) 276 } 277 return nil, newError("failed to read checksum, userHash tainted").Base(err) 278 } 279 return nil, newError("failed to read checksum").Base(err) 280 } 281 282 fnv1a := fnv.New32a() 283 common.Must2(fnv1a.Write(buffer.BytesTo(-4))) 284 actualHash := fnv1a.Sum32() 285 expectedHash := binary.BigEndian.Uint32(buffer.BytesFrom(-4)) 286 287 if actualHash != expectedHash { 288 if !s.isAEADRequest { 289 Autherr := newError("invalid auth, legacy userHash tainted") 290 burnErr := s.userValidator.BurnTaintFuse(fixedSizeAuthID[:]) 291 if burnErr != nil { 292 Autherr = newError("invalid auth, can't taint legacy userHash").Base(burnErr) 293 } 294 // It is possible that we are under attack described in https://github.com/xray/xray-core/issues/2523 295 return nil, drainConnection(Autherr) 296 } else { 297 return nil, newError("invalid auth, but this is a AEAD request") 298 } 299 } 300 301 if request.Address == nil { 302 return nil, newError("invalid remote address") 303 } 304 305 if request.Security == protocol.SecurityType_UNKNOWN || request.Security == protocol.SecurityType_AUTO { 306 return nil, newError("unknown security type: ", request.Security) 307 } 308 309 return request, nil 310 } 311 312 // DecodeRequestBody returns Reader from which caller can fetch decrypted body. 313 func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reader io.Reader) buf.Reader { 314 var sizeParser crypto.ChunkSizeDecoder = crypto.PlainChunkSizeParser{} 315 if request.Option.Has(protocol.RequestOptionChunkMasking) { 316 sizeParser = NewShakeSizeParser(s.requestBodyIV[:]) 317 } 318 var padding crypto.PaddingLengthGenerator 319 if request.Option.Has(protocol.RequestOptionGlobalPadding) { 320 padding = sizeParser.(crypto.PaddingLengthGenerator) 321 } 322 323 switch request.Security { 324 case protocol.SecurityType_NONE: 325 if request.Option.Has(protocol.RequestOptionChunkStream) { 326 if request.Command.TransferType() == protocol.TransferTypeStream { 327 return crypto.NewChunkStreamReader(sizeParser, reader) 328 } 329 330 auth := &crypto.AEADAuthenticator{ 331 AEAD: new(NoOpAuthenticator), 332 NonceGenerator: crypto.GenerateEmptyBytes(), 333 AdditionalDataGenerator: crypto.GenerateEmptyBytes(), 334 } 335 return crypto.NewAuthenticationReader(auth, sizeParser, reader, protocol.TransferTypePacket, padding) 336 } 337 return buf.NewReader(reader) 338 339 case protocol.SecurityType_LEGACY: 340 aesStream := crypto.NewAesDecryptionStream(s.requestBodyKey[:], s.requestBodyIV[:]) 341 cryptionReader := crypto.NewCryptionReader(aesStream, reader) 342 if request.Option.Has(protocol.RequestOptionChunkStream) { 343 auth := &crypto.AEADAuthenticator{ 344 AEAD: new(FnvAuthenticator), 345 NonceGenerator: crypto.GenerateEmptyBytes(), 346 AdditionalDataGenerator: crypto.GenerateEmptyBytes(), 347 } 348 return crypto.NewAuthenticationReader(auth, sizeParser, cryptionReader, request.Command.TransferType(), padding) 349 } 350 return buf.NewReader(cryptionReader) 351 352 case protocol.SecurityType_AES128_GCM: 353 aead := crypto.NewAesGcm(s.requestBodyKey[:]) 354 auth := &crypto.AEADAuthenticator{ 355 AEAD: aead, 356 NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())), 357 AdditionalDataGenerator: crypto.GenerateEmptyBytes(), 358 } 359 return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding) 360 361 case protocol.SecurityType_CHACHA20_POLY1305: 362 aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(s.requestBodyKey[:])) 363 364 auth := &crypto.AEADAuthenticator{ 365 AEAD: aead, 366 NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())), 367 AdditionalDataGenerator: crypto.GenerateEmptyBytes(), 368 } 369 return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding) 370 371 default: 372 panic("Unknown security type.") 373 } 374 } 375 376 // EncodeResponseHeader writes encoded response header into the given writer. 377 func (s *ServerSession) EncodeResponseHeader(header *protocol.ResponseHeader, writer io.Writer) { 378 var encryptionWriter io.Writer 379 if !s.isAEADRequest { 380 s.responseBodyKey = md5.Sum(s.requestBodyKey[:]) 381 s.responseBodyIV = md5.Sum(s.requestBodyIV[:]) 382 } else { 383 BodyKey := sha256.Sum256(s.requestBodyKey[:]) 384 copy(s.responseBodyKey[:], BodyKey[:16]) 385 BodyIV := sha256.Sum256(s.requestBodyIV[:]) 386 copy(s.responseBodyIV[:], BodyIV[:16]) 387 } 388 389 aesStream := crypto.NewAesEncryptionStream(s.responseBodyKey[:], s.responseBodyIV[:]) 390 encryptionWriter = crypto.NewCryptionWriter(aesStream, writer) 391 s.responseWriter = encryptionWriter 392 393 aeadEncryptedHeaderBuffer := bytes.NewBuffer(nil) 394 395 if s.isAEADRequest { 396 encryptionWriter = aeadEncryptedHeaderBuffer 397 } 398 399 common.Must2(encryptionWriter.Write([]byte{s.responseHeader, byte(header.Option)})) 400 err := MarshalCommand(header.Command, encryptionWriter) 401 if err != nil { 402 common.Must2(encryptionWriter.Write([]byte{0x00, 0x00})) 403 } 404 405 if s.isAEADRequest { 406 aeadResponseHeaderLengthEncryptionKey := vmessaead.KDF16(s.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderLenKey) 407 aeadResponseHeaderLengthEncryptionIV := vmessaead.KDF(s.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderLenIV)[:12] 408 409 aeadResponseHeaderLengthEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderLengthEncryptionKey)).(cipher.Block) 410 aeadResponseHeaderLengthEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderLengthEncryptionKeyAESBlock)).(cipher.AEAD) 411 412 aeadResponseHeaderLengthEncryptionBuffer := bytes.NewBuffer(nil) 413 414 decryptedResponseHeaderLengthBinaryDeserializeBuffer := uint16(aeadEncryptedHeaderBuffer.Len()) 415 416 common.Must(binary.Write(aeadResponseHeaderLengthEncryptionBuffer, binary.BigEndian, decryptedResponseHeaderLengthBinaryDeserializeBuffer)) 417 418 AEADEncryptedLength := aeadResponseHeaderLengthEncryptionAEAD.Seal(nil, aeadResponseHeaderLengthEncryptionIV, aeadResponseHeaderLengthEncryptionBuffer.Bytes(), nil) 419 common.Must2(io.Copy(writer, bytes.NewReader(AEADEncryptedLength))) 420 421 aeadResponseHeaderPayloadEncryptionKey := vmessaead.KDF16(s.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadKey) 422 aeadResponseHeaderPayloadEncryptionIV := vmessaead.KDF(s.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadIV)[:12] 423 424 aeadResponseHeaderPayloadEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderPayloadEncryptionKey)).(cipher.Block) 425 aeadResponseHeaderPayloadEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderPayloadEncryptionKeyAESBlock)).(cipher.AEAD) 426 427 aeadEncryptedHeaderPayload := aeadResponseHeaderPayloadEncryptionAEAD.Seal(nil, aeadResponseHeaderPayloadEncryptionIV, aeadEncryptedHeaderBuffer.Bytes(), nil) 428 common.Must2(io.Copy(writer, bytes.NewReader(aeadEncryptedHeaderPayload))) 429 } 430 } 431 432 // EncodeResponseBody returns a Writer that auto-encrypt content written by caller. 433 func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writer io.Writer) buf.Writer { 434 var sizeParser crypto.ChunkSizeEncoder = crypto.PlainChunkSizeParser{} 435 if request.Option.Has(protocol.RequestOptionChunkMasking) { 436 sizeParser = NewShakeSizeParser(s.responseBodyIV[:]) 437 } 438 var padding crypto.PaddingLengthGenerator 439 if request.Option.Has(protocol.RequestOptionGlobalPadding) { 440 padding = sizeParser.(crypto.PaddingLengthGenerator) 441 } 442 443 switch request.Security { 444 case protocol.SecurityType_NONE: 445 if request.Option.Has(protocol.RequestOptionChunkStream) { 446 if request.Command.TransferType() == protocol.TransferTypeStream { 447 return crypto.NewChunkStreamWriter(sizeParser, writer) 448 } 449 450 auth := &crypto.AEADAuthenticator{ 451 AEAD: new(NoOpAuthenticator), 452 NonceGenerator: crypto.GenerateEmptyBytes(), 453 AdditionalDataGenerator: crypto.GenerateEmptyBytes(), 454 } 455 return crypto.NewAuthenticationWriter(auth, sizeParser, writer, protocol.TransferTypePacket, padding) 456 } 457 return buf.NewWriter(writer) 458 459 case protocol.SecurityType_LEGACY: 460 if request.Option.Has(protocol.RequestOptionChunkStream) { 461 auth := &crypto.AEADAuthenticator{ 462 AEAD: new(FnvAuthenticator), 463 NonceGenerator: crypto.GenerateEmptyBytes(), 464 AdditionalDataGenerator: crypto.GenerateEmptyBytes(), 465 } 466 return crypto.NewAuthenticationWriter(auth, sizeParser, s.responseWriter, request.Command.TransferType(), padding) 467 } 468 return &buf.SequentialWriter{Writer: s.responseWriter} 469 470 case protocol.SecurityType_AES128_GCM: 471 aead := crypto.NewAesGcm(s.responseBodyKey[:]) 472 auth := &crypto.AEADAuthenticator{ 473 AEAD: aead, 474 NonceGenerator: GenerateChunkNonce(s.responseBodyIV[:], uint32(aead.NonceSize())), 475 AdditionalDataGenerator: crypto.GenerateEmptyBytes(), 476 } 477 return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding) 478 479 case protocol.SecurityType_CHACHA20_POLY1305: 480 aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(s.responseBodyKey[:])) 481 482 auth := &crypto.AEADAuthenticator{ 483 AEAD: aead, 484 NonceGenerator: GenerateChunkNonce(s.responseBodyIV[:], uint32(aead.NonceSize())), 485 AdditionalDataGenerator: crypto.GenerateEmptyBytes(), 486 } 487 return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding) 488 489 default: 490 panic("Unknown security type.") 491 } 492 } 493 494 func (s *ServerSession) DrainConnN(reader io.Reader, n int) error { 495 _, err := io.CopyN(ioutil.Discard, reader, int64(n)) 496 return err 497 }