Use SHA3-256 instead of SHA2-256; Support XTLS Vision for random appearance

https://github.com/XTLS/Xray-core/pull/4952#issuecomment-3197163816
This commit is contained in:
RPRX
2025-08-18 14:58:21 +00:00
committed by GitHub
parent d1fb485212
commit 49580705f6
8 changed files with 129 additions and 74 deletions

View File

@@ -61,10 +61,6 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
return nil, errors.New(`VLESS clients: "flow" doesn't support "` + account.Flow + `" in this version`) return nil, errors.New(`VLESS clients: "flow" doesn't support "` + account.Flow + `" in this version`)
} }
if strings.Contains(c.Decryption, "xored") && account.Flow == vless.XRV {
return nil, errors.New(`VLESS clients: "xored" doesn't support "flow" yet`)
}
if account.Encryption != "" { if account.Encryption != "" {
return nil, errors.New(`VLESS clients: "encryption" should not in inbound settings`) return nil, errors.New(`VLESS clients: "encryption" should not in inbound settings`)
} }
@@ -213,11 +209,7 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
account.Id = u.String() account.Id = u.String()
switch account.Flow { switch account.Flow {
case "": case "", vless.XRV, vless.XRV + "-udp443":
case vless.XRV, vless.XRV + "-udp443":
if strings.Contains(account.Encryption, "xored") {
return nil, errors.New(`VLESS users: "xored" doesn't support "flow" yet`)
}
default: default:
return nil, errors.New(`VLESS users: "flow" doesn't support "` + account.Flow + `" in this version`) return nil, errors.New(`VLESS users: "flow" doesn't support "` + account.Flow + `" in this version`)
} }

View File

