Browse Source

global padding in vmess protocol

Darien Raymond 7 years ago
parent
commit
f54a8735ab

+ 37 - 10
common/crypto/auth.go

@@ -2,6 +2,7 @@ package crypto
 
 import (
 	"crypto/cipher"
+	"crypto/rand"
 	"io"
 
 	"v2ray.com/core/common"
@@ -85,31 +86,39 @@ type AuthenticationReader struct {
 	reader       *buf.BufferedReader
 	sizeParser   ChunkSizeDecoder
 	transferType protocol.TransferType
+	padding      PaddingLengthGenerator
 	size         int32
+	paddingLen   int32
 }
 
-func NewAuthenticationReader(auth Authenticator, sizeParser ChunkSizeDecoder, reader io.Reader, transferType protocol.TransferType) *AuthenticationReader {
+func NewAuthenticationReader(auth Authenticator, sizeParser ChunkSizeDecoder, reader io.Reader, transferType protocol.TransferType, paddingLen PaddingLengthGenerator) *AuthenticationReader {
 	return &AuthenticationReader{
 		auth:         auth,
 		reader:       &buf.BufferedReader{Reader: buf.NewReader(reader)},
 		sizeParser:   sizeParser,
 		transferType: transferType,
+		padding:      paddingLen,
 		size:         -1,
+		paddingLen:   -1,
 	}
 }
 
-func (r *AuthenticationReader) readSize() (int32, error) {
+func (r *AuthenticationReader) readSize() (int32, int32, error) {
 	if r.size != -1 {
 		s := r.size
 		r.size = -1
-		return s, nil
+		return s, r.paddingLen, nil
 	}
 	sizeBytes := make([]byte, r.sizeParser.SizeBytes())
 	if _, err := io.ReadFull(r.reader, sizeBytes); err != nil {
-		return 0, err
+		return 0, 0, err
+	}
+	var padding int32
+	if r.padding != nil {
+		padding = int32(r.padding.NextPaddingLen())
 	}
 	size, err := r.sizeParser.Decode(sizeBytes)
-	return int32(size), err
+	return int32(size), padding, err
 }
 
 var errSoft = newError("waiting for more data")
@@ -119,18 +128,19 @@ func (r *AuthenticationReader) readInternal(soft bool) (*buf.Buffer, error) {
 		return nil, errSoft
 	}
 
-	size, err := r.readSize()
+	size, padding, err := r.readSize()
 	if err != nil {
 		return nil, err
 	}
 
-	if size == -2 || size == int32(r.auth.Overhead()) {
+	if size == -2 || size == int32(r.auth.Overhead())+padding {
 		r.size = -2
 		return nil, io.EOF
 	}
 
 	if soft && size > r.reader.BufferedBytes() {
 		r.size = size
+		r.paddingLen = padding
 		return nil, errSoft
 	}
 
@@ -140,6 +150,8 @@ func (r *AuthenticationReader) readInternal(soft bool) (*buf.Buffer, error) {
 		return nil, err
 	}
 
+	size -= padding
+
 	rb, err := r.auth.Open(b.BytesTo(0), b.BytesTo(size))
 	if err != nil {
 		b.Release()
@@ -179,23 +191,29 @@ type AuthenticationWriter struct {
 	writer       buf.Writer
 	sizeParser   ChunkSizeEncoder
 	transferType protocol.TransferType
+	padding      PaddingLengthGenerator
 }
 
-func NewAuthenticationWriter(auth Authenticator, sizeParser ChunkSizeEncoder, writer io.Writer, transferType protocol.TransferType) *AuthenticationWriter {
+func NewAuthenticationWriter(auth Authenticator, sizeParser ChunkSizeEncoder, writer io.Writer, transferType protocol.TransferType, padding PaddingLengthGenerator) *AuthenticationWriter {
 	return &AuthenticationWriter{
 		auth:         auth,
 		writer:       buf.NewWriter(writer),
 		sizeParser:   sizeParser,
 		transferType: transferType,
+		padding:      padding,
 	}
 }
 
 func (w *AuthenticationWriter) seal(b *buf.Buffer) (*buf.Buffer, error) {
 	encryptedSize := int(b.Len()) + w.auth.Overhead()
+	paddingSize := 0
+	if w.padding != nil {
+		paddingSize = int(w.padding.NextPaddingLen())
+	}
 
 	eb := buf.New()
 	common.Must(eb.Reset(func(bb []byte) (int, error) {
-		w.sizeParser.Encode(uint16(encryptedSize), bb[:0])
+		w.sizeParser.Encode(uint16(encryptedSize+paddingSize), bb[:0])
 		return int(w.sizeParser.SizeBytes()), nil
 	}))
 	if err := eb.AppendSupplier(func(bb []byte) (int, error) {
@@ -205,6 +223,15 @@ func (w *AuthenticationWriter) seal(b *buf.Buffer) (*buf.Buffer, error) {
 		eb.Release()
 		return nil, err
 	}
+	if paddingSize > 0 {
+		if err := eb.AppendSupplier(func(bb []byte) (int, error) {
+			common.Must2(rand.Read(bb[:paddingSize]))
+			return paddingSize, nil
+		}); err != nil {
+			eb.Release()
+			return nil, err
+		}
+	}
 
 	return eb, nil
 }
@@ -212,7 +239,7 @@ func (w *AuthenticationWriter) seal(b *buf.Buffer) (*buf.Buffer, error) {
 func (w *AuthenticationWriter) writeStream(mb buf.MultiBuffer) error {
 	defer mb.Release()
 
-	payloadSize := buf.Size - int32(w.auth.Overhead()) - w.sizeParser.SizeBytes()
+	payloadSize := buf.Size - int32(w.auth.Overhead()) - w.sizeParser.SizeBytes() - 64 /* padding buffer */
 	mb2Write := buf.NewMultiBufferCap(int32(len(mb) + 10))
 
 	for {

+ 5 - 5
common/crypto/auth_test.go

@@ -40,17 +40,17 @@ func TestAuthenticationReaderWriter(t *testing.T) {
 		AEAD:                    aead,
 		NonceGenerator:          GenerateStaticBytes(iv),
 		AdditionalDataGenerator: GenerateEmptyBytes(),
-	}, PlainChunkSizeParser{}, cache, protocol.TransferTypeStream)
+	}, PlainChunkSizeParser{}, cache, protocol.TransferTypeStream, nil)
 
 	assert(writer.WriteMultiBuffer(buf.NewMultiBufferValue(payload)), IsNil)
-	assert(cache.Len(), Equals, int32(82658))
+	assert(cache.Len(), Equals, int32(82676))
 	assert(writer.WriteMultiBuffer(buf.MultiBuffer{}), IsNil)
 
 	reader := NewAuthenticationReader(&AEADAuthenticator{
 		AEAD:                    aead,
 		NonceGenerator:          GenerateStaticBytes(iv),
 		AdditionalDataGenerator: GenerateEmptyBytes(),
-	}, PlainChunkSizeParser{}, cache, protocol.TransferTypeStream)
+	}, PlainChunkSizeParser{}, cache, protocol.TransferTypeStream, nil)
 
 	var mb buf.MultiBuffer
 
@@ -90,7 +90,7 @@ func TestAuthenticationReaderWriterPacket(t *testing.T) {
 		AEAD:                    aead,
 		NonceGenerator:          GenerateStaticBytes(iv),
 		AdditionalDataGenerator: GenerateEmptyBytes(),
-	}, PlainChunkSizeParser{}, cache, protocol.TransferTypePacket)
+	}, PlainChunkSizeParser{}, cache, protocol.TransferTypePacket, nil)
 
 	var payload buf.MultiBuffer
 	pb1 := buf.New()
@@ -110,7 +110,7 @@ func TestAuthenticationReaderWriterPacket(t *testing.T) {
 		AEAD:                    aead,
 		NonceGenerator:          GenerateStaticBytes(iv),
 		AdditionalDataGenerator: GenerateEmptyBytes(),
-	}, PlainChunkSizeParser{}, cache, protocol.TransferTypePacket)
+	}, PlainChunkSizeParser{}, cache, protocol.TransferTypePacket, nil)
 
 	mb, err := reader.ReadMultiBuffer()
 	assert(err, IsNil)

