From 8eba1c8afde7d22761216c904a539b098eac52f7 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 4 Sep 2025 21:57:52 +0800 Subject: [PATCH] chore: sync vless encryption code --- docs/config.yaml | 5 ++- listener/sing_vless/server.go | 4 +- transport/vless/encryption/doc.go | 1 + transport/vless/encryption/factory.go | 20 +++++---- transport/vless/encryption/server.go | 63 ++++++++++----------------- 5 files changed, 40 insertions(+), 53 deletions(-) diff --git a/docs/config.yaml b/docs/config.yaml index ae4cf71b..77992164 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -1375,7 +1375,8 @@ listeners: # grpc-service-name: "GunService" # 如果不为空则开启 grpc 传输层 # ------------------------- # vless encryption服务端配置: - # (原生外观 / 只 XOR 公钥 / 全随机数。只允许 1-RTT 模式 / 同时允许 1-RTT 模式与 600 秒复用的 0-RTT 模式) + # (原生外观 / 只 XOR 公钥 / 全随机数。1-RTT 每次下发随机 300 到 600 秒的 ticket 以便 0-RTT 复用 / 只允许 1-RTT) + # 填写 "600s" 会每次随机取 50% 到 100%,即相当于填写 "300-600s" # / 是只能选一个,后面 base64 至少一个,无限串联,使用 mihomo generate vless-x25519 和 mihomo generate vless-mlkem768 生成,替换值时需去掉括号 # # Padding 是可选的参数,仅作用于 1-RTT 以消除握手的长度特征,双端默认值均为 "100-111-1111.75-0-111.50-0-3333": @@ -1384,7 +1385,7 @@ listeners: # 再次以 50% 的概率发送随机 0 到 3333 字节的 padding(若为 0 则不 Write()) # 服务端、客户端可以设置不同的 padding 参数,按 len、gap 的顺序无限串联,第一个 padding 需概率 100%、至少 35 字节 # ------------------------- - # decryption: "mlkem768x25519plus.native/xorpub/random.1rtt/600s.(padding len).(padding gap).(X25519 PrivateKey).(ML-KEM-768 Seed)..." + # decryption: "mlkem768x25519plus.native/xorpub/random.600s(300-600s)/0s.(padding len).(padding gap).(X25519 PrivateKey).(ML-KEM-768 Seed)..." # 下面两项如果填写则开启 tls(需要同时填写) # certificate: ./server.crt # private-key: ./server.key diff --git a/listener/sing_vless/server.go b/listener/sing_vless/server.go index 3a5943b5..764f687f 100644 --- a/listener/sing_vless/server.go +++ b/listener/sing_vless/server.go @@ -100,7 +100,7 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition) if sl.decryption != nil { defer func() { // decryption must be closed to avoid the goroutine leak if err != nil { - _ = sl.decryption.Close() + //_ = sl.decryption.Close() sl.decryption = nil } }() @@ -210,7 +210,7 @@ func (l *Listener) Close() error { } } if l.decryption != nil { - _ = l.decryption.Close() + //_ = l.decryption.Close() } return retErr } diff --git a/transport/vless/encryption/doc.go b/transport/vless/encryption/doc.go index 83b04f3c..0486b594 100644 --- a/transport/vless/encryption/doc.go +++ b/transport/vless/encryption/doc.go @@ -25,4 +25,5 @@ // https://github.com/XTLS/Xray-core/commit/e8b02cd6649f14889841e8ab8ee6b2acca71dbe6 // https://github.com/XTLS/Xray-core/commit/6768a22f676c9121cfc9dc4f51181a8a07837c8d // https://github.com/XTLS/Xray-core/commit/4c6fd94d97159f5a3e740ba6dd2d9b65e3ed320c +// https://github.com/XTLS/Xray-core/commit/19f890729656bc923ae3dee8426168c93b8ee9c2 package encryption diff --git a/transport/vless/encryption/factory.go b/transport/vless/encryption/factory.go index 246f49dd..179e79cb 100644 --- a/transport/vless/encryption/factory.go +++ b/transport/vless/encryption/factory.go @@ -77,17 +77,19 @@ func NewServer(decryption string) (*ServerInstance, error) { default: return nil, fmt.Errorf("invaild vless decryption value: %s", decryption) } - var seconds uint32 - if s[2] != "1rtt" { - t := strings.TrimSuffix(s[2], "s") - if t == s[0] { - return nil, fmt.Errorf("invaild vless decryption value: %s", decryption) - } - i, err := strconv.Atoi(t) + t := strings.SplitN(strings.TrimSuffix(s[2], "s"), "-", 2) + i, err := strconv.Atoi(t[0]) + if err != nil { + return nil, fmt.Errorf("invaild vless decryption value: %s", decryption) + } + secondsFrom := uint32(i) + var secondsTo uint32 + if len(t) > 1 { + i, err = strconv.Atoi(t[1]) if err != nil { return nil, fmt.Errorf("invaild vless decryption value: %s", decryption) } - seconds = uint32(i) + secondsTo = uint32(i) } var nfsSKeysBytes [][]byte var paddings []string @@ -107,7 +109,7 @@ func NewServer(decryption string) (*ServerInstance, error) { } padding := strings.Join(paddings, ".") server := &ServerInstance{} - if err := server.Init(nfsSKeysBytes, xorMode, seconds, padding); err != nil { + if err := server.Init(nfsSKeysBytes, xorMode, secondsFrom, secondsTo, padding); err != nil { return nil, fmt.Errorf("failed to use decryption: %w", err) } return server, nil diff --git a/transport/vless/encryption/server.go b/transport/vless/encryption/server.go index 69317cc0..3a85bc9a 100644 --- a/transport/vless/encryption/server.go +++ b/transport/vless/encryption/server.go @@ -17,7 +17,6 @@ import ( ) type ServerSession struct { - Expire time.Time PfsKey []byte NfsKeys sync.Map } @@ -28,16 +27,16 @@ type ServerInstance struct { Hash32s [][32]byte RelaysLength int XorMode uint32 - Seconds uint32 + SecondsFrom uint32 + SecondsTo uint32 PaddingLens [][3]int PaddingGaps [][3]int RWLock sync.RWMutex Sessions map[[16]byte]*ServerSession - Closed bool } -func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, seconds uint32, padding string) (err error) { +func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, secondsFrom, secondsTo uint32, padding string) (err error) { if i.NfsSKeys != nil { return errors.New("already initialized") } @@ -66,37 +65,12 @@ func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, seconds uint32, p } i.RelaysLength -= 32 i.XorMode = xorMode - if seconds > 0 { - i.Seconds = seconds - i.Sessions = make(map[[16]byte]*ServerSession) - go func() { - for { - time.Sleep(time.Minute) - i.RWLock.Lock() - if i.Closed { - i.RWLock.Unlock() - return - } - now := time.Now() - for ticket, session := range i.Sessions { - if now.After(session.Expire) { - delete(i.Sessions, ticket) - } - } - i.RWLock.Unlock() - } - }() - } + i.SecondsFrom = secondsFrom + i.SecondsTo = secondsTo + i.Sessions = make(map[[16]byte]*ServerSession) return ParsePadding(padding, &i.PaddingLens, &i.PaddingGaps) } -func (i *ServerInstance) Close() (err error) { - i.RWLock.Lock() - i.Closed = true - i.RWLock.Unlock() - return -} - func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn, error) { if i.NfsSKeys == nil { return nil, errors.New("uninitialized") @@ -131,7 +105,7 @@ func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn return nil, err } if publicKey.Bytes()[31] > 127 { // we just don't want the observer can change even one bit without breaking the connection, though it has nothing to do with security - return nil, errors.New("the highest bit of the last byte of the peer-sent X25519 public key must be 0") + return nil, errors.New("the highest bit of the last byte of the peer-sent X25519 public key is not 0") } nfsKey, err = k.ECDH(publicKey) if err != nil { @@ -179,7 +153,7 @@ func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn length := DecodeLength(decryptedLength) if length == 32 { - if i.Seconds == 0 { + if i.SecondsFrom == 0 && i.SecondsTo == 0 { return nil, errors.New("0-RTT is not allowed") } encryptedTicket := make([]byte, 32) @@ -251,14 +225,23 @@ func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn ticket := make([]byte, 16) rand.Read(ticket) - copy(ticket, EncodeLength(int(i.Seconds*4/5))) - if i.Seconds > 0 { + seconds := 0 + if i.SecondsTo == 0 { + seconds = int(i.SecondsFrom) * int(randBetween(50, 100)) / 100 + } else { + seconds = int(randBetween(int64(i.SecondsFrom), int64(i.SecondsTo))) + } + copy(ticket, EncodeLength(int(seconds))) + if seconds > 0 { i.RWLock.Lock() - i.Sessions[[16]byte(ticket)] = &ServerSession{ - Expire: time.Now().Add(time.Duration(i.Seconds) * time.Second), - PfsKey: pfsKey, - } + i.Sessions[[16]byte(ticket)] = &ServerSession{PfsKey: pfsKey} i.RWLock.Unlock() + go func() { + time.Sleep(time.Duration(seconds)*time.Second + time.Minute) + i.RWLock.Lock() + delete(i.Sessions, [16]byte(ticket)) + i.RWLock.Unlock() + }() } pfsKeyExchangeLength := 1088 + 32 + 16