package password import ( "crypto/rand" "errors" "math/big" ) const defaultLength = 16 // charset excludes visually ambiguous characters: 0/O, 1/l/I const ( upperChars = "ABCDEFGHJKLMNPQRSTUVWXYZ" lowerChars = "abcdefghjkmnpqrstuvwxyz" digitChars = "23456789" specialChars = "!@#$%^&*-_=+" allChars = upperChars + lowerChars + digitChars + specialChars ) // GenerateSecure returns a cryptographically random password that satisfies // uppercase, lowercase, digit, and special character requirements. func GenerateSecure() (string, error) { return GenerateSecureWithLength(defaultLength) } // GenerateSecureWithLength generates a password of at least 12 characters. func GenerateSecureWithLength(length int) (string, error) { if length < 12 { length = 12 } password := make([]byte, length) requiredSets := []string{upperChars, lowerChars, digitChars, specialChars} for i, charset := range requiredSets { ch, err := randomChar(charset) if err != nil { return "", err } password[i] = ch } for i := len(requiredSets); i < length; i++ { ch, err := randomChar(allChars) if err != nil { return "", err } password[i] = ch } if err := shuffle(password); err != nil { return "", err } return string(password), nil } func randomChar(charset string) (byte, error) { n := big.NewInt(int64(len(charset))) idx, err := rand.Int(rand.Reader, n) if err != nil { return 0, err } return charset[idx.Int64()], nil } func shuffle(password []byte) error { for i := len(password) - 1; i > 0; i-- { jBig, err := rand.Int(rand.Reader, big.NewInt(int64(i+1))) if err != nil { return err } j := int(jBig.Int64()) password[i], password[j] = password[j], password[i] } return nil } // ValidatePolicy checks that a password meets admin-creation policy. func ValidatePolicy(password string) error { if len(password) < 8 { return errors.New("password too short") } hasUpper, hasLower, hasDigit, hasSpecial := false, false, false, false for _, char := range password { switch { case char >= 'A' && char <= 'Z': hasUpper = true case char >= 'a' && char <= 'z': hasLower = true case char >= '0' && char <= '9': hasDigit = true default: if isSpecialRune(char) { hasSpecial = true } } } if !hasUpper || !hasLower || !hasDigit || !hasSpecial { return errors.New("password does not meet policy") } return nil } func isSpecialRune(char rune) bool { for _, c := range specialChars { if char == c { return true } } return false }