VMess AEAD based packet length

(cherry picked from commit 08221600082a79376bdc262f2ffec1a3129ae98d)
This commit is contained in:
Shelikhoo 2021-04-29 06:29:42 +08:00 committed by 世界
parent 5bf2b31883
commit 778876df9e
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
8 changed files with 232 additions and 6 deletions

View file

@ -38,6 +38,8 @@ const (
RequestOptionChunkMasking bitmask.Byte = 0x04 RequestOptionChunkMasking bitmask.Byte = 0x04
RequestOptionGlobalPadding bitmask.Byte = 0x08 RequestOptionGlobalPadding bitmask.Byte = 0x08
RequestOptionAuthenticatedLength bitmask.Byte = 0x10
) )
type RequestHeader struct { type RequestHeader struct {

View file

@ -15,9 +15,10 @@ import (
) )
type VMessAccount struct { type VMessAccount struct {
ID string `json:"id"` ID string `json:"id"`
AlterIds uint16 `json:"alterId"` AlterIds uint16 `json:"alterId"`
Security string `json:"security"` Security string `json:"security"`
Experiments string `json:"experiments"`
} }
// Build implements Buildable // Build implements Buildable
@ -43,6 +44,7 @@ func (a *VMessAccount) Build() *vmess.Account {
SecuritySettings: &protocol.SecurityConfig{ SecuritySettings: &protocol.SecurityConfig{
Type: st, Type: st,
}, },
TestsEnabled: a.Experiments,
} }
} }

View file

@ -1,6 +1,8 @@
package vmess package vmess
import ( import (
"strings"
"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/uuid" "github.com/xtls/xray-core/common/uuid"
@ -14,6 +16,8 @@ type MemoryAccount struct {
AlterIDs []*protocol.ID 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
AuthenticatedLengthExperiment bool
} }
// AnyValidID returns an ID that is either the main ID or one of the alternative IDs if any. // AnyValidID returns an ID that is either the main ID or one of the alternative IDs if any.
@ -41,9 +45,14 @@ func (a *Account) AsAccount() (protocol.Account, error) {
return nil, newError("failed to parse ID").Base(err).AtError() return nil, newError("failed to parse ID").Base(err).AtError()
} }
protoID := protocol.NewID(id) protoID := protocol.NewID(id)
var AuthenticatedLength bool
if strings.Contains(a.TestsEnabled, "AuthenticatedLength") {
AuthenticatedLength = true
}
return &MemoryAccount{ return &MemoryAccount{
ID: protoID, ID: protoID,
AlterIDs: protocol.NewAlterIDs(protoID, uint16(a.AlterId)), AlterIDs: protocol.NewAlterIDs(protoID, uint16(a.AlterId)),
Security: a.SecuritySettings.GetSecurityType(), Security: a.SecuritySettings.GetSecurityType(),
AuthenticatedLengthExperiment: AuthenticatedLength,
}, nil }, nil
} }

View file

