Skip to content

Commit a0bdb86

Browse files
committed
chore: rebuild vless encryption string parsing
1 parent eca5a27 commit a0bdb86

File tree

3 files changed

+109
-78
lines changed

3 files changed

+109
-78
lines changed

adapter/outbound/vless.go

Lines changed: 5 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,11 @@ package outbound
33
import (
44
"context"
55
"crypto/tls"
6-
"encoding/base64"
76
"errors"
87
"fmt"
98
"net"
109
"net/http"
1110
"strconv"
12-
"strings"
13-
"time"
1411

1512
"github.com/metacubex/mihomo/common/convert"
1613
N "github.com/metacubex/mihomo/common/net"
@@ -456,41 +453,11 @@ func NewVless(option VlessOption) (*Vless, error) {
456453
option: &option,
457454
}
458455

459-
if s := strings.SplitN(option.Encryption, "-", 4); len(s) == 4 && s[2] == "mlkem768client" {
460-
var minutes uint32
461-
if s[0] != "1rtt" {
462-
t := strings.TrimSuffix(s[0], "min")
463-
if t == s[0] {
464-
return nil, fmt.Errorf("invaild vless encryption value: %s", option.Encryption)
465-
}
466-
var i int
467-
i, err = strconv.Atoi(t)
468-
if err != nil {
469-
return nil, fmt.Errorf("invaild vless encryption value: %s", option.Encryption)
470-
}
471-
minutes = uint32(i)
472-
}
473-
var xor uint32
474-
switch s[1] {
475-
case "vless":
476-
case "aes128xor":
477-
xor = 1
478-
default:
479-
return nil, fmt.Errorf("invaild vless encryption value: %s", option.Encryption)
480-
}
481-
var b []byte
482-
b, err = base64.RawURLEncoding.DecodeString(s[3])
483-
if err != nil {
484-
return nil, fmt.Errorf("invaild vless encryption value: %s", option.Encryption)
485-
}
486-
if len(b) == encryption.MLKEM768ClientLength {
487-
v.encryption = &encryption.ClientInstance{}
488-
if err = v.encryption.Init(b, xor, time.Duration(minutes)*time.Minute); err != nil {
489-
return nil, fmt.Errorf("failed to use mlkem768seed: %w", err)
490-
}
491-
} else {
492-
return nil, fmt.Errorf("invaild vless encryption value: %s", option.Encryption)
493-
}
456+
v.encryption, err = encryption.NewClient(option.Encryption)
457+
if err != nil {
458+
return nil, err
459+
}
460+
if v.encryption != nil {
494461
if option.Flow != "" {
495462
return nil, errors.New(`vless "encryption" doesn't support "flow" yet`)
496463
}

listener/sing_vless/server.go

Lines changed: 5 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,11 @@ package sing_vless
22

33
import (
44
"context"
5-
"encoding/base64"
65
"errors"
7-
"fmt"
86
"net"
97
"net/http"
108
"reflect"
11-
"strconv"
129
"strings"
13-
"time"
1410
"unsafe"
1511

1612
"github.com/metacubex/mihomo/adapter/inbound"
@@ -88,42 +84,11 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition)
8884

8985
sl = &Listener{config: config, service: service}
9086

91-
if s := strings.SplitN(config.Decryption, "-", 4); len(s) == 4 && s[2] == "mlkem768seed" {
92-
var minutes uint32
93-
if s[0] != "1rtt" {
94-
t := strings.TrimSuffix(s[0], "min")
95-
if t == s[0] {
96-
return nil, fmt.Errorf("invaild vless decryption value: %s", config.Decryption)
97-
}
98-
var i int
99-
i, err = strconv.Atoi(t)
100-
if err != nil {
101-
return nil, fmt.Errorf("invaild vless decryption value: %s", config.Decryption)
102-
}
103-
minutes = uint32(i)
104-
}
105-
var xor uint32
106-
switch s[1] {
107-
case "vless":
108-
case "aes128xor":
109-
xor = 1
110-
default:
111-
return nil, fmt.Errorf("invaild vless decryption value: %s", config.Decryption)
112-
}
113-
var b []byte
114-
b, err = base64.RawURLEncoding.DecodeString(s[3])
115-
if err != nil {
116-
return nil, fmt.Errorf("invaild vless decryption value: %s", config.Decryption)
117-
}
118-
if len(b) == encryption.MLKEM768SeedLength {
119-
sl.decryption = &encryption.ServerInstance{}
120-
if err = sl.decryption.Init(b, xor, time.Duration(minutes)*time.Minute); err != nil {
121-
return nil, fmt.Errorf("failed to use mlkem768seed: %w", err)
122-
}
123-
} else {
124-
return nil, fmt.Errorf("invaild vless decryption value: %s", config.Decryption)
125-
}
126-
87+
sl.decryption, err = encryption.NewServer(config.Decryption)
88+
if err != nil {
89+
return nil, err
90+
}
91+
if sl.decryption != nil {
12792
defer func() { // decryption must be closed to avoid the goroutine leak
12893
if err != nil {
12994
_ = sl.decryption.Close()

transport/vless/encryption/factory.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package encryption
2+
3+
import (
4+
"encoding/base64"
5+
"fmt"
6+
"strconv"
7+
"strings"
8+
"time"
9+
)
10+
11+
// NewClient new client from encryption string
12+
// maybe return a nil *ClientInstance without any error, that means don't need to encrypt
13+
func NewClient(encryption string) (*ClientInstance, error) {
14+
switch encryption {
15+
case "", "none": // We will not reject empty string like xray-core does, because we need to ensure compatibility
16+
return nil, nil
17+
}
18+
if s := strings.SplitN(encryption, "-", 4); len(s) == 4 && s[2] == "mlkem768client" {
19+
var minutes uint32
20+
if s[0] != "1rtt" {
21+
t := strings.TrimSuffix(s[0], "min")
22+
if t == s[0] {
23+
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
24+
}
25+
i, err := strconv.Atoi(t)
26+
if err != nil {
27+
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
28+
}
29+
minutes = uint32(i)
30+
}
31+
var xor uint32
32+
switch s[1] {
33+
case "vless":
34+
case "aes128xor":
35+
xor = 1
36+
default:
37+
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
38+
}
39+
b, err := base64.RawURLEncoding.DecodeString(s[3])
40+
if err != nil {
41+
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
42+
}
43+
if len(b) == MLKEM768ClientLength {
44+
client := &ClientInstance{}
45+
if err = client.Init(b, xor, time.Duration(minutes)*time.Minute); err != nil {
46+
return nil, fmt.Errorf("failed to use mlkem768seed: %w", err)
47+
}
48+
return client, nil
49+
} else {
50+
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
51+
}
52+
}
53+
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
54+
}
55+
56+
// NewServer new server from decryption string
57+
// maybe return a nil *ServerInstance without any error, that means don't need to decrypt
58+
func NewServer(decryption string) (*ServerInstance, error) {
59+
switch decryption {
60+
case "", "none": // We will not reject empty string like xray-core does, because we need to ensure compatibility
61+
return nil, nil
62+
}
63+
if s := strings.SplitN(decryption, "-", 4); len(s) == 4 && s[2] == "mlkem768seed" {
64+
var minutes uint32
65+
if s[0] != "1rtt" {
66+
t := strings.TrimSuffix(s[0], "min")
67+
if t == s[0] {
68+
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
69+
}
70+
i, err := strconv.Atoi(t)
71+
if err != nil {
72+
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
73+
}
74+
minutes = uint32(i)
75+
}
76+
var xor uint32
77+
switch s[1] {
78+
case "vless":
79+
case "aes128xor":
80+
xor = 1
81+
default:
82+
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
83+
}
84+
b, err := base64.RawURLEncoding.DecodeString(s[3])
85+
if err != nil {
86+
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
87+
}
88+
if len(b) == MLKEM768SeedLength {
89+
server := &ServerInstance{}
90+
if err = server.Init(b, xor, time.Duration(minutes)*time.Minute); err != nil {
91+
return nil, fmt.Errorf("failed to use mlkem768seed: %w", err)
92+
}
93+
return server, nil
94+
} else {
95+
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
96+
}
97+
}
98+
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
99+
}

0 commit comments

Comments
 (0)