feat: password.
This commit is contained in:
111
password/argon2.go
Normal file
111
password/argon2.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package password
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/subtle"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/argon2"
|
||||
)
|
||||
|
||||
// Argon2Crypto 实现 Argon2id 密码哈希算法
|
||||
type Argon2Crypto struct {
|
||||
// 参数可配置,默认使用推荐值
|
||||
Memory uint32
|
||||
Iterations uint32
|
||||
Parallelism uint8
|
||||
SaltLength uint32
|
||||
KeyLength uint32
|
||||
}
|
||||
|
||||
// NewArgon2Crypto 创建带默认参数的 Argon2 加密器
|
||||
func NewArgon2Crypto() *Argon2Crypto {
|
||||
return &Argon2Crypto{
|
||||
Memory: 64 * 1024, // 64MB
|
||||
Iterations: 3,
|
||||
Parallelism: 2,
|
||||
SaltLength: 16,
|
||||
KeyLength: 32,
|
||||
}
|
||||
}
|
||||
|
||||
// Encrypt 实现密码加密
|
||||
func (a *Argon2Crypto) Encrypt(password string) (string, error) {
|
||||
// 生成随机盐值
|
||||
salt := make([]byte, a.SaltLength)
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 生成哈希
|
||||
hash := argon2.IDKey(
|
||||
[]byte(password),
|
||||
salt,
|
||||
a.Iterations,
|
||||
a.Memory,
|
||||
a.Parallelism,
|
||||
a.KeyLength,
|
||||
)
|
||||
|
||||
// 格式化输出(兼容标准格式)
|
||||
return fmt.Sprintf(
|
||||
"$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s",
|
||||
argon2.Version,
|
||||
a.Memory,
|
||||
a.Iterations,
|
||||
a.Parallelism,
|
||||
base64.RawStdEncoding.EncodeToString(salt),
|
||||
base64.RawStdEncoding.EncodeToString(hash),
|
||||
), nil
|
||||
}
|
||||
|
||||
// Verify 验证密码
|
||||
func (a *Argon2Crypto) Verify(password, encrypted string) (bool, error) {
|
||||
// 解析哈希字符串
|
||||
parts := strings.Split(encrypted, "$")
|
||||
if len(parts) != 6 {
|
||||
return false, errors.New("无效的 Argon2 哈希格式")
|
||||
}
|
||||
|
||||
// 解析参数
|
||||
var version int
|
||||
var memory, iterations uint32
|
||||
var parallelism uint8
|
||||
_, err := fmt.Sscanf(parts[2], "v=%d", &version)
|
||||
if err != nil || version != argon2.Version {
|
||||
return false, errors.New("不支持的 Argon2 版本")
|
||||
}
|
||||
|
||||
_, err = fmt.Sscanf(parts[3], "m=%d,t=%d,p=%d", &memory, &iterations, ¶llelism)
|
||||
if err != nil {
|
||||
return false, errors.New("无效的 Argon2 参数")
|
||||
}
|
||||
|
||||
// 解码盐值和哈希
|
||||
salt, err := base64.RawStdEncoding.DecodeString(parts[4])
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
decodedHash, err := base64.RawStdEncoding.DecodeString(parts[5])
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
keyLength := uint32(len(decodedHash))
|
||||
|
||||
// 使用相同参数生成新哈希
|
||||
newHash := argon2.IDKey(
|
||||
[]byte(password),
|
||||
salt,
|
||||
iterations,
|
||||
memory,
|
||||
parallelism,
|
||||
keyLength,
|
||||
)
|
||||
|
||||
// 安全比较
|
||||
return subtle.ConstantTimeCompare(newHash, decodedHash) == 1, nil
|
||||
}
|
||||
Reference in New Issue
Block a user