Skip to content

Commit 4958070

Browse files
authored
Use SHA3-256 instead of SHA2-256; Support XTLS Vision for random appearance
#4952 (comment)
1 parent d1fb485 commit 4958070

File tree

8 files changed

+129
-74
lines changed

8 files changed

+129
-74
lines changed

infra/conf/vless.go

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,6 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
6161
return nil, errors.New(`VLESS clients: "flow" doesn't support "` + account.Flow + `" in this version`)
6262
}
6363

64-
if strings.Contains(c.Decryption, "xored") && account.Flow == vless.XRV {
65-
return nil, errors.New(`VLESS clients: "xored" doesn't support "flow" yet`)
66-
}
67-
6864
if account.Encryption != "" {
6965
return nil, errors.New(`VLESS clients: "encryption" should not in inbound settings`)
7066
}
@@ -213,11 +209,7 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
213209
account.Id = u.String()
214210

215211
switch account.Flow {
216-
case "":
217-
case vless.XRV, vless.XRV + "-udp443":
218-
if strings.Contains(account.Encryption, "xored") {
219-
return nil, errors.New(`VLESS users: "xored" doesn't support "flow" yet`)
220-
}
212+
case "", vless.XRV, vless.XRV + "-udp443":
221213
default:
222214
return nil, errors.New(`VLESS users: "flow" doesn't support "` + account.Flow + `" in this version`)
223215
}

