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] }