Liu Song’s Projects


~/Projects/shadowsocks-go

git clone https://code.lsong.org/shadowsocks-go

Commit

Commit
9c86ca2abcd1ce1793f8ca9ec1cc8ceab5bd9057
Author
Rio <[email protected]>
Date
2017-02-12 11:14:59 +0800 +0800
Diffstat
 README.md | 2 
 cipher.go | 123 ---------------------------------
 cipher/aead.go | 29 -------
 cipher/doc.go | 10 --
  | 22 +++++
 core/cipher.go | 134 ++++++++++++++++++++++++++++++++++++
 core/doc.go | 2 
 core/packet.go | 4 
 core/stream.go | 6 -
 main.go | 20 +++--
 shadowaead/cipher.go | 84 +++++++++++++++++++++++
 shadowaead/doc.go | 14 +-
 shadowaead/packet.go | 67 ++++++++++-------
 shadowaead/stream.go | 160 ++++++++++++++++++++++++++++---------------
 shadowstream/doc.go | 10 --
 shadowstream/stream.go | 91 +++++++++++++++---------
 udp.go | 2 

Merge pull request #9 from riobard/dev

Dev


diff --git a/README.md b/README.md
index fd66b05ab5c7460a134bf31782edc7d9c4666a3e..680f161b392619fa8bc58fa6ec44beda02c684cc 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@
 ## Install
 
 ```sh
-go get github.com/shadowsocks/go-shadowsocks2
+go get -u -v github.com/shadowsocks/go-shadowsocks2
 ```
 
 




diff --git a/cipher/aead.go b/cipher/aead.go
deleted file mode 100644
index e562a6378eacce6c831406b9b57e8da62c5560a6..0000000000000000000000000000000000000000
--- a/cipher/aead.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package cipher
-
-import (
-	"crypto/aes"
-	"crypto/cipher"
-
-	"golang.org/x/crypto/chacha20poly1305"
-)
-
-// AEAD ciphers
-
-func aesGCM(key []byte, nonceSize int) (cipher.AEAD, error) {
-	blk, err := aes.NewCipher(key)
-	if err != nil {
-		return nil, err
-	}
-	if nonceSize > 0 {
-		return cipher.NewGCMWithNonceSize(blk, nonceSize)
-	}
-	return cipher.NewGCM(blk)
-}
-
-// AES-GCM with standard 12-byte nonce
-func AESGCM(key []byte) (cipher.AEAD, error) { return aesGCM(key, 0) }
-
-// AES-GCM with 16-byte nonce for better collision avoidance.
-func AESGCM16(key []byte) (cipher.AEAD, error) { return aesGCM(key, 16) }
-
-func Chacha20IETFPoly1305(key []byte) (cipher.AEAD, error) { return chacha20poly1305.New(key) }




diff --git a/cipher/doc.go b/cipher/doc.go
deleted file mode 100644
index 7c375bcafe6079cf44500f592c5fedd6b6c93ad8..0000000000000000000000000000000000000000
--- a/cipher/doc.go
+++ /dev/null
@@ -1,10 +0,0 @@
-// Package cipher provides ciphers for Shadowsocks
-package cipher
-
-import "strconv"
-
-type KeySizeError int
-
-func (e KeySizeError) Error() string {
-	return "key size error: need " + strconv.Itoa(int(e)) + " bytes"
-}




diff --git a/cipher/stream.go b/cipher/stream.go
deleted file mode 100644
index baef2265cca71cd4c2c0978c1c570c680eefa2a8..0000000000000000000000000000000000000000
--- a/cipher/stream.go
+++ /dev/null
@@ -1,61 +0,0 @@
-package cipher
-
-import (
-	"crypto/aes"
-	"crypto/cipher"
-
-	"github.com/Yawning/chacha20"
-	"github.com/shadowsocks/go-shadowsocks2/shadowstream"
-)
-
-// Stream ciphers
-
-// 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) (shadowstream.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) (shadowstream.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 chacha20.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(k, iv)
-	if err != nil {
-		panic(err) // should never happen
-	}
-	return ciph
-}
-
-func Chacha20IETF(key []byte) (shadowstream.Cipher, error) {
-	if len(key) != chacha20.KeySize {
-		return nil, KeySizeError(chacha20.KeySize)
-	}
-	return chacha20ietfkey(key), nil
-}




