github.com/Uhtred009/v2ray-core-1@v4.31.2+incompatible/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  	"v2ray.com/core/common"
    19  	"v2ray.com/core/common/bitmask"
    20  	"v2ray.com/core/common/buf"
    21  	"v2ray.com/core/common/crypto"
    22  	"v2ray.com/core/common/dice"
    23  	"v2ray.com/core/common/protocol"
    24  	"v2ray.com/core/common/serial"
    25  	"v2ray.com/core/proxy/vmess"
    26  	vmessaead "v2ray.com/core/proxy/vmess/aead"
    27  )
    28  
    29  func hashTimestamp(h hash.Hash, t protocol.Timestamp) []byte {
    30  	common.Must2(serial.WriteUint64(h, uint64(t)))
    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  	return h.Sum(nil)
    35  }
    36  
    37  // ClientSession stores connection session info for VMess client.
    38  type ClientSession struct {
    39  	isAEAD          bool
    40  	idHash          protocol.IDHash
    41  	requestBodyKey  [16]byte
    42  	requestBodyIV   [16]byte
    43  	responseBodyKey [16]byte
    44  	responseBodyIV  [16]byte
    45  	responseReader  io.Reader
    46  	responseHeader  byte
    47  }
    48  
    49  // NewClientSession creates a new ClientSession.
    50  func NewClientSession(isAEAD bool, idHash protocol.IDHash, ctx context.Context) *ClientSession {
    51  
    52  	session := &ClientSession{
    53  		isAEAD: isAEAD,
    54  		idHash: idHash,
    55  	}
    56  
    57  	randomBytes := make([]byte, 33) // 16 + 16 + 1
    58  	common.Must2(rand.Read(randomBytes))
    59  	copy(session.requestBodyKey[:], randomBytes[:16])
    60  	copy(session.requestBodyIV[:], randomBytes[16:32])
    61  	session.responseHeader = randomBytes[32]
    62  
    63  	if !session.isAEAD {
    64  		session.responseBodyKey = md5.Sum(session.requestBodyKey[:])
    65  		session.responseBodyIV = md5.Sum(session.requestBodyIV[:])
    66  	} else {
    67  		BodyKey := sha256.Sum256(session.requestBodyKey[:])
    68  		copy(session.responseBodyKey[:], BodyKey[:16])
    69  		BodyIV := sha256.Sum256(session.requestBodyIV[:])
    70  		copy(session.responseBodyIV[:], BodyIV[:16])
    71  	}
    72  
    73  	return session
    74  }
    75  
    76  func (c *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writer io.Writer) error {
    77  	timestamp := protocol.NewTimestampGenerator(protocol.NowTime(), 30)()
    78  	account := header.User.Account.(*vmess.MemoryAccount)
    79  	if !c.isAEAD {
    80  		idHash := c.idHash(account.AnyValidID().Bytes())
    81  		common.Must2(serial.WriteUint64(idHash, uint64(timestamp)))
    82  		common.Must2(writer.Write(idHash.Sum(nil)))
    83  	}
    84  
    85  	buffer := buf.New()
    86  	defer buffer.Release()
    87  
    88  	common.Must(buffer.WriteByte(Version))
    89  	common.Must2(buffer.Write(c.requestBodyIV[:]))
    90  	common.Must2(buffer.Write(c.requestBodyKey[:]))
    91  	common.Must(buffer.WriteByte(c.responseHeader))
    92  	common.Must(buffer.WriteByte(byte(header.Option)))
    93  
    94  	padingLen := dice.Roll(16)
    95  	security := byte(padingLen<<4) | byte(header.Security)
    96  	common.Must2(buffer.Write([]byte{security, byte(0), byte(header.Command)}))
    97  
    98  	if header.Command != protocol.RequestCommandMux {
    99  		if err := addrParser.WriteAddressPort(buffer, header.Address, header.Port); err != nil {
   100  			return newError("failed to writer address and port").Base(err)
   101  		}
   102  	}
   103  
   104  	if padingLen > 0 {
   105  		common.Must2(buffer.ReadFullFrom(rand.Reader, int32(padingLen)))
   106  	}
   107  
   108  	{
   109  		fnv1a := fnv.New32a()
   110  		common.Must2(fnv1a.Write(buffer.Bytes()))
   111  		hashBytes := buffer.Extend(int32(fnv1a.Size()))
   112  		fnv1a.Sum(hashBytes[:0])
   113  	}
   114  
   115  	if !c.isAEAD {
   116  		iv := hashTimestamp(md5.New(), timestamp)
   117  		aesStream := crypto.NewAesEncryptionStream(account.ID.CmdKey(), iv[:])
   118  		aesStream.XORKeyStream(buffer.Bytes(), buffer.Bytes())
   119  		common.Must2(writer.Write(buffer.Bytes()))
   120  	} else {
   121  		var fixedLengthCmdKey [16]byte
   122  		copy(fixedLengthCmdKey[:], account.ID.CmdKey())
   123  		vmessout := vmessaead.SealVMessAEADHeader(fixedLengthCmdKey, buffer.Bytes())
   124  		common.Must2(io.Copy(writer, bytes.NewReader(vmessout)))
   125  	}
   126  
   127  	return nil
   128  }
   129  
   130  func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, writer io.Writer) buf.Writer {
   131  	var sizeParser crypto.ChunkSizeEncoder = crypto.PlainChunkSizeParser{}
   132  	if request.Option.Has(protocol.RequestOptionChunkMasking) {
   133  		sizeParser = NewShakeSizeParser(c.requestBodyIV[:])
   134  	}
   135  	var padding crypto.PaddingLengthGenerator
   136  	if request.Option.Has(protocol.RequestOptionGlobalPadding) {
   137  		padding = sizeParser.(crypto.PaddingLengthGenerator)
   138  	}
   139  
   140  	switch request.Security {
   141  	case protocol.SecurityType_NONE:
   142  		if request.Option.Has(protocol.RequestOptionChunkStream) {
   143  			if request.Command.TransferType() == protocol.TransferTypeStream {
   144  				return crypto.NewChunkStreamWriter(sizeParser, writer)
   145  			}
   146  			auth := &crypto.AEADAuthenticator{
   147  				AEAD:                    new(NoOpAuthenticator),
   148  				NonceGenerator:          crypto.GenerateEmptyBytes(),
   149  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   150  			}
   151  			return crypto.NewAuthenticationWriter(auth, sizeParser, writer, protocol.TransferTypePacket, padding)
   152  		}
   153  
   154  		return buf.NewWriter(writer)
   155  	case protocol.SecurityType_LEGACY:
   156  		aesStream := crypto.NewAesEncryptionStream(c.requestBodyKey[:], c.requestBodyIV[:])
   157  		cryptionWriter := crypto.NewCryptionWriter(aesStream, writer)
   158  		if request.Option.Has(protocol.RequestOptionChunkStream) {
   159  			auth := &crypto.AEADAuthenticator{
   160  				AEAD:                    new(FnvAuthenticator),
   161  				NonceGenerator:          crypto.GenerateEmptyBytes(),
   162  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   163  			}
   164  			return crypto.NewAuthenticationWriter(auth, sizeParser, cryptionWriter, request.Command.TransferType(), padding)
   165  		}
   166  
   167  		return &buf.SequentialWriter{Writer: cryptionWriter}
   168  	case protocol.SecurityType_AES128_GCM:
   169  		aead := crypto.NewAesGcm(c.requestBodyKey[:])
   170  		auth := &crypto.AEADAuthenticator{
   171  			AEAD:                    aead,
   172  			NonceGenerator:          GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())),
   173  			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   174  		}
   175  		return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding)
   176  	case protocol.SecurityType_CHACHA20_POLY1305:
   177  		aead, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(c.requestBodyKey[:]))
   178  		common.Must(err)
   179  
   180  		auth := &crypto.AEADAuthenticator{
   181  			AEAD:                    aead,
   182  			NonceGenerator:          GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())),
   183  			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   184  		}
   185  		return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding)
   186  	default:
   187  		panic("Unknown security type.")
   188  	}
   189  }
   190  
   191  func (c *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.ResponseHeader, error) {
   192  	if !c.isAEAD {
   193  		aesStream := crypto.NewAesDecryptionStream(c.responseBodyKey[:], c.responseBodyIV[:])
   194  		c.responseReader = crypto.NewCryptionReader(aesStream, reader)
   195  	} else {
   196  		aeadResponseHeaderLengthEncryptionKey := vmessaead.KDF16(c.responseBodyKey[:], vmessaead.KDFSaltConst_AEADRespHeaderLenKey)
   197  		aeadResponseHeaderLengthEncryptionIV := vmessaead.KDF(c.responseBodyIV[:], vmessaead.KDFSaltConst_AEADRespHeaderLenIV)[:12]
   198  
   199  		aeadResponseHeaderLengthEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderLengthEncryptionKey)).(cipher.Block)
   200  		aeadResponseHeaderLengthEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderLengthEncryptionKeyAESBlock)).(cipher.AEAD)
   201  
   202  		var aeadEncryptedResponseHeaderLength [18]byte
   203  		var decryptedResponseHeaderLength int
   204  		var decryptedResponseHeaderLengthBinaryDeserializeBuffer uint16
   205  
   206  		if _, err := io.ReadFull(reader, aeadEncryptedResponseHeaderLength[:]); err != nil {
   207  			return nil, newError("Unable to Read Header Len").Base(err)
   208  		}
   209  		if decryptedResponseHeaderLengthBinaryBuffer, err := aeadResponseHeaderLengthEncryptionAEAD.Open(nil, aeadResponseHeaderLengthEncryptionIV, aeadEncryptedResponseHeaderLength[:], nil); err != nil {
   210  			return nil, newError("Failed To Decrypt Length").Base(err)
   211  		} else {
   212  			common.Must(binary.Read(bytes.NewReader(decryptedResponseHeaderLengthBinaryBuffer), binary.BigEndian, &decryptedResponseHeaderLengthBinaryDeserializeBuffer))
   213  			decryptedResponseHeaderLength = int(decryptedResponseHeaderLengthBinaryDeserializeBuffer)
   214  		}
   215  
   216  		aeadResponseHeaderPayloadEncryptionKey := vmessaead.KDF16(c.responseBodyKey[:], vmessaead.KDFSaltConst_AEADRespHeaderPayloadKey)
   217  		aeadResponseHeaderPayloadEncryptionIV := vmessaead.KDF(c.responseBodyIV[:], vmessaead.KDFSaltConst_AEADRespHeaderPayloadIV)[:12]
   218  
   219  		aeadResponseHeaderPayloadEncryptionKeyAESBlock := common.Must2(aes.NewCipher(aeadResponseHeaderPayloadEncryptionKey)).(cipher.Block)
   220  		aeadResponseHeaderPayloadEncryptionAEAD := common.Must2(cipher.NewGCM(aeadResponseHeaderPayloadEncryptionKeyAESBlock)).(cipher.AEAD)
   221  
   222  		encryptedResponseHeaderBuffer := make([]byte, decryptedResponseHeaderLength+16)
   223  
   224  		if _, err := io.ReadFull(reader, encryptedResponseHeaderBuffer); err != nil {
   225  			return nil, newError("Unable to Read Header Data").Base(err)
   226  		}
   227  
   228  		if decryptedResponseHeaderBuffer, err := aeadResponseHeaderPayloadEncryptionAEAD.Open(nil, aeadResponseHeaderPayloadEncryptionIV, encryptedResponseHeaderBuffer, nil); err != nil {
   229  			return nil, newError("Failed To Decrypt Payload").Base(err)
   230  		} else {
   231  			c.responseReader = bytes.NewReader(decryptedResponseHeaderBuffer)
   232  		}
   233  	}
   234  
   235  	buffer := buf.StackNew()
   236  	defer buffer.Release()
   237  
   238  	if _, err := buffer.ReadFullFrom(c.responseReader, 4); err != nil {
   239  		return nil, newError("failed to read response header").Base(err).AtWarning()
   240  	}
   241  
   242  	if buffer.Byte(0) != c.responseHeader {
   243  		return nil, newError("unexpected response header. Expecting ", int(c.responseHeader), " but actually ", int(buffer.Byte(0)))
   244  	}
   245  
   246  	header := &protocol.ResponseHeader{
   247  		Option: bitmask.Byte(buffer.Byte(1)),
   248  	}
   249  
   250  	if buffer.Byte(2) != 0 {
   251  		cmdID := buffer.Byte(2)
   252  		dataLen := int32(buffer.Byte(3))
   253  
   254  		buffer.Clear()
   255  		if _, err := buffer.ReadFullFrom(c.responseReader, dataLen); err != nil {
   256  			return nil, newError("failed to read response command").Base(err)
   257  		}
   258  		command, err := UnmarshalCommand(cmdID, buffer.Bytes())
   259  		if err == nil {
   260  			header.Command = command
   261  		}
   262  	}
   263  	if c.isAEAD {
   264  		aesStream := crypto.NewAesDecryptionStream(c.responseBodyKey[:], c.responseBodyIV[:])
   265  		c.responseReader = crypto.NewCryptionReader(aesStream, reader)
   266  	}
   267  	return header, nil
   268  }
   269  
   270  func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, reader io.Reader) buf.Reader {
   271  	var sizeParser crypto.ChunkSizeDecoder = crypto.PlainChunkSizeParser{}
   272  	if request.Option.Has(protocol.RequestOptionChunkMasking) {
   273  		sizeParser = NewShakeSizeParser(c.responseBodyIV[:])
   274  	}
   275  	var padding crypto.PaddingLengthGenerator
   276  	if request.Option.Has(protocol.RequestOptionGlobalPadding) {
   277  		padding = sizeParser.(crypto.PaddingLengthGenerator)
   278  	}
   279  
   280  	switch request.Security {
   281  	case protocol.SecurityType_NONE:
   282  		if request.Option.Has(protocol.RequestOptionChunkStream) {
   283  			if request.Command.TransferType() == protocol.TransferTypeStream {
   284  				return crypto.NewChunkStreamReader(sizeParser, reader)
   285  			}
   286  
   287  			auth := &crypto.AEADAuthenticator{
   288  				AEAD:                    new(NoOpAuthenticator),
   289  				NonceGenerator:          crypto.GenerateEmptyBytes(),
   290  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   291  			}
   292  
   293  			return crypto.NewAuthenticationReader(auth, sizeParser, reader, protocol.TransferTypePacket, padding)
   294  		}
   295  
   296  		return buf.NewReader(reader)
   297  	case protocol.SecurityType_LEGACY:
   298  		if request.Option.Has(protocol.RequestOptionChunkStream) {
   299  			auth := &crypto.AEADAuthenticator{
   300  				AEAD:                    new(FnvAuthenticator),
   301  				NonceGenerator:          crypto.GenerateEmptyBytes(),
   302  				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   303  			}
   304  			return crypto.NewAuthenticationReader(auth, sizeParser, c.responseReader, request.Command.TransferType(), padding)
   305  		}
   306  
   307  		return buf.NewReader(c.responseReader)
   308  	case protocol.SecurityType_AES128_GCM:
   309  		aead := crypto.NewAesGcm(c.responseBodyKey[:])
   310  
   311  		auth := &crypto.AEADAuthenticator{
   312  			AEAD:                    aead,
   313  			NonceGenerator:          GenerateChunkNonce(c.responseBodyIV[:], uint32(aead.NonceSize())),
   314  			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   315  		}
   316  		return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding)
   317  	case protocol.SecurityType_CHACHA20_POLY1305:
   318  		aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(c.responseBodyKey[:]))
   319  
   320  		auth := &crypto.AEADAuthenticator{
   321  			AEAD:                    aead,
   322  			NonceGenerator:          GenerateChunkNonce(c.responseBodyIV[:], uint32(aead.NonceSize())),
   323  			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
   324  		}
   325  		return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding)
   326  	default:
   327  		panic("Unknown security type.")
   328  	}
   329  }
   330  
   331  func GenerateChunkNonce(nonce []byte, size uint32) crypto.BytesGenerator {
   332  	c := append([]byte(nil), nonce...)
   333  	count := uint16(0)
   334  	return func() []byte {
   335  		binary.BigEndian.PutUint16(c, count)
   336  		count++
   337  		return c[:size]
   338  	}
   339  }