From 4b4722a29edf7742cd49d4916d7235ca86166db3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E3=81=AE=E3=81=8B=E3=81=88=E3=81=A7?= Date: Fri, 2 Jul 2021 23:21:29 +0800 Subject: [PATCH 1/8] Refactor: new Shadowsocks validator --- proxy/shadowsocks/config.go | 27 ------ proxy/shadowsocks/protocol.go | 109 ++++++++--------------- proxy/shadowsocks/server.go | 4 - proxy/shadowsocks/validator.go | 155 +++++++++++++++++---------------- 4 files changed, 115 insertions(+), 180 deletions(-) diff --git a/proxy/shadowsocks/config.go b/proxy/shadowsocks/config.go index 607894b1b958..dfc3ce3d1677 100644 --- a/proxy/shadowsocks/config.go +++ b/proxy/shadowsocks/config.go @@ -7,8 +7,6 @@ import ( "crypto/md5" "crypto/sha1" "io" - "reflect" - "strconv" "golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/hkdf" @@ -33,31 +31,6 @@ func (a *MemoryAccount) Equals(another protocol.Account) bool { return false } -func (a *MemoryAccount) GetCipherName() string { - switch a.Cipher.(type) { - case *AesCfb: - keyBytes := a.Cipher.(*AesCfb).KeyBytes - return "AES_" + strconv.FormatInt(int64(keyBytes*8), 10) + "_CFB" - case *ChaCha20: - if a.Cipher.(*ChaCha20).IVBytes == 8 { - return "CHACHA20" - } - return "CHACHA20_IETF" - case *AEADCipher: - switch reflect.ValueOf(a.Cipher.(*AEADCipher).AEADAuthCreator).Pointer() { - case reflect.ValueOf(createAesGcm).Pointer(): - keyBytes := a.Cipher.(*AEADCipher).KeyBytes - return "AES_" + strconv.FormatInt(int64(keyBytes*8), 10) + "_GCM" - case reflect.ValueOf(createChacha20Poly1305).Pointer(): - return "CHACHA20_POLY1305" - } - case *NoneCipher: - return "NONE" - } - - return "" -} - func createAesGcm(key []byte) cipher.AEAD { block, err := aes.NewCipher(key) common.Must(err) diff --git a/proxy/shadowsocks/protocol.go b/proxy/shadowsocks/protocol.go index 69db3ba22f5d..6aae5abfe8fb 100644 --- a/proxy/shadowsocks/protocol.go +++ b/proxy/shadowsocks/protocol.go @@ -1,11 +1,7 @@ package shadowsocks import ( - "crypto/cipher" - "crypto/hmac" "crypto/rand" - "crypto/sha256" - "hash/crc32" "io" "io/ioutil" @@ -55,11 +51,7 @@ func (r *FullReader) Read(p []byte) (n int, err error) { // ReadTCPSession reads a Shadowsocks TCP session from the given reader, returns its header and remaining parts. func ReadTCPSession(validator *Validator, reader io.Reader) (*protocol.RequestHeader, buf.Reader, error) { - - hashkdf := hmac.New(sha256.New, []byte("SSBSKDF")) - - behaviorSeed := crc32.ChecksumIEEE(hashkdf.Sum(nil)) - + behaviorSeed := validator.GetBehaviorSeed() behaviorRand := dice.NewDeterministicDice(int64(behaviorSeed)) BaseDrainSize := behaviorRand.Roll(3266) RandDrainMax := behaviorRand.Roll(64) + 1 @@ -67,69 +59,46 @@ func ReadTCPSession(validator *Validator, reader io.Reader) (*protocol.RequestHe DrainSize := BaseDrainSize + 16 + 38 + RandDrainRolled readSizeRemain := DrainSize - var r2 buf.Reader + var r buf.Reader buffer := buf.New() defer buffer.Release() - var user *protocol.MemoryUser - var ivLen int32 - var err error - - count := validator.Count() - if count == 0 { + if _, err := buffer.ReadFullFrom(reader, 50); err != nil { readSizeRemain -= int(buffer.Len()) DrainConnN(reader, readSizeRemain) - return nil, nil, newError("invalid user") - } else if count > 1 { - var aead cipher.AEAD - - if _, err := buffer.ReadFullFrom(reader, 50); err != nil { - readSizeRemain -= int(buffer.Len()) - DrainConnN(reader, readSizeRemain) - return nil, nil, newError("failed to read 50 bytes").Base(err) - } + return nil, nil, newError("failed to read 50 bytes").Base(err) + } - bs := buffer.Bytes() - user, aead, _, ivLen, err = validator.Get(bs, protocol.RequestCommandTCP) + bs := buffer.Bytes() + user, aead, _, ivLen, err := validator.Get(bs, protocol.RequestCommandTCP) - if user != nil { - reader = &FullReader{reader, bs[ivLen:]} + if user != nil { + reader = &FullReader{reader, bs[ivLen:]} + if aead != nil { auth := &crypto.AEADAuthenticator{ AEAD: aead, NonceGenerator: crypto.GenerateInitialAEADNonce(), } - r2 = crypto.NewAuthenticationReader(auth, &crypto.AEADChunkSizeParser{ + r = crypto.NewAuthenticationReader(auth, &crypto.AEADChunkSizeParser{ Auth: auth, }, reader, protocol.TransferTypeStream, nil) } else { - readSizeRemain -= int(buffer.Len()) - DrainConnN(reader, readSizeRemain) - return nil, nil, newError("failed to match an user").Base(err) - } - } else { - user, ivLen = validator.GetOnlyUser() - account := user.Account.(*MemoryAccount) - hashkdf.Write(account.Key) - var iv []byte - if ivLen > 0 { - if _, err := buffer.ReadFullFrom(reader, ivLen); err != nil { + account := user.Account.(*MemoryAccount) + iv := append([]byte(nil), buffer.BytesTo(ivLen)...) + r, err = account.Cipher.NewDecryptionReader(account.Key, iv, reader) + if err != nil { readSizeRemain -= int(buffer.Len()) DrainConnN(reader, readSizeRemain) - return nil, nil, newError("failed to read IV").Base(err) + return nil, nil, newError("failed to initialize decoding stream").Base(err).AtError() } - iv = append([]byte(nil), buffer.BytesTo(ivLen)...) - } - - r, err := account.Cipher.NewDecryptionReader(account.Key, iv, reader) - if err != nil { - readSizeRemain -= int(buffer.Len()) - DrainConnN(reader, readSizeRemain) - return nil, nil, newError("failed to initialize decoding stream").Base(err).AtError() } - r2 = r + } else { + readSizeRemain -= int(buffer.Len()) + DrainConnN(reader, readSizeRemain) + return nil, nil, newError("failed to match an user").Base(err) } - br := &buf.BufferedReader{Reader: r2} + br := &buf.BufferedReader{Reader: r} request := &protocol.RequestHeader{ Version: Version, @@ -255,35 +224,25 @@ func DecodeUDPPacket(validator *Validator, payload *buf.Buffer) (*protocol.Reque return nil, nil, newError("len(bs) <= 32") } - var user *protocol.MemoryUser - var err error + user, _, d, _, err := validator.Get(bs, protocol.RequestCommandUDP) - count := validator.Count() - if count == 0 { - return nil, nil, newError("invalid user") - } else if count > 1 { - var d []byte - user, _, d, _, err = validator.Get(bs, protocol.RequestCommandUDP) - - if user != nil { + if user != nil { + account := user.Account.(*MemoryAccount) + if account.Cipher.IsAEAD() { payload.Clear() payload.Write(d) } else { - return nil, nil, newError("failed to decrypt UDP payload").Base(err) - } - } else { - user, _ = validator.GetOnlyUser() - account := user.Account.(*MemoryAccount) + if account.Cipher.IVSize() > 0 { + iv := make([]byte, account.Cipher.IVSize()) + copy(iv, payload.BytesTo(account.Cipher.IVSize())) + } - var iv []byte - if !account.Cipher.IsAEAD() && account.Cipher.IVSize() > 0 { - // Keep track of IV as it gets removed from payload in DecodePacket. - iv = make([]byte, account.Cipher.IVSize()) - copy(iv, payload.BytesTo(account.Cipher.IVSize())) - } - if err = account.Cipher.DecodePacket(account.Key, payload); err != nil { - return nil, nil, newError("failed to decrypt UDP payload").Base(err) + if err = account.Cipher.DecodePacket(account.Key, payload); err != nil { + return nil, nil, newError("failed to decrypt UDP payload").Base(err) + } } + } else { + return nil, nil, newError("failed to decrypt UDP payload").Base(err) } request := &protocol.RequestHeader{ diff --git a/proxy/shadowsocks/server.go b/proxy/shadowsocks/server.go index c34798f62253..c87c6984bb21 100644 --- a/proxy/shadowsocks/server.go +++ b/proxy/shadowsocks/server.go @@ -114,10 +114,6 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn internet.Connection, panic("no inbound metadata") } - if s.validator.Count() == 1 { - inbound.User, _ = s.validator.GetOnlyUser() - } - var dest *net.Destination reader := buf.NewPacketReader(conn) diff --git a/proxy/shadowsocks/validator.go b/proxy/shadowsocks/validator.go index 7d796bc23400..79c656010bef 100644 --- a/proxy/shadowsocks/validator.go +++ b/proxy/shadowsocks/validator.go @@ -2,112 +2,119 @@ package shadowsocks import ( "crypto/cipher" + "crypto/hmac" + "crypto/sha256" + "hash/crc64" "strings" "sync" + "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/protocol" ) // Validator stores valid Shadowsocks users. type Validator struct { - // Considering email's usage here, map + sync.Mutex/RWMutex may have better performance. - email sync.Map - users sync.Map + sync.RWMutex + users []*protocol.MemoryUser + + behaviorSeed uint64 + behaviorFused bool } -// Add a Shadowsocks user, Email must be empty or unique. +// Add a Shadowsocks user. func (v *Validator) Add(u *protocol.MemoryUser) error { - account := u.Account.(*MemoryAccount) + v.Lock() + defer v.Unlock() - if !account.Cipher.IsAEAD() && v.Count() > 0 { - return newError("The cipher do not support Single-port Multi-user") + account := u.Account.(*MemoryAccount) + if !account.Cipher.IsAEAD() && len(v.users) > 0 { + return newError("The cipher is not support Single-port Multi-user") } + v.users = append(v.users, u) - if u.Email != "" { - _, loaded := v.email.LoadOrStore(strings.ToLower(u.Email), u) - if loaded { - return newError("User ", u.Email, " already exists.") - } + if !v.behaviorFused { + hashkdf := hmac.New(sha256.New, []byte("SSBSKDF")) + hashkdf.Write(account.Key) + v.behaviorSeed = crc64.Update(v.behaviorSeed, crc64.MakeTable(crc64.ECMA), hashkdf.Sum(nil)) } - v.users.Store(string(account.Key)+"&"+account.GetCipherName(), u) return nil } // Del a Shadowsocks user with a non-empty Email. -func (v *Validator) Del(e string) error { - if e == "" { +func (v *Validator) Del(email string) error { + if email == "" { return newError("Email must not be empty.") } - le := strings.ToLower(e) - u, _ := v.email.Load(le) - if u == nil { - return newError("User ", e, " not found.") - } - account := u.(*protocol.MemoryUser).Account.(*MemoryAccount) - v.email.Delete(le) - v.users.Delete(string(account.Key) + "&" + account.GetCipherName()) - return nil -} -// Count the number of Shadowsocks users -func (v *Validator) Count() int { - length := 0 - v.users.Range(func(_, _ interface{}) bool { - length++ + v.Lock() + defer v.Unlock() - return true - }) - return length -} - -// Get a Shadowsocks user and the user's cipher. -func (v *Validator) Get(bs []byte, command protocol.RequestCommand) (u *protocol.MemoryUser, aead cipher.AEAD, ret []byte, ivLen int32, err error) { - var dataSize int + email = strings.ToLower(email) + idx := -1 + for i, u := range v.users { + if strings.EqualFold(u.Email, email) { + idx = i + break + } + } - switch command { - case protocol.RequestCommandTCP: - dataSize = 16 - case protocol.RequestCommandUDP: - dataSize = 8192 + if idx == -1 { + return newError("User ", email, " not found.") } + ulen := len(v.users) - var aeadCipher *AEADCipher - subkey := make([]byte, 32) - data := make([]byte, dataSize) - - v.users.Range(func(key, user interface{}) bool { - account := user.(*protocol.MemoryUser).Account.(*MemoryAccount) - aeadCipher = account.Cipher.(*AEADCipher) - ivLen = aeadCipher.IVSize() - subkey = subkey[:aeadCipher.KeyBytes] - hkdfSHA1(account.Key, bs[:ivLen], subkey) - aead = aeadCipher.AEADAuthCreator(subkey) - - switch command { - case protocol.RequestCommandTCP: - ret, err = aead.Open(data[:0], data[4:16], bs[ivLen:ivLen+18], nil) - case protocol.RequestCommandUDP: - ret, err = aead.Open(data[:0], data[8180:8192], bs[ivLen:], nil) - } + v.users[idx] = v.users[ulen-1] + v.users[ulen-1] = nil + v.users = v.users[:ulen-1] + + return nil +} - if err == nil { - u = user.(*protocol.MemoryUser) - return false +// Get a Shadowsocks user. +func (v *Validator) Get(bs []byte, command protocol.RequestCommand) (u *protocol.MemoryUser, aead cipher.AEAD, ret []byte, ivLen int32, err error) { + v.RLock() + defer v.RUnlock() + + for _, user := range v.users { + if account := user.Account.(*MemoryAccount); account.Cipher.IsAEAD() { + aeadCipher := account.Cipher.(*AEADCipher) + ivLen = aeadCipher.IVSize() + subkey := make([]byte, 32) + subkey = subkey[:aeadCipher.KeyBytes] + hkdfSHA1(account.Key, bs[:ivLen], subkey) + aead = aeadCipher.AEADAuthCreator(subkey) + + switch command { + case protocol.RequestCommandTCP: + data := make([]byte, 16) + ret, err = aead.Open(data[:0], data[4:16], bs[ivLen:ivLen+18], nil) + case protocol.RequestCommandUDP: + data := make([]byte, 8192) + ret, err = aead.Open(data[:0], data[8180:8192], bs[ivLen:], nil) + } + + if err == nil { + u = user + break + } + } else { + u = user + ivLen = u.Account.(*MemoryAccount).Cipher.IVSize() + break } - return true - }) + } return } -// Get the only user without authentication -func (v *Validator) GetOnlyUser() (u *protocol.MemoryUser, ivLen int32) { - v.users.Range(func(_, user interface{}) bool { - u = user.(*protocol.MemoryUser) - return false - }) - ivLen = u.Account.(*MemoryAccount).Cipher.IVSize() +func (v *Validator) GetBehaviorSeed() uint64 { + v.Lock() + defer v.Unlock() - return + v.behaviorFused = true + if v.behaviorSeed == 0 { + v.behaviorSeed = dice.RollUint64() + } + return v.behaviorSeed } From 8f6b27485ef5f5abe1b98c7a1bbe493b9f79d437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E3=81=AE=E3=81=8B=E3=81=88=E3=81=A7?= Date: Fri, 2 Jul 2021 23:40:55 +0800 Subject: [PATCH 2/8] Fix NoneCliper cannot work --- proxy/shadowsocks/protocol.go | 2 +- proxy/shadowsocks/validator.go | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/proxy/shadowsocks/protocol.go b/proxy/shadowsocks/protocol.go index 6aae5abfe8fb..cc53558c1715 100644 --- a/proxy/shadowsocks/protocol.go +++ b/proxy/shadowsocks/protocol.go @@ -228,7 +228,7 @@ func DecodeUDPPacket(validator *Validator, payload *buf.Buffer) (*protocol.Reque if user != nil { account := user.Account.(*MemoryAccount) - if account.Cipher.IsAEAD() { + if _, isAEAD := account.Cipher.(*AEADCipher); isAEAD { payload.Clear() payload.Write(d) } else { diff --git a/proxy/shadowsocks/validator.go b/proxy/shadowsocks/validator.go index 79c656010bef..377ff6163cf5 100644 --- a/proxy/shadowsocks/validator.go +++ b/proxy/shadowsocks/validator.go @@ -27,7 +27,7 @@ func (v *Validator) Add(u *protocol.MemoryUser) error { defer v.Unlock() account := u.Account.(*MemoryAccount) - if !account.Cipher.IsAEAD() && len(v.users) > 0 { + if _, isAEAD := account.Cipher.(*AEADCipher); !isAEAD && len(v.users) > 0 { return newError("The cipher is not support Single-port Multi-user") } v.users = append(v.users, u) @@ -77,7 +77,8 @@ func (v *Validator) Get(bs []byte, command protocol.RequestCommand) (u *protocol defer v.RUnlock() for _, user := range v.users { - if account := user.Account.(*MemoryAccount); account.Cipher.IsAEAD() { + account := user.Account.(*MemoryAccount) + if _, isAEAD := account.Cipher.(*AEADCipher); isAEAD { aeadCipher := account.Cipher.(*AEADCipher) ivLen = aeadCipher.IVSize() subkey := make([]byte, 32) From ffb0e58a05e08baab5806257e0a23b7d2a05f0c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E3=81=AE=E3=81=8B=E3=81=88=E3=81=A7?= Date: Sat, 3 Jul 2021 06:42:35 +0800 Subject: [PATCH 3/8] Revert "Fix NoneCliper cannot work" This reverts commit 8f6b27485ef5f5abe1b98c7a1bbe493b9f79d437. --- proxy/shadowsocks/protocol.go | 2 +- proxy/shadowsocks/validator.go | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/proxy/shadowsocks/protocol.go b/proxy/shadowsocks/protocol.go index cc53558c1715..6aae5abfe8fb 100644 --- a/proxy/shadowsocks/protocol.go +++ b/proxy/shadowsocks/protocol.go @@ -228,7 +228,7 @@ func DecodeUDPPacket(validator *Validator, payload *buf.Buffer) (*protocol.Reque if user != nil { account := user.Account.(*MemoryAccount) - if _, isAEAD := account.Cipher.(*AEADCipher); isAEAD { + if account.Cipher.IsAEAD() { payload.Clear() payload.Write(d) } else { diff --git a/proxy/shadowsocks/validator.go b/proxy/shadowsocks/validator.go index 377ff6163cf5..79c656010bef 100644 --- a/proxy/shadowsocks/validator.go +++ b/proxy/shadowsocks/validator.go @@ -27,7 +27,7 @@ func (v *Validator) Add(u *protocol.MemoryUser) error { defer v.Unlock() account := u.Account.(*MemoryAccount) - if _, isAEAD := account.Cipher.(*AEADCipher); !isAEAD && len(v.users) > 0 { + if !account.Cipher.IsAEAD() && len(v.users) > 0 { return newError("The cipher is not support Single-port Multi-user") } v.users = append(v.users, u) @@ -77,8 +77,7 @@ func (v *Validator) Get(bs []byte, command protocol.RequestCommand) (u *protocol defer v.RUnlock() for _, user := range v.users { - account := user.Account.(*MemoryAccount) - if _, isAEAD := account.Cipher.(*AEADCipher); isAEAD { + if account := user.Account.(*MemoryAccount); account.Cipher.IsAEAD() { aeadCipher := account.Cipher.(*AEADCipher) ivLen = aeadCipher.IVSize() subkey := make([]byte, 32) From 5134477d71d544d9df152911b8afebd6d3abbc22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E3=81=AE=E3=81=8B=E3=81=88=E3=81=A7?= Date: Sat, 3 Jul 2021 06:43:58 +0800 Subject: [PATCH 4/8] Fix NoneCliper cannot work --- proxy/shadowsocks/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/shadowsocks/config.go b/proxy/shadowsocks/config.go index dfc3ce3d1677..0ce32fa2abdb 100644 --- a/proxy/shadowsocks/config.go +++ b/proxy/shadowsocks/config.go @@ -267,7 +267,7 @@ type NoneCipher struct{} func (NoneCipher) KeySize() int32 { return 0 } func (NoneCipher) IVSize() int32 { return 0 } func (NoneCipher) IsAEAD() bool { - return true // to avoid OTA + return false } func (NoneCipher) NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error) { From 6d87f9cfa9218df02f8057af27de083bc15243c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E3=81=AE=E3=81=8B=E3=81=88=E3=81=A7?= Date: Sat, 3 Jul 2021 12:33:31 +0800 Subject: [PATCH 5/8] Feat: refine the size of drain --- proxy/shadowsocks/protocol.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proxy/shadowsocks/protocol.go b/proxy/shadowsocks/protocol.go index 6aae5abfe8fb..638fdbd071f2 100644 --- a/proxy/shadowsocks/protocol.go +++ b/proxy/shadowsocks/protocol.go @@ -74,6 +74,8 @@ func ReadTCPSession(validator *Validator, reader io.Reader) (*protocol.RequestHe if user != nil { reader = &FullReader{reader, bs[ivLen:]} + readSizeRemain -= int(ivLen) + if aead != nil { auth := &crypto.AEADAuthenticator{ AEAD: aead, @@ -87,7 +89,6 @@ func ReadTCPSession(validator *Validator, reader io.Reader) (*protocol.RequestHe iv := append([]byte(nil), buffer.BytesTo(ivLen)...) r, err = account.Cipher.NewDecryptionReader(account.Key, iv, reader) if err != nil { - readSizeRemain -= int(buffer.Len()) DrainConnN(reader, readSizeRemain) return nil, nil, newError("failed to initialize decoding stream").Base(err).AtError() } @@ -106,7 +107,6 @@ func ReadTCPSession(validator *Validator, reader io.Reader) (*protocol.RequestHe Command: protocol.RequestCommandTCP, } - readSizeRemain -= int(buffer.Len()) buffer.Clear() addr, port, err := addrParser.ReadAddressPort(buffer, br) From 6e5bd7bfec9fa99d6486da118c824a64578e6164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E3=81=AE=E3=81=8B=E3=81=88=E3=81=A7?= Date: Sat, 30 Oct 2021 10:42:05 +0800 Subject: [PATCH 6/8] fix: fix validator after merge 'main' --- proxy/shadowsocks/validator.go | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/proxy/shadowsocks/validator.go b/proxy/shadowsocks/validator.go index 79c656010bef..cd537544b536 100644 --- a/proxy/shadowsocks/validator.go +++ b/proxy/shadowsocks/validator.go @@ -21,6 +21,10 @@ type Validator struct { behaviorFused bool } +var ( + ErrNotFound = newError("Not Found") +) + // Add a Shadowsocks user. func (v *Validator) Add(u *protocol.MemoryUser) error { v.Lock() @@ -85,27 +89,30 @@ func (v *Validator) Get(bs []byte, command protocol.RequestCommand) (u *protocol hkdfSHA1(account.Key, bs[:ivLen], subkey) aead = aeadCipher.AEADAuthCreator(subkey) + var matchErr error switch command { case protocol.RequestCommandTCP: data := make([]byte, 16) - ret, err = aead.Open(data[:0], data[4:16], bs[ivLen:ivLen+18], nil) + ret, matchErr = aead.Open(data[:0], data[4:16], bs[ivLen:ivLen+18], nil) case protocol.RequestCommandUDP: data := make([]byte, 8192) - ret, err = aead.Open(data[:0], data[8180:8192], bs[ivLen:], nil) + ret, matchErr = aead.Open(data[:0], data[8180:8192], bs[ivLen:], nil) } - if err == nil { + if matchErr == nil { u = user - break + err = account.CheckIV(bs[:ivLen]) + return } } else { u = user - ivLen = u.Account.(*MemoryAccount).Cipher.IVSize() - break + ivLen = user.Account.(*MemoryAccount).Cipher.IVSize() + // err = user.Account.(*MemoryAccount).CheckIV(bs[:ivLen]) // The IV size of None Cipher is 0. + return } } - return + return nil, nil, nil, 0, ErrNotFound } func (v *Validator) GetBehaviorSeed() uint64 { From 01a533e213a747a5a6ee539c3187e30052037bd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E3=81=AE=E3=81=8B=E3=81=88=E3=81=A7?= Date: Sat, 30 Oct 2021 10:46:17 +0800 Subject: [PATCH 7/8] fix: UDP user logic --- proxy/shadowsocks/protocol.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/proxy/shadowsocks/protocol.go b/proxy/shadowsocks/protocol.go index cb224288db91..68bc3919c132 100644 --- a/proxy/shadowsocks/protocol.go +++ b/proxy/shadowsocks/protocol.go @@ -239,8 +239,12 @@ func DecodeUDPPacket(validator *Validator, payload *buf.Buffer) (*protocol.Reque } user, _, d, _, err := validator.Get(bs, protocol.RequestCommandUDP) - - if user != nil { + switch err { + case ErrIVNotUnique: + return nil, nil, newError("failed iv check").Base(err) + case ErrNotFound: + return nil, nil, newError("failed to match an user").Base(err) + default: account := user.Account.(*MemoryAccount) if account.Cipher.IsAEAD() { payload.Clear() @@ -250,13 +254,10 @@ func DecodeUDPPacket(validator *Validator, payload *buf.Buffer) (*protocol.Reque iv := make([]byte, account.Cipher.IVSize()) copy(iv, payload.BytesTo(account.Cipher.IVSize())) } - if err = account.Cipher.DecodePacket(account.Key, payload); err != nil { return nil, nil, newError("failed to decrypt UDP payload").Base(err) } } - } else { - return nil, nil, newError("failed to decrypt UDP payload").Base(err) } request := &protocol.RequestHeader{ From 33cc68f067e9dfa9ed50d75dca57cbb6cdeb9347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E3=81=AE=E3=81=8B=E3=81=88=E3=81=A7?= Date: Sat, 30 Oct 2021 13:24:52 +0800 Subject: [PATCH 8/8] style: refine code style --- proxy/shadowsocks/validator.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proxy/shadowsocks/validator.go b/proxy/shadowsocks/validator.go index cd537544b536..b36e9bc8271f 100644 --- a/proxy/shadowsocks/validator.go +++ b/proxy/shadowsocks/validator.go @@ -84,9 +84,10 @@ func (v *Validator) Get(bs []byte, command protocol.RequestCommand) (u *protocol if account := user.Account.(*MemoryAccount); account.Cipher.IsAEAD() { aeadCipher := account.Cipher.(*AEADCipher) ivLen = aeadCipher.IVSize() + iv := bs[:ivLen] subkey := make([]byte, 32) subkey = subkey[:aeadCipher.KeyBytes] - hkdfSHA1(account.Key, bs[:ivLen], subkey) + hkdfSHA1(account.Key, iv, subkey) aead = aeadCipher.AEADAuthCreator(subkey) var matchErr error @@ -101,7 +102,7 @@ func (v *Validator) Get(bs []byte, command protocol.RequestCommand) (u *protocol if matchErr == nil { u = user - err = account.CheckIV(bs[:ivLen]) + err = account.CheckIV(iv) return } } else {