mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-08-22 01:26:49 +08:00
VLESS protocol: Add lightweight Post-Quantum ML-KEM-768-based PFS 1-RTT / anti-replay 0-RTT AEAD encryption
https://github.com/XTLS/Xray-core/pull/4952#issuecomment-3163335040
This commit is contained in:
@@ -79,20 +79,18 @@ type CommandSwitchAccount struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
|
// Keep in sync with crypto/tls/cipher_suites.go.
|
||||||
|
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ && cpu.X86.HasSSE41 && cpu.X86.HasSSSE3
|
||||||
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
|
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
|
||||||
// Keep in sync with crypto/aes/cipher_s390x.go.
|
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCTR && cpu.S390X.HasGHASH
|
||||||
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR &&
|
hasGCMAsmPPC64 = runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le"
|
||||||
(cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)
|
|
||||||
|
|
||||||
hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 ||
|
HasAESGCMHardwareSupport = hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X || hasGCMAsmPPC64
|
||||||
runtime.GOARCH == "arm64" && hasGCMAsmARM64 ||
|
|
||||||
runtime.GOARCH == "s390x" && hasGCMAsmS390X
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (sc *SecurityConfig) GetSecurityType() SecurityType {
|
func (sc *SecurityConfig) GetSecurityType() SecurityType {
|
||||||
if sc == nil || sc.Type == SecurityType_AUTO {
|
if sc == nil || sc.Type == SecurityType_AUTO {
|
||||||
if hasAESGCMHardwareSupport {
|
if HasAESGCMHardwareSupport {
|
||||||
return SecurityType_AES128_GCM
|
return SecurityType_AES128_GCM
|
||||||
}
|
}
|
||||||
return SecurityType_CHACHA20_POLY1305
|
return SecurityType_CHACHA20_POLY1305
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package conf
|
package conf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -55,12 +56,16 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
|||||||
account.Id = u.String()
|
account.Id = u.String()
|
||||||
|
|
||||||
switch account.Flow {
|
switch account.Flow {
|
||||||
case "", vless.XRV:
|
case "":
|
||||||
|
case vless.XRV:
|
||||||
|
if c.Decryption != "none" {
|
||||||
|
return nil, errors.New(`VLESS clients: "decryption" doesn't support "flow" yet`)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return nil, errors.New(`VLESS clients: "flow" doesn't support "` + account.Flow + `" in this version`)
|
return nil, errors.New(`VLESS clients: "flow" doesn't support "` + account.Flow + `" in this version`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if account.Encryption != "" {
|
if len(account.Encryption) > 0 {
|
||||||
return nil, errors.New(`VLESS clients: "encryption" should not in inbound settings`)
|
return nil, errors.New(`VLESS clients: "encryption" should not in inbound settings`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,10 +73,34 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
|||||||
config.Clients[idx] = user
|
config.Clients[idx] = user
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Decryption != "none" {
|
if !func() bool {
|
||||||
return nil, errors.New(`VLESS settings: please add/set "decryption":"none" to every settings`)
|
s := strings.Split(c.Decryption, "-mlkem768seed-")
|
||||||
|
if len(s) != 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s[0] != "1rtt" {
|
||||||
|
t := strings.TrimSuffix(s[0], "min")
|
||||||
|
if t == s[0] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
i, err := strconv.Atoi(t)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
config.Minutes = uint32(i)
|
||||||
|
}
|
||||||
|
b, err := base64.RawURLEncoding.DecodeString(s[1])
|
||||||
|
if len(b) != 64 || err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
config.Decryption = s[1]
|
||||||
|
return true
|
||||||
|
}() && c.Decryption != "none" {
|
||||||
|
if c.Decryption == "" {
|
||||||
|
return nil, errors.New(`VLESS settings: please add/set "decryption":"none" to every settings`)
|
||||||
|
}
|
||||||
|
return nil, errors.New(`VLESS settings: unsupported "decryption": ` + c.Decryption)
|
||||||
}
|
}
|
||||||
config.Decryption = c.Decryption
|
|
||||||
|
|
||||||
for _, fb := range c.Fallbacks {
|
for _, fb := range c.Fallbacks {
|
||||||
var i uint16
|
var i uint16
|
||||||
@@ -143,16 +172,16 @@ type VLessOutboundConfig struct {
|
|||||||
func (c *VLessOutboundConfig) Build() (proto.Message, error) {
|
func (c *VLessOutboundConfig) Build() (proto.Message, error) {
|
||||||
config := new(outbound.Config)
|
config := new(outbound.Config)
|
||||||
|
|
||||||
if len(c.Vnext) == 0 {
|
if len(c.Vnext) != 1 {
|
||||||
return nil, errors.New(`VLESS settings: "vnext" is empty`)
|
return nil, errors.New(`VLESS settings: "vnext" should have one and only one member`)
|
||||||
}
|
}
|
||||||
config.Vnext = make([]*protocol.ServerEndpoint, len(c.Vnext))
|
config.Vnext = make([]*protocol.ServerEndpoint, len(c.Vnext))
|
||||||
for idx, rec := range c.Vnext {
|
for idx, rec := range c.Vnext {
|
||||||
if rec.Address == nil {
|
if rec.Address == nil {
|
||||||
return nil, errors.New(`VLESS vnext: "address" is not set`)
|
return nil, errors.New(`VLESS vnext: "address" is not set`)
|
||||||
}
|
}
|
||||||
if len(rec.Users) == 0 {
|
if len(rec.Users) != 1 {
|
||||||
return nil, errors.New(`VLESS vnext: "users" is empty`)
|
return nil, errors.New(`VLESS vnext: "users" should have one and only one member`)
|
||||||
}
|
}
|
||||||
spec := &protocol.ServerEndpoint{
|
spec := &protocol.ServerEndpoint{
|
||||||
Address: rec.Address.Build(),
|
Address: rec.Address.Build(),
|
||||||
@@ -176,13 +205,42 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
|
|||||||
account.Id = u.String()
|
account.Id = u.String()
|
||||||
|
|
||||||
switch account.Flow {
|
switch account.Flow {
|
||||||
case "", vless.XRV, vless.XRV + "-udp443":
|
case "":
|
||||||
|
case vless.XRV, vless.XRV + "-udp443":
|
||||||
|
if account.Encryption != "none" {
|
||||||
|
return nil, errors.New(`VLESS users: "encryption" doesn't support "flow" yet`)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return nil, errors.New(`VLESS users: "flow" doesn't support "` + account.Flow + `" in this version`)
|
return nil, errors.New(`VLESS users: "flow" doesn't support "` + account.Flow + `" in this version`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if account.Encryption != "none" {
|
if !func() bool {
|
||||||
return nil, errors.New(`VLESS users: please add/set "encryption":"none" for every user`)
|
s := strings.Split(account.Encryption, "-mlkem768client-")
|
||||||
|
if len(s) != 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s[0] != "1rtt" {
|
||||||
|
t := strings.TrimSuffix(s[0], "min")
|
||||||
|
if t == s[0] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
i, err := strconv.Atoi(t)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
account.Minutes = uint32(i)
|
||||||
|
}
|
||||||
|
b, err := base64.RawURLEncoding.DecodeString(s[1])
|
||||||
|
if len(b) != 1184 || err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
account.Encryption = s[1]
|
||||||
|
return true
|
||||||
|
}() && account.Encryption != "none" {
|
||||||
|
if account.Encryption == "" {
|
||||||
|
return nil, errors.New(`VLESS users: please add/set "encryption":"none" for every user`)
|
||||||
|
}
|
||||||
|
return nil, errors.New(`VLESS users: unsupported "encryption": ` + account.Encryption)
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Account = serial.ToTypedMessage(account)
|
user.Account = serial.ToTypedMessage(account)
|
||||||
|
@@ -17,5 +17,6 @@ func init() {
|
|||||||
cmdX25519,
|
cmdX25519,
|
||||||
cmdWG,
|
cmdWG,
|
||||||
cmdMLDSA65,
|
cmdMLDSA65,
|
||||||
|
cmdMLKEM768,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -11,9 +11,9 @@ import (
|
|||||||
|
|
||||||
var cmdMLDSA65 = &base.Command{
|
var cmdMLDSA65 = &base.Command{
|
||||||
UsageLine: `{{.Exec}} mldsa65 [-i "seed (base64.RawURLEncoding)"]`,
|
UsageLine: `{{.Exec}} mldsa65 [-i "seed (base64.RawURLEncoding)"]`,
|
||||||
Short: `Generate key pair for ML-DSA-65 post-quantum signature`,
|
Short: `Generate key pair for ML-DSA-65 post-quantum signature (REALITY)`,
|
||||||
Long: `
|
Long: `
|
||||||
Generate key pair for ML-DSA-65 post-quantum signature.
|
Generate key pair for ML-DSA-65 post-quantum signature (REALITY).
|
||||||
|
|
||||||
Random: {{.Exec}} mldsa65
|
Random: {{.Exec}} mldsa65
|
||||||
|
|
||||||
@@ -25,12 +25,16 @@ func init() {
|
|||||||
cmdMLDSA65.Run = executeMLDSA65 // break init loop
|
cmdMLDSA65.Run = executeMLDSA65 // break init loop
|
||||||
}
|
}
|
||||||
|
|
||||||
var input_seed = cmdMLDSA65.Flag.String("i", "", "")
|
var input_mldsa65 = cmdMLDSA65.Flag.String("i", "", "")
|
||||||
|
|
||||||
func executeMLDSA65(cmd *base.Command, args []string) {
|
func executeMLDSA65(cmd *base.Command, args []string) {
|
||||||
var seed [32]byte
|
var seed [32]byte
|
||||||
if len(*input_seed) > 0 {
|
if len(*input_mldsa65) > 0 {
|
||||||
s, _ := base64.RawURLEncoding.DecodeString(*input_seed)
|
s, _ := base64.RawURLEncoding.DecodeString(*input_mldsa65)
|
||||||
|
if len(s) != 32 {
|
||||||
|
fmt.Println("Invalid length of ML-DSA-65 seed.")
|
||||||
|
return
|
||||||
|
}
|
||||||
seed = [32]byte(s)
|
seed = [32]byte(s)
|
||||||
} else {
|
} else {
|
||||||
rand.Read(seed[:])
|
rand.Read(seed[:])
|
||||||
|
47
main/commands/all/mlkem768.go
Normal file
47
main/commands/all/mlkem768.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package all
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/mlkem"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/main/commands/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmdMLKEM768 = &base.Command{
|
||||||
|
UsageLine: `{{.Exec}} mlkem768 [-i "seed (base64.RawURLEncoding)"]`,
|
||||||
|
Short: `Generate key pair for ML-KEM-768 post-quantum key exchange (VLESS)`,
|
||||||
|
Long: `
|
||||||
|
Generate key pair for ML-KEM-768 post-quantum key exchange (VLESS).
|
||||||
|
|
||||||
|
Random: {{.Exec}} mlkem768
|
||||||
|
|
||||||
|
From seed: {{.Exec}} mlkem768 -i "seed (base64.RawURLEncoding)"
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cmdMLKEM768.Run = executeMLKEM768 // break init loop
|
||||||
|
}
|
||||||
|
|
||||||
|
var input_mlkem768 = cmdMLKEM768.Flag.String("i", "", "")
|
||||||
|
|
||||||
|
func executeMLKEM768(cmd *base.Command, args []string) {
|
||||||
|
var seed [64]byte
|
||||||
|
if len(*input_mlkem768) > 0 {
|
||||||
|
s, _ := base64.RawURLEncoding.DecodeString(*input_mlkem768)
|
||||||
|
if len(s) != 64 {
|
||||||
|
fmt.Println("Invalid length of ML-KEM-768 seed.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
seed = [64]byte(s)
|
||||||
|
} else {
|
||||||
|
rand.Read(seed[:])
|
||||||
|
}
|
||||||
|
key, _ := mlkem.NewDecapsulationKey768(seed[:])
|
||||||
|
pub := key.EncapsulationKey()
|
||||||
|
fmt.Printf("Seed: %v\nClient: %v",
|
||||||
|
base64.RawURLEncoding.EncodeToString(seed[:]),
|
||||||
|
base64.RawURLEncoding.EncodeToString(pub.Bytes()))
|
||||||
|
}
|
@@ -9,9 +9,9 @@ import (
|
|||||||
|
|
||||||
var cmdUUID = &base.Command{
|
var cmdUUID = &base.Command{
|
||||||
UsageLine: `{{.Exec}} uuid [-i "example"]`,
|
UsageLine: `{{.Exec}} uuid [-i "example"]`,
|
||||||
Short: `Generate UUIDv4 or UUIDv5`,
|
Short: `Generate UUIDv4 or UUIDv5 (VLESS)`,
|
||||||
Long: `
|
Long: `
|
||||||
Generate UUIDv4 or UUIDv5.
|
Generate UUIDv4 or UUIDv5 (VLESS).
|
||||||
|
|
||||||
UUIDv4 (random): {{.Exec}} uuid
|
UUIDv4 (random): {{.Exec}} uuid
|
||||||
|
|
||||||
|
@@ -6,9 +6,9 @@ import (
|
|||||||
|
|
||||||
var cmdWG = &base.Command{
|
var cmdWG = &base.Command{
|
||||||
UsageLine: `{{.Exec}} wg [-i "private key (base64.StdEncoding)"]`,
|
UsageLine: `{{.Exec}} wg [-i "private key (base64.StdEncoding)"]`,
|
||||||
Short: `Generate key pair for wireguard key exchange`,
|
Short: `Generate key pair for X25519 key exchange (WireGuard)`,
|
||||||
Long: `
|
Long: `
|
||||||
Generate key pair for wireguard key exchange.
|
Generate key pair for X25519 key exchange (WireGuard).
|
||||||
|
|
||||||
Random: {{.Exec}} wg
|
Random: {{.Exec}} wg
|
||||||
|
|
||||||
|
@@ -6,9 +6,9 @@ import (
|
|||||||
|
|
||||||
var cmdX25519 = &base.Command{
|
var cmdX25519 = &base.Command{
|
||||||
UsageLine: `{{.Exec}} x25519 [-i "private key (base64.RawURLEncoding)"] [--std-encoding]`,
|
UsageLine: `{{.Exec}} x25519 [-i "private key (base64.RawURLEncoding)"] [--std-encoding]`,
|
||||||
Short: `Generate key pair for x25519 key exchange`,
|
Short: `Generate key pair for X25519 key exchange (REALITY)`,
|
||||||
Long: `
|
Long: `
|
||||||
Generate key pair for x25519 key exchange.
|
Generate key pair for X25519 key exchange (REALITY).
|
||||||
|
|
||||||
Random: {{.Exec}} x25519
|
Random: {{.Exec}} x25519
|
||||||
|
|
||||||
|
@@ -18,6 +18,7 @@ func (a *Account) AsAccount() (protocol.Account, error) {
|
|||||||
ID: protocol.NewID(id),
|
ID: protocol.NewID(id),
|
||||||
Flow: a.Flow, // needs parser here?
|
Flow: a.Flow, // needs parser here?
|
||||||
Encryption: a.Encryption, // needs parser here?
|
Encryption: a.Encryption, // needs parser here?
|
||||||
|
Minutes: a.Minutes,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,8 +28,9 @@ type MemoryAccount struct {
|
|||||||
ID *protocol.ID
|
ID *protocol.ID
|
||||||
// Flow of the account. May be "xtls-rprx-vision".
|
// Flow of the account. May be "xtls-rprx-vision".
|
||||||
Flow string
|
Flow string
|
||||||
// Encryption of the account. Used for client connections, and only accepts "none" for now.
|
|
||||||
Encryption string
|
Encryption string
|
||||||
|
Minutes uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equals implements protocol.Account.Equals().
|
// Equals implements protocol.Account.Equals().
|
||||||
@@ -45,5 +47,6 @@ func (a *MemoryAccount) ToProto() proto.Message {
|
|||||||
Id: a.ID.String(),
|
Id: a.ID.String(),
|
||||||
Flow: a.Flow,
|
Flow: a.Flow,
|
||||||
Encryption: a.Encryption,
|
Encryption: a.Encryption,
|
||||||
|
Minutes: a.Minutes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -28,9 +28,9 @@ type Account struct {
|
|||||||
// ID of the account, in the form of a UUID, e.g., "66ad4540-b58c-4ad2-9926-ea63445a9b57".
|
// ID of the account, in the form of a UUID, e.g., "66ad4540-b58c-4ad2-9926-ea63445a9b57".
|
||||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
// Flow settings. May be "xtls-rprx-vision".
|
// Flow settings. May be "xtls-rprx-vision".
|
||||||
Flow string `protobuf:"bytes,2,opt,name=flow,proto3" json:"flow,omitempty"`
|
Flow string `protobuf:"bytes,2,opt,name=flow,proto3" json:"flow,omitempty"`
|
||||||
// Encryption settings. Only applies to client side, and only accepts "none" for now.
|
|
||||||
Encryption string `protobuf:"bytes,3,opt,name=encryption,proto3" json:"encryption,omitempty"`
|
Encryption string `protobuf:"bytes,3,opt,name=encryption,proto3" json:"encryption,omitempty"`
|
||||||
|
Minutes uint32 `protobuf:"varint,4,opt,name=minutes,proto3" json:"minutes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Account) Reset() {
|
func (x *Account) Reset() {
|
||||||
@@ -84,23 +84,32 @@ func (x *Account) GetEncryption() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Account) GetMinutes() uint32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Minutes
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
var File_proxy_vless_account_proto protoreflect.FileDescriptor
|
var File_proxy_vless_account_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_proxy_vless_account_proto_rawDesc = []byte{
|
var file_proxy_vless_account_proto_rawDesc = []byte{
|
||||||
0x0a, 0x19, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x61, 0x63,
|
0x0a, 0x19, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x61, 0x63,
|
||||||
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61,
|
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61,
|
||||||
0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x22, 0x4d, 0x0a,
|
0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x22, 0x67, 0x0a,
|
||||||
0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
|
0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
|
||||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6c, 0x6f, 0x77,
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6c, 0x6f, 0x77,
|
||||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1e, 0x0a, 0x0a,
|
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1e, 0x0a, 0x0a,
|
||||||
0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
|
0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
|
||||||
0x52, 0x0a, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x52, 0x0a, 0x14,
|
0x52, 0x0a, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07,
|
||||||
0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76,
|
0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6d,
|
||||||
0x6c, 0x65, 0x73, 0x73, 0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
|
0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72,
|
||||||
0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72,
|
0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x50, 0x01,
|
||||||
0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0xaa, 0x02, 0x10,
|
0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c,
|
||||||
0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73,
|
0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78,
|
||||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0xaa, 0x02, 0x10, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50,
|
||||||
|
0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -11,6 +11,7 @@ message Account {
|
|||||||
string id = 1;
|
string id = 1;
|
||||||
// Flow settings. May be "xtls-rprx-vision".
|
// Flow settings. May be "xtls-rprx-vision".
|
||||||
string flow = 2;
|
string flow = 2;
|
||||||
// Encryption settings. Only applies to client side, and only accepts "none" for now.
|
|
||||||
string encryption = 3;
|
string encryption = 3;
|
||||||
|
uint32 minutes = 4;
|
||||||
}
|
}
|
||||||
|
228
proxy/vless/encryption/client.go
Normal file
228
proxy/vless/encryption/client.go
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
package encryption
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/mlkem"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/crypto"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
"github.com/xtls/xray-core/common/protocol"
|
||||||
|
"golang.org/x/crypto/hkdf"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ClientCipher byte
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if !protocol.HasAESGCMHardwareSupport {
|
||||||
|
ClientCipher = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientInstance struct {
|
||||||
|
sync.RWMutex
|
||||||
|
eKeyNfs *mlkem.EncapsulationKey768
|
||||||
|
minutes time.Duration
|
||||||
|
expire time.Time
|
||||||
|
baseKey []byte
|
||||||
|
reuse []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientConn struct {
|
||||||
|
net.Conn
|
||||||
|
instance *ClientInstance
|
||||||
|
baseKey []byte
|
||||||
|
reuse []byte
|
||||||
|
random []byte
|
||||||
|
aead cipher.AEAD
|
||||||
|
nonce []byte
|
||||||
|
peerAead cipher.AEAD
|
||||||
|
peerNonce []byte
|
||||||
|
peerCache []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ClientInstance) Init(eKeyNfsData []byte, minutes time.Duration) (err error) {
|
||||||
|
i.eKeyNfs, err = mlkem.NewEncapsulationKey768(eKeyNfsData)
|
||||||
|
i.minutes = minutes
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ClientInstance) Handshake(conn net.Conn) (net.Conn, error) {
|
||||||
|
if i.eKeyNfs == nil {
|
||||||
|
return nil, errors.New("uninitialized")
|
||||||
|
}
|
||||||
|
c := &ClientConn{Conn: conn}
|
||||||
|
|
||||||
|
if i.minutes > 0 {
|
||||||
|
i.RLock()
|
||||||
|
if time.Now().Before(i.expire) {
|
||||||
|
c.instance = i
|
||||||
|
c.baseKey = i.baseKey
|
||||||
|
c.reuse = i.reuse
|
||||||
|
i.RUnlock()
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
i.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
nfsKey, encapsulatedNfsKey := i.eKeyNfs.Encapsulate()
|
||||||
|
seed := make([]byte, 64)
|
||||||
|
rand.Read(seed)
|
||||||
|
dKeyPfs, _ := mlkem.NewDecapsulationKey768(seed)
|
||||||
|
eKeyPfs := dKeyPfs.EncapsulationKey().Bytes()
|
||||||
|
padding := crypto.RandBetween(100, 1000)
|
||||||
|
|
||||||
|
clientHello := make([]byte, 1088+1184+1+5+padding)
|
||||||
|
copy(clientHello, encapsulatedNfsKey)
|
||||||
|
copy(clientHello[1088:], eKeyPfs)
|
||||||
|
clientHello[2272] = ClientCipher
|
||||||
|
encodeHeader(clientHello[2273:], int(padding))
|
||||||
|
|
||||||
|
if _, err := c.Conn.Write(clientHello); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// we can send more padding if needed
|
||||||
|
|
||||||
|
peerServerHello := make([]byte, 1088+21)
|
||||||
|
if _, err := io.ReadFull(c.Conn, peerServerHello); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
encapsulatedPfsKey := peerServerHello[:1088]
|
||||||
|
c.reuse = peerServerHello[1088:]
|
||||||
|
|
||||||
|
pfsKey, err := dKeyPfs.Decapsulate(encapsulatedPfsKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c.baseKey = append(nfsKey, pfsKey...)
|
||||||
|
|
||||||
|
authKey := make([]byte, 32)
|
||||||
|
hkdf.New(sha256.New, c.baseKey, encapsulatedNfsKey, eKeyPfs).Read(authKey)
|
||||||
|
nonce := make([]byte, 12)
|
||||||
|
VLESS, _ := newAead(ClientCipher, authKey).Open(nil, nonce, c.reuse, encapsulatedPfsKey)
|
||||||
|
if !bytes.Equal(VLESS, []byte("VLESS")) { // TODO: more message
|
||||||
|
return nil, errors.New("invalid server").AtError()
|
||||||
|
}
|
||||||
|
|
||||||
|
if i.minutes > 0 {
|
||||||
|
i.Lock()
|
||||||
|
i.expire = time.Now().Add(i.minutes)
|
||||||
|
i.baseKey = c.baseKey
|
||||||
|
i.reuse = c.reuse
|
||||||
|
i.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) Write(b []byte) (int, error) {
|
||||||
|
if len(b) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
var data []byte
|
||||||
|
if c.aead == nil {
|
||||||
|
c.random = make([]byte, 32)
|
||||||
|
rand.Read(c.random)
|
||||||
|
key := make([]byte, 32)
|
||||||
|
hkdf.New(sha256.New, c.baseKey, c.random, c.reuse).Read(key)
|
||||||
|
c.aead = newAead(ClientCipher, key)
|
||||||
|
c.nonce = make([]byte, 12)
|
||||||
|
|
||||||
|
data = make([]byte, 21+32+5+len(b)+16)
|
||||||
|
copy(data, c.reuse)
|
||||||
|
copy(data[21:], c.random)
|
||||||
|
encodeHeader(data[53:], len(b)+16)
|
||||||
|
c.aead.Seal(data[:58], c.nonce, b, data[53:58])
|
||||||
|
} else {
|
||||||
|
data = make([]byte, 5+len(b)+16)
|
||||||
|
encodeHeader(data, len(b)+16)
|
||||||
|
c.aead.Seal(data[:5], c.nonce, b, data[:5])
|
||||||
|
}
|
||||||
|
increaseNonce(c.nonce)
|
||||||
|
if _, err := c.Conn.Write(data); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) Read(b []byte) (int, error) { // after first Write()
|
||||||
|
if len(b) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
peerHeader := make([]byte, 5)
|
||||||
|
if c.peerAead == nil {
|
||||||
|
if c.instance == nil {
|
||||||
|
for {
|
||||||
|
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
peerPadding, _ := decodeHeader(peerHeader)
|
||||||
|
if peerPadding == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if _, err := io.ReadFull(c.Conn, make([]byte, peerPadding)); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
peerRandom := make([]byte, 32)
|
||||||
|
copy(peerRandom, peerHeader)
|
||||||
|
if _, err := io.ReadFull(c.Conn, peerRandom[5:]); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if c.random == nil {
|
||||||
|
return 0, errors.New("can not Read() first")
|
||||||
|
}
|
||||||
|
peerKey := make([]byte, 32)
|
||||||
|
hkdf.New(sha256.New, c.baseKey, peerRandom, c.random).Read(peerKey)
|
||||||
|
c.peerAead = newAead(ClientCipher, peerKey)
|
||||||
|
c.peerNonce = make([]byte, 12)
|
||||||
|
}
|
||||||
|
if len(c.peerCache) != 0 {
|
||||||
|
n := copy(b, c.peerCache)
|
||||||
|
c.peerCache = c.peerCache[n:]
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
peerLength, err := decodeHeader(peerHeader) // 17~17000
|
||||||
|
if err != nil {
|
||||||
|
if c.instance != nil {
|
||||||
|
c.instance.Lock()
|
||||||
|
if bytes.Equal(c.reuse, c.instance.reuse) {
|
||||||
|
c.instance.expire = time.Now() // expired
|
||||||
|
}
|
||||||
|
c.instance.Unlock()
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
peerData := make([]byte, peerLength)
|
||||||
|
if _, err := io.ReadFull(c.Conn, peerData); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
dst := peerData[:peerLength-16]
|
||||||
|
if len(dst) <= len(b) {
|
||||||
|
dst = b[:len(dst)] // max=8192 is recommended for peer
|
||||||
|
}
|
||||||
|
_, err = c.peerAead.Open(dst[:0], c.peerNonce, peerData, peerHeader)
|
||||||
|
increaseNonce(c.peerNonce)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if len(dst) > len(b) {
|
||||||
|
c.peerCache = dst[copy(b, dst):]
|
||||||
|
dst = b // for len(dst)
|
||||||
|
}
|
||||||
|
return len(dst), nil
|
||||||
|
}
|
55
proxy/vless/encryption/common.go
Normal file
55
proxy/vless/encryption/common.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package encryption
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
|
)
|
||||||
|
|
||||||
|
func encodeHeader(b []byte, l int) {
|
||||||
|
b[0] = 23
|
||||||
|
b[1] = 3
|
||||||
|
b[2] = 3
|
||||||
|
b[3] = byte(l >> 8)
|
||||||
|
b[4] = byte(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeHeader(b []byte) (int, error) {
|
||||||
|
if b[0] == 23 && b[1] == 3 && b[2] == 3 {
|
||||||
|
l := int(b[3])<<8 | int(b[4])
|
||||||
|
if l < 17 || l > 17000 { // TODO
|
||||||
|
return 0, errors.New("invalid length in record's header: " + strconv.Itoa(l))
|
||||||
|
}
|
||||||
|
return l, nil
|
||||||
|
}
|
||||||
|
return 0, errors.New("invalid record's header")
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAead(c byte, k []byte) cipher.AEAD {
|
||||||
|
switch c {
|
||||||
|
case 0:
|
||||||
|
if block, err := aes.NewCipher(k); err == nil {
|
||||||
|
aead, _ := cipher.NewGCM(block)
|
||||||
|
return aead
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
aead, _ := chacha20poly1305.New(k)
|
||||||
|
return aead
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func increaseNonce(nonce []byte) {
|
||||||
|
for i := range 12 {
|
||||||
|
nonce[11-i]++
|
||||||
|
if nonce[11-i] != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if i == 11 {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
251
proxy/vless/encryption/server.go
Normal file
251
proxy/vless/encryption/server.go
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
package encryption
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/mlkem"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/crypto"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
"golang.org/x/crypto/hkdf"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerSession struct {
|
||||||
|
expire time.Time
|
||||||
|
cipher byte
|
||||||
|
baseKey []byte
|
||||||
|
randoms sync.Map
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerInstance struct {
|
||||||
|
sync.RWMutex
|
||||||
|
dKeyNfs *mlkem.DecapsulationKey768
|
||||||
|
minutes time.Duration
|
||||||
|
sessions map[[21]byte]*ServerSession
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConn struct {
|
||||||
|
net.Conn
|
||||||
|
cipher byte
|
||||||
|
baseKey []byte
|
||||||
|
reuse []byte
|
||||||
|
peerRandom []byte
|
||||||
|
peerAead cipher.AEAD
|
||||||
|
peerNonce []byte
|
||||||
|
peerCache []byte
|
||||||
|
aead cipher.AEAD
|
||||||
|
nonce []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ServerInstance) Init(dKeyNfsData []byte, minutes time.Duration) (err error) {
|
||||||
|
i.dKeyNfs, err = mlkem.NewDecapsulationKey768(dKeyNfsData)
|
||||||
|
if minutes > 0 {
|
||||||
|
i.minutes = minutes
|
||||||
|
i.sessions = make(map[[21]byte]*ServerSession)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
time.Sleep(time.Minute)
|
||||||
|
now := time.Now()
|
||||||
|
i.Lock()
|
||||||
|
for index, session := range i.sessions {
|
||||||
|
if now.After(session.expire) {
|
||||||
|
delete(i.sessions, index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i.Unlock()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ServerInstance) Handshake(conn net.Conn) (net.Conn, error) {
|
||||||
|
if i.dKeyNfs == nil {
|
||||||
|
return nil, errors.New("uninitialized")
|
||||||
|
}
|
||||||
|
c := &ServerConn{Conn: conn}
|
||||||
|
|
||||||
|
peerReuseHello := make([]byte, 21+32)
|
||||||
|
if _, err := io.ReadFull(c.Conn, peerReuseHello); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if i.minutes > 0 {
|
||||||
|
i.RLock()
|
||||||
|
s := i.sessions[[21]byte(peerReuseHello)]
|
||||||
|
i.RUnlock()
|
||||||
|
if s != nil {
|
||||||
|
if _, replay := s.randoms.LoadOrStore([32]byte(peerReuseHello[21:]), true); !replay {
|
||||||
|
c.cipher = s.cipher
|
||||||
|
c.baseKey = s.baseKey
|
||||||
|
c.reuse = peerReuseHello[:21]
|
||||||
|
c.peerRandom = peerReuseHello[21:]
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
peerHeader := make([]byte, 5)
|
||||||
|
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if l, _ := decodeHeader(peerHeader); l != 0 {
|
||||||
|
c.Conn.Write(make([]byte, crypto.RandBetween(100, 1000))) // make client do new handshake
|
||||||
|
return nil, errors.New("invalid reuse")
|
||||||
|
}
|
||||||
|
|
||||||
|
peerClientHello := make([]byte, 1088+1184+1)
|
||||||
|
copy(peerClientHello, peerReuseHello)
|
||||||
|
copy(peerClientHello[53:], peerHeader)
|
||||||
|
if _, err := io.ReadFull(c.Conn, peerClientHello[58:]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
encapsulatedNfsKey := peerClientHello[:1088]
|
||||||
|
eKeyPfsData := peerClientHello[1088:2272]
|
||||||
|
c.cipher = peerClientHello[2272]
|
||||||
|
if c.cipher != 0 && c.cipher != 1 {
|
||||||
|
return nil, errors.New("invalid cipher")
|
||||||
|
}
|
||||||
|
|
||||||
|
nfsKey, err := i.dKeyNfs.Decapsulate(encapsulatedNfsKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
eKeyPfs, err := mlkem.NewEncapsulationKey768(eKeyPfsData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pfsKey, encapsulatedPfsKey := eKeyPfs.Encapsulate()
|
||||||
|
c.baseKey = append(nfsKey, pfsKey...)
|
||||||
|
|
||||||
|
authKey := make([]byte, 32)
|
||||||
|
hkdf.New(sha256.New, c.baseKey, encapsulatedNfsKey, eKeyPfsData).Read(authKey)
|
||||||
|
nonce := make([]byte, 12)
|
||||||
|
c.reuse = newAead(c.cipher, authKey).Seal(nil, nonce, []byte("VLESS"), encapsulatedPfsKey)
|
||||||
|
|
||||||
|
padding := crypto.RandBetween(100, 1000)
|
||||||
|
|
||||||
|
serverHello := make([]byte, 1088+21+5+padding)
|
||||||
|
copy(serverHello, encapsulatedPfsKey)
|
||||||
|
copy(serverHello[1088:], c.reuse)
|
||||||
|
encodeHeader(serverHello[1109:], int(padding))
|
||||||
|
|
||||||
|
if _, err := c.Conn.Write(serverHello); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if i.minutes > 0 {
|
||||||
|
i.Lock()
|
||||||
|
i.sessions[[21]byte(c.reuse)] = &ServerSession{
|
||||||
|
expire: time.Now().Add(i.minutes),
|
||||||
|
cipher: c.cipher,
|
||||||
|
baseKey: c.baseKey,
|
||||||
|
}
|
||||||
|
i.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServerConn) Read(b []byte) (int, error) {
|
||||||
|
if len(b) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
peerHeader := make([]byte, 5)
|
||||||
|
if c.peerAead == nil {
|
||||||
|
if c.peerRandom == nil {
|
||||||
|
for {
|
||||||
|
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
peerPadding, _ := decodeHeader(peerHeader)
|
||||||
|
if peerPadding == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if _, err := io.ReadFull(c.Conn, make([]byte, peerPadding)); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
peerIndex := make([]byte, 21)
|
||||||
|
copy(peerIndex, peerHeader)
|
||||||
|
if _, err := io.ReadFull(c.Conn, peerIndex[5:]); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if !bytes.Equal(peerIndex, c.reuse) {
|
||||||
|
return 0, errors.New("naughty boy")
|
||||||
|
}
|
||||||
|
c.peerRandom = make([]byte, 32)
|
||||||
|
if _, err := io.ReadFull(c.Conn, c.peerRandom); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
peerKey := make([]byte, 32)
|
||||||
|
hkdf.New(sha256.New, c.baseKey, c.peerRandom, c.reuse).Read(peerKey)
|
||||||
|
c.peerAead = newAead(c.cipher, peerKey)
|
||||||
|
c.peerNonce = make([]byte, 12)
|
||||||
|
}
|
||||||
|
if len(c.peerCache) != 0 {
|
||||||
|
n := copy(b, c.peerCache)
|
||||||
|
c.peerCache = c.peerCache[n:]
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
peerLength, err := decodeHeader(peerHeader) // 17~17000
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
peerData := make([]byte, peerLength)
|
||||||
|
if _, err := io.ReadFull(c.Conn, peerData); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
dst := peerData[:peerLength-16]
|
||||||
|
if len(dst) <= len(b) {
|
||||||
|
dst = b[:len(dst)] // max=8192 is recommended for peer
|
||||||
|
}
|
||||||
|
_, err = c.peerAead.Open(dst[:0], c.peerNonce, peerData, peerHeader)
|
||||||
|
increaseNonce(c.peerNonce)
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.New("error")
|
||||||
|
}
|
||||||
|
if len(dst) > len(b) {
|
||||||
|
c.peerCache = dst[copy(b, dst):]
|
||||||
|
dst = b // for len(dst)
|
||||||
|
}
|
||||||
|
return len(dst), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServerConn) Write(b []byte) (int, error) { // after first Read()
|
||||||
|
if len(b) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
var data []byte
|
||||||
|
if c.aead == nil {
|
||||||
|
if c.peerRandom == nil {
|
||||||
|
return 0, errors.New("can not Write() first")
|
||||||
|
}
|
||||||
|
data = make([]byte, 32+5+len(b)+16)
|
||||||
|
rand.Read(data[:32])
|
||||||
|
key := make([]byte, 32)
|
||||||
|
hkdf.New(sha256.New, c.baseKey, data[:32], c.peerRandom).Read(key)
|
||||||
|
c.aead = newAead(c.cipher, key)
|
||||||
|
c.nonce = make([]byte, 12)
|
||||||
|
encodeHeader(data[32:], len(b)+16)
|
||||||
|
c.aead.Seal(data[:37], c.nonce, b, data[32:37])
|
||||||
|
} else {
|
||||||
|
data = make([]byte, 5+len(b)+16)
|
||||||
|
encodeHeader(data, len(b)+16)
|
||||||
|
c.aead.Seal(data[:5], c.nonce, b, data[:5])
|
||||||
|
}
|
||||||
|
increaseNonce(c.nonce)
|
||||||
|
if _, err := c.Conn.Write(data); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return len(b), nil
|
||||||
|
}
|
@@ -111,11 +111,10 @@ type Config struct {
|
|||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
Clients []*protocol.User `protobuf:"bytes,1,rep,name=clients,proto3" json:"clients,omitempty"`
|
Clients []*protocol.User `protobuf:"bytes,1,rep,name=clients,proto3" json:"clients,omitempty"`
|
||||||
// Decryption settings. Only applies to server side, and only accepts "none"
|
Fallbacks []*Fallback `protobuf:"bytes,2,rep,name=fallbacks,proto3" json:"fallbacks,omitempty"`
|
||||||
// for now.
|
Decryption string `protobuf:"bytes,3,opt,name=decryption,proto3" json:"decryption,omitempty"`
|
||||||
Decryption string `protobuf:"bytes,2,opt,name=decryption,proto3" json:"decryption,omitempty"`
|
Minutes uint32 `protobuf:"varint,4,opt,name=minutes,proto3" json:"minutes,omitempty"`
|
||||||
Fallbacks []*Fallback `protobuf:"bytes,3,rep,name=fallbacks,proto3" json:"fallbacks,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) Reset() {
|
func (x *Config) Reset() {
|
||||||
@@ -155,6 +154,13 @@ func (x *Config) GetClients() []*protocol.User {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetFallbacks() []*Fallback {
|
||||||
|
if x != nil {
|
||||||
|
return x.Fallbacks
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (x *Config) GetDecryption() string {
|
func (x *Config) GetDecryption() string {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Decryption
|
return x.Decryption
|
||||||
@@ -162,11 +168,11 @@ func (x *Config) GetDecryption() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) GetFallbacks() []*Fallback {
|
func (x *Config) GetMinutes() uint32 {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Fallbacks
|
return x.Minutes
|
||||||
}
|
}
|
||||||
return nil
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
var File_proxy_vless_inbound_config_proto protoreflect.FileDescriptor
|
var File_proxy_vless_inbound_config_proto protoreflect.FileDescriptor
|
||||||
@@ -185,25 +191,26 @@ var file_proxy_vless_inbound_config_proto_rawDesc = []byte{
|
|||||||
0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
|
0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20,
|
0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20,
|
||||||
0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x78, 0x76, 0x65,
|
0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x78, 0x76, 0x65,
|
||||||
0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x78, 0x76, 0x65, 0x72, 0x22, 0xa0, 0x01,
|
0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x78, 0x76, 0x65, 0x72, 0x22, 0xba, 0x01,
|
||||||
0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x63, 0x6c, 0x69, 0x65,
|
0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x63, 0x6c, 0x69, 0x65,
|
||||||
0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
||||||
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
|
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
|
||||||
0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1e,
|
0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x40,
|
||||||
0x0a, 0x0a, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01,
|
0x0a, 0x09, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
|
||||||
0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x40,
|
|
||||||
0x0a, 0x09, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,
|
|
||||||
0x0b, 0x32, 0x22, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76,
|
0x0b, 0x32, 0x22, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76,
|
||||||
0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x46, 0x61, 0x6c,
|
0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x46, 0x61, 0x6c,
|
||||||
0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x09, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73,
|
0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x09, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73,
|
||||||
0x42, 0x6a, 0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f,
|
0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03,
|
||||||
0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64,
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e,
|
||||||
0x50, 0x01, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78,
|
0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28,
|
||||||
0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72,
|
0x0d, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x42, 0x6a, 0x0a, 0x1c, 0x63, 0x6f,
|
||||||
0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e,
|
0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65,
|
||||||
0x64, 0xaa, 0x02, 0x18, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56,
|
0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2d, 0x67, 0x69,
|
||||||
0x6c, 0x65, 0x73, 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72,
|
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72,
|
||||||
0x6f, 0x74, 0x6f, 0x33,
|
0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c,
|
||||||
|
0x65, 0x73, 0x73, 0x2f, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0xaa, 0x02, 0x18, 0x58, 0x72,
|
||||||
|
0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x49,
|
||||||
|
0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -19,8 +19,8 @@ message Fallback {
|
|||||||
|
|
||||||
message Config {
|
message Config {
|
||||||
repeated xray.common.protocol.User clients = 1;
|
repeated xray.common.protocol.User clients = 1;
|
||||||
// Decryption settings. Only applies to server side, and only accepts "none"
|
repeated Fallback fallbacks = 2;
|
||||||
// for now.
|
|
||||||
string decryption = 2;
|
string decryption = 3;
|
||||||
repeated Fallback fallbacks = 3;
|
uint32 minutes = 4;
|
||||||
}
|
}
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
gotls "crypto/tls"
|
gotls "crypto/tls"
|
||||||
|
"encoding/base64"
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -29,6 +30,7 @@ import (
|
|||||||
"github.com/xtls/xray-core/proxy"
|
"github.com/xtls/xray-core/proxy"
|
||||||
"github.com/xtls/xray-core/proxy/vless"
|
"github.com/xtls/xray-core/proxy/vless"
|
||||||
"github.com/xtls/xray-core/proxy/vless/encoding"
|
"github.com/xtls/xray-core/proxy/vless/encoding"
|
||||||
|
"github.com/xtls/xray-core/proxy/vless/encryption"
|
||||||
"github.com/xtls/xray-core/transport/internet/reality"
|
"github.com/xtls/xray-core/transport/internet/reality"
|
||||||
"github.com/xtls/xray-core/transport/internet/stat"
|
"github.com/xtls/xray-core/transport/internet/stat"
|
||||||
"github.com/xtls/xray-core/transport/internet/tls"
|
"github.com/xtls/xray-core/transport/internet/tls"
|
||||||
@@ -67,6 +69,7 @@ type Handler struct {
|
|||||||
policyManager policy.Manager
|
policyManager policy.Manager
|
||||||
validator vless.Validator
|
validator vless.Validator
|
||||||
dns dns.Client
|
dns dns.Client
|
||||||
|
decryption *encryption.ServerInstance
|
||||||
fallbacks map[string]map[string]map[string]*Fallback // or nil
|
fallbacks map[string]map[string]map[string]*Fallback // or nil
|
||||||
// regexps map[string]*regexp.Regexp // or nil
|
// regexps map[string]*regexp.Regexp // or nil
|
||||||
}
|
}
|
||||||
@@ -81,6 +84,14 @@ func New(ctx context.Context, config *Config, dc dns.Client, validator vless.Val
|
|||||||
validator: validator,
|
validator: validator,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
d, _ := base64.RawURLEncoding.DecodeString(config.Decryption)
|
||||||
|
if len(d) == 64 {
|
||||||
|
handler.decryption = &encryption.ServerInstance{}
|
||||||
|
if err := handler.decryption.Init(d, time.Duration(config.Minutes)*time.Minute); err != nil {
|
||||||
|
return nil, errors.New("failed to use mlkem768seed").Base(err).AtError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if config.Fallbacks != nil {
|
if config.Fallbacks != nil {
|
||||||
handler.fallbacks = make(map[string]map[string]map[string]*Fallback)
|
handler.fallbacks = make(map[string]map[string]map[string]*Fallback)
|
||||||
// handler.regexps = make(map[string]*regexp.Regexp)
|
// handler.regexps = make(map[string]*regexp.Regexp)
|
||||||
@@ -204,6 +215,14 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
|||||||
return errors.New("unable to set read deadline").Base(err).AtWarning()
|
return errors.New("unable to set read deadline").Base(err).AtWarning()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if h.decryption != nil {
|
||||||
|
var err error
|
||||||
|
connection, err = h.decryption.Handshake(connection)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("ML-KEM-768 handshake failed").Base(err).AtInfo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
first := buf.FromBytes(make([]byte, buf.Size))
|
first := buf.FromBytes(make([]byte, buf.Size))
|
||||||
first.Clear()
|
first.Clear()
|
||||||
firstLen, errR := first.ReadFrom(connection)
|
firstLen, errR := first.ReadFrom(connection)
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
gotls "crypto/tls"
|
gotls "crypto/tls"
|
||||||
|
"encoding/base64"
|
||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
@@ -24,6 +25,7 @@ import (
|
|||||||
"github.com/xtls/xray-core/proxy"
|
"github.com/xtls/xray-core/proxy"
|
||||||
"github.com/xtls/xray-core/proxy/vless"
|
"github.com/xtls/xray-core/proxy/vless"
|
||||||
"github.com/xtls/xray-core/proxy/vless/encoding"
|
"github.com/xtls/xray-core/proxy/vless/encoding"
|
||||||
|
"github.com/xtls/xray-core/proxy/vless/encryption"
|
||||||
"github.com/xtls/xray-core/transport"
|
"github.com/xtls/xray-core/transport"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
"github.com/xtls/xray-core/transport/internet/reality"
|
"github.com/xtls/xray-core/transport/internet/reality"
|
||||||
@@ -43,6 +45,7 @@ type Handler struct {
|
|||||||
serverPicker protocol.ServerPicker
|
serverPicker protocol.ServerPicker
|
||||||
policyManager policy.Manager
|
policyManager policy.Manager
|
||||||
cone bool
|
cone bool
|
||||||
|
encryption *encryption.ClientInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new VLess outbound handler.
|
// New creates a new VLess outbound handler.
|
||||||
@@ -64,6 +67,15 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
|
|||||||
cone: ctx.Value("cone").(bool),
|
cone: ctx.Value("cone").(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a := handler.serverPicker.PickServer().PickUser().Account.(*vless.MemoryAccount)
|
||||||
|
e, _ := base64.RawURLEncoding.DecodeString(a.Encryption)
|
||||||
|
if len(e) == 1184 {
|
||||||
|
handler.encryption = &encryption.ClientInstance{}
|
||||||
|
if err := handler.encryption.Init(e, time.Duration(a.Minutes)*time.Minute); err != nil {
|
||||||
|
return nil, errors.New("failed to use mlkem768client").Base(err).AtError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return handler, nil
|
return handler, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,6 +110,14 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
target := ob.Target
|
target := ob.Target
|
||||||
errors.LogInfo(ctx, "tunneling request to ", target, " via ", rec.Destination().NetAddr())
|
errors.LogInfo(ctx, "tunneling request to ", target, " via ", rec.Destination().NetAddr())
|
||||||
|
|
||||||
|
if h.encryption != nil {
|
||||||
|
var err error
|
||||||
|
conn, err = h.encryption.Handshake(conn)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("ML-KEM-768 handshake failed").Base(err).AtInfo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
command := protocol.RequestCommandTCP
|
command := protocol.RequestCommandTCP
|
||||||
if target.Network == net.Network_UDP {
|
if target.Network == net.Network_UDP {
|
||||||
command = protocol.RequestCommandUDP
|
command = protocol.RequestCommandUDP
|
||||||
|
Reference in New Issue
Block a user