@@ -25,6 +25,7 @@ import (
"github.com/xtls/xray-core/common/signal" "github.com/xtls/xray-core/common/signal"
"github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/features/stats" "github.com/xtls/xray-core/features/stats"
"github.com/xtls/xray-core/proxy/vless/encryption"
"github.com/xtls/xray-core/transport" "github.com/xtls/xray-core/transport"
"github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet"
"github.com/xtls/xray-core/transport/internet/reality" "github.com/xtls/xray-core/transport/internet/reality"
@@ -534,6 +535,9 @@ func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) {
readCounter = statConn.ReadCounter readCounter = statConn.ReadCounter
writerCounter = statConn.WriteCounter writerCounter = statConn.WriteCounter
} }
if _, ok := conn.(*encryption.XorConn); ok {
return conn, readCounter, writerCounter
}
if xc, ok := conn.(*tls.Conn); ok { if xc, ok := conn.(*tls.Conn); ok {
conn = xc.NetConn() conn = xc.NetConn()
} else if utlsConn, ok := conn.(*tls.UConn); ok { } else if utlsConn, ok := conn.(*tls.UConn); ok {

View File

@@ -5,7 +5,7 @@ import (
"crypto/cipher" "crypto/cipher"
"crypto/mlkem" "crypto/mlkem"
"crypto/rand" "crypto/rand"
"crypto/sha256" "crypto/sha3"
"io" "io"
"net" "net"
"strings" "strings"
@@ -58,10 +58,10 @@ func (i *ClientInstance) Init(nfsEKeyBytes []byte, xor uint32, minutes time.Dura
if err != nil { if err != nil {
return return
} }
hash256 := sha256.Sum256(nfsEKeyBytes) hash256 := sha3.Sum256(nfsEKeyBytes)
copy(i.hash11[:], hash256[:]) copy(i.hash11[:], hash256[:])
if xor > 0 { if xor > 0 {
xorKey := sha256.Sum256(nfsEKeyBytes) xorKey := sha3.Sum256(nfsEKeyBytes)
i.xorKey = xorKey[:] i.xorKey = xorKey[:]
} }
i.minutes = minutes i.minutes = minutes

View File

@@ -4,14 +4,14 @@ import (
"bytes" "bytes"
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/sha256" "crypto/hkdf"
"crypto/sha3"
"fmt" "fmt"
"io" "io"
"net" "net"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/hkdf"
) )
var MaxNonce = bytes.Repeat([]byte{255}, 12) var MaxNonce = bytes.Repeat([]byte{255}, 12)
@@ -73,8 +73,7 @@ func ReadAndDiscardPaddings(conn net.Conn) (h []byte, t byte, l int, err error)
} }
func NewAead(c byte, secret, salt, info []byte) (aead cipher.AEAD) { func NewAead(c byte, secret, salt, info []byte) (aead cipher.AEAD) {
key := make([]byte, 32) key, _ := hkdf.Key(sha3.New256, secret, salt, string(info), 32)
hkdf.New(sha256.New, secret, salt, info).Read(key)
if c&1 == 1 { if c&1 == 1 {
block, _ := aes.NewCipher(key) block, _ := aes.NewCipher(key)
aead, _ = cipher.NewGCM(block) aead, _ = cipher.NewGCM(block)

View File

@@ -5,7 +5,7 @@ import (
"crypto/cipher" "crypto/cipher"
"crypto/mlkem" "crypto/mlkem"
"crypto/rand" "crypto/rand"
"crypto/sha256" "crypto/sha3"
"fmt" "fmt"
"io" "io"
"net" "net"
@@ -55,10 +55,10 @@ func (i *ServerInstance) Init(nfsDKeySeed []byte, xor uint32, minutes time.Durat
if err != nil { if err != nil {
return return
} }
hash256 := sha256.Sum256(i.nfsDKey.EncapsulationKey().Bytes()) hash256 := sha3.Sum256(i.nfsDKey.EncapsulationKey().Bytes())
copy(i.hash11[:], hash256[:]) copy(i.hash11[:], hash256[:])
if xor > 0 { if xor > 0 {
xorKey := sha256.Sum256(i.nfsDKey.EncapsulationKey().Bytes()) xorKey := sha3.Sum256(i.nfsDKey.EncapsulationKey().Bytes())
i.xorKey = xorKey[:] i.xorKey = xorKey[:]
} }
if minutes > 0 { if minutes > 0 {
@@ -282,9 +282,9 @@ func (c *ServerConn) Write(b []byte) (int, error) {
data = make([]byte, 5+32+5+len(b)+16) data = make([]byte, 5+32+5+len(b)+16)
EncodeHeader(data, 0, 32) EncodeHeader(data, 0, 32)
rand.Read(data[5 : 5+32]) rand.Read(data[5 : 5+32])
EncodeHeader(data[5+32:], 23, len(b)+16)
c.aead = NewAead(c.cipher, c.baseKey, data[5:5+32], c.peerRandom) c.aead = NewAead(c.cipher, c.baseKey, data[5:5+32], c.peerRandom)
c.nonce = make([]byte, 12) c.nonce = make([]byte, 12)
EncodeHeader(data[5+32:], 23, len(b)+16)
c.aead.Seal(data[:5+32+5], c.nonce, b, data[5+32:5+32+5]) c.aead.Seal(data[:5+32+5], c.nonce, b, data[5+32:5+32+5])
} else { } else {
data = make([]byte, 5+len(b)+16) data = make([]byte, 5+len(b)+16)

View File

@@ -15,6 +15,14 @@ type XorConn struct {
peerCtr cipher.Stream peerCtr cipher.Stream
isHeader bool isHeader bool
skipNext bool skipNext bool
out_after0 bool
out_header []byte
out_skip int
in_after0 bool
in_header []byte
in_skip int
} }
func NewXorConn(conn net.Conn, key []byte) *XorConn { func NewXorConn(conn net.Conn, key []byte) *XorConn {
@@ -26,6 +34,7 @@ func (c *XorConn) Write(b []byte) (int, error) { // whole one/two records
if len(b) == 0 { if len(b) == 0 {
return 0, nil return 0, nil
} }
if !c.out_after0 {
var iv []byte var iv []byte
if c.ctr == nil { if c.ctr == nil {
block, _ := aes.NewCipher(c.key) block, _ := aes.NewCipher(c.key)
@@ -38,6 +47,9 @@ func (c *XorConn) Write(b []byte) (int, error) { // whole one/two records
l = 5 l = 5
} else { // 1/0 + 23, or noises only } else { // 1/0 + 23, or noises only
l += 10 l += 10
if t == 0 {
c.out_after0 = true
}
} }
c.ctr.XORKeyStream(b[:l], b[:l]) // caller MUST discard b c.ctr.XORKeyStream(b[:l], b[:l]) // caller MUST discard b
if iv != nil { if iv != nil {
@@ -51,11 +63,35 @@ func (c *XorConn) Write(b []byte) (int, error) { // whole one/two records
} }
return len(b), nil return len(b), nil
} }
for p := b; ; { // for XTLS
if len(p) <= c.out_skip {
c.out_skip -= len(p)
break
}
p = p[c.out_skip:]
c.out_skip = 0
need := 5 - len(c.out_header)
if len(p) < need {
c.out_header = append(c.out_header, p...)
c.ctr.XORKeyStream(p, p)
break
}
_, c.out_skip, _ = DecodeHeader(append(c.out_header, p[:need]...))
c.out_header = make([]byte, 0, 5) // DO NOT CHANGE
c.ctr.XORKeyStream(p[:need], p[:need])
p = p[need:]
}
if _, err := c.Conn.Write(b); err != nil {
return 0, err
}
return len(b), nil
}
func (c *XorConn) Read(b []byte) (int, error) { // 5-bytes, data, 5-bytes... func (c *XorConn) Read(b []byte) (int, error) { // 5-bytes, data, 5-bytes...
if len(b) == 0 { if len(b) == 0 {
return 0, nil return 0, nil
} }
if !c.in_after0 || !c.isHeader {
if c.peerCtr == nil { if c.peerCtr == nil {
peerIv := make([]byte, 16) peerIv := make([]byte, 16)
if _, err := io.ReadFull(c.Conn, peerIv); err != nil { if _, err := io.ReadFull(c.Conn, peerIv); err != nil {
@@ -78,9 +114,33 @@ func (c *XorConn) Read(b []byte) (int, error) { // 5-bytes, data, 5-bytes...
c.skipNext = true c.skipNext = true
} else { } else {
c.isHeader = false c.isHeader = false
if t == 0 {
c.in_after0 = true
}
} }
} else { } else {
c.isHeader = true c.isHeader = true
} }
return len(b), nil return len(b), nil
} }
n, err := c.Conn.Read(b)
for p := b[:n]; ; { // for XTLS
if len(p) <= c.in_skip {
c.in_skip -= len(p)
break
}
p = p[c.in_skip:]
c.in_skip = 0
need := 5 - len(c.in_header)
if len(p) < need {
c.peerCtr.XORKeyStream(p, p)
c.in_header = append(c.in_header, p...)
break
}
c.peerCtr.XORKeyStream(p[:need], p[:need])
_, c.in_skip, _ = DecodeHeader(append(c.in_header, p[:need]...))
c.in_header = make([]byte, 0, 5) // DO NOT CHANGE
p = p[need:]
}
return n, err
}

View File

@@ -208,6 +208,14 @@ func (*Handler) Network() []net.Network {
// Process implements proxy.Inbound.Process(). // Process implements proxy.Inbound.Process().
func (h *Handler) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error { func (h *Handler) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error {
if h.decryption != nil {
var err error
connection, err = h.decryption.Handshake(connection)
if err != nil {
return errors.New("ML-KEM-768 handshake failed").Base(err).AtInfo()
}
}
iConn := connection iConn := connection
if statConn, ok := iConn.(*stat.CounterConnection); ok { if statConn, ok := iConn.(*stat.CounterConnection); ok {
iConn = statConn.Connection iConn = statConn.Connection
@@ -218,14 +226,6 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
return errors.New("unable to set read deadline").Base(err).AtWarning() return errors.New("unable to set read deadline").Base(err).AtWarning()
} }
if h.decryption != nil {
var err error
connection, err = h.decryption.Handshake(connection)
if err != nil {
return errors.New("ML-KEM-768 handshake failed").Base(err).AtInfo()
}
}
first := buf.FromBytes(make([]byte, buf.Size)) first := buf.FromBytes(make([]byte, buf.Size))
first.Clear() first.Clear()
firstLen, errR := first.ReadFrom(connection) firstLen, errR := first.ReadFrom(connection)

View File

@@ -103,13 +103,6 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
} }
defer conn.Close() defer conn.Close()
iConn := conn
if statConn, ok := iConn.(*stat.CounterConnection); ok {
iConn = statConn.Connection
}
target := ob.Target
errors.LogInfo(ctx, "tunneling request to ", target, " via ", rec.Destination().NetAddr())
if h.encryption != nil { if h.encryption != nil {
var err error var err error
conn, err = h.encryption.Handshake(conn) conn, err = h.encryption.Handshake(conn)
@@ -118,6 +111,13 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
} }
} }
iConn := conn
if statConn, ok := iConn.(*stat.CounterConnection); ok {
iConn = statConn.Connection
}
target := ob.Target
errors.LogInfo(ctx, "tunneling request to ", target, " via ", rec.Destination().NetAddr())
command := protocol.RequestCommandTCP command := protocol.RequestCommandTCP
if target.Network == net.Network_UDP { if target.Network == net.Network_UDP {
command = protocol.RequestCommandUDP command = protocol.RequestCommandUDP