Files
ocdp-go/backend/internal/pkg/password/password.go
mangomqy c5e51ed069 ocdp v1
2025-11-13 02:54:06 +00:00

98 lines
2.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package password
import (
"crypto/rand"
"crypto/subtle"
"encoding/base64"
"fmt"
"strings"
"golang.org/x/crypto/argon2"
)
const (
// Argon2id 参数
memory = 64 * 1024 // 64 MB
iterations = 3
parallelism = 2
saltLength = 16
keyLength = 32
)
// Hasher 密码哈希器
type Hasher struct{}
// NewHasher 创建密码哈希器
func NewHasher() *Hasher {
return &Hasher{}
}
// Hash 哈希密码(使用 Argon2id
func (h *Hasher) Hash(password string) (string, error) {
// 生成随机 salt
salt := make([]byte, saltLength)
if _, err := rand.Read(salt); err != nil {
return "", fmt.Errorf("failed to generate salt: %w", err)
}
// 使用 Argon2id 哈希密码
hash := argon2.IDKey([]byte(password), salt, iterations, memory, parallelism, keyLength)
// 编码为字符串格式: $argon2id$v=19$m=65536,t=3,p=2$salt$hash
b64Salt := base64.RawStdEncoding.EncodeToString(salt)
b64Hash := base64.RawStdEncoding.EncodeToString(hash)
encodedHash := fmt.Sprintf("$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s",
argon2.Version, memory, iterations, parallelism, b64Salt, b64Hash)
return encodedHash, nil
}
// Verify 验证密码
func (h *Hasher) Verify(password, encodedHash string) error {
// 解析编码的哈希
parts := strings.Split(encodedHash, "$")
if len(parts) != 6 {
return fmt.Errorf("invalid hash format")
}
if parts[1] != "argon2id" {
return fmt.Errorf("unsupported algorithm: %s", parts[1])
}
// 解析参数
var version int
var m, t, p uint32
_, err := fmt.Sscanf(parts[2], "v=%d", &version)
if err != nil {
return fmt.Errorf("failed to parse version: %w", err)
}
_, err = fmt.Sscanf(parts[3], "m=%d,t=%d,p=%d", &m, &t, &p)
if err != nil {
return fmt.Errorf("failed to parse parameters: %w", err)
}
// 解码 salt 和 hash
salt, err := base64.RawStdEncoding.DecodeString(parts[4])
if err != nil {
return fmt.Errorf("failed to decode salt: %w", err)
}
hash, err := base64.RawStdEncoding.DecodeString(parts[5])
if err != nil {
return fmt.Errorf("failed to decode hash: %w", err)
}
// 使用相同参数哈希输入的密码
computedHash := argon2.IDKey([]byte(password), salt, t, m, uint8(p), uint32(len(hash)))
// 使用常量时间比较防止时序攻击
if subtle.ConstantTimeCompare(hash, computedHash) == 1 {
return nil
}
return fmt.Errorf("password does not match")
}