72 lines
1.7 KiB
Go
72 lines
1.7 KiB
Go
package customer
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"crypto/subtle"
|
|
"encoding/base64"
|
|
"encoding/binary"
|
|
"errors"
|
|
"strings"
|
|
)
|
|
|
|
var rawURL = base64.RawURLEncoding
|
|
|
|
func RandomToken(n int) string {
|
|
if n < 16 {
|
|
n = 16
|
|
}
|
|
b := make([]byte, n)
|
|
_, _ = rand.Read(b)
|
|
return rawURL.EncodeToString(b)
|
|
}
|
|
|
|
func NewPasswordHash(password string) (salt string, hash string, err error) {
|
|
if len(strings.TrimSpace(password)) < 12 {
|
|
return "", "", errors.New("password must be at least 12 characters")
|
|
}
|
|
s := make([]byte, 16)
|
|
if _, err := rand.Read(s); err != nil {
|
|
return "", "", err
|
|
}
|
|
h := pbkdf2SHA256([]byte(password), s, 310000, 32)
|
|
return rawURL.EncodeToString(s), rawURL.EncodeToString(h), nil
|
|
}
|
|
func VerifyPassword(password, salt, hash string) bool {
|
|
s, err := rawURL.DecodeString(salt)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
want, err := rawURL.DecodeString(hash)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
got := pbkdf2SHA256([]byte(password), s, 310000, len(want))
|
|
return len(got) == len(want) && subtle.ConstantTimeCompare(got, want) == 1
|
|
}
|
|
func pbkdf2SHA256(password, salt []byte, iterations, keyLen int) []byte {
|
|
hLen := sha256.Size
|
|
blocks := (keyLen + hLen - 1) / hLen
|
|
out := make([]byte, 0, blocks*hLen)
|
|
for block := 1; block <= blocks; block++ {
|
|
mac := hmac.New(sha256.New, password)
|
|
mac.Write(salt)
|
|
var n [4]byte
|
|
binary.BigEndian.PutUint32(n[:], uint32(block))
|
|
mac.Write(n[:])
|
|
u := mac.Sum(nil)
|
|
t := append([]byte(nil), u...)
|
|
for i := 1; i < iterations; i++ {
|
|
mac = hmac.New(sha256.New, password)
|
|
mac.Write(u)
|
|
u = mac.Sum(nil)
|
|
for j := range t {
|
|
t[j] ^= u[j]
|
|
}
|
|
}
|
|
out = append(out, t...)
|
|
}
|
|
return out[:keyLen]
|
|
}
|