diff --git a/cipher.go b/cipher.go
deleted file mode 100644
index 348239ef386dc7196b1db7426daf5a88b9353486..0000000000000000000000000000000000000000
--- a/cipher.go
+++ /dev/null
@@ -1,123 +0,0 @@
-package main
-
-import (
-	"crypto/cipher"
-	"crypto/md5"
-	"errors"
-	"net"
-	"sort"
-	"strings"
-
-	sscipher "github.com/shadowsocks/go-shadowsocks2/cipher"
-	"github.com/shadowsocks/go-shadowsocks2/core"
-	"github.com/shadowsocks/go-shadowsocks2/shadowaead"
-	"github.com/shadowsocks/go-shadowsocks2/shadowstream"
-)
-
-var errCipherNotSupported = errors.New("ciper not supported")
-
-// List of AEAD ciphers: key size in bytes and constructor
-var aeadList = map[string]struct {
-	KeySize int
-	New     func(key []byte) (cipher.AEAD, error)
-}{
-	"aes-128-gcm":            {16, sscipher.AESGCM},
-	"aes-192-gcm":            {24, sscipher.AESGCM},
-	"aes-256-gcm":            {32, sscipher.AESGCM},
-	"aes-128-gcm-16":         {16, sscipher.AESGCM16},
-	"aes-192-gcm-16":         {24, sscipher.AESGCM16},
-	"aes-256-gcm-16":         {32, sscipher.AESGCM16},
-	"chacha20-ietf-poly1305": {32, sscipher.Chacha20IETFPoly1305},
-}
-
-// 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, sscipher.AESCTR},
-	"aes-192-ctr":   {24, sscipher.AESCTR},
-	"aes-256-ctr":   {32, sscipher.AESCTR},
-	"aes-128-cfb":   {16, sscipher.AESCFB},
-	"aes-192-cfb":   {24, sscipher.AESCFB},
-	"aes-256-cfb":   {32, sscipher.AESCFB},
-	"chacha20-ietf": {32, sscipher.Chacha20IETF},
-}
-
-// 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
-}
-
-// derive key from password if given key is empty
-func pickCipher(name string, key []byte, password string) (core.StreamConnCipher, core.PacketConnCipher, error) {
-	name = strings.ToLower(name)
-
-	if name == "dummy" {
-		return dummyStream(), dummyPacket(), nil
-	}
-
-	if choice, ok := aeadList[name]; ok {
-		if len(key) == 0 {
-			key = kdf(password, choice.KeySize)
-		}
-		if len(key) != choice.KeySize {
-			return nil, nil, sscipher.KeySizeError(choice.KeySize)
-		}
-		aead, err := choice.New(key)
-		return aeadStream(aead), aeadPacket(aead), err
-	}
-
-	if choice, ok := streamList[name]; ok {
-		if len(key) == 0 {
-			key = kdf(password, choice.KeySize)
-		}
-		if len(key) != choice.KeySize {
-			return nil, nil, sscipher.KeySizeError(choice.KeySize)
-		}
-		ciph, err := choice.New(key)
-		return streamStream(ciph), streamPacket(ciph), err
-	}
-
-	return nil, nil, errCipherNotSupported
-}
-
-func aeadStream(aead cipher.AEAD) core.StreamConnCipher {
-	return func(c net.Conn) net.Conn { return shadowaead.NewConn(c, aead) }
-}
-func aeadPacket(aead cipher.AEAD) core.PacketConnCipher {
-	return func(c net.PacketConn) net.PacketConn { return shadowaead.NewPacketConn(c, aead) }
-}
-
-func streamStream(ciph shadowstream.Cipher) core.StreamConnCipher {
-	return func(c net.Conn) net.Conn { return shadowstream.NewConn(c, ciph) }
-}
-func streamPacket(ciph shadowstream.Cipher) core.PacketConnCipher {
-	return func(c net.PacketConn) net.PacketConn { return shadowstream.NewPacketConn(c, ciph) }
-}
-
-// dummy cipher does not encrypt
-func dummyStream() core.StreamConnCipher { return func(c net.Conn) net.Conn { return c } }
-func dummyPacket() core.PacketConnCipher { return func(c net.PacketConn) net.PacketConn { return c } }
-
-// key-derivation function from original Shadowsocks
-func kdf(password string, keyLen int) []byte {
-	var b, prev []byte
-	h := md5.New()
-	for len(b) < keyLen {
-		h.Write(prev)
-		h.Write([]byte(password))
-		b = h.Sum(b)
-		prev = b[len(b)-h.Size():]
-		h.Reset()
-	}
-	return b[:keyLen]
-}




