update frame header masking strategy

pull/432/head
Darien Raymond 8 years ago
parent 104446afdf
commit 3c032f0d53
No known key found for this signature in database
GPG Key ID: 7251FFA14BB18169

@ -4,6 +4,7 @@ import (
"crypto/cipher" "crypto/cipher"
"io" "io"
"golang.org/x/crypto/sha3"
"v2ray.com/core/common" "v2ray.com/core/common"
"v2ray.com/core/common/buf" "v2ray.com/core/common/buf"
"v2ray.com/core/common/errors" "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 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 { type AuthenticationReader struct {
auth Authenticator auth Authenticator
buffer *buf.Buffer buffer *buf.Buffer
reader io.Reader reader io.Reader
sizeMask uint16 sizeMask Uint16Generator
chunk []byte chunk []byte
} }
@ -80,7 +126,7 @@ const (
readerBufferSize = 32 * 1024 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{ return &AuthenticationReader{
auth: auth, auth: auth,
buffer: buf.NewLocal(readerBufferSize), 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 { if v.buffer.Len() < 2 {
return errInsufficientBuffer 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 { if size > v.buffer.Len()-2 {
return errInsufficientBuffer return errInsufficientBuffer
} }
@ -136,8 +182,9 @@ func (v *AuthenticationReader) EnsureChunk() error {
atHead = true atHead = true
} }
mask := v.sizeMask.Next()
for { for {
err := v.NextChunk() err := v.NextChunk(mask)
if err != errInsufficientBuffer { if err != errInsufficientBuffer {
return err return err
} }
@ -173,10 +220,10 @@ type AuthenticationWriter struct {
auth Authenticator auth Authenticator
buffer []byte buffer []byte
writer io.Writer 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{ return &AuthenticationWriter{
auth: auth, auth: auth,
buffer: make([]byte, 32*1024), buffer: make([]byte, 32*1024),
@ -191,7 +238,7 @@ func (v *AuthenticationWriter) Write(b []byte) (int, error) {
return 0, err return 0, err
} }
size := uint16(len(cipherChunk)) ^ v.sizeMask size := uint16(len(cipherChunk)) ^ v.sizeMask.Next()
serial.Uint16ToBytes(size, v.buffer[:0]) serial.Uint16ToBytes(size, v.buffer[:0])
_, err = v.writer.Write(v.buffer[:2+len(cipherChunk)]) _, err = v.writer.Write(v.buffer[:2+len(cipherChunk)])
return len(b), err return len(b), err

@ -10,15 +10,12 @@ import (
"v2ray.com/core/common/buf" "v2ray.com/core/common/buf"
. "v2ray.com/core/common/crypto" . "v2ray.com/core/common/crypto"
"v2ray.com/core/common/dice"
"v2ray.com/core/testing/assert" "v2ray.com/core/testing/assert"
) )
func TestAuthenticationReaderWriter(t *testing.T) { func TestAuthenticationReaderWriter(t *testing.T) {
assert := assert.On(t) assert := assert.On(t)
sizeMask := uint16(dice.Roll(65536))
key := make([]byte, 16) key := make([]byte, 16)
rand.Read(key) rand.Read(key)
block, err := aes.NewCipher(key) block, err := aes.NewCipher(key)
@ -40,7 +37,7 @@ func TestAuthenticationReaderWriter(t *testing.T) {
Content: iv, Content: iv,
}, },
AdditionalDataGenerator: &NoOpBytesGenerator{}, AdditionalDataGenerator: &NoOpBytesGenerator{},
}, cache, sizeMask) }, cache, NewShakeUint16Generator([]byte{'a'}))
nBytes, err := writer.Write(payload) nBytes, err := writer.Write(payload)
assert.Error(err).IsNil() assert.Error(err).IsNil()
@ -55,7 +52,7 @@ func TestAuthenticationReaderWriter(t *testing.T) {
Content: iv, Content: iv,
}, },
AdditionalDataGenerator: &NoOpBytesGenerator{}, AdditionalDataGenerator: &NoOpBytesGenerator{},
}, cache, sizeMask) }, cache, NewShakeUint16Generator([]byte{'a'}))
actualPayload := make([]byte, 16*1024) actualPayload := make([]byte, 16*1024)
nBytes, err = reader.Read(actualPayload) nBytes, err = reader.Read(actualPayload)
@ -70,8 +67,6 @@ func TestAuthenticationReaderWriter(t *testing.T) {
func TestAuthenticationReaderWriterPartial(t *testing.T) { func TestAuthenticationReaderWriterPartial(t *testing.T) {
assert := assert.On(t) assert := assert.On(t)
sizeMask := uint16(dice.Roll(65536))
key := make([]byte, 16) key := make([]byte, 16)
rand.Read(key) rand.Read(key)
block, err := aes.NewCipher(key) block, err := aes.NewCipher(key)
@ -93,7 +88,7 @@ func TestAuthenticationReaderWriterPartial(t *testing.T) {
Content: iv, Content: iv,
}, },
AdditionalDataGenerator: &NoOpBytesGenerator{}, AdditionalDataGenerator: &NoOpBytesGenerator{},
}, cache, sizeMask) }, cache, NewShakeUint16Generator([]byte{'a', 'b'}))
writer.Write([]byte{'a', 'b', 'c', 'd'}) writer.Write([]byte{'a', 'b', 'c', 'd'})
@ -123,7 +118,7 @@ func TestAuthenticationReaderWriterPartial(t *testing.T) {
Content: iv, Content: iv,
}, },
AdditionalDataGenerator: &NoOpBytesGenerator{}, AdditionalDataGenerator: &NoOpBytesGenerator{},
}, pr, sizeMask) }, pr, NewShakeUint16Generator([]byte{'a', 'b'}))
actualPayload := make([]byte, 7*1024) actualPayload := make([]byte, 7*1024)
nBytes, err = reader.Read(actualPayload) nBytes, err = reader.Read(actualPayload)

