chore: sync vless encryption code

This commit is contained in:
wwqgtxx 2025-09-04 21:57:52 +08:00
parent 65d3920f02
commit 8eba1c8afd
5 changed files with 40 additions and 53 deletions

View File

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

View File

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

View File

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

View File

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

View File

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