axe/internal/auth/password.go
2026-10-18 22:07:25 +03:00

69 lines
2.6 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package auth
import (
"errors"
"unicode"
"golang.org/x/crypto/bcrypt"
)
// Политика установки пароля. Применяется только при создании/смене
// пароля: уже существующие аккаунты входят со своими старыми паролями.
const (
// MinPasswordLen — минимум символов (считаем руны, не байты:
// кириллический символ — уже два байта).
MinPasswordLen = 10
// MaxPasswordLen — потолок bcrypt: он режет вход длиннее 72 байт,
// пароль «длиннее максимума» молча стал бы короче.
MaxPasswordLen = 72
)
// Ошибки политики пароля. Тексты — короткие и англоязычные,
// как и все ошибки API.
var (
ErrPasswordShort = errors.New("password must be at least 10 characters")
ErrPasswordLong = errors.New("password must be at most 72 characters")
ErrPasswordWeak = errors.New("password must contain letters and digits")
)
// ValidatePassword проверяет пароль по политике: длина 10..72 и
// обязательные буквы И цифры. Спецсимволы не требуются — их принуждение
// чаще порождает «P@ssw0rd!», чем стойкость; зато цифры и буквы отсекают
// «password» и «1234567890».
func ValidatePassword(password string) error {
n := 0
var hasLetter, hasDigit bool
for _, r := range password {
n++
switch {
case unicode.IsLetter(r):
hasLetter = true
case unicode.IsDigit(r):
hasDigit = true
}
}
if n < MinPasswordLen {
return ErrPasswordShort
}
if len(password) > MaxPasswordLen {
return ErrPasswordLong
}
if !hasLetter || !hasDigit {
return ErrPasswordWeak
}
return nil
}
// HashPassword считает bcrypt-хеш с cost 12. Ошибка возможна только
// на очень длинных паролях (bcrypt режет вход длиннее 72 байт).
func HashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 12)
return string(bytes), err
}
// CheckPassword сверяет пароль с хешем. Сравнение за постоянное время —
// перебор по времени ответа не даёт выигрыша.
func CheckPassword(hash, password string) bool {
return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) == nil
}