@ -7,6 +7,8 @@ import (
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/crypto"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
) )
@ -116,3 +118,11 @@ func (s *ShakeSizeParser) NextPaddingLen() uint16 {
func (s *ShakeSizeParser) MaxPaddingLen() uint16 { func (s *ShakeSizeParser) MaxPaddingLen() uint16 {
return 64 return 64
} }
type AEADSizeParser struct {
crypto.AEADChunkSizeParser
}
func NewAEADSizeParser(auth *crypto.AEADAuthenticator) *AEADSizeParser {
return &AEADSizeParser{crypto.AEADChunkSizeParser{Auth: auth}}
}

View file

@ -171,6 +171,17 @@ func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
NonceGenerator: GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())), NonceGenerator: GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())),
AdditionalDataGenerator: crypto.GenerateEmptyBytes(), AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
} }
if request.Option.Has(protocol.RequestOptionAuthenticatedLength) {
AuthenticatedLengthKey := vmessaead.KDF16(c.requestBodyKey[:], "auth_len")
AuthenticatedLengthKeyAEAD := crypto.NewAesGcm(AuthenticatedLengthKey)
lengthAuth := &crypto.AEADAuthenticator{
AEAD: AuthenticatedLengthKeyAEAD,
NonceGenerator: GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())),
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
}
sizeParser = NewAEADSizeParser(lengthAuth)
}
return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding) return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding)
case protocol.SecurityType_CHACHA20_POLY1305: case protocol.SecurityType_CHACHA20_POLY1305:
aead, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(c.requestBodyKey[:])) aead, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(c.requestBodyKey[:]))
@ -181,6 +192,18 @@ func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
NonceGenerator: GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())), NonceGenerator: GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())),
AdditionalDataGenerator: crypto.GenerateEmptyBytes(), AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
} }
if request.Option.Has(protocol.RequestOptionAuthenticatedLength) {
AuthenticatedLengthKey := vmessaead.KDF16(c.requestBodyKey[:], "auth_len")
AuthenticatedLengthKeyAEAD, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(AuthenticatedLengthKey))
common.Must(err)
lengthAuth := &crypto.AEADAuthenticator{
AEAD: AuthenticatedLengthKeyAEAD,
NonceGenerator: GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())),
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
}
sizeParser = NewAEADSizeParser(lengthAuth)
}
return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding) return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding)
default: default:
panic("Unknown security type.") panic("Unknown security type.")
@ -312,6 +335,17 @@ func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read
NonceGenerator: GenerateChunkNonce(c.responseBodyIV[:], uint32(aead.NonceSize())), NonceGenerator: GenerateChunkNonce(c.responseBodyIV[:], uint32(aead.NonceSize())),
AdditionalDataGenerator: crypto.GenerateEmptyBytes(), AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
} }
if request.Option.Has(protocol.RequestOptionAuthenticatedLength) {
AuthenticatedLengthKey := vmessaead.KDF16(c.requestBodyKey[:], "auth_len")
AuthenticatedLengthKeyAEAD := crypto.NewAesGcm(AuthenticatedLengthKey)
lengthAuth := &crypto.AEADAuthenticator{
AEAD: AuthenticatedLengthKeyAEAD,
NonceGenerator: GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())),
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
}
sizeParser = NewAEADSizeParser(lengthAuth)
}
return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding) return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding)
case protocol.SecurityType_CHACHA20_POLY1305: case protocol.SecurityType_CHACHA20_POLY1305:
aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(c.responseBodyKey[:])) aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(c.responseBodyKey[:]))
@ -321,6 +355,18 @@ func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read
NonceGenerator: GenerateChunkNonce(c.responseBodyIV[:], uint32(aead.NonceSize())), NonceGenerator: GenerateChunkNonce(c.responseBodyIV[:], uint32(aead.NonceSize())),
AdditionalDataGenerator: crypto.GenerateEmptyBytes(), AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
} }
if request.Option.Has(protocol.RequestOptionAuthenticatedLength) {
AuthenticatedLengthKey := vmessaead.KDF16(c.requestBodyKey[:], "auth_len")
AuthenticatedLengthKeyAEAD, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(AuthenticatedLengthKey))
common.Must(err)
lengthAuth := &crypto.AEADAuthenticator{
AEAD: AuthenticatedLengthKeyAEAD,
NonceGenerator: GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())),
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
}
sizeParser = NewAEADSizeParser(lengthAuth)
}
return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding) return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding)
default: default:
panic("Unknown security type.") panic("Unknown security type.")

View file

@ -362,6 +362,17 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())), NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())),
AdditionalDataGenerator: crypto.GenerateEmptyBytes(), AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
} }
if request.Option.Has(protocol.RequestOptionAuthenticatedLength) {
AuthenticatedLengthKey := vmessaead.KDF16(s.requestBodyKey[:], "auth_len")
AuthenticatedLengthKeyAEAD := crypto.NewAesGcm(AuthenticatedLengthKey)
lengthAuth := &crypto.AEADAuthenticator{
AEAD: AuthenticatedLengthKeyAEAD,
NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())),
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
}
sizeParser = NewAEADSizeParser(lengthAuth)
}
return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding) return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding)
case protocol.SecurityType_CHACHA20_POLY1305: case protocol.SecurityType_CHACHA20_POLY1305:
@ -372,6 +383,18 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())), NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())),
AdditionalDataGenerator: crypto.GenerateEmptyBytes(), AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
} }
if request.Option.Has(protocol.RequestOptionAuthenticatedLength) {
AuthenticatedLengthKey := vmessaead.KDF16(s.requestBodyKey[:], "auth_len")
AuthenticatedLengthKeyAEAD, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(AuthenticatedLengthKey))
common.Must(err)
lengthAuth := &crypto.AEADAuthenticator{
AEAD: AuthenticatedLengthKeyAEAD,
NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())),
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
}
sizeParser = NewAEADSizeParser(lengthAuth)
}
return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding) return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding)
default: default:
@ -480,6 +503,17 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ
NonceGenerator: GenerateChunkNonce(s.responseBodyIV[:], uint32(aead.NonceSize())), NonceGenerator: GenerateChunkNonce(s.responseBodyIV[:], uint32(aead.NonceSize())),
AdditionalDataGenerator: crypto.GenerateEmptyBytes(), AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
} }
if request.Option.Has(protocol.RequestOptionAuthenticatedLength) {
AuthenticatedLengthKey := vmessaead.KDF16(s.requestBodyKey[:], "auth_len")
AuthenticatedLengthKeyAEAD := crypto.NewAesGcm(AuthenticatedLengthKey)
lengthAuth := &crypto.AEADAuthenticator{
AEAD: AuthenticatedLengthKeyAEAD,
NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())),
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
}
sizeParser = NewAEADSizeParser(lengthAuth)
}
return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding) return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding)
case protocol.SecurityType_CHACHA20_POLY1305: case protocol.SecurityType_CHACHA20_POLY1305:
@ -490,6 +524,18 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ
NonceGenerator: GenerateChunkNonce(s.responseBodyIV[:], uint32(aead.NonceSize())), NonceGenerator: GenerateChunkNonce(s.responseBodyIV[:], uint32(aead.NonceSize())),
AdditionalDataGenerator: crypto.GenerateEmptyBytes(), AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
} }
if request.Option.Has(protocol.RequestOptionAuthenticatedLength) {
AuthenticatedLengthKey := vmessaead.KDF16(s.requestBodyKey[:], "auth_len")
AuthenticatedLengthKeyAEAD, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(AuthenticatedLengthKey))
common.Must(err)
lengthAuth := &crypto.AEADAuthenticator{
AEAD: AuthenticatedLengthKeyAEAD,
NonceGenerator: GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())),
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
}
sizeParser = NewAEADSizeParser(lengthAuth)
}
return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding) return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding)
default: default:

