feat(app): add account and config components
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const accountPasswordIterations = 600000
|
||||
|
||||
func normalizeAccountUsername(name string) (string, string, error) {
|
||||
name = strings.TrimSpace(name)
|
||||
if !utf8.ValidString(name) || utf8.RuneCountInString(name) < 2 || utf8.RuneCountInString(name) > 32 {
|
||||
return "", "", errors.New("用户名需为 2–32 个字,支持中文、字母、数字、下划线和短横线")
|
||||
}
|
||||
for _, char := range name {
|
||||
if !unicode.IsLetter(char) && !unicode.IsDigit(char) && char != '_' && char != '-' {
|
||||
return "", "", errors.New("用户名只能包含中文、字母、数字、下划线和短横线")
|
||||
}
|
||||
}
|
||||
return strings.ToLower(name), name, nil
|
||||
}
|
||||
|
||||
func validateAccountPassword(password string) error {
|
||||
count := utf8.RuneCountInString(password)
|
||||
if !utf8.ValidString(password) || count < 10 || count > 128 {
|
||||
return errors.New("密码需为 10–128 个字符,可使用长短语")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func accountPasswordKey(password string, salt []byte, iterations int) []byte {
|
||||
mac := hmac.New(sha256.New, []byte(password))
|
||||
mac.Write(salt)
|
||||
mac.Write([]byte{0, 0, 0, 1})
|
||||
block := mac.Sum(nil)
|
||||
key := append([]byte(nil), block...)
|
||||
for remaining := iterations - 1; remaining > 0; remaining-- {
|
||||
mac.Reset()
|
||||
mac.Write(block)
|
||||
block = mac.Sum(block[:0])
|
||||
for index, value := range block {
|
||||
key[index] ^= value
|
||||
}
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func newAccountPassword(password string) (string, string, error) {
|
||||
if err := validateAccountPassword(password); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
salt := randomHex(16)
|
||||
if salt == "" {
|
||||
return "", "", errors.New("无法生成密码盐,请重试")
|
||||
}
|
||||
decoded, _ := hex.DecodeString(salt)
|
||||
return salt, hex.EncodeToString(accountPasswordKey(password, decoded, accountPasswordIterations)), nil
|
||||
}
|
||||
|
||||
func verifyAccountPassword(account accountRecord, password string) bool {
|
||||
salt, _ := hex.DecodeString(account.Salt)
|
||||
expected, _ := hex.DecodeString(account.PasswordHash)
|
||||
return hmac.Equal(expected, accountPasswordKey(password, salt, account.Iterations))
|
||||
}
|
||||
Reference in New Issue
Block a user