+ 4 - 0
common/crypto/chunk.go

@@ -20,6 +20,10 @@ type ChunkSizeEncoder interface {
 	Encode(uint16, []byte) []byte
 }
 
+type PaddingLengthGenerator interface {
+	NextPaddingLen() uint16
+}
+
 type PlainChunkSizeParser struct{}
 
 func (PlainChunkSizeParser) SizeBytes() int32 {

+ 2 - 0
common/protocol/headers.go

@@ -36,6 +36,8 @@ const (
 	RequestOptionConnectionReuse bitmask.Byte = 0x02
 
 	RequestOptionChunkMasking bitmask.Byte = 0x04
+
+	RequestOptionGlobalPadding bitmask.Byte = 0x08
 )
 
 type RequestHeader struct {

+ 2 - 2
proxy/shadowsocks/config.go

@@ -182,14 +182,14 @@ func (c *AEADCipher) NewEncryptionWriter(key []byte, iv []byte, writer io.Writer
 	auth := c.createAuthenticator(key, iv)
 	return crypto.NewAuthenticationWriter(auth, &crypto.AEADChunkSizeParser{
 		Auth: auth,
-	}, writer, protocol.TransferTypeStream), nil
+	}, writer, protocol.TransferTypeStream, nil), nil
 }
 
 func (c *AEADCipher) NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error) {
 	auth := c.createAuthenticator(key, iv)
 	return crypto.NewAuthenticationReader(auth, &crypto.AEADChunkSizeParser{
 		Auth: auth,
-	}, reader, protocol.TransferTypeStream), nil
+	}, reader, protocol.TransferTypeStream, nil), nil
 }
 
 func (c *AEADCipher) EncodePacket(key []byte, b *buf.Buffer) error {

+ 4 - 0
proxy/vmess/encoding/auth.go

@@ -107,3 +107,7 @@ func (s *ShakeSizeParser) Encode(size uint16, b []byte) []byte {
 	mask := s.next()
 	return serial.Uint16ToBytes(mask^size, b[:0])
 }
+
+func (s *ShakeSizeParser) NextPaddingLen() uint16 {
+	return s.next() % 64
+}

+ 18 - 8
proxy/vmess/encoding/client.go

@@ -110,6 +110,11 @@ func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
 	if request.Option.Has(protocol.RequestOptionChunkMasking) {
 		sizeParser = NewShakeSizeParser(c.requestBodyIV[:])
 	}
+	var padding crypto.PaddingLengthGenerator = nil
+	if request.Option.Has(protocol.RequestOptionGlobalPadding) {
+		padding = sizeParser.(crypto.PaddingLengthGenerator)
+	}
+
 	switch request.Security {
 	case protocol.SecurityType_NONE:
 		if request.Option.Has(protocol.RequestOptionChunkStream) {
@@ -121,7 +126,7 @@ func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
 				NonceGenerator:          crypto.GenerateEmptyBytes(),
 				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
 			}
-			return crypto.NewAuthenticationWriter(auth, sizeParser, writer, protocol.TransferTypePacket)
+			return crypto.NewAuthenticationWriter(auth, sizeParser, writer, protocol.TransferTypePacket, padding)
 		}
 
 		return buf.NewWriter(writer)
@@ -134,7 +139,7 @@ func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
 				NonceGenerator:          crypto.GenerateEmptyBytes(),
 				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
 			}
