package vmess import ( "crypto/hmac" "crypto/sha256" "hash/crc64" "strings" "sync" "github.com/xtls/xray-core/common/dice" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/proxy/vmess/aead" ) // TimedUserValidator is a user Validator based on time. type TimedUserValidator struct { sync.RWMutex users []*protocol.MemoryUser behaviorSeed uint64 behaviorFused bool aeadDecoderHolder *aead.AuthIDDecoderHolder } // NewTimedUserValidator creates a new TimedUserValidator. func NewTimedUserValidator() *TimedUserValidator { tuv := &TimedUserValidator{ users: make([]*protocol.MemoryUser, 0, 16), aeadDecoderHolder: aead.NewAuthIDDecoderHolder(), } return tuv } func (v *TimedUserValidator) Add(u *protocol.MemoryUser) error { v.Lock() defer v.Unlock() v.users = append(v.users, u) account, ok := u.Account.(*MemoryAccount) if !ok { return errors.New("account type is incorrect") } if !v.behaviorFused { hashkdf := hmac.New(sha256.New, []byte("VMESSBSKDF")) hashkdf.Write(account.ID.Bytes()) v.behaviorSeed = crc64.Update(v.behaviorSeed, crc64.MakeTable(crc64.ECMA), hashkdf.Sum(nil)) } var cmdkeyfl [16]byte copy(cmdkeyfl[:], account.ID.CmdKey()) v.aeadDecoderHolder.AddUser(cmdkeyfl, u) return nil } func (v *TimedUserValidator) GetUsers() []*protocol.MemoryUser { v.Lock() defer v.Unlock() dst := make([]*protocol.MemoryUser, len(v.users)) copy(dst, v.users) return dst } func (v *TimedUserValidator) GetCount() int64 { v.Lock() defer v.Unlock() return int64(len(v.users)) } func (v *TimedUserValidator) GetAEAD(userHash []byte) (*protocol.MemoryUser, bool, error) { v.RLock() defer v.RUnlock() var userHashFL [16]byte copy(userHashFL[:], userHash) userd, err := v.aeadDecoderHolder.Match(userHashFL) if err != nil { return nil, false, err } return userd.(*protocol.MemoryUser), true, nil } func (v *TimedUserValidator) Remove(email string) bool { v.Lock() defer v.Unlock() email = strings.ToLower(email) idx := -1 for i, u := range v.users { if strings.EqualFold(u.Email, email) { idx = i var cmdkeyfl [16]byte copy(cmdkeyfl[:], u.Account.(*MemoryAccount).ID.CmdKey()) v.aeadDecoderHolder.RemoveUser(cmdkeyfl) break } } if idx == -1 { return false } ulen := len(v.users) v.users[idx] = v.users[ulen-1] v.users[ulen-1] = nil v.users = v.users[:ulen-1] return true } func (v *TimedUserValidator) GetBehaviorSeed() uint64 { v.Lock() defer v.Unlock() v.behaviorFused = true if v.behaviorSeed == 0 { v.behaviorSeed = dice.RollUint64() } return v.behaviorSeed } var ErrNotFound = errors.New("Not Found") var ErrTainted = errors.New("ErrTainted")