mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-08-22 09:36:49 +08:00
Mainly reverse VLESS Client Hello
https://github.com/XTLS/Xray-core/pull/4952#issuecomment-3176751311
This commit is contained in:
@@ -20,14 +20,15 @@ import (
|
|||||||
var ClientCipher byte
|
var ClientCipher byte
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
if !protocol.HasAESGCMHardwareSupport {
|
if protocol.HasAESGCMHardwareSupport {
|
||||||
ClientCipher = 1
|
ClientCipher = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClientInstance struct {
|
type ClientInstance struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
eKeyNfs *mlkem.EncapsulationKey768
|
nfsEKey *mlkem.EncapsulationKey768
|
||||||
|
nfsEKeyBytes []byte
|
||||||
xor uint32
|
xor uint32
|
||||||
minutes time.Duration
|
minutes time.Duration
|
||||||
expire time.Time
|
expire time.Time
|
||||||
@@ -48,19 +49,22 @@ type ClientConn struct {
|
|||||||
peerCache []byte
|
peerCache []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ClientInstance) Init(eKeyNfsData []byte, xor uint32, minutes time.Duration) (err error) {
|
func (i *ClientInstance) Init(nfsEKeyBytes []byte, xor uint32, minutes time.Duration) (err error) {
|
||||||
i.eKeyNfs, err = mlkem.NewEncapsulationKey768(eKeyNfsData)
|
i.nfsEKey, err = mlkem.NewEncapsulationKey768(nfsEKeyBytes)
|
||||||
|
if xor > 0 {
|
||||||
|
i.nfsEKeyBytes = nfsEKeyBytes
|
||||||
i.xor = xor
|
i.xor = xor
|
||||||
|
}
|
||||||
i.minutes = minutes
|
i.minutes = minutes
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) {
|
func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) {
|
||||||
if i.eKeyNfs == nil {
|
if i.nfsEKey == nil {
|
||||||
return nil, errors.New("uninitialized")
|
return nil, errors.New("uninitialized")
|
||||||
}
|
}
|
||||||
if i.xor == 1 {
|
if i.xor > 0 {
|
||||||
conn = NewXorConn(conn, i.eKeyNfs.Bytes())
|
conn = NewXorConn(conn, i.nfsEKeyBytes)
|
||||||
}
|
}
|
||||||
c := &ClientConn{Conn: conn}
|
c := &ClientConn{Conn: conn}
|
||||||
|
|
||||||
@@ -76,18 +80,19 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) {
|
|||||||
i.RUnlock()
|
i.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
nfsKey, encapsulatedNfsKey := i.eKeyNfs.Encapsulate()
|
pfsDKeySeed := make([]byte, 64)
|
||||||
seed := make([]byte, 64)
|
rand.Read(pfsDKeySeed)
|
||||||
rand.Read(seed)
|
pfsDKey, _ := mlkem.NewDecapsulationKey768(pfsDKeySeed)
|
||||||
dKeyPfs, _ := mlkem.NewDecapsulationKey768(seed)
|
pfsEKeyBytes := pfsDKey.EncapsulationKey().Bytes()
|
||||||
eKeyPfs := dKeyPfs.EncapsulationKey().Bytes()
|
nfsKey, encapsulatedNfsKey := i.nfsEKey.Encapsulate()
|
||||||
padding := crypto.RandBetween(100, 1000)
|
paddingLen := crypto.RandBetween(100, 1000)
|
||||||
|
|
||||||
clientHello := make([]byte, 1088+1184+1+5+padding)
|
clientHello := make([]byte, 1+1184+1088+5+paddingLen)
|
||||||
copy(clientHello, encapsulatedNfsKey)
|
clientHello[0] = ClientCipher
|
||||||
copy(clientHello[1088:], eKeyPfs)
|
copy(clientHello[1:], pfsEKeyBytes)
|
||||||
clientHello[2272] = ClientCipher
|
copy(clientHello[1185:], encapsulatedNfsKey)
|
||||||
encodeHeader(clientHello[2273:], int(padding))
|
encodeHeader(clientHello[2273:], int(paddingLen))
|
||||||
|
rand.Read(clientHello[2278:])
|
||||||
|
|
||||||
if _, err := c.Conn.Write(clientHello); err != nil {
|
if _, err := c.Conn.Write(clientHello); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -101,16 +106,16 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) {
|
|||||||
encapsulatedPfsKey := peerServerHello[:1088]
|
encapsulatedPfsKey := peerServerHello[:1088]
|
||||||
c.ticket = peerServerHello[1088:]
|
c.ticket = peerServerHello[1088:]
|
||||||
|
|
||||||
pfsKey, err := dKeyPfs.Decapsulate(encapsulatedPfsKey)
|
pfsKey, err := pfsDKey.Decapsulate(encapsulatedPfsKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
c.baseKey = append(nfsKey, pfsKey...)
|
c.baseKey = append(pfsKey, nfsKey...)
|
||||||
|
|
||||||
authKey := make([]byte, 32)
|
authKey := make([]byte, 32)
|
||||||
hkdf.New(sha256.New, c.baseKey, encapsulatedNfsKey, eKeyPfs).Read(authKey)
|
hkdf.New(sha256.New, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Read(authKey)
|
||||||
nonce := make([]byte, 12)
|
nonce := [12]byte{ClientCipher}
|
||||||
VLESS, _ := newAead(ClientCipher, authKey).Open(nil, nonce, c.ticket, encapsulatedPfsKey)
|
VLESS, _ := newAead(ClientCipher, authKey).Open(nil, nonce[:], c.ticket, pfsEKeyBytes)
|
||||||
if !bytes.Equal(VLESS, []byte("VLESS")) { // TODO: more message
|
if !bytes.Equal(VLESS, []byte("VLESS")) { // TODO: more message
|
||||||
return nil, errors.New("invalid server").AtError()
|
return nil, errors.New("invalid server").AtError()
|
||||||
}
|
}
|
||||||
@@ -130,6 +135,12 @@ func (c *ClientConn) Write(b []byte) (int, error) {
|
|||||||
if len(b) == 0 {
|
if len(b) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
for n := 0; n < len(b); {
|
||||||
|
b := b[n:]
|
||||||
|
if len(b) > 8192 {
|
||||||
|
b = b[:8192] // for avoiding another copy() in server's Read()
|
||||||
|
}
|
||||||
|
n += len(b)
|
||||||
var data []byte
|
var data []byte
|
||||||
if c.aead == nil {
|
if c.aead == nil {
|
||||||
c.random = make([]byte, 32)
|
c.random = make([]byte, 32)
|
||||||
@@ -153,10 +164,11 @@ func (c *ClientConn) Write(b []byte) (int, error) {
|
|||||||
if _, err := c.Conn.Write(data); err != nil {
|
if _, err := c.Conn.Write(data); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return len(b), nil
|
return len(b), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ClientConn) Read(b []byte) (int, error) { // after first Write()
|
func (c *ClientConn) Read(b []byte) (int, error) {
|
||||||
if len(b) == 0 {
|
if len(b) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
@@ -167,11 +179,11 @@ func (c *ClientConn) Read(b []byte) (int, error) { // after first Write()
|
|||||||
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
peerPadding, _ := decodeHeader(peerHeader)
|
peerPaddingLen, _ := decodeHeader(peerHeader)
|
||||||
if peerPadding == 0 {
|
if peerPaddingLen == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if _, err := io.ReadFull(c.Conn, make([]byte, peerPadding)); err != nil {
|
if _, err := io.ReadFull(c.Conn, make([]byte, peerPaddingLen)); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,7 +198,7 @@ func (c *ClientConn) Read(b []byte) (int, error) { // after first Write()
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if c.random == nil {
|
if c.random == nil {
|
||||||
return 0, errors.New("can not Read() first")
|
return 0, errors.New("empty c.random")
|
||||||
}
|
}
|
||||||
peerKey := make([]byte, 32)
|
peerKey := make([]byte, 32)
|
||||||
hkdf.New(sha256.New, c.baseKey, peerRandom, c.random).Read(peerKey)
|
hkdf.New(sha256.New, c.baseKey, peerRandom, c.random).Read(peerKey)
|
||||||
@@ -218,7 +230,7 @@ func (c *ClientConn) Read(b []byte) (int, error) { // after first Write()
|
|||||||
}
|
}
|
||||||
dst := peerData[:peerLength-16]
|
dst := peerData[:peerLength-16]
|
||||||
if len(dst) <= len(b) {
|
if len(dst) <= len(b) {
|
||||||
dst = b[:len(dst)] // max=8192 is recommended for peer
|
dst = b[:len(dst)] // avoids another copy()
|
||||||
}
|
}
|
||||||
_, err = c.peerAead.Open(dst[:0], c.peerNonce, peerData, peerHeader)
|
_, err = c.peerAead.Open(dst[:0], c.peerNonce, peerData, peerHeader)
|
||||||
increaseNonce(c.peerNonce)
|
increaseNonce(c.peerNonce)
|
||||||
|
@@ -28,18 +28,14 @@ func decodeHeader(b []byte) (int, error) {
|
|||||||
return 0, errors.New("invalid record's header")
|
return 0, errors.New("invalid record's header")
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAead(c byte, k []byte) cipher.AEAD {
|
func newAead(c byte, k []byte) (aead cipher.AEAD) {
|
||||||
switch c {
|
if c&1 == 1 {
|
||||||
case 0:
|
block, _ := aes.NewCipher(k)
|
||||||
if block, err := aes.NewCipher(k); err == nil {
|
aead, _ = cipher.NewGCM(block)
|
||||||
aead, _ := cipher.NewGCM(block)
|
} else {
|
||||||
return aead
|
aead, _ = chacha20poly1305.New(k)
|
||||||
}
|
}
|
||||||
case 1:
|
return
|
||||||
aead, _ := chacha20poly1305.New(k)
|
|
||||||
return aead
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func increaseNonce(nonce []byte) {
|
func increaseNonce(nonce []byte) {
|
||||||
|
@@ -25,7 +25,8 @@ type ServerSession struct {
|
|||||||
|
|
||||||
type ServerInstance struct {
|
type ServerInstance struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
dKeyNfs *mlkem.DecapsulationKey768
|
nfsDKey *mlkem.DecapsulationKey768
|
||||||
|
nfsEKeyBytes []byte
|
||||||
xor uint32
|
xor uint32
|
||||||
minutes time.Duration
|
minutes time.Duration
|
||||||
sessions map[[21]byte]*ServerSession
|
sessions map[[21]byte]*ServerSession
|
||||||
@@ -44,9 +45,12 @@ type ServerConn struct {
|
|||||||
nonce []byte
|
nonce []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ServerInstance) Init(dKeyNfsData []byte, xor uint32, minutes time.Duration) (err error) {
|
func (i *ServerInstance) Init(nfsDKeySeed []byte, xor uint32, minutes time.Duration) (err error) {
|
||||||
i.dKeyNfs, err = mlkem.NewDecapsulationKey768(dKeyNfsData)
|
i.nfsDKey, err = mlkem.NewDecapsulationKey768(nfsDKeySeed)
|
||||||
|
if xor > 0 {
|
||||||
|
i.nfsEKeyBytes = i.nfsDKey.EncapsulationKey().Bytes()
|
||||||
i.xor = xor
|
i.xor = xor
|
||||||
|
}
|
||||||
if minutes > 0 {
|
if minutes > 0 {
|
||||||
i.minutes = minutes
|
i.minutes = minutes
|
||||||
i.sessions = make(map[[21]byte]*ServerSession)
|
i.sessions = make(map[[21]byte]*ServerSession)
|
||||||
@@ -55,9 +59,9 @@ func (i *ServerInstance) Init(dKeyNfsData []byte, xor uint32, minutes time.Durat
|
|||||||
time.Sleep(time.Minute)
|
time.Sleep(time.Minute)
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
i.Lock()
|
i.Lock()
|
||||||
for index, session := range i.sessions {
|
for ticket, session := range i.sessions {
|
||||||
if now.After(session.expire) {
|
if now.After(session.expire) {
|
||||||
delete(i.sessions, index)
|
delete(i.sessions, ticket)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i.Unlock()
|
i.Unlock()
|
||||||
@@ -68,11 +72,11 @@ func (i *ServerInstance) Init(dKeyNfsData []byte, xor uint32, minutes time.Durat
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) {
|
func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) {
|
||||||
if i.dKeyNfs == nil {
|
if i.nfsDKey == nil {
|
||||||
return nil, errors.New("uninitialized")
|
return nil, errors.New("uninitialized")
|
||||||
}
|
}
|
||||||
if i.xor == 1 {
|
if i.xor > 0 {
|
||||||
conn = NewXorConn(conn, i.dKeyNfs.EncapsulationKey().Bytes())
|
conn = NewXorConn(conn, i.nfsEKeyBytes)
|
||||||
}
|
}
|
||||||
c := &ServerConn{Conn: conn}
|
c := &ServerConn{Conn: conn}
|
||||||
|
|
||||||
@@ -100,49 +104,50 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if l, _ := decodeHeader(peerHeader); l != 0 {
|
if l, _ := decodeHeader(peerHeader); l != 0 {
|
||||||
c.Conn.Write(make([]byte, crypto.RandBetween(100, 1000))) // make client do new handshake
|
noise := make([]byte, crypto.RandBetween(100, 1000))
|
||||||
|
rand.Read(noise)
|
||||||
|
c.Conn.Write(noise) // make client do new handshake
|
||||||
return nil, errors.New("invalid ticket")
|
return nil, errors.New("invalid ticket")
|
||||||
}
|
}
|
||||||
|
|
||||||
peerClientHello := make([]byte, 1088+1184+1)
|
peerClientHello := make([]byte, 1+1184+1088)
|
||||||
copy(peerClientHello, peerTicketHello)
|
copy(peerClientHello, peerTicketHello)
|
||||||
copy(peerClientHello[53:], peerHeader)
|
copy(peerClientHello[53:], peerHeader)
|
||||||
if _, err := io.ReadFull(c.Conn, peerClientHello[58:]); err != nil {
|
if _, err := io.ReadFull(c.Conn, peerClientHello[58:]); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
encapsulatedNfsKey := peerClientHello[:1088]
|
c.cipher = peerClientHello[0]
|
||||||
eKeyPfsData := peerClientHello[1088:2272]
|
pfsEKeyBytes := peerClientHello[1:1185]
|
||||||
c.cipher = peerClientHello[2272]
|
encapsulatedNfsKey := peerClientHello[1185:2273]
|
||||||
if c.cipher != 0 && c.cipher != 1 {
|
|
||||||
return nil, errors.New("invalid cipher")
|
|
||||||
}
|
|
||||||
|
|
||||||
nfsKey, err := i.dKeyNfs.Decapsulate(encapsulatedNfsKey)
|
pfsEKey, err := mlkem.NewEncapsulationKey768(pfsEKeyBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
eKeyPfs, err := mlkem.NewEncapsulationKey768(eKeyPfsData)
|
nfsKey, err := i.nfsDKey.Decapsulate(encapsulatedNfsKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
pfsKey, encapsulatedPfsKey := eKeyPfs.Encapsulate()
|
pfsKey, encapsulatedPfsKey := pfsEKey.Encapsulate()
|
||||||
c.baseKey = append(nfsKey, pfsKey...)
|
c.baseKey = append(pfsKey, nfsKey...)
|
||||||
|
|
||||||
authKey := make([]byte, 32)
|
authKey := make([]byte, 32)
|
||||||
hkdf.New(sha256.New, c.baseKey, encapsulatedNfsKey, eKeyPfsData).Read(authKey)
|
hkdf.New(sha256.New, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Read(authKey)
|
||||||
nonce := make([]byte, 12)
|
nonce := [12]byte{c.cipher}
|
||||||
c.ticket = newAead(c.cipher, authKey).Seal(nil, nonce, []byte("VLESS"), encapsulatedPfsKey)
|
c.ticket = newAead(c.cipher, authKey).Seal(nil, nonce[:], []byte("VLESS"), pfsEKeyBytes)
|
||||||
|
|
||||||
padding := crypto.RandBetween(100, 1000)
|
paddingLen := crypto.RandBetween(100, 1000)
|
||||||
|
|
||||||
serverHello := make([]byte, 1088+21+5+padding)
|
serverHello := make([]byte, 1088+21+5+paddingLen)
|
||||||
copy(serverHello, encapsulatedPfsKey)
|
copy(serverHello, encapsulatedPfsKey)
|
||||||
copy(serverHello[1088:], c.ticket)
|
copy(serverHello[1088:], c.ticket)
|
||||||
encodeHeader(serverHello[1109:], int(padding))
|
encodeHeader(serverHello[1109:], int(paddingLen))
|
||||||
|
rand.Read(serverHello[1114:])
|
||||||
|
|
||||||
if _, err := c.Conn.Write(serverHello); err != nil {
|
if _, err := c.Conn.Write(serverHello); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// we can send more padding if needed
|
||||||
|
|
||||||
if i.minutes > 0 {
|
if i.minutes > 0 {
|
||||||
i.Lock()
|
i.Lock()
|
||||||
@@ -168,11 +173,11 @@ func (c *ServerConn) Read(b []byte) (int, error) {
|
|||||||
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
peerPadding, _ := decodeHeader(peerHeader)
|
peerPaddingLen, _ := decodeHeader(peerHeader)
|
||||||
if peerPadding == 0 {
|
if peerPaddingLen == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if _, err := io.ReadFull(c.Conn, make([]byte, peerPadding)); err != nil {
|
if _, err := io.ReadFull(c.Conn, make([]byte, peerPaddingLen)); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -212,7 +217,7 @@ func (c *ServerConn) Read(b []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
dst := peerData[:peerLength-16]
|
dst := peerData[:peerLength-16]
|
||||||
if len(dst) <= len(b) {
|
if len(dst) <= len(b) {
|
||||||
dst = b[:len(dst)] // max=8192 is recommended for peer
|
dst = b[:len(dst)] // avoids another copy()
|
||||||
}
|
}
|
||||||
_, err = c.peerAead.Open(dst[:0], c.peerNonce, peerData, peerHeader)
|
_, err = c.peerAead.Open(dst[:0], c.peerNonce, peerData, peerHeader)
|
||||||
increaseNonce(c.peerNonce)
|
increaseNonce(c.peerNonce)
|
||||||
@@ -226,14 +231,20 @@ func (c *ServerConn) Read(b []byte) (int, error) {
|
|||||||
return len(dst), nil
|
return len(dst), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ServerConn) Write(b []byte) (int, error) { // after first Read()
|
func (c *ServerConn) Write(b []byte) (int, error) {
|
||||||
if len(b) == 0 {
|
if len(b) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
for n := 0; n < len(b); {
|
||||||
|
b := b[n:]
|
||||||
|
if len(b) > 8192 {
|
||||||
|
b = b[:8192] // for avoiding another copy() in client's Read()
|
||||||
|
}
|
||||||
|
n += len(b)
|
||||||
var data []byte
|
var data []byte
|
||||||
if c.aead == nil {
|
if c.aead == nil {
|
||||||
if c.peerRandom == nil {
|
if c.peerRandom == nil {
|
||||||
return 0, errors.New("can not Write() first")
|
return 0, errors.New("empty c.peerRandom")
|
||||||
}
|
}
|
||||||
data = make([]byte, 32+5+len(b)+16)
|
data = make([]byte, 32+5+len(b)+16)
|
||||||
rand.Read(data[:32])
|
rand.Read(data[:32])
|
||||||
@@ -252,5 +263,6 @@ func (c *ServerConn) Write(b []byte) (int, error) { // after first Read()
|
|||||||
if _, err := c.Conn.Write(data); err != nil {
|
if _, err := c.Conn.Write(data); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return len(b), nil
|
return len(b), nil
|
||||||
}
|
}
|
||||||
|
@@ -38,7 +38,7 @@ func (c *XorConn) Write(b []byte) (int, error) {
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if iv != nil {
|
if iv != nil {
|
||||||
b = b[16:]
|
b = b[16:] // for len(b)
|
||||||
}
|
}
|
||||||
return len(b), nil
|
return len(b), nil
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user