Files
cmroubao/backend-api/internal/platform/password/bcrypt.go
T

70 lines
1.5 KiB
Go
Raw Normal View History

package password
import (
"errors"
"unicode/utf8"
"cmroubao/backend-api/internal/domain"
"golang.org/x/crypto/bcrypt"
)
var (
ErrInvalidPassword = errors.New("password is invalid")
ErrPasswordMismatch = errors.New("password does not match")
)
type Bcrypt struct {
cost int
dummyHash string
}
func NewBcrypt(cost int) (*Bcrypt, error) {
if cost < bcrypt.MinCost || cost > bcrypt.MaxCost {
return nil, errors.New("bcrypt cost is out of range")
}
dummyHash, err := bcrypt.GenerateFromPassword(
[]byte("cmroubao-dummy-password"),
cost,
)
if err != nil {
return nil, errors.New("initialize bcrypt dummy hash")
}
return &Bcrypt{cost: cost, dummyHash: string(dummyHash)}, nil
}
func (manager *Bcrypt) Hash(plain string) (string, error) {
if !utf8.ValidString(plain) ||
len([]byte(plain)) < domain.MinPasswordBytes ||
len([]byte(plain)) > domain.MaxPasswordBytes {
return "", ErrInvalidPassword
}
value, err := bcrypt.GenerateFromPassword([]byte(plain), manager.cost)
if err != nil {
return "", errors.New("hash password")
}
return string(value), nil
}
func (manager *Bcrypt) VerifyDummy(plain string) {
_ = bcrypt.CompareHashAndPassword(
[]byte(manager.dummyHash),
[]byte(plain),
)
}
func (manager *Bcrypt) Verify(encoded string, plain string) error {
if encoded == "" ||
plain == "" ||
len([]byte(plain)) > domain.MaxPasswordBytes {
return ErrPasswordMismatch
}
if err := bcrypt.CompareHashAndPassword(
[]byte(encoded),
[]byte(plain),
); err != nil {
return ErrPasswordMismatch
}
return nil
}