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:
parent
65819228c1
commit
87b15b2b20
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -16,6 +16,15 @@
|
||||
"password": "v2ray-another",
|
||||
"udp": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"protocol": "shadowsocks",
|
||||
"port": 50056,
|
||||
"settings": {
|
||||
"method": "chacha20",
|
||||
"password": "new-password",
|
||||
"udp": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbound": {
|
||||
|
@ -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()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user