-			return crypto.NewAuthenticationWriter(auth, sizeParser, cryptionWriter, request.Command.TransferType())
+			return crypto.NewAuthenticationWriter(auth, sizeParser, cryptionWriter, request.Command.TransferType(), padding)
 		}
 
 		return buf.NewWriter(cryptionWriter)
@@ -147,7 +152,7 @@ func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
 			NonceGenerator:          GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())),
 			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
 		}
-		return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType())
+		return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding)
 	case protocol.SecurityType_CHACHA20_POLY1305:
 		aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(c.requestBodyKey[:]))
 
@@ -156,7 +161,7 @@ func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
 			NonceGenerator:          GenerateChunkNonce(c.requestBodyIV[:], uint32(aead.NonceSize())),
 			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
 		}
-		return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType())
+		return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding)
 	default:
 		panic("Unknown security type.")
 	}
@@ -202,6 +207,11 @@ func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read
 	if request.Option.Has(protocol.RequestOptionChunkMasking) {
 		sizeParser = NewShakeSizeParser(c.responseBodyIV[:])
 	}
+	var padding crypto.PaddingLengthGenerator = nil
+	if request.Option.Has(protocol.RequestOptionGlobalPadding) {
+		padding = sizeParser.(crypto.PaddingLengthGenerator)
+	}
+
 	switch request.Security {
 	case protocol.SecurityType_NONE:
 		if request.Option.Has(protocol.RequestOptionChunkStream) {
@@ -215,7 +225,7 @@ func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read
 				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
 			}
 
-			return crypto.NewAuthenticationReader(auth, sizeParser, reader, protocol.TransferTypePacket)
+			return crypto.NewAuthenticationReader(auth, sizeParser, reader, protocol.TransferTypePacket, padding)
 		}
 
 		return buf.NewReader(reader)
@@ -226,7 +236,7 @@ func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read
 				NonceGenerator:          crypto.GenerateEmptyBytes(),
 				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
 			}
-			return crypto.NewAuthenticationReader(auth, sizeParser, c.responseReader, request.Command.TransferType())
+			return crypto.NewAuthenticationReader(auth, sizeParser, c.responseReader, request.Command.TransferType(), padding)
 		}
 
 		return buf.NewReader(c.responseReader)
