package data import ( "crypto/subtle" "errors" "fmt" "time" "github.com/oklog/ulid/v2" "golang.org/x/crypto/scrypt" ) const ( scryptN = 32768 scryptR = 8 scryptP = 2 ) type User struct { ID string Created int64 User string Password []byte Salt []byte Roles []string } type Login struct { User string `json:"user"` Password string `json:"password,omitzero"` Token string `json:"token,omitzero"` } func derive(password string, salt []byte) ([]byte, error) { return scrypt.Key([]byte(password), salt, scryptN, scryptR, scryptP, 32) } func (u *User) Check(login *Login) bool { if u.User != login.User { return false } derived, err := derive(login.Password, u.Salt) if err != nil { return false } if subtle.ConstantTimeCompare(derived, u.Password) != 1 { return false } return true } func (u *User) Register(login *Login) error { var err error if u.User != "" && u.User != login.User { return errors.New("invalid user") } if u.ID == "" { u.ID = ulid.Make().String() } u.User = login.User u.Salt, err = Salt() if err != nil { return fmt.Errorf("failed to register user: %w", err) } u.Password, err = derive(login.Password, u.Salt) if err != nil { return fmt.Errorf("key derivation failed: %w", err) } u.Created = time.Now().Unix() return nil }