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  }