82 lines
1.9 KiB
Go
82 lines
1.9 KiB
Go
// Package pw 提供口令散列(PBKDF2-HMAC-SHA256),仅用标准库。
|
|
package pw
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"crypto/subtle"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
const iterations = 120000
|
|
|
|
// Hash 生成 `pbkdf2-sha256$迭代次数$盐$摘要` 形式的字符串。
|
|
func Hash(password string) (string, error) {
|
|
salt := make([]byte, 16)
|
|
if _, err := rand.Read(salt); err != nil {
|
|
return "", err
|
|
}
|
|
dk := pbkdf2Key([]byte(password), salt, iterations, 32)
|
|
return fmt.Sprintf("pbkdf2-sha256$%d$%s$%s",
|
|
iterations,
|
|
base64.RawStdEncoding.EncodeToString(salt),
|
|
base64.RawStdEncoding.EncodeToString(dk)), nil
|
|
}
|
|
|
|
// Verify 校验口令,常量时间比较。
|
|
func Verify(password, encoded string) bool {
|
|
parts := strings.Split(encoded, "$")
|
|
if len(parts) != 4 || parts[0] != "pbkdf2-sha256" {
|
|
return false
|
|
}
|
|
iter, err := strconv.Atoi(parts[1])
|
|
if err != nil || iter <= 0 {
|
|
return false
|
|
}
|
|
salt, err := base64.RawStdEncoding.DecodeString(parts[2])
|
|
if err != nil {
|
|
return false
|
|
}
|
|
want, err := base64.RawStdEncoding.DecodeString(parts[3])
|
|
if err != nil || len(want) == 0 {
|
|
return false
|
|
}
|
|
got := pbkdf2Key([]byte(password), salt, iter, len(want))
|
|
return subtle.ConstantTimeCompare(got, want) == 1
|
|
}
|
|
|
|
func pbkdf2Key(password, salt []byte, iter, keyLen int) []byte {
|
|
prf := hmac.New(sha256.New, password)
|
|
hashLen := prf.Size()
|
|
numBlocks := (keyLen + hashLen - 1) / hashLen
|
|
var buf [4]byte
|
|
dk := make([]byte, 0, numBlocks*hashLen)
|
|
u := make([]byte, hashLen)
|
|
for block := 1; block <= numBlocks; block++ {
|
|
prf.Reset()
|
|
prf.Write(salt)
|
|
buf[0] = byte(block >> 24)
|
|
buf[1] = byte(block >> 16)
|
|
buf[2] = byte(block >> 8)
|
|
buf[3] = byte(block)
|
|
prf.Write(buf[:4])
|
|
dk = prf.Sum(dk)
|
|
t := dk[len(dk)-hashLen:]
|
|
copy(u, t)
|
|
for n := 2; n <= iter; n++ {
|
|
prf.Reset()
|
|
prf.Write(u)
|
|
u = u[:0]
|
|
u = prf.Sum(u)
|
|
for x := range u {
|
|
t[x] ^= u[x]
|
|
}
|
|
}
|
|
}
|
|
return dk[:keyLen]
|
|
}
|