github.com/xraypb/Xray-core@v1.8.1/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  	"sync"
    13  	"time"
    14  
    15  	"github.com/xraypb/Xray-core/common"
    16  	"github.com/xraypb/Xray-core/common/bitmask"
    17  	"github.com/xraypb/Xray-core/common/buf"
    18  	"github.com/xraypb/Xray-core/common/crypto"
    19  	"github.com/xraypb/Xray-core/common/drain"
    20  	"github.com/xraypb/Xray-core/common/net"
    21  	"github.com/xraypb/Xray-core/common/protocol"
    22  	"github.com/xraypb/Xray-core/common/task"
    23  	"github.com/xraypb/Xray-core/proxy/vmess"
    24  	vmessaead "github.com/xraypb/Xray-core/proxy/vmess/aead"
    25  	"golang.org/x/crypto/chacha20poly1305"
    26  )
    27  
    28  type sessionID struct {
    29  	user  [16]byte
    30  	key   [16]byte
    31  	nonce [16]byte
    32  }
    33  
    34  // SessionHistory keeps track of historical session ids, to prevent replay attacks.
    35  type SessionHistory struct {
    36  	sync.RWMutex
    37  	cache map[sessionID]time.Time
    38  	task  *task.Periodic
    39  }
    40  
    41  // NewSessionHistory creates a new SessionHistory object.
    42  func NewSessionHistory() *SessionHistory {
    43  	h := &SessionHistory{
    44  		cache: make(map[sessionID]time.Time, 128),
    45  	}
    46  	h.task = &task.Periodic{
    47  		Interval: time.Second * 30,
    48  		Execute:  h.removeExpiredEntries,
    49  	}
    50  	return h
    51  }
    52  
    53  // Close implements common.Closable.
    54  func (h *SessionHistory) Close() error {
    55  	return h.task.Close()
    56  }
    57  
    58  func (h *SessionHistory) addIfNotExits(session sessionID) bool {
    59  	h.Lock()
    60  
    61  	if expire, found := h.cache[session]; found && expire.After(time.Now()) {
    62  		h.Unlock()
    63  		return false
    64  	}
    65  
    66  	h.cache[session] = time.Now().Add(time.Minute * 3)
    67  	h.Unlock()
    68  	common.Must(h.task.Start())
    69  	return true
    70  }
    71  
    72  func (h *SessionHistory) removeExpiredEntries() error {
    73  	now := time.Now()
    74  
    75  	h.Lock()
    76  	defer h.Unlock()
    77  
    78  	if len(h.cache) == 0 {
    79  		return newError("nothing to do")
    80  	}
    81  
    82  	for session, expire := range h.cache {
    83  		if expire.Before(now) {
    84  			delete(h.cache, session)
    85  		}
    86  	}
    87  
    88  	if len(h.cache) == 0 {
    89  		h.cache = make(map[sessionID]time.Time, 128)
    90  	}
    91  
    92  	return nil
    93  }
    94  
    95  // ServerSession keeps information for a session in VMess server.
    96  type ServerSession struct {
    97  	userValidator   *vmess.TimedUserValidator
    98  	sessionHistory  *SessionHistory
    99  	requestBodyKey  [16]byte
   100  	requestBodyIV   [16]byte
   101  	responseBodyKey [16]byte
   102  	responseBodyIV  [16]byte
   103  	responseWriter  io.Writer
   104  	responseHeader  byte
   105  
   106  	isAEADRequest bool
   107  
   108  	isAEADForced bool
   109  }
   110  
   111  // NewServerSession creates a new ServerSession, using the given UserValidator.
   112  // The ServerSession instance doesn't take ownership of the validator.
   113  func NewServerSession(validator *vmess.TimedUserValidator, sessionHistory *SessionHistory) *ServerSession {
   114  	return &ServerSession{
   115  		userValidator:  validator,
   116  		sessionHistory: sessionHistory,
   117  	}
   118  }
   119  
   120  // SetAEADForced sets isAEADForced for a ServerSession.
   121  func (s *ServerSession) SetAEADForced(isAEADForced bool) {
   122  	s.isAEADForced = isAEADForced
   123  }
   124  
   125  func parseSecurityType(b byte) protocol.SecurityType {
   126  	if _, f := protocol.SecurityType_name[int32(b)]; f {
   127  		st := protocol.SecurityType(b)
   128  		// For backward compatibility.
   129  		if st == protocol.SecurityType_UNKNOWN {
   130  			st = protocol.SecurityType_LEGACY
   131  		}
   132  		return st
   133  	}
   134  	return protocol.SecurityType_UNKNOWN
   135  }
   136  
   137  // DecodeRequestHeader decodes and returns (if successful) a RequestHeader from an input stream.
   138  func (s *ServerSession) DecodeRequestHeader(reader io.Reader, isDrain bool) (*protocol.RequestHeader, error) {
   139  	buffer := buf.New()
   140  
   141  	drainer, err := drain.NewBehaviorSeedLimitedDrainer(int64(s.userValidator.GetBehaviorSeed()), 16+38, 3266, 64)
   142  	if err != nil {
   143  		return nil, newError("failed to initialize drainer").Base(err)
   144  	}
   145  
   146  	drainConnection := func(e error) error {
   147  		// We read a deterministic generated length of data before closing the connection to offset padding read pattern
   148  		drainer.AcknowledgeReceive(int(buffer.Len()))
   149  		if isDrain {
   150  			return drain.WithError(drainer, reader, e)
   151  		}
   152  		return e
   153  	}
   154  
   155  	defer func() {
   156  		buffer.Release()
   157  	}()
   158  
   159  	if _, err := buffer.ReadFullFrom(reader, protocol.IDBytesLen); err != nil {
   160  		return nil, newError("failed to read request header").Base(err)
   161  	}
   162  
   163  	var decryptor io.Reader
   164  	var vmessAccount *vmess.MemoryAccount
   165  
   166  	user, foundAEAD, errorAEAD := s.userValidator.GetAEAD(buffer.Bytes())
   167  
   168  	var fixedSizeAuthID [16]byte
   169  	copy(fixedSizeAuthID[:], buffer.Bytes())
   170  
   171  	switch {
   172  	case foundAEAD:
   173  		vmessAccount = user.Account.(*vmess.MemoryAccount)
   174  		var fixedSizeCmdKey [16]byte
   175  		copy(fixedSizeCmdKey[:], vmessAccount.ID.CmdKey())
   176  		aeadData, shouldDrain, bytesRead, errorReason := vmessaead.OpenVMessAEADHeader(fixedSizeCmdKey, fixedSizeAuthID, reader)
   177  		if errorReason != nil {
   178  			if shouldDrain {
   179  				drainer.AcknowledgeReceive(bytesRead)
   180  				return nil, drainConnection(newError("AEAD read failed").Base(errorReason))
   181  			} else {
   182  				return nil, drainConnection(newError("AEAD read failed, drain skipped").Base(errorReason))
   183  			}
   184  		}
   185  		decryptor = bytes.NewReader(aeadData)
   186  		s.isAEADRequest = true
   187  
   188  	case errorAEAD == vmessaead.ErrNotFound:
   189  		userLegacy, timestamp, valid, userValidationError := s.userValidator.Get(buffer.Bytes())
   190  		if !valid || userValidationError != nil {
   191  			return nil, drainConnection(newError("invalid user").Base(userValidationError))
   192  		}
   193  		if s.isAEADForced {
   194  			return nil, drainConnection(newError("invalid user: VMessAEAD is enforced and a non VMessAEAD connection is received. You can still disable this security feature with environment variable xray.vmess.aead.forced = false . You will not be able to enable legacy header workaround in the future."))
   195  		}
   196  		if s.userValidator.ShouldShowLegacyWarn() {
   197  			newError("Critical Warning: potentially invalid user: a non VMessAEAD connection is received. From 2022 Jan 1st, this kind of connection will be rejected by default. You should update or replace your client software now. This message will not be shown for further violation on this inbound.").AtWarning().WriteToLog()
   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  	drainer.AcknowledgeReceive(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, error) {
   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  		var ok bool
   321  		padding, ok = sizeParser.(crypto.PaddingLengthGenerator)
   322  		if !ok {
   323  			return nil, newError("invalid option: RequestOptionGlobalPadding")
   324  		}
   325  	}
   326  
   327  	switch request.Security {
   328  	case protocol.SecurityType_NONE:
   329  		if request.Option.Has(protocol.RequestOptionChunkStream) {
   330  			if request.Command.TransferType() == protocol.TransferTypeStream {
   331  				return crypto.NewChunkStreamReader(sizeParser, reader), nil
   332  			}
   333  
   334  			auth := &crypto.AEADAuthenticator{
   335  				AEAD:                    new(NoOpAuthenticator),
   336  				NonceGenerator:          crypto.GenerateEmptyBytes(),
   337  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   338  			}
   339  			return crypto.NewAuthenticationReader(auth, sizeParser, reader, protocol.TransferTypePacket, padding), nil
   340  		}
   341  		return buf.NewReader(reader), nil
   342  
   343  	case protocol.SecurityType_LEGACY:
   344  		aesStream := crypto.NewAesDecryptionStream(s.requestBodyKey[:], s.requestBodyIV[:])
   345  		cryptionReader := crypto.NewCryptionReader(aesStream, reader)
   346  		if request.Option.Has(protocol.RequestOptionChunkStream) {
   347  			auth := &crypto.AEADAuthenticator{
   348  				AEAD:                    new(FnvAuthenticator),
   349  				NonceGenerator:          crypto.GenerateEmptyBytes(),
   350  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   351  			}
   352  			return crypto.NewAuthenticationReader(auth, sizeParser, cryptionReader, request.Command.TransferType(), padding), nil
   353  		}
   354  		return buf.NewReader(cryptionReader), nil
   355  
   356  	case protocol.SecurityType_AES128_GCM:
   357  		aead := crypto.NewAesGcm(s.requestBodyKey[:])
   358  		auth := &crypto.AEADAuthenticator{
   359  			AEAD:                    aead,
   360  			NonceGenerator:          GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())),
   361  			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   362  		}
   363  		if request.Option.Has(protocol.RequestOptionAuthenticatedLength) {
   364  			AuthenticatedLengthKey := vmessaead.KDF16(s.requestBodyKey[:], "auth_len")
   365  			AuthenticatedLengthKeyAEAD := crypto.NewAesGcm(AuthenticatedLengthKey)
   366  
   367  			lengthAuth := &crypto.AEADAuthenticator{
   368  				AEAD:                    AuthenticatedLengthKeyAEAD,
   369  				NonceGenerator:          GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())),
   370  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   371  			}
   372  			sizeParser = NewAEADSizeParser(lengthAuth)
   373  		}
   374  		return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding), nil
   375  
   376  	case protocol.SecurityType_CHACHA20_POLY1305:
   377  		aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(s.requestBodyKey[:]))
   378  
   379  		auth := &crypto.AEADAuthenticator{
   380  			AEAD:                    aead,
   381  			NonceGenerator:          GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())),
   382  			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   383  		}
   384  		if request.Option.Has(protocol.RequestOptionAuthenticatedLength) {
   385  			AuthenticatedLengthKey := vmessaead.KDF16(s.requestBodyKey[:], "auth_len")
   386  			AuthenticatedLengthKeyAEAD, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(AuthenticatedLengthKey))
   387  			common.Must(err)
   388  
   389  			lengthAuth := &crypto.AEADAuthenticator{
   390  				AEAD:                    AuthenticatedLengthKeyAEAD,
   391  				NonceGenerator:          GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())),
   392  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   393  			}
   394  			sizeParser = NewAEADSizeParser(lengthAuth)
   395  		}
   396  		return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding), nil
   397  
   398  	default:
   399  		return nil, newError("invalid option: Security")
   400  	}
   401  }
   402  
   403  // EncodeResponseHeader writes encoded response header into the given writer.
   404  func (s *ServerSession) EncodeResponseHeader(header *protocol.ResponseHeader, writer io.Writer) {
   405  	var encryptionWriter io.Writer
   406  	if !s.isAEADRequest {
   407  		s.responseBodyKey = md5.Sum(s.requestBodyKey[:])
   408  		s.responseBodyIV = md5.Sum(s.requestBodyIV[:])
   409  	} else {
   410  		BodyKey := sha256.Sum256(s.requestBodyKey[:])
   411  		copy(s.responseBodyKey[:], BodyKey[:16])
   412  		BodyIV := sha256.Sum256(s.requestBodyIV[:])
   413  		copy(s.responseBodyIV[:], BodyIV[:16])
   414  	}
   415  
   416  	aesStream := crypto.NewAesEncryptionStream(s.responseBodyKey[:], s.responseBodyIV[:])
   417  	encryptionWriter = crypto.NewCryptionWriter(aesStream, writer)
   418  	s.responseWriter = encryptionWriter
   419  
   420  	aeadEncryptedHeaderBuffer := bytes.NewBuffer(nil)
   421  
   422  	if s.isAEADRequest {
   423  		encryptionWriter = aeadEncryptedHeaderBuffer
   424  	}
   425  
   426  	common.Must2(encryptionWriter.Write([]byte{s.responseHeader, byte(header.Option)}))
   427  	err := MarshalCommand(header.Command, encryptionWriter)
   428  	if err != nil {
   429  		common.Must2(encryptionWriter.Write([]byte{0x00, 0x00}))
   430  	}
   431  
   432  	if s.isAEADRequest {
   433  		aeadResponseHeaderLengthEncryptionKey := vmessaead.KDF16(s.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderLenKey)
   434  		aeadResponseHeaderLengthEncryptionIV := vmessaead.KDF(s.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderLenIV)[:12]
   435  
   436  		aeadResponseHeaderLengthEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderLengthEncryptionKey)).(cipher.Block)
   437  		aeadResponseHeaderLengthEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderLengthEncryptionKeyAESBlock)).(cipher.AEAD)
   438  
   439  		aeadResponseHeaderLengthEncryptionBuffer := bytes.NewBuffer(nil)
   440  
   441  		decryptedResponseHeaderLengthBinaryDeserializeBuffer := uint16(aeadEncryptedHeaderBuffer.Len())
   442  
   443  		common.Must(binary.Write(aeadResponseHeaderLengthEncryptionBuffer, binary.BigEndian, decryptedResponseHeaderLengthBinaryDeserializeBuffer))
   444  
   445  		AEADEncryptedLength := aeadResponseHeaderLengthEncryptionAEAD.Seal(nil, aeadResponseHeaderLengthEncryptionIV, aeadResponseHeaderLengthEncryptionBuffer.Bytes(), nil)
   446  		common.Must2(io.Copy(writer, bytes.NewReader(AEADEncryptedLength)))
   447  
   448  		aeadResponseHeaderPayloadEncryptionKey := vmessaead.KDF16(s.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadKey)
   449  		aeadResponseHeaderPayloadEncryptionIV := vmessaead.KDF(s.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadIV)[:12]
   450  
   451  		aeadResponseHeaderPayloadEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderPayloadEncryptionKey)).(cipher.Block)
   452  		aeadResponseHeaderPayloadEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderPayloadEncryptionKeyAESBlock)).(cipher.AEAD)
   453  
   454  		aeadEncryptedHeaderPayload := aeadResponseHeaderPayloadEncryptionAEAD.Seal(nil, aeadResponseHeaderPayloadEncryptionIV, aeadEncryptedHeaderBuffer.Bytes(), nil)
   455  		common.Must2(io.Copy(writer, bytes.NewReader(aeadEncryptedHeaderPayload)))
   456  	}
   457  }
   458  
   459  // EncodeResponseBody returns a Writer that auto-encrypt content written by caller.
   460  func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writer io.Writer) (buf.Writer, error) {
   461  	var sizeParser crypto.ChunkSizeEncoder = crypto.PlainChunkSizeParser{}
   462  	if request.Option.Has(protocol.RequestOptionChunkMasking) {
   463  		sizeParser = NewShakeSizeParser(s.responseBodyIV[:])
   464  	}
   465  	var padding crypto.PaddingLengthGenerator
   466  	if request.Option.Has(protocol.RequestOptionGlobalPadding) {
   467  		var ok bool
   468  		padding, ok = sizeParser.(crypto.PaddingLengthGenerator)
   469  		if !ok {
   470  			return nil, newError("invalid option: RequestOptionGlobalPadding")
   471  		}
   472  	}
   473  
   474  	switch request.Security {
   475  	case protocol.SecurityType_NONE:
   476  		if request.Option.Has(protocol.RequestOptionChunkStream) {
   477  			if request.Command.TransferType() == protocol.TransferTypeStream {
   478  				return crypto.NewChunkStreamWriter(sizeParser, writer), nil
   479  			}
   480  
   481  			auth := &crypto.AEADAuthenticator{
   482  				AEAD:                    new(NoOpAuthenticator),
   483  				NonceGenerator:          crypto.GenerateEmptyBytes(),
   484  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   485  			}
   486  			return crypto.NewAuthenticationWriter(auth, sizeParser, writer, protocol.TransferTypePacket, padding), nil
   487  		}
   488  		return buf.NewWriter(writer), nil
   489  
   490  	case protocol.SecurityType_LEGACY:
   491  		if request.Option.Has(protocol.RequestOptionChunkStream) {
   492  			auth := &crypto.AEADAuthenticator{
   493  				AEAD:                    new(FnvAuthenticator),
   494  				NonceGenerator:          crypto.GenerateEmptyBytes(),
   495  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   496  			}
   497  			return crypto.NewAuthenticationWriter(auth, sizeParser, s.responseWriter, request.Command.TransferType(), padding), nil
   498  		}
   499  		return &buf.SequentialWriter{Writer: s.responseWriter}, nil
   500  
   501  	case protocol.SecurityType_AES128_GCM:
   502  		aead := crypto.NewAesGcm(s.responseBodyKey[:])
   503  		auth := &crypto.AEADAuthenticator{
   504  			AEAD:                    aead,
   505  			NonceGenerator:          GenerateChunkNonce(s.responseBodyIV[:], uint32(aead.NonceSize())),
   506  			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   507  		}
   508  		if request.Option.Has(protocol.RequestOptionAuthenticatedLength) {
   509  			AuthenticatedLengthKey := vmessaead.KDF16(s.requestBodyKey[:], "auth_len")
   510  			AuthenticatedLengthKeyAEAD := crypto.NewAesGcm(AuthenticatedLengthKey)
   511  
   512  			lengthAuth := &crypto.AEADAuthenticator{
   513  				AEAD:                    AuthenticatedLengthKeyAEAD,
   514  				NonceGenerator:          GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())),
   515  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   516  			}
   517  			sizeParser = NewAEADSizeParser(lengthAuth)
   518  		}
   519  		return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding), nil
   520  
   521  	case protocol.SecurityType_CHACHA20_POLY1305:
   522  		aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(s.responseBodyKey[:]))
   523  
   524  		auth := &crypto.AEADAuthenticator{
   525  			AEAD:                    aead,
   526  			NonceGenerator:          GenerateChunkNonce(s.responseBodyIV[:], uint32(aead.NonceSize())),
   527  			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   528  		}
   529  		if request.Option.Has(protocol.RequestOptionAuthenticatedLength) {
   530  			AuthenticatedLengthKey := vmessaead.KDF16(s.requestBodyKey[:], "auth_len")
   531  			AuthenticatedLengthKeyAEAD, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(AuthenticatedLengthKey))
   532  			common.Must(err)
   533  
   534  			lengthAuth := &crypto.AEADAuthenticator{
   535  				AEAD:                    AuthenticatedLengthKeyAEAD,
   536  				NonceGenerator:          GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())),
   537  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   538  			}
   539  			sizeParser = NewAEADSizeParser(lengthAuth)
   540  		}
   541  		return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding), nil
   542  
   543  	default:
   544  		return nil, newError("invalid option: Security")
   545  	}
   546  }