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