mirror of
https://github.com/v2fly/v2ray-core.git
synced 2024-12-30 05:56:54 -05:00
update frame header masking strategy
This commit is contained in:
parent
104446afdf
commit
3c032f0d53
@ -4,6 +4,7 @@ import (
|
||||
"crypto/cipher"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/sha3"
|
||||
"v2ray.com/core/common"
|
||||
"v2ray.com/core/common/buf"
|
||||
"v2ray.com/core/common/errors"
|
||||
@ -67,11 +68,56 @@ func (v *AEADAuthenticator) Seal(dst, plainText []byte) ([]byte, error) {
|
||||
return v.AEAD.Seal(dst, iv, plainText, additionalData), nil
|
||||
}
|
||||
|
||||
type Uint16Generator interface {
|
||||
Next() uint16
|
||||
}
|
||||
|
||||
type StaticUint16Generator uint16
|
||||
|
||||
func (g StaticUint16Generator) Next() uint16 {
|
||||
return uint16(g)
|
||||
}
|
||||
|
||||
type MultiplyUint16Generator struct {
|
||||
base uint16
|
||||
value uint16
|
||||
}
|
||||
|
||||
func NewMultiplyUint16Generator(base uint16) *MultiplyUint16Generator {
|
||||
return &MultiplyUint16Generator{
|
||||
base: base,
|
||||
value: 1,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *MultiplyUint16Generator) Next() uint16 {
|
||||
g.value *= g.base
|
||||
return g.value
|
||||
}
|
||||
|
||||
type ShakeUint16Generator struct {
|
||||
shake sha3.ShakeHash
|
||||
buffer [2]byte
|
||||
}
|
||||
|
||||
func NewShakeUint16Generator(nonce []byte) *ShakeUint16Generator {
|
||||
shake := sha3.NewShake128()
|
||||
shake.Write(nonce)
|
||||
return &ShakeUint16Generator{
|
||||
shake: shake,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *ShakeUint16Generator) Next() uint16 {
|
||||
g.shake.Read(g.buffer[:])
|
||||
return serial.BytesToUint16(g.buffer[:])
|
||||
}
|
||||
|
||||
type AuthenticationReader struct {
|
||||
auth Authenticator
|
||||
buffer *buf.Buffer
|
||||
reader io.Reader
|
||||
sizeMask uint16
|
||||
sizeMask Uint16Generator
|
||||
|
||||
chunk []byte
|
||||
}
|
||||
@ -80,7 +126,7 @@ const (
|
||||
readerBufferSize = 32 * 1024
|
||||
)
|
||||
|
||||
func NewAuthenticationReader(auth Authenticator, reader io.Reader, sizeMask uint16) *AuthenticationReader {
|
||||
func NewAuthenticationReader(auth Authenticator, reader io.Reader, sizeMask Uint16Generator) *AuthenticationReader {
|
||||
return &AuthenticationReader{
|
||||
auth: auth,
|
||||
buffer: buf.NewLocal(readerBufferSize),
|
||||
@ -89,11 +135,11 @@ func NewAuthenticationReader(auth Authenticator, reader io.Reader, sizeMask uint
|
||||
}
|
||||
}
|
||||
|
||||
func (v *AuthenticationReader) NextChunk() error {
|
||||
func (v *AuthenticationReader) NextChunk(mask uint16) error {
|
||||
if v.buffer.Len() < 2 {
|
||||
return errInsufficientBuffer
|
||||
}
|
||||
size := int(serial.BytesToUint16(v.buffer.BytesTo(2)) ^ v.sizeMask)
|
||||
size := int(serial.BytesToUint16(v.buffer.BytesTo(2)) ^ mask)
|
||||
if size > v.buffer.Len()-2 {
|
||||
return errInsufficientBuffer
|
||||
}
|
||||
@ -136,8 +182,9 @@ func (v *AuthenticationReader) EnsureChunk() error {
|
||||
atHead = true
|
||||
}
|
||||
|
||||
mask := v.sizeMask.Next()
|
||||
for {
|
||||
err := v.NextChunk()
|
||||
err := v.NextChunk(mask)
|
||||
if err != errInsufficientBuffer {
|
||||
return err
|
||||
}
|
||||
@ -173,10 +220,10 @@ type AuthenticationWriter struct {
|
||||
auth Authenticator
|
||||
buffer []byte
|
||||
writer io.Writer
|
||||
sizeMask uint16
|
||||
sizeMask Uint16Generator
|
||||
}
|
||||
|
||||
func NewAuthenticationWriter(auth Authenticator, writer io.Writer, sizeMask uint16) *AuthenticationWriter {
|
||||
func NewAuthenticationWriter(auth Authenticator, writer io.Writer, sizeMask Uint16Generator) *AuthenticationWriter {
|
||||
return &AuthenticationWriter{
|
||||
auth: auth,
|
||||
buffer: make([]byte, 32*1024),
|
||||
@ -191,7 +238,7 @@ func (v *AuthenticationWriter) Write(b []byte) (int, error) {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
size := uint16(len(cipherChunk)) ^ v.sizeMask
|
||||
size := uint16(len(cipherChunk)) ^ v.sizeMask.Next()
|
||||
serial.Uint16ToBytes(size, v.buffer[:0])
|
||||
_, err = v.writer.Write(v.buffer[:2+len(cipherChunk)])
|
||||
return len(b), err
|
||||
|
@ -10,15 +10,12 @@ import (
|
||||
|
||||
"v2ray.com/core/common/buf"
|
||||
. "v2ray.com/core/common/crypto"
|
||||
"v2ray.com/core/common/dice"
|
||||
"v2ray.com/core/testing/assert"
|
||||
)
|
||||
|
||||
func TestAuthenticationReaderWriter(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
sizeMask := uint16(dice.Roll(65536))
|
||||
|
||||
key := make([]byte, 16)
|
||||
rand.Read(key)
|
||||
block, err := aes.NewCipher(key)
|
||||
@ -40,7 +37,7 @@ func TestAuthenticationReaderWriter(t *testing.T) {
|
||||
Content: iv,
|
||||
},
|
||||
AdditionalDataGenerator: &NoOpBytesGenerator{},
|
||||
}, cache, sizeMask)
|
||||
}, cache, NewShakeUint16Generator([]byte{'a'}))
|
||||
|
||||
nBytes, err := writer.Write(payload)
|
||||
assert.Error(err).IsNil()
|
||||
@ -55,7 +52,7 @@ func TestAuthenticationReaderWriter(t *testing.T) {
|
||||
Content: iv,
|
||||
},
|
||||
AdditionalDataGenerator: &NoOpBytesGenerator{},
|
||||
}, cache, sizeMask)
|
||||
}, cache, NewShakeUint16Generator([]byte{'a'}))
|
||||
|
||||
actualPayload := make([]byte, 16*1024)
|
||||
nBytes, err = reader.Read(actualPayload)
|
||||
@ -70,8 +67,6 @@ func TestAuthenticationReaderWriter(t *testing.T) {
|
||||
func TestAuthenticationReaderWriterPartial(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
sizeMask := uint16(dice.Roll(65536))
|
||||
|
||||
key := make([]byte, 16)
|
||||
rand.Read(key)
|
||||
block, err := aes.NewCipher(key)
|
||||
@ -93,7 +88,7 @@ func TestAuthenticationReaderWriterPartial(t *testing.T) {
|
||||
Content: iv,
|
||||
},
|
||||
AdditionalDataGenerator: &NoOpBytesGenerator{},
|
||||
}, cache, sizeMask)
|
||||
}, cache, NewShakeUint16Generator([]byte{'a', 'b'}))
|
||||
|
||||
writer.Write([]byte{'a', 'b', 'c', 'd'})
|
||||
|
||||
@ -123,7 +118,7 @@ func TestAuthenticationReaderWriterPartial(t *testing.T) {
|
||||
Content: iv,
|
||||
},
|
||||
AdditionalDataGenerator: &NoOpBytesGenerator{},
|
||||
}, pr, sizeMask)
|
||||
}, pr, NewShakeUint16Generator([]byte{'a', 'b'}))
|
||||
|
||||
actualPayload := make([]byte, 7*1024)
|
||||
nBytes, err = reader.Read(actualPayload)
|
||||
|
@ -24,8 +24,7 @@ const (
|
||||
// RequestOptionConnectionReuse indicates client side expects to reuse the connection.
|
||||
RequestOptionConnectionReuse = RequestOption(0x02)
|
||||
|
||||
// RequestOptionCompressedStream indicates request payload is compressed.
|
||||
RequestOptionCompressedStream = RequestOption(0x04)
|
||||
RequestOptionChunkMasking = RequestOption(0x04)
|
||||
)
|
||||
|
||||
func (v RequestOption) Has(option RequestOption) bool {
|
||||
|
@ -119,6 +119,10 @@ func (v *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writ
|
||||
|
||||
func (v *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, writer io.Writer) buf.Writer {
|
||||
var authWriter io.Writer
|
||||
var sizeMask crypto.Uint16Generator = crypto.StaticUint16Generator(0)
|
||||
if request.Option.Has(protocol.RequestOptionChunkMasking) {
|
||||
sizeMask = getSizeMask(v.requestBodyIV)
|
||||
}
|
||||
if request.Security.Is(protocol.SecurityType_NONE) {
|
||||
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
||||
auth := &crypto.AEADAuthenticator{
|
||||
@ -126,7 +130,7 @@ func (v *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
|
||||
NonceGenerator: crypto.NoOpBytesGenerator{},
|
||||
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||
}
|
||||
authWriter = crypto.NewAuthenticationWriter(auth, writer, getSizeMask(v.requestBodyIV))
|
||||
authWriter = crypto.NewAuthenticationWriter(auth, writer, sizeMask)
|
||||
} else {
|
||||
authWriter = writer
|
||||
}
|
||||
@ -139,7 +143,7 @@ func (v *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
|
||||
NonceGenerator: crypto.NoOpBytesGenerator{},
|
||||
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||
}
|
||||
authWriter = crypto.NewAuthenticationWriter(auth, cryptionWriter, 0)
|
||||
authWriter = crypto.NewAuthenticationWriter(auth, cryptionWriter, sizeMask)
|
||||
} else {
|
||||
authWriter = cryptionWriter
|
||||
}
|
||||
@ -155,7 +159,7 @@ func (v *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
|
||||
},
|
||||
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||
}
|
||||
authWriter = crypto.NewAuthenticationWriter(auth, writer, getSizeMask(v.requestBodyIV))
|
||||
authWriter = crypto.NewAuthenticationWriter(auth, writer, sizeMask)
|
||||
} else if request.Security.Is(protocol.SecurityType_CHACHA20_POLY1305) {
|
||||
aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(v.requestBodyKey))
|
||||
|
||||
@ -167,7 +171,7 @@ func (v *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
|
||||
},
|
||||
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||
}
|
||||
authWriter = crypto.NewAuthenticationWriter(auth, writer, getSizeMask(v.requestBodyIV))
|
||||
authWriter = crypto.NewAuthenticationWriter(auth, writer, sizeMask)
|
||||
}
|
||||
|
||||
return buf.NewWriter(authWriter)
|
||||
@ -214,14 +218,18 @@ func (v *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.Respon
|
||||
|
||||
func (v *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, reader io.Reader) buf.Reader {
|
||||
var authReader io.Reader
|
||||
var sizeMask crypto.Uint16Generator = crypto.StaticUint16Generator(0)
|
||||
if request.Option.Has(protocol.RequestOptionChunkMasking) {
|
||||
sizeMask = getSizeMask(v.responseBodyIV)
|
||||
}
|
||||
if request.Security.Is(protocol.SecurityType_NONE) {
|
||||
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
||||
auth := &crypto.AEADAuthenticator{
|
||||
AEAD: new(FnvAuthenticator),
|
||||
AEAD: NoOpAuthenticator{},
|
||||
NonceGenerator: crypto.NoOpBytesGenerator{},
|
||||
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||
}
|
||||
authReader = crypto.NewAuthenticationReader(auth, reader, getSizeMask(v.responseBodyIV))
|
||||
authReader = crypto.NewAuthenticationReader(auth, reader, sizeMask)
|
||||
} else {
|
||||
authReader = reader
|
||||
}
|
||||
@ -232,7 +240,7 @@ func (v *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read
|
||||
NonceGenerator: crypto.NoOpBytesGenerator{},
|
||||
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||
}
|
||||
authReader = crypto.NewAuthenticationReader(auth, v.responseReader, 0)
|
||||
authReader = crypto.NewAuthenticationReader(auth, v.responseReader, sizeMask)
|
||||
} else {
|
||||
authReader = v.responseReader
|
||||
}
|
||||
@ -248,7 +256,7 @@ func (v *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read
|
||||
},
|
||||
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||
}
|
||||
authReader = crypto.NewAuthenticationReader(auth, reader, getSizeMask(v.responseBodyIV))
|
||||
authReader = crypto.NewAuthenticationReader(auth, reader, sizeMask)
|
||||
} else if request.Security.Is(protocol.SecurityType_CHACHA20_POLY1305) {
|
||||
aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(v.responseBodyKey))
|
||||
|
||||
@ -260,7 +268,7 @@ func (v *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read
|
||||
},
|
||||
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||
}
|
||||
authReader = crypto.NewAuthenticationReader(auth, reader, getSizeMask(v.responseBodyIV))
|
||||
authReader = crypto.NewAuthenticationReader(auth, reader, sizeMask)
|
||||
}
|
||||
|
||||
return buf.NewReader(authReader)
|
||||
|
@ -94,12 +94,8 @@ func (h *SessionHistory) run() {
|
||||
}
|
||||
}
|
||||
|
||||
func getSizeMask(b []byte) uint16 {
|
||||
mask := uint16(0)
|
||||
for i := 0; i < len(b); i += 2 {
|
||||
mask ^= serial.BytesToUint16(b[i : i+2])
|
||||
}
|
||||
return mask
|
||||
func getSizeMask(b []byte) crypto.Uint16Generator {
|
||||
return crypto.NewShakeUint16Generator(b)
|
||||
}
|
||||
|
||||
type ServerSession struct {
|
||||
@ -245,6 +241,10 @@ func (v *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
|
||||
|
||||
func (v *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reader io.Reader) buf.Reader {
|
||||
var authReader io.Reader
|
||||
var sizeMask crypto.Uint16Generator = crypto.StaticUint16Generator(0)
|
||||
if request.Option.Has(protocol.RequestOptionChunkMasking) {
|
||||
sizeMask = getSizeMask(v.requestBodyIV)
|
||||
}
|
||||
if request.Security.Is(protocol.SecurityType_NONE) {
|
||||
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
||||
auth := &crypto.AEADAuthenticator{
|
||||
@ -252,7 +252,7 @@ func (v *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
|
||||
NonceGenerator: crypto.NoOpBytesGenerator{},
|
||||
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||
}
|
||||
authReader = crypto.NewAuthenticationReader(auth, reader, getSizeMask(v.requestBodyIV))
|
||||
authReader = crypto.NewAuthenticationReader(auth, reader, sizeMask)
|
||||
} else {
|
||||
authReader = reader
|
||||
}
|
||||
@ -265,7 +265,7 @@ func (v *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
|
||||
NonceGenerator: crypto.NoOpBytesGenerator{},
|
||||
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||
}
|
||||
authReader = crypto.NewAuthenticationReader(auth, cryptionReader, 0)
|
||||
authReader = crypto.NewAuthenticationReader(auth, cryptionReader, sizeMask)
|
||||
} else {
|
||||
authReader = cryptionReader
|
||||
}
|
||||
@ -281,7 +281,7 @@ func (v *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
|
||||
},
|
||||
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||
}
|
||||
authReader = crypto.NewAuthenticationReader(auth, reader, getSizeMask(v.requestBodyIV))
|
||||
authReader = crypto.NewAuthenticationReader(auth, reader, sizeMask)
|
||||
} else if request.Security.Is(protocol.SecurityType_CHACHA20_POLY1305) {
|
||||
aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(v.requestBodyKey))
|
||||
|
||||
@ -293,7 +293,7 @@ func (v *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
|
||||
},
|
||||
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||
}
|
||||
authReader = crypto.NewAuthenticationReader(auth, reader, getSizeMask(v.requestBodyIV))
|
||||
authReader = crypto.NewAuthenticationReader(auth, reader, sizeMask)
|
||||
}
|
||||
|
||||
return buf.NewReader(authReader)
|
||||
@ -318,14 +318,18 @@ func (v *ServerSession) EncodeResponseHeader(header *protocol.ResponseHeader, wr
|
||||
|
||||
func (v *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writer io.Writer) buf.Writer {
|
||||
var authWriter io.Writer
|
||||
var sizeMask crypto.Uint16Generator = crypto.StaticUint16Generator(0)
|
||||
if request.Option.Has(protocol.RequestOptionChunkMasking) {
|
||||
sizeMask = getSizeMask(v.responseBodyIV)
|
||||
}
|
||||
if request.Security.Is(protocol.SecurityType_NONE) {
|
||||
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
||||
auth := &crypto.AEADAuthenticator{
|
||||
AEAD: new(FnvAuthenticator),
|
||||
AEAD: NoOpAuthenticator{},
|
||||
NonceGenerator: crypto.NoOpBytesGenerator{},
|
||||
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||
}
|
||||
authWriter = crypto.NewAuthenticationWriter(auth, writer, getSizeMask(v.responseBodyIV))
|
||||
authWriter = crypto.NewAuthenticationWriter(auth, writer, sizeMask)
|
||||
} else {
|
||||
authWriter = writer
|
||||
}
|
||||
@ -336,7 +340,7 @@ func (v *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ
|
||||
NonceGenerator: crypto.NoOpBytesGenerator{},
|
||||
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||
}
|
||||
authWriter = crypto.NewAuthenticationWriter(auth, v.responseWriter, 0)
|
||||
authWriter = crypto.NewAuthenticationWriter(auth, v.responseWriter, sizeMask)
|
||||
} else {
|
||||
authWriter = v.responseWriter
|
||||
}
|
||||
@ -352,7 +356,7 @@ func (v *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ
|
||||
},
|
||||
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||
}
|
||||
authWriter = crypto.NewAuthenticationWriter(auth, writer, getSizeMask(v.responseBodyIV))
|
||||
authWriter = crypto.NewAuthenticationWriter(auth, writer, sizeMask)
|
||||
} else if request.Security.Is(protocol.SecurityType_CHACHA20_POLY1305) {
|
||||
aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(v.responseBodyKey))
|
||||
|
||||
@ -364,7 +368,7 @@ func (v *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ
|
||||
},
|
||||
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||
}
|
||||
authWriter = crypto.NewAuthenticationWriter(auth, writer, getSizeMask(v.responseBodyIV))
|
||||
authWriter = crypto.NewAuthenticationWriter(auth, writer, sizeMask)
|
||||
}
|
||||
|
||||
return buf.NewWriter(authWriter)
|
||||
|
@ -93,6 +93,10 @@ func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial
|
||||
account := rawAccount.(*vmess.InternalAccount)
|
||||
request.Security = account.Security
|
||||
|
||||
if request.Security.Is(protocol.SecurityType_AES128_GCM) || request.Security.Is(protocol.SecurityType_NONE) || request.Security.Is(protocol.SecurityType_CHACHA20_POLY1305) {
|
||||
request.Option.Set(protocol.RequestOptionChunkMasking)
|
||||
}
|
||||
|
||||
conn.SetReusable(true)
|
||||
if conn.Reusable() { // Conn reuse may be disabled on transportation layer
|
||||
request.Option.Set(protocol.RequestOptionConnectionReuse)
|
||||
|
Loading…
Reference in New Issue
Block a user