package state import ( "crypto/rand" "crypto/sha256" "crypto/subtle" "encoding/hex" "fmt" "sort" "strings" "time" "unicode" ) func randomHex(bytes int) string { buf := make([]byte, bytes) _, _ = rand.Read(buf) return hex.EncodeToString(buf) } func newUUIDv4() string { buf := make([]byte, 16) _, _ = rand.Read(buf) buf[6] = (buf[6] & 0x0f) | 0x40 buf[8] = (buf[8] & 0x3f) | 0x80 return fmt.Sprintf("%x-%x-%x-%x-%x", buf[0:4], buf[4:6], buf[6:8], buf[8:10], buf[10:16]) } func normalizeName(input string, max int) string { trimmed := strings.TrimSpace(input) if trimmed == "" { return "" } builder := strings.Builder{} for _, r := range trimmed { if r == '\n' || r == '\r' || r == '\t' { continue } if unicode.IsPrint(r) { builder.WriteRune(r) } if builder.Len() >= max { break } } return strings.TrimSpace(builder.String()) } func normalizeCard(input string) string { return normalizeName(input, 8) } func hashPassword(password, salt string) string { h := sha256.Sum256([]byte(salt + ":" + password)) return hex.EncodeToString(h[:]) } func passwordMatches(password, salt, expectedHash string) bool { computed := hashPassword(password, salt) return subtle.ConstantTimeCompare([]byte(computed), []byte(expectedHash)) == 1 } func secureTokenMatches(expected, provided string) bool { if expected == "" || provided == "" || len(expected) != len(provided) { return false } return subtle.ConstantTimeCompare([]byte(expected), []byte(provided)) == 1 } func nowUTC() time.Time { return time.Now().UTC() } func sortParticipants(participants map[string]*Participant) []*Participant { list := make([]*Participant, 0, len(participants)) for _, participant := range participants { list = append(list, participant) } sort.SliceStable(list, func(i, j int) bool { if list[i].JoinedAt.Equal(list[j].JoinedAt) { return list[i].Username < list[j].Username } return list[i].JoinedAt.Before(list[j].JoinedAt) }) return list }