From 86b1f42e3c5601ff10621a9779cf0076590797a1 Mon Sep 17 00:00:00 2001 From: Chia Date: Wed, 5 Aug 2026 09:26:05 +1200 Subject: add sth. --- internal/security/password.go | 60 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 internal/security/password.go (limited to 'internal/security/password.go') diff --git a/internal/security/password.go b/internal/security/password.go new file mode 100644 index 0000000..5d805ca --- /dev/null +++ b/internal/security/password.go @@ -0,0 +1,60 @@ +package security + +import ( + "crypto/pbkdf2" + "crypto/rand" + "crypto/sha256" + "crypto/subtle" + "errors" + "unicode" +) + +const ( + PasswordSaltBytes = 16 + PasswordHashBytes = 32 + PasswordIterations = 600_000 +) + +var ErrWeakPassword = errors.New("password must be 12-128 characters and contain letters and numbers") + +func ValidatePassword(password string) error { + runes := []rune(password) + if len(runes) < 12 || len(runes) > 128 { + return ErrWeakPassword + } + var letter, number bool + for _, value := range runes { + letter = letter || unicode.IsLetter(value) + number = number || unicode.IsNumber(value) + } + if !letter || !number { + return ErrWeakPassword + } + return nil +} + +func HashPassword(password string) (hash, salt []byte, iterations int, err error) { + if err := ValidatePassword(password); err != nil { + return nil, nil, 0, err + } + salt = make([]byte, PasswordSaltBytes) + if _, err := rand.Read(salt); err != nil { + return nil, nil, 0, err + } + hash, err = pbkdf2.Key(sha256.New, password, salt, PasswordIterations, PasswordHashBytes) + if err != nil { + return nil, nil, 0, err + } + return hash, salt, PasswordIterations, nil +} + +func VerifyPassword(password string, expectedHash, salt []byte, iterations int) bool { + if len(expectedHash) != PasswordHashBytes || len(salt) != PasswordSaltBytes || iterations < 100_000 || iterations > 10_000_000 || len([]rune(password)) > 128 { + return false + } + actual, err := pbkdf2.Key(sha256.New, password, salt, iterations, len(expectedHash)) + if err != nil { + return false + } + return subtle.ConstantTimeCompare(actual, expectedHash) == 1 +} -- cgit v1.2.3