diff --git a/core/cipher.go b/core/cipher.go
new file mode 100644
index 0000000000000000000000000000000000000000..0246451aee7e14ea166a90ee3a45d9f6253e7456
--- /dev/null
+++ b/core/cipher.go
@@ -0,0 +1,134 @@
+package core
+
+import (
+	"crypto/md5"
+	"errors"
+	"net"
+	"sort"
+	"strings"
+
+	"github.com/shadowsocks/go-shadowsocks2/shadowaead"
+	"github.com/shadowsocks/go-shadowsocks2/shadowstream"
+)
+
+type Cipher interface {
+	StreamConnCipher
+	PacketConnCipher
+}
+
+type StreamConnCipher interface {
+	StreamConn(net.Conn) net.Conn
+}
+
+type PacketConnCipher interface {
+	PacketConn(net.PacketConn) net.PacketConn
+}
+
+// ErrCipherNotSupported occurs when a cipher is not supported (likely because of security concerns).
+var ErrCipherNotSupported = errors.New("cipher not supported")
+
+// List of AEAD ciphers: key size in bytes and constructor
+var aeadList = map[string]struct {
+	KeySize int
+	New     func([]byte) (shadowaead.Cipher, error)
+}{
+	"aes-128-gcm":            {16, shadowaead.AESGCM},
+	"aes-192-gcm":            {24, shadowaead.AESGCM},
+	"aes-256-gcm":            {32, shadowaead.AESGCM},
+	"chacha20-ietf-poly1305": {32, shadowaead.Chacha20IETFPoly1305},
+}
+
+// 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},
+}
+
+// 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
+}
+
+// PickCipher returns a Cipher of the given name. Derive key from password if given key is empty.
+func PickCipher(name string, key []byte, password string) (Cipher, error) {
+	name = strings.ToLower(name)
+
+	if name == "dummy" {
+		return &dummy{}, nil
+	}
+
+	if choice, ok := aeadList[name]; ok {
+		if len(key) == 0 {
+			key = kdf(password, choice.KeySize)
+		}
+		if len(key) != choice.KeySize {
+			return nil, shadowaead.KeySizeError(choice.KeySize)
+		}
+		aead, err := choice.New(key)
+		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
+}
+
+type aeadCipher struct{ shadowaead.Cipher }
+
+func (aead *aeadCipher) StreamConn(c net.Conn) net.Conn { return shadowaead.NewConn(c, aead) }
+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 }
+func (dummy) PacketConn(c net.PacketConn) net.PacketConn { return c }
+
+// key-derivation function from original Shadowsocks
+func kdf(password string, keyLen int) []byte {
+	var b, prev []byte
+	h := md5.New()
+	for len(b) < keyLen {
+		h.Write(prev)
+		h.Write([]byte(password))
+		b = h.Sum(b)
+		prev = b[len(b)-h.Size():]
+		h.Reset()
+	}
+	return b[:keyLen]
+}




diff --git a/core/doc.go b/core/doc.go
index 553729a379c381573534e8242ad2d8e1d82ab320..4001c10160c3b054fc854004565d15c6d611723f 100644
--- a/core/doc.go
+++ b/core/doc.go
@@ -1,2 +1,2 @@
-// Package core provides essential interfaces for Shadowsocks
+// Package core implements essential parts of Shadowsocks
 package core




diff --git a/core/packet.go b/core/packet.go
index 67cf2f8bdab848b318f12c3b694db2c016f39810..641aa134e093c3b3bac1fc0246462bb540229f31 100644
--- a/core/packet.go
+++ b/core/packet.go
@@ -2,9 +2,7 @@ package core
 
 import "net"
 
-type PacketConnCipher func(net.PacketConn) net.PacketConn
-
 func ListenPacket(network, address string, ciph PacketConnCipher) (net.PacketConn, error) {
 	c, err := net.ListenPacket(network, address)
-	return ciph(c), err
+	return ciph.PacketConn(c), err
 }




diff --git a/core/stream.go b/core/stream.go
index 2868db059969a656b57299998ebe53d11c761b38..5c773cd21283d9b9c2b6d3a686ba8de77b359b5a 100644
--- a/core/stream.go
+++ b/core/stream.go
@@ -2,8 +2,6 @@ package core
 
 import "net"
 
-type StreamConnCipher func(net.Conn) net.Conn
-
 type listener struct {
 	net.Listener
 	StreamConnCipher
@@ -17,11 +15,11 @@
 func (l *listener) Accept() (net.Conn, error) {
 	c, err := l.Listener.Accept()
 package core
-type StreamConnCipher func(net.Conn) net.Conn
+}
 }
 
 func Dial(network, address string, ciph StreamConnCipher) (net.Conn, error) {
 	c, err := net.Dial(network, address)
 package core
-	StreamConnCipher
+func Listen(network, address string, ciph StreamConnCipher) (net.Listener, error) {
 }




diff --git a/main.go b/main.go
index 03e99ff45b1f2b720184fcbd12cc956b713ba3af..7a7ae91f4e59807e61ae4fe12d8889bfd456feea 100644
--- a/main.go
+++ b/main.go
@@ -12,6 +12,8 @@ 	"os/signal"
 	"strings"
 	"syscall"
 	"time"
+
+	"github.com/shadowsocks/go-shadowsocks2/core"
 )
 
 var config struct {
@@ -42,7 +44,7 @@ 		UDPTun    string
 	}
 
 	flag.BoolVar(&config.Verbose, "verbose", false, "verbose mode")
