github.com/v2fly/v2ray-core/v4@v4.45.2/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  	"golang.org/x/crypto/chacha20poly1305"
    16  
    17  	"github.com/v2fly/v2ray-core/v4/common"
    18  	"github.com/v2fly/v2ray-core/v4/common/bitmask"
    19  	"github.com/v2fly/v2ray-core/v4/common/buf"
    20  	"github.com/v2fly/v2ray-core/v4/common/crypto"
    21  	"github.com/v2fly/v2ray-core/v4/common/drain"
    22  	"github.com/v2fly/v2ray-core/v4/common/net"
    23  	"github.com/v2fly/v2ray-core/v4/common/protocol"
    24  	"github.com/v2fly/v2ray-core/v4/common/task"
    25  	"github.com/v2fly/v2ray-core/v4/proxy/vmess"
    26  	vmessaead "github.com/v2fly/v2ray-core/v4/proxy/vmess/aead"
    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) (*protocol.RequestHeader, error) {
   140  	buffer := buf.New()
   141  
   142  	drainer, err := drain.NewBehaviorSeedLimitedDrainer(int64(s.userValidator.GetBehaviorSeed()), 16+38, 3266, 64)
   143  	if err != nil {
   144  		return nil, newError("failed to initialize drainer").Base(err)
   145  	}
   146  
   147  	drainConnection := func(e error) error {
   148  		// We read a deterministic generated length of data before closing the connection to offset padding read pattern
   149  		drainer.AcknowledgeReceive(int(buffer.Len()))
   150  		return drain.WithError(drainer, reader, e)
   151  	}
   152  
   153  	defer func() {
   154  		buffer.Release()
   155  	}()
   156  
   157  	if _, err := buffer.ReadFullFrom(reader, protocol.IDBytesLen); err != nil {
   158  		return nil, newError("failed to read request header").Base(err)
   159  	}
   160  
   161  	var decryptor io.Reader
   162  	var vmessAccount *vmess.MemoryAccount
   163  
   164  	user, foundAEAD, errorAEAD := s.userValidator.GetAEAD(buffer.Bytes())
   165  
   166  	var fixedSizeAuthID [16]byte
   167  	copy(fixedSizeAuthID[:], buffer.Bytes())
   168  
   169  	switch {
   170  	case foundAEAD:
   171  		vmessAccount = user.Account.(*vmess.MemoryAccount)
   172  		var fixedSizeCmdKey [16]byte
   173  		copy(fixedSizeCmdKey[:], vmessAccount.ID.CmdKey())
   174  		aeadData, shouldDrain, bytesRead, errorReason := vmessaead.OpenVMessAEADHeader(fixedSizeCmdKey, fixedSizeAuthID, reader)
   175  		if errorReason != nil {
   176  			if shouldDrain {
   177  				drainer.AcknowledgeReceive(bytesRead)
   178  				return nil, drainConnection(newError("AEAD read failed").Base(errorReason))
   179  			}
   180  			return nil, drainConnection(newError("AEAD read failed, drain skipped").Base(errorReason))
   181  		}
   182  		decryptor = bytes.NewReader(aeadData)
   183  		s.isAEADRequest = true
   184  
   185  	case errorAEAD == vmessaead.ErrNotFound:
   186  		userLegacy, timestamp, valid, userValidationError := s.userValidator.Get(buffer.Bytes())
   187  		if !valid || userValidationError != nil {
   188  			return nil, drainConnection(newError("invalid user").Base(userValidationError))
   189  		}
   190  		if s.isAEADForced {
   191  			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 v2ray.vmess.aead.forced = false . You will not be able to enable legacy header workaround in the future."))
   192  		}
   193  		if s.userValidator.ShouldShowLegacyWarn() {
   194  			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()
   195  		}
   196  		user = userLegacy
   197  		iv := hashTimestamp(md5.New(), timestamp)
   198  		vmessAccount = userLegacy.Account.(*vmess.MemoryAccount)
   199  
   200  		aesStream := crypto.NewAesDecryptionStream(vmessAccount.ID.CmdKey(), iv)
   201  		decryptor = crypto.NewCryptionReader(aesStream, reader)
   202  
   203  	default:
   204  		return nil, drainConnection(newError("invalid user").Base(errorAEAD))
   205  	}
   206  
   207  	drainer.AcknowledgeReceive(int(buffer.Len()))
   208  	buffer.Clear()
   209  	if _, err := buffer.ReadFullFrom(decryptor, 38); err != nil {
   210  		return nil, newError("failed to read request header").Base(err)
   211  	}
   212  
   213  	request := &protocol.RequestHeader{
   214  		User:    user,
   215  		Version: buffer.Byte(0),
   216  	}
   217  
   218  	copy(s.requestBodyIV[:], buffer.BytesRange(1, 17))   // 16 bytes
   219  	copy(s.requestBodyKey[:], buffer.BytesRange(17, 33)) // 16 bytes
   220  	var sid sessionID
   221  	copy(sid.user[:], vmessAccount.ID.Bytes())
   222  	sid.key = s.requestBodyKey
   223  	sid.nonce = s.requestBodyIV
   224  	if !s.sessionHistory.addIfNotExits(sid) {
   225  		if !s.isAEADRequest {
   226  			drainErr := s.userValidator.BurnTaintFuse(fixedSizeAuthID[:])
   227  			if drainErr != nil {
   228  				return nil, drainConnection(newError("duplicated session id, possibly under replay attack, and failed to taint userHash").Base(drainErr))
   229  			}
   230  			return nil, drainConnection(newError("duplicated session id, possibly under replay attack, userHash tainted"))
   231  		}
   232  		return nil, newError("duplicated session id, possibly under replay attack, but this is a AEAD request")
   233  	}
   234  
   235  	s.responseHeader = buffer.Byte(33)             // 1 byte
   236  	request.Option = bitmask.Byte(buffer.Byte(34)) // 1 byte
   237  	paddingLen := int(buffer.Byte(35) >> 4)
   238  	request.Security = parseSecurityType(buffer.Byte(35) & 0x0F)
   239  	// 1 bytes reserved
   240  	request.Command = protocol.RequestCommand(buffer.Byte(37))
   241  
   242  	switch request.Command {
   243  	case protocol.RequestCommandMux:
   244  		request.Address = net.DomainAddress("v1.mux.cool")
   245  		request.Port = 0
   246  
   247  	case protocol.RequestCommandTCP, protocol.RequestCommandUDP:
   248  		if addr, port, err := addrParser.ReadAddressPort(buffer, decryptor); err == nil {
   249  			request.Address = addr
   250  			request.Port = port
   251  		}
   252  	}
   253  
   254  	if paddingLen > 0 {
   255  		if _, err := buffer.ReadFullFrom(decryptor, int32(paddingLen)); err != nil {
   256  			if !s.isAEADRequest {
   257  				burnErr := s.userValidator.BurnTaintFuse(fixedSizeAuthID[:])
   258  				if burnErr != nil {
   259  					return nil, newError("failed to read padding, failed to taint userHash").Base(burnErr).Base(err)
   260  				}
   261  				return nil, newError("failed to read padding, userHash tainted").Base(err)
   262  			}
   263  			return nil, newError("failed to read padding").Base(err)
   264  		}
   265  	}
   266  
   267  	if _, err := buffer.ReadFullFrom(decryptor, 4); err != nil {
   268  		if !s.isAEADRequest {
   269  			burnErr := s.userValidator.BurnTaintFuse(fixedSizeAuthID[:])
   270  			if burnErr != nil {
   271  				return nil, newError("failed to read checksum, failed to taint userHash").Base(burnErr).Base(err)
   272  			}
   273  			return nil, newError("failed to read checksum, userHash tainted").Base(err)
   274  		}
   275  		return nil, newError("failed to read checksum").Base(err)
   276  	}
   277  
   278  	fnv1a := fnv.New32a()
   279  	common.Must2(fnv1a.Write(buffer.BytesTo(-4)))
   280  	actualHash := fnv1a.Sum32()
   281  	expectedHash := binary.BigEndian.Uint32(buffer.BytesFrom(-4))
   282  
   283  	if actualHash != expectedHash {
   284  		if !s.isAEADRequest {
   285  			Autherr := newError("invalid auth, legacy userHash tainted")
   286  			burnErr := s.userValidator.BurnTaintFuse(fixedSizeAuthID[:])
   287  			if burnErr != nil {
   288  				Autherr = newError("invalid auth, can't taint legacy userHash").Base(burnErr)
   289  			}
   290  			// It is possible that we are under attack described in https://github.com/v2ray/v2ray-core/issues/2523
   291  			return nil, drainConnection(Autherr)
   292  		}
   293  		return nil, newError("invalid auth, but this is a AEAD request")
   294  	}
   295  
   296  	if request.Address == nil {
   297  		return nil, newError("invalid remote address")
   298  	}
   299  
   300  	if request.Security == protocol.SecurityType_UNKNOWN || request.Security == protocol.SecurityType_AUTO {
   301  		return nil, newError("unknown security type: ", request.Security)
   302  	}
   303  
   304  	return request, nil
   305  }
   306  
   307  // DecodeRequestBody returns Reader from which caller can fetch decrypted body.
   308  func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reader io.Reader) (buf.Reader, error) {
   309  	var sizeParser crypto.ChunkSizeDecoder = crypto.PlainChunkSizeParser{}
   310  	if request.Option.Has(protocol.RequestOptionChunkMasking) {
   311  		sizeParser = NewShakeSizeParser(s.requestBodyIV[:])
   312  	}
   313  	var padding crypto.PaddingLengthGenerator
   314  	if request.Option.Has(protocol.RequestOptionGlobalPadding) {
   315  		var ok bool
   316  		padding, ok = sizeParser.(crypto.PaddingLengthGenerator)
   317  		if !ok {
   318  			return nil, newError("invalid option: RequestOptionGlobalPadding")
   319  		}
   320  	}
   321  
   322  	switch request.Security {
   323  	case protocol.SecurityType_NONE:
   324  		if request.Option.Has(protocol.RequestOptionChunkStream) {
   325  			if request.Command.TransferType() == protocol.TransferTypeStream {
   326  				return crypto.NewChunkStreamReader(sizeParser, reader), nil
   327  			}
   328  
   329  			auth := &crypto.AEADAuthenticator{
   330  				AEAD:                    new(NoOpAuthenticator),
   331  				NonceGenerator:          crypto.GenerateEmptyBytes(),
   332  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   333  			}
   334  			return crypto.NewAuthenticationReader(auth, sizeParser, reader, protocol.TransferTypePacket, padding), nil
   335  		}
   336  		return buf.NewReader(reader), nil
   337  
   338  	case protocol.SecurityType_LEGACY:
   339  		aesStream := crypto.NewAesDecryptionStream(s.requestBodyKey[:], s.requestBodyIV[:])
   340  		cryptionReader := crypto.NewCryptionReader(aesStream, reader)
   341  		if request.Option.Has(protocol.RequestOptionChunkStream) {
   342  			auth := &crypto.AEADAuthenticator{
   343  				AEAD:                    new(FnvAuthenticator),
   344  				NonceGenerator:          crypto.GenerateEmptyBytes(),
   345  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   346  			}
   347  			return crypto.NewAuthenticationReader(auth, sizeParser, cryptionReader, request.Command.TransferType(), padding), nil
   348  		}
   349  		return buf.NewReader(cryptionReader), nil
   350  
   351  	case protocol.SecurityType_AES128_GCM:
   352  		aead := crypto.NewAesGcm(s.requestBodyKey[:])
   353  		auth := &crypto.AEADAuthenticator{
   354  			AEAD:                    aead,
   355  			NonceGenerator:          GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())),
   356  			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   357  		}
   358  		if request.Option.Has(protocol.RequestOptionAuthenticatedLength) {
   359  			AuthenticatedLengthKey := vmessaead.KDF16(s.requestBodyKey[:], "auth_len")
   360  			AuthenticatedLengthKeyAEAD := crypto.NewAesGcm(AuthenticatedLengthKey)
   361  
   362  			lengthAuth := &crypto.AEADAuthenticator{
   363  				AEAD:                    AuthenticatedLengthKeyAEAD,
   364  				NonceGenerator:          GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())),
   365  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   366  			}
   367  			sizeParser = NewAEADSizeParser(lengthAuth)
   368  		}
   369  		return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding), nil
   370  
   371  	case protocol.SecurityType_CHACHA20_POLY1305:
   372  		aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(s.requestBodyKey[:]))
   373  
   374  		auth := &crypto.AEADAuthenticator{
   375  			AEAD:                    aead,
   376  			NonceGenerator:          GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())),
   377  			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   378  		}
   379  		if request.Option.Has(protocol.RequestOptionAuthenticatedLength) {
   380  			AuthenticatedLengthKey := vmessaead.KDF16(s.requestBodyKey[:], "auth_len")
   381  			AuthenticatedLengthKeyAEAD, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(AuthenticatedLengthKey))
   382  			common.Must(err)
   383  
   384  			lengthAuth := &crypto.AEADAuthenticator{
   385  				AEAD:                    AuthenticatedLengthKeyAEAD,
   386  				NonceGenerator:          GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())),
   387  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   388  			}
   389  			sizeParser = NewAEADSizeParser(lengthAuth)
   390  		}
   391  		return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding), nil
   392  
   393  	default:
   394  		return nil, newError("invalid option: Security")
   395  	}
   396  }
   397  
   398  // EncodeResponseHeader writes encoded response header into the given writer.
   399  func (s *ServerSession) EncodeResponseHeader(header *protocol.ResponseHeader, writer io.Writer) {
   400  	var encryptionWriter io.Writer
   401  	if !s.isAEADRequest {
   402  		s.responseBodyKey = md5.Sum(s.requestBodyKey[:])
   403  		s.responseBodyIV = md5.Sum(s.requestBodyIV[:])
   404  	} else {
   405  		BodyKey := sha256.Sum256(s.requestBodyKey[:])
   406  		copy(s.responseBodyKey[:], BodyKey[:16])
   407  		BodyIV := sha256.Sum256(s.requestBodyIV[:])
   408  		copy(s.responseBodyIV[:], BodyIV[:16])
   409  	}
   410  
   411  	aesStream := crypto.NewAesEncryptionStream(s.responseBodyKey[:], s.responseBodyIV[:])
   412  	encryptionWriter = crypto.NewCryptionWriter(aesStream, writer)
   413  	s.responseWriter = encryptionWriter
   414  
   415  	aeadEncryptedHeaderBuffer := bytes.NewBuffer(nil)
   416  
   417  	if s.isAEADRequest {
   418  		encryptionWriter = aeadEncryptedHeaderBuffer
   419  	}
   420  
   421  	common.Must2(encryptionWriter.Write([]byte{s.responseHeader, byte(header.Option)}))
   422  	err := MarshalCommand(header.Command, encryptionWriter)
   423  	if err != nil {
   424  		common.Must2(encryptionWriter.Write([]byte{0x00, 0x00}))
   425  	}
   426  
   427  	if s.isAEADRequest {
   428  		aeadResponseHeaderLengthEncryptionKey := vmessaead.KDF16(s.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderLenKey)
   429  		aeadResponseHeaderLengthEncryptionIV := vmessaead.KDF(s.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderLenIV)[:12]
   430  
   431  		aeadResponseHeaderLengthEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderLengthEncryptionKey)).(cipher.Block)
   432  		aeadResponseHeaderLengthEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderLengthEncryptionKeyAESBlock)).(cipher.AEAD)
   433  
   434  		aeadResponseHeaderLengthEncryptionBuffer := bytes.NewBuffer(nil)
   435  
   436  		decryptedResponseHeaderLengthBinaryDeserializeBuffer := uint16(aeadEncryptedHeaderBuffer.Len())
   437  
   438  		common.Must(binary.Write(aeadResponseHeaderLengthEncryptionBuffer, binary.BigEndian, decryptedResponseHeaderLengthBinaryDeserializeBuffer))
   439  
   440  		AEADEncryptedLength := aeadResponseHeaderLengthEncryptionAEAD.Seal(nil, aeadResponseHeaderLengthEncryptionIV, aeadResponseHeaderLengthEncryptionBuffer.Bytes(), nil)
   441  		common.Must2(io.Copy(writer, bytes.NewReader(AEADEncryptedLength)))
   442  
   443  		aeadResponseHeaderPayloadEncryptionKey := vmessaead.KDF16(s.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadKey)
   444  		aeadResponseHeaderPayloadEncryptionIV := vmessaead.KDF(s.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadIV)[:12]
   445  
   446  		aeadResponseHeaderPayloadEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderPayloadEncryptionKey)).(cipher.Block)
   447  		aeadResponseHeaderPayloadEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderPayloadEncryptionKeyAESBlock)).(cipher.AEAD)
   448  
   449  		aeadEncryptedHeaderPayload := aeadResponseHeaderPayloadEncryptionAEAD.Seal(nil, aeadResponseHeaderPayloadEncryptionIV, aeadEncryptedHeaderBuffer.Bytes(), nil)
   450  		common.Must2(io.Copy(writer, bytes.NewReader(aeadEncryptedHeaderPayload)))
   451  	}
   452  }
   453  
   454  // EncodeResponseBody returns a Writer that auto-encrypt content written by caller.
   455  func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writer io.Writer) (buf.Writer, error) {
   456  	var sizeParser crypto.ChunkSizeEncoder = crypto.PlainChunkSizeParser{}
   457  	if request.Option.Has(protocol.RequestOptionChunkMasking) {
   458  		sizeParser = NewShakeSizeParser(s.responseBodyIV[:])
   459  	}
   460  	var padding crypto.PaddingLengthGenerator
   461  	if request.Option.Has(protocol.RequestOptionGlobalPadding) {
   462  		var ok bool
   463  		padding, ok = sizeParser.(crypto.PaddingLengthGenerator)
   464  		if !ok {
   465  			return nil, newError("invalid option: RequestOptionGlobalPadding")
   466  		}
   467  	}
   468  
   469  	switch request.Security {
   470  	case protocol.SecurityType_NONE:
   471  		if request.Option.Has(protocol.RequestOptionChunkStream) {
   472  			if request.Command.TransferType() == protocol.TransferTypeStream {
   473  				return crypto.NewChunkStreamWriter(sizeParser, writer), nil
   474  			}
   475  
   476  			auth := &crypto.AEADAuthenticator{
   477  				AEAD:                    new(NoOpAuthenticator),
   478  				NonceGenerator:          crypto.GenerateEmptyBytes(),
   479  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   480  			}
   481  			return crypto.NewAuthenticationWriter(auth, sizeParser, writer, protocol.TransferTypePacket, padding), nil
   482  		}
   483  		return buf.NewWriter(writer), nil
   484  
   485  	case protocol.SecurityType_LEGACY:
   486  		if request.Option.Has(protocol.RequestOptionChunkStream) {
   487  			auth := &crypto.AEADAuthenticator{
   488  				AEAD:                    new(FnvAuthenticator),
   489  				NonceGenerator:          crypto.GenerateEmptyBytes(),
   490  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   491  			}
   492  			return crypto.NewAuthenticationWriter(auth, sizeParser, s.responseWriter, request.Command.TransferType(), padding), nil
   493  		}
   494  		return &buf.SequentialWriter{Writer: s.responseWriter}, nil
   495  
   496  	case protocol.SecurityType_AES128_GCM:
   497  		aead := crypto.NewAesGcm(s.responseBodyKey[:])
   498  		auth := &crypto.AEADAuthenticator{
   499  			AEAD:                    aead,
   500  			NonceGenerator:          GenerateChunkNonce(s.responseBodyIV[:], uint32(aead.NonceSize())),
   501  			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   502  		}
   503  		if request.Option.Has(protocol.RequestOptionAuthenticatedLength) {
   504  			AuthenticatedLengthKey := vmessaead.KDF16(s.requestBodyKey[:], "auth_len")
   505  			AuthenticatedLengthKeyAEAD := crypto.NewAesGcm(AuthenticatedLengthKey)
   506  
   507  			lengthAuth := &crypto.AEADAuthenticator{
   508  				AEAD:                    AuthenticatedLengthKeyAEAD,
   509  				NonceGenerator:          GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())),
   510  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   511  			}
   512  			sizeParser = NewAEADSizeParser(lengthAuth)
   513  		}
   514  		return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding), nil
   515  
   516  	case protocol.SecurityType_CHACHA20_POLY1305:
   517  		aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(s.responseBodyKey[:]))
   518  
   519  		auth := &crypto.AEADAuthenticator{
   520  			AEAD:                    aead,
   521  			NonceGenerator:          GenerateChunkNonce(s.responseBodyIV[:], uint32(aead.NonceSize())),
   522  			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   523  		}
   524  		if request.Option.Has(protocol.RequestOptionAuthenticatedLength) {
   525  			AuthenticatedLengthKey := vmessaead.KDF16(s.requestBodyKey[:], "auth_len")
   526  			AuthenticatedLengthKeyAEAD, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(AuthenticatedLengthKey))
   527  			common.Must(err)
   528  
   529  			lengthAuth := &crypto.AEADAuthenticator{
   530  				AEAD:                    AuthenticatedLengthKeyAEAD,
   531  				NonceGenerator:          GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())),
   532  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   533  			}
   534  			sizeParser = NewAEADSizeParser(lengthAuth)
   535  		}
   536  		return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding), nil
   537  
   538  	default:
   539  		return nil, newError("invalid option: Security")
   540  	}
   541  }