@ -24,8 +24,7 @@ const (
// RequestOptionConnectionReuse indicates client side expects to reuse the connection. // RequestOptionConnectionReuse indicates client side expects to reuse the connection.
RequestOptionConnectionReuse = RequestOption(0x02) RequestOptionConnectionReuse = RequestOption(0x02)
// RequestOptionCompressedStream indicates request payload is compressed. RequestOptionChunkMasking = RequestOption(0x04)
RequestOptionCompressedStream = RequestOption(0x04)
) )
func (v RequestOption) Has(option RequestOption) bool { 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 { func (v *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, writer io.Writer) buf.Writer {
var authWriter io.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.Security.Is(protocol.SecurityType_NONE) {
if request.Option.Has(protocol.RequestOptionChunkStream) { if request.Option.Has(protocol.RequestOptionChunkStream) {
auth := &crypto.AEADAuthenticator{ auth := &crypto.AEADAuthenticator{
@ -126,7 +130,7 @@ func (v *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
NonceGenerator: crypto.NoOpBytesGenerator{}, NonceGenerator: crypto.NoOpBytesGenerator{},
AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
} }
authWriter = crypto.NewAuthenticationWriter(auth, writer, getSizeMask(v.requestBodyIV)) authWriter = crypto.NewAuthenticationWriter(auth, writer, sizeMask)
} else { } else {
authWriter = writer authWriter = writer
} }
@ -139,7 +143,7 @@ func (v *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
NonceGenerator: crypto.NoOpBytesGenerator{}, NonceGenerator: crypto.NoOpBytesGenerator{},
AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
} }
authWriter = crypto.NewAuthenticationWriter(auth, cryptionWriter, 0) authWriter = crypto.NewAuthenticationWriter(auth, cryptionWriter, sizeMask)
} else { } else {
authWriter = cryptionWriter authWriter = cryptionWriter
} }
@ -155,7 +159,7 @@ func (v *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
}, },
AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, 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) { } else if request.Security.Is(protocol.SecurityType_CHACHA20_POLY1305) {
aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(v.requestBodyKey)) aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(v.requestBodyKey))
@ -167,7 +171,7 @@ func (v *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
}, },
AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
} }
authWriter = crypto.NewAuthenticationWriter(auth, writer, getSizeMask(v.requestBodyIV)) authWriter = crypto.NewAuthenticationWriter(auth, writer, sizeMask)
} }
return buf.NewWriter(authWriter) 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 { func (v *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, reader io.Reader) buf.Reader {
var authReader io.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.Security.Is(protocol.SecurityType_NONE) {
if request.Option.Has(protocol.RequestOptionChunkStream) { if request.Option.Has(protocol.RequestOptionChunkStream) {
auth := &crypto.AEADAuthenticator{ auth := &crypto.AEADAuthenticator{
AEAD: new(FnvAuthenticator), AEAD: NoOpAuthenticator{},
NonceGenerator: crypto.NoOpBytesGenerator{}, NonceGenerator: crypto.NoOpBytesGenerator{},
AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
} }
authReader = crypto.NewAuthenticationReader(auth, reader, getSizeMask(v.responseBodyIV)) authReader = crypto.NewAuthenticationReader(auth, reader, sizeMask)
} else { } else {
authReader = reader authReader = reader
} }
@ -232,7 +240,7 @@ func (v *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read
NonceGenerator: crypto.NoOpBytesGenerator{}, NonceGenerator: crypto.NoOpBytesGenerator{},
AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
} }
authReader = crypto.NewAuthenticationReader(auth, v.responseReader, 0) authReader = crypto.NewAuthenticationReader(auth, v.responseReader, sizeMask)
} else { } else {
authReader = v.responseReader authReader = v.responseReader
} }
@ -248,7 +256,7 @@ func (v *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read
}, },
AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, 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) { } else if request.Security.Is(protocol.SecurityType_CHACHA20_POLY1305) {
aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(v.responseBodyKey)) aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(v.responseBodyKey))
@ -260,7 +268,7 @@ func (v *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read
}, },
AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
} }
authReader = crypto.NewAuthenticationReader(auth, reader, getSizeMask(v.responseBodyIV)) authReader = crypto.NewAuthenticationReader(auth, reader, sizeMask)
} }
return buf.NewReader(authReader) return buf.NewReader(authReader)

