package shadowsocksr import ( "crypto/aes" "crypto/cipher" "crypto/des" "crypto/md5" "crypto/rand" "crypto/rc4" "encoding/binary" "errors" "github.com/sun8911879/shadowsocksR/tools" "github.com/sun8911879/shadowsocksR/tools/leakybuf" "github.com/Yawning/chacha20" "github.com/dgryski/go-camellia" "github.com/dgryski/go-idea" "github.com/dgryski/go-rc2" "golang.org/x/crypto/blowfish" "golang.org/x/crypto/cast5" "golang.org/x/crypto/salsa20/salsa" ) var errEmptyPassword = errors.New("empty key") type DecOrEnc int const ( Decrypt DecOrEnc = iota Encrypt ) func newCTRStream(block cipher.Block, err error, key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { if err != nil { return nil, err } return cipher.NewCTR(block, iv), nil } func newAESCTRStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { block, err := aes.NewCipher(key) return newCTRStream(block, err, key, iv, doe) } func newOFBStream(block cipher.Block, err error, key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { if err != nil { return nil, err } return cipher.NewCTR(block, iv), nil } func newAESOFBStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { block, err := aes.NewCipher(key) return newOFBStream(block, err, key, iv, doe) } func newCFBStream(block cipher.Block, err error, key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { if err != nil { return nil, err } if doe == Encrypt { return cipher.NewCFBEncrypter(block, iv), nil } else { return cipher.NewCFBDecrypter(block, iv), nil } } func newAESCFBStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { block, err := aes.NewCipher(key) return newCFBStream(block, err, key, iv, doe) } func newDESStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { block, err := des.NewCipher(key) return newCFBStream(block, err, key, iv, doe) } func newBlowFishStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { block, err := blowfish.NewCipher(key) return newCFBStream(block, err, key, iv, doe) } func newCast5Stream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { block, err := cast5.NewCipher(key) return newCFBStream(block, err, key, iv, doe) } func newRC4MD5Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { h := md5.New() h.Write(key) h.Write(iv) rc4key := h.Sum(nil) return rc4.NewCipher(rc4key) } func newChaCha20Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { return chacha20.NewCipher(key, iv) } func newChacha20IETFStream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { return chacha20.NewCipher(key, iv) } type salsaStreamCipher struct { nonce [8]byte key [32]byte counter int } func (c *salsaStreamCipher) XORKeyStream(dst, src []byte) { var buf []byte padLen := c.counter % 64 dataSize := len(src) + padLen if cap(dst) >= dataSize { buf = dst[:dataSize] } else if leakybuf.GlobalLeakyBufSize >= dataSize { buf = leakybuf.GlobalLeakyBuf.Get() defer leakybuf.GlobalLeakyBuf.Put(buf) buf = buf[:dataSize] } else { buf = make([]byte, dataSize) } var subNonce [16]byte copy(subNonce[:], c.nonce[:]) binary.LittleEndian.PutUint64(subNonce[len(c.nonce):], uint64(c.counter/64)) // It's difficult to avoid data copy here. src or dst maybe slice from // Conn.Read/Write, which can't have padding. copy(buf[padLen:], src[:]) salsa.XORKeyStream(buf, buf, &subNonce, &c.key) copy(dst, buf[padLen:]) c.counter += len(src) } func newSalsa20Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { var c salsaStreamCipher copy(c.nonce[:], iv[:8]) copy(c.key[:], key[:32]) return &c, nil } func newCamelliaStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { block, err := camellia.New(key) return newCFBStream(block, err, key, iv, doe) } func newIdeaStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { block, err := idea.NewCipher(key) return newCFBStream(block, err, key, iv, doe) } func newRC2Stream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { block, err := rc2.New(key, 16) return newCFBStream(block, err, key, iv, doe) } func newSeedStream(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) { // TODO: SEED block cipher implementation is required block, err := rc2.New(key, 16) return newCFBStream(block, err, key, iv, doe) } type cipherInfo struct { keyLen int ivLen int newStream func(key, iv []byte, doe DecOrEnc) (cipher.Stream, error) } var streamCipherMethod = map[string]*cipherInfo{ "aes-128-cfb": {16, 16, newAESCFBStream}, "aes-192-cfb": {24, 16, newAESCFBStream}, "aes-256-cfb": {32, 16, newAESCFBStream}, "aes-128-ctr": {16, 16, newAESCTRStream}, "aes-192-ctr": {24, 16, newAESCTRStream}, "aes-256-ctr": {32, 16, newAESCTRStream}, "aes-128-ofb": {16, 16, newAESOFBStream}, "aes-192-ofb": {24, 16, newAESOFBStream}, "aes-256-ofb": {32, 16, newAESOFBStream}, "des-cfb": {8, 8, newDESStream}, "bf-cfb": {16, 8, newBlowFishStream}, "cast5-cfb": {16, 8, newCast5Stream}, "rc4-md5": {16, 16, newRC4MD5Stream}, "rc4-md5-6": {16, 6, newRC4MD5Stream}, "chacha20": {32, 8, newChaCha20Stream}, "chacha20-ietf": {32, 12, newChacha20IETFStream}, "salsa20": {32, 8, newSalsa20Stream}, "camellia-128-cfb": {16, 16, newCamelliaStream}, "camellia-192-cfb": {24, 16, newCamelliaStream}, "camellia-256-cfb": {32, 16, newCamelliaStream}, "idea-cfb": {16, 8, newIdeaStream}, "rc2-cfb": {16, 8, newRC2Stream}, "seed-cfb": {16, 8, newSeedStream}, } func CheckCipherMethod(method string) error { if method == "" { method = "rc4-md5" } _, ok := streamCipherMethod[method] if !ok { return errors.New("Unsupported encryption method: " + method) } return nil } type StreamCipher struct { enc cipher.Stream dec cipher.Stream key []byte info *cipherInfo iv []byte } // NewStreamCipher creates a cipher that can be used in Dial() etc. // Use cipher.Copy() to create a new cipher with the same method and password // to avoid the cost of repeated cipher initialization. func NewStreamCipher(method, password string) (c *StreamCipher, err error) { if password == "" { return nil, errEmptyPassword } if method == "" { method = "rc4-md5" } mi, ok := streamCipherMethod[method] if !ok { return nil, errors.New("Unsupported encryption method: " + method) } key := tools.EVPBytesToKey(password, mi.keyLen) c = &StreamCipher{key: key, info: mi} if err != nil { return nil, err } return c, nil } // Initializes the block cipher with CFB mode, returns IV. func (c *StreamCipher) initEncrypt() (iv []byte, err error) { if c.iv == nil { iv = make([]byte, c.info.ivLen) rand.Read(iv) c.iv = iv } else { iv = c.iv } c.enc, err = c.info.newStream(c.key, iv, Encrypt) return } func (c *StreamCipher) initDecrypt(iv []byte) (err error) { c.dec, err = c.info.newStream(c.key, iv, Decrypt) return } func (c *StreamCipher) encrypt(dst, src []byte) { c.enc.XORKeyStream(dst, src) } func (c *StreamCipher) decrypt(dst, src []byte) { c.dec.XORKeyStream(dst, src) } // Copy creates a new cipher at it's initial state. func (c *StreamCipher) Copy() *StreamCipher { // This optimization maybe not necessary. But without this function, we // need to maintain a table cache for newTableCipher and use lock to // protect concurrent access to that cache. // AES and DES ciphers does not return specific types, so it's difficult // to create copy. But their initialization time is less than 4000ns on my // 2.26 GHz Intel Core 2 Duo processor. So no need to worry. // Currently, blow-fish and cast5 initialization cost is an order of // magnitude slower than other ciphers. (I'm not sure whether this is // because the current implementation is not highly optimized, or this is // the nature of the algorithm.) nc := *c nc.enc = nil nc.dec = nil return &nc } func (c *StreamCipher) Key() (key []byte, keyLen int) { return c.key, c.info.keyLen } func (c *StreamCipher) IV() ([]byte, int) { return c.iv, c.info.ivLen }