Clean legacy vmess (#2199)

* Remove legacy Vmess

* validators

* protos
This commit is contained in:
yuhan6665 2023-06-12 10:32:25 -04:00 committed by GitHub
parent bf4b1fab3c
commit 9112cfd39c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 150 additions and 761 deletions

View File

@ -6,8 +6,7 @@
"clients": [ "clients": [
{ {
"id": "1eb6e917-774b-4a84-aff6-b058577c60a5", "id": "1eb6e917-774b-4a84-aff6-b058577c60a5",
"level": 1, "level": 1
"alterId": 64
} }
] ]
} }

View File

@ -30,11 +30,10 @@ func (c RequestCommand) TransferType() TransferType {
} }
const ( const (
// RequestOptionChunkStream indicates request payload is chunked. Each chunk consists of length, authentication and payload. // [DEPRECATED 2023-06] RequestOptionChunkStream indicates request payload is chunked. Each chunk consists of length, authentication and payload.
RequestOptionChunkStream bitmask.Byte = 0x01 RequestOptionChunkStream bitmask.Byte = 0x01
// RequestOptionConnectionReuse indicates client side expects to reuse the connection. // 0x02 legacy setting
RequestOptionConnectionReuse bitmask.Byte = 0x02
RequestOptionChunkMasking bitmask.Byte = 0x04 RequestOptionChunkMasking bitmask.Byte = 0x04
@ -76,7 +75,6 @@ type CommandSwitchAccount struct {
Port net.Port Port net.Port
ID uuid.UUID ID uuid.UUID
Level uint32 Level uint32
AlterIds uint16
ValidMin byte ValidMin byte
} }

View File

@ -24,7 +24,6 @@ type SecurityType int32
const ( const (
SecurityType_UNKNOWN SecurityType = 0 SecurityType_UNKNOWN SecurityType = 0
SecurityType_LEGACY SecurityType = 1
SecurityType_AUTO SecurityType = 2 SecurityType_AUTO SecurityType = 2
SecurityType_AES128_GCM SecurityType = 3 SecurityType_AES128_GCM SecurityType = 3
SecurityType_CHACHA20_POLY1305 SecurityType = 4 SecurityType_CHACHA20_POLY1305 SecurityType = 4
@ -36,7 +35,6 @@ const (
var ( var (
SecurityType_name = map[int32]string{ SecurityType_name = map[int32]string{
0: "UNKNOWN", 0: "UNKNOWN",
1: "LEGACY",
2: "AUTO", 2: "AUTO",
3: "AES128_GCM", 3: "AES128_GCM",
4: "CHACHA20_POLY1305", 4: "CHACHA20_POLY1305",
@ -45,7 +43,6 @@ var (
} }
SecurityType_value = map[string]int32{ SecurityType_value = map[string]int32{
"UNKNOWN": 0, "UNKNOWN": 0,
"LEGACY": 1,
"AUTO": 2, "AUTO": 2,
"AES128_GCM": 3, "AES128_GCM": 3,
"CHACHA20_POLY1305": 4, "CHACHA20_POLY1305": 4,
@ -139,20 +136,19 @@ var file_common_protocol_headers_proto_rawDesc = []byte{
0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x63, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x63,
0x75, 0x72, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x2a, 0x75, 0x72, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x2a,
0x6c, 0x0a, 0x0c, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x60, 0x0a, 0x0c, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12,
0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04,
0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x55, 0x54, 0x4f, 0x41, 0x55, 0x54, 0x4f, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x41, 0x45, 0x53, 0x31, 0x32, 0x38,
0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x41, 0x45, 0x53, 0x31, 0x32, 0x38, 0x5f, 0x47, 0x43, 0x4d, 0x5f, 0x47, 0x43, 0x4d, 0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x48, 0x41, 0x43, 0x48, 0x41,
0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x48, 0x41, 0x43, 0x48, 0x41, 0x32, 0x30, 0x5f, 0x50, 0x32, 0x30, 0x5f, 0x50, 0x4f, 0x4c, 0x59, 0x31, 0x33, 0x30, 0x35, 0x10, 0x04, 0x12, 0x08, 0x0a,
0x4f, 0x4c, 0x59, 0x31, 0x33, 0x30, 0x35, 0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x05, 0x12, 0x08, 0x0a, 0x04, 0x5a, 0x45, 0x52, 0x4f, 0x10,
0x45, 0x10, 0x05, 0x12, 0x08, 0x0a, 0x04, 0x5a, 0x45, 0x52, 0x4f, 0x10, 0x06, 0x42, 0x5e, 0x0a, 0x06, 0x42, 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f,
0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x50, 0x01, 0x5a,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x50, 0x01, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73,
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0xaa, 0x02, 0x14, 0x58, 0x72, 0x61,
0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0xaa, 0x02, 0x14, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x79, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f,
0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x62, 0x06, 0x70, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (

View File

@ -8,11 +8,10 @@ option java_multiple_files = true;
enum SecurityType { enum SecurityType {
UNKNOWN = 0; UNKNOWN = 0;
LEGACY = 1;
AUTO = 2; AUTO = 2;
AES128_GCM = 3; AES128_GCM = 3;
CHACHA20_POLY1305 = 4; CHACHA20_POLY1305 = 4;
NONE = 5; NONE = 5; // [DEPRECATED 2023-06]
ZERO = 6; ZERO = 6;
} }

View File

@ -1,9 +1,7 @@
package protocol package protocol
import ( import (
"crypto/hmac"
"crypto/md5" "crypto/md5"
"hash"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/uuid" "github.com/xtls/xray-core/common/uuid"
@ -13,12 +11,6 @@ const (
IDBytesLen = 16 IDBytesLen = 16
) )
type IDHash func(key []byte) hash.Hash
func DefaultIDHash(key []byte) hash.Hash {
return hmac.New(md5.New, key)
}
// The ID of en entity, in the form of a UUID. // The ID of en entity, in the form of a UUID.
type ID struct { type ID struct {
uuid uuid.UUID uuid uuid.UUID
@ -55,28 +47,3 @@ func NewID(uuid uuid.UUID) *ID {
md5hash.Sum(id.cmdKey[:0]) md5hash.Sum(id.cmdKey[:0])
return id return id
} }
func nextID(u *uuid.UUID) uuid.UUID {
md5hash := md5.New()
common.Must2(md5hash.Write(u.Bytes()))
common.Must2(md5hash.Write([]byte("16167dc8-16b6-4e6d-b8bb-65dd68113a81")))
var newid uuid.UUID
for {
md5hash.Sum(newid[:0])
if !newid.Equals(u) {
return newid
}
common.Must2(md5hash.Write([]byte("533eff8a-4113-4b10-b5ce-0f5d76b98cd2")))
}
}
func NewAlterIDs(primary *ID, alterIDCount uint16) []*ID {
alterIDs := make([]*ID, alterIDCount)
prevID := primary.UUID()
for idx := range alterIDs {
newid := nextID(&prevID)
alterIDs[idx] = NewID(newid)
prevID = newid
}
return alterIDs
}

View File

@ -15,7 +15,6 @@ import (
type VMessAccount struct { type VMessAccount struct {
ID string `json:"id"` ID string `json:"id"`
AlterIds uint16 `json:"alterId"`
Security string `json:"security"` Security string `json:"security"`
Experiments string `json:"experiments"` Experiments string `json:"experiments"`
} }
@ -39,7 +38,6 @@ func (a *VMessAccount) Build() *vmess.Account {
} }
return &vmess.Account{ return &vmess.Account{
Id: a.ID, Id: a.ID,
AlterId: uint32(a.AlterIds),
SecuritySettings: &protocol.SecurityConfig{ SecuritySettings: &protocol.SecurityConfig{
Type: st, Type: st,
}, },
@ -63,14 +61,12 @@ type FeaturesConfig struct {
} }
type VMessDefaultConfig struct { type VMessDefaultConfig struct {
AlterIDs uint16 `json:"alterId"`
Level byte `json:"level"` Level byte `json:"level"`
} }
// Build implements Buildable // Build implements Buildable
func (c *VMessDefaultConfig) Build() *inbound.DefaultConfig { func (c *VMessDefaultConfig) Build() *inbound.DefaultConfig {
config := new(inbound.DefaultConfig) config := new(inbound.DefaultConfig)
config.AlterId = uint32(c.AlterIDs)
config.Level = uint32(c.Level) config.Level = uint32(c.Level)
return config return config
} }
@ -80,14 +76,11 @@ type VMessInboundConfig struct {
Features *FeaturesConfig `json:"features"` Features *FeaturesConfig `json:"features"`
Defaults *VMessDefaultConfig `json:"default"` Defaults *VMessDefaultConfig `json:"default"`
DetourConfig *VMessDetourConfig `json:"detour"` DetourConfig *VMessDetourConfig `json:"detour"`
SecureOnly bool `json:"disableInsecureEncryption"`
} }
// Build implements Buildable // Build implements Buildable
func (c *VMessInboundConfig) Build() (proto.Message, error) { func (c *VMessInboundConfig) Build() (proto.Message, error) {
config := &inbound.Config{ config := &inbound.Config{}
SecureEncryptionOnly: c.SecureOnly,
}
if c.Defaults != nil { if c.Defaults != nil {
config.Default = c.Defaults.Build() config.Default = c.Defaults.Build()

View File

@ -105,7 +105,6 @@ func TestVMessInbound(t *testing.T) {
Detour: &inbound.DetourConfig{ Detour: &inbound.DetourConfig{
To: "tag_to_detour", To: "tag_to_detour",
}, },
SecureEncryptionOnly: true,
}, },
}, },
}) })

View File

@ -3,7 +3,6 @@ package vmess
import ( import (
"strings" "strings"
"github.com/xtls/xray-core/common/dice"
"github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/uuid" "github.com/xtls/xray-core/common/uuid"
) )
@ -12,8 +11,6 @@ import (
type MemoryAccount struct { type MemoryAccount struct {
// ID is the main ID of the account. // ID is the main ID of the account.
ID *protocol.ID ID *protocol.ID
// AlterIDs are the alternative IDs of the account.
AlterIDs []*protocol.ID
// Security type of the account. Used for client connections. // Security type of the account. Used for client connections.
Security protocol.SecurityType Security protocol.SecurityType
@ -21,21 +18,12 @@ type MemoryAccount struct {
NoTerminationSignal bool NoTerminationSignal bool
} }
// AnyValidID returns an ID that is either the main ID or one of the alternative IDs if any.
func (a *MemoryAccount) AnyValidID() *protocol.ID {
if len(a.AlterIDs) == 0 {
return a.ID
}
return a.AlterIDs[dice.Roll(len(a.AlterIDs))]
}
// Equals implements protocol.Account. // Equals implements protocol.Account.
func (a *MemoryAccount) Equals(account protocol.Account) bool { func (a *MemoryAccount) Equals(account protocol.Account) bool {
vmessAccount, ok := account.(*MemoryAccount) vmessAccount, ok := account.(*MemoryAccount)
if !ok { if !ok {
return false return false
} }
// TODO: handle AlterIds difference
return a.ID.Equals(vmessAccount.ID) return a.ID.Equals(vmessAccount.ID)
} }
@ -55,7 +43,6 @@ func (a *Account) AsAccount() (protocol.Account, error) {
} }
return &MemoryAccount{ return &MemoryAccount{
ID: protoID, ID: protoID,
AlterIDs: protocol.NewAlterIDs(protoID, uint16(a.AlterId)),
Security: a.SecuritySettings.GetSecurityType(), Security: a.SecuritySettings.GetSecurityType(),
AuthenticatedLengthExperiment: AuthenticatedLength, AuthenticatedLengthExperiment: AuthenticatedLength,
NoTerminationSignal: NoTerminationSignal, NoTerminationSignal: NoTerminationSignal,

View File

@ -29,8 +29,6 @@ type Account struct {
// ID of the account, in the form of a UUID, e.g., // ID of the account, in the form of a UUID, e.g.,
// "66ad4540-b58c-4ad2-9926-ea63445a9b57". // "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"`
// Number of alternative IDs. Client and server must share the same number.
AlterId uint32 `protobuf:"varint,2,opt,name=alter_id,json=alterId,proto3" json:"alter_id,omitempty"`
// Security settings. Only applies to client side. // Security settings. Only applies to client side.
SecuritySettings *protocol.SecurityConfig `protobuf:"bytes,3,opt,name=security_settings,json=securitySettings,proto3" json:"security_settings,omitempty"` SecuritySettings *protocol.SecurityConfig `protobuf:"bytes,3,opt,name=security_settings,json=securitySettings,proto3" json:"security_settings,omitempty"`
// Define tests enabled for this account // Define tests enabled for this account
@ -76,13 +74,6 @@ func (x *Account) GetId() string {
return "" return ""
} }
func (x *Account) GetAlterId() uint32 {
if x != nil {
return x.AlterId
}
return 0
}
func (x *Account) GetSecuritySettings() *protocol.SecurityConfig { func (x *Account) GetSecuritySettings() *protocol.SecurityConfig {
if x != nil { if x != nil {
return x.SecuritySettings return x.SecuritySettings
@ -104,24 +95,22 @@ var file_proxy_vmess_account_proto_rawDesc = []byte{
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, 0x6d, 0x65, 0x73, 0x73, 0x1a, 0x1d, 0x63, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x1a, 0x1d, 0x63,
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x68, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x68,
0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xac, 0x01, 0x0a, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x91, 0x01, 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, 0x19, 0x0a, 0x08, 0x61, 0x6c, 0x74, 0x65, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x51, 0x0a, 0x11, 0x73, 0x65, 0x63, 0x75,
0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20,
0x72, 0x49, 0x64, 0x12, 0x51, 0x0a, 0x11, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72,
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x10, 0x73, 0x65, 0x63, 0x75, 0x72,
0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x69, 0x74, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x74,
0x6e, 0x66, 0x69, 0x67, 0x52, 0x10, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x65, 0x65, 0x73, 0x74, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01,
0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x73, 0x5f, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x65, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f,
0x65, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, 0x52, 0x0a, 0x14, 0x63, 0x78, 0x79, 0x2e, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68,
0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6d, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79,
0x65, 0x73, 0x73, 0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6d, 0x65, 0x73,
0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x73, 0xaa, 0x02, 0x10, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56,
0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6d, 0x65, 0x73, 0x73, 0xaa, 0x02, 0x10, 0x58, 0x6d, 0x65, 0x73, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6d, 0x65, 0x73, 0x73, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (

View File

@ -12,8 +12,6 @@ message Account {
// ID of the account, in the form of a UUID, e.g., // ID of the account, in the form of a UUID, e.g.,
// "66ad4540-b58c-4ad2-9926-ea63445a9b57". // "66ad4540-b58c-4ad2-9926-ea63445a9b57".
string id = 1; string id = 1;
// Number of alternative IDs. Client and server must share the same number.
uint32 alter_id = 2;
// Security settings. Only applies to client side. // Security settings. Only applies to client side.
xray.common.protocol.SecurityConfig security_settings = 3; xray.common.protocol.SecurityConfig security_settings = 3;
// Define tests enabled for this account // Define tests enabled for this account

View File

@ -17,6 +17,7 @@ func Authenticate(b []byte) uint32 {
return fnv1hash.Sum32() return fnv1hash.Sum32()
} }
// [DEPRECATED 2023-06]
type NoOpAuthenticator struct{} type NoOpAuthenticator struct{}
func (NoOpAuthenticator) NonceSize() int { func (NoOpAuthenticator) NonceSize() int {
@ -37,34 +38,6 @@ func (NoOpAuthenticator) Open(dst, nonce, ciphertext, additionalData []byte) ([]
return append(dst[:0], ciphertext...), nil return append(dst[:0], ciphertext...), nil
} }
// FnvAuthenticator is an AEAD based on Fnv hash.
type FnvAuthenticator struct{}
// NonceSize implements AEAD.NonceSize().
func (*FnvAuthenticator) NonceSize() int {
return 0
}
// Overhead impelements AEAD.Overhead().
func (*FnvAuthenticator) Overhead() int {
return 4
}
// Seal implements AEAD.Seal().
func (*FnvAuthenticator) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
dst = append(dst, 0, 0, 0, 0)
binary.BigEndian.PutUint32(dst, Authenticate(plaintext))
return append(dst, plaintext...)
}
// Open implements AEAD.Open().
func (*FnvAuthenticator) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
if binary.BigEndian.Uint32(ciphertext[:4]) != Authenticate(ciphertext[4:]) {
return dst, newError("invalid authentication")
}
return append(dst, ciphertext[4:]...), nil
}
// GenerateChacha20Poly1305Key generates a 32-byte key from a given 16-byte array. // GenerateChacha20Poly1305Key generates a 32-byte key from a given 16-byte array.
func GenerateChacha20Poly1305Key(b []byte) []byte { func GenerateChacha20Poly1305Key(b []byte) []byte {
key := make([]byte, 32) key := make([]byte, 32)

View File

@ -1,26 +0,0 @@
package encoding_test
import (
"crypto/rand"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/xtls/xray-core/common"
. "github.com/xtls/xray-core/proxy/vmess/encoding"
)
func TestFnvAuth(t *testing.T) {
fnvAuth := new(FnvAuthenticator)
expectedText := make([]byte, 256)
_, err := rand.Read(expectedText)
common.Must(err)
buffer := make([]byte, 512)
b := fnvAuth.Seal(buffer[:0], nil, expectedText, nil)
b, err = fnvAuth.Open(buffer[:0], nil, b, nil)
common.Must(err)
if r := cmp.Diff(b, expectedText); r != "" {
t.Error(r)
}
}

View File

@ -5,11 +5,9 @@ import (
"context" "context"
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/md5"
"crypto/rand" "crypto/rand"
"crypto/sha256" "crypto/sha256"
"encoding/binary" "encoding/binary"
"hash"
"hash/fnv" "hash/fnv"
"io" "io"
@ -20,24 +18,13 @@ import (
"github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/dice"
"github.com/xtls/xray-core/common/drain" "github.com/xtls/xray-core/common/drain"
"github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/proxy/vmess" "github.com/xtls/xray-core/proxy/vmess"
vmessaead "github.com/xtls/xray-core/proxy/vmess/aead" vmessaead "github.com/xtls/xray-core/proxy/vmess/aead"
"golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/chacha20poly1305"
) )
func hashTimestamp(h hash.Hash, t protocol.Timestamp) []byte {
common.Must2(serial.WriteUint64(h, uint64(t)))
common.Must2(serial.WriteUint64(h, uint64(t)))
common.Must2(serial.WriteUint64(h, uint64(t)))
common.Must2(serial.WriteUint64(h, uint64(t)))
return h.Sum(nil)
}
// ClientSession stores connection session info for VMess client. // ClientSession stores connection session info for VMess client.
type ClientSession struct { type ClientSession struct {
isAEAD bool
idHash protocol.IDHash
requestBodyKey [16]byte requestBodyKey [16]byte
requestBodyIV [16]byte requestBodyIV [16]byte
responseBodyKey [16]byte responseBodyKey [16]byte
@ -49,11 +36,8 @@ type ClientSession struct {
} }
// NewClientSession creates a new ClientSession. // NewClientSession creates a new ClientSession.
func NewClientSession(ctx context.Context, isAEAD bool, idHash protocol.IDHash, behaviorSeed int64) *ClientSession { func NewClientSession(ctx context.Context, behaviorSeed int64) *ClientSession {
session := &ClientSession{ session := &ClientSession{}
isAEAD: isAEAD,
idHash: idHash,
}
randomBytes := make([]byte, 33) // 16 + 16 + 1 randomBytes := make([]byte, 33) // 16 + 16 + 1
common.Must2(rand.Read(randomBytes)) common.Must2(rand.Read(randomBytes))
@ -61,15 +45,10 @@ func NewClientSession(ctx context.Context, isAEAD bool, idHash protocol.IDHash,
copy(session.requestBodyIV[:], randomBytes[16:32]) copy(session.requestBodyIV[:], randomBytes[16:32])
session.responseHeader = randomBytes[32] session.responseHeader = randomBytes[32]
if !session.isAEAD {
session.responseBodyKey = md5.Sum(session.requestBodyKey[:])
session.responseBodyIV = md5.Sum(session.requestBodyIV[:])
} else {
BodyKey := sha256.Sum256(session.requestBodyKey[:]) BodyKey := sha256.Sum256(session.requestBodyKey[:])
copy(session.responseBodyKey[:], BodyKey[:16]) copy(session.responseBodyKey[:], BodyKey[:16])
BodyIV := sha256.Sum256(session.requestBodyIV[:]) BodyIV := sha256.Sum256(session.requestBodyIV[:])
copy(session.responseBodyIV[:], BodyIV[:16]) copy(session.responseBodyIV[:], BodyIV[:16])
}
{ {
var err error var err error
session.readDrainer, err = drain.NewBehaviorSeedLimitedDrainer(behaviorSeed, 18, 3266, 64) session.readDrainer, err = drain.NewBehaviorSeedLimitedDrainer(behaviorSeed, 18, 3266, 64)
@ -83,13 +62,7 @@ func NewClientSession(ctx context.Context, isAEAD bool, idHash protocol.IDHash,
} }
func (c *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writer io.Writer) error { func (c *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writer io.Writer) error {
timestamp := protocol.NewTimestampGenerator(protocol.NowTime(), 30)()
account := header.User.Account.(*vmess.MemoryAccount) account := header.User.Account.(*vmess.MemoryAccount)
if !c.isAEAD {
idHash := c.idHash(account.AnyValidID().Bytes())
common.Must2(serial.WriteUint64(idHash, uint64(timestamp)))
common.Must2(writer.Write(idHash.Sum(nil)))
}
buffer := buf.New() buffer := buf.New()
defer buffer.Release() defer buffer.Release()
@ -121,17 +94,10 @@ func (c *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writ
fnv1a.Sum(hashBytes[:0]) fnv1a.Sum(hashBytes[:0])
} }
if !c.isAEAD {
iv := hashTimestamp(md5.New(), timestamp)
aesStream := crypto.NewAesEncryptionStream(account.ID.CmdKey(), iv)
aesStream.XORKeyStream(buffer.Bytes(), buffer.Bytes())
common.Must2(writer.Write(buffer.Bytes()))
} else {
var fixedLengthCmdKey [16]byte var fixedLengthCmdKey [16]byte
copy(fixedLengthCmdKey[:], account.ID.CmdKey()) copy(fixedLengthCmdKey[:], account.ID.CmdKey())
vmessout := vmessaead.SealVMessAEADHeader(fixedLengthCmdKey, buffer.Bytes()) vmessout := vmessaead.SealVMessAEADHeader(fixedLengthCmdKey, buffer.Bytes())
common.Must2(io.Copy(writer, bytes.NewReader(vmessout))) common.Must2(io.Copy(writer, bytes.NewReader(vmessout)))
}
return nil return nil
} }
@ -165,19 +131,6 @@ func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
} }
return buf.NewWriter(writer), nil return buf.NewWriter(writer), nil
case protocol.SecurityType_LEGACY:
aesStream := crypto.NewAesEncryptionStream(c.requestBodyKey[:], c.requestBodyIV[:])
cryptionWriter := crypto.NewCryptionWriter(aesStream, writer)
if request.Option.Has(protocol.RequestOptionChunkStream) {
auth := &crypto.AEADAuthenticator{
AEAD: new(FnvAuthenticator),
NonceGenerator: crypto.GenerateEmptyBytes(),
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
}
return crypto.NewAuthenticationWriter(auth, sizeParser, cryptionWriter, request.Command.TransferType(), padding), nil
}
return &buf.SequentialWriter{Writer: cryptionWriter}, nil
case protocol.SecurityType_AES128_GCM: case protocol.SecurityType_AES128_GCM:
aead := crypto.NewAesGcm(c.requestBodyKey[:]) aead := crypto.NewAesGcm(c.requestBodyKey[:])
auth := &crypto.AEADAuthenticator{ auth := &crypto.AEADAuthenticator{
@ -225,10 +178,6 @@ func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
} }
func (c *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.ResponseHeader, error) { func (c *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.ResponseHeader, error) {
if !c.isAEAD {
aesStream := crypto.NewAesDecryptionStream(c.responseBodyKey[:], c.responseBodyIV[:])
c.responseReader = crypto.NewCryptionReader(aesStream, reader)
} else {
aeadResponseHeaderLengthEncryptionKey := vmessaead.KDF16(c.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderLenKey) aeadResponseHeaderLengthEncryptionKey := vmessaead.KDF16(c.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderLenKey)
aeadResponseHeaderLengthEncryptionIV := vmessaead.KDF(c.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderLenIV)[:12] aeadResponseHeaderLengthEncryptionIV := vmessaead.KDF(c.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderLenIV)[:12]
@ -272,7 +221,6 @@ func (c *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.Respon
} else { // nolint: golint } else { // nolint: golint
c.responseReader = bytes.NewReader(decryptedResponseHeaderBuffer) c.responseReader = bytes.NewReader(decryptedResponseHeaderBuffer)
} }
}
buffer := buf.StackNew() buffer := buf.StackNew()
defer buffer.Release() defer buffer.Release()
@ -302,10 +250,8 @@ func (c *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.Respon
header.Command = command header.Command = command
} }
} }
if c.isAEAD {
aesStream := crypto.NewAesDecryptionStream(c.responseBodyKey[:], c.responseBodyIV[:]) aesStream := crypto.NewAesDecryptionStream(c.responseBodyKey[:], c.responseBodyIV[:])
c.responseReader = crypto.NewCryptionReader(aesStream, reader) c.responseReader = crypto.NewCryptionReader(aesStream, reader)
}
return header, nil return header, nil
} }
@ -340,17 +286,6 @@ func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read
} }
return buf.NewReader(reader), nil return buf.NewReader(reader), nil
case protocol.SecurityType_LEGACY:
if request.Option.Has(protocol.RequestOptionChunkStream) {
auth := &crypto.AEADAuthenticator{
AEAD: new(FnvAuthenticator),
NonceGenerator: crypto.GenerateEmptyBytes(),
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
}
return crypto.NewAuthenticationReader(auth, sizeParser, c.responseReader, request.Command.TransferType(), padding), nil
}
return buf.NewReader(c.responseReader), nil
case protocol.SecurityType_AES128_GCM: case protocol.SecurityType_AES128_GCM:
aead := crypto.NewAesGcm(c.responseBodyKey[:]) aead := crypto.NewAesGcm(c.responseBodyKey[:])

View File

@ -101,7 +101,7 @@ func (f *CommandSwitchAccountFactory) Marshal(command interface{}, writer io.Wri
idBytes := cmd.ID.Bytes() idBytes := cmd.ID.Bytes()
common.Must2(writer.Write(idBytes)) common.Must2(writer.Write(idBytes))
common.Must2(serial.WriteUint16(writer, cmd.AlterIds)) common.Must2(serial.WriteUint16(writer, 0)) // compatible with legacy alterId
common.Must2(writer.Write([]byte{byte(cmd.Level)})) common.Must2(writer.Write([]byte{byte(cmd.Level)}))
common.Must2(writer.Write([]byte{cmd.ValidMin})) common.Must2(writer.Write([]byte{cmd.ValidMin}))
@ -130,12 +130,7 @@ func (f *CommandSwitchAccountFactory) Unmarshal(data []byte) (interface{}, error
return nil, ErrInsufficientLength return nil, ErrInsufficientLength
} }
cmd.ID, _ = uuid.ParseBytes(data[idStart : idStart+16]) cmd.ID, _ = uuid.ParseBytes(data[idStart : idStart+16])
alterIDStart := idStart + 16 levelStart := idStart + 16 + 2
if len(data) < alterIDStart+2 {
return nil, ErrInsufficientLength
}
cmd.AlterIds = binary.BigEndian.Uint16(data[alterIDStart : alterIDStart+2])
levelStart := alterIDStart + 2
if len(data) < levelStart+1 { if len(data) < levelStart+1 {
return nil, ErrInsufficientLength return nil, ErrInsufficientLength
} }

View File

@ -16,7 +16,6 @@ func TestSwitchAccount(t *testing.T) {
sa := &protocol.CommandSwitchAccount{ sa := &protocol.CommandSwitchAccount{
Port: 1234, Port: 1234,
ID: uuid.New(), ID: uuid.New(),
AlterIds: 1024,
Level: 128, Level: 128,
ValidMin: 16, ValidMin: 16,
} }
@ -40,7 +39,6 @@ func TestSwitchAccountBugOffByOne(t *testing.T) {
sa := &protocol.CommandSwitchAccount{ sa := &protocol.CommandSwitchAccount{
Port: 1234, Port: 1234,
ID: uuid.New(), ID: uuid.New(),
AlterIds: 1024,
Level: 128, Level: 128,
ValidMin: 16, ValidMin: 16,
} }

View File

@ -41,7 +41,7 @@ func TestRequestSerialization(t *testing.T) {
} }
buffer := buf.New() buffer := buf.New()
client := NewClientSession(context.TODO(), true, protocol.DefaultIDHash, 0) client := NewClientSession(context.TODO(), 0)
common.Must(client.EncodeRequestHeader(expectedRequest, buffer)) common.Must(client.EncodeRequestHeader(expectedRequest, buffer))
buffer2 := buf.New() buffer2 := buf.New()
@ -50,7 +50,7 @@ func TestRequestSerialization(t *testing.T) {
sessionHistory := NewSessionHistory() sessionHistory := NewSessionHistory()
defer common.Close(sessionHistory) defer common.Close(sessionHistory)
userValidator := vmess.NewTimedUserValidator(protocol.DefaultIDHash) userValidator := vmess.NewTimedUserValidator()
userValidator.Add(user) userValidator.Add(user)
defer common.Close(userValidator) defer common.Close(userValidator)
@ -90,7 +90,7 @@ func TestInvalidRequest(t *testing.T) {
} }
buffer := buf.New() buffer := buf.New()
client := NewClientSession(context.TODO(), true, protocol.DefaultIDHash, 0) client := NewClientSession(context.TODO(), 0)
common.Must(client.EncodeRequestHeader(expectedRequest, buffer)) common.Must(client.EncodeRequestHeader(expectedRequest, buffer))
buffer2 := buf.New() buffer2 := buf.New()
@ -99,7 +99,7 @@ func TestInvalidRequest(t *testing.T) {
sessionHistory := NewSessionHistory() sessionHistory := NewSessionHistory()
defer common.Close(sessionHistory) defer common.Close(sessionHistory)
userValidator := vmess.NewTimedUserValidator(protocol.DefaultIDHash) userValidator := vmess.NewTimedUserValidator()
userValidator.Add(user) userValidator.Add(user)
defer common.Close(userValidator) defer common.Close(userValidator)
@ -130,7 +130,7 @@ func TestMuxRequest(t *testing.T) {
} }
buffer := buf.New() buffer := buf.New()
client := NewClientSession(context.TODO(), true, protocol.DefaultIDHash, 0) client := NewClientSession(context.TODO(), 0)
common.Must(client.EncodeRequestHeader(expectedRequest, buffer)) common.Must(client.EncodeRequestHeader(expectedRequest, buffer))
buffer2 := buf.New() buffer2 := buf.New()
@ -139,7 +139,7 @@ func TestMuxRequest(t *testing.T) {
sessionHistory := NewSessionHistory() sessionHistory := NewSessionHistory()
defer common.Close(sessionHistory) defer common.Close(sessionHistory)
userValidator := vmess.NewTimedUserValidator(protocol.DefaultIDHash) userValidator := vmess.NewTimedUserValidator()
userValidator.Add(user) userValidator.Add(user)
defer common.Close(userValidator) defer common.Close(userValidator)

View File

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/md5"
"crypto/sha256" "crypto/sha256"
"encoding/binary" "encoding/binary"
"hash/fnv" "hash/fnv"
@ -102,10 +101,6 @@ type ServerSession struct {
responseBodyIV [16]byte responseBodyIV [16]byte
responseWriter io.Writer responseWriter io.Writer
responseHeader byte responseHeader byte
isAEADRequest bool
isAEADForced bool
} }
// NewServerSession creates a new ServerSession, using the given UserValidator. // NewServerSession creates a new ServerSession, using the given UserValidator.
@ -117,17 +112,12 @@ func NewServerSession(validator *vmess.TimedUserValidator, sessionHistory *Sessi
} }
} }
// SetAEADForced sets isAEADForced for a ServerSession.
func (s *ServerSession) SetAEADForced(isAEADForced bool) {
s.isAEADForced = isAEADForced
}
func parseSecurityType(b byte) protocol.SecurityType { func parseSecurityType(b byte) protocol.SecurityType {
if _, f := protocol.SecurityType_name[int32(b)]; f { if _, f := protocol.SecurityType_name[int32(b)]; f {
st := protocol.SecurityType(b) st := protocol.SecurityType(b)
// For backward compatibility. // For backward compatibility.
if st == protocol.SecurityType_UNKNOWN { if st == protocol.SecurityType_UNKNOWN {
st = protocol.SecurityType_LEGACY st = protocol.SecurityType_AUTO
} }
return st return st
} }
@ -183,26 +173,6 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader, isDrain bool) (*pr
} }
} }
decryptor = bytes.NewReader(aeadData) decryptor = bytes.NewReader(aeadData)
s.isAEADRequest = true
case errorAEAD == vmessaead.ErrNotFound:
userLegacy, timestamp, valid, userValidationError := s.userValidator.Get(buffer.Bytes())
if !valid || userValidationError != nil {
return nil, drainConnection(newError("invalid user").Base(userValidationError))
}
if s.isAEADForced {
return nil, drainConnection(newError("invalid user: VMessAEAD is enforced and a non VMessAEAD connection is received. You can still disable this security feature with environment variable xray.vmess.aead.forced = false . You will not be able to enable legacy header workaround in the future."))
}
if s.userValidator.ShouldShowLegacyWarn() {
newError("Critical Warning: potentially invalid user: a non VMessAEAD connection is received. From 2022 Jan 1st, this kind of connection will be rejected by default. You should update or replace your client software now. This message will not be shown for further violation on this inbound.").AtWarning().WriteToLog()
}
user = userLegacy
iv := hashTimestamp(md5.New(), timestamp)
vmessAccount = userLegacy.Account.(*vmess.MemoryAccount)
aesStream := crypto.NewAesDecryptionStream(vmessAccount.ID.CmdKey(), iv)
decryptor = crypto.NewCryptionReader(aesStream, reader)
default: default:
return nil, drainConnection(newError("invalid user").Base(errorAEAD)) return nil, drainConnection(newError("invalid user").Base(errorAEAD))
} }
@ -225,16 +195,8 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader, isDrain bool) (*pr
sid.key = s.requestBodyKey sid.key = s.requestBodyKey
sid.nonce = s.requestBodyIV sid.nonce = s.requestBodyIV
if !s.sessionHistory.addIfNotExits(sid) { if !s.sessionHistory.addIfNotExits(sid) {
if !s.isAEADRequest {
drainErr := s.userValidator.BurnTaintFuse(fixedSizeAuthID[:])
if drainErr != nil {
return nil, drainConnection(newError("duplicated session id, possibly under replay attack, and failed to taint userHash").Base(drainErr))
}
return nil, drainConnection(newError("duplicated session id, possibly under replay attack, userHash tainted"))
} else {
return nil, newError("duplicated session id, possibly under replay attack, but this is a AEAD request") return nil, newError("duplicated session id, possibly under replay attack, but this is a AEAD request")
} }
}
s.responseHeader = buffer.Byte(33) // 1 byte s.responseHeader = buffer.Byte(33) // 1 byte
request.Option = bitmask.Byte(buffer.Byte(34)) // 1 byte request.Option = bitmask.Byte(buffer.Byte(34)) // 1 byte
@ -257,25 +219,11 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader, isDrain bool) (*pr
if paddingLen > 0 { if paddingLen > 0 {
if _, err := buffer.ReadFullFrom(decryptor, int32(paddingLen)); err != nil { if _, err := buffer.ReadFullFrom(decryptor, int32(paddingLen)); err != nil {
if !s.isAEADRequest {
burnErr := s.userValidator.BurnTaintFuse(fixedSizeAuthID[:])
if burnErr != nil {
return nil, newError("failed to read padding, failed to taint userHash").Base(burnErr).Base(err)
}
return nil, newError("failed to read padding, userHash tainted").Base(err)
}
return nil, newError("failed to read padding").Base(err) return nil, newError("failed to read padding").Base(err)
} }
} }
if _, err := buffer.ReadFullFrom(decryptor, 4); err != nil { if _, err := buffer.ReadFullFrom(decryptor, 4); err != nil {
if !s.isAEADRequest {
burnErr := s.userValidator.BurnTaintFuse(fixedSizeAuthID[:])
if burnErr != nil {
return nil, newError("failed to read checksum, failed to taint userHash").Base(burnErr).Base(err)
}
return nil, newError("failed to read checksum, userHash tainted").Base(err)
}
return nil, newError("failed to read checksum").Base(err) return nil, newError("failed to read checksum").Base(err)
} }
@ -285,18 +233,8 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader, isDrain bool) (*pr
expectedHash := binary.BigEndian.Uint32(buffer.BytesFrom(-4)) expectedHash := binary.BigEndian.Uint32(buffer.BytesFrom(-4))
if actualHash != expectedHash { if actualHash != expectedHash {
if !s.isAEADRequest {
Autherr := newError("invalid auth, legacy userHash tainted")
burnErr := s.userValidator.BurnTaintFuse(fixedSizeAuthID[:])
if burnErr != nil {
Autherr = newError("invalid auth, can't taint legacy userHash").Base(burnErr)
}
// It is possible that we are under attack described in https://github.com/xray/xray-core/issues/2523
return nil, drainConnection(Autherr)
} else {
return nil, newError("invalid auth, but this is a AEAD request") return nil, newError("invalid auth, but this is a AEAD request")
} }
}
if request.Address == nil { if request.Address == nil {
return nil, newError("invalid remote address") return nil, newError("invalid remote address")
@ -340,19 +278,6 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
} }
return buf.NewReader(reader), nil return buf.NewReader(reader), nil
case protocol.SecurityType_LEGACY:
aesStream := crypto.NewAesDecryptionStream(s.requestBodyKey[:], s.requestBodyIV[:])
cryptionReader := crypto.NewCryptionReader(aesStream, reader)
if request.Option.Has(protocol.RequestOptionChunkStream) {
auth := &crypto.AEADAuthenticator{
AEAD: new(FnvAuthenticator),
NonceGenerator: crypto.GenerateEmptyBytes(),
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
}
return crypto.NewAuthenticationReader(auth, sizeParser, cryptionReader, request.Command.TransferType(), padding), nil
}
return buf.NewReader(cryptionReader), nil
case protocol.SecurityType_AES128_GCM: case protocol.SecurityType_AES128_GCM:
aead := crypto.NewAesGcm(s.requestBodyKey[:]) aead := crypto.NewAesGcm(s.requestBodyKey[:])
auth := &crypto.AEADAuthenticator{ auth := &crypto.AEADAuthenticator{
@ -403,25 +328,17 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
// EncodeResponseHeader writes encoded response header into the given writer. // EncodeResponseHeader writes encoded response header into the given writer.
func (s *ServerSession) EncodeResponseHeader(header *protocol.ResponseHeader, writer io.Writer) { func (s *ServerSession) EncodeResponseHeader(header *protocol.ResponseHeader, writer io.Writer) {
var encryptionWriter io.Writer var encryptionWriter io.Writer
if !s.isAEADRequest {
s.responseBodyKey = md5.Sum(s.requestBodyKey[:])
s.responseBodyIV = md5.Sum(s.requestBodyIV[:])
} else {
BodyKey := sha256.Sum256(s.requestBodyKey[:]) BodyKey := sha256.Sum256(s.requestBodyKey[:])
copy(s.responseBodyKey[:], BodyKey[:16]) copy(s.responseBodyKey[:], BodyKey[:16])
BodyIV := sha256.Sum256(s.requestBodyIV[:]) BodyIV := sha256.Sum256(s.requestBodyIV[:])
copy(s.responseBodyIV[:], BodyIV[:16]) copy(s.responseBodyIV[:], BodyIV[:16])
}
aesStream := crypto.NewAesEncryptionStream(s.responseBodyKey[:], s.responseBodyIV[:]) aesStream := crypto.NewAesEncryptionStream(s.responseBodyKey[:], s.responseBodyIV[:])
encryptionWriter = crypto.NewCryptionWriter(aesStream, writer) encryptionWriter = crypto.NewCryptionWriter(aesStream, writer)
s.responseWriter = encryptionWriter s.responseWriter = encryptionWriter
aeadEncryptedHeaderBuffer := bytes.NewBuffer(nil) aeadEncryptedHeaderBuffer := bytes.NewBuffer(nil)
if s.isAEADRequest {
encryptionWriter = aeadEncryptedHeaderBuffer encryptionWriter = aeadEncryptedHeaderBuffer
}
common.Must2(encryptionWriter.Write([]byte{s.responseHeader, byte(header.Option)})) common.Must2(encryptionWriter.Write([]byte{s.responseHeader, byte(header.Option)}))
err := MarshalCommand(header.Command, encryptionWriter) err := MarshalCommand(header.Command, encryptionWriter)
@ -429,7 +346,6 @@ func (s *ServerSession) EncodeResponseHeader(header *protocol.ResponseHeader, wr
common.Must2(encryptionWriter.Write([]byte{0x00, 0x00})) common.Must2(encryptionWriter.Write([]byte{0x00, 0x00}))
} }
if s.isAEADRequest {
aeadResponseHeaderLengthEncryptionKey := vmessaead.KDF16(s.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderLenKey) aeadResponseHeaderLengthEncryptionKey := vmessaead.KDF16(s.responseBodyKey[:], vmessaead.KDFSaltConstAEADRespHeaderLenKey)
aeadResponseHeaderLengthEncryptionIV := vmessaead.KDF(s.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderLenIV)[:12] aeadResponseHeaderLengthEncryptionIV := vmessaead.KDF(s.responseBodyIV[:], vmessaead.KDFSaltConstAEADRespHeaderLenIV)[:12]
@ -453,7 +369,6 @@ func (s *ServerSession) EncodeResponseHeader(header *protocol.ResponseHeader, wr
aeadEncryptedHeaderPayload := aeadResponseHeaderPayloadEncryptionAEAD.Seal(nil, aeadResponseHeaderPayloadEncryptionIV, aeadEncryptedHeaderBuffer.Bytes(), nil) aeadEncryptedHeaderPayload := aeadResponseHeaderPayloadEncryptionAEAD.Seal(nil, aeadResponseHeaderPayloadEncryptionIV, aeadEncryptedHeaderBuffer.Bytes(), nil)
common.Must2(io.Copy(writer, bytes.NewReader(aeadEncryptedHeaderPayload))) common.Must2(io.Copy(writer, bytes.NewReader(aeadEncryptedHeaderPayload)))
}
} }
// EncodeResponseBody returns a Writer that auto-encrypt content written by caller. // EncodeResponseBody returns a Writer that auto-encrypt content written by caller.
@ -487,17 +402,6 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ
} }
return buf.NewWriter(writer), nil return buf.NewWriter(writer), nil
case protocol.SecurityType_LEGACY:
if request.Option.Has(protocol.RequestOptionChunkStream) {
auth := &crypto.AEADAuthenticator{
AEAD: new(FnvAuthenticator),
NonceGenerator: crypto.GenerateEmptyBytes(),
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
}
return crypto.NewAuthenticationWriter(auth, sizeParser, s.responseWriter, request.Command.TransferType(), padding), nil
}
return &buf.SequentialWriter{Writer: s.responseWriter}, nil
case protocol.SecurityType_AES128_GCM: case protocol.SecurityType_AES128_GCM:
aead := crypto.NewAesGcm(s.responseBodyKey[:]) aead := crypto.NewAesGcm(s.responseBodyKey[:])
auth := &crypto.AEADAuthenticator{ auth := &crypto.AEADAuthenticator{

View File

@ -73,7 +73,6 @@ type DefaultConfig struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
AlterId uint32 `protobuf:"varint,1,opt,name=alter_id,json=alterId,proto3" json:"alter_id,omitempty"`
Level uint32 `protobuf:"varint,2,opt,name=level,proto3" json:"level,omitempty"` Level uint32 `protobuf:"varint,2,opt,name=level,proto3" json:"level,omitempty"`
} }
@ -109,13 +108,6 @@ func (*DefaultConfig) Descriptor() ([]byte, []int) {
return file_proxy_vmess_inbound_config_proto_rawDescGZIP(), []int{1} return file_proxy_vmess_inbound_config_proto_rawDescGZIP(), []int{1}
} }
func (x *DefaultConfig) GetAlterId() uint32 {
if x != nil {
return x.AlterId
}
return 0
}
func (x *DefaultConfig) GetLevel() uint32 { func (x *DefaultConfig) GetLevel() uint32 {
if x != nil { if x != nil {
return x.Level return x.Level
@ -130,8 +122,7 @@ type Config struct {
User []*protocol.User `protobuf:"bytes,1,rep,name=user,proto3" json:"user,omitempty"` User []*protocol.User `protobuf:"bytes,1,rep,name=user,proto3" json:"user,omitempty"`
Default *DefaultConfig `protobuf:"bytes,2,opt,name=default,proto3" json:"default,omitempty"` Default *DefaultConfig `protobuf:"bytes,2,opt,name=default,proto3" json:"default,omitempty"`
Detour *DetourConfig `protobuf:"bytes,3,opt,name=detour,proto3" json:"detour,omitempty"` Detour *DetourConfig `protobuf:"bytes,3,opt,name=detour,proto3" json:"detour,omitempty"` // 4 is for legacy setting
SecureEncryptionOnly bool `protobuf:"varint,4,opt,name=secure_encryption_only,json=secureEncryptionOnly,proto3" json:"secure_encryption_only,omitempty"`
} }
func (x *Config) Reset() { func (x *Config) Reset() {
@ -187,13 +178,6 @@ func (x *Config) GetDetour() *DetourConfig {
return nil return nil
} }
func (x *Config) GetSecureEncryptionOnly() bool {
if x != nil {
return x.SecureEncryptionOnly
}
return false
}
var File_proxy_vmess_inbound_config_proto protoreflect.FileDescriptor var File_proxy_vmess_inbound_config_proto protoreflect.FileDescriptor
var file_proxy_vmess_inbound_config_proto_rawDesc = []byte{ var file_proxy_vmess_inbound_config_proto_rawDesc = []byte{
@ -204,34 +188,29 @@ var file_proxy_vmess_inbound_config_proto_rawDesc = []byte{
0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x75, 0x73, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x75, 0x73,
0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x1e, 0x0a, 0x0c, 0x44, 0x65, 0x74, 0x6f, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x1e, 0x0a, 0x0c, 0x44, 0x65, 0x74, 0x6f,
0x75, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x01, 0x75, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x74, 0x6f, 0x22, 0x40, 0x0a, 0x0d, 0x44, 0x65, 0x66, 0x61, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x74, 0x6f, 0x22, 0x25, 0x0a, 0x0d, 0x44, 0x65, 0x66, 0x61,
0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x6c, 0x74, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76,
0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x61, 0x6c, 0x74, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22,
0x65, 0x72, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x02, 0x20, 0xbb, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x0a, 0x04, 0x75, 0x73,
0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0xf1, 0x01, 0x0a, 0x06, 0x43, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e,
0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x41, 0x0a, 0x07, 0x64, 0x65,
0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x72,
0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x41, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x2e, 0x69,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f,
0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x3e, 0x0a,
0x64, 0x2e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x64, 0x65, 0x74, 0x6f, 0x75, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e,
0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x3e, 0x0a, 0x06, 0x64, 0x65, 0x74, 0x6f, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6d, 0x65, 0x73, 0x73,
0x75, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x44, 0x65, 0x74, 0x6f, 0x75, 0x72, 0x43,
0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x64, 0x65, 0x74, 0x6f, 0x75, 0x72, 0x42, 0x6a, 0x0a,
0x75, 0x6e, 0x64, 0x2e, 0x44, 0x65, 0x74, 0x6f, 0x75, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e,
0x52, 0x06, 0x64, 0x65, 0x74, 0x6f, 0x75, 0x72, 0x12, 0x34, 0x0a, 0x16, 0x73, 0x65, 0x63, 0x75, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x01, 0x5a,
0x72, 0x65, 0x5f, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x6e, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73,
0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79,
0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x6e, 0x6c, 0x79, 0x42, 0x6a, 0x2f, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x2f, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0xaa, 0x02,
0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6d, 0x65, 0x73,
0x2e, 0x76, 0x6d, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x01, 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x33,
0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78,
0x79, 0x2f, 0x76, 0x6d, 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, 0x6d, 0x65,
0x73, 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
} }
var ( var (

View File

@ -13,7 +13,6 @@ message DetourConfig {
} }
message DefaultConfig { message DefaultConfig {
uint32 alter_id = 1;
uint32 level = 2; uint32 level = 2;
} }
@ -21,5 +20,5 @@ message Config {
repeated xray.common.protocol.User user = 1; repeated xray.common.protocol.User user = 1;
DefaultConfig default = 2; DefaultConfig default = 2;
DetourConfig detour = 3; DetourConfig detour = 3;
bool secure_encryption_only = 4; // 4 is for legacy setting
} }

View File

@ -14,7 +14,6 @@ import (
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/log"
"github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/platform"
"github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/signal" "github.com/xtls/xray-core/common/signal"
@ -29,23 +28,16 @@ import (
"github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/stat"
) )
var (
aeadForced = false
aeadForced2022 = false
)
type userByEmail struct { type userByEmail struct {
sync.Mutex sync.Mutex
cache map[string]*protocol.MemoryUser cache map[string]*protocol.MemoryUser
defaultLevel uint32 defaultLevel uint32
defaultAlterIDs uint16
} }
func newUserByEmail(config *DefaultConfig) *userByEmail { func newUserByEmail(config *DefaultConfig) *userByEmail {
return &userByEmail{ return &userByEmail{
cache: make(map[string]*protocol.MemoryUser), cache: make(map[string]*protocol.MemoryUser),
defaultLevel: config.Level, defaultLevel: config.Level,
defaultAlterIDs: uint16(config.AlterId),
} }
} }
@ -77,7 +69,6 @@ func (v *userByEmail) Get(email string) (*protocol.MemoryUser, bool) {
id := uuid.New() id := uuid.New()
rawAccount := &vmess.Account{ rawAccount := &vmess.Account{
Id: id.String(), Id: id.String(),
AlterId: uint32(v.defaultAlterIDs),
} }
account, err := rawAccount.AsAccount() account, err := rawAccount.AsAccount()
common.Must(err) common.Must(err)
@ -112,7 +103,6 @@ type Handler struct {
usersByEmail *userByEmail usersByEmail *userByEmail
detours *DetourConfig detours *DetourConfig
sessionHistory *encoding.SessionHistory sessionHistory *encoding.SessionHistory
secure bool
} }
// New creates a new VMess inbound handler. // New creates a new VMess inbound handler.
@ -121,11 +111,10 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
handler := &Handler{ handler := &Handler{
policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
inboundHandlerManager: v.GetFeature(feature_inbound.ManagerType()).(feature_inbound.Manager), inboundHandlerManager: v.GetFeature(feature_inbound.ManagerType()).(feature_inbound.Manager),
clients: vmess.NewTimedUserValidator(protocol.DefaultIDHash), clients: vmess.NewTimedUserValidator(),
detours: config.Detour, detours: config.Detour,
usersByEmail: newUserByEmail(config.GetDefaultValue()), usersByEmail: newUserByEmail(config.GetDefaultValue()),
sessionHistory: encoding.NewSessionHistory(), sessionHistory: encoding.NewSessionHistory(),
secure: config.SecureEncryptionOnly,
} }
for _, user := range config.User { for _, user := range config.User {
@ -145,7 +134,6 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
// Close implements common.Closable. // Close implements common.Closable.
func (h *Handler) Close() error { func (h *Handler) Close() error {
return errors.Combine( return errors.Combine(
h.clients.Close(),
h.sessionHistory.Close(), h.sessionHistory.Close(),
common.Close(h.usersByEmail)) common.Close(h.usersByEmail))
} }
@ -219,10 +207,6 @@ func transferResponse(timer signal.ActivityUpdater, session *encoding.ServerSess
return nil return nil
} }
func isInsecureEncryption(s protocol.SecurityType) bool {
return s == protocol.SecurityType_NONE || s == protocol.SecurityType_LEGACY || s == protocol.SecurityType_UNKNOWN
}
// Process implements proxy.Inbound.Process(). // Process implements proxy.Inbound.Process().
func (h *Handler) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error { func (h *Handler) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error {
sessionPolicy := h.policyManager.ForLevel(0) sessionPolicy := h.policyManager.ForLevel(0)
@ -241,7 +225,6 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
reader := &buf.BufferedReader{Reader: buf.NewReader(connection)} reader := &buf.BufferedReader{Reader: buf.NewReader(connection)}
svrSession := encoding.NewServerSession(h.clients, h.sessionHistory) svrSession := encoding.NewServerSession(h.clients, h.sessionHistory)
svrSession.SetAEADForced(aeadForced)
request, err := svrSession.DecodeRequestHeader(reader, isDrain) request, err := svrSession.DecodeRequestHeader(reader, isDrain)
if err != nil { if err != nil {
if errors.Cause(err) != io.EOF { if errors.Cause(err) != io.EOF {
@ -256,17 +239,6 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
return err return err
} }
if h.secure && isInsecureEncryption(request.Security) {
log.Record(&log.AccessMessage{
From: connection.RemoteAddr(),
To: "",
Status: log.AccessRejected,
Reason: "Insecure encryption",
Email: request.User.Email,
})
return newError("client is using insecure encryption: ", request.Security)
}
if request.Command != protocol.RequestCommandMux { if request.Command != protocol.RequestCommandMux {
ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{
From: connection.RemoteAddr(), From: connection.RemoteAddr(),
@ -361,7 +333,6 @@ func (h *Handler) generateCommand(ctx context.Context, request *protocol.Request
return &protocol.CommandSwitchAccount{ return &protocol.CommandSwitchAccount{
Port: port, Port: port,
ID: account.ID.UUID(), ID: account.ID.UUID(),
AlterIds: uint16(len(account.AlterIDs)),
Level: user.Level, Level: user.Level,
ValidMin: byte(availableMin), ValidMin: byte(availableMin),
} }
@ -376,18 +347,4 @@ func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return New(ctx, config.(*Config)) return New(ctx, config.(*Config))
})) }))
defaultFlagValue := "NOT_DEFINED_AT_ALL"
if time.Now().Year() >= 2022 {
defaultFlagValue = "true_by_default_2022"
}
isAeadForced := platform.NewEnvFlag("xray.vmess.aead.forced").GetValue(func() string { return defaultFlagValue })
aeadForced = (isAeadForced == "true")
if isAeadForced == "true_by_default_2022" {
aeadForced = true
aeadForced2022 = true
}
} }

View File

@ -12,9 +12,8 @@ import (
func (h *Handler) handleSwitchAccount(cmd *protocol.CommandSwitchAccount) { func (h *Handler) handleSwitchAccount(cmd *protocol.CommandSwitchAccount) {
rawAccount := &vmess.Account{ rawAccount := &vmess.Account{
Id: cmd.ID.String(), Id: cmd.ID.String(),
AlterId: uint32(cmd.AlterIds),
SecuritySettings: &protocol.SecurityConfig{ SecuritySettings: &protocol.SecurityConfig{
Type: protocol.SecurityType_LEGACY, Type: protocol.SecurityType_AUTO,
}, },
} }

View File

@ -128,11 +128,6 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
input := link.Reader input := link.Reader
output := link.Writer output := link.Writer
isAEAD := false
if !aeadDisabled && len(account.AlterIDs) == 0 {
isAEAD = true
}
hashkdf := hmac.New(sha256.New, []byte("VMessBF")) hashkdf := hmac.New(sha256.New, []byte("VMessBF"))
hashkdf.Write(account.ID.Bytes()) hashkdf.Write(account.ID.Bytes())
@ -144,7 +139,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
newCtx, newCancel = context.WithCancel(context.Background()) newCtx, newCancel = context.WithCancel(context.Background())
} }
session := encoding.NewClientSession(ctx, isAEAD, protocol.DefaultIDHash, int64(behaviorSeed)) session := encoding.NewClientSession(ctx, int64(behaviorSeed))
sessionPolicy := h.policyManager.ForLevel(request.User.Level) sessionPolicy := h.policyManager.ForLevel(request.User.Level)
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
@ -233,7 +228,6 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
var ( var (
enablePadding = false enablePadding = false
aeadDisabled = false
) )
func shouldEnablePadding(s protocol.SecurityType) bool { func shouldEnablePadding(s protocol.SecurityType) bool {
@ -251,9 +245,4 @@ func init() {
if paddingValue != defaultFlagValue { if paddingValue != defaultFlagValue {
enablePadding = true enablePadding = true
} }
isAeadDisabled := platform.NewEnvFlag("xray.vmess.aead.disabled").GetValue(func() string { return defaultFlagValue })
if isAeadDisabled == "true" {
aeadDisabled = true
}
} }

View File

@ -6,146 +6,39 @@ import (
"hash/crc64" "hash/crc64"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"time"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/dice"
"github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/proxy/vmess/aead" "github.com/xtls/xray-core/proxy/vmess/aead"
) )
const (
updateInterval = 10 * time.Second
cacheDurationSec = 120
)
type user struct {
user protocol.MemoryUser
lastSec protocol.Timestamp
}
// TimedUserValidator is a user Validator based on time. // TimedUserValidator is a user Validator based on time.
type TimedUserValidator struct { type TimedUserValidator struct {
sync.RWMutex sync.RWMutex
users []*user users []*protocol.MemoryUser
userHash map[[16]byte]indexTimePair
hasher protocol.IDHash
baseTime protocol.Timestamp
task *task.Periodic
behaviorSeed uint64 behaviorSeed uint64
behaviorFused bool behaviorFused bool
aeadDecoderHolder *aead.AuthIDDecoderHolder aeadDecoderHolder *aead.AuthIDDecoderHolder
legacyWarnShown bool
}
type indexTimePair struct {
user *user
timeInc uint32
taintedFuse *uint32
} }
// NewTimedUserValidator creates a new TimedUserValidator. // NewTimedUserValidator creates a new TimedUserValidator.
func NewTimedUserValidator(hasher protocol.IDHash) *TimedUserValidator { func NewTimedUserValidator() *TimedUserValidator {
tuv := &TimedUserValidator{ tuv := &TimedUserValidator{
users: make([]*user, 0, 16), users: make([]*protocol.MemoryUser, 0, 16),
userHash: make(map[[16]byte]indexTimePair, 1024),
hasher: hasher,
baseTime: protocol.Timestamp(time.Now().Unix() - cacheDurationSec*2),
aeadDecoderHolder: aead.NewAuthIDDecoderHolder(), aeadDecoderHolder: aead.NewAuthIDDecoderHolder(),
} }
tuv.task = &task.Periodic{
Interval: updateInterval,
Execute: func() error {
tuv.updateUserHash()
return nil
},
}
common.Must(tuv.task.Start())
return tuv return tuv
} }
// visible for testing
func (v *TimedUserValidator) GetBaseTime() protocol.Timestamp {
return v.baseTime
}
func (v *TimedUserValidator) generateNewHashes(nowSec protocol.Timestamp, user *user) {
var hashValue [16]byte
genEndSec := nowSec + cacheDurationSec
genHashForID := func(id *protocol.ID) {
idHash := v.hasher(id.Bytes())
genBeginSec := user.lastSec
if genBeginSec < nowSec-cacheDurationSec {
genBeginSec = nowSec - cacheDurationSec
}
for ts := genBeginSec; ts <= genEndSec; ts++ {
common.Must2(serial.WriteUint64(idHash, uint64(ts)))
idHash.Sum(hashValue[:0])
idHash.Reset()
v.userHash[hashValue] = indexTimePair{
user: user,
timeInc: uint32(ts - v.baseTime),
taintedFuse: new(uint32),
}
}
}
account := user.user.Account.(*MemoryAccount)
genHashForID(account.ID)
for _, id := range account.AlterIDs {
genHashForID(id)
}
user.lastSec = genEndSec
}
func (v *TimedUserValidator) removeExpiredHashes(expire uint32) {
for key, pair := range v.userHash {
if pair.timeInc < expire {
delete(v.userHash, key)
}
}
}
func (v *TimedUserValidator) updateUserHash() {
now := time.Now()
nowSec := protocol.Timestamp(now.Unix())
v.Lock()
defer v.Unlock()
for _, user := range v.users {
v.generateNewHashes(nowSec, user)
}
expire := protocol.Timestamp(now.Unix() - cacheDurationSec)
if expire > v.baseTime {
v.removeExpiredHashes(uint32(expire - v.baseTime))
}
}
func (v *TimedUserValidator) Add(u *protocol.MemoryUser) error { func (v *TimedUserValidator) Add(u *protocol.MemoryUser) error {
v.Lock() v.Lock()
defer v.Unlock() defer v.Unlock()
nowSec := time.Now().Unix() v.users = append(v.users, u)
uu := &user{ account := u.Account.(*MemoryAccount)
user: *u,
lastSec: protocol.Timestamp(nowSec - cacheDurationSec),
}
v.users = append(v.users, uu)
v.generateNewHashes(protocol.Timestamp(nowSec), uu)
account := uu.user.Account.(*MemoryAccount)
if !v.behaviorFused { if !v.behaviorFused {
hashkdf := hmac.New(sha256.New, []byte("VMESSBSKDF")) hashkdf := hmac.New(sha256.New, []byte("VMESSBSKDF"))
hashkdf.Write(account.ID.Bytes()) hashkdf.Write(account.ID.Bytes())
@ -159,25 +52,6 @@ func (v *TimedUserValidator) Add(u *protocol.MemoryUser) error {
return nil return nil
} }
func (v *TimedUserValidator) Get(userHash []byte) (*protocol.MemoryUser, protocol.Timestamp, bool, error) {
v.RLock()
defer v.RUnlock()
v.behaviorFused = true
var fixedSizeHash [16]byte
copy(fixedSizeHash[:], userHash)
pair, found := v.userHash[fixedSizeHash]
if found {
user := pair.user.user
if atomic.LoadUint32(pair.taintedFuse) == 0 {
return &user, protocol.Timestamp(pair.timeInc) + v.baseTime, true, nil
}
return nil, 0, false, ErrTainted
}
return nil, 0, false, ErrNotFound
}
func (v *TimedUserValidator) GetAEAD(userHash []byte) (*protocol.MemoryUser, bool, error) { func (v *TimedUserValidator) GetAEAD(userHash []byte) (*protocol.MemoryUser, bool, error) {
v.RLock() v.RLock()
defer v.RUnlock() defer v.RUnlock()
@ -199,10 +73,10 @@ func (v *TimedUserValidator) Remove(email string) bool {
email = strings.ToLower(email) email = strings.ToLower(email)
idx := -1 idx := -1
for i, u := range v.users { for i, u := range v.users {
if strings.EqualFold(u.user.Email, email) { if strings.EqualFold(u.Email, email) {
idx = i idx = i
var cmdkeyfl [16]byte var cmdkeyfl [16]byte
copy(cmdkeyfl[:], u.user.Account.(*MemoryAccount).ID.CmdKey()) copy(cmdkeyfl[:], u.Account.(*MemoryAccount).ID.CmdKey())
v.aeadDecoderHolder.RemoveUser(cmdkeyfl) v.aeadDecoderHolder.RemoveUser(cmdkeyfl)
break break
} }
@ -219,11 +93,6 @@ func (v *TimedUserValidator) Remove(email string) bool {
return true return true
} }
// Close implements common.Closable.
func (v *TimedUserValidator) Close() error {
return v.task.Close()
}
func (v *TimedUserValidator) GetBehaviorSeed() uint64 { func (v *TimedUserValidator) GetBehaviorSeed() uint64 {
v.Lock() v.Lock()
defer v.Unlock() defer v.Unlock()
@ -235,36 +104,6 @@ func (v *TimedUserValidator) GetBehaviorSeed() uint64 {
return v.behaviorSeed return v.behaviorSeed
} }
func (v *TimedUserValidator) BurnTaintFuse(userHash []byte) error {
v.RLock()
defer v.RUnlock()
var userHashFL [16]byte
copy(userHashFL[:], userHash)
pair, found := v.userHash[userHashFL]
if found {
if atomic.CompareAndSwapUint32(pair.taintedFuse, 0, 1) {
return nil
}
return ErrTainted
}
return ErrNotFound
}
/*
ShouldShowLegacyWarn will return whether a Legacy Warning should be shown
Not guaranteed to only return true once for every inbound, but it is okay.
*/
func (v *TimedUserValidator) ShouldShowLegacyWarn() bool {
if v.legacyWarnShown {
return false
}
v.legacyWarnShown = true
return true
}
var ErrNotFound = newError("Not Found") var ErrNotFound = newError("Not Found")
var ErrTainted = newError("ErrTainted") var ErrTainted = newError("ErrTainted")

View File

@ -5,7 +5,6 @@ import (
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/common/uuid" "github.com/xtls/xray-core/common/uuid"
. "github.com/xtls/xray-core/proxy/vmess" . "github.com/xtls/xray-core/proxy/vmess"
) )
@ -16,81 +15,9 @@ func toAccount(a *Account) protocol.Account {
return account return account
} }
func TestUserValidator(t *testing.T) {
hasher := protocol.DefaultIDHash
v := NewTimedUserValidator(hasher)
defer common.Close(v)
id := uuid.New()
user := &protocol.MemoryUser{
Email: "test",
Account: toAccount(&Account{
Id: id.String(),
}),
}
common.Must(v.Add(user))
{
testSmallLag := func(lag int64) {
ts := int64(v.GetBaseTime()) + lag + 240
idHash := hasher(id.Bytes())
common.Must2(serial.WriteUint64(idHash, uint64(ts)))
userHash := idHash.Sum(nil)
euser, ets, found, _ := v.Get(userHash)
if !found {
t.Fatal("user not found")
}
if euser.Email != user.Email {
t.Error("unexpected user email: ", euser.Email, " want ", user.Email)
}
if int64(ets) != ts {
t.Error("unexpected timestamp: ", ets, " want ", ts)
}
}
testSmallLag(0)
testSmallLag(40)
testSmallLag(-40)
testSmallLag(80)
testSmallLag(-80)
testSmallLag(120)
testSmallLag(-120)
}
{
testBigLag := func(lag int64) {
ts := int64(v.GetBaseTime()) + lag + 240
idHash := hasher(id.Bytes())
common.Must2(serial.WriteUint64(idHash, uint64(ts)))
userHash := idHash.Sum(nil)
euser, _, found, _ := v.Get(userHash)
if found || euser != nil {
t.Error("unexpected user")
}
}
testBigLag(121)
testBigLag(-121)
testBigLag(310)
testBigLag(-310)
testBigLag(500)
testBigLag(-500)
}
if v := v.Remove(user.Email); !v {
t.Error("unable to remove user")
}
if v := v.Remove(user.Email); v {
t.Error("remove user twice")
}
}
func BenchmarkUserValidator(b *testing.B) { func BenchmarkUserValidator(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
hasher := protocol.DefaultIDHash v := NewTimedUserValidator()
v := NewTimedUserValidator(hasher)
for j := 0; j < 1500; j++ { for j := 0; j < 1500; j++ {
id := uuid.New() id := uuid.New()

View File

@ -1,4 +0,0 @@
package vmess
// example
const AlterID = "VMessCtxInterface_AlterID"