chore: sync vless encryption code
Some checks are pending
Test / test (1.20, macos-13) (push) Waiting to run
Test / test (1.20, macos-latest) (push) Waiting to run
Test / test (1.20, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.20, ubuntu-latest) (push) Waiting to run
Test / test (1.20, windows-latest) (push) Waiting to run
Test / test (1.21, macos-13) (push) Waiting to run
Test / test (1.21, macos-latest) (push) Waiting to run
Test / test (1.21, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.21, ubuntu-latest) (push) Waiting to run
Test / test (1.21, windows-latest) (push) Waiting to run
Test / test (1.22, macos-13) (push) Waiting to run
Test / test (1.22, macos-latest) (push) Waiting to run
Test / test (1.22, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.22, ubuntu-latest) (push) Waiting to run
Test / test (1.22, windows-latest) (push) Waiting to run
Test / test (1.23, macos-13) (push) Waiting to run
Test / test (1.23, macos-latest) (push) Waiting to run
Test / test (1.23, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.23, ubuntu-latest) (push) Waiting to run
Test / test (1.23, windows-latest) (push) Waiting to run
Test / test (1.24, macos-13) (push) Waiting to run
Test / test (1.24, macos-latest) (push) Waiting to run
Test / test (1.24, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.24, ubuntu-latest) (push) Waiting to run
Test / test (1.24, windows-latest) (push) Waiting to run
Test / test (1.25, macos-13) (push) Waiting to run
Test / test (1.25, macos-latest) (push) Waiting to run
Test / test (1.25, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.25, ubuntu-latest) (push) Waiting to run
Test / test (1.25, windows-latest) (push) Waiting to run
Trigger CMFA Update / trigger-CMFA-update (push) Waiting to run

This commit is contained in:
wwqgtxx 2025-08-18 23:08:30 +08:00
parent ce760fcf19
commit 4e20ed65f2
6 changed files with 114 additions and 48 deletions

View File

@ -116,6 +116,11 @@ func TestInboundVless_Encryption(t *testing.T) {
Encryption: "8min-xored-mlkem768client-" + clientBase64, Encryption: "8min-xored-mlkem768client-" + clientBase64,
} }
testInboundVless(t, inboundOptions, outboundOptions) testInboundVless(t, inboundOptions, outboundOptions)
t.Run("xtls-rprx-vision", func(t *testing.T) {
outboundOptions := outboundOptions
outboundOptions.Flow = "xtls-rprx-vision"
testInboundVless(t, inboundOptions, outboundOptions)
})
}) })
} }

View File

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"crypto/cipher" "crypto/cipher"
"crypto/rand" "crypto/rand"
"crypto/sha256"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -15,6 +14,7 @@ import (
"time" "time"
"github.com/metacubex/utls/mlkem" "github.com/metacubex/utls/mlkem"
"golang.org/x/crypto/sha3"
"golang.org/x/sys/cpu" "golang.org/x/sys/cpu"
) )
@ -69,10 +69,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

@ -5,7 +5,6 @@ import (
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/rand" "crypto/rand"
"crypto/sha256"
"fmt" "fmt"
"io" "io"
"math/big" "math/big"
@ -13,6 +12,7 @@ import (
"golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/hkdf" "golang.org/x/crypto/hkdf"
"golang.org/x/crypto/sha3"
) )
var MaxNonce = bytes.Repeat([]byte{255}, 12) 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) { func NewAead(c byte, secret, salt, info []byte) (aead cipher.AEAD) {
key := make([]byte, 32) 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 { if c&1 == 1 {
block, _ := aes.NewCipher(key) block, _ := aes.NewCipher(key)
aead, _ = cipher.NewGCM(block) aead, _ = cipher.NewGCM(block)

View File

@ -12,4 +12,5 @@
// https://github.com/XTLS/Xray-core/commit/2807ee432a1fbeb301815647189eacd650b12a8b // https://github.com/XTLS/Xray-core/commit/2807ee432a1fbeb301815647189eacd650b12a8b
// https://github.com/XTLS/Xray-core/commit/bfe4820f2f086daf639b1957eb23dc13c843cad1 // https://github.com/XTLS/Xray-core/commit/bfe4820f2f086daf639b1957eb23dc13c843cad1
// https://github.com/XTLS/Xray-core/commit/d1fb48521271251a8c74bd64fcc2fc8700717a3b // https://github.com/XTLS/Xray-core/commit/d1fb48521271251a8c74bd64fcc2fc8700717a3b
// https://github.com/XTLS/Xray-core/commit/49580705f6029648399304b816a2737f991582a8
package encryption package encryption

View File

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"crypto/cipher" "crypto/cipher"
"crypto/rand" "crypto/rand"
"crypto/sha256"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -13,6 +12,7 @@ import (
"time" "time"
"github.com/metacubex/utls/mlkem" "github.com/metacubex/utls/mlkem"
"golang.org/x/crypto/sha3"
) )
type ServerSession struct { type ServerSession struct {
@ -54,10 +54,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 {
@ -279,9 +279,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,29 +34,56 @@ 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
} }
var iv []byte if !c.out_after0 {
if c.ctr == nil { var iv []byte
block, _ := aes.NewCipher(c.key) if c.ctr == nil {
iv = make([]byte, 16) block, _ := aes.NewCipher(c.key)
rand.Read(iv) iv = make([]byte, 16)
c.ctr = cipher.NewCTR(block, iv) 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) for p := b; ; { // for XTLS
if t == 23 { // single 23 if len(p) <= c.out_skip {
l = 5 c.out_skip -= len(p)
} else { // 1/0 + 23, or noises only break
l += 10 }
} p = p[c.out_skip:]
c.ctr.XORKeyStream(b[:l], b[:l]) // caller MUST discard b c.out_skip = 0
if iv != nil { need := 5 - len(c.out_header)
b = append(iv, b...) 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 { if _, err := c.Conn.Write(b); err != nil {
return 0, err return 0, err
} }
if iv != nil {
b = b[16:] // for len(b)
}
return len(b), nil 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 { if len(b) == 0 {
return 0, nil return 0, nil
} }
if c.peerCtr == nil { if !c.in_after0 || !c.isHeader {
peerIv := make([]byte, 16) if c.peerCtr == nil {
if _, err := io.ReadFull(c.Conn, peerIv); err != 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 return 0, err
} }
block, _ := aes.NewCipher(c.key) if c.skipNext {
c.peerCtr = cipher.NewCTR(block, peerIv) c.skipNext = false
c.isHeader = true return len(b), nil
} }
if _, err := io.ReadFull(c.Conn, b); err != nil { c.peerCtr.XORKeyStream(b, b)
return 0, err if c.isHeader { // always 5-bytes
} if t, _, _ := DecodeHeader(b); t == 23 {
if c.skipNext { c.skipNext = true
c.skipNext = false } else {
c.isHeader = false
if t == 0 {
c.in_after0 = true
}
}
} else {
c.isHeader = true
}
return len(b), nil return len(b), nil
} }
c.peerCtr.XORKeyStream(b, b) n, err := c.Conn.Read(b)
if c.isHeader { // always 5-bytes for p := b[:n]; ; { // for XTLS
if t, _, _ := DecodeHeader(b); t == 23 { if len(p) <= c.in_skip {
c.skipNext = true c.in_skip -= len(p)
} else { break
c.isHeader = false
} }
} else { p = p[c.in_skip:]
c.isHeader = true 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
} }