diff --git a/listener/inbound/vless_test.go b/listener/inbound/vless_test.go index c110228d..f3fcd39c 100644 --- a/listener/inbound/vless_test.go +++ b/listener/inbound/vless_test.go @@ -116,6 +116,11 @@ func TestInboundVless_Encryption(t *testing.T) { Encryption: "8min-xored-mlkem768client-" + clientBase64, } testInboundVless(t, inboundOptions, outboundOptions) + t.Run("xtls-rprx-vision", func(t *testing.T) { + outboundOptions := outboundOptions + outboundOptions.Flow = "xtls-rprx-vision" + testInboundVless(t, inboundOptions, outboundOptions) + }) }) } diff --git a/transport/vless/encryption/client.go b/transport/vless/encryption/client.go index c23a7f60..9d249038 100644 --- a/transport/vless/encryption/client.go +++ b/transport/vless/encryption/client.go @@ -4,7 +4,6 @@ import ( "bytes" "crypto/cipher" "crypto/rand" - "crypto/sha256" "errors" "fmt" "io" @@ -15,6 +14,7 @@ import ( "time" "github.com/metacubex/utls/mlkem" + "golang.org/x/crypto/sha3" "golang.org/x/sys/cpu" ) @@ -69,10 +69,10 @@ func (i *ClientInstance) Init(nfsEKeyBytes []byte, xor uint32, minutes time.Dura if err != nil { return } - hash256 := sha256.Sum256(nfsEKeyBytes) + hash256 := sha3.Sum256(nfsEKeyBytes) copy(i.hash11[:], hash256[:]) if xor > 0 { - xorKey := sha256.Sum256(nfsEKeyBytes) + xorKey := sha3.Sum256(nfsEKeyBytes) i.xorKey = xorKey[:] } i.minutes = minutes diff --git a/transport/vless/encryption/common.go b/transport/vless/encryption/common.go index 151a48e4..a67a6169 100644 --- a/transport/vless/encryption/common.go +++ b/transport/vless/encryption/common.go @@ -5,7 +5,6 @@ import ( "crypto/aes" "crypto/cipher" "crypto/rand" - "crypto/sha256" "fmt" "io" "math/big" @@ -13,6 +12,7 @@ import ( "golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/hkdf" + "golang.org/x/crypto/sha3" ) var MaxNonce = bytes.Repeat([]byte{255}, 12) @@ -75,7 +75,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) { key := make([]byte, 32) - hkdf.New(sha256.New, secret, salt, info).Read(key) + hkdf.New(sha3.New256, secret, salt, info).Read(key) if c&1 == 1 { block, _ := aes.NewCipher(key) aead, _ = cipher.NewGCM(block) diff --git a/transport/vless/encryption/doc.go b/transport/vless/encryption/doc.go index a5004c27..9ba7eb11 100644 --- a/transport/vless/encryption/doc.go +++ b/transport/vless/encryption/doc.go @@ -12,4 +12,5 @@ // https://github.com/XTLS/Xray-core/commit/2807ee432a1fbeb301815647189eacd650b12a8b // https://github.com/XTLS/Xray-core/commit/bfe4820f2f086daf639b1957eb23dc13c843cad1 // https://github.com/XTLS/Xray-core/commit/d1fb48521271251a8c74bd64fcc2fc8700717a3b +// https://github.com/XTLS/Xray-core/commit/49580705f6029648399304b816a2737f991582a8 package encryption diff --git a/transport/vless/encryption/server.go b/transport/vless/encryption/server.go index 5bc24133..9c5c102e 100644 --- a/transport/vless/encryption/server.go +++ b/transport/vless/encryption/server.go @@ -4,7 +4,6 @@ import ( "bytes" "crypto/cipher" "crypto/rand" - "crypto/sha256" "errors" "fmt" "io" @@ -13,6 +12,7 @@ import ( "time" "github.com/metacubex/utls/mlkem" + "golang.org/x/crypto/sha3" ) type ServerSession struct { @@ -54,10 +54,10 @@ func (i *ServerInstance) Init(nfsDKeySeed []byte, xor uint32, minutes time.Durat if err != nil { return } - hash256 := sha256.Sum256(i.nfsDKey.EncapsulationKey().Bytes()) + hash256 := sha3.Sum256(i.nfsDKey.EncapsulationKey().Bytes()) copy(i.hash11[:], hash256[:]) if xor > 0 { - xorKey := sha256.Sum256(i.nfsDKey.EncapsulationKey().Bytes()) + xorKey := sha3.Sum256(i.nfsDKey.EncapsulationKey().Bytes()) i.xorKey = xorKey[:] } if minutes > 0 { @@ -279,9 +279,9 @@ func (c *ServerConn) Write(b []byte) (int, error) { data = make([]byte, 5+32+5+len(b)+16) EncodeHeader(data, 0, 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.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]) } else { data = make([]byte, 5+len(b)+16) diff --git a/transport/vless/encryption/xor.go b/transport/vless/encryption/xor.go index 64828eaa..69ff1578 100644 --- a/transport/vless/encryption/xor.go +++ b/transport/vless/encryption/xor.go @@ -15,6 +15,14 @@ type XorConn struct { peerCtr cipher.Stream isHeader 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 { @@ -26,29 +34,56 @@ func (c *XorConn) Write(b []byte) (int, error) { // whole one/two records if len(b) == 0 { return 0, nil } - var iv []byte - if c.ctr == nil { - block, _ := aes.NewCipher(c.key) - iv = make([]byte, 16) - rand.Read(iv) - c.ctr = cipher.NewCTR(block, iv) + if !c.out_after0 { + var iv []byte + if c.ctr == nil { + block, _ := aes.NewCipher(c.key) + iv = make([]byte, 16) + rand.Read(iv) + c.ctr = cipher.NewCTR(block, iv) + } + t, l, _ := DecodeHeader(b) + if t == 23 { // single 23 + l = 5 + } else { // 1/0 + 23, or noises only + l += 10 + if t == 0 { + c.out_after0 = true + } + } + c.ctr.XORKeyStream(b[:l], b[:l]) // caller MUST discard b + if iv != nil { + b = append(iv, b...) + } + if _, err := c.Conn.Write(b); err != nil { + return 0, err + } + if iv != nil { + b = b[16:] // for len(b) + } + return len(b), nil } - t, l, _ := DecodeHeader(b) - if t == 23 { // single 23 - l = 5 - } else { // 1/0 + 23, or noises only - l += 10 - } - c.ctr.XORKeyStream(b[:l], b[:l]) // caller MUST discard b - if iv != nil { - b = append(iv, b...) + 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 } - if iv != nil { - b = b[16:] // for len(b) - } return len(b), nil } @@ -56,31 +91,56 @@ func (c *XorConn) Read(b []byte) (int, error) { // 5-bytes, data, 5-bytes... if len(b) == 0 { return 0, nil } - if c.peerCtr == nil { - peerIv := make([]byte, 16) - if _, err := io.ReadFull(c.Conn, peerIv); err != nil { + if !c.in_after0 || !c.isHeader { + if c.peerCtr == nil { + peerIv := make([]byte, 16) + if _, err := io.ReadFull(c.Conn, peerIv); err != nil { + return 0, err + } + block, _ := aes.NewCipher(c.key) + c.peerCtr = cipher.NewCTR(block, peerIv) + c.isHeader = true + } + if _, err := io.ReadFull(c.Conn, b); err != nil { return 0, err } - block, _ := aes.NewCipher(c.key) - c.peerCtr = cipher.NewCTR(block, peerIv) - c.isHeader = true - } - if _, err := io.ReadFull(c.Conn, b); err != nil { - return 0, err - } - if c.skipNext { - c.skipNext = false + if c.skipNext { + c.skipNext = false + return len(b), nil + } + c.peerCtr.XORKeyStream(b, b) + if c.isHeader { // always 5-bytes + if t, _, _ := DecodeHeader(b); t == 23 { + c.skipNext = true + } else { + c.isHeader = false + if t == 0 { + c.in_after0 = true + } + } + } else { + c.isHeader = true + } return len(b), nil } - c.peerCtr.XORKeyStream(b, b) - if c.isHeader { // always 5-bytes - if t, _, _ := DecodeHeader(b); t == 23 { - c.skipNext = true - } else { - c.isHeader = false + n, err := c.Conn.Read(b) + for p := b[:n]; ; { // for XTLS + if len(p) <= c.in_skip { + c.in_skip -= len(p) + break } - } else { - c.isHeader = true + 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 len(b), nil + return n, err }