github.com/xtls/xray-core@v1.8.12-0.20240518155711-3168d27b0bdb/proxy/shadowsocks/validator.go (about) 1 package shadowsocks 2 3 import ( 4 "crypto/cipher" 5 "crypto/hmac" 6 "crypto/sha256" 7 "hash/crc64" 8 "strings" 9 "sync" 10 11 "github.com/xtls/xray-core/common/dice" 12 "github.com/xtls/xray-core/common/protocol" 13 ) 14 15 // Validator stores valid Shadowsocks users. 16 type Validator struct { 17 sync.RWMutex 18 users []*protocol.MemoryUser 19 20 behaviorSeed uint64 21 behaviorFused bool 22 } 23 24 var ErrNotFound = newError("Not Found") 25 26 // Add a Shadowsocks user. 27 func (v *Validator) Add(u *protocol.MemoryUser) error { 28 v.Lock() 29 defer v.Unlock() 30 31 account := u.Account.(*MemoryAccount) 32 if !account.Cipher.IsAEAD() && len(v.users) > 0 { 33 return newError("The cipher is not support Single-port Multi-user") 34 } 35 v.users = append(v.users, u) 36 37 if !v.behaviorFused { 38 hashkdf := hmac.New(sha256.New, []byte("SSBSKDF")) 39 hashkdf.Write(account.Key) 40 v.behaviorSeed = crc64.Update(v.behaviorSeed, crc64.MakeTable(crc64.ECMA), hashkdf.Sum(nil)) 41 } 42 43 return nil 44 } 45 46 // Del a Shadowsocks user with a non-empty Email. 47 func (v *Validator) Del(email string) error { 48 if email == "" { 49 return newError("Email must not be empty.") 50 } 51 52 v.Lock() 53 defer v.Unlock() 54 55 email = strings.ToLower(email) 56 idx := -1 57 for i, u := range v.users { 58 if strings.EqualFold(u.Email, email) { 59 idx = i 60 break 61 } 62 } 63 64 if idx == -1 { 65 return newError("User ", email, " not found.") 66 } 67 ulen := len(v.users) 68 69 v.users[idx] = v.users[ulen-1] 70 v.users[ulen-1] = nil 71 v.users = v.users[:ulen-1] 72 73 return nil 74 } 75 76 // Get a Shadowsocks user. 77 func (v *Validator) Get(bs []byte, command protocol.RequestCommand) (u *protocol.MemoryUser, aead cipher.AEAD, ret []byte, ivLen int32, err error) { 78 v.RLock() 79 defer v.RUnlock() 80 81 for _, user := range v.users { 82 if account := user.Account.(*MemoryAccount); account.Cipher.IsAEAD() { 83 // AEAD payload decoding requires the payload to be over 32 bytes 84 if len(bs) < 32 { 85 continue 86 } 87 88 aeadCipher := account.Cipher.(*AEADCipher) 89 ivLen = aeadCipher.IVSize() 90 iv := bs[:ivLen] 91 subkey := make([]byte, 32) 92 subkey = subkey[:aeadCipher.KeyBytes] 93 hkdfSHA1(account.Key, iv, subkey) 94 aead = aeadCipher.AEADAuthCreator(subkey) 95 96 var matchErr error 97 switch command { 98 case protocol.RequestCommandTCP: 99 data := make([]byte, 4+aead.NonceSize()) 100 ret, matchErr = aead.Open(data[:0], data[4:], bs[ivLen:ivLen+18], nil) 101 case protocol.RequestCommandUDP: 102 data := make([]byte, 8192) 103 ret, matchErr = aead.Open(data[:0], data[8192-aead.NonceSize():8192], bs[ivLen:], nil) 104 } 105 106 if matchErr == nil { 107 u = user 108 err = account.CheckIV(iv) 109 return 110 } 111 } else { 112 u = user 113 ivLen = user.Account.(*MemoryAccount).Cipher.IVSize() 114 // err = user.Account.(*MemoryAccount).CheckIV(bs[:ivLen]) // The IV size of None Cipher is 0. 115 return 116 } 117 } 118 119 return nil, nil, nil, 0, ErrNotFound 120 } 121 122 func (v *Validator) GetBehaviorSeed() uint64 { 123 v.Lock() 124 defer v.Unlock() 125 126 v.behaviorFused = true 127 if v.behaviorSeed == 0 { 128 v.behaviorSeed = dice.RollUint64() 129 } 130 return v.behaviorSeed 131 }