From 5f160906f6654743d53ab5c25439d2a4d5e269cd Mon Sep 17 00:00:00 2001 From: Ben Schwartz Date: Mon, 5 Oct 2020 13:47:15 -0400 Subject: [PATCH 1/2] Use our own implementation of Pack for UDP --- client/client.go | 2 +- service/udp.go | 2 +- shadowsocks/packet.go | 22 ++++++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/client/client.go b/client/client.go index cb869b5d..871c495d 100644 --- a/client/client.go +++ b/client/client.go @@ -111,7 +111,7 @@ func (c *packetConn) WriteTo(b []byte, addr net.Addr) (int, error) { // partially overlapping the plaintext and cipher slices since `Pack` skips the salt when calling // `AEAD.Seal` (see https://golang.org/pkg/crypto/cipher/#AEAD). plaintextBuf := append(append(cipherBuf[saltSize:saltSize], socksTargetAddr...), b...) - buf, err := shadowaead.Pack(cipherBuf, plaintextBuf, c.cipher) + buf, err := ss.Pack(cipherBuf, plaintextBuf, c.cipher) if err != nil { return 0, err } diff --git a/service/udp.go b/service/udp.go index 5a0adbe3..253745ef 100644 --- a/service/udp.go +++ b/service/udp.go @@ -446,7 +446,7 @@ func timedCopy(clientAddr net.Addr, clientConn net.PacketConn, targetConn *natco // [ packBuf ] // [ buf ] packBuf := pkt[saltStart:] - buf, err := shadowaead.Pack(packBuf, plaintextBuf, targetConn.cipher) // Encrypt in-place + buf, err := ss.Pack(packBuf, plaintextBuf, targetConn.cipher) // Encrypt in-place if err != nil { return onet.NewConnectionError("ERR_PACK", "Failed to pack data to client", err) } diff --git a/shadowsocks/packet.go b/shadowsocks/packet.go index 57f8ec14..d8a3962d 100644 --- a/shadowsocks/packet.go +++ b/shadowsocks/packet.go @@ -27,6 +27,28 @@ var ErrShortPacket = errors.New("short packet") // This array must be at least service.maxNonceSize bytes. var zeroNonce [12]byte +// Pack encrypts a Shadowsocks-UDP packet and returns a slice containing the encrypted packet. +// dst must be big enough to hold the encrypted packet. +// If plaintext and dst overlap but are not aligned for in-place encryption, this +// function will panic. +func Pack(dst, plaintext []byte, cipher shadowaead.Cipher) ([]byte, error) { + saltSize := cipher.SaltSize() + salt := dst[:saltSize] + if err := RandomSaltGenerator.GetSalt(salt); err != nil { + return nil, err + } + + aead, err := cipher.Encrypter(salt) + if err != nil { + return nil, err + } + + if len(dst) < saltSize+len(plaintext)+aead.Overhead() { + return nil, io.ErrShortBuffer + } + return aead.Seal(salt, zeroNonce[:aead.NonceSize()], plaintext, nil), nil +} + // Unpack decrypts a Shadowsocks-UDP packet and returns a slice containing the decrypted payload or an error. // If dst is present, it is used to store the plaintext, and must have enough capacity. // If dst is nil, decryption proceeds in-place. From 2b2558d9006516e466f1e1a98e736bf5156e5652 Mon Sep 17 00:00:00 2001 From: Ben Schwartz Date: Mon, 5 Oct 2020 16:03:23 -0400 Subject: [PATCH 2/2] Add early length check --- shadowsocks/packet.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shadowsocks/packet.go b/shadowsocks/packet.go index d8a3962d..d1f30f9d 100644 --- a/shadowsocks/packet.go +++ b/shadowsocks/packet.go @@ -33,6 +33,9 @@ var zeroNonce [12]byte // function will panic. func Pack(dst, plaintext []byte, cipher shadowaead.Cipher) ([]byte, error) { saltSize := cipher.SaltSize() + if len(dst) < saltSize { + return nil, io.ErrShortBuffer + } salt := dst[:saltSize] if err := RandomSaltGenerator.GetSalt(salt); err != nil { return nil, err