github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/proxy/vmess/encoding/client.go (about)

     1  package encoding
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/aes"
     7  	"crypto/cipher"
     8  	"crypto/md5"
     9  	"crypto/rand"
    10  	"crypto/sha256"
    11  	"encoding/binary"
    12  	"hash"
    13  	"hash/fnv"
    14  	"io"
    15  
    16  	"golang.org/x/crypto/chacha20poly1305"
    17  
    18  	"github.com/v2fly/v2ray-core/v5/common"
    19  	"github.com/v2fly/v2ray-core/v5/common/bitmask"
    20  	"github.com/v2fly/v2ray-core/v5/common/buf"
    21  	"github.com/v2fly/v2ray-core/v5/common/crypto"
    22  	"github.com/v2fly/v2ray-core/v5/common/dice"
    23  	"github.com/v2fly/v2ray-core/v5/common/drain"
    24  	"github.com/v2fly/v2ray-core/v5/common/protocol"
    25  	"github.com/v2fly/v2ray-core/v5/common/serial"
    26  	"github.com/v2fly/v2ray-core/v5/proxy/vmess"
    27  	vmessaead "github.com/v2fly/v2ray-core/v5/proxy/vmess/aead"
    28  )
    29  
    30  func hashTimestamp(h hash.Hash, t protocol.Timestamp) []byte {
    31  	common.Must2(serial.WriteUint64(h, uint64(t)))
    32  	common.Must2(serial.WriteUint64(h, uint64(t)))
    33  	common.Must2(serial.WriteUint64(h, uint64(t)))
    34  	common.Must2(serial.WriteUint64(h, uint64(t)))
    35  	return h.Sum(nil)
    36  }
    37  
    38  // ClientSession stores connection session info for VMess client.
    39  type ClientSession struct {
    40  	isAEAD          bool
    41  	idHash          protocol.IDHash
    42  	requestBodyKey  [16]byte
    43  	requestBodyIV   [16]byte
    44  	responseBodyKey [16]byte
    45  	responseBodyIV  [16]byte
    46  	responseReader  io.Reader
    47  	responseHeader  byte
    48  
    49  	readDrainer drain.Drainer
    50  }
    51  
    52  // NewClientSession creates a new ClientSession.
    53  func NewClientSession(ctx context.Context, isAEAD bool, idHash protocol.IDHash, behaviorSeed int64) *ClientSession {
    54  	session := &ClientSession{
    55  		isAEAD: isAEAD,
    56  		idHash: idHash,
    57  	}
    58  
    59  	randomBytes := make([]byte, 33) // 16 + 16 + 1
    60  	common.Must2(rand.Read(randomBytes))
    61  	copy(session.requestBodyKey[:], randomBytes[:16])
    62  	copy(session.requestBodyIV[:], randomBytes[16:32])
    63  	session.responseHeader = randomBytes[32]
    64  
    65  	if !session.isAEAD {
    66  		session.responseBodyKey = md5.Sum(session.requestBodyKey[:])
    67  		session.responseBodyIV = md5.Sum(session.requestBodyIV[:])
    68  	} else {
    69  		BodyKey := sha256.Sum256(session.requestBodyKey[:])
    70  		copy(session.responseBodyKey[:], BodyKey[:16])
    71  		BodyIV := sha256.Sum256(session.requestBodyIV[:])
    72  		copy(session.responseBodyIV[:], BodyIV[:16])
    73  	}
    74  	{
    75  		var err error
    76  		session.readDrainer, err = drain.NewBehaviorSeedLimitedDrainer(behaviorSeed, 18, 3266, 64)
    77  		if err != nil {
    78  			newError("unable to initialize drainer").Base(err).WriteToLog()
    79  			session.readDrainer = drain.NewNopDrainer()
    80  		}
    81  	}
    82  
    83  	return session
    84  }
    85  
    86  func (c *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writer io.Writer) error {
    87  	timestamp := protocol.NewTimestampGenerator(protocol.NowTime(), 30)()
    88  	account := header.User.Account.(*vmess.MemoryAccount)
    89  	if !c.isAEAD {
    90  		idHash := c.idHash(account.AnyValidID().Bytes())
    91  		common.Must2(serial.WriteUint64(idHash, uint64(timestamp)))
    92  		common.Must2(writer.Write(idHash.Sum(nil)))
    93  	}
    94  
    95  	buffer := buf.New()
    96  	defer buffer.Release()
    97  
    98  	common.Must(buffer.WriteByte(Version))
    99  	common.Must2(buffer.Write(c.requestBodyIV[:]))
   100  	common.Must2(buffer.Write(c.requestBodyKey[:]))
   101  	common.Must(buffer.WriteByte(c.responseHeader))
   102  	common.Must(buffer.WriteByte(byte(header.Option)))
   103  
   104  	paddingLen := dice.RollWith(16, rand.Reader)
   105  	security := byte(paddingLen<<4) | byte(header.Security)
   106  	common.Must2(buffer.Write([]byte{security, byte(0), byte(header.Command)}))
   107  
   108  	if header.Command != protocol.RequestCommandMux {
   109  		if err := addrParser.WriteAddressPort(buffer, header.Address, header.Port); err != nil {
   110  			return newError("failed to writer address and port").Base(err)
   111  		}
   112  	}
   113  
   114  	if paddingLen > 0 {
   115  		common.Must2(buffer.ReadFullFrom(rand.Reader, int32(paddingLen)))
   116  	}
   117  
   118  	{
   119  		fnv1a := fnv.New32a()
   120  		common.Must2(fnv1a.Write(buffer.Bytes()))
   121  		hashBytes := buffer.Extend(int32(fnv1a.Size()))
   122  		fnv1a.Sum(hashBytes[:0])
   123  	}
   124  
   125  	if !c.isAEAD {
   126  		iv := hashTimestamp(md5.New(), timestamp)
   127  		aesStream := crypto.NewAesEncryptionStream(account.ID.CmdKey(), iv)
   128  		aesStream.XORKeyStream(buffer.Bytes(), buffer.Bytes())
   129  		common.Must2(writer.Write(buffer.Bytes()))
   130  	} else {
   131  		var fixedLengthCmdKey [16]byte
   132  		copy(fixedLengthCmdKey[:], account.ID.CmdKey())
   133  		vmessout := vmessaead.SealVMessAEADHeader(fixedLengthCmdKey, buffer.Bytes())
   134  		common.Must2(io.Copy(writer, bytes.NewReader(vmessout)))
   135  	}
   136  
   137  	return nil
   138  }
   139  
   140  func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, writer io.Writer) (buf.Writer, error) {
   141  	var sizeParser crypto.ChunkSizeEncoder = crypto.PlainChunkSizeParser{}
   142  	if request.Option.Has(protocol.RequestOptionChunkMasking) {
   143  		sizeParser = NewShakeSizeParser(c.requestBodyIV[:])
   144  	}
   145  	var padding crypto.PaddingLengthGenerator
   146  	if request.Option.Has(protocol.RequestOptionGlobalPadding) {
   147  		var ok bool
   148  		padding, ok = sizeParser.(crypto.PaddingLengthGenerator)
   149  		if !ok {
   150  			return nil, newError("invalid option: RequestOptionGlobalPadding")
   151  		}
   152  	}
   153  
   154  	switch request.Security {
   155  	case protocol.SecurityType_NONE:
   156  		if request.Option.Has(protocol.RequestOptionChunkStream) {
   157  			if request.Command.TransferType() == protocol.TransferTypeStream {
   158  				return crypto.NewChunkStreamWriter(sizeParser, writer), nil
   159  			}
   160  			auth := &crypto.AEADAuthenticator{
   161  				AEAD:                    new(NoOpAuthenticator),
   162  				NonceGenerator:          crypto.GenerateEmptyBytes(),
   163  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   164  			}
   165  			return crypto.NewAuthenticationWriter(auth, sizeParser, writer, protocol.TransferTypePacket, padding), nil
   166  		}
   167  
   168  		return buf.NewWriter(writer), nil
   169  	case protocol.SecurityType_LEGACY:
   170  		aesStream := crypto.NewAesEncryptionStream(c.requestBodyKey[:], c.requestBodyIV[:])
   171  		cryptionWriter := crypto.NewCryptionWriter(aesStream, writer)
   172  		if request.Option.Has(protocol.RequestOptionChunkStream) {
   173  			auth := &crypto.AEADAuthenticator{
   174  				AEAD:                    new(FnvAuthenticator),
   175  				NonceGenerator:          crypto.GenerateEmptyBytes(),
   176  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   177  			}
   178  			return crypto.NewAuthenticationWriter(auth, sizeParser, cryptionWriter, request.Command.TransferType(), padding), nil
   179  		}
   180  
   181  		return &buf.SequentialWriter{Writer: cryptionWriter}, nil
   182  	case protocol.SecurityType_AES128_GCM:
   183  		aead := crypto.NewAesGcm(c.requestBodyKey[:])
   184  		auth := &crypto.AEADAuthenticator{
   185  			AEAD:                    aead,
   186  			NonceGenerator:          GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())),
   187  			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   188  		}
   189  		if request.Option.Has(protocol.RequestOptionAuthenticatedLength) {
   190  			AuthenticatedLengthKey := vmessaead.KDF16(c.requestBodyKey[:], "auth_len")
   191  			AuthenticatedLengthKeyAEAD := crypto.NewAesGcm(AuthenticatedLengthKey)
   192  
   193  			lengthAuth := &crypto.AEADAuthenticator{
   194  				AEAD:                    AuthenticatedLengthKeyAEAD,
   195  				NonceGenerator:          GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())),
   196  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   197  			}
   198  			sizeParser = NewAEADSizeParser(lengthAuth)
   199  		}
   200  		return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding), nil
   201  	case protocol.SecurityType_CHACHA20_POLY1305:
   202  		aead, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(c.requestBodyKey[:]))
   203  		common.Must(err)
   204  
   205  		auth := &crypto.AEADAuthenticator{
   206  			AEAD:                    aead,
   207  			NonceGenerator:          GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())),
   208  			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   209  		}
   210  		if request.Option.Has(protocol.RequestOptionAuthenticatedLength) {
   211  			AuthenticatedLengthKey := vmessaead.KDF16(c.requestBodyKey[:], "auth_len")
   212  			AuthenticatedLengthKeyAEAD, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(AuthenticatedLengthKey))
   213  			common.Must(err)
   214  
   215  			lengthAuth := &crypto.AEADAuthenticator{
   216  				AEAD:                    AuthenticatedLengthKeyAEAD,
   217  				NonceGenerator:          GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())),
   218  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   219  			}
   220  			sizeParser = NewAEADSizeParser(lengthAuth)
   221  		}
   222  		return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding), nil
   223  	default:
   224  		return nil, newError("invalid option: Security")
   225  	}
   226  }
   227  
   228  func (c *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.ResponseHeader, error) {
   229  	if !c.isAEAD {
   230  		aesStream := crypto.NewAesDecryptionStream(c.responseBodyKey[:], c.responseBodyIV[:])
   231  		c.responseReader = crypto.NewCryptionReader(aesStream, reader)
   232  	} else {
   233  		aeadResponseHeaderLengthEncryptionKey := vmessaead.KDF16(c.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderLenKey)
   234  		aeadResponseHeaderLengthEncryptionIV := vmessaead.KDF(c.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderLenIV)[:12]
   235  
   236  		aeadResponseHeaderLengthEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderLengthEncryptionKey)).(cipher.Block)
   237  		aeadResponseHeaderLengthEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderLengthEncryptionKeyAESBlock)).(cipher.AEAD)
   238  
   239  		var aeadEncryptedResponseHeaderLength [18]byte
   240  		var decryptedResponseHeaderLength int
   241  		var decryptedResponseHeaderLengthBinaryDeserializeBuffer uint16
   242  
   243  		if n, err := io.ReadFull(reader, aeadEncryptedResponseHeaderLength[:]); err != nil {
   244  			c.readDrainer.AcknowledgeReceive(n)
   245  			return nil, drain.WithError(c.readDrainer, reader, newError("Unable to Read Header Len").Base(err))
   246  		} else { // nolint: revive
   247  			c.readDrainer.AcknowledgeReceive(n)
   248  		}
   249  		if decryptedResponseHeaderLengthBinaryBuffer, err := aeadResponseHeaderLengthEncryptionAEAD.Open(nil, aeadResponseHeaderLengthEncryptionIV, aeadEncryptedResponseHeaderLength[:], nil); err != nil {
   250  			return nil, drain.WithError(c.readDrainer, reader, newError("Failed To Decrypt Length").Base(err))
   251  		} else { // nolint: revive
   252  			common.Must(binary.Read(bytes.NewReader(decryptedResponseHeaderLengthBinaryBuffer), binary.BigEndian, &decryptedResponseHeaderLengthBinaryDeserializeBuffer))
   253  			decryptedResponseHeaderLength = int(decryptedResponseHeaderLengthBinaryDeserializeBuffer)
   254  		}
   255  
   256  		aeadResponseHeaderPayloadEncryptionKey := vmessaead.KDF16(c.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadKey)
   257  		aeadResponseHeaderPayloadEncryptionIV := vmessaead.KDF(c.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderPayloadIV)[:12]
   258  
   259  		aeadResponseHeaderPayloadEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderPayloadEncryptionKey)).(cipher.Block)
   260  		aeadResponseHeaderPayloadEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderPayloadEncryptionKeyAESBlock)).(cipher.AEAD)
   261  
   262  		encryptedResponseHeaderBuffer := make([]byte, decryptedResponseHeaderLength+16)
   263  
   264  		if n, err := io.ReadFull(reader, encryptedResponseHeaderBuffer); err != nil {
   265  			c.readDrainer.AcknowledgeReceive(n)
   266  			return nil, drain.WithError(c.readDrainer, reader, newError("Unable to Read Header Data").Base(err))
   267  		} else { // nolint: revive
   268  			c.readDrainer.AcknowledgeReceive(n)
   269  		}
   270  
   271  		if decryptedResponseHeaderBuffer, err := aeadResponseHeaderPayloadEncryptionAEAD.Open(nil, aeadResponseHeaderPayloadEncryptionIV, encryptedResponseHeaderBuffer, nil); err != nil {
   272  			return nil, drain.WithError(c.readDrainer, reader, newError("Failed To Decrypt Payload").Base(err))
   273  		} else { // nolint: revive
   274  			c.responseReader = bytes.NewReader(decryptedResponseHeaderBuffer)
   275  		}
   276  	}
   277  
   278  	buffer := buf.StackNew()
   279  	defer buffer.Release()
   280  
   281  	if _, err := buffer.ReadFullFrom(c.responseReader, 4); err != nil {
   282  		return nil, newError("failed to read response header").Base(err).AtWarning()
   283  	}
   284  
   285  	if buffer.Byte(0) != c.responseHeader {
   286  		return nil, newError("unexpected response header. Expecting ", int(c.responseHeader), " but actually ", int(buffer.Byte(0)))
   287  	}
   288  
   289  	header := &protocol.ResponseHeader{
   290  		Option: bitmask.Byte(buffer.Byte(1)),
   291  	}
   292  
   293  	if buffer.Byte(2) != 0 {
   294  		cmdID := buffer.Byte(2)
   295  		dataLen := int32(buffer.Byte(3))
   296  
   297  		buffer.Clear()
   298  		if _, err := buffer.ReadFullFrom(c.responseReader, dataLen); err != nil {
   299  			return nil, newError("failed to read response command").Base(err)
   300  		}
   301  		command, err := UnmarshalCommand(cmdID, buffer.Bytes())
   302  		if err == nil {
   303  			header.Command = command
   304  		}
   305  	}
   306  	if c.isAEAD {
   307  		aesStream := crypto.NewAesDecryptionStream(c.responseBodyKey[:], c.responseBodyIV[:])
   308  		c.responseReader = crypto.NewCryptionReader(aesStream, reader)
   309  	}
   310  	return header, nil
   311  }
   312  
   313  func (c *ClientSession) DecodeResponseBody(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(c.responseBodyIV[:])
   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  
   340  			return crypto.NewAuthenticationReader(auth, sizeParser, reader, protocol.TransferTypePacket, padding), nil
   341  		}
   342  
   343  		return buf.NewReader(reader), nil
   344  	case protocol.SecurityType_LEGACY:
   345  		if request.Option.Has(protocol.RequestOptionChunkStream) {
   346  			auth := &crypto.AEADAuthenticator{
   347  				AEAD:                    new(FnvAuthenticator),
   348  				NonceGenerator:          crypto.GenerateEmptyBytes(),
   349  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   350  			}
   351  			return crypto.NewAuthenticationReader(auth, sizeParser, c.responseReader, request.Command.TransferType(), padding), nil
   352  		}
   353  
   354  		return buf.NewReader(c.responseReader), nil
   355  	case protocol.SecurityType_AES128_GCM:
   356  		aead := crypto.NewAesGcm(c.responseBodyKey[:])
   357  
   358  		auth := &crypto.AEADAuthenticator{
   359  			AEAD:                    aead,
   360  			NonceGenerator:          GenerateChunkNonce(c.responseBodyIV[:], uint32(aead.NonceSize())),
   361  			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   362  		}
   363  		if request.Option.Has(protocol.RequestOptionAuthenticatedLength) {
   364  			AuthenticatedLengthKey := vmessaead.KDF16(c.requestBodyKey[:], "auth_len")
   365  			AuthenticatedLengthKeyAEAD := crypto.NewAesGcm(AuthenticatedLengthKey)
   366  
   367  			lengthAuth := &crypto.AEADAuthenticator{
   368  				AEAD:                    AuthenticatedLengthKeyAEAD,
   369  				NonceGenerator:          GenerateChunkNonce(c.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  	case protocol.SecurityType_CHACHA20_POLY1305:
   376  		aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(c.responseBodyKey[:]))
   377  
   378  		auth := &crypto.AEADAuthenticator{
   379  			AEAD:                    aead,
   380  			NonceGenerator:          GenerateChunkNonce(c.responseBodyIV[:], uint32(aead.NonceSize())),
   381  			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   382  		}
   383  		if request.Option.Has(protocol.RequestOptionAuthenticatedLength) {
   384  			AuthenticatedLengthKey := vmessaead.KDF16(c.requestBodyKey[:], "auth_len")
   385  			AuthenticatedLengthKeyAEAD, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(AuthenticatedLengthKey))
   386  			common.Must(err)
   387  
   388  			lengthAuth := &crypto.AEADAuthenticator{
   389  				AEAD:                    AuthenticatedLengthKeyAEAD,
   390  				NonceGenerator:          GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())),
   391  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   392  			}
   393  			sizeParser = NewAEADSizeParser(lengthAuth)
   394  		}
   395  		return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding), nil
   396  	default:
   397  		return nil, newError("invalid option: Security")
   398  	}
   399  }
   400  
   401  func GenerateChunkNonce(nonce []byte, size uint32) crypto.BytesGenerator {
   402  	c := append([]byte(nil), nonce...)
   403  	count := uint16(0)
   404  	return func() []byte {
   405  		binary.BigEndian.PutUint16(c, count)
   406  		count++
   407  		return c[:size]
   408  	}
   409  }