diff options
| author | Chia <Chia@93.nz> | 2026-08-05 09:26:05 +1200 |
|---|---|---|
| committer | Chia <Chia@93.nz> | 2026-08-05 09:26:05 +1200 |
| commit | 86b1f42e3c5601ff10621a9779cf0076590797a1 (patch) | |
| tree | ccf584f0404dece2e4dae2eee847b665f57c0737 /internal/security/password.go | |
| parent | 1a3d7f9a8a181df48f0e911cbe17a3fad3ab9ac9 (diff) | |
add sth.
Diffstat (limited to 'internal/security/password.go')
| -rw-r--r-- | internal/security/password.go | 60 |
1 files changed, 60 insertions, 0 deletions
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 +} |