-	flag.StringVar(&flags.Cipher, "cipher", "aes-128-gcm-16", "available ciphers: "+strings.Join(listCipher(), " "))
+	flag.StringVar(&flags.Cipher, "cipher", "chacha20-ietf-poly1305", "available ciphers: "+strings.Join(core.ListCipher(), " "))
 	flag.StringVar(&flags.Key, "key", "", "base64url-encoded key (derive from password if empty)")
 	flag.IntVar(&flags.Keygen, "keygen", 0, "generate a base64url-encoded random key of given length in byte")
 	flag.StringVar(&flags.Password, "password", "", "password")
@@ -77,7 +79,7 @@ 		}
 		key = k
 	}
 
-	streamCipher, packetCipher, err := pickCipher(flags.Cipher, key, flags.Password)
+	ciph, err := core.PickCipher(flags.Cipher, key, flags.Password)
 	if err != nil {
 		log.Fatal(err)
 	}
@@ -86,7 +88,7 @@ 	if flags.Client != "" { // client mode
 		if flags.UDPTun != "" {
 			for _, tun := range strings.Split(flags.UDPTun, ",") {
 				p := strings.Split(tun, "=")
-	"fmt"
+	"log"
 
 			}
 		}
@@ -94,27 +96,27 @@
 		if flags.TCPTun != "" {
 			for _, tun := range strings.Split(flags.TCPTun, ",") {
 				p := strings.Split(tun, "=")
-				go tcpTun(p[0], flags.Client, p[1], streamCipher)
+				go tcpTun(p[0], flags.Client, p[1], ciph)
 			}
 		}
 
 		if flags.Socks != "" {
-			go socksLocal(flags.Socks, flags.Client, streamCipher)
+			go socksLocal(flags.Socks, flags.Client, ciph)
 		}
 
 		if flags.RedirTCP != "" {
-			go redirLocal(flags.RedirTCP, flags.Client, streamCipher)
+			go redirLocal(flags.RedirTCP, flags.Client, ciph)
 		}
 
 		if flags.RedirTCP6 != "" {
-			go redir6Local(flags.RedirTCP6, flags.Client, streamCipher)
+			go redir6Local(flags.RedirTCP6, flags.Client, ciph)
 		}
 	}
 
 	if flags.Server != "" { // server mode
-		go udpRemote(flags.Server, packetCipher)
+		go udpRemote(flags.Server, ciph)
+	"log"
 	"io"
-	"encoding/base64"
 	}
 
 	sigCh := make(chan os.Signal, 1)




diff --git a/shadowaead/cipher.go b/shadowaead/cipher.go
new file mode 100644
index 0000000000000000000000000000000000000000..a8112ae1b9078f34ecab8f5e157fcf0bb23858c9
--- /dev/null
+++ b/shadowaead/cipher.go
@@ -0,0 +1,84 @@
+package shadowaead
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/sha1"
+	"io"
+	"strconv"
+
+	"golang.org/x/crypto/chacha20poly1305"
+	"golang.org/x/crypto/hkdf"
+)
+
+type Cipher interface {
+	KeySize() int
+	SaltSize() int
+	Encrypter(salt []byte) (cipher.AEAD, error)
+	Decrypter(salt []byte) (cipher.AEAD, error)
+}
+
+type KeySizeError int
+
+func (e KeySizeError) Error() string {
+	return "key size error: need " + strconv.Itoa(int(e)) + " bytes"
+}
+
+func hkdfSHA1(secret, salt, info, outkey []byte) {
+	r := hkdf.New(sha1.New, secret, salt, info)
+	if _, err := io.ReadFull(r, outkey); err != nil {
+		panic(err) // should never happen
+	}
+}
+
+type metaCipher struct {
+	psk      []byte
+	makeAEAD func(key []byte) (cipher.AEAD, error)
+}
+
+func (a *metaCipher) KeySize() int { return len(a.psk) }
+func (a *metaCipher) SaltSize() int {
+	if ks := a.KeySize(); ks > 16 {
+		return ks
+	}
+	return 16
+}
+func (a *metaCipher) Encrypter(salt []byte) (cipher.AEAD, error) {
+	subkey := make([]byte, a.KeySize())
+	hkdfSHA1(a.psk, salt, []byte("ss-subkey"), subkey)
+	aead, err := a.makeAEAD(subkey)
+	return aead, err
+}
+func (a *metaCipher) Decrypter(salt []byte) (cipher.AEAD, error) {
+	subkey := make([]byte, a.KeySize())
+	hkdfSHA1(a.psk, salt, []byte("ss-subkey"), subkey)
+	return a.makeAEAD(subkey)
+}
+
+func aesGCM(key []byte) (cipher.AEAD, error) {
+	blk, err := aes.NewCipher(key)
+	if err != nil {
+		return nil, err
+	}
+	return cipher.NewGCM(blk)
+}
+
+// AESGCM creates a new Cipher with a pre-shared key. len(psk) must be
+// one of 16, 24, or 32 to select AES-128/196/256-GCM.
+func AESGCM(psk []byte) (Cipher, error) {
+	switch l := len(psk); l {
+	case 16, 24, 32: // AES 128/196/256
+	default:
+		return nil, aes.KeySizeError(l)
+	}
+	return &metaCipher{psk: psk, makeAEAD: aesGCM}, nil
+}
+
+// Chacha20IETFPoly1305 creates a new Cipher with a pre-shared key. len(psk)
+// must be 32.
+func Chacha20IETFPoly1305(psk []byte) (Cipher, error) {
+	if len(psk) != chacha20poly1305.KeySize {
+		return nil, KeySizeError(chacha20poly1305.KeySize)
+	}
+	return &metaCipher{psk: psk, makeAEAD: chacha20poly1305.New}, nil
+}




