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"
"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…
Cancel
Save