-
Notifications
You must be signed in to change notification settings - Fork 0
/
hash.go
executable file
·77 lines (60 loc) · 2.07 KB
/
hash.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package secure
import (
"crypto/rand"
"crypto/subtle"
"encoding/base64"
"errors"
"fmt"
"strings"
"golang.org/x/crypto/argon2"
"golang.org/x/crypto/bcrypt"
)
// BcryptGenerateHash generates a new hash from a password at the given cost.
func BcryptGenerateHash(password []byte, cost int) ([]byte, error) {
return bcrypt.GenerateFromPassword(password, cost)
}
// BcryptCompareHash compares an hashed password with its plain equivalent.
func BcryptCompareHash(hashedPassword []byte, password []byte) error {
return bcrypt.CompareHashAndPassword(hashedPassword, password)
}
// Argon2GenerateHash generates a new hash from a password with the given parameters.
func Argon2GenerateHash(password []byte, memory uint32, time uint32, threads uint8, saltLen int, keyLen uint32) ([]byte, error) {
salt := make([]byte, saltLen)
if _, err := rand.Read(salt); err != nil {
return nil, err
}
hash := argon2.IDKey(password, salt, time, memory, threads, keyLen)
b64Salt := base64.RawStdEncoding.EncodeToString(salt)
b64Hash := base64.RawStdEncoding.EncodeToString(hash)
format := "$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s"
encoded := []byte(fmt.Sprintf(format, argon2.Version, memory, time, threads, b64Salt, b64Hash))
return encoded, nil
}
// Argon2CompareHash compares an hashed password with its plain equivalent.
func Argon2CompareHash(hashedPassword []byte, password []byte) error {
parts := strings.Split(string(hashedPassword), "$")
c := struct {
memory uint32
time uint32
threads uint8
keyLen uint32
}{}
_, err := fmt.Sscanf(parts[3], "m=%d,t=%d,p=%d", &c.memory, &c.time, &c.threads)
if err != nil {
return err
}
salt, err := base64.RawStdEncoding.DecodeString(parts[4])
if err != nil {
return err
}
decodedHash, err := base64.RawStdEncoding.DecodeString(parts[5])
if err != nil {
return err
}
c.keyLen = uint32(len(decodedHash))
comparisonHash := argon2.IDKey([]byte(password), salt, c.time, c.memory, c.threads, c.keyLen)
if subtle.ConstantTimeCompare(decodedHash, comparisonHash) == 1 {
return nil
}
return errors.New("secure: password does not match")
}