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) } 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 := int64(i) secondsTo := int64(0) if len(t) == 2 { i, err = strconv.Atoi(t[1]) if err != nil { return nil, fmt.Errorf("invaild vless decryption value: %s", decryption) } secondsTo = int64(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, secondsFrom, secondsTo, 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) }