View file

@ -119,6 +119,10 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
request.Option.Clear(protocol.RequestOptionChunkMasking) request.Option.Clear(protocol.RequestOptionChunkMasking)
} }
if account.AuthenticatedLengthExperiment {
request.Option.Set(protocol.RequestOptionAuthenticatedLength)
}
input := link.Reader input := link.Reader
output := link.Writer output := link.Writer

View file

@ -1330,3 +1330,110 @@ func TestVMessZero(t *testing.T) {
t.Error(err) t.Error(err)
} }
} }
func TestVMessGCMLengthAuth(t *testing.T) {
tcpServer := tcp.Server{
MsgProcessor: xor,
}
dest, err := tcpServer.Start()
common.Must(err)
defer tcpServer.Close()
userID := protocol.NewID(uuid.New())
serverPort := tcp.PickPort()
serverConfig := &core.Config{
App: []*serial.TypedMessage{
serial.ToTypedMessage(&log.Config{
ErrorLogLevel: clog.Severity_Debug,
ErrorLogType: log.LogType_Console,
}),
},
Inbound: []*core.InboundHandlerConfig{
{
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
PortRange: net.SinglePortRange(serverPort),
Listen: net.NewIPOrDomain(net.LocalHostIP),
}),
ProxySettings: serial.ToTypedMessage(&inbound.Config{
User: []*protocol.User{
{
Account: serial.ToTypedMessage(&vmess.Account{
Id: userID.String(),
AlterId: 64,
}),
},
},
}),
},
},
Outbound: []*core.OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
},
},
}
clientPort := tcp.PickPort()
clientConfig := &core.Config{
App: []*serial.TypedMessage{
serial.ToTypedMessage(&log.Config{
ErrorLogLevel: clog.Severity_Debug,
ErrorLogType: log.LogType_Console,
}),
},
Inbound: []*core.InboundHandlerConfig{
{
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
PortRange: net.SinglePortRange(clientPort),
Listen: net.NewIPOrDomain(net.LocalHostIP),
}),
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
Address: net.NewIPOrDomain(dest.Address),
Port: uint32(dest.Port),
NetworkList: &net.NetworkList{
Network: []net.Network{net.Network_TCP},
},
}),
},
},
Outbound: []*core.OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&outbound.Config{
Receiver: []*protocol.ServerEndpoint{
{
Address: net.NewIPOrDomain(net.LocalHostIP),
Port: uint32(serverPort),
User: []*protocol.User{
{
Account: serial.ToTypedMessage(&vmess.Account{
Id: userID.String(),
AlterId: 64,
SecuritySettings: &protocol.SecurityConfig{
Type: protocol.SecurityType_AES128_GCM,
},
TestsEnabled: "AuthenticatedLength",
}),
},
},
},
},
}),
},
},
}
servers, err := InitializeServerConfigs(serverConfig, clientConfig)
if err != nil {
t.Fatal("Failed to initialize all servers: ", err.Error())
}
defer CloseAllServers(servers)
var errg errgroup.Group
for i := 0; i < 10; i++ {
errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*40))
}
if err := errg.Wait(); err != nil {
t.Error(err)
}
}