diff --git a/.travis.yml b/.travis.yml index c68e325..7eb7ebc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ go: install: - go get golang.org/x/crypto/blowfish - go get golang.org/x/crypto/cast5 + - go get golang.org/x/crypto/salsa20 - go install ./cmd/shadowsocks-local - go install ./cmd/shadowsocks-server script: diff --git a/shadowsocks/encrypt.go b/shadowsocks/encrypt.go index 3dbb673..6ddf131 100644 --- a/shadowsocks/encrypt.go +++ b/shadowsocks/encrypt.go @@ -4,6 +4,7 @@ import ( "bytes" "golang.org/x/crypto/blowfish" "golang.org/x/crypto/cast5" + "golang.org/x/crypto/salsa20/salsa" "crypto/aes" "crypto/cipher" "crypto/des" @@ -137,6 +138,31 @@ func newRC4MD5Stream(key, iv []byte, _ DecOrEnc) (cipher.Stream, error) { return rc4.NewCipher(rc4key) } +type salsaStreamCipher struct { + nonce [8]byte + key [32]byte + counter int +} + +func (c *salsaStreamCipher) XORKeyStream(dst, src []byte) { + var subNonce [16]byte + padlen := c.counter % 64 + buf := make([]byte, padlen+len(src)) + copy(buf[padlen:], src[:]) + copy(subNonce[:], c.nonce[:]) + binary.LittleEndian.PutUint64(subNonce[len(c.nonce):], uint64(c.counter / 64)) + 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 +} + type cipherInfo struct { keyLen int ivLen int @@ -153,6 +179,7 @@ var cipherMethod = map[string]*cipherInfo{ "rc4-md5": {16, 16, newRC4MD5Stream}, "rc4": {16, 0, nil}, "table": {16, 0, nil}, + "salsa20": {32, 8, newSalsa20Stream}, } func CheckCipherMethod(method string) error { diff --git a/shadowsocks/encrypt_test.go b/shadowsocks/encrypt_test.go index e9ff06d..ced2247 100644 --- a/shadowsocks/encrypt_test.go +++ b/shadowsocks/encrypt_test.go @@ -3,6 +3,8 @@ package shadowsocks import ( "crypto/rc4" "reflect" + "io" + "crypto/rand" "testing" ) @@ -144,11 +146,15 @@ func TestRC4MD5(t *testing.T) { } var cipherKey = make([]byte, 64) +var cipherIv = make([]byte, 64) + +const CIPHER_BENCHMARK_BUFFER_LEN = 4096 func init() { for i := 0; i < len(cipherKey); i++ { cipherKey[i] = byte(i) } + io.ReadFull(rand.Reader, cipherIv) } func BenchmarkRC4Init(b *testing.B) { @@ -200,3 +206,124 @@ func BenchmarkRC4MD5Init(b *testing.B) { ci := cipherMethod["rc4-md5"] benchmarkCipherInit(b, ci) } + +func BenchmarkSalsa20Init(b *testing.B) { + ci := cipherMethod["salsa20"] + benchmarkCipherInit(b, ci) +} + +func benchmarkCipherEncrypt(b *testing.B, ci *cipherInfo) { + key := cipherKey[:ci.keyLen] + iv := cipherIv[:ci.ivLen] + enc, err := ci.newStream(key, iv, Encrypt) + if err != nil { + b.Error(err) + } + src := make([]byte, CIPHER_BENCHMARK_BUFFER_LEN) + dst := make([]byte, CIPHER_BENCHMARK_BUFFER_LEN) + io.ReadFull(rand.Reader, src) + for i := 0; i < b.N; i++ { + enc.XORKeyStream(dst, src) + } +} + +func BenchmarkAES128Encrypt(b *testing.B) { + ci := cipherMethod["aes-128-cfb"] + benchmarkCipherEncrypt(b, ci) +} + +func BenchmarkAES192Encrypt(b *testing.B) { + ci := cipherMethod["aes-192-cfb"] + benchmarkCipherEncrypt(b, ci) +} + +func BenchmarkAES256Encrypt(b *testing.B) { + ci := cipherMethod["aes-256-cfb"] + benchmarkCipherEncrypt(b, ci) +} + +func BenchmarkBlowFishEncrypt(b *testing.B) { + ci := cipherMethod["bf-cfb"] + benchmarkCipherEncrypt(b, ci) +} + +func BenchmarkCast5Encrypt(b *testing.B) { + ci := cipherMethod["bf-cfb"] + benchmarkCipherEncrypt(b, ci) +} + +func BenchmarkDESEncrypt(b *testing.B) { + ci := cipherMethod["des-cfb"] + benchmarkCipherEncrypt(b, ci) +} + +func BenchmarkRC4MD5Encrypt(b *testing.B) { + ci := cipherMethod["rc4-md5"] + benchmarkCipherEncrypt(b, ci) +} + +func BenchmarkSalsa20Encrypt(b *testing.B) { + ci := cipherMethod["salsa20"] + benchmarkCipherEncrypt(b, ci) +} + +func benchmarkCipherDecrypt(b *testing.B, ci *cipherInfo) { + key := cipherKey[:ci.keyLen] + iv := cipherIv[:ci.ivLen] + enc, err := ci.newStream(key, iv, Encrypt) + if err != nil { + b.Error(err) + } + dec, err := ci.newStream(key, iv, Decrypt) + if err != nil { + b.Error(err) + } + src := make([]byte, CIPHER_BENCHMARK_BUFFER_LEN) + dst := make([]byte, CIPHER_BENCHMARK_BUFFER_LEN) + io.ReadFull(rand.Reader, src) + enc.XORKeyStream(dst, src) + for i := 0; i < b.N; i++ { + dec.XORKeyStream(src, dst) + } +} + +func BenchmarkAES128Decrypt(b *testing.B) { + ci := cipherMethod["aes-128-cfb"] + benchmarkCipherDecrypt(b, ci) +} + +func BenchmarkAES192Decrypt(b *testing.B) { + ci := cipherMethod["aes-192-cfb"] + benchmarkCipherDecrypt(b, ci) +} + +func BenchmarkAES256Decrypt(b *testing.B) { + ci := cipherMethod["aes-256-cfb"] + benchmarkCipherDecrypt(b, ci) +} + +func BenchmarkBlowFishDecrypt(b *testing.B) { + ci := cipherMethod["bf-cfb"] + benchmarkCipherDecrypt(b, ci) +} + +func BenchmarkCast5Decrypt(b *testing.B) { + ci := cipherMethod["bf-cfb"] + benchmarkCipherDecrypt(b, ci) +} + +func BenchmarkDESDecrypt(b *testing.B) { + ci := cipherMethod["des-cfb"] + benchmarkCipherDecrypt(b, ci) +} + +func BenchmarkRC4MD5Decrypt(b *testing.B) { + ci := cipherMethod["rc4-md5"] + benchmarkCipherDecrypt(b, ci) +} + +func BenchmarkSalsa20Decrypt(b *testing.B) { + ci := cipherMethod["salsa20"] + benchmarkCipherDecrypt(b, ci) +} +