mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-08-22 09:36:49 +08:00
Use X25519 for XOR; Add "divide" (ECH, before and includes type 0); Change config format
https://github.com/XTLS/Xray-core/pull/4952#issuecomment-3207449672
This commit is contained in:
@@ -71,8 +71,8 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
|||||||
|
|
||||||
config.Decryption = c.Decryption
|
config.Decryption = c.Decryption
|
||||||
if !func() bool {
|
if !func() bool {
|
||||||
s := strings.SplitN(config.Decryption, "-", 4)
|
s := strings.Split(config.Decryption, ".")
|
||||||
if len(s) != 4 || s[2] != "mlkem768seed" {
|
if len(s) != 5 || s[2] != "mlkem768Seed" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if s[0] != "1rtt" {
|
if s[0] != "1rtt" {
|
||||||
@@ -87,17 +87,21 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
|||||||
config.Minutes = uint32(i)
|
config.Minutes = uint32(i)
|
||||||
}
|
}
|
||||||
switch s[1] {
|
switch s[1] {
|
||||||
case "vless":
|
case "native":
|
||||||
case "xored":
|
case "divide":
|
||||||
config.Xor = 1
|
config.XorMode = 1
|
||||||
|
case "random":
|
||||||
|
config.XorMode = 2
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
b, err := base64.RawURLEncoding.DecodeString(s[3])
|
if b, _ := base64.RawURLEncoding.DecodeString(s[3]); len(b) != 32 {
|
||||||
if len(b) != 64 || err != nil {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
config.Decryption = s[3]
|
if b, _ := base64.RawURLEncoding.DecodeString(s[4]); len(b) != 64 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
config.Decryption = s[4] + "." + s[3]
|
||||||
return true
|
return true
|
||||||
}() && config.Decryption != "none" {
|
}() && config.Decryption != "none" {
|
||||||
if config.Decryption == "" {
|
if config.Decryption == "" {
|
||||||
@@ -215,8 +219,8 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !func() bool {
|
if !func() bool {
|
||||||
s := strings.SplitN(account.Encryption, "-", 4)
|
s := strings.Split(account.Encryption, ".")
|
||||||
if len(s) != 4 || s[2] != "mlkem768client" {
|
if len(s) != 5 || s[2] != "mlkem768Client" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if s[0] != "1rtt" {
|
if s[0] != "1rtt" {
|
||||||
@@ -231,17 +235,21 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
|
|||||||
account.Minutes = uint32(i)
|
account.Minutes = uint32(i)
|
||||||
}
|
}
|
||||||
switch s[1] {
|
switch s[1] {
|
||||||
case "vless":
|
case "native":
|
||||||
case "xored":
|
case "divide":
|
||||||
account.Xor = 1
|
account.XorMode = 1
|
||||||
|
case "random":
|
||||||
|
account.XorMode = 2
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
b, err := base64.RawURLEncoding.DecodeString(s[3])
|
if b, _ := base64.RawURLEncoding.DecodeString(s[3]); len(b) != 32 {
|
||||||
if len(b) != 1184 || err != nil {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
account.Encryption = s[3]
|
if b, _ := base64.RawURLEncoding.DecodeString(s[4]); len(b) != 1184 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
account.Encryption = s[4] + "." + s[3]
|
||||||
return true
|
return true
|
||||||
}() && account.Encryption != "none" {
|
}() && account.Encryption != "none" {
|
||||||
if account.Encryption == "" {
|
if account.Encryption == "" {
|
||||||
|
@@ -1,17 +1,13 @@
|
|||||||
package all
|
package all
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/ecdh"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"golang.org/x/crypto/curve25519"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Curve25519Genkey(StdEncoding bool, input_base64 string) {
|
func Curve25519Genkey(StdEncoding bool, input_base64 string) {
|
||||||
var output string
|
|
||||||
var err error
|
|
||||||
var privateKey, publicKey []byte
|
|
||||||
var encoding *base64.Encoding
|
var encoding *base64.Encoding
|
||||||
if *input_stdEncoding || StdEncoding {
|
if *input_stdEncoding || StdEncoding {
|
||||||
encoding = base64.StdEncoding
|
encoding = base64.StdEncoding
|
||||||
@@ -19,24 +15,17 @@ func Curve25519Genkey(StdEncoding bool, input_base64 string) {
|
|||||||
encoding = base64.RawURLEncoding
|
encoding = base64.RawURLEncoding
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var privateKey []byte
|
||||||
if len(input_base64) > 0 {
|
if len(input_base64) > 0 {
|
||||||
privateKey, err = encoding.DecodeString(input_base64)
|
privateKey, _ = encoding.DecodeString(input_base64)
|
||||||
if err != nil {
|
if len(privateKey) != 32 {
|
||||||
output = err.Error()
|
fmt.Println("Invalid length of X25519 private key.")
|
||||||
goto out
|
return
|
||||||
}
|
|
||||||
if len(privateKey) != curve25519.ScalarSize {
|
|
||||||
output = "Invalid length of private key."
|
|
||||||
goto out
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if privateKey == nil {
|
if privateKey == nil {
|
||||||
privateKey = make([]byte, curve25519.ScalarSize)
|
privateKey = make([]byte, 32)
|
||||||
if _, err = rand.Read(privateKey); err != nil {
|
rand.Read(privateKey)
|
||||||
output = err.Error()
|
|
||||||
goto out
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modify random bytes using algorithm described at:
|
// Modify random bytes using algorithm described at:
|
||||||
@@ -45,14 +34,12 @@ func Curve25519Genkey(StdEncoding bool, input_base64 string) {
|
|||||||
privateKey[31] &= 127
|
privateKey[31] &= 127
|
||||||
privateKey[31] |= 64
|
privateKey[31] |= 64
|
||||||
|
|
||||||
if publicKey, err = curve25519.X25519(privateKey, curve25519.Basepoint); err != nil {
|
key, err := ecdh.X25519().NewPrivateKey(privateKey)
|
||||||
output = err.Error()
|
if err != nil {
|
||||||
goto out
|
fmt.Println(err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
fmt.Printf("PrivateKey: %v\nPassword: %v",
|
||||||
output = fmt.Sprintf("Private key: %v\nPublic key: %v",
|
|
||||||
encoding.EncodeToString(privateKey),
|
encoding.EncodeToString(privateKey),
|
||||||
encoding.EncodeToString(publicKey))
|
encoding.EncodeToString(key.PublicKey().Bytes()))
|
||||||
out:
|
|
||||||
fmt.Println(output)
|
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@ package all
|
|||||||
import (
|
import (
|
||||||
"crypto/mlkem"
|
"crypto/mlkem"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"crypto/sha3"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
@@ -40,8 +41,10 @@ func executeMLKEM768(cmd *base.Command, args []string) {
|
|||||||
rand.Read(seed[:])
|
rand.Read(seed[:])
|
||||||
}
|
}
|
||||||
key, _ := mlkem.NewDecapsulationKey768(seed[:])
|
key, _ := mlkem.NewDecapsulationKey768(seed[:])
|
||||||
pub := key.EncapsulationKey()
|
client := key.EncapsulationKey().Bytes()
|
||||||
fmt.Printf("Seed: %v\nClient: %v",
|
hash32 := sha3.Sum256(client)
|
||||||
|
fmt.Printf("Seed: %v\nClient: %v\nHash11: %v",
|
||||||
base64.RawURLEncoding.EncodeToString(seed[:]),
|
base64.RawURLEncoding.EncodeToString(seed[:]),
|
||||||
base64.RawURLEncoding.EncodeToString(pub.Bytes()))
|
base64.RawURLEncoding.EncodeToString(client),
|
||||||
|
base64.RawURLEncoding.EncodeToString(hash32[:11]))
|
||||||
}
|
}
|
||||||
|
@@ -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 (REALITY)`,
|
Short: `Generate key pair for X25519 key exchange (VLESS, REALITY)`,
|
||||||
Long: `
|
Long: `
|
||||||
Generate key pair for X25519 key exchange (REALITY).
|
Generate key pair for X25519 key exchange (VLESS, REALITY).
|
||||||
|
|
||||||
Random: {{.Exec}} x25519
|
Random: {{.Exec}} x25519
|
||||||
|
|
||||||
|
@@ -539,7 +539,10 @@ func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) {
|
|||||||
isEncryption = true
|
isEncryption = true
|
||||||
}
|
}
|
||||||
if xorConn, ok := conn.(*encryption.XorConn); ok {
|
if xorConn, ok := conn.(*encryption.XorConn); ok {
|
||||||
return xorConn, nil, nil // xorConn should not be penetrated
|
if !xorConn.Divide {
|
||||||
|
return xorConn, nil, nil // full-random xorConn should not be penetrated
|
||||||
|
}
|
||||||
|
conn = xorConn.Conn
|
||||||
}
|
}
|
||||||
if statConn, ok := conn.(*stat.CounterConnection); ok {
|
if statConn, ok := conn.(*stat.CounterConnection); ok {
|
||||||
conn = statConn.Connection
|
conn = statConn.Connection
|
||||||
@@ -652,3 +655,14 @@ func readV(ctx context.Context, reader buf.Reader, writer buf.Writer, timer sign
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsRAWTransport(conn stat.Connection) bool {
|
||||||
|
iConn := conn
|
||||||
|
if statConn, ok := iConn.(*stat.CounterConnection); ok {
|
||||||
|
iConn = statConn.Connection
|
||||||
|
}
|
||||||
|
_, ok1 := iConn.(*proxyproto.Conn)
|
||||||
|
_, ok2 := iConn.(*net.TCPConn)
|
||||||
|
_, ok3 := iConn.(*internet.UnixConnWrapper)
|
||||||
|
return ok1 || ok2 || ok3
|
||||||
|
}
|
||||||
|
@@ -18,7 +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?
|
||||||
Xor: a.Xor,
|
XorMode: a.XorMode,
|
||||||
Minutes: a.Minutes,
|
Minutes: a.Minutes,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ type MemoryAccount struct {
|
|||||||
Flow string
|
Flow string
|
||||||
|
|
||||||
Encryption string
|
Encryption string
|
||||||
Xor uint32
|
XorMode uint32
|
||||||
Minutes uint32
|
Minutes uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ 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,
|
||||||
Xor: a.Xor,
|
XorMode: a.XorMode,
|
||||||
Minutes: a.Minutes,
|
Minutes: a.Minutes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -30,7 +30,7 @@ type Account struct {
|
|||||||
// 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 string `protobuf:"bytes,3,opt,name=encryption,proto3" json:"encryption,omitempty"`
|
Encryption string `protobuf:"bytes,3,opt,name=encryption,proto3" json:"encryption,omitempty"`
|
||||||
Xor uint32 `protobuf:"varint,4,opt,name=xor,proto3" json:"xor,omitempty"`
|
XorMode uint32 `protobuf:"varint,4,opt,name=xorMode,proto3" json:"xorMode,omitempty"`
|
||||||
Minutes uint32 `protobuf:"varint,5,opt,name=minutes,proto3" json:"minutes,omitempty"`
|
Minutes uint32 `protobuf:"varint,5,opt,name=minutes,proto3" json:"minutes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,9 +85,9 @@ func (x *Account) GetEncryption() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Account) GetXor() uint32 {
|
func (x *Account) GetXorMode() uint32 {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Xor
|
return x.XorMode
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -104,21 +104,21 @@ 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, 0x79, 0x0a,
|
0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x22, 0x81, 0x01,
|
||||||
0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
|
0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
|
||||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6c, 0x6f, 0x77,
|
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6c, 0x6f,
|
||||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1e, 0x0a, 0x0a,
|
0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1e, 0x0a,
|
||||||
0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
|
0x0a, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||||
0x52, 0x0a, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03,
|
0x09, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a,
|
||||||
0x78, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x78, 0x6f, 0x72, 0x12, 0x18,
|
0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07,
|
||||||
0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52,
|
0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x75, 0x74,
|
||||||
0x07, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e,
|
0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65,
|
||||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73,
|
0x73, 0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72,
|
||||||
0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78,
|
0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74,
|
||||||
0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72,
|
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61,
|
||||||
0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0xaa, 0x02, 0x10, 0x58, 0x72, 0x61, 0x79,
|
0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65,
|
||||||
0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x62, 0x06, 0x70, 0x72,
|
0x73, 0x73, 0xaa, 0x02, 0x10, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e,
|
||||||
0x6f, 0x74, 0x6f, 0x33,
|
0x56, 0x6c, 0x65, 0x73, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -13,6 +13,6 @@ message Account {
|
|||||||
string flow = 2;
|
string flow = 2;
|
||||||
|
|
||||||
string encryption = 3;
|
string encryption = 3;
|
||||||
uint32 xor = 4;
|
uint32 xorMode = 4;
|
||||||
uint32 minutes = 5;
|
uint32 minutes = 5;
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@ package encryption
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
|
"crypto/ecdh"
|
||||||
"crypto/mlkem"
|
"crypto/mlkem"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha3"
|
"crypto/sha3"
|
||||||
@@ -29,7 +30,8 @@ type ClientInstance struct {
|
|||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
nfsEKey *mlkem.EncapsulationKey768
|
nfsEKey *mlkem.EncapsulationKey768
|
||||||
hash11 [11]byte // no more capacity
|
hash11 [11]byte // no more capacity
|
||||||
xorKey []byte
|
xorMode uint32
|
||||||
|
xorPKey *ecdh.PublicKey
|
||||||
minutes time.Duration
|
minutes time.Duration
|
||||||
expire time.Time
|
expire time.Time
|
||||||
baseKey []byte
|
baseKey []byte
|
||||||
@@ -49,22 +51,23 @@ type ClientConn struct {
|
|||||||
PeerCache []byte
|
PeerCache []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ClientInstance) Init(nfsEKeyBytes []byte, xor uint32, minutes time.Duration) (err error) {
|
func (i *ClientInstance) Init(nfsEKeyBytes, xorPKeyBytes []byte, xorMode, minutes uint32) (err error) {
|
||||||
if i.nfsEKey != nil {
|
if i.nfsEKey != nil {
|
||||||
err = errors.New("already initialized")
|
err = errors.New("already initialized")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
i.nfsEKey, err = mlkem.NewEncapsulationKey768(nfsEKeyBytes)
|
if i.nfsEKey, err = mlkem.NewEncapsulationKey768(nfsEKeyBytes); err != nil {
|
||||||
if err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
hash32 := sha3.Sum256(nfsEKeyBytes)
|
hash32 := sha3.Sum256(nfsEKeyBytes)
|
||||||
copy(i.hash11[:], hash32[:])
|
copy(i.hash11[:], hash32[:])
|
||||||
if xor > 0 {
|
if xorMode > 0 {
|
||||||
xorKey := sha3.Sum256(nfsEKeyBytes)
|
i.xorMode = xorMode
|
||||||
i.xorKey = xorKey[:]
|
if i.xorPKey, err = ecdh.X25519().NewPublicKey(xorPKeyBytes); err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
i.minutes = minutes
|
}
|
||||||
|
i.minutes = time.Duration(minutes) * time.Minute
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,8 +75,8 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*ClientConn, error) {
|
|||||||
if i.nfsEKey == nil {
|
if i.nfsEKey == nil {
|
||||||
return nil, errors.New("uninitialized")
|
return nil, errors.New("uninitialized")
|
||||||
}
|
}
|
||||||
if i.xorKey != nil {
|
if i.xorMode > 0 {
|
||||||
conn = NewXorConn(conn, i.xorKey)
|
conn, _ = NewXorConn(conn, i.xorMode, i.xorPKey, nil)
|
||||||
}
|
}
|
||||||
c := &ClientConn{Conn: conn}
|
c := &ClientConn{Conn: conn}
|
||||||
|
|
||||||
@@ -134,7 +137,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*ClientConn, error) {
|
|||||||
}
|
}
|
||||||
c.baseKey = append(pfsKey, nfsKey...)
|
c.baseKey = append(pfsKey, nfsKey...)
|
||||||
|
|
||||||
VLESS, _ := NewAead(ClientCipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Open(nil, append(i.hash11[:], ClientCipher), c.ticket[11:], pfsEKeyBytes)
|
VLESS, _ := NewAEAD(ClientCipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Open(nil, append(i.hash11[:], ClientCipher), c.ticket[11:], pfsEKeyBytes)
|
||||||
if !bytes.Equal(VLESS, []byte("VLESS")) {
|
if !bytes.Equal(VLESS, []byte("VLESS")) {
|
||||||
return nil, errors.New("invalid server").AtError()
|
return nil, errors.New("invalid server").AtError()
|
||||||
}
|
}
|
||||||
@@ -169,7 +172,7 @@ func (c *ClientConn) Write(b []byte) (int, error) {
|
|||||||
rand.Read(c.random)
|
rand.Read(c.random)
|
||||||
copy(data[5+32:], c.random)
|
copy(data[5+32:], c.random)
|
||||||
EncodeHeader(data[5+32+32:], 23, len(b)+16)
|
EncodeHeader(data[5+32+32:], 23, len(b)+16)
|
||||||
c.aead = NewAead(ClientCipher, c.baseKey, c.random, c.ticket)
|
c.aead = NewAEAD(ClientCipher, c.baseKey, c.random, c.ticket)
|
||||||
c.nonce = make([]byte, 12)
|
c.nonce = make([]byte, 12)
|
||||||
c.aead.Seal(data[:5+32+32+5], c.nonce, b, data[5+32+32:5+32+32+5])
|
c.aead.Seal(data[:5+32+32+5], c.nonce, b, data[5+32+32:5+32+32+5])
|
||||||
} else {
|
} else {
|
||||||
@@ -177,7 +180,7 @@ func (c *ClientConn) Write(b []byte) (int, error) {
|
|||||||
EncodeHeader(data, 23, len(b)+16)
|
EncodeHeader(data, 23, len(b)+16)
|
||||||
c.aead.Seal(data[:5], c.nonce, b, data[:5])
|
c.aead.Seal(data[:5], c.nonce, b, data[:5])
|
||||||
if bytes.Equal(c.nonce, MaxNonce) {
|
if bytes.Equal(c.nonce, MaxNonce) {
|
||||||
c.aead = NewAead(ClientCipher, c.baseKey, data[5:], data[:5])
|
c.aead = NewAEAD(ClientCipher, c.baseKey, data[5:], data[:5])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IncreaseNonce(c.nonce)
|
IncreaseNonce(c.nonce)
|
||||||
@@ -218,7 +221,7 @@ func (c *ClientConn) Read(b []byte) (int, error) {
|
|||||||
if c.random == nil {
|
if c.random == nil {
|
||||||
return 0, errors.New("empty c.random")
|
return 0, errors.New("empty c.random")
|
||||||
}
|
}
|
||||||
c.peerAead = NewAead(ClientCipher, c.baseKey, peerRandomHello, c.random)
|
c.peerAead = NewAEAD(ClientCipher, c.baseKey, peerRandomHello, c.random)
|
||||||
c.peerNonce = make([]byte, 12)
|
c.peerNonce = make([]byte, 12)
|
||||||
}
|
}
|
||||||
if len(c.PeerCache) != 0 {
|
if len(c.PeerCache) != 0 {
|
||||||
@@ -243,7 +246,7 @@ func (c *ClientConn) Read(b []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
var peerAead cipher.AEAD
|
var peerAead cipher.AEAD
|
||||||
if bytes.Equal(c.peerNonce, MaxNonce) {
|
if bytes.Equal(c.peerNonce, MaxNonce) {
|
||||||
peerAead = NewAead(ClientCipher, c.baseKey, peerData, h)
|
peerAead = NewAEAD(ClientCipher, c.baseKey, peerData, h)
|
||||||
}
|
}
|
||||||
_, err = c.peerAead.Open(dst[:0], c.peerNonce, peerData, h)
|
_, err = c.peerAead.Open(dst[:0], c.peerNonce, peerData, h)
|
||||||
if peerAead != nil {
|
if peerAead != nil {
|
||||||
|
@@ -72,7 +72,7 @@ func ReadAndDiscardPaddings(conn net.Conn) (h []byte, t byte, l int, err error)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAead(c byte, secret, salt, info []byte) (aead cipher.AEAD) {
|
func NewAEAD(c byte, secret, salt, info []byte) (aead cipher.AEAD) {
|
||||||
key, _ := hkdf.Key(sha3.New256, secret, salt, string(info), 32)
|
key, _ := hkdf.Key(sha3.New256, secret, salt, string(info), 32)
|
||||||
if c&1 == 1 {
|
if c&1 == 1 {
|
||||||
block, _ := aes.NewCipher(key)
|
block, _ := aes.NewCipher(key)
|
||||||
|
@@ -3,6 +3,7 @@ package encryption
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
|
"crypto/ecdh"
|
||||||
"crypto/mlkem"
|
"crypto/mlkem"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha3"
|
"crypto/sha3"
|
||||||
@@ -27,7 +28,8 @@ type ServerInstance struct {
|
|||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
nfsDKey *mlkem.DecapsulationKey768
|
nfsDKey *mlkem.DecapsulationKey768
|
||||||
hash11 [11]byte // no more capacity
|
hash11 [11]byte // no more capacity
|
||||||
xorKey []byte
|
xorMode uint32
|
||||||
|
xorSKey *ecdh.PrivateKey
|
||||||
minutes time.Duration
|
minutes time.Duration
|
||||||
sessions map[[32]byte]*ServerSession
|
sessions map[[32]byte]*ServerSession
|
||||||
closed bool
|
closed bool
|
||||||
@@ -46,23 +48,24 @@ type ServerConn struct {
|
|||||||
nonce []byte
|
nonce []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ServerInstance) Init(nfsDKeySeed []byte, xor uint32, minutes time.Duration) (err error) {
|
func (i *ServerInstance) Init(nfsDKeySeed, xorSKeyBytes []byte, xorMode, minutes uint32) (err error) {
|
||||||
if i.nfsDKey != nil {
|
if i.nfsDKey != nil {
|
||||||
err = errors.New("already initialized")
|
err = errors.New("already initialized")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
i.nfsDKey, err = mlkem.NewDecapsulationKey768(nfsDKeySeed)
|
if i.nfsDKey, err = mlkem.NewDecapsulationKey768(nfsDKeySeed); err != nil {
|
||||||
if err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
hash32 := sha3.Sum256(i.nfsDKey.EncapsulationKey().Bytes())
|
hash32 := sha3.Sum256(i.nfsDKey.EncapsulationKey().Bytes())
|
||||||
copy(i.hash11[:], hash32[:])
|
copy(i.hash11[:], hash32[:])
|
||||||
if xor > 0 {
|
if xorMode > 0 {
|
||||||
xorKey := sha3.Sum256(i.nfsDKey.EncapsulationKey().Bytes())
|
i.xorMode = xorMode
|
||||||
i.xorKey = xorKey[:]
|
if i.xorSKey, err = ecdh.X25519().NewPrivateKey(xorSKeyBytes); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if minutes > 0 {
|
if minutes > 0 {
|
||||||
i.minutes = minutes
|
i.minutes = time.Duration(minutes) * time.Minute
|
||||||
i.sessions = make(map[[32]byte]*ServerSession)
|
i.sessions = make(map[[32]byte]*ServerSession)
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
@@ -96,8 +99,11 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*ServerConn, error) {
|
|||||||
if i.nfsDKey == nil {
|
if i.nfsDKey == nil {
|
||||||
return nil, errors.New("uninitialized")
|
return nil, errors.New("uninitialized")
|
||||||
}
|
}
|
||||||
if i.xorKey != nil {
|
if i.xorMode > 0 {
|
||||||
conn = NewXorConn(conn, i.xorKey)
|
var err error
|
||||||
|
if conn, err = NewXorConn(conn, i.xorMode, nil, i.xorSKey); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
c := &ServerConn{Conn: conn}
|
c := &ServerConn{Conn: conn}
|
||||||
|
|
||||||
@@ -168,7 +174,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*ServerConn, error) {
|
|||||||
pfsKey, encapsulatedPfsKey := pfsEKey.Encapsulate()
|
pfsKey, encapsulatedPfsKey := pfsEKey.Encapsulate()
|
||||||
c.baseKey = append(pfsKey, nfsKey...)
|
c.baseKey = append(pfsKey, nfsKey...)
|
||||||
|
|
||||||
c.ticket = append(i.hash11[:], NewAead(c.cipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Seal(nil, peerClientHello[:12], []byte("VLESS"), pfsEKeyBytes)...)
|
c.ticket = append(i.hash11[:], NewAEAD(c.cipher, c.baseKey, encapsulatedPfsKey, encapsulatedNfsKey).Seal(nil, peerClientHello[:12], []byte("VLESS"), pfsEKeyBytes)...)
|
||||||
|
|
||||||
paddingLen := crypto.RandBetween(100, 1000)
|
paddingLen := crypto.RandBetween(100, 1000)
|
||||||
|
|
||||||
@@ -222,7 +228,7 @@ func (c *ServerConn) Read(b []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
c.peerRandom = peerTicketHello[32:]
|
c.peerRandom = peerTicketHello[32:]
|
||||||
}
|
}
|
||||||
c.peerAead = NewAead(c.cipher, c.baseKey, c.peerRandom, c.ticket)
|
c.peerAead = NewAEAD(c.cipher, c.baseKey, c.peerRandom, c.ticket)
|
||||||
c.peerNonce = make([]byte, 12)
|
c.peerNonce = make([]byte, 12)
|
||||||
}
|
}
|
||||||
if len(c.PeerCache) != 0 {
|
if len(c.PeerCache) != 0 {
|
||||||
@@ -247,7 +253,7 @@ func (c *ServerConn) Read(b []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
var peerAead cipher.AEAD
|
var peerAead cipher.AEAD
|
||||||
if bytes.Equal(c.peerNonce, MaxNonce) {
|
if bytes.Equal(c.peerNonce, MaxNonce) {
|
||||||
peerAead = NewAead(c.cipher, c.baseKey, peerData, h)
|
peerAead = NewAEAD(c.cipher, c.baseKey, peerData, h)
|
||||||
}
|
}
|
||||||
_, err = c.peerAead.Open(dst[:0], c.peerNonce, peerData, h)
|
_, err = c.peerAead.Open(dst[:0], c.peerNonce, peerData, h)
|
||||||
if peerAead != nil {
|
if peerAead != nil {
|
||||||
@@ -283,7 +289,7 @@ func (c *ServerConn) Write(b []byte) (int, error) {
|
|||||||
EncodeHeader(data, 0, 32)
|
EncodeHeader(data, 0, 32)
|
||||||
rand.Read(data[5 : 5+32])
|
rand.Read(data[5 : 5+32])
|
||||||
EncodeHeader(data[5+32:], 23, len(b)+16)
|
EncodeHeader(data[5+32:], 23, len(b)+16)
|
||||||
c.aead = NewAead(c.cipher, c.baseKey, data[5:5+32], c.peerRandom)
|
c.aead = NewAEAD(c.cipher, c.baseKey, data[5:5+32], c.peerRandom)
|
||||||
c.nonce = make([]byte, 12)
|
c.nonce = make([]byte, 12)
|
||||||
c.aead.Seal(data[:5+32+5], c.nonce, b, data[5+32:5+32+5])
|
c.aead.Seal(data[:5+32+5], c.nonce, b, data[5+32:5+32+5])
|
||||||
} else {
|
} else {
|
||||||
@@ -291,7 +297,7 @@ func (c *ServerConn) Write(b []byte) (int, error) {
|
|||||||
EncodeHeader(data, 23, len(b)+16)
|
EncodeHeader(data, 23, len(b)+16)
|
||||||
c.aead.Seal(data[:5], c.nonce, b, data[:5])
|
c.aead.Seal(data[:5], c.nonce, b, data[:5])
|
||||||
if bytes.Equal(c.nonce, MaxNonce) {
|
if bytes.Equal(c.nonce, MaxNonce) {
|
||||||
c.aead = NewAead(c.cipher, c.baseKey, data[5:], data[:5])
|
c.aead = NewAEAD(c.cipher, c.baseKey, data[5:], data[:5])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IncreaseNonce(c.nonce)
|
IncreaseNonce(c.nonce)
|
||||||
|
@@ -3,13 +3,21 @@ package encryption
|
|||||||
import (
|
import (
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
|
"crypto/ecdh"
|
||||||
|
"crypto/hkdf"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"crypto/sha3"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type XorConn struct {
|
type XorConn struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
|
Divide bool
|
||||||
|
|
||||||
|
head []byte
|
||||||
key []byte
|
key []byte
|
||||||
ctr cipher.Stream
|
ctr cipher.Stream
|
||||||
peerCtr cipher.Stream
|
peerCtr cipher.Stream
|
||||||
@@ -25,8 +33,55 @@ type XorConn struct {
|
|||||||
in_skip int
|
in_skip int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewXorConn(conn net.Conn, key []byte) *XorConn {
|
func NewCTR(key, iv []byte, isServer bool) cipher.Stream {
|
||||||
return &XorConn{Conn: conn, key: key}
|
info := "CLIENT"
|
||||||
|
if isServer {
|
||||||
|
info = "SERVER" // avoids attackers sending traffic back to the client, though the encryption layer has its own protection
|
||||||
|
}
|
||||||
|
key, _ = hkdf.Key(sha3.New256, key, iv, info, 32) // avoids using pKey directly if attackers sent the basepoint, or whaterver they like
|
||||||
|
block, _ := aes.NewCipher(key)
|
||||||
|
return cipher.NewCTR(block, iv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewXorConn(conn net.Conn, mode uint32, pKey *ecdh.PublicKey, sKey *ecdh.PrivateKey) (*XorConn, error) {
|
||||||
|
if mode == 0 || (pKey == nil && sKey == nil) || (pKey != nil && sKey != nil) {
|
||||||
|
return nil, errors.New("invalid parameters")
|
||||||
|
}
|
||||||
|
c := &XorConn{
|
||||||
|
Conn: conn,
|
||||||
|
Divide: mode == 1,
|
||||||
|
isHeader: true,
|
||||||
|
out_header: make([]byte, 0, 5), // important
|
||||||
|
in_header: make([]byte, 0, 5), // important
|
||||||
|
}
|
||||||
|
if pKey != nil {
|
||||||
|
c.head = make([]byte, 16+32)
|
||||||
|
rand.Read(c.head)
|
||||||
|
eSKey, _ := ecdh.X25519().GenerateKey(rand.Reader)
|
||||||
|
NewCTR(pKey.Bytes(), c.head[:16], false).XORKeyStream(c.head[16:], eSKey.PublicKey().Bytes()) // make X25519 public key distinguishable from random bytes
|
||||||
|
c.key, _ = eSKey.ECDH(pKey)
|
||||||
|
c.ctr = NewCTR(c.key, c.head[:16], false)
|
||||||
|
}
|
||||||
|
if sKey != nil {
|
||||||
|
peerHead := make([]byte, 16+32)
|
||||||
|
if _, err := io.ReadFull(c.Conn, peerHead); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
NewCTR(sKey.PublicKey().Bytes(), peerHead[:16], false).XORKeyStream(peerHead[16:], peerHead[16:]) // we don't use buggy elligator, because we have PSK :)
|
||||||
|
ePKey, err := ecdh.X25519().NewPublicKey(peerHead[16:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
key, err := sKey.ECDH(ePKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c.peerCtr = NewCTR(key, peerHead[:16], false)
|
||||||
|
c.head = make([]byte, 16)
|
||||||
|
rand.Read(c.head) // make sure the server always replies random bytes even when received replays, though it is not important
|
||||||
|
c.ctr = NewCTR(key, c.head, true) // the same key links the upload & download, though the encryption layer has its own link
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
//chacha20.NewUnauthenticatedCipher()
|
//chacha20.NewUnauthenticatedCipher()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,13 +90,6 @@ func (c *XorConn) Write(b []byte) (int, error) { // whole one/two records
|
|||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
if !c.out_after0 {
|
if !c.out_after0 {
|
||||||
var iv []byte
|
|
||||||
if c.ctr == nil {
|
|
||||||
block, _ := aes.NewCipher(c.key)
|
|
||||||
iv = make([]byte, 16)
|
|
||||||
rand.Read(iv)
|
|
||||||
c.ctr = cipher.NewCTR(block, iv)
|
|
||||||
}
|
|
||||||
t, l, _ := DecodeHeader(b)
|
t, l, _ := DecodeHeader(b)
|
||||||
if t == 23 { // single 23
|
if t == 23 { // single 23
|
||||||
l = 5
|
l = 5
|
||||||
@@ -49,20 +97,24 @@ func (c *XorConn) Write(b []byte) (int, error) { // whole one/two records
|
|||||||
l += 10
|
l += 10
|
||||||
if t == 0 {
|
if t == 0 {
|
||||||
c.out_after0 = true
|
c.out_after0 = true
|
||||||
c.out_header = make([]byte, 0, 5) // important
|
if c.Divide {
|
||||||
|
l -= 5
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.ctr.XORKeyStream(b[:l], b[:l]) // caller MUST discard b
|
c.ctr.XORKeyStream(b[:l], b[:l]) // caller MUST discard b
|
||||||
if iv != nil {
|
l = len(b)
|
||||||
b = append(iv, b...)
|
if c.head != nil {
|
||||||
|
b = append(c.head, b...)
|
||||||
|
c.head = nil
|
||||||
}
|
}
|
||||||
if _, err := c.Conn.Write(b); err != nil {
|
if _, err := c.Conn.Write(b); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if iv != nil {
|
return l, nil
|
||||||
b = b[16:] // for len(b)
|
|
||||||
}
|
}
|
||||||
return len(b), nil
|
if c.Divide {
|
||||||
|
return c.Conn.Write(b)
|
||||||
}
|
}
|
||||||
for p := b; ; { // for XTLS
|
for p := b; ; { // for XTLS
|
||||||
if len(p) <= c.out_skip {
|
if len(p) <= c.out_skip {
|
||||||
@@ -93,14 +145,12 @@ func (c *XorConn) Read(b []byte) (int, error) { // 5-bytes, data, 5-bytes...
|
|||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
if !c.in_after0 || !c.isHeader {
|
if !c.in_after0 || !c.isHeader {
|
||||||
if c.peerCtr == nil {
|
if c.peerCtr == nil { // for client
|
||||||
peerIv := make([]byte, 16)
|
peerIv := make([]byte, 16)
|
||||||
if _, err := io.ReadFull(c.Conn, peerIv); err != nil {
|
if _, err := io.ReadFull(c.Conn, peerIv); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
block, _ := aes.NewCipher(c.key)
|
c.peerCtr = NewCTR(c.key, peerIv, true)
|
||||||
c.peerCtr = cipher.NewCTR(block, peerIv)
|
|
||||||
c.isHeader = true
|
|
||||||
}
|
}
|
||||||
if _, err := io.ReadFull(c.Conn, b); err != nil {
|
if _, err := io.ReadFull(c.Conn, b); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@@ -117,7 +167,6 @@ func (c *XorConn) Read(b []byte) (int, error) { // 5-bytes, data, 5-bytes...
|
|||||||
c.isHeader = false
|
c.isHeader = false
|
||||||
if t == 0 {
|
if t == 0 {
|
||||||
c.in_after0 = true
|
c.in_after0 = true
|
||||||
c.in_header = make([]byte, 0, 5) // important
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -125,6 +174,9 @@ func (c *XorConn) Read(b []byte) (int, error) { // 5-bytes, data, 5-bytes...
|
|||||||
}
|
}
|
||||||
return len(b), nil
|
return len(b), nil
|
||||||
}
|
}
|
||||||
|
if c.Divide {
|
||||||
|
return c.Conn.Read(b)
|
||||||
|
}
|
||||||
n, err := c.Conn.Read(b)
|
n, err := c.Conn.Read(b)
|
||||||
for p := b[:n]; ; { // for XTLS
|
for p := b[:n]; ; { // for XTLS
|
||||||
if len(p) <= c.in_skip {
|
if len(p) <= c.in_skip {
|
||||||
|
@@ -114,7 +114,7 @@ type Config struct {
|
|||||||
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"`
|
||||||
Fallbacks []*Fallback `protobuf:"bytes,2,rep,name=fallbacks,proto3" json:"fallbacks,omitempty"`
|
Fallbacks []*Fallback `protobuf:"bytes,2,rep,name=fallbacks,proto3" json:"fallbacks,omitempty"`
|
||||||
Decryption string `protobuf:"bytes,3,opt,name=decryption,proto3" json:"decryption,omitempty"`
|
Decryption string `protobuf:"bytes,3,opt,name=decryption,proto3" json:"decryption,omitempty"`
|
||||||
Xor uint32 `protobuf:"varint,4,opt,name=xor,proto3" json:"xor,omitempty"`
|
XorMode uint32 `protobuf:"varint,4,opt,name=xorMode,proto3" json:"xorMode,omitempty"`
|
||||||
Minutes uint32 `protobuf:"varint,5,opt,name=minutes,proto3" json:"minutes,omitempty"`
|
Minutes uint32 `protobuf:"varint,5,opt,name=minutes,proto3" json:"minutes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,9 +169,9 @@ func (x *Config) GetDecryption() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) GetXor() uint32 {
|
func (x *Config) GetXorMode() uint32 {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Xor
|
return x.XorMode
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -199,7 +199,7 @@ 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, 0xcc, 0x01,
|
0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x78, 0x76, 0x65, 0x72, 0x22, 0xd4, 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,
|
||||||
@@ -210,16 +210,17 @@ var file_proxy_vless_inbound_config_proto_rawDesc = []byte{
|
|||||||
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,
|
||||||
0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03,
|
0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03,
|
||||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e,
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e,
|
||||||
0x12, 0x10, 0x0a, 0x03, 0x78, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x78,
|
0x12, 0x18, 0x0a, 0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28,
|
||||||
0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20,
|
0x0d, 0x52, 0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69,
|
||||||
0x01, 0x28, 0x0d, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x42, 0x6a, 0x0a, 0x1c,
|
0x6e, 0x75, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6d, 0x69, 0x6e,
|
||||||
0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76,
|
0x75, 0x74, 0x65, 0x73, 0x42, 0x6a, 0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
||||||
0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2d,
|
0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62,
|
||||||
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f,
|
0x6f, 0x75, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
|
||||||
0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f,
|
0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72,
|
||||||
0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0xaa, 0x02, 0x18,
|
0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x69, 0x6e,
|
||||||
0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73,
|
0x62, 0x6f, 0x75, 0x6e, 0x64, 0xaa, 0x02, 0x18, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f,
|
||||||
0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
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 (
|
||||||
|
@@ -22,6 +22,6 @@ message Config {
|
|||||||
repeated Fallback fallbacks = 2;
|
repeated Fallback fallbacks = 2;
|
||||||
|
|
||||||
string decryption = 3;
|
string decryption = 3;
|
||||||
uint32 xor = 4;
|
uint32 xorMode = 4;
|
||||||
uint32 minutes = 5;
|
uint32 minutes = 5;
|
||||||
}
|
}
|
||||||
|
@@ -12,7 +12,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/pires/go-proxyproto"
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/buf"
|
"github.com/xtls/xray-core/common/buf"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
@@ -32,7 +31,6 @@ import (
|
|||||||
"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/proxy/vless/encryption"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
|
||||||
"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"
|
||||||
@@ -86,10 +84,11 @@ func New(ctx context.Context, config *Config, dc dns.Client, validator vless.Val
|
|||||||
validator: validator,
|
validator: validator,
|
||||||
}
|
}
|
||||||
|
|
||||||
d, _ := base64.RawURLEncoding.DecodeString(config.Decryption)
|
if s := strings.Split(config.Decryption, "."); len(s) == 2 {
|
||||||
if len(d) == 64 {
|
nfsDKeySeed, _ := base64.RawURLEncoding.DecodeString(s[0])
|
||||||
|
xorSKeyBytes, _ := base64.RawURLEncoding.DecodeString(s[1])
|
||||||
handler.decryption = &encryption.ServerInstance{}
|
handler.decryption = &encryption.ServerInstance{}
|
||||||
if err := handler.decryption.Init(d, config.Xor, time.Duration(config.Minutes)*time.Minute); err != nil {
|
if err := handler.decryption.Init(nfsDKeySeed, xorSKeyBytes, config.XorMode, config.Minutes); err != nil {
|
||||||
return nil, errors.New("failed to use mlkem768seed").Base(err).AtError()
|
return nil, errors.New("failed to use mlkem768seed").Base(err).AtError()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -501,12 +500,8 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
|||||||
case protocol.RequestCommandTCP:
|
case protocol.RequestCommandTCP:
|
||||||
if serverConn, ok := connection.(*encryption.ServerConn); ok {
|
if serverConn, ok := connection.(*encryption.ServerConn); ok {
|
||||||
peerCache = &serverConn.PeerCache
|
peerCache = &serverConn.PeerCache
|
||||||
_, ok0 := serverConn.Conn.(*encryption.XorConn)
|
if xorConn, ok := serverConn.Conn.(*encryption.XorConn); (ok && !xorConn.Divide) || !proxy.IsRAWTransport(iConn) {
|
||||||
_, ok1 := iConn.(*proxyproto.Conn)
|
inbound.CanSpliceCopy = 3 // full-random xorConn / non-RAW transport can not use Linux Splice
|
||||||
_, ok2 := iConn.(*net.TCPConn)
|
|
||||||
_, ok3 := iConn.(*internet.UnixConnWrapper)
|
|
||||||
if ok0 || (!ok1 && !ok2 && !ok3) {
|
|
||||||
inbound.CanSpliceCopy = 3 // xorConn/non-RAW can not use Linux Splice
|
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@@ -6,10 +6,10 @@ import (
|
|||||||
gotls "crypto/tls"
|
gotls "crypto/tls"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/pires/go-proxyproto"
|
|
||||||
utls "github.com/refraction-networking/utls"
|
utls "github.com/refraction-networking/utls"
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/buf"
|
"github.com/xtls/xray-core/common/buf"
|
||||||
@@ -69,10 +69,11 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
a := handler.serverPicker.PickServer().PickUser().Account.(*vless.MemoryAccount)
|
a := handler.serverPicker.PickServer().PickUser().Account.(*vless.MemoryAccount)
|
||||||
e, _ := base64.RawURLEncoding.DecodeString(a.Encryption)
|
if s := strings.Split(a.Encryption, "."); len(s) == 2 {
|
||||||
if len(e) == 1184 {
|
nfsEKeyBytes, _ := base64.RawURLEncoding.DecodeString(s[0])
|
||||||
|
xorPKeyBytes, _ := base64.RawURLEncoding.DecodeString(s[1])
|
||||||
handler.encryption = &encryption.ClientInstance{}
|
handler.encryption = &encryption.ClientInstance{}
|
||||||
if err := handler.encryption.Init(e, a.Xor, time.Duration(a.Minutes)*time.Minute); err != nil {
|
if err := handler.encryption.Init(nfsEKeyBytes, xorPKeyBytes, a.XorMode, a.Minutes); err != nil {
|
||||||
return nil, errors.New("failed to use mlkem768client").Base(err).AtError()
|
return nil, errors.New("failed to use mlkem768client").Base(err).AtError()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,12 +163,8 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
case protocol.RequestCommandTCP:
|
case protocol.RequestCommandTCP:
|
||||||
if clientConn, ok := conn.(*encryption.ClientConn); ok {
|
if clientConn, ok := conn.(*encryption.ClientConn); ok {
|
||||||
peerCache = &clientConn.PeerCache
|
peerCache = &clientConn.PeerCache
|
||||||
_, ok0 := clientConn.Conn.(*encryption.XorConn)
|
if xorConn, ok := clientConn.Conn.(*encryption.XorConn); (ok && !xorConn.Divide) || !proxy.IsRAWTransport(iConn) {
|
||||||
_, ok1 := iConn.(*proxyproto.Conn)
|
ob.CanSpliceCopy = 3 // full-random xorConn / non-RAW transport can not use Linux Splice
|
||||||
_, ok2 := iConn.(*net.TCPConn)
|
|
||||||
_, ok3 := iConn.(*internet.UnixConnWrapper)
|
|
||||||
if ok0 || (!ok1 && !ok2 && !ok3) {
|
|
||||||
ob.CanSpliceCopy = 3 // xorConn/non-RAW can not use Linux Splice
|
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user