1
2
3
4
5
6
7
8
9
10
11 package password
12
13 import (
14 "crypto/rand"
15 "errors"
16 "io"
17 "math/big"
18 "strings"
19 )
20
21
22 var _ PasswordGenerator = (*Generator)(nil)
23
24
25
26
27 type PasswordGenerator interface {
28 Generate(int, int, int, bool, bool) (string, error)
29 MustGenerate(int, int, int, bool, bool) string
30 }
31
32 const (
33
34 LowerLetters = "abcdefghijklmnopqrstuvwxyz"
35
36
37 UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
38
39
40 Digits = "0123456789"
41
42
43 Symbols = "~!@#$%^&*()_+`-={}|[]\\:\"<>?,./"
44 )
45
46 var (
47
48
49 ErrExceedsTotalLength = errors.New("number of digits and symbols must be less than total length")
50
51
52
53 ErrLettersExceedsAvailable = errors.New("number of letters exceeds available letters and repeats are not allowed")
54
55
56
57 ErrDigitsExceedsAvailable = errors.New("number of digits exceeds available digits and repeats are not allowed")
58
59
60
61 ErrSymbolsExceedsAvailable = errors.New("number of symbols exceeds available symbols and repeats are not allowed")
62 )
63
64
65
66 type Generator struct {
67 lowerLetters string
68 upperLetters string
69 digits string
70 symbols string
71 reader io.Reader
72 }
73
74
75 type GeneratorInput struct {
76 LowerLetters string
77 UpperLetters string
78 Digits string
79 Symbols string
80 Reader io.Reader
81 }
82
83
84
85
86 func NewGenerator(i *GeneratorInput) (*Generator, error) {
87 if i == nil {
88 i = new(GeneratorInput)
89 }
90
91 g := &Generator{
92 lowerLetters: i.LowerLetters,
93 upperLetters: i.UpperLetters,
94 digits: i.Digits,
95 symbols: i.Symbols,
96 reader: i.Reader,
97 }
98
99 if g.lowerLetters == "" {
100 g.lowerLetters = LowerLetters
101 }
102
103 if g.upperLetters == "" {
104 g.upperLetters = UpperLetters
105 }
106
107 if g.digits == "" {
108 g.digits = Digits
109 }
110
111 if g.symbols == "" {
112 g.symbols = Symbols
113 }
114
115 if g.reader == nil {
116 g.reader = rand.Reader
117 }
118
119 return g, nil
120 }
121
122
123
124
125
126
127
128
129
130 func (g *Generator) Generate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) (string, error) {
131 letters := g.lowerLetters
132 if !noUpper {
133 letters += g.upperLetters
134 }
135
136 chars := length - numDigits - numSymbols
137 if chars < 0 {
138 return "", ErrExceedsTotalLength
139 }
140
141 if !allowRepeat && chars > len(letters) {
142 return "", ErrLettersExceedsAvailable
143 }
144
145 if !allowRepeat && numDigits > len(g.digits) {
146 return "", ErrDigitsExceedsAvailable
147 }
148
149 if !allowRepeat && numSymbols > len(g.symbols) {
150 return "", ErrSymbolsExceedsAvailable
151 }
152
153 var result string
154
155
156 for i := 0; i < chars; i++ {
157 ch, err := randomElement(g.reader, letters)
158 if err != nil {
159 return "", err
160 }
161
162 if !allowRepeat && strings.Contains(result, ch) {
163 i--
164 continue
165 }
166
167 result, err = randomInsert(g.reader, result, ch)
168 if err != nil {
169 return "", err
170 }
171 }
172
173
174 for i := 0; i < numDigits; i++ {
175 d, err := randomElement(g.reader, g.digits)
176 if err != nil {
177 return "", err
178 }
179
180 if !allowRepeat && strings.Contains(result, d) {
181 i--
182 continue
183 }
184
185 result, err = randomInsert(g.reader, result, d)
186 if err != nil {
187 return "", err
188 }
189 }
190
191
192 for i := 0; i < numSymbols; i++ {
193 sym, err := randomElement(g.reader, g.symbols)
194 if err != nil {
195 return "", err
196 }
197
198 if !allowRepeat && strings.Contains(result, sym) {
199 i--
200 continue
201 }
202
203 result, err = randomInsert(g.reader, result, sym)
204 if err != nil {
205 return "", err
206 }
207 }
208
209 return result, nil
210 }
211
212
213 func (g *Generator) MustGenerate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) string {
214 res, err := g.Generate(length, numDigits, numSymbols, noUpper, allowRepeat)
215 if err != nil {
216 panic(err)
217 }
218 return res
219 }
220
221
222 func Generate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) (string, error) {
223 gen, err := NewGenerator(nil)
224 if err != nil {
225 return "", err
226 }
227
228 return gen.Generate(length, numDigits, numSymbols, noUpper, allowRepeat)
229 }
230
231
232 func MustGenerate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) string {
233 res, err := Generate(length, numDigits, numSymbols, noUpper, allowRepeat)
234 if err != nil {
235 panic(err)
236 }
237 return res
238 }
239
240
241 func randomInsert(reader io.Reader, s, val string) (string, error) {
242 if s == "" {
243 return val, nil
244 }
245
246 n, err := rand.Int(reader, big.NewInt(int64(len(s)+1)))
247 if err != nil {
248 return "", err
249 }
250 i := n.Int64()
251 return s[0:i] + val + s[i:], nil
252 }
253
254
255 func randomElement(reader io.Reader, s string) (string, error) {
256 n, err := rand.Int(reader, big.NewInt(int64(len(s))))
257 if err != nil {
258 return "", err
259 }
260 return string(s[n.Int64()]), nil
261 }
262
View as plain text