/
password.go
70 lines (57 loc) · 1.6 KB
/
password.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
package cryp
import (
"time"
"golang.org/x/crypto/bcrypt"
)
func HashPassword(password string, cost int) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), cost)
return string(bytes), err
}
func ComparePassword(hashed, password string) error {
return bcrypt.CompareHashAndPassword([]byte(hashed), []byte(password))
}
func ShouldUpdateCost(hashed string, cost int) (bool, error) {
hashedCost, err := bcrypt.Cost([]byte(hashed))
return hashedCost < cost || err != nil, err
}
func OptimalCost(target time.Duration) int {
cost := bcrypt.MinCost
start := time.Now()
HashPassword("microbenchmark", cost)
end := time.Now()
dur := end.Sub(start)
for dur < target {
cost = cost + 1
dur = 2 * dur
}
cost = cost - 1
if cost > bcrypt.MaxCost {
return bcrypt.MaxCost
}
return cost
}
type passwordHasher struct {
cost int
}
type PasswordHasher interface {
Cost() int
HashPassword(password string) (string, error)
ComparePassword(hashed, password string) error
ShouldUpdateCost(hashed string) (bool, error)
}
func NewPasswordHasher(target time.Duration) PasswordHasher {
return &passwordHasher{cost: OptimalCost(target)}
}
func (ph *passwordHasher) Cost() int {
return ph.cost
}
func (ph *passwordHasher) HashPassword(password string) (string, error) {
return HashPassword(password, ph.cost)
}
func (ph *passwordHasher) ComparePassword(hashed, password string) error {
return ComparePassword(hashed, password)
}
func (ph *passwordHasher) ShouldUpdateCost(hashed string) (bool, error) {
hashedCost, err := bcrypt.Cost([]byte(hashed))
return hashedCost < ph.cost || err != nil, err
}