mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2025-12-19 16:30:07 +08:00
chore: sync vless encryption code
This commit is contained in:
parent
d094075c88
commit
c98f5f44b7
@ -641,8 +641,14 @@ proxies: # socks5
|
|||||||
# vless encryption客户端配置:
|
# vless encryption客户端配置:
|
||||||
# (native/xorpub 的 XTLS Vision 可以 Splice。只使用 1-RTT 模式 / 若服务端发的 ticket 中秒数不为零则 0-RTT 复用)
|
# (native/xorpub 的 XTLS Vision 可以 Splice。只使用 1-RTT 模式 / 若服务端发的 ticket 中秒数不为零则 0-RTT 复用)
|
||||||
# / 是只能选一个,后面 base64 至少一个,无限串联,使用 mihomo generate vless-x25519 和 mihomo generate vless-mlkem768 生成,替换值时需去掉括号
|
# / 是只能选一个,后面 base64 至少一个,无限串联,使用 mihomo generate vless-x25519 和 mihomo generate vless-mlkem768 生成,替换值时需去掉括号
|
||||||
|
#
|
||||||
|
# Padding 是可选的参数,仅作用于 1-RTT 以消除握手的长度特征,双端默认值均为 "111-1111.111--66.3333--1234",它的含义是:
|
||||||
|
# 在 1-RTT client/server hello 后粘上随机 111 到 1111 字节的 padding
|
||||||
|
# 等待随机 111 到负 66 毫秒,若随机到了负值则不等待
|
||||||
|
# 再次发送随机 3333 到负 1234 字节的 padding,若随机到了负值则不发送
|
||||||
|
# 服务端、客户端可以设置不同的 padding 参数,正数写在左边,按 len、gap 的顺序无限串联,第一个 padding 需大于 16 字节
|
||||||
# -------------------------
|
# -------------------------
|
||||||
encryption: "mlkem768x25519plus.native/xorpub/random.1rtt/0rtt.(X25519 Password).(ML-KEM-768 Client)..."
|
encryption: "mlkem768x25519plus.native/xorpub/random.1rtt/0rtt.(padding len).(padding gap).(X25519 Password).(ML-KEM-768 Client)..."
|
||||||
tls: false #可以不开启tls
|
tls: false #可以不开启tls
|
||||||
udp: true
|
udp: true
|
||||||
|
|
||||||
@ -1366,8 +1372,14 @@ listeners:
|
|||||||
# vless encryption服务端配置:
|
# vless encryption服务端配置:
|
||||||
# (原生外观 / 只 XOR 公钥 / 全随机数。只允许 1-RTT 模式 / 同时允许 1-RTT 模式与 600 秒复用的 0-RTT 模式)
|
# (原生外观 / 只 XOR 公钥 / 全随机数。只允许 1-RTT 模式 / 同时允许 1-RTT 模式与 600 秒复用的 0-RTT 模式)
|
||||||
# / 是只能选一个,后面 base64 至少一个,无限串联,使用 mihomo generate vless-x25519 和 mihomo generate vless-mlkem768 生成,替换值时需去掉括号
|
# / 是只能选一个,后面 base64 至少一个,无限串联,使用 mihomo generate vless-x25519 和 mihomo generate vless-mlkem768 生成,替换值时需去掉括号
|
||||||
|
#
|
||||||
|
# Padding 是可选的参数,仅作用于 1-RTT 以消除握手的长度特征,双端默认值均为 "111-1111.111--66.3333--1234",它的含义是:
|
||||||
|
# 在 1-RTT client/server hello 后粘上随机 111 到 1111 字节的 padding
|
||||||
|
# 等待随机 111 到负 66 毫秒,若随机到了负值则不等待
|
||||||
|
# 再次发送随机 3333 到负 1234 字节的 padding,若随机到了负值则不发送
|
||||||
|
# 服务端、客户端可以设置不同的 padding 参数,正数写在左边,按 len、gap 的顺序无限串联,第一个 padding 需大于 16 字节
|
||||||
# -------------------------
|
# -------------------------
|
||||||
# decryption: "mlkem768x25519plus.native/xorpub/random.1rtt/600s.(X25519 PrivateKey).(ML-KEM-768 Seed)..."
|
# decryption: "mlkem768x25519plus.native/xorpub/random.1rtt/600s.(padding len).(padding gap).(X25519 PrivateKey).(ML-KEM-768 Seed)..."
|
||||||
# 下面两项如果填写则开启 tls(需要同时填写)
|
# 下面两项如果填写则开启 tls(需要同时填写)
|
||||||
# certificate: ./server.crt
|
# certificate: ./server.crt
|
||||||
# private-key: ./server.key
|
# private-key: ./server.key
|
||||||
|
|||||||
@ -99,6 +99,15 @@ func TestInboundVless_Encryption(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
paddings := []struct {
|
||||||
|
name string
|
||||||
|
data string
|
||||||
|
}{
|
||||||
|
{"unconfigured-padding", ""},
|
||||||
|
{"default-padding", "111-1111.111--66.3333--1234."},
|
||||||
|
{"old-padding", "100-1000."}, // Xray-core v25.8.29
|
||||||
|
{"custom-padding", "7890-1234.1111--999.6666--3333.777--777."},
|
||||||
|
}
|
||||||
var modes = []string{
|
var modes = []string{
|
||||||
"native",
|
"native",
|
||||||
"xorpub",
|
"xorpub",
|
||||||
@ -107,11 +116,15 @@ func TestInboundVless_Encryption(t *testing.T) {
|
|||||||
for i := range modes {
|
for i := range modes {
|
||||||
mode := modes[i]
|
mode := modes[i]
|
||||||
t.Run(mode, func(t *testing.T) {
|
t.Run(mode, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
for i := range paddings {
|
||||||
|
padding := paddings[i].data
|
||||||
|
t.Run(paddings[i].name, func(t *testing.T) {
|
||||||
inboundOptions := inbound.VlessOption{
|
inboundOptions := inbound.VlessOption{
|
||||||
Decryption: "mlkem768x25519plus." + mode + ".600s." + privateKeyBase64 + "." + seedBase64,
|
Decryption: "mlkem768x25519plus." + mode + ".600s." + padding + privateKeyBase64 + "." + seedBase64,
|
||||||
}
|
}
|
||||||
outboundOptions := outbound.VlessOption{
|
outboundOptions := outbound.VlessOption{
|
||||||
Encryption: "mlkem768x25519plus." + mode + ".0rtt." + passwordBase64 + "." + clientBase64,
|
Encryption: "mlkem768x25519plus." + mode + ".0rtt." + padding + passwordBase64 + "." + clientBase64,
|
||||||
}
|
}
|
||||||
testInboundVless(t, inboundOptions, outboundOptions)
|
testInboundVless(t, inboundOptions, outboundOptions)
|
||||||
t.Run("xtls-rprx-vision", func(t *testing.T) {
|
t.Run("xtls-rprx-vision", func(t *testing.T) {
|
||||||
@ -121,6 +134,9 @@ func TestInboundVless_Encryption(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInboundVless_Wss1(t *testing.T) {
|
func TestInboundVless_Wss1(t *testing.T) {
|
||||||
|
|||||||
@ -230,7 +230,7 @@ func (l *Listener) HandleConn(conn net.Conn, tunnel C.Tunnel, additions ...inbou
|
|||||||
ctx := sing.WithAdditions(context.TODO(), additions...)
|
ctx := sing.WithAdditions(context.TODO(), additions...)
|
||||||
if l.decryption != nil {
|
if l.decryption != nil {
|
||||||
var err error
|
var err error
|
||||||
conn, err = l.decryption.Handshake(conn)
|
conn, err = l.decryption.Handshake(conn, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,6 +33,8 @@ type ClientInstance struct {
|
|||||||
RelaysLength int
|
RelaysLength int
|
||||||
XorMode uint32
|
XorMode uint32
|
||||||
Seconds uint32
|
Seconds uint32
|
||||||
|
PaddingLens [][2]int
|
||||||
|
PaddingGaps [][2]int
|
||||||
|
|
||||||
RWLock sync.RWMutex
|
RWLock sync.RWMutex
|
||||||
Expire time.Time
|
Expire time.Time
|
||||||
@ -40,15 +42,13 @@ type ClientInstance struct {
|
|||||||
Ticket []byte
|
Ticket []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ClientInstance) Init(nfsPKeysBytes [][]byte, xorMode, seconds uint32) (err error) {
|
func (i *ClientInstance) Init(nfsPKeysBytes [][]byte, xorMode, seconds uint32, padding string) (err error) {
|
||||||
if i.NfsPKeys != nil {
|
if i.NfsPKeys != nil {
|
||||||
err = errors.New("already initialized")
|
return errors.New("already initialized")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
l := len(nfsPKeysBytes)
|
l := len(nfsPKeysBytes)
|
||||||
if l == 0 {
|
if l == 0 {
|
||||||
err = errors.New("empty nfsPKeysBytes")
|
return errors.New("empty nfsPKeysBytes")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
i.NfsPKeys = make([]any, l)
|
i.NfsPKeys = make([]any, l)
|
||||||
i.NfsPKeysBytes = nfsPKeysBytes
|
i.NfsPKeysBytes = nfsPKeysBytes
|
||||||
@ -70,7 +70,7 @@ func (i *ClientInstance) Init(nfsPKeysBytes [][]byte, xorMode, seconds uint32) (
|
|||||||
i.RelaysLength -= 32
|
i.RelaysLength -= 32
|
||||||
i.XorMode = xorMode
|
i.XorMode = xorMode
|
||||||
i.Seconds = seconds
|
i.Seconds = seconds
|
||||||
return
|
return ParsePadding(padding, &i.PaddingLens, &i.PaddingGaps)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||||
@ -81,7 +81,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
|||||||
|
|
||||||
ivAndRealysLength := 16 + i.RelaysLength
|
ivAndRealysLength := 16 + i.RelaysLength
|
||||||
pfsKeyExchangeLength := 18 + 1184 + 32 + 16
|
pfsKeyExchangeLength := 18 + 1184 + 32 + 16
|
||||||
paddingLength := int(randBetween(100, 1000))
|
paddingLength, paddingLens, paddingGaps := CreatPadding(i.PaddingLens, i.PaddingGaps)
|
||||||
clientHello := make([]byte, ivAndRealysLength+pfsKeyExchangeLength+paddingLength)
|
clientHello := make([]byte, ivAndRealysLength+pfsKeyExchangeLength+paddingLength)
|
||||||
|
|
||||||
iv := clientHello[:16]
|
iv := clientHello[:16]
|
||||||
@ -150,10 +150,18 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
|||||||
nfsAEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
|
nfsAEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
|
||||||
nfsAEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
|
nfsAEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
|
||||||
|
|
||||||
if _, err := conn.Write(clientHello); err != nil {
|
paddingLens[0] = ivAndRealysLength + pfsKeyExchangeLength + paddingLens[0]
|
||||||
|
for i, l := range paddingLens { // sends padding in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
|
||||||
|
if l > 0 {
|
||||||
|
if _, err := conn.Write(clientHello[:l]); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// padding can be sent in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
|
clientHello = clientHello[l:]
|
||||||
|
}
|
||||||
|
if len(paddingGaps) > i {
|
||||||
|
time.Sleep(paddingGaps[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
encryptedPfsPublicKey := make([]byte, 1088+32+16)
|
encryptedPfsPublicKey := make([]byte, 1088+32+16)
|
||||||
if _, err := io.ReadFull(conn, encryptedPfsPublicKey); err != nil {
|
if _, err := io.ReadFull(conn, encryptedPfsPublicKey); err != nil {
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/metacubex/mihomo/common/pool"
|
"github.com/metacubex/mihomo/common/pool"
|
||||||
@ -96,11 +98,11 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
|||||||
if c.input.Len() > 0 {
|
if c.input.Len() > 0 {
|
||||||
return c.input.Read(b)
|
return c.input.Read(b)
|
||||||
}
|
}
|
||||||
peerHeader := make([]byte, 5)
|
peerHeader := [5]byte{}
|
||||||
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
if _, err := io.ReadFull(c.Conn, peerHeader[:]); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
l, err := DecodeHeader(peerHeader) // l: 17~17000
|
l, err := DecodeHeader(peerHeader[:]) // l: 17~17000
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if c.Client != nil && errors.Is(err, ErrInvalidHeader) { // client's 0-RTT
|
if c.Client != nil && errors.Is(err, ErrInvalidHeader) { // client's 0-RTT
|
||||||
c.Client.RWLock.Lock()
|
c.Client.RWLock.Lock()
|
||||||
@ -113,7 +115,9 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
c.Client = nil
|
c.Client = nil
|
||||||
c.rawInput.Grow(l)
|
if c.rawInput.Cap() < l {
|
||||||
|
c.rawInput.Grow(l) // no need to use sync.Pool, because we are always reading
|
||||||
|
}
|
||||||
peerData := c.rawInput.Bytes()[:l]
|
peerData := c.rawInput.Bytes()[:l]
|
||||||
if _, err := io.ReadFull(c.Conn, peerData); err != nil {
|
if _, err := io.ReadFull(c.Conn, peerData); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@ -124,9 +128,9 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
var newAEAD *AEAD
|
var newAEAD *AEAD
|
||||||
if bytes.Equal(c.PeerAEAD.Nonce[:], MaxNonce) {
|
if bytes.Equal(c.PeerAEAD.Nonce[:], MaxNonce) {
|
||||||
newAEAD = NewAEAD(append(peerHeader, peerData...), c.UnitedKey, c.UseAES)
|
newAEAD = NewAEAD(append(peerHeader[:], peerData...), c.UnitedKey, c.UseAES)
|
||||||
}
|
}
|
||||||
_, err = c.PeerAEAD.Open(dst[:0], nil, peerData, peerHeader)
|
_, err = c.PeerAEAD.Open(dst[:0], nil, peerData, peerHeader[:])
|
||||||
if newAEAD != nil {
|
if newAEAD != nil {
|
||||||
c.PeerAEAD = newAEAD
|
c.PeerAEAD = newAEAD
|
||||||
}
|
}
|
||||||
@ -213,9 +217,69 @@ func DecodeHeader(h []byte) (l int, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ParsePadding(padding string, paddingLens, paddingGaps *[][2]int) (err error) {
|
||||||
|
if padding == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
maxLen := 0
|
||||||
|
for i, s := range strings.Split(padding, ".") {
|
||||||
|
x := strings.SplitN(s, "-", 2)
|
||||||
|
if len(x) != 2 || x[0] == "" || x[1] == "" {
|
||||||
|
return errors.New("invalid padding lenth/gap parameter: " + s)
|
||||||
|
}
|
||||||
|
y := [2]int{}
|
||||||
|
if y[0], err = strconv.Atoi(x[0]); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if y[1], err = strconv.Atoi(x[1]); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if i == 0 && (y[0] < 17 || y[1] < 17) {
|
||||||
|
return errors.New("first padding length must be larger than 16")
|
||||||
|
}
|
||||||
|
if i%2 == 0 {
|
||||||
|
*paddingLens = append(*paddingLens, y)
|
||||||
|
maxLen += max(y[0], y[1])
|
||||||
|
} else {
|
||||||
|
*paddingGaps = append(*paddingGaps, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if maxLen > 65535 {
|
||||||
|
return errors.New("total padding length must be smaller than 65536")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreatPadding(paddingLens, paddingGaps [][2]int) (length int, lens []int, gaps []time.Duration) {
|
||||||
|
if len(paddingLens) == 0 {
|
||||||
|
paddingLens = [][2]int{{111, 1111}, {3333, -1234}}
|
||||||
|
paddingGaps = [][2]int{{111, -66}}
|
||||||
|
}
|
||||||
|
for _, l := range paddingLens {
|
||||||
|
lens = append(lens, int(max(0, randBetween(int64(l[0]), int64(l[1])))))
|
||||||
|
length += lens[len(lens)-1]
|
||||||
|
}
|
||||||
|
for _, g := range paddingGaps {
|
||||||
|
gaps = append(gaps, time.Duration(max(0, randBetween(int64(g[0]), int64(g[1]))))*time.Millisecond)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func max[T ~int | ~uint | ~int64 | ~uint64 | ~int32 | ~uint32 | ~int16 | ~uint16 | ~int8 | ~uint8](a, b T) T {
|
||||||
|
if a > b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
func randBetween(from int64, to int64) int64 {
|
func randBetween(from int64, to int64) int64 {
|
||||||
if from == to {
|
if from == to {
|
||||||
return from
|
return from
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if to < from {
|
||||||
|
from, to = to, from
|
||||||
|
}
|
||||||
|
|
||||||
return from + randv2.Int64N(to-from)
|
return from + randv2.Int64N(to-from)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,4 +22,5 @@
|
|||||||
// https://github.com/XTLS/Xray-core/commit/fce1195b60f48ca18a953dbd5c7d991869de9a5e
|
// https://github.com/XTLS/Xray-core/commit/fce1195b60f48ca18a953dbd5c7d991869de9a5e
|
||||||
// https://github.com/XTLS/Xray-core/commit/b0b220985c9c1bc832665458d5fd6e0c287b67ae
|
// https://github.com/XTLS/Xray-core/commit/b0b220985c9c1bc832665458d5fd6e0c287b67ae
|
||||||
// https://github.com/XTLS/Xray-core/commit/82ea7a3cc5ff23280b87e3052f0f83b04f0267fa
|
// https://github.com/XTLS/Xray-core/commit/82ea7a3cc5ff23280b87e3052f0f83b04f0267fa
|
||||||
|
// https://github.com/XTLS/Xray-core/commit/e8b02cd6649f14889841e8ab8ee6b2acca71dbe6
|
||||||
package encryption
|
package encryption
|
||||||
|
|||||||
@ -34,7 +34,12 @@ func NewClient(encryption string) (*ClientInstance, error) {
|
|||||||
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
|
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
|
||||||
}
|
}
|
||||||
var nfsPKeysBytes [][]byte
|
var nfsPKeysBytes [][]byte
|
||||||
|
var paddings []string
|
||||||
for _, r := range s[3:] {
|
for _, r := range s[3:] {
|
||||||
|
if len(r) < 20 {
|
||||||
|
paddings = append(paddings, r)
|
||||||
|
continue
|
||||||
|
}
|
||||||
b, err := base64.RawURLEncoding.DecodeString(r)
|
b, err := base64.RawURLEncoding.DecodeString(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
|
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
|
||||||
@ -44,8 +49,9 @@ func NewClient(encryption string) (*ClientInstance, error) {
|
|||||||
}
|
}
|
||||||
nfsPKeysBytes = append(nfsPKeysBytes, b)
|
nfsPKeysBytes = append(nfsPKeysBytes, b)
|
||||||
}
|
}
|
||||||
|
padding := strings.Join(paddings, ".")
|
||||||
client := &ClientInstance{}
|
client := &ClientInstance{}
|
||||||
if err := client.Init(nfsPKeysBytes, xorMode, seconds); err != nil {
|
if err := client.Init(nfsPKeysBytes, xorMode, seconds, padding); err != nil {
|
||||||
return nil, fmt.Errorf("failed to use encryption: %w", err)
|
return nil, fmt.Errorf("failed to use encryption: %w", err)
|
||||||
}
|
}
|
||||||
return client, nil
|
return client, nil
|
||||||
@ -84,7 +90,12 @@ func NewServer(decryption string) (*ServerInstance, error) {
|
|||||||
seconds = uint32(i)
|
seconds = uint32(i)
|
||||||
}
|
}
|
||||||
var nfsSKeysBytes [][]byte
|
var nfsSKeysBytes [][]byte
|
||||||
|
var paddings []string
|
||||||
for _, r := range s[3:] {
|
for _, r := range s[3:] {
|
||||||
|
if len(r) < 20 {
|
||||||
|
paddings = append(paddings, r)
|
||||||
|
continue
|
||||||
|
}
|
||||||
b, err := base64.RawURLEncoding.DecodeString(r)
|
b, err := base64.RawURLEncoding.DecodeString(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
|
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
|
||||||
@ -94,8 +105,9 @@ func NewServer(decryption string) (*ServerInstance, error) {
|
|||||||
}
|
}
|
||||||
nfsSKeysBytes = append(nfsSKeysBytes, b)
|
nfsSKeysBytes = append(nfsSKeysBytes, b)
|
||||||
}
|
}
|
||||||
|
padding := strings.Join(paddings, ".")
|
||||||
server := &ServerInstance{}
|
server := &ServerInstance{}
|
||||||
if err := server.Init(nfsSKeysBytes, xorMode, seconds); err != nil {
|
if err := server.Init(nfsSKeysBytes, xorMode, seconds, padding); err != nil {
|
||||||
return nil, fmt.Errorf("failed to use decryption: %w", err)
|
return nil, fmt.Errorf("failed to use decryption: %w", err)
|
||||||
}
|
}
|
||||||
return server, nil
|
return server, nil
|
||||||
|
|||||||
@ -29,21 +29,21 @@ type ServerInstance struct {
|
|||||||
RelaysLength int
|
RelaysLength int
|
||||||
XorMode uint32
|
XorMode uint32
|
||||||
Seconds uint32
|
Seconds uint32
|
||||||
|
PaddingLens [][2]int
|
||||||
|
PaddingGaps [][2]int
|
||||||
|
|
||||||
RWLock sync.RWMutex
|
RWLock sync.RWMutex
|
||||||
Sessions map[[16]byte]*ServerSession
|
Sessions map[[16]byte]*ServerSession
|
||||||
Closed bool
|
Closed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, seconds uint32) (err error) {
|
func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, seconds uint32, padding string) (err error) {
|
||||||
if i.NfsSKeys != nil {
|
if i.NfsSKeys != nil {
|
||||||
err = errors.New("already initialized")
|
return errors.New("already initialized")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
l := len(nfsSKeysBytes)
|
l := len(nfsSKeysBytes)
|
||||||
if l == 0 {
|
if l == 0 {
|
||||||
err = errors.New("empty nfsSKeysBytes")
|
return errors.New("empty nfsSKeysBytes")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
i.NfsSKeys = make([]any, l)
|
i.NfsSKeys = make([]any, l)
|
||||||
i.NfsPKeysBytes = make([][]byte, l)
|
i.NfsPKeysBytes = make([][]byte, l)
|
||||||
@ -87,7 +87,7 @@ func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, seconds uint32) (
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
return
|
return ParsePadding(padding, &i.PaddingLens, &i.PaddingGaps)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ServerInstance) Close() (err error) {
|
func (i *ServerInstance) Close() (err error) {
|
||||||
@ -97,7 +97,7 @@ func (i *ServerInstance) Close() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn, error) {
|
||||||
if i.NfsSKeys == nil {
|
if i.NfsSKeys == nil {
|
||||||
return nil, errors.New("uninitialized")
|
return nil, errors.New("uninitialized")
|
||||||
}
|
}
|
||||||
@ -107,6 +107,9 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
|||||||
if _, err := io.ReadFull(conn, ivAndRelays); err != nil {
|
if _, err := io.ReadFull(conn, ivAndRelays); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if fallback != nil {
|
||||||
|
*fallback = append(*fallback, ivAndRelays...)
|
||||||
|
}
|
||||||
iv := ivAndRelays[:16]
|
iv := ivAndRelays[:16]
|
||||||
relays := ivAndRelays[16:]
|
relays := ivAndRelays[16:]
|
||||||
var nfsKey []byte
|
var nfsKey []byte
|
||||||
@ -156,6 +159,9 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
|||||||
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if fallback != nil {
|
||||||
|
*fallback = append(*fallback, encryptedLength...)
|
||||||
|
}
|
||||||
decryptedLength := make([]byte, 2)
|
decryptedLength := make([]byte, 2)
|
||||||
if _, err := nfsAEAD.Open(decryptedLength[:0], nil, encryptedLength, nil); err != nil {
|
if _, err := nfsAEAD.Open(decryptedLength[:0], nil, encryptedLength, nil); err != nil {
|
||||||
c.UseAES = !c.UseAES
|
c.UseAES = !c.UseAES
|
||||||
@ -164,6 +170,9 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if fallback != nil {
|
||||||
|
*fallback = nil
|
||||||
|
}
|
||||||
length := DecodeLength(decryptedLength)
|
length := DecodeLength(decryptedLength)
|
||||||
|
|
||||||
if length == 32 {
|
if length == 32 {
|
||||||
@ -182,7 +191,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
|||||||
s := i.Sessions[[16]byte(ticket)]
|
s := i.Sessions[[16]byte(ticket)]
|
||||||
i.RWLock.RUnlock()
|
i.RWLock.RUnlock()
|
||||||
if s == nil {
|
if s == nil {
|
||||||
noises := make([]byte, randBetween(1268, 2268)) // matches 1-RTT's server hello length for "random", though it is not important, just for example
|
noises := make([]byte, randBetween(1279, 2279)) // matches 1-RTT's server hello length for "random", though it is not important, just for example
|
||||||
var err error
|
var err error
|
||||||
for err == nil {
|
for err == nil {
|
||||||
rand.Read(noises)
|
rand.Read(noises)
|
||||||
@ -236,25 +245,10 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
|||||||
c.UnitedKey = append(pfsKey, nfsKey...)
|
c.UnitedKey = append(pfsKey, nfsKey...)
|
||||||
c.AEAD = NewAEAD(pfsPublicKey, c.UnitedKey, c.UseAES)
|
c.AEAD = NewAEAD(pfsPublicKey, c.UnitedKey, c.UseAES)
|
||||||
c.PeerAEAD = NewAEAD(encryptedPfsPublicKey[:1184+32], c.UnitedKey, c.UseAES)
|
c.PeerAEAD = NewAEAD(encryptedPfsPublicKey[:1184+32], c.UnitedKey, c.UseAES)
|
||||||
|
|
||||||
ticket := make([]byte, 16)
|
ticket := make([]byte, 16)
|
||||||
rand.Read(ticket)
|
rand.Read(ticket)
|
||||||
copy(ticket, EncodeLength(int(i.Seconds*4/5)))
|
copy(ticket, EncodeLength(int(i.Seconds*4/5)))
|
||||||
|
|
||||||
pfsKeyExchangeLength := 1088 + 32 + 16
|
|
||||||
encryptedTicketLength := 32
|
|
||||||
paddingLength := int(randBetween(100, 1000))
|
|
||||||
serverHello := make([]byte, pfsKeyExchangeLength+encryptedTicketLength+paddingLength)
|
|
||||||
nfsAEAD.Seal(serverHello[:0], MaxNonce, pfsPublicKey, nil)
|
|
||||||
c.AEAD.Seal(serverHello[:pfsKeyExchangeLength], nil, ticket, nil)
|
|
||||||
padding := serverHello[pfsKeyExchangeLength+encryptedTicketLength:]
|
|
||||||
c.AEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
|
|
||||||
c.AEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
|
|
||||||
|
|
||||||
if _, err := conn.Write(serverHello); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// padding can be sent in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
|
|
||||||
|
|
||||||
if i.Seconds > 0 {
|
if i.Seconds > 0 {
|
||||||
i.RWLock.Lock()
|
i.RWLock.Lock()
|
||||||
i.Sessions[[16]byte(ticket)] = &ServerSession{
|
i.Sessions[[16]byte(ticket)] = &ServerSession{
|
||||||
@ -264,6 +258,29 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
|||||||
i.RWLock.Unlock()
|
i.RWLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pfsKeyExchangeLength := 1088 + 32 + 16
|
||||||
|
encryptedTicketLength := 32
|
||||||
|
paddingLength, paddingLens, paddingGaps := CreatPadding(i.PaddingLens, i.PaddingGaps)
|
||||||
|
serverHello := make([]byte, pfsKeyExchangeLength+encryptedTicketLength+paddingLength)
|
||||||
|
nfsAEAD.Seal(serverHello[:0], MaxNonce, pfsPublicKey, nil)
|
||||||
|
c.AEAD.Seal(serverHello[:pfsKeyExchangeLength], nil, ticket, nil)
|
||||||
|
padding := serverHello[pfsKeyExchangeLength+encryptedTicketLength:]
|
||||||
|
c.AEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
|
||||||
|
c.AEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
|
||||||
|
|
||||||
|
paddingLens[0] = pfsKeyExchangeLength + encryptedTicketLength + paddingLens[0]
|
||||||
|
for i, l := range paddingLens { // sends padding in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
|
||||||
|
if l > 0 {
|
||||||
|
if _, err := conn.Write(serverHello[:l]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
serverHello = serverHello[l:]
|
||||||
|
}
|
||||||
|
if len(paddingGaps) > i {
|
||||||
|
time.Sleep(paddingGaps[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// important: allows client sends padding slowly, eliminating 1-RTT's traffic pattern
|
// important: allows client sends padding slowly, eliminating 1-RTT's traffic pattern
|
||||||
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user