From db41472efc3fd1d053c4a58606017e17a520aea8 Mon Sep 17 00:00:00 2001 From: Naveen Mahalingam Date: Sat, 20 Apr 2024 10:31:05 -0700 Subject: [PATCH] passphrase: ahead of time capitalization --- README.md | 2 +- passphrase/errors.go | 3 ++- passphrase/generator.go | 35 +++++++++++++++++++++-------------- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 7e96792..670e7c9 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,7 @@ goos: linux goarch: amd64 pkg: github.com/jedib0t/go-passwords/passphrase cpu: AMD Ryzen 9 5900X 12-Core Processor -BenchmarkGenerator_Generate-12 3015926 392.7 ns/op 167 B/op 8 allocs/op +BenchmarkGenerator_Generate-12 4030634 292.0 ns/op 144 B/op 5 allocs/op PASS ok github.com/jedib0t/go-passwords/passphrase 1.603s diff --git a/passphrase/errors.go b/passphrase/errors.go index a018777..036a035 100644 --- a/passphrase/errors.go +++ b/passphrase/errors.go @@ -7,6 +7,7 @@ import ( var ( ErrDictionaryTooSmall = errors.New(fmt.Sprintf("dictionary cannot have less than %d words after word-length restrictions are applied", MinWordsInDictionary)) - ErrNumWordsInvalid = errors.New(fmt.Sprintf("number of words cannot be less than %d", MinNumWords)) + ErrNumWordsTooSmall = errors.New(fmt.Sprintf("number of words cannot be less than %d", NumWordsMin)) + ErrNumWordsTooLarge = errors.New(fmt.Sprintf("number of words cannot be more than %d", NumWordsMax)) ErrWordLengthInvalid = errors.New("word-length rule invalid") ) diff --git a/passphrase/generator.go b/passphrase/generator.go index ab1cf5f..e83d106 100644 --- a/passphrase/generator.go +++ b/passphrase/generator.go @@ -11,8 +11,9 @@ import ( ) const ( - MinNumWords = 2 MinWordsInDictionary = 256 + NumWordsMin = 2 + NumWordsMax = 32 ) type Generator interface { @@ -48,7 +49,7 @@ func NewGenerator(rules ...Rule) (Generator, error) { func (g *generator) Generate() string { var words []string - // generate words + // select words for idx := 0; idx < g.numWords; idx++ { var word string for word == "" || slices.Contains(words, word) { @@ -56,15 +57,6 @@ func (g *generator) Generate() string { } words = append(words, word) } - // capitalize all words - if g.capitalize { - for idx := range words { - r, size := utf8.DecodeRuneInString(words[idx]) - if r != utf8.RuneError { - words[idx] = string(unicode.ToUpper(r)) + words[idx][size:] - } - } - } // inject a random number after one of the words if g.withNumber { idx := g.rng.IntN(len(words)) @@ -83,15 +75,30 @@ func (g *generator) sanitize() (Generator, error) { if g.wordLenMin < 1 || g.wordLenMin > g.wordLenMax { return nil, ErrWordLengthInvalid } - // filter the dictionary and remove too-short or too-long words + + // remove words that are too-short & too-long slices.DeleteFunc(g.dictionary, func(word string) bool { return len(word) < g.wordLenMin || len(word) > g.wordLenMax }) if len(g.dictionary) < MinWordsInDictionary { return nil, ErrDictionaryTooSmall } - if g.numWords < MinNumWords { - return nil, ErrNumWordsInvalid + + // capitalize all words in the dictionary ahead of time + if g.capitalize { + for idx := range g.dictionary { + r, size := utf8.DecodeRuneInString(g.dictionary[idx]) + if r != utf8.RuneError { + g.dictionary[idx] = string(unicode.ToUpper(r)) + g.dictionary[idx][size:] + } + } + } + + if g.numWords < NumWordsMin { + return nil, ErrNumWordsTooSmall + } + if g.numWords > NumWordsMax { + return nil, ErrNumWordsTooLarge } return g, nil }