71 lines
2.0 KiB
Go
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]
|
|
}
|