@ -94,12 +94,8 @@ func (h *SessionHistory) run() {
} }
} }
func getSizeMask(b []byte) uint16 { func getSizeMask(b []byte) crypto.Uint16Generator {
mask := uint16(0) return crypto.NewShakeUint16Generator(b)
for i := 0; i < len(b); i += 2 {
mask ^= serial.BytesToUint16(b[i : i+2])
}
return mask
} }
type ServerSession struct { 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 { func (v *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reader io.Reader) buf.Reader {
var authReader io.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.Security.Is(protocol.SecurityType_NONE) {
if request.Option.Has(protocol.RequestOptionChunkStream) { if request.Option.Has(protocol.RequestOptionChunkStream) {
auth := &crypto.AEADAuthenticator{ auth := &crypto.AEADAuthenticator{
@ -252,7 +252,7 @@ func (v *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
NonceGenerator: crypto.NoOpBytesGenerator{}, NonceGenerator: crypto.NoOpBytesGenerator{},
AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
} }
authReader = crypto.NewAuthenticationReader(auth, reader, getSizeMask(v.requestBodyIV)) authReader = crypto.NewAuthenticationReader(auth, reader, sizeMask)
} else { } else {
authReader = reader authReader = reader
} }
@ -265,7 +265,7 @@ func (v *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
NonceGenerator: crypto.NoOpBytesGenerator{}, NonceGenerator: crypto.NoOpBytesGenerator{},
AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
} }
authReader = crypto.NewAuthenticationReader(auth, cryptionReader, 0) authReader = crypto.NewAuthenticationReader(auth, cryptionReader, sizeMask)
} else { } else {
authReader = cryptionReader authReader = cryptionReader
} }
@ -281,7 +281,7 @@ func (v *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
}, },
AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, 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) { } else if request.Security.Is(protocol.SecurityType_CHACHA20_POLY1305) {
aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(v.requestBodyKey)) aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(v.requestBodyKey))
@ -293,7 +293,7 @@ func (v *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
}, },
AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
} }
authReader = crypto.NewAuthenticationReader(auth, reader, getSizeMask(v.requestBodyIV)) authReader = crypto.NewAuthenticationReader(auth, reader, sizeMask)
} }
return buf.NewReader(authReader) 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 { func (v *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writer io.Writer) buf.Writer {
var authWriter io.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.Security.Is(protocol.SecurityType_NONE) {
if request.Option.Has(protocol.RequestOptionChunkStream) { if request.Option.Has(protocol.RequestOptionChunkStream) {
auth := &crypto.AEADAuthenticator{ auth := &crypto.AEADAuthenticator{
AEAD: new(FnvAuthenticator), AEAD: NoOpAuthenticator{},
NonceGenerator: crypto.NoOpBytesGenerator{}, NonceGenerator: crypto.NoOpBytesGenerator{},
AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
} }
authWriter = crypto.NewAuthenticationWriter(auth, writer, getSizeMask(v.responseBodyIV)) authWriter = crypto.NewAuthenticationWriter(auth, writer, sizeMask)
} else { } else {
authWriter = writer authWriter = writer
} }
@ -336,7 +340,7 @@ func (v *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ
NonceGenerator: crypto.NoOpBytesGenerator{}, NonceGenerator: crypto.NoOpBytesGenerator{},
AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
} }
authWriter = crypto.NewAuthenticationWriter(auth, v.responseWriter, 0) authWriter = crypto.NewAuthenticationWriter(auth, v.responseWriter, sizeMask)
} else { } else {
authWriter = v.responseWriter authWriter = v.responseWriter
} }
@ -352,7 +356,7 @@ func (v *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ
}, },
AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, 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) { } else if request.Security.Is(protocol.SecurityType_CHACHA20_POLY1305) {
aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(v.responseBodyKey)) aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(v.responseBodyKey))
@ -364,7 +368,7 @@ func (v *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ
}, },
AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
} }
authWriter = crypto.NewAuthenticationWriter(auth, writer, getSizeMask(v.responseBodyIV)) authWriter = crypto.NewAuthenticationWriter(auth, writer, sizeMask)
} }
return buf.NewWriter(authWriter) return buf.NewWriter(authWriter)

@ -93,6 +93,10 @@ func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial
account := rawAccount.(*vmess.InternalAccount) account := rawAccount.(*vmess.InternalAccount)
request.Security = account.Security 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) conn.SetReusable(true)
if conn.Reusable() { // Conn reuse may be disabled on transportation layer if conn.Reusable() { // Conn reuse may be disabled on transportation layer
request.Option.Set(protocol.RequestOptionConnectionReuse) request.Option.Set(protocol.RequestOptionConnectionReuse)

Loading…
Cancel
Save