mihomo/transport/vless/encryption/factory.go
2025-08-31 12:31:13 +08:00

117 lines
3.5 KiB
Go

package encryption
import (
"encoding/base64"
"fmt"
"strconv"
"strings"
)
// NewClient new client from encryption string
// maybe return a nil *ClientInstance without any error, that means don't need to encrypt
func NewClient(encryption string) (*ClientInstance, error) {
switch encryption {
case "", "none": // We will not reject empty string like xray-core does, because we need to ensure compatibility
return nil, nil
}
if s := strings.Split(encryption, "."); len(s) >= 4 && s[0] == "mlkem768x25519plus" {
var xorMode uint32
switch s[1] {
case "native":
case "xorpub":
xorMode = 1
case "random":
xorMode = 2
default:
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
}
var seconds uint32
switch s[2] {
case "1rtt":
case "0rtt":
seconds = 1
default:
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
}
var nfsPKeysBytes [][]byte
var paddings []string
for _, r := range s[3:] {
if len(r) < 20 {
paddings = append(paddings, r)
continue
}
b, err := base64.RawURLEncoding.DecodeString(r)
if err != nil {
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
}
if len(b) != X25519PasswordSize && len(b) != MLKEM768ClientLength {
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
}
nfsPKeysBytes = append(nfsPKeysBytes, b)
}
padding := strings.Join(paddings, ".")
client := &ClientInstance{}
if err := client.Init(nfsPKeysBytes, xorMode, seconds, padding); err != nil {
return nil, fmt.Errorf("failed to use encryption: %w", err)
}
return client, nil
}
return nil, fmt.Errorf("invaild vless encryption value: %s", encryption)
}
// NewServer new server from decryption string
// maybe return a nil *ServerInstance without any error, that means don't need to decrypt
func NewServer(decryption string) (*ServerInstance, error) {
switch decryption {
case "", "none": // We will not reject empty string like xray-core does, because we need to ensure compatibility
return nil, nil
}
if s := strings.Split(decryption, "."); len(s) >= 4 && s[0] == "mlkem768x25519plus" {
var xorMode uint32
switch s[1] {
case "native":
case "xorpub":
xorMode = 1
case "random":
xorMode = 2
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)
if err != nil {
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
}
seconds = uint32(i)
}
var nfsSKeysBytes [][]byte
var paddings []string
for _, r := range s[3:] {
if len(r) < 20 {
paddings = append(paddings, r)
continue
}
b, err := base64.RawURLEncoding.DecodeString(r)
if err != nil {
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
}
if len(b) != X25519PrivateKeySize && len(b) != MLKEM768SeedLength {
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
}
nfsSKeysBytes = append(nfsSKeysBytes, b)
}
padding := strings.Join(paddings, ".")
server := &ServerInstance{}
if err := server.Init(nfsSKeysBytes, xorMode, seconds, padding); err != nil {
return nil, fmt.Errorf("failed to use decryption: %w", err)
}
return server, nil
}
return nil, fmt.Errorf("invaild vless decryption value: %s", decryption)
}