GameOver1337 / GitClone Public
К репозиторию
2.0 KB
internal/auth/password.go
2.0 KB
1package auth
2
3import (
4 "crypto/rand"
5 "crypto/subtle"
6 "encoding/base64"
7 "errors"
8 "fmt"
9 "strings"
10
11 "golang.org/x/crypto/argon2"
12)
13
14// Параметры Argon2id (RFC 9106 recommended low-memory profile)
15const (
16 argonTime = 1
17 argonMemory = 64 * 1024
18 argonThreads = 1
19 argonKeyLen = 32
20 saltLen = 16
21)
22
23var ErrInvalidHash = errors.New("auth: hash format invalid")
24
25// HashPassword возвращает строку формата:
26// $argon2id$v=19$m=65536,t=1,p=1$<salt>$<hash>
27func HashPassword(password string) (string, error) {
28 salt := make([]byte, saltLen)
29 if _, err := rand.Read(salt); err != nil {
30 return "", err
31 }
32 hash := argon2.IDKey([]byte(password), salt, uint32(argonTime), argonMemory, uint8(argonThreads), argonKeyLen)
33 return fmt.Sprintf("$argon2id$v=19$m=%d,t=%d,p=%d$%s$%s",
34 argonMemory, argonTime, argonThreads,
35 base64.RawStdEncoding.EncodeToString(salt),
36 base64.RawStdEncoding.EncodeToString(hash),
37 ), nil
38}
39
40// ComparePassword сверяет пароль с сохранённым хэшем.
41func ComparePassword(password, encoded string) (bool, error) {
42 parts := strings.Split(encoded, "$")
43 // ["", "argon2id", "v=19", "m=..,t=..,p=..", salt, hash]
44 if len(parts) != 6 || parts[1] != "argon2id" {
45 return false, ErrInvalidHash
46 }
47
48 var memory, iterations, parallelism uint32
49 _, err := fmt.Sscanf(parts[3], "m=%d,t=%d,p=%d", &memory, &iterations, &parallelism)
50 if err != nil {
51 return false, ErrInvalidHash
52 }
53
54 salt, err := base64.RawStdEncoding.DecodeString(parts[4])
55 if err != nil {
56 return false, ErrInvalidHash
57 }
58 want, err := base64.RawStdEncoding.DecodeString(parts[5])
59 if err != nil {
60 return false, ErrInvalidHash
61 }
62
63 hash := argon2.IDKey([]byte(password), salt, iterations, memory, uint8(parallelism), uint32(len(want)))
64 return subtle.ConstantTimeCompare(hash, want) == 1, nil
65}
66
67// CheckPassword — удобная обёртка: true если пароль верный.
68func CheckPassword(password, encoded string) bool {
69 ok, err := ComparePassword(password, encoded)
70 return err == nil && ok
71}