package util import ( "encoding/base64" "golang.org/x/crypto/bcrypt" "hash/fnv" "math/rand" "regexp" "strconv" "strings" ) const tempStringMask = "GAEKEEPHIENOHJOHEOJH" // random string to avoid collision with other text nodes const keyDelimiter = "-" // NewSecret creates a new secret func NewSecret() (string, error) { return NewSecretWithLength(32) } // NewSecretWithLength creates a new secret for a given length func NewSecretWithLength(length int) (string, error) { return RandomString(length) } func randomBytes(len int) ([]byte, error) { b := make([]byte, len) if _, err := rand.Read(b); err != nil { return nil, err } return b, nil } func RandomString(length int) (string, error) { b, err := randomBytes(length) if err != nil { return "", err } s := base64.URLEncoding.EncodeToString(b) return cryptID(s, length) } func cryptID(t string, length int) (string, error) { hash, err := bcrypt.GenerateFromPassword([]byte(t), bcrypt.DefaultCost) if err != nil { return "", err } r := strings.ToUpper(string(hash[len(hash)-length-1 : len(hash)-1])) return r, nil } func hashString(s string) string { h := fnv.New32a() h.Write([]byte(s)) return strconv.Itoa(int(h.Sum32())) } func BuildTextKey(txt string) (string, error) { if txt == "" { return "", nil } txt = strings.TrimSpace(txt) txt = strings.ToLower(txt) txt = strings.ReplaceAll(txt, keyDelimiter, tempStringMask) reg, err := regexp.Compile("[^a-z0-9_-]+") if err != nil { return "", err } newStr := reg.ReplaceAllString(txt, keyDelimiter) newStr = strings.ReplaceAll(newStr, tempStringMask, keyDelimiter) newStr = strings.Trim(newStr, keyDelimiter) if len(newStr) > 20 { newStr = newStr[:20] + keyDelimiter + hashString(newStr) } c := strings.TrimSpace(newStr) if c == "" { return "", nil } if len(c) < 2 { return "", nil } return c, nil }