Skip to content

Commit ef4e3b3

Browse files
authored
VLESS Encryption: Improve server-side tickets' expiration mechanism
#5067 (comment)
1 parent d20397c commit ef4e3b3

File tree

5 files changed

+67
-30
lines changed

5 files changed

+67
-30
lines changed

infra/conf/vless.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,13 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
101101
if err != nil {
102102
return false
103103
}
104-
config.SecondsFrom = uint32(i)
105-
if len(t) > 1 {
104+
config.SecondsFrom = int64(i)
105+
if len(t) == 2 {
106106
i, err := strconv.Atoi(t[1])
107107
if err != nil {
108108
return false
109109
}
110-
config.SecondsTo = uint32(i)
110+
config.SecondsTo = int64(i)
111111
}
112112
padding := 0
113113
for _, r := range s[3:] {

proxy/vless/encryption/server.go

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,19 @@ type ServerInstance struct {
2828
Hash32s [][32]byte
2929
RelaysLength int
3030
XorMode uint32
31-
SecondsFrom uint32
32-
SecondsTo uint32
31+
SecondsFrom int64
32+
SecondsTo int64
3333
PaddingLens [][3]int
3434
PaddingGaps [][3]int
3535

3636
RWLock sync.RWMutex
37+
Closed bool
38+
Lasts map[int64][16]byte
39+
Tickets [][16]byte
3740
Sessions map[[16]byte]*ServerSession
3841
}
3942

40-
func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, secondsFrom, secondsTo uint32, padding string) (err error) {
43+
func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode uint32, secondsFrom, secondsTo int64, padding string) (err error) {
4144
if i.NfsSKeys != nil {
4245
return errors.New("already initialized")
4346
}
@@ -68,10 +71,45 @@ func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, secondsFrom, seco
6871
i.XorMode = xorMode
6972
i.SecondsFrom = secondsFrom
7073
i.SecondsTo = secondsTo
71-
i.Sessions = make(map[[16]byte]*ServerSession)
74+
if i.SecondsFrom > 0 || i.SecondsTo > 0 {
75+
i.Lasts = make(map[int64][16]byte)
76+
i.Tickets = make([][16]byte, 0, 1024)
77+
i.Sessions = make(map[[16]byte]*ServerSession)
78+
go func() {
79+
for {
80+
time.Sleep(time.Minute)
81+
i.RWLock.Lock()
82+
if i.Closed {
83+
i.RWLock.Unlock()
84+
return
85+
}
86+
minute := time.Now().Unix() / 60
87+
last := i.Lasts[minute]
88+
delete(i.Lasts, minute)
89+
delete(i.Lasts, minute-1) // for insurance
90+
if last != [16]byte{} {
91+
for j, ticket := range i.Tickets {
92+
delete(i.Sessions, ticket)
93+
if ticket == last {
94+
i.Tickets = i.Tickets[j+1:]
95+
break
96+
}
97+
}
98+
}
99+
i.RWLock.Unlock()
100+
}
101+
}()
102+
}
72103
return ParsePadding(padding, &i.PaddingLens, &i.PaddingGaps)
73104
}
74105

106+
func (i *ServerInstance) Close() (err error) {
107+
i.RWLock.Lock()
108+
i.Closed = true
109+
i.RWLock.Unlock()
110+
return
111+
}
112+
75113
func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn, error) {
76114
if i.NfsSKeys == nil {
77115
return nil, errors.New("uninitialized")
@@ -224,33 +262,29 @@ func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn
224262
c.AEAD = NewAEAD(pfsPublicKey, c.UnitedKey, c.UseAES)
225263
c.PeerAEAD = NewAEAD(encryptedPfsPublicKey[:1184+32], c.UnitedKey, c.UseAES)
226264

227-
ticket := make([]byte, 16)
228-
rand.Read(ticket)
229-
seconds := 0
265+
ticket := [16]byte{}
266+
rand.Read(ticket[:])
267+
var seconds int64
230268
if i.SecondsTo == 0 {
231-
seconds = int(i.SecondsFrom) * int(crypto.RandBetween(50, 100)) / 100
269+
seconds = i.SecondsFrom * crypto.RandBetween(50, 100) / 100
232270
} else {
233-
seconds = int(crypto.RandBetween(int64(i.SecondsFrom), int64(i.SecondsTo)))
271+
seconds = crypto.RandBetween(i.SecondsFrom, i.SecondsTo)
234272
}
235-
copy(ticket, EncodeLength(int(seconds)))
273+
copy(ticket[:], EncodeLength(int(seconds)))
236274
if seconds > 0 {
237275
i.RWLock.Lock()
238-
i.Sessions[[16]byte(ticket)] = &ServerSession{PfsKey: pfsKey}
276+
i.Lasts[(time.Now().Unix()+max(i.SecondsFrom, i.SecondsTo))/60+2] = ticket
277+
i.Tickets = append(i.Tickets, ticket)
278+
i.Sessions[ticket] = &ServerSession{PfsKey: pfsKey}
239279
i.RWLock.Unlock()
240-
go func() {
241-
time.Sleep(time.Duration(seconds)*time.Second + time.Minute)
242-
i.RWLock.Lock()
243-
delete(i.Sessions, [16]byte(ticket))
244-
i.RWLock.Unlock()
245-
}()
246280
}
247281

248282
pfsKeyExchangeLength := 1088 + 32 + 16
249283
encryptedTicketLength := 32
250284
paddingLength, paddingLens, paddingGaps := CreatPadding(i.PaddingLens, i.PaddingGaps)
251285
serverHello := make([]byte, pfsKeyExchangeLength+encryptedTicketLength+paddingLength)
252286
nfsAEAD.Seal(serverHello[:0], MaxNonce, pfsPublicKey, nil)
253-
c.AEAD.Seal(serverHello[:pfsKeyExchangeLength], nil, ticket, nil)
287+
c.AEAD.Seal(serverHello[:pfsKeyExchangeLength], nil, ticket[:], nil)
254288
padding := serverHello[pfsKeyExchangeLength+encryptedTicketLength:]
255289
c.AEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
256290
c.AEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
@@ -284,7 +318,7 @@ func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn
284318
}
285319

286320
if i.XorMode == 2 {
287-
c.Conn = NewXorConn(conn, NewCTR(c.UnitedKey, ticket), NewCTR(c.UnitedKey, iv), 0, 0)
321+
c.Conn = NewXorConn(conn, NewCTR(c.UnitedKey, ticket[:]), NewCTR(c.UnitedKey, iv), 0, 0)
288322
}
289323
return c, nil
290324
}

proxy/vless/inbound/config.pb.go

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

proxy/vless/inbound/config.proto

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ message Config {
2323

2424
string decryption = 3;
2525
uint32 xorMode = 4;
26-
uint32 seconds_from = 5;
27-
uint32 seconds_to = 6;
26+
int64 seconds_from = 5;
27+
int64 seconds_to = 6;
2828
string padding = 7;
2929
}

proxy/vless/inbound/inbound.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,9 @@ func isMuxAndNotXUDP(request *protocol.RequestHeader, first *buf.Buffer) bool {
176176

177177
// Close implements common.Closable.Close().
178178
func (h *Handler) Close() error {
179+
if h.decryption != nil {
180+
h.decryption.Close()
181+
}
179182
return errors.Combine(common.Close(h.validator))
180183
}
181184

0 commit comments

Comments
 (0)