diff --git a/core/cipher.go b/core/cipher.go index f2899622..71a67d8a 100644 --- a/core/cipher.go +++ b/core/cipher.go @@ -8,7 +8,6 @@ import ( "strings" "github.com/shadowsocks/go-shadowsocks2/shadowaead" - "github.com/shadowsocks/go-shadowsocks2/shadowstream" ) type Cipher interface { @@ -29,7 +28,6 @@ var ErrCipherNotSupported = errors.New("cipher not supported") const ( aeadAes128Gcm = "AEAD_AES_128_GCM" - aeadAes192Gcm = "AEAD_AES_192_GCM" aeadAes256Gcm = "AEAD_AES_256_GCM" aeadChacha20Poly1305 = "AEAD_CHACHA20_POLY1305" ) @@ -40,35 +38,16 @@ var aeadList = map[string]struct { New func([]byte) (shadowaead.Cipher, error) }{ aeadAes128Gcm: {16, shadowaead.AESGCM}, - aeadAes192Gcm: {24, shadowaead.AESGCM}, aeadAes256Gcm: {32, shadowaead.AESGCM}, aeadChacha20Poly1305: {32, shadowaead.Chacha20Poly1305}, } -// List of stream ciphers: key size in bytes and constructor -var streamList = map[string]struct { - KeySize int - New func(key []byte) (shadowstream.Cipher, error) -}{ - "AES-128-CTR": {16, shadowstream.AESCTR}, - "AES-192-CTR": {24, shadowstream.AESCTR}, - "AES-256-CTR": {32, shadowstream.AESCTR}, - "AES-128-CFB": {16, shadowstream.AESCFB}, - "AES-192-CFB": {24, shadowstream.AESCFB}, - "AES-256-CFB": {32, shadowstream.AESCFB}, - "CHACHA20-IETF": {32, shadowstream.Chacha20IETF}, - "XCHACHA20": {32, shadowstream.Xchacha20}, -} - // ListCipher returns a list of available cipher names sorted alphabetically. func ListCipher() []string { var l []string for k := range aeadList { l = append(l, k) } - for k := range streamList { - l = append(l, k) - } sort.Strings(l) return l } @@ -84,8 +63,6 @@ func PickCipher(name string, key []byte, password string) (Cipher, error) { name = aeadChacha20Poly1305 case "AES-128-GCM": name = aeadAes128Gcm - case "AES-192-GCM": - name = aeadAes192Gcm case "AES-256-GCM": name = aeadAes256Gcm } @@ -101,17 +78,6 @@ func PickCipher(name string, key []byte, password string) (Cipher, error) { return &aeadCipher{aead}, err } - if choice, ok := streamList[name]; ok { - if len(key) == 0 { - key = kdf(password, choice.KeySize) - } - if len(key) != choice.KeySize { - return nil, shadowstream.KeySizeError(choice.KeySize) - } - ciph, err := choice.New(key) - return &streamCipher{ciph}, err - } - return nil, ErrCipherNotSupported } @@ -122,15 +88,7 @@ func (aead *aeadCipher) PacketConn(c net.PacketConn) net.PacketConn { return shadowaead.NewPacketConn(c, aead) } -type streamCipher struct{ shadowstream.Cipher } - -func (ciph *streamCipher) StreamConn(c net.Conn) net.Conn { return shadowstream.NewConn(c, ciph) } -func (ciph *streamCipher) PacketConn(c net.PacketConn) net.PacketConn { - return shadowstream.NewPacketConn(c, ciph) -} - // dummy cipher does not encrypt - type dummy struct{} func (dummy) StreamConn(c net.Conn) net.Conn { return c } diff --git a/go.mod b/go.mod index ec09b99e..453b627d 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/shadowsocks/go-shadowsocks2 go 1.12 require ( - github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da github.com/riobard/go-bloom v0.0.0-20200213042214-218e1707c495 golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 ) diff --git a/go.sum b/go.sum index 23592e19..0e6b7bf8 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= -github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/golang/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= github.com/golang/crypto v0.0.0-20190426145343-a29dc8fdc734 h1:qNA2pFZuLItM7tUdO3b9JA2ibgtrUMUznl87BaPSgIg= github.com/golang/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= diff --git a/shadowstream/cipher.go b/shadowstream/cipher.go deleted file mode 100644 index a0aedba7..00000000 --- a/shadowstream/cipher.go +++ /dev/null @@ -1,96 +0,0 @@ -package shadowstream - -import ( - "crypto/aes" - "crypto/cipher" - "errors" - "strconv" - - "github.com/aead/chacha20" - "github.com/aead/chacha20/chacha" -) - -// ErrRepeatedSalt means detected a reused salt -var ErrRepeatedSalt = errors.New("repeated salt detected") - -// Cipher generates a pair of stream ciphers for encryption and decryption. -type Cipher interface { - IVSize() int - Encrypter(iv []byte) cipher.Stream - Decrypter(iv []byte) cipher.Stream -} - -type KeySizeError int - -func (e KeySizeError) Error() string { - return "key size error: need " + strconv.Itoa(int(e)) + " bytes" -} - -// CTR mode -type ctrStream struct{ cipher.Block } - -func (b *ctrStream) IVSize() int { return b.BlockSize() } -func (b *ctrStream) Decrypter(iv []byte) cipher.Stream { return b.Encrypter(iv) } -func (b *ctrStream) Encrypter(iv []byte) cipher.Stream { return cipher.NewCTR(b, iv) } - -func AESCTR(key []byte) (Cipher, error) { - blk, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - return &ctrStream{blk}, nil -} - -// CFB mode -type cfbStream struct{ cipher.Block } - -func (b *cfbStream) IVSize() int { return b.BlockSize() } -func (b *cfbStream) Decrypter(iv []byte) cipher.Stream { return cipher.NewCFBDecrypter(b, iv) } -func (b *cfbStream) Encrypter(iv []byte) cipher.Stream { return cipher.NewCFBEncrypter(b, iv) } - -func AESCFB(key []byte) (Cipher, error) { - blk, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - return &cfbStream{blk}, nil -} - -// IETF-variant of chacha20 -type chacha20ietfkey []byte - -func (k chacha20ietfkey) IVSize() int { return chacha.INonceSize } -func (k chacha20ietfkey) Decrypter(iv []byte) cipher.Stream { return k.Encrypter(iv) } -func (k chacha20ietfkey) Encrypter(iv []byte) cipher.Stream { - ciph, err := chacha20.NewCipher(iv, k) - if err != nil { - panic(err) // should never happen - } - return ciph -} - -func Chacha20IETF(key []byte) (Cipher, error) { - if len(key) != chacha.KeySize { - return nil, KeySizeError(chacha.KeySize) - } - return chacha20ietfkey(key), nil -} - -type xchacha20key []byte - -func (k xchacha20key) IVSize() int { return chacha.XNonceSize } -func (k xchacha20key) Decrypter(iv []byte) cipher.Stream { return k.Encrypter(iv) } -func (k xchacha20key) Encrypter(iv []byte) cipher.Stream { - ciph, err := chacha20.NewCipher(iv, k) - if err != nil { - panic(err) // should never happen - } - return ciph -} - -func Xchacha20(key []byte) (Cipher, error) { - if len(key) != chacha.KeySize { - return nil, KeySizeError(chacha.KeySize) - } - return xchacha20key(key), nil -} diff --git a/shadowstream/doc.go b/shadowstream/doc.go deleted file mode 100644 index 4c0897ab..00000000 --- a/shadowstream/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package shadowstream implements the original Shadowsocks protocol protected by stream cipher. -package shadowstream diff --git a/shadowstream/packet.go b/shadowstream/packet.go deleted file mode 100644 index 4ae7ee43..00000000 --- a/shadowstream/packet.go +++ /dev/null @@ -1,86 +0,0 @@ -package shadowstream - -import ( - "crypto/rand" - "errors" - "io" - "net" - "sync" - - "github.com/shadowsocks/go-shadowsocks2/internal" -) - -// ErrShortPacket means the packet is too short to be a valid encrypted packet. -var ErrShortPacket = errors.New("short packet") - -// Pack encrypts plaintext using stream cipher s and a random IV. -// Returns a slice of dst containing random IV and ciphertext. -// Ensure len(dst) >= s.IVSize() + len(plaintext). -func Pack(dst, plaintext []byte, s Cipher) ([]byte, error) { - if len(dst) < s.IVSize()+len(plaintext) { - return nil, io.ErrShortBuffer - } - iv := dst[:s.IVSize()] - _, err := io.ReadFull(rand.Reader, iv) - if err != nil { - return nil, err - } - internal.AddSalt(iv) - s.Encrypter(iv).XORKeyStream(dst[len(iv):], plaintext) - return dst[:len(iv)+len(plaintext)], nil -} - -// Unpack decrypts pkt using stream cipher s. -// Returns a slice of dst containing decrypted plaintext. -func Unpack(dst, pkt []byte, s Cipher) ([]byte, error) { - if len(pkt) < s.IVSize() { - return nil, ErrShortPacket - } - - if len(dst) < len(pkt)-s.IVSize() { - return nil, io.ErrShortBuffer - } - iv := pkt[:s.IVSize()] - if internal.TestSalt(iv) { - return nil, ErrRepeatedSalt - } - internal.AddSalt(iv) - s.Decrypter(iv).XORKeyStream(dst, pkt[len(iv):]) - return dst[:len(pkt)-len(iv)], nil -} - -type packetConn struct { - net.PacketConn - Cipher - buf []byte - sync.Mutex // write lock -} - -// NewPacketConn wraps a net.PacketConn with stream cipher encryption/decryption. -func NewPacketConn(c net.PacketConn, ciph Cipher) net.PacketConn { - return &packetConn{PacketConn: c, Cipher: ciph, buf: make([]byte, 64*1024)} -} - -func (c *packetConn) WriteTo(b []byte, addr net.Addr) (int, error) { - c.Lock() - defer c.Unlock() - buf, err := Pack(c.buf, b, c.Cipher) - if err != nil { - return 0, err - } - _, err = c.PacketConn.WriteTo(buf, addr) - return len(b), err -} - -func (c *packetConn) ReadFrom(b []byte) (int, net.Addr, error) { - n, addr, err := c.PacketConn.ReadFrom(b) - if err != nil { - return n, addr, err - } - bb, err := Unpack(b[c.IVSize():], b[:n], c.Cipher) - if err != nil { - return n, addr, err - } - copy(b, bb) - return len(bb), addr, err -} diff --git a/shadowstream/stream.go b/shadowstream/stream.go deleted file mode 100644 index 0cbf8fb2..00000000 --- a/shadowstream/stream.go +++ /dev/null @@ -1,178 +0,0 @@ -package shadowstream - -import ( - "bytes" - "crypto/cipher" - "crypto/rand" - "io" - "net" - - "github.com/shadowsocks/go-shadowsocks2/internal" -) - -const bufSize = 32 * 1024 - -type writer struct { - io.Writer - cipher.Stream - buf []byte -} - -// NewWriter wraps an io.Writer with stream cipher encryption. -func NewWriter(w io.Writer, s cipher.Stream) io.Writer { - return &writer{Writer: w, Stream: s, buf: make([]byte, bufSize)} -} - -func (w *writer) ReadFrom(r io.Reader) (n int64, err error) { - for { - buf := w.buf - nr, er := r.Read(buf) - if nr > 0 { - n += int64(nr) - buf = buf[:nr] - w.XORKeyStream(buf, buf) - _, ew := w.Writer.Write(buf) - if ew != nil { - err = ew - return - } - } - - if er != nil { - if er != io.EOF { // ignore EOF as per io.ReaderFrom contract - err = er - } - return - } - } -} - -func (w *writer) Write(b []byte) (int, error) { - n, err := w.ReadFrom(bytes.NewBuffer(b)) - return int(n), err -} - -type reader struct { - io.Reader - cipher.Stream - buf []byte -} - -// NewReader wraps an io.Reader with stream cipher decryption. -func NewReader(r io.Reader, s cipher.Stream) io.Reader { - return &reader{Reader: r, Stream: s, buf: make([]byte, bufSize)} -} - -func (r *reader) Read(b []byte) (int, error) { - - n, err := r.Reader.Read(b) - if err != nil { - return 0, err - } - b = b[:n] - r.XORKeyStream(b, b) - return n, nil -} - -func (r *reader) WriteTo(w io.Writer) (n int64, err error) { - for { - buf := r.buf - nr, er := r.Read(buf) - if nr > 0 { - nw, ew := w.Write(buf[:nr]) - n += int64(nw) - - if ew != nil { - err = ew - return - } - } - - if er != nil { - if er != io.EOF { // ignore EOF as per io.Copy contract (using src.WriteTo shortcut) - err = er - } - return - } - } -} - -type conn struct { - net.Conn - Cipher - r *reader - w *writer -} - -// NewConn wraps a stream-oriented net.Conn with stream cipher encryption/decryption. -func NewConn(c net.Conn, ciph Cipher) net.Conn { - return &conn{Conn: c, Cipher: ciph} -} - -func (c *conn) initReader() error { - if c.r == nil { - buf := make([]byte, bufSize) - iv := buf[:c.IVSize()] - if _, err := io.ReadFull(c.Conn, iv); err != nil { - return err - } - if internal.TestSalt(iv) { - return ErrRepeatedSalt - } - internal.AddSalt(iv) - c.r = &reader{Reader: c.Conn, Stream: c.Decrypter(iv), buf: buf} - } - return nil -} - -func (c *conn) Read(b []byte) (int, error) { - if c.r == nil { - if err := c.initReader(); err != nil { - return 0, err - } - } - return c.r.Read(b) -} - -func (c *conn) WriteTo(w io.Writer) (int64, error) { - if c.r == nil { - if err := c.initReader(); err != nil { - return 0, err - } - } - return c.r.WriteTo(w) -} - -func (c *conn) initWriter() error { - if c.w == nil { - buf := make([]byte, bufSize) - iv := buf[:c.IVSize()] - if _, err := io.ReadFull(rand.Reader, iv); err != nil { - return err - } - if _, err := c.Conn.Write(iv); err != nil { - return err - } - internal.AddSalt(iv) - c.w = &writer{Writer: c.Conn, Stream: c.Encrypter(iv), buf: buf} - } - return nil -} - -func (c *conn) Write(b []byte) (int, error) { - if c.w == nil { - if err := c.initWriter(); err != nil { - return 0, err - } - } - return c.w.Write(b) -} - -func (c *conn) ReadFrom(r io.Reader) (int64, error) { - if c.w == nil { - if err := c.initWriter(); err != nil { - return 0, err - } - } - return c.w.ReadFrom(r) -}