@@ -239,7 +249,7 @@ func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read
 			NonceGenerator:          GenerateChunkNonce(c.responseBodyIV[:], uint32(aead.NonceSize())),
 			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
 		}
-		return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType())
+		return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding)
 	case protocol.SecurityType_CHACHA20_POLY1305:
 		aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(c.responseBodyKey[:]))
 
@@ -248,7 +258,7 @@ func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read
 			NonceGenerator:          GenerateChunkNonce(c.responseBodyIV[:], uint32(aead.NonceSize())),
 			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
 		}
-		return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType())
+		return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding)
 	default:
 		panic("Unknown security type.")
 	}

+ 18 - 8
proxy/vmess/encoding/server.go

@@ -238,6 +238,11 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
 	if request.Option.Has(protocol.RequestOptionChunkMasking) {
 		sizeParser = NewShakeSizeParser(s.requestBodyIV[:])
 	}
+	var padding crypto.PaddingLengthGenerator = nil
+	if request.Option.Has(protocol.RequestOptionGlobalPadding) {
+		padding = sizeParser.(crypto.PaddingLengthGenerator)
+	}
+
 	switch request.Security {
 	case protocol.SecurityType_NONE:
 		if request.Option.Has(protocol.RequestOptionChunkStream) {
@@ -250,7 +255,7 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
 				NonceGenerator:          crypto.GenerateEmptyBytes(),
 				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
 			}
-			return crypto.NewAuthenticationReader(auth, sizeParser, reader, protocol.TransferTypePacket)
+			return crypto.NewAuthenticationReader(auth, sizeParser, reader, protocol.TransferTypePacket, padding)
 		}
 
 		return buf.NewReader(reader)
@@ -263,7 +268,7 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
 				NonceGenerator:          crypto.GenerateEmptyBytes(),
 				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
 			}
-			return crypto.NewAuthenticationReader(auth, sizeParser, cryptionReader, request.Command.TransferType())
+			return crypto.NewAuthenticationReader(auth, sizeParser, cryptionReader, request.Command.TransferType(), padding)
 		}
 
 		return buf.NewReader(cryptionReader)
@@ -276,7 +281,7 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
 			NonceGenerator:          GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())),
 			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
 		}
-		return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType())
+		return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding)
 	case protocol.SecurityType_CHACHA20_POLY1305:
 		aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(s.requestBodyKey[:]))
 
@@ -285,7 +290,7 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
 			NonceGenerator:          GenerateChunkNonce(s.requestBodyIV[:], uint32(aead.NonceSize())),
 			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
 		}
-		return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType())
+		return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding)
 	default:
 		panic("Unknown security type.")
 	}
@@ -313,6 +318,11 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ
 	if request.Option.Has(protocol.RequestOptionChunkMasking) {
 		sizeParser = NewShakeSizeParser(s.responseBodyIV[:])
 	}
+	var padding crypto.PaddingLengthGenerator = nil
+	if request.Option.Has(protocol.RequestOptionGlobalPadding) {
+		padding = sizeParser.(crypto.PaddingLengthGenerator)
+	}
+
 	switch request.Security {
 	case protocol.SecurityType_NONE:
 		if request.Option.Has(protocol.RequestOptionChunkStream) {
@@ -325,7 +335,7 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ
 				NonceGenerator:          crypto.GenerateEmptyBytes(),
 				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
 			}
-			return crypto.NewAuthenticationWriter(auth, sizeParser, writer, protocol.TransferTypePacket)
+			return crypto.NewAuthenticationWriter(auth, sizeParser, writer, protocol.TransferTypePacket, padding)
 		}
 
 		return buf.NewWriter(writer)
@@ -336,7 +346,7 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ
 				NonceGenerator:          crypto.GenerateEmptyBytes(),
 				AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
 			}
-			return crypto.NewAuthenticationWriter(auth, sizeParser, s.responseWriter, request.Command.TransferType())
+			return crypto.NewAuthenticationWriter(auth, sizeParser, s.responseWriter, request.Command.TransferType(), padding)
 		}
 
 		return buf.NewWriter(s.responseWriter)
@@ -349,7 +359,7 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ
 			NonceGenerator:          GenerateChunkNonce(s.responseBodyIV[:], uint32(aead.NonceSize())),
 			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
 		}
-		return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType())
+		return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding)
 	case protocol.SecurityType_CHACHA20_POLY1305:
 		aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(s.responseBodyKey[:]))
 
@@ -358,7 +368,7 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ
 			NonceGenerator:          GenerateChunkNonce(s.responseBodyIV[:], uint32(aead.NonceSize())),
 			AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
 		}
-		return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType())
+		return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding)
 	default:
 		panic("Unknown security type.")
 	}