1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2024-12-30 05:56:54 -05:00

Add ChaCha20 in Shadowsocks

This commit is contained in:
v2ray 2016-02-23 18:16:13 +01:00
parent 65819228c1
commit 87b15b2b20
5 changed files with 88 additions and 22 deletions

View File

@ -1,8 +1,8 @@
package shadowsocks
import (
"crypto/cipher"
"crypto/md5"
"io"
"github.com/v2ray/v2ray-core/common/crypto"
"github.com/v2ray/v2ray-core/common/protocol"
@ -11,8 +11,8 @@ import (
type Cipher interface {
KeySize() int
IVSize() int
NewEncodingStream(key []byte, iv []byte, writer io.Writer) (io.Writer, error)
NewDecodingStream(key []byte, iv []byte, reader io.Reader) (io.Reader, error)
NewEncodingStream(key []byte, iv []byte) (cipher.Stream, error)
NewDecodingStream(key []byte, iv []byte) (cipher.Stream, error)
}
type AesCfb struct {
@ -27,22 +27,40 @@ func (this *AesCfb) IVSize() int {
return 16
}
func (this *AesCfb) NewEncodingStream(key []byte, iv []byte, writer io.Writer) (io.Writer, error) {
func (this *AesCfb) NewEncodingStream(key []byte, iv []byte) (cipher.Stream, error) {
stream, err := crypto.NewAesEncryptionStream(key, iv)
if err != nil {
return nil, err
}
aesWriter := crypto.NewCryptionWriter(stream, writer)
return aesWriter, nil
return stream, nil
}
func (this *AesCfb) NewDecodingStream(key []byte, iv []byte, reader io.Reader) (io.Reader, error) {
func (this *AesCfb) NewDecodingStream(key []byte, iv []byte) (cipher.Stream, error) {
stream, err := crypto.NewAesDecryptionStream(key, iv)
if err != nil {
return nil, err
}
aesReader := crypto.NewCryptionReader(stream, reader)
return aesReader, nil
return stream, nil
}
type ChaCha20 struct {
IVBytes int
}
func (this *ChaCha20) KeySize() int {
return 32
}
func (this *ChaCha20) IVSize() int {
return this.IVBytes
}
func (this *ChaCha20) NewEncodingStream(key []byte, iv []byte) (cipher.Stream, error) {
return crypto.NewChaCha20Stream(key, iv), nil
}
func (this *ChaCha20) NewDecodingStream(key []byte, iv []byte) (cipher.Stream, error) {
return crypto.NewChaCha20Stream(key, iv), nil
}
type Config struct {

View File

@ -35,6 +35,14 @@ func (this *Config) UnmarshalJSON(data []byte) error {
this.Cipher = &AesCfb{
KeyBytes: 16,
}
case "chacha20":
this.Cipher = &ChaCha20{
IVBytes: 8,
}
case "chacha20-ietf":
this.Cipher = &ChaCha20{
IVBytes: 12,
}
default:
log.Error("Shadowsocks: Unknown cipher method: ", jsonConfig.Cipher)
return internal.ErrorBadConfiguration

View File

@ -9,6 +9,7 @@ import (
"github.com/v2ray/v2ray-core/app"
"github.com/v2ray/v2ray-core/app/dispatcher"
"github.com/v2ray/v2ray-core/common/alloc"
"github.com/v2ray/v2ray-core/common/crypto"
v2io "github.com/v2ray/v2ray-core/common/io"
"github.com/v2ray/v2ray-core/common/log"
v2net "github.com/v2ray/v2ray-core/common/net"
@ -90,16 +91,19 @@ func (this *Shadowsocks) Listen(port v2net.Port) error {
func (this *Shadowsocks) handlerUDPPayload(payload *alloc.Buffer, source v2net.Destination) {
defer payload.Release()
iv := payload.Value[:this.config.Cipher.IVSize()]
ivLen := this.config.Cipher.IVSize()
iv := payload.Value[:ivLen]
key := this.config.Key
payload.SliceFrom(this.config.Cipher.IVSize())
payload.SliceFrom(ivLen)
reader, err := this.config.Cipher.NewDecodingStream(key, iv, payload)
stream, err := this.config.Cipher.NewDecodingStream(key, iv)
if err != nil {
log.Error("Shadowsocks: Failed to create decoding stream: ", err)
return
}
reader := crypto.NewCryptionReader(stream, payload)
request, err := ReadRequest(reader, NewAuthenticator(HeaderKeyGenerator(key, iv)), true)
if err != nil {
log.Access(source, serial.StringLiteral(""), log.AccessRejected, serial.StringLiteral(err.Error()))
@ -115,18 +119,20 @@ func (this *Shadowsocks) handlerUDPPayload(payload *alloc.Buffer, source v2net.D
this.udpServer.Dispatch(source, packet, func(packet v2net.Packet) {
defer packet.Chunk().Release()
response := alloc.NewBuffer().Slice(0, this.config.Cipher.IVSize())
response := alloc.NewBuffer().Slice(0, ivLen)
defer response.Release()
rand.Read(response.Value)
respIv := response.Value
writer, err := this.config.Cipher.NewEncodingStream(key, respIv, response)
stream, err := this.config.Cipher.NewEncodingStream(key, respIv)
if err != nil {
log.Error("Shadowsocks: Failed to create encoding stream: ", err)
return
}
writer := crypto.NewCryptionWriter(stream, response)
switch {
case request.Address.IsIPv4():
writer.Write([]byte{AddrTypeIPv4})
@ -144,7 +150,7 @@ func (this *Shadowsocks) handlerUDPPayload(payload *alloc.Buffer, source v2net.D
if request.OTA {
respAuth := NewAuthenticator(HeaderKeyGenerator(key, respIv))
respAuth.Authenticate(response.Value, response.Value[this.config.Cipher.IVSize():])
respAuth.Authenticate(response.Value, response.Value[ivLen:])
}
this.udpHub.WriteTo(response.Value, source)
@ -159,22 +165,25 @@ func (this *Shadowsocks) handleConnection(conn *hub.TCPConn) {
timedReader := v2net.NewTimeOutReader(16, conn)
_, err := io.ReadFull(timedReader, buffer.Value[:this.config.Cipher.IVSize()])
ivLen := this.config.Cipher.IVSize()
_, err := io.ReadFull(timedReader, buffer.Value[:ivLen])
if err != nil {
log.Access(conn.RemoteAddr(), serial.StringLiteral(""), log.AccessRejected, serial.StringLiteral(err.Error()))
log.Error("Shadowsocks: Failed to read IV: ", err)
return
}
iv := buffer.Value[:this.config.Cipher.IVSize()]
iv := buffer.Value[:ivLen]
key := this.config.Key
reader, err := this.config.Cipher.NewDecodingStream(key, iv, timedReader)
stream, err := this.config.Cipher.NewDecodingStream(key, iv)
if err != nil {
log.Error("Shadowsocks: Failed to create decoding stream: ", err)
return
}
reader := crypto.NewCryptionReader(stream, timedReader)
request, err := ReadRequest(reader, NewAuthenticator(HeaderKeyGenerator(iv, key)), false)
if err != nil {
log.Access(conn.RemoteAddr(), serial.StringLiteral(""), log.AccessRejected, serial.StringLiteral(err.Error()))
@ -196,17 +205,20 @@ func (this *Shadowsocks) handleConnection(conn *hub.TCPConn) {
writeFinish.Lock()
go func() {
if payload, ok := <-ray.InboundOutput(); ok {
payload.SliceBack(16)
rand.Read(payload.Value[:16])
payload.SliceBack(ivLen)
rand.Read(payload.Value[:ivLen])
writer, err := this.config.Cipher.NewEncodingStream(key, payload.Value[:16], conn)
stream, err := this.config.Cipher.NewEncodingStream(key, payload.Value[:ivLen])
if err != nil {
log.Error("Shadowsocks: Failed to create encoding stream: ", err)
return
}
stream.XORKeyStream(payload.Value[ivLen:], payload.Value[ivLen:])
writer.Write(payload.Value)
conn.Write(payload.Value)
payload.Release()
writer := crypto.NewCryptionWriter(stream, conn)
v2io.ChanToRawWriter(writer, ray.InboundOutput())
}
writeFinish.Unlock()

View File

@ -16,6 +16,15 @@
"password": "v2ray-another",
"udp": true
}
},
{
"protocol": "shadowsocks",
"port": 50056,
"settings": {
"method": "chacha20",
"password": "new-password",
"udp": true
}
}
],
"outbound": {

View File

@ -69,5 +69,24 @@ func TestShadowsocksTCP(t *testing.T) {
assert.StringLiteral("Processed: " + payload).Equals(string(response[:nBytes]))
conn.Close()
cipher, err = ssclient.NewCipher("chacha20", "new-password")
assert.Error(err).IsNil()
conn, err = ssclient.DialWithRawAddr(rawAddr, "127.0.0.1:50056", cipher)
assert.Error(err).IsNil()
payload = "shadowsocks request 3."
nBytes, err = conn.Write([]byte(payload))
assert.Error(err).IsNil()
assert.Int(nBytes).Equals(len(payload))
conn.Conn.(*net.TCPConn).CloseWrite()
response = make([]byte, 1024)
nBytes, err = conn.Read(response)
assert.Error(err).IsNil()
assert.StringLiteral("Processed: " + payload).Equals(string(response[:nBytes]))
conn.Close()
CloseAllServers()
}