Update On Mon Aug 18 20:42:38 CEST 2025

This commit is contained in:
github-action[bot]
2025-08-18 20:42:38 +02:00
parent 4b335cfee5
commit 1a86afbccb
133 changed files with 3243 additions and 2562 deletions

View File

@@ -179,6 +179,7 @@ jobs:
- name: Revert Golang1.25 commit for Windows7/8
if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '' }}
run: |
alias curl='curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}"'
cd $(go env GOROOT)
curl https://github.com/MetaCubeX/go/commit/8cb5472d94c34b88733a81091bd328e70ee565a4.diff | patch --verbose -p 1
curl https://github.com/MetaCubeX/go/commit/6788c4c6f9fafb56729bad6b660f7ee2272d699f.diff | patch --verbose -p 1
@@ -197,6 +198,7 @@ jobs:
- name: Revert Golang1.24 commit for Windows7/8
if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '1.24' }}
run: |
alias curl='curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}"'
cd $(go env GOROOT)
curl https://github.com/MetaCubeX/go/commit/2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8.diff | patch --verbose -p 1
curl https://github.com/MetaCubeX/go/commit/7b1fd7d39c6be0185fbe1d929578ab372ac5c632.diff | patch --verbose -p 1
@@ -215,6 +217,7 @@ jobs:
- name: Revert Golang1.23 commit for Windows7/8
if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '1.23' }}
run: |
alias curl='curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}"'
cd $(go env GOROOT)
curl https://github.com/MetaCubeX/go/commit/9ac42137ef6730e8b7daca016ece831297a1d75b.diff | patch --verbose -p 1
curl https://github.com/MetaCubeX/go/commit/21290de8a4c91408de7c2b5b68757b1e90af49dd.diff | patch --verbose -p 1
@@ -233,6 +236,7 @@ jobs:
- name: Revert Golang1.22 commit for Windows7/8
if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '1.22' }}
run: |
alias curl='curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}"'
cd $(go env GOROOT)
curl https://github.com/MetaCubeX/go/commit/9779155f18b6556a034f7bb79fb7fb2aad1e26a9.diff | patch --verbose -p 1
curl https://github.com/MetaCubeX/go/commit/ef0606261340e608017860b423ffae5c1ce78239.diff | patch --verbose -p 1
@@ -243,6 +247,7 @@ jobs:
- name: Revert Golang1.21 commit for Windows7/8
if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '1.21' }}
run: |
alias curl='curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}"'
cd $(go env GOROOT)
curl https://github.com/golang/go/commit/9e43850a3298a9b8b1162ba0033d4c53f8637571.diff | patch --verbose -R -p 1

View File

@@ -60,6 +60,7 @@ jobs:
- name: Revert Golang1.25 commit for Windows7/8
if: ${{ matrix.jobs.goos == 'windows' && matrix.jobs.goversion == '1.25' }}
run: |
alias curl='curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}"'
cd $(go env GOROOT)
curl https://github.com/MetaCubeX/go/commit/8cb5472d94c34b88733a81091bd328e70ee565a4.diff | patch --verbose -p 1
curl https://github.com/MetaCubeX/go/commit/6788c4c6f9fafb56729bad6b660f7ee2272d699f.diff | patch --verbose -p 1
@@ -78,6 +79,7 @@ jobs:
- name: Revert Golang1.24 commit for Windows7/8
if: ${{ runner.os == 'Windows' && matrix.go-version == '1.24' }}
run: |
alias curl='curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}"'
cd $(go env GOROOT)
curl https://github.com/MetaCubeX/go/commit/2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8.diff | patch --verbose -p 1
curl https://github.com/MetaCubeX/go/commit/7b1fd7d39c6be0185fbe1d929578ab372ac5c632.diff | patch --verbose -p 1
@@ -96,6 +98,7 @@ jobs:
- name: Revert Golang1.23 commit for Windows7/8
if: ${{ runner.os == 'Windows' && matrix.go-version == '1.23' }}
run: |
alias curl='curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}"'
cd $(go env GOROOT)
curl https://github.com/MetaCubeX/go/commit/9ac42137ef6730e8b7daca016ece831297a1d75b.diff | patch --verbose -p 1
curl https://github.com/MetaCubeX/go/commit/21290de8a4c91408de7c2b5b68757b1e90af49dd.diff | patch --verbose -p 1
@@ -114,6 +117,7 @@ jobs:
- name: Revert Golang1.22 commit for Windows7/8
if: ${{ runner.os == 'Windows' && matrix.go-version == '1.22' }}
run: |
alias curl='curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}"'
cd $(go env GOROOT)
curl https://github.com/MetaCubeX/go/commit/9779155f18b6556a034f7bb79fb7fb2aad1e26a9.diff | patch --verbose -p 1
curl https://github.com/MetaCubeX/go/commit/ef0606261340e608017860b423ffae5c1ce78239.diff | patch --verbose -p 1
@@ -124,6 +128,7 @@ jobs:
- name: Revert Golang1.21 commit for Windows7/8
if: ${{ runner.os == 'Windows' && matrix.go-version == '1.21' }}
run: |
alias curl='curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}"'
cd $(go env GOROOT)
curl https://github.com/golang/go/commit/9e43850a3298a9b8b1162ba0033d4c53f8637571.diff | patch --verbose -R -p 1