proxy/proxy.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/xtls/xray-core/common/signal"
2626
"github.com/xtls/xray-core/features/routing"
2727
"github.com/xtls/xray-core/features/stats"
28+
"github.com/xtls/xray-core/proxy/vless/encryption"
2829
"github.com/xtls/xray-core/transport"
2930
"github.com/xtls/xray-core/transport/internet"
3031
"github.com/xtls/xray-core/transport/internet/reality"
@@ -534,6 +535,9 @@ func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) {
534535
readCounter = statConn.ReadCounter
535536
writerCounter = statConn.WriteCounter
536537
}
538+
if _, ok := conn.(*encryption.XorConn); ok {
539+
return conn, readCounter, writerCounter
540+
}
537541
if xc, ok := conn.(*tls.Conn); ok {
538542
conn = xc.NetConn()
539543
} else if utlsConn, ok := conn.(*tls.UConn); ok {

proxy/vless/encryption/client.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"crypto/cipher"
66
"crypto/mlkem"
77
"crypto/rand"
8-
"crypto/sha256"
8+
"crypto/sha3"
99
"io"
1010
"net"
1111
"strings"
@@ -58,10 +58,10 @@ func (i *ClientInstance) Init(nfsEKeyBytes []byte, xor uint32, minutes time.Dura
5858
if err != nil {
5959
return
6060
}
61-
hash256 := sha256.Sum256(nfsEKeyBytes)
61+
hash256 := sha3.Sum256(nfsEKeyBytes)
6262
copy(i.hash11[:], hash256[:])
6363
if xor > 0 {
64-
xorKey := sha256.Sum256(nfsEKeyBytes)
64+
xorKey := sha3.Sum256(nfsEKeyBytes)
6565
i.xorKey = xorKey[:]
6666
}
6767
i.minutes = minutes

proxy/vless/encryption/common.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ import (
44
"bytes"
55
"crypto/aes"
66
"crypto/cipher"
7-
"crypto/sha256"
7+
"crypto/hkdf"
8+
"crypto/sha3"
89
"fmt"
910
"io"
1011
"net"
1112

1213
"github.com/xtls/xray-core/common/errors"
1314
"golang.org/x/crypto/chacha20poly1305"
14-
"golang.org/x/crypto/hkdf"
1515
)
1616

1717
var MaxNonce = bytes.Repeat([]byte{255}, 12)
@@ -73,8 +73,7 @@ func ReadAndDiscardPaddings(conn net.Conn) (h []byte, t byte, l int, err error)
7373
}
7474

7575
func NewAead(c byte, secret, salt, info []byte) (aead cipher.AEAD) {
76-
key := make([]byte, 32)
77-
hkdf.New(sha256.New, secret, salt, info).Read(key)
76+
key, _ := hkdf.Key(sha3.New256, secret, salt, string(info), 32)
7877
if c&1 == 1 {
7978
block, _ := aes.NewCipher(key)
8079
aead, _ = cipher.NewGCM(block)

proxy/vless/encryption/server.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"crypto/cipher"
66
"crypto/mlkem"
77
"crypto/rand"
8-
"crypto/sha256"
8+
"crypto/sha3"
99
"fmt"
1010
"io"
1111
"net"
@@ -55,10 +55,10 @@ func (i *ServerInstance) Init(nfsDKeySeed []byte, xor uint32, minutes time.Durat
5555
if err != nil {
5656
return
5757
}
58-
hash256 := sha256.Sum256(i.nfsDKey.EncapsulationKey().Bytes())
58+
hash256 := sha3.Sum256(i.nfsDKey.EncapsulationKey().Bytes())
5959
copy(i.hash11[:], hash256[:])
6060
if xor > 0 {
61-
xorKey := sha256.Sum256(i.nfsDKey.EncapsulationKey().Bytes())
61+
xorKey := sha3.Sum256(i.nfsDKey.EncapsulationKey().Bytes())
6262
i.xorKey = xorKey[:]
6363
}
6464
if minutes > 0 {
@@ -282,9 +282,9 @@ func (c *ServerConn) Write(b []byte) (int, error) {
282282
data = make([]byte, 5+32+5+len(b)+16)
283283
EncodeHeader(data, 0, 32)
284284
rand.Read(data[5 : 5+32])
285+
EncodeHeader(data[5+32:], 23, len(b)+16)
285286
c.aead = NewAead(c.cipher, c.baseKey, data[5:5+32], c.peerRandom)
286287
c.nonce = make([]byte, 12)
287-
EncodeHeader(data[5+32:], 23, len(b)+16)
288288
c.aead.Seal(data[:5+32+5], c.nonce, b, data[5+32:5+32+5])
289289
} else {
290290
data = make([]byte, 5+len(b)+16)

proxy/vless/encryption/xor.go

Lines changed: 99 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ type XorConn struct {
1515
peerCtr cipher.Stream
1616
isHeader bool
1717
skipNext bool
18+
19+
out_after0 bool
20+
out_header []byte
21+
out_skip int
22+
23+
in_after0 bool
24+
in_header []byte
25+
in_skip int
1826
}
1927

2028
func NewXorConn(conn net.Conn, key []byte) *XorConn {
@@ -26,61 +34,113 @@ func (c *XorConn) Write(b []byte) (int, error) { // whole one/two records
2634
if len(b) == 0 {
2735
return 0, nil
2836
}
29-
var iv []byte
30-
if c.ctr == nil {
31-
block, _ := aes.NewCipher(c.key)
32-
iv = make([]byte, 16)
33-
rand.Read(iv)
34-
c.ctr = cipher.NewCTR(block, iv)
35-
}
36-
t, l, _ := DecodeHeader(b)
37-
if t == 23 { // single 23
38-
l = 5
39-
} else { // 1/0 + 23, or noises only
40-
l += 10
37+
if !c.out_after0 {
38+
var iv []byte
39+
if c.ctr == nil {
40+
block, _ := aes.NewCipher(c.key)
41+
iv = make([]byte, 16)
42+
rand.Read(iv)
43+
c.ctr = cipher.NewCTR(block, iv)
44+
}
45+
t, l, _ := DecodeHeader(b)
46+
if t == 23 { // single 23
47+
l = 5
48+
} else { // 1/0 + 23, or noises only
49+
l += 10
50+
if t == 0 {
51+
c.out_after0 = true
52+
}
53+
}
54+
c.ctr.XORKeyStream(b[:l], b[:l]) // caller MUST discard b
55+
if iv != nil {
56+
b = append(iv, b...)
57+
}
58+
if _, err := c.Conn.Write(b); err != nil {
59+
return 0, err
60+
}
61+
if iv != nil {
62+
b = b[16:] // for len(b)
63+
}
64+
return len(b), nil
4165
}
42-
c.ctr.XORKeyStream(b[:l], b[:l]) // caller MUST discard b
43-
if iv != nil {
44-
b = append(iv, b...)
66+
for p := b; ; { // for XTLS
67+
if len(p) <= c.out_skip {
68+
c.out_skip -= len(p)
69+
break
70+
}
71+
p = p[c.out_skip:]
72+
c.out_skip = 0
73+
need := 5 - len(c.out_header)
74+
if len(p) < need {
75+
c.out_header = append(c.out_header, p...)
76+
c.ctr.XORKeyStream(p, p)
77+
break
78+
}
79+
_, c.out_skip, _ = DecodeHeader(append(c.out_header, p[:need]...))
80+
c.out_header = make([]byte, 0, 5) // DO NOT CHANGE
81+
c.ctr.XORKeyStream(p[:need], p[:need])
82+
p = p[need:]
4583
}
4684
if _, err := c.Conn.Write(b); err != nil {
4785
return 0, err
4886
}
49-
if iv != nil {
50-
b = b[16:] // for len(b)
51-
}
5287
return len(b), nil
5388
}
5489

5590
func (c *XorConn) Read(b []byte) (int, error) { // 5-bytes, data, 5-bytes...
5691
if len(b) == 0 {
5792
return 0, nil
5893
}
59-
if c.peerCtr == nil {
60-
peerIv := make([]byte, 16)
61-
if _, err := io.ReadFull(c.Conn, peerIv); err != nil {
94+
if !c.in_after0 || !c.isHeader {
95+
if c.peerCtr == nil {
96+
peerIv := make([]byte, 16)
97+
if _, err := io.ReadFull(c.Conn, peerIv); err != nil {
98+
return 0, err
99+
}
100+
block, _ := aes.NewCipher(c.key)
101+
c.peerCtr = cipher.NewCTR(block, peerIv)
102+
c.isHeader = true
103+
}
104+
if _, err := io.ReadFull(c.Conn, b); err != nil {
62105
return 0, err
63106
}
64-
block, _ := aes.NewCipher(c.key)
65-
c.peerCtr = cipher.NewCTR(block, peerIv)
66-
c.isHeader = true
67-
}
68-
if _, err := io.ReadFull(c.Conn, b); err != nil {
69-
return 0, err
70-
}
71-
if c.skipNext {
72-
c.skipNext = false
107+
if c.skipNext {
108+
c.skipNext = false
109+
return len(b), nil
110+
}
111+
c.peerCtr.XORKeyStream(b, b)
112+
if c.isHeader { // always 5-bytes
113+
if t, _, _ := DecodeHeader(b); t == 23 {
114+
c.skipNext = true
115+
} else {
116+
c.isHeader = false
117+
if t == 0 {
118+
c.in_after0 = true
119+
}
120+
}
121+
} else {
122+
c.isHeader = true
123+
}
73124
return len(b), nil
74125
}
75-
c.peerCtr.XORKeyStream(b, b)
76-
if c.isHeader { // always 5-bytes
77-
if t, _, _ := DecodeHeader(b); t == 23 {
78-
c.skipNext = true
79-
} else {
80-
c.isHeader = false
126+
n, err := c.Conn.Read(b)
127+
for p := b[:n]; ; { // for XTLS
128+
if len(p) <= c.in_skip {
129+
c.in_skip -= len(p)
130+
break
81131
}
82-
} else {
83-
c.isHeader = true
132+
p = p[c.in_skip:]
133+
c.in_skip = 0
134+
need := 5 - len(c.in_header)
135+
if len(p) < need {
136+
c.peerCtr.XORKeyStream(p, p)
137+
c.in_header = append(c.in_header, p...)
138+
break
139+
}
140+
c.peerCtr.XORKeyStream(p[:need], p[:need])
141+
_, c.in_skip, _ = DecodeHeader(append(c.in_header, p[:need]...))
142+
c.in_header = make([]byte, 0, 5) // DO NOT CHANGE
143+
p = p[need:]
84144
}
85-
return len(b), nil
145+
return n, err
86146
}

proxy/vless/inbound/inbound.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,14 @@ func (*Handler) Network() []net.Network {
208208

209209
// Process implements proxy.Inbound.Process().
210210
func (h *Handler) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error {
211+
if h.decryption != nil {
212+
var err error
213+
connection, err = h.decryption.Handshake(connection)
214+
if err != nil {
215+
return errors.New("ML-KEM-768 handshake failed").Base(err).AtInfo()
216+
}
217+
}
218+
211219
iConn := connection
212220
if statConn, ok := iConn.(*stat.CounterConnection); ok {
213221
iConn = statConn.Connection
@@ -218,14 +226,6 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
218226
return errors.New("unable to set read deadline").Base(err).AtWarning()
219227
}
220228

221-
if h.decryption != nil {
222-
var err error
223-
connection, err = h.decryption.Handshake(connection)
224-
if err != nil {
225-
return errors.New("ML-KEM-768 handshake failed").Base(err).AtInfo()
226-
}
227-
}
228-
229229
first := buf.FromBytes(make([]byte, buf.Size))
230230
first.Clear()
231231
firstLen, errR := first.ReadFrom(connection)

proxy/vless/outbound/outbound.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,6 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
103103
}
104104
defer conn.Close()
105105

106-
iConn := conn
107-
if statConn, ok := iConn.(*stat.CounterConnection); ok {
108-
iConn = statConn.Connection
109-
}
110-
target := ob.Target
111-
errors.LogInfo(ctx, "tunneling request to ", target, " via ", rec.Destination().NetAddr())
112-
113106
if h.encryption != nil {
114107
var err error
115108
conn, err = h.encryption.Handshake(conn)
@@ -118,6 +111,13 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
118111
}
119112
}
120113

114+
iConn := conn
115+
if statConn, ok := iConn.(*stat.CounterConnection); ok {
116+
iConn = statConn.Connection
117+
}
118+
target := ob.Target
119+
errors.LogInfo(ctx, "tunneling request to ", target, " via ", rec.Destination().NetAddr())
120+
121121
command := protocol.RequestCommandTCP
122122
if target.Network == net.Network_UDP {
123123
command = protocol.RequestCommandUDP

0 commit comments

Comments
 (0)