diff --git a/stringy/gen.go b/stringy/gen.go new file mode 100644 index 0000000..fee0e93 --- /dev/null +++ b/stringy/gen.go @@ -0,0 +1,67 @@ +package stringy + +import ( + "crypto/rand" + "fmt" + "math" + "math/big" + "strings" +) + +// StringGen generates a random string of a specified length and complexity. +// Requires: complexity (minimum percentage of special characters to be returned in the generated string; set to 0 to generate a simple string), +// complexCharsetLevel (1 = safe for filenames, 2 = safe for most password entries, 3 = safe only for well-made password entries) +func StringGen(length int, complexity float64, complexCharsetLevel uint8) string { + var actualSpecialChars int // track the number of special characters in the generated string + var minSpecialChars int // track the minimum number of special characters to accept + var extendedCharset string // additions to character set used for complex strings + + charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" // default character set used for all strings + const extendedCharsetFiles = "!#$%&+,-.;=@_~^()[]{}`'" // additional special characters for complex strings (safe in file names) + const extendedCharsetMostPassword = "*:> 0 { + minSpecialChars = int(math.Round(float64(length) * complexity)) // determine minimum number of special characters to accept + switch complexCharsetLevel { + case 1: + extendedCharset = extendedCharsetFiles + case 2: + extendedCharset = extendedCharsetMostPassword + extendedCharsetFiles[:len(extendedCharsetFiles)-9] + case 3: + extendedCharset = extendedCharsetFiles + extendedCharsetMostPassword + extendedCharsetSpecialPassword + } + charset += extendedCharset + } + + // loop until a string of the desired complexity is generated + for { + // generate a random string + result := make([]byte, length) + for i := range result { + val, _ := rand.Int(rand.Reader, big.NewInt(int64(len(charset)))) + result[i] = charset[val.Int64()] + } + + // return early if the string is not complex + if complexity <= 0 { + return string(result) + } + + // count the number of special characters in the generated string + for _, char := range string(result) { + if strings.ContainsRune(extendedCharset, char) { + actualSpecialChars++ + } + } + + // return the generated string if it contains enough special characters + if actualSpecialChars >= minSpecialChars { + return string(result) + } + + // reset special character counter + fmt.Println("Regenerating string until desired complexity is achieved...") + actualSpecialChars = 0 + } +}