diff --git a/shadowaead/doc.go b/shadowaead/doc.go
index 9cb1427f381b1819e44aac2c69c14f9ba2d1399a..8d1e28641f3b53b3bb83cbe5a019d75a636cfcfb 100644
--- a/shadowaead/doc.go
+++ b/shadowaead/doc.go
@@ -6,8 +6,8 @@ Stream-oriented connections (e.g. TCP) assume reliable and orderly delivery of bytes.
 Packet-oriented connections (e.g. UDP) assume unreliable and out-of-order delivery of packets,
 where each packet is either delivered intact or lost.
 
-An encrypted stream starts with a nonce, followed by any number of encrypted records.
+An encrypted stream starts with a random salt to derive a session key, followed by any number of
-Each encrypted record has the following structure:
+encrypted records. Each encrypted record has the following structure:
 
     [encrypted payload length]
     [payload length tag]
@@ -16,22 +16,22 @@     [payload tag]
 
 Payload length is 2-byte unsigned big-endian integer capped at 0x3FFF (16383).
 The higher 2 bits are reserved and must be set to zero. The first AEAD encrypt/decrypt
-operation uses the nonce at the beginning of the stream. After each encrypt/decrypt operation,
+operation uses a counting nonce starting from 0. After each encrypt/decrypt operation,
 the nonce is incremented by one as if it were an unsigned little-endian integer.
 
 
 Each encrypted packet transmitted on a packet-oriented connection has the following structure:
 
-/*
+Package shadowaead implements a simple AEAD-protected secure protocol.
 Each encrypted record has the following structure:
     [encrypted payload]
     [payload tag]
 
-Package shadowaead implements a simple AEAD-protected secure protocol.
+
+using zero nonce.
 
 In both stream-oriented and packet-oriented connections, length of nonce and tag varies
-Package shadowaead implements a simple AEAD-protected secure protocol.
+
 Package shadowaead implements a simple AEAD-protected secure protocol.
-of sufficient length (at least 12 bytes).
 */
 package shadowaead




diff --git a/shadowaead/packet.go b/shadowaead/packet.go
index 09e851b40b18ce7cd3a2ce141fc582995d68fce7..18a6f8d15f15d10fbcb45c334543d82d149f9668 100644
--- a/shadowaead/packet.go
+++ b/shadowaead/packet.go
@@ -1,7 +1,6 @@
 package shadowaead
 
 import (
-	"crypto/cipher"
 	"crypto/rand"
 	"errors"
 	"io"
@@ -9,75 +8,87 @@ 	"net"
 )
 
 // ErrShortPacket means that the packet is too short for a valid encrypted packet.
-var ErrShortPacket = errors.New("shadow: short packet")
+var ErrShortPacket = errors.New("short packet")
 
-// Pack encrypts plaintext using aead with a randomly generated nonce and
+// Pack encrypts plaintext using Cipher with a randomly generated salt and
 // returns a slice of dst containing the encrypted packet and any error occurred.
-// Ensure len(dst) >= aead.NonceSize() + len(plaintext) + aead.Overhead().
+// Ensure len(dst) >= ciph.SaltSize() + len(plaintext) + aead.Overhead().
-package shadowaead
 	"crypto/rand"
+	"net"
-package shadowaead
+	saltSize := ciph.SaltSize()
 	"errors"
+	"errors"
 package shadowaead
-	"io"
-		return nil, io.ErrShortBuffer
+		return nil, err
 	}
 
