Files
2026-08-29 17:02:42 +03:00

71 lines
2.0 KiB
Go

package domain
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"crypto/subtle"
"encoding/base64"
"errors"
"strconv"
"strings"
)
const creatorPasswordIterations = 120000
var ErrInvalidCreatorPassword = errors.New("invalid creator password")
func HashCreatorPassword(password string) (string, error) {
if len(password) < 6 || len(password) > 4096 {
return "", ErrInvalidCreatorPassword
}
salt := make([]byte, 16)
if _, err := rand.Read(salt); err != nil {
return "", err
}
derived := pbkdf2SHA256([]byte(password), salt, creatorPasswordIterations, 32)
return "pbkdf2-sha256$" + strconv.Itoa(creatorPasswordIterations) + "$" + base64.RawStdEncoding.EncodeToString(salt) + "$" + base64.RawStdEncoding.EncodeToString(derived), nil
}
func VerifyCreatorPassword(encoded, password string) bool {
parts := strings.Split(encoded, "$")
if len(parts) != 4 || parts[0] != "pbkdf2-sha256" || password == "" {
return false
}
iterations, err := strconv.Atoi(parts[1])
if err != nil || iterations < 100000 || iterations > 1000000 {
return false
}
salt, err := base64.RawStdEncoding.DecodeString(parts[2])
if err != nil || len(salt) < 16 {
return false
}
expected, err := base64.RawStdEncoding.DecodeString(parts[3])
if err != nil || len(expected) != 32 {
return false
}
actual := pbkdf2SHA256([]byte(password), salt, iterations, len(expected))
return subtle.ConstantTimeCompare(actual, expected) == 1
}
func pbkdf2SHA256(password, salt []byte, iterations, keyLength int) []byte {
key := make([]byte, 0, keyLength)
for block := 1; len(key) < keyLength; block++ {
mac := hmac.New(sha256.New, password)
mac.Write(salt)
mac.Write([]byte{byte(block >> 24), byte(block >> 16), byte(block >> 8), byte(block)})
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]
}
}
key = append(key, t...)
}
return key[:keyLength]
}