View File

@@ -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)
})
})
}

View File

@@ -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"
)
@@ -39,6 +39,7 @@ func init() {
type ClientInstance struct {
sync.RWMutex
nfsEKey *mlkem.EncapsulationKey768
hash11 [11]byte // no more capacity
xorKey []byte
minutes time.Duration
expire time.Time
@@ -56,7 +57,7 @@ type ClientConn struct {
nonce []byte
peerAead cipher.AEAD
peerNonce []byte
peerCache []byte
input bytes.Reader // peerCache
}
func (i *ClientInstance) Init(nfsEKeyBytes []byte, xor uint32, minutes time.Duration) (err error) {
@@ -68,8 +69,10 @@ func (i *ClientInstance) Init(nfsEKeyBytes []byte, xor uint32, minutes time.Dura
if err != nil {
return
}
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
@@ -104,13 +107,14 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) {
nfsKey, encapsulatedNfsKey := i.nfsEKey.Encapsulate()
paddingLen := randBetween(100, 1000)
clientHello := make([]byte, 5+1+1184+1088+5+paddingLen)
EncodeHeader(clientHello, 1, 1+1184+1088)
clientHello[5] = ClientCipher
copy(clientHello[5+1:], pfsEKeyBytes)
copy(clientHello[5+1+1184:], encapsulatedNfsKey)
EncodeHeader(clientHello[5+1+1184+1088:], 23, int(paddingLen))
rand.Read(clientHello[5+1+1184+1088+5:])
clientHello := make([]byte, 5+11+1+1184+1088+5+paddingLen)
EncodeHeader(clientHello, 1, 11+1+1184+1088)
copy(clientHello[5:], i.hash11[:])
clientHello[5+11] = ClientCipher
copy(clientHello[5+11+1:], pfsEKeyBytes)
copy(clientHello[5+11+1+1184:], encapsulatedNfsKey)
EncodeHeader(clientHello[5+11+1+1184+1088:], 23, int(paddingLen))
rand.Read(clientHello[5+11+1+1184+1088+5:])
if _, err := c.Conn.Write(clientHello); err != nil {
return nil, err
@@ -123,17 +127,17 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) {
}
if t != 1 {
return nil, fmt.Errorf("unexpected type %v, expect random hello", t)
return nil, fmt.Errorf("unexpected type %v, expect server hello", t)
}
peerRandomHello := make([]byte, 1088+21)
if l != len(peerRandomHello) {
return nil, fmt.Errorf("unexpected length %v for random hello", l)
peerServerHello := make([]byte, 1088+21)
if l != len(peerServerHello) {
return nil, fmt.Errorf("unexpected length %v for server hello", l)
}
if _, err := io.ReadFull(c.Conn, peerRandomHello); err != nil {
if _, err := io.ReadFull(c.Conn, peerServerHello); err != nil {
return nil, err
}
encapsulatedPfsKey := peerRandomHello[:1088]
c.ticket = peerRandomHello[1088:]
encapsulatedPfsKey := peerServerHello[:1088]
c.ticket = append(i.hash11[:], peerServerHello[1088:]...)
pfsKey, err := pfsDKey.Decapsulate(encapsulatedPfsKey)
if err != nil {
@@ -141,8 +145,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) {
}
c.baseKey = append(pfsKey, nfsKey...)
nonce := [12]byte{ClientCipher}
VLESS, _ := NewAead(ClientCipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Open(nil, nonce[:], c.ticket, pfsEKeyBytes)
VLESS, _ := NewAead(ClientCipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Open(nil, append(i.hash11[:], ClientCipher), c.ticket[11:], pfsEKeyBytes)
if !bytes.Equal(VLESS, []byte("VLESS")) {
return nil, errors.New("invalid server")
}
@@ -170,16 +173,16 @@ func (c *ClientConn) Write(b []byte) (int, error) {
}
n += len(b)
if c.aead == nil {
data = make([]byte, 5+32+32+5+len(b)+16)
EncodeHeader(data, 0, 32+32)
copy(data[5:], c.ticket)
c.random = make([]byte, 32)
rand.Read(c.random)
copy(data[5+32:], c.random)
EncodeHeader(data[5+32+32:], 23, len(b)+16)
c.aead = NewAead(ClientCipher, c.baseKey, c.random, c.ticket)
c.nonce = make([]byte, 12)
data = make([]byte, 5+21+32+5+len(b)+16)
EncodeHeader(data, 0, 21+32)
copy(data[5:], c.ticket)
copy(data[5+21:], c.random)
EncodeHeader(data[5+21+32:], 23, len(b)+16)
c.aead.Seal(data[:5+21+32+5], c.nonce, b, data[5+21+32:5+21+32+5])
c.aead.Seal(data[:5+32+32+5], c.nonce, b, data[5+32+32:5+32+32+5])
} else {
data = make([]byte, 5+len(b)+16)
EncodeHeader(data, 23, len(b)+16)
@@ -216,23 +219,21 @@ func (c *ClientConn) Read(b []byte) (int, error) {
if t != 0 {
return 0, fmt.Errorf("unexpected type %v, expect server random", t)
}
peerRandom := make([]byte, 32)
if l != len(peerRandom) {
peerRandomHello := make([]byte, 32)
if l != len(peerRandomHello) {
return 0, fmt.Errorf("unexpected length %v for server random", l)
}
if _, err := io.ReadFull(c.Conn, peerRandom); err != nil {
if _, err := io.ReadFull(c.Conn, peerRandomHello); err != nil {
return 0, err
}
if c.random == nil {
return 0, errors.New("empty c.random")
}
c.peerAead = NewAead(ClientCipher, c.baseKey, peerRandom, c.random)
c.peerAead = NewAead(ClientCipher, c.baseKey, peerRandomHello, c.random)
c.peerNonce = make([]byte, 12)
}
if len(c.peerCache) != 0 {
n := copy(b, c.peerCache)
c.peerCache = c.peerCache[n:]
return n, nil
if c.input.Len() > 0 {
return c.input.Read(b)
}
h, t, l, err := ReadAndDecodeHeader(c.Conn) // l: 17~17000
if err != nil {
@@ -262,7 +263,7 @@ func (c *ClientConn) Read(b []byte) (int, error) {
return 0, err
}
if len(dst) > len(b) {
c.peerCache = dst[copy(b, dst):]
c.input.Reset(dst[copy(b, dst):])
dst = b // for len(dst)
}
return len(dst), nil

View File

@@ -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)

View File

@@ -11,4 +11,6 @@
// https://github.com/XTLS/Xray-core/commit/09cc92c61d9067e0d65c1cae9124664ecfc78f43
// 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

View File

@@ -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 {
@@ -25,9 +25,10 @@ type ServerSession struct {
type ServerInstance struct {
sync.RWMutex
nfsDKey *mlkem.DecapsulationKey768
hash11 [11]byte // no more capacity
xorKey []byte
minutes time.Duration
sessions map[[21]byte]*ServerSession
sessions map[[32]byte]*ServerSession
closed bool
}
@@ -39,7 +40,7 @@ type ServerConn struct {
peerRandom []byte
peerAead cipher.AEAD
peerNonce []byte
peerCache []byte
input bytes.Reader // peerCache
aead cipher.AEAD
nonce []byte
}
@@ -53,13 +54,15 @@ func (i *ServerInstance) Init(nfsDKeySeed []byte, xor uint32, minutes time.Durat
if err != nil {
return
}
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 {
i.minutes = minutes
i.sessions = make(map[[21]byte]*ServerSession)
i.sessions = make(map[[32]byte]*ServerSession)
go func() {
for {
time.Sleep(time.Minute)
@@ -106,15 +109,18 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) {
if i.minutes == 0 {
return nil, errors.New("0-RTT is not allowed")
}
peerTicketHello := make([]byte, 21+32)
peerTicketHello := make([]byte, 32+32)
if l != len(peerTicketHello) {
return nil, fmt.Errorf("unexpected length %v for ticket hello", l)
}
if _, err := io.ReadFull(c.Conn, peerTicketHello); err != nil {
return nil, err
}
if !bytes.Equal(peerTicketHello[:11], i.hash11[:]) {
return nil, fmt.Errorf("unexpected hash11: %v", peerTicketHello[:11])
}
i.RLock()
s := i.sessions[[21]byte(peerTicketHello)]
s := i.sessions[[32]byte(peerTicketHello)]
i.RUnlock()
if s == nil {
noises := make([]byte, randBetween(100, 1000))
@@ -126,26 +132,29 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) {
c.Conn.Write(noises) // make client do new handshake
return nil, errors.New("expired ticket")
}
if _, replay := s.randoms.LoadOrStore([32]byte(peerTicketHello[21:]), true); replay {
if _, replay := s.randoms.LoadOrStore([32]byte(peerTicketHello[32:]), true); replay {
return nil, errors.New("replay detected")
}
c.cipher = s.cipher
c.baseKey = s.baseKey
c.ticket = peerTicketHello[:21]
c.peerRandom = peerTicketHello[21:]
c.ticket = peerTicketHello[:32]
c.peerRandom = peerTicketHello[32:]
return c, nil
}
peerClientHello := make([]byte, 1+1184+1088)
peerClientHello := make([]byte, 11+1+1184+1088)
if l != len(peerClientHello) {
return nil, fmt.Errorf("unexpected length %v for client hello", l)
}
if _, err := io.ReadFull(c.Conn, peerClientHello); err != nil {
return nil, err
}
c.cipher = peerClientHello[0]
pfsEKeyBytes := peerClientHello[1:1185]
encapsulatedNfsKey := peerClientHello[1185:2273]
if !bytes.Equal(peerClientHello[:11], i.hash11[:]) {
return nil, fmt.Errorf("unexpected hash11: %v", peerClientHello[:11])
}
c.cipher = peerClientHello[11]
pfsEKeyBytes := peerClientHello[11+1 : 11+1+1184]
encapsulatedNfsKey := peerClientHello[11+1+1184:]
pfsEKey, err := mlkem.NewEncapsulationKey768(pfsEKeyBytes)
if err != nil {
@@ -158,15 +167,14 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) {
pfsKey, encapsulatedPfsKey := pfsEKey.Encapsulate()
c.baseKey = append(pfsKey, nfsKey...)
nonce := [12]byte{c.cipher}
c.ticket = NewAead(c.cipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Seal(nil, nonce[:], []byte("VLESS"), pfsEKeyBytes)
c.ticket = append(i.hash11[:], NewAead(c.cipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Seal(nil, peerClientHello[:12], []byte("VLESS"), pfsEKeyBytes)...)
paddingLen := randBetween(100, 1000)
serverHello := make([]byte, 5+1088+21+5+paddingLen)
EncodeHeader(serverHello, 1, 1088+21)
copy(serverHello[5:], encapsulatedPfsKey)
copy(serverHello[5+1088:], c.ticket)
copy(serverHello[5+1088:], c.ticket[11:])
EncodeHeader(serverHello[5+1088+21:], 23, int(paddingLen))
rand.Read(serverHello[5+1088+21+5:])
@@ -177,7 +185,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) {
if i.minutes > 0 {
i.Lock()
i.sessions[[21]byte(c.ticket)] = &ServerSession{
i.sessions[[32]byte(c.ticket)] = &ServerSession{
expire: time.Now().Add(i.minutes),
cipher: c.cipher,
baseKey: c.baseKey,
@@ -201,25 +209,23 @@ func (c *ServerConn) Read(b []byte) (int, error) {
if t != 0 {
return 0, fmt.Errorf("unexpected type %v, expect ticket hello", t)
}
peerTicketHello := make([]byte, 21+32)
peerTicketHello := make([]byte, 32+32)
if l != len(peerTicketHello) {
return 0, fmt.Errorf("unexpected length %v for ticket hello", l)
}
if _, err := io.ReadFull(c.Conn, peerTicketHello); err != nil {
return 0, err
}
if !bytes.Equal(peerTicketHello[:21], c.ticket) {
if !bytes.Equal(peerTicketHello[:32], c.ticket) {
return 0, errors.New("naughty boy")
}
c.peerRandom = peerTicketHello[21:]
c.peerRandom = peerTicketHello[32:]
}
c.peerAead = NewAead(c.cipher, c.baseKey, c.peerRandom, c.ticket)
c.peerNonce = make([]byte, 12)
}
if len(c.peerCache) != 0 {
n := copy(b, c.peerCache)
c.peerCache = c.peerCache[n:]
return n, nil
if c.input.Len() > 0 {
return c.input.Read(b)
}
h, t, l, err := ReadAndDecodeHeader(c.Conn) // l: 17~17000
if err != nil {
@@ -249,7 +255,7 @@ func (c *ServerConn) Read(b []byte) (int, error) {
return 0, err
}
if len(dst) > len(b) {
c.peerCache = dst[copy(b, dst):]
c.input.Reset(dst[copy(b, dst):])
dst = b // for len(dst)
}
return len(dst), nil
@@ -273,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)

View File

@@ -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
}