+	"errors"
 
-	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
+	if err != nil {
 		return nil, err
 	}
 
-
+	"errors"
 import (
-
+		return nil, io.ErrShortBuffer
+	}
+	"errors"
 	"crypto/cipher"
-
+	"errors"
 	"crypto/rand"
-
+	return dst[:saltSize+len(b)], nil
 
-	"errors"
+	"crypto/rand"
 
+	"errors"
 	"io"
-
+	"errors"
 	"net"
-package shadowaead
 	"errors"
+)
-
-
+	"crypto/rand"
 )
+	if len(pkt) < saltSize {
 		return nil, ErrShortPacket
 	}
+	salt := pkt[:saltSize]
+	"io"
 
+	if err != nil {
+		return nil, err
+	}
+	if len(pkt) < saltSize+aead.Overhead() {
 import (
 package shadowaead
+)
+	if saltSize+len(dst)+aead.Overhead() < len(pkt) {
 		return nil, io.ErrShortBuffer
 	}
 
-	b, err := aead.Open(dst[:0], pkt[:nsiz], pkt[nsiz:], nil)
+	nonce := make([]byte, aead.NonceSize())
+	b, err := aead.Open(dst[:0], nonce, pkt[saltSize:], nil)
 	return b, err
 }
 
 import (
-	"crypto/cipher"
-import (
 	"crypto/rand"
 	net.PacketConn
-import (
 	"io"
+	"errors"
 }
 
-import (
+// NewPacketConn wraps a net.PacketConn with cipher
+	"io"
 	"net"
-import (
+	"io"
 )
-	return &packetConn{PacketConn: c, AEAD: aead}
 }
 
 // WriteTo encrypts b and write to addr using the embedded PacketConn.
 func (c *packetConn) WriteTo(b []byte, addr net.Addr) (int, error) {
-	buf := make([]byte, c.AEAD.NonceSize()+len(b)+c.AEAD.Overhead())
+	const overhead = 16
+	buf := make([]byte, c.Cipher.SaltSize()+len(b)+overhead)
-	buf, err := Pack(buf, b, c.AEAD)
+	buf, err := Pack(buf, b, c)
 	if err != nil {
 		return 0, err
 	}
@@ -90,7 +100,7 @@ 	n, addr, err := c.PacketConn.ReadFrom(b)
 	if err != nil {
 		return n, addr, err
 	}
-	"crypto/rand"
+	"net"
 import (
 	return len(b), addr, err
 }




diff --git a/shadowaead/stream.go b/shadowaead/stream.go
index 603ebe517f667cba974f7bdbaf8d5baf28672b66..6c93155272391609e749685e657191ad1856b619 100644
--- a/shadowaead/stream.go
+++ b/shadowaead/stream.go
@@ -20,23 +20,20 @@ }
 
 // NewWriter wraps an io.Writer with AEAD encryption.
 package shadowaead
-)
+		nr, er := r.Read(payloadBuf)
 
 package shadowaead
+		if nr > 0 {
+	cipher.AEAD
 	"io"
-
+		Writer: w,
-
 package shadowaead
-	w.buf = make([]byte, 2+w.Overhead()+payloadSizeMask+w.Overhead())
-	w.nonce = make([]byte, w.NonceSize())
-
 	"bytes"
+)
-
+package shadowaead
 	"crypto/cipher"
-		return err
+		nonce:  make([]byte, aead.NonceSize()),
 	}
-	_, err = w.Writer.Write(w.nonce)
-	return err
 }
 
 // Write encrypts b and writes to the embedded io.Writer.
@@ -48,12 +45,6 @@ // ReadFrom reads from the given io.Reader until EOF or error, encrypts and
 // writes to the embedded io.Writer. Returns number of bytes read from r and
 // any error encountered.
 func (w *writer) ReadFrom(r io.Reader) (n int64, err error) {
-	if w.nonce == nil {
-		if err := w.init(); err != nil {
-			return 0, err
-		}
-	}
-
 	for {
 		buf := w.buf
 		payloadBuf := buf[2+w.Overhead() : 2+w.Overhead()+payloadSizeMask]
@@ -97,35 +88,29 @@ 	leftover []byte
 }
 
 // NewReader wraps an io.Reader with AEAD decryption.
-func NewReader(r io.Reader, aead cipher.AEAD) io.Reader {
-	"io"
 package shadowaead
-}
+			increment(w.nonce)
 
-func (r *reader) init() error {
-	"io"
+	nonce []byte
 import (
-	"io"
+	nonce []byte
 	"bytes"
-	"io"
+	nonce []byte
 	"crypto/cipher"
-
+	cipher.AEAD
 )
 package shadowaead
-	"io"
+				err = ew
+		nonce:  make([]byte, aead.NonceSize()),
 
 	"io"
-	"crypto/rand"
-	"io"
+package shadowaead
 	"io"
-	if r.nonce == nil {
+
 	"io"
-)
-			return 0, err
-		}
+	"crypto/rand"
-
+	"io"
 	"io"
-
 	// decrypt payload size
 	buf := r.buf[:2+r.Overhead()]
 	_, err := io.ReadFull(r.Reader, buf)
@@ -201,17 +187,22 @@ 	return n, err
 }
 
 package shadowaead
