Kaynağa Gözat

implementation for Shadowsocks AEAD

Darien Raymond 8 yıl önce
ebeveyn
işleme
713ebfb203

+ 20 - 0
common/crypto/auth.go

@@ -29,6 +29,26 @@ func (v StaticBytesGenerator) Next() []byte {
 	return v.Content
 }
 
+type IncreasingAEADNonceGenerator struct {
+	nonce []byte
+}
+
+func NewIncreasingAEADNonceGenerator() *IncreasingAEADNonceGenerator {
+	return &IncreasingAEADNonceGenerator{
+		nonce: []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+	}
+}
+
+func (g *IncreasingAEADNonceGenerator) Next() []byte {
+	for i := range g.nonce {
+		g.nonce[i]++
+		if g.nonce[i] != 0 {
+			break
+		}
+	}
+	return g.nonce
+}
+
 type Authenticator interface {
 	NonceSize() int
 	Overhead() int

+ 23 - 0
common/crypto/chunk.go

@@ -34,6 +34,29 @@ func (PlainChunkSizeParser) Decode(b []byte) (uint16, error) {
 	return serial.BytesToUint16(b), nil
 }
 
+type AEADChunkSizeParser struct {
+	Auth *AEADAuthenticator
+}
+
+func (p *AEADChunkSizeParser) SizeBytes() int {
+	return 2 + p.Auth.Overhead()
+}
+
+func (p *AEADChunkSizeParser) Encode(size uint16, b []byte) []byte {
+	b = serial.Uint16ToBytes(size, b)
+	b, err := p.Auth.Seal(b[:0], b)
+	common.Must(err)
+	return b
+}
+
+func (p *AEADChunkSizeParser) Decode(b []byte) (uint16, error) {
+	b, err := p.Auth.Open(b[:0], b)
+	if err != nil {
+		return 0, err
+	}
+	return serial.BytesToUint16(b), nil
+}
+
 type ChunkStreamReader struct {
 	sizeDecoder ChunkSizeDecoder
 	reader      buf.Reader

+ 2 - 9
proxy/shadowsocks/client.go

@@ -105,10 +105,7 @@ func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale
 		}
 
 		requestDone := signal.ExecuteAsync(func() error {
-			if err := buf.Copy(outboundRay.OutboundInput(), bodyWriter, buf.UpdateActivity(timer)); err != nil {
-				return err
-			}
-			return nil
+			return buf.Copy(outboundRay.OutboundInput(), bodyWriter, buf.UpdateActivity(timer))
 		})
 
 		responseDone := signal.ExecuteAsync(func() error {
@@ -119,11 +116,7 @@ func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale
 				return err
 			}
 
-			if err := buf.Copy(responseReader, outboundRay.OutboundOutput(), buf.UpdateActivity(timer)); err != nil {
-				return err
-			}
-
-			return nil
+			return buf.Copy(responseReader, outboundRay.OutboundOutput(), buf.UpdateActivity(timer))
 		})
 
 		if err := signal.ErrorOrFinish2(ctx, requestDone, responseDone); err != nil {

+ 97 - 10
proxy/shadowsocks/config.go

@@ -2,9 +2,17 @@ package shadowsocks
 
 import (
 	"bytes"
+	"crypto/aes"
 	"crypto/cipher"
 	"crypto/md5"
+	"crypto/sha1"
+	"io"
 
+	"golang.org/x/crypto/chacha20poly1305"
+	"golang.org/x/crypto/hkdf"
+
+	"v2ray.com/core/common"
+	"v2ray.com/core/common/buf"
 	"v2ray.com/core/common/crypto"
 	"v2ray.com/core/common/protocol"
 )
@@ -22,6 +30,20 @@ func (v *ShadowsocksAccount) Equals(another protocol.Account) bool {
 	return false
 }
 
+func createAesGcm(key []byte) cipher.AEAD {
+	block, err := aes.NewCipher(key)
+	common.Must(err)
+	gcm, err := cipher.NewGCM(block)
+	common.Must(err)
+	return gcm
+}
+
+func createChacha20Poly1305(key []byte) cipher.AEAD {
+	chacha20, err := chacha20poly1305.New(key)
+	common.Must(err)
+	return chacha20
+}
+
 func (v *Account) GetCipher() (Cipher, error) {
 	switch v.CipherType {
 	case CipherType_AES_128_CFB:
@@ -32,6 +54,24 @@ func (v *Account) GetCipher() (Cipher, error) {
 		return &ChaCha20{IVBytes: 8}, nil
 	case CipherType_CHACHA20_IETF:
 		return &ChaCha20{IVBytes: 12}, nil
+	case CipherType_AES_128_GCM:
+		return &AEADCipher{
+			KeyBytes:        16,
+			IVBytes:         16,
+			AEADAuthCreator: createAesGcm,
+		}, nil
+	case CipherType_AES_256_GCM:
+		return &AEADCipher{
+			KeyBytes:        32,
+			IVBytes:         32,
+			AEADAuthCreator: createAesGcm,
+		}, nil
+	case CipherType_CHACHA20_POLY1305:
+		return &AEADCipher{
+			KeyBytes:        32,
+			IVBytes:         32,
+			AEADAuthCreator: createChacha20Poly1305,
+		}, nil
 	default:
 		return nil, newError("Unsupported cipher.")
 	}
@@ -60,8 +100,8 @@ func (v *Account) GetCipherKey() []byte {
 type Cipher interface {
 	KeySize() int
 	IVSize() int
-	NewEncodingStream(key []byte, iv []byte) (cipher.Stream, error)
-	NewDecodingStream(key []byte, iv []byte) (cipher.Stream, error)
+	NewEncryptionWriter(key []byte, iv []byte, writer io.Writer) (buf.Writer, error)
+	NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error)
 }
 
 type AesCfb struct {
@@ -76,14 +116,54 @@ func (v *AesCfb) IVSize() int {
 	return 16
 }
 
-func (v *AesCfb) NewEncodingStream(key []byte, iv []byte) (cipher.Stream, error) {
+func (v *AesCfb) NewEncryptionWriter(key []byte, iv []byte, writer io.Writer) (buf.Writer, error) {
 	stream := crypto.NewAesEncryptionStream(key, iv)
-	return stream, nil
+	return buf.NewWriter(crypto.NewCryptionWriter(stream, writer)), nil
 }
 
-func (v *AesCfb) NewDecodingStream(key []byte, iv []byte) (cipher.Stream, error) {
+func (v *AesCfb) NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error) {
 	stream := crypto.NewAesDecryptionStream(key, iv)
-	return stream, nil
+	return buf.NewReader(crypto.NewCryptionReader(stream, reader)), nil
+}
+
+type AEADCipher struct {
+	KeyBytes        int
+	IVBytes         int
+	AEADAuthCreator func(key []byte) cipher.AEAD
+}
+
+func (c *AEADCipher) KeySize() int {
+	return c.KeyBytes
+}
+
+func (c *AEADCipher) IVSize() int {
+	return c.IVBytes
+}
+
+func (c *AEADCipher) NewEncryptionWriter(key []byte, iv []byte, writer io.Writer) (buf.Writer, error) {
+	nonce := crypto.NewIncreasingAEADNonceGenerator()
+	subkey := make([]byte, c.KeyBytes)
+	hkdfSHA1(key, iv, subkey)
+	auth := &crypto.AEADAuthenticator{
+		AEAD:           c.AEADAuthCreator(subkey),
+		NonceGenerator: nonce,
+	}
+	return crypto.NewAuthenticationWriter(auth, &crypto.AEADChunkSizeParser{
+		Auth: auth,
+	}, writer, protocol.TransferTypeStream), nil
+}
+
+func (c *AEADCipher) NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error) {
+	nonce := crypto.NewIncreasingAEADNonceGenerator()
+	subkey := make([]byte, c.KeyBytes)
+	hkdfSHA1(key, iv, subkey)
+	auth := &crypto.AEADAuthenticator{
+		AEAD:           c.AEADAuthCreator(subkey),
+		NonceGenerator: nonce,
+	}
+	return crypto.NewAuthenticationReader(auth, &crypto.AEADChunkSizeParser{
+		Auth: auth,
+	}, reader, protocol.TransferTypeStream), nil
 }
 
 type ChaCha20 struct {
@@ -98,12 +178,14 @@ func (v *ChaCha20) IVSize() int {
 	return v.IVBytes
 }
 
-func (v *ChaCha20) NewEncodingStream(key []byte, iv []byte) (cipher.Stream, error) {
-	return crypto.NewChaCha20Stream(key, iv), nil
+func (v *ChaCha20) NewEncryptionWriter(key []byte, iv []byte, writer io.Writer) (buf.Writer, error) {
+	stream := crypto.NewChaCha20Stream(key, iv)
+	return buf.NewWriter(crypto.NewCryptionWriter(stream, writer)), nil
 }
 
-func (v *ChaCha20) NewDecodingStream(key []byte, iv []byte) (cipher.Stream, error) {
-	return crypto.NewChaCha20Stream(key, iv), nil
+func (v *ChaCha20) NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error) {
+	stream := crypto.NewChaCha20Stream(key, iv)
+	return buf.NewReader(crypto.NewCryptionReader(stream, reader)), nil
 }
 
 func PasswordToCipherKey(password string, keySize int) []byte {
@@ -123,3 +205,8 @@ func PasswordToCipherKey(password string, keySize int) []byte {
 	}
 	return key
 }
+
+func hkdfSHA1(secret, salt, outkey []byte) {
+	r := hkdf.New(sha1.New, secret, salt, []byte("ss-subkey"))
+	common.Must2(io.ReadFull(r, outkey))
+}

+ 31 - 30
proxy/shadowsocks/protocol.go

@@ -8,7 +8,6 @@ import (
 	"v2ray.com/core/common"
 	"v2ray.com/core/common/bitmask"
 	"v2ray.com/core/common/buf"
-	"v2ray.com/core/common/crypto"
 	"v2ray.com/core/common/net"
 	"v2ray.com/core/common/protocol"
 	"v2ray.com/core/common/serial"
@@ -40,11 +39,11 @@ func ReadTCPSession(user *protocol.User, reader io.Reader) (*protocol.RequestHea
 
 	iv := append([]byte(nil), buffer.BytesTo(ivLen)...)
 
-	stream, err := account.Cipher.NewDecodingStream(account.Key, iv)
+	r, err := account.Cipher.NewDecryptionReader(account.Key, iv, reader)
 	if err != nil {
 		return nil, nil, newError("failed to initialize decoding stream").Base(err).AtError()
 	}
-	reader = crypto.NewCryptionReader(stream, reader)
+	reader = r.(io.Reader)
 
 	authenticator := NewAuthenticator(HeaderKeyGenerator(account.Key, iv))
 	request := &protocol.RequestHeader{
@@ -144,12 +143,12 @@ func WriteTCPRequest(request *protocol.RequestHeader, writer io.Writer) (buf.Wri
 		return nil, newError("failed to write IV")
 	}
 
-	stream, err := account.Cipher.NewEncodingStream(account.Key, iv)
+	w, err := account.Cipher.NewEncryptionWriter(account.Key, iv, writer)
 	if err != nil {
 		return nil, newError("failed to create encoding stream").Base(err).AtError()
 	}
 
-	writer = crypto.NewCryptionWriter(stream, writer)
+	writer = w.(io.Writer)
 
 	header := buf.NewLocal(512)
 
@@ -208,11 +207,7 @@ func ReadTCPResponse(user *protocol.User, reader io.Reader) (buf.Reader, error)
 		return nil, newError("failed to read IV").Base(err)
 	}
 
-	stream, err := account.Cipher.NewDecodingStream(account.Key, iv)
-	if err != nil {
-		return nil, newError("failed to initialize decoding stream").Base(err).AtError()
-	}
-	return buf.NewReader(crypto.NewCryptionReader(stream, reader)), nil
+	return account.Cipher.NewDecryptionReader(account.Key, iv, reader)
 }
 
 func WriteTCPResponse(request *protocol.RequestHeader, writer io.Writer) (buf.Writer, error) {
@@ -230,12 +225,7 @@ func WriteTCPResponse(request *protocol.RequestHeader, writer io.Writer) (buf.Wr
 		return nil, newError("failed to write IV.").Base(err)
 	}
 
-	stream, err := account.Cipher.NewEncodingStream(account.Key, iv)
-	if err != nil {
-		return nil, newError("failed to create encoding stream.").Base(err).AtError()
-	}
-
-	return buf.NewWriter(crypto.NewCryptionWriter(stream, writer)), nil
+	return account.Cipher.NewEncryptionWriter(account.Key, iv, writer)
 }
 
 func EncodeUDPPacket(request *protocol.RequestHeader, payload []byte) (*buf.Buffer, error) {
@@ -251,36 +241,41 @@ func EncodeUDPPacket(request *protocol.RequestHeader, payload []byte) (*buf.Buff
 	buffer.AppendSupplier(buf.ReadFullFrom(rand.Reader, ivLen))
 	iv := buffer.Bytes()
 
+	payloadBuffer := buf.NewLocal(512)
+	defer payloadBuffer.Release()
+
 	switch request.Address.Family() {
 	case net.AddressFamilyIPv4:
-		buffer.AppendBytes(AddrTypeIPv4)
-		buffer.Append([]byte(request.Address.IP()))
+		payloadBuffer.AppendBytes(AddrTypeIPv4)
+		payloadBuffer.Append([]byte(request.Address.IP()))
 	case net.AddressFamilyIPv6:
-		buffer.AppendBytes(AddrTypeIPv6)
-		buffer.Append([]byte(request.Address.IP()))
+		payloadBuffer.AppendBytes(AddrTypeIPv6)
+		payloadBuffer.Append([]byte(request.Address.IP()))
 	case net.AddressFamilyDomain:
-		buffer.AppendBytes(AddrTypeDomain, byte(len(request.Address.Domain())))
-		buffer.Append([]byte(request.Address.Domain()))
+		payloadBuffer.AppendBytes(AddrTypeDomain, byte(len(request.Address.Domain())))
+		payloadBuffer.Append([]byte(request.Address.Domain()))
 	default:
 		return nil, newError("unsupported address type: ", request.Address.Family()).AtError()
 	}
 
-	buffer.AppendSupplier(serial.WriteUint16(uint16(request.Port)))
-	buffer.Append(payload)
+	payloadBuffer.AppendSupplier(serial.WriteUint16(uint16(request.Port)))
+	payloadBuffer.Append(payload)
 
 	if request.Option.Has(RequestOptionOneTimeAuth) {
 		authenticator := NewAuthenticator(HeaderKeyGenerator(account.Key, iv))
-		buffer.SetByte(ivLen, buffer.Byte(ivLen)|0x10)
+		payloadBuffer.SetByte(0, payloadBuffer.Byte(0)|0x10)
 
-		buffer.AppendSupplier(authenticator.Authenticate(buffer.BytesFrom(ivLen)))
+		payloadBuffer.AppendSupplier(authenticator.Authenticate(payloadBuffer.Bytes()))
 	}
 
-	stream, err := account.Cipher.NewEncodingStream(account.Key, iv)
+	w, err := account.Cipher.NewEncryptionWriter(account.Key, iv, buffer)
 	if err != nil {
 		return nil, newError("failed to create encoding stream").Base(err).AtError()
 	}
+	if err := w.WriteMultiBuffer(buf.NewMultiBufferValue(payloadBuffer)); err != nil {
+		return nil, newError("failed to encrypt UDP payload").Base(err).AtWarning()
+	}
 
-	stream.XORKeyStream(buffer.BytesFrom(ivLen), buffer.BytesFrom(ivLen))
 	return buffer, nil
 }
 
@@ -295,11 +290,17 @@ func DecodeUDPPacket(user *protocol.User, payload *buf.Buffer) (*protocol.Reques
 	iv := payload.BytesTo(ivLen)
 	payload.SliceFrom(ivLen)
 
-	stream, err := account.Cipher.NewDecodingStream(account.Key, iv)
+	r, err := account.Cipher.NewDecryptionReader(account.Key, iv, payload)
 	if err != nil {
 		return nil, nil, newError("failed to initialize decoding stream").Base(err).AtError()
 	}
-	stream.XORKeyStream(payload.Bytes(), payload.Bytes())
+	mb, err := r.ReadMultiBuffer()
+	if err != nil {
+		return nil, nil, newError("failed to decrypt UDP payload").Base(err).AtWarning()
+	}
+	payload.Release()
+	payload = mb.SplitFirst()
+	mb.Release()
 
 	authenticator := NewAuthenticator(HeaderKeyGenerator(account.Key, iv))
 	request := &protocol.RequestHeader{