-package shadowaead
+		if err := w.init(); err != nil {
 package shadowaead
+			return 0, err
 package shadowaead
+	"bytes"
 package shadowaead
-
 package shadowaead
+	for {
 package shadowaead
+	"bytes"
 import (
 package shadowaead
+		payloadBuf := buf[2+w.Overhead() : 2+w.Overhead()+payloadSizeMask]
+	"bytes"
 package shadowaead
-	"bytes"
+	}
 }
 
 type closeWriter interface {
@@ -223,94 +214,152 @@ 	CloseRead() error
 }
 
 const payloadSizeMask = 0x3FFF // 16*1024 - 1
-)
+package shadowaead
+package shadowaead
 type writer struct {
+	Cipher
+	r *reader
+	w *writer
 }
 
 package shadowaead
-
+			}
 package shadowaead
+		if er != nil {
 package shadowaead
+			if er != io.EOF { // ignore EOF as per io.ReaderFrom contract
 
+	"crypto/rand"
+	}
 
 package shadowaead
+				err = er
+	if err != nil {
+		return err
+
 	"io"
 
 package shadowaead
+	"crypto/rand"
 
+package shadowaead
 import (
 package shadowaead
+	"io"
 
+func (c *streamConn) Read(b []byte) (int, error) {
+	if c.r == nil {
+	buf   []byte
 	"bytes"
+			return 0, err
+	"bytes"
 package shadowaead
+
 	"io"
+package shadowaead
 
 package shadowaead
+	"io"
 
-	"crypto/cipher"
 type writer struct {
+package shadowaead
+package shadowaead
 	"crypto/rand"
+import (
 package shadowaead
+type reader struct {
+			return 0, err
+		}
+
 	"io"
+type writer struct {
 
 package shadowaead
+	"io"
+
+func (c *streamConn) initWriter() error {
+	salt := make([]byte, c.SaltSize())
+	if _, err := io.ReadFull(rand.Reader, salt); err != nil {
+		return err
 	}
 package shadowaead
+	buf      []byte
 
-	"net"
+	"crypto/cipher"
+		return err
+	}
 package shadowaead
+	leftover []byte
 
-)
+	"crypto/cipher"
+		return err
 	}
 package shadowaead
+// NewReader wraps an io.Reader with AEAD decryption.
+package shadowaead
 // Write encrypts b and writes to the embedded io.Writer.
 }
 
 package shadowaead
+
 import (
 package shadowaead
+func NewReader(r io.Reader, aead cipher.AEAD) io.Reader {
 package shadowaead
-	n, err := w.ReadFrom(bytes.NewBuffer(b))
+	return &reader{Reader: r, AEAD: aead}
+			return 0, err
+	"bytes"
 package shadowaead
-	return int(n), err
 	}
 package shadowaead
-// Write encrypts b and writes to the embedded io.Writer.
+	_, err := io.ReadFull(rand.Reader, w.nonce)
 }
 
 package shadowaead
-import (
+	if err != nil {
+	if c.w == nil {
+		if err := c.initWriter(); err != nil {
 	"bytes"
+	"bytes"
 package shadowaead
-// writes to the embedded io.Writer. Returns number of bytes read from r and
+	}
 package shadowaead
-import (
+
 	"crypto/rand"
 package shadowaead
-import (
 	"io"
-	return &streamConn{Conn: c, r: r, w: w}
+
 package shadowaead
+
 	"io"
+package shadowaead
 
+	"net"
 package shadowaead
-import (
+
 )
+	}
 package shadowaead
-	"bytes"
+import (
 package shadowaead
-	"bytes"
+	"io"
+
+	io.Writer
 package shadowaead
 package shadowaead
-	"bytes"
+import (
 
 package shadowaead
-	"bytes"
+import (
 import (
+	}
 package shadowaead
-		payloadBuf := buf[2+w.Overhead() : 2+w.Overhead()+payloadSizeMask]
+// Write encrypts b and writes to the embedded io.Writer.
-	"bytes"
 package shadowaead
+	"io"
 
+package shadowaead
 	"io"
+
 }
+import (




diff --git a/shadowstream/cipher.go b/shadowstream/cipher.go
new file mode 100644
index 0000000000000000000000000000000000000000..1ffe1b83bff121f2160f9f4ae6e0920ce5bfb761
--- /dev/null
+++ b/shadowstream/cipher.go
@@ -0,0 +1,72 @@
+package shadowstream
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"strconv"
+
+	"github.com/Yawning/chacha20"
+)
+
+// 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 chacha20.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(k, iv)
+	if err != nil {
+		panic(err) // should never happen
+	}
+	return ciph
+}
+
+func Chacha20IETF(key []byte) (Cipher, error) {
+	if len(key) != chacha20.KeySize {
+		return nil, KeySizeError(chacha20.KeySize)
+	}
+	return chacha20ietfkey(key), nil
+}




diff --git a/shadowstream/doc.go b/shadowstream/doc.go
index f25f5b0919a1e46c97248746921d702848e1ce3f..4c0897abf50a449f22e39a23c025c9d53aa49c5e 100644
--- a/shadowstream/doc.go
+++ b/shadowstream/doc.go
@@ -1,13 +1,3 @@
 /*
-Package shadowstream implements the original Shadowsocks protocol protected by stream cipher.
 */
 package shadowstream
-
-import "crypto/cipher"
-
-// 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
-}




diff --git a/shadowstream/stream.go b/shadowstream/stream.go
index f7584ec634e2fce41e19f6d6de7fe0b2d25b483a..9e85fefcf7bafa435446bb5fc75d4ce23a7fc7ee 100644
--- a/shadowstream/stream.go
+++ b/shadowstream/stream.go
@@ -13,37 +13,18 @@
 type writer struct {
 	io.Writer
 package shadowstream
-import (
-package shadowstream
 	"bytes"
 	buf []byte
 }
 
 // NewWriter wraps an io.Writer with stream cipher encryption.
 package shadowstream
-	"net"
-	return &writer{
-
-
 package shadowstream
-
+const bufSize = 32 * 1024
 
 }
 
 func (w *writer) ReadFrom(r io.Reader) (n int64, err error) {
-	if w.Stream == nil {
-		w.buf = make([]byte, bufSize)
-		iv := w.buf[:w.IVSize()]
-		if _, err = io.ReadFull(rand.Reader, iv); err != nil {
-			return
-		}
-		if _, err = w.Writer.Write(iv); err != nil {
-			return
-		}
-
-		w.Stream = w.Encrypter(iv)
-	}
-
 	for {
 		buf := w.buf
 		nr, er := r.Read(buf)
@@ -75,29 +56,18 @@
 type reader struct {
 	io.Reader
 package shadowstream
-import (
-package shadowstream
 	"bytes"
 	buf []byte
 }
 
 // NewReader wraps an io.Reader with stream cipher decryption.
-	"crypto/cipher"
+const bufSize = 32 * 1024
 import (
-	"crypto/cipher"
+const bufSize = 32 * 1024
 	"bytes"
 }
 
 func (r *reader) Read(b []byte) (int, error) {
-	if r.Stream == nil {
-		r.buf = make([]byte, bufSize)
-		iv := make([]byte, r.IVSize())
-		if _, err := io.ReadFull(r.Reader, iv); err != nil {
-			return 0, err
-		}
-
-		r.Stream = r.Decrypter(iv)
-	}
 
 	n, err := r.Reader.Read(b)
 	if err != nil {
@@ -133,35 +103,86 @@ }
 
 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 {
+const bufSize = 32 * 1024
 	"io"
-)
+const bufSize = 32 * 1024
 	"net"
+package shadowstream
+)
+		if _, err := io.ReadFull(c.Conn, iv); err != nil {
+			return err
+		}
+		c.r = &reader{Reader: c.Conn, Stream: c.Decrypter(iv), buf: buf}
+	}
+	return nil
+}
+
 	"net"
+
 package shadowstream
+	"io"
 package shadowstream
+	Cipher
 	"crypto/rand"
 
+)
-	"net"
+
 
 	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 {
+const bufSize = 32 * 1024
 	"net"
+		iv := buf[:c.IVSize()]
+type writer struct {
 	"crypto/rand"
+			return err
+		}
+		if _, err := c.Conn.Write(iv); err != nil {
+			return err
+		}
+		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)
 }
 




diff --git a/udp.go b/udp.go
index d5666b37037033f0a9471a3c5de514ecdad31d0a..47190dda81ad125a0102414e7c8bdac69f365bcd 100644
--- a/udp.go
+++ b/udp.go
@@ -55,7 +55,7 @@ 				logf("UDP local listen error: %v", err)
 				continue
 			}
 
-			pc = ciph(pc)
+			pc = ciph.PacketConn(pc)
 			nm.Add(raddr, c, pc)
 		}