passgen 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +13 -0
- data/Manifest +11 -0
- data/README.rdoc +158 -0
- data/Rakefile +15 -0
- data/lib/passgen.rb +328 -0
- data/lib/passgen/probabilities.rb +804 -0
- data/lib/passgen/strength_analyzer.rb +282 -0
- data/passgen.gemspec +29 -0
- data/rails/init.rb +2 -0
- data/spec/passgen/strength_analyzer_spec.rb +75 -0
- data/spec/passgen_spec.rb +150 -0
- metadata +66 -0
data/CHANGELOG
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
v1.0.1 Corrected syntax error with ruby1.9.2
|
2
|
+
|
3
|
+
v1.0.0. Added password strength analyzer
|
4
|
+
|
5
|
+
v0.9.1. Had not added new files in 0.9.0 version
|
6
|
+
|
7
|
+
v0.9.0. Added pronounceable passwords
|
8
|
+
|
9
|
+
v0.1.3. Updated help
|
10
|
+
|
11
|
+
v0.1.2. Moved to rubygems.org
|
12
|
+
|
13
|
+
v0.1.1. First public release
|
data/Manifest
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
= Passgen
|
2
|
+
|
3
|
+
Ruby gem for generating passwords quickly and easily. Although it is
|
4
|
+
suitable for use within Rails it has no Rails dependencies and can be used in
|
5
|
+
non-Rails applications as well.
|
6
|
+
|
7
|
+
It can generate passwords including lower case, upper case, digits and symbols and also
|
8
|
+
pronounceable passwords.
|
9
|
+
|
10
|
+
Since 1.0.0 you can also analyze the quality of a password, both as a numeric score
|
11
|
+
between 0 and 100 and as a complexity ranking.
|
12
|
+
|
13
|
+
The algorithm used exists in a number of variations for different languages, if if anyone
|
14
|
+
knows its origin and would like to include credit to the original author, please get in
|
15
|
+
touch via GitHub.
|
16
|
+
|
17
|
+
== Install
|
18
|
+
|
19
|
+
gem install passgen
|
20
|
+
|
21
|
+
== Usage
|
22
|
+
|
23
|
+
The usage could not be easier. Just require and call the generate method:
|
24
|
+
|
25
|
+
>> require 'rubygems'
|
26
|
+
>> require 'passgen'
|
27
|
+
>> Passgen::generate
|
28
|
+
=> "zLWCeS3xC9"
|
29
|
+
|
30
|
+
You check the strength of a password by calling analyze:
|
31
|
+
|
32
|
+
>> info = Passgen::analyze("zLWCeS3xC9")
|
33
|
+
=> #<Passgen::StrengthAnalyzer:0xb728654c @complexity="Strong", @score=78, @errors=[], @password="zLWCeS3xC9">
|
34
|
+
>> info.score
|
35
|
+
=> 78
|
36
|
+
>> info.complexity
|
37
|
+
=> "Strong"
|
38
|
+
|
39
|
+
== Examples
|
40
|
+
|
41
|
+
>> Passgen::generate
|
42
|
+
=> "zLWCeS3xC9"
|
43
|
+
|
44
|
+
>> Passgen::generate(:length => 20)
|
45
|
+
=> "6lCcHvkuEW6OuzAtkoAs"
|
46
|
+
|
47
|
+
>> Passgen::generate(:symbols => true)
|
48
|
+
=> "gr)$6bIym1"
|
49
|
+
|
50
|
+
>> Passgen::generate(:lowercase => :only)
|
51
|
+
=> "ysbwuxbcea"
|
52
|
+
|
53
|
+
>> Passgen::generate(:number => 3)
|
54
|
+
=> ["REdOigTkdI", "PQu8DsV9WZ", "qptKLbw8YQ"]
|
55
|
+
|
56
|
+
>> Passgen::generate(:seed => 5)
|
57
|
+
=> "JoV9M2qjiK"
|
58
|
+
>> Passgen::generate(:seed => 5) # Will generate same password again
|
59
|
+
=> "JoV9M2qjiK"
|
60
|
+
|
61
|
+
>> Passgen::generate(:pronounceable => true) # Pronounceable, mixed case password
|
62
|
+
=> "ActeodEuRT"
|
63
|
+
>> Passgen::generate(:pronounceable => true, :lowercase => :only) # Pronounceable lower case
|
64
|
+
=> "terysolang"
|
65
|
+
>> Passgen::generate(:pronounceable => true, :uppercase => :only) # Pronounceable upper case
|
66
|
+
=> "ACTOPECHEI"
|
67
|
+
>> Passgen::generate(:pronounceable => true, :digits_before => 3) # Pad with digits in front
|
68
|
+
=> "886uRApLIN"
|
69
|
+
>> Passgen::generate(:pronounceable => true, :digits_before => 3) # Pad with digits at the end
|
70
|
+
=> "uRAPLIN886"
|
71
|
+
|
72
|
+
== Options:
|
73
|
+
|
74
|
+
=== :lowercase => true/false/:only
|
75
|
+
* true - Use lowercase letters in the generated password.
|
76
|
+
* false - Do not use lowercase letters in the generated password.
|
77
|
+
* :only - Only use lowercase letters in the generated password.
|
78
|
+
|
79
|
+
=== :uppercase => true/false/:only
|
80
|
+
* true - Use uppercase letters in the generated password.
|
81
|
+
* false - Do not use uppercase letters in the generated password.
|
82
|
+
* :only - Only use uppercase letters in the generated password.
|
83
|
+
|
84
|
+
=== :digits => true/false/:only
|
85
|
+
* true - Use digits in the generated password.
|
86
|
+
* false - Do not use digits in the generated password.
|
87
|
+
* :only - Only use digits in the generated password.
|
88
|
+
|
89
|
+
=== :symbols => true/false/:only/:list
|
90
|
+
* true - Use symbols in the generated password.
|
91
|
+
* false - Do not use symbols in the generated password.
|
92
|
+
* :only - Only use symbols in the generated password.
|
93
|
+
* :list - A string with the symbols to use. Not implemented yet.
|
94
|
+
|
95
|
+
=== :number => integer
|
96
|
+
Number of passwords to generate. If >1 the result is an Array.
|
97
|
+
|
98
|
+
=== :length => integer/range
|
99
|
+
The number of characters in the generated passwords. A range results in passwords
|
100
|
+
lengths within the given range.
|
101
|
+
|
102
|
+
=== :seed => integer/:default
|
103
|
+
Set the srand seed to the given integer prior to generating the passwords.
|
104
|
+
|
105
|
+
=== :pronounceable => true/false
|
106
|
+
* true - Generate pronounceable passwords
|
107
|
+
* false - No effect
|
108
|
+
|
109
|
+
=== :digits_after => true/number (Only in combination with :pronounceable)
|
110
|
+
* Pads the pronounceable password with number digits at the end. Defaults to 2 if true is passed.
|
111
|
+
|
112
|
+
=== :digits_before => true/number (Only in combination with :pronounceable)
|
113
|
+
* Pads the pronounceable password with number digits in front. Defaults to 2 if true is passed.
|
114
|
+
|
115
|
+
=== Default values:
|
116
|
+
|
117
|
+
:lowercase => true
|
118
|
+
|
119
|
+
:uppercase => true
|
120
|
+
|
121
|
+
:digits => true
|
122
|
+
|
123
|
+
:symbols => false
|
124
|
+
|
125
|
+
:pronounceable => Not implemented yet.
|
126
|
+
|
127
|
+
:number => 1
|
128
|
+
|
129
|
+
:length => 10
|
130
|
+
|
131
|
+
:seed => nil
|
132
|
+
|
133
|
+
:pronounceable => false
|
134
|
+
|
135
|
+
:digits_after => 2 (Only in combination with pronounceable)
|
136
|
+
|
137
|
+
:digits_before => 2 (Only in combination with pronounceable)
|
138
|
+
|
139
|
+
== Copyright and license
|
140
|
+
|
141
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
142
|
+
a copy of this software and associated documentation files (the
|
143
|
+
"Software"), to deal in the Software without restriction, including
|
144
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
145
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
146
|
+
permit persons to whom the Software is furnished to do so, subject to
|
147
|
+
the following conditions:
|
148
|
+
|
149
|
+
The above copyright notice and this permission notice shall be
|
150
|
+
included in all copies or substantial portions of the Software.
|
151
|
+
|
152
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
153
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
154
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
155
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
156
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
157
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
158
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# Rakefile
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rake'
|
4
|
+
require 'echoe'
|
5
|
+
|
6
|
+
Echoe.new('passgen', '1.0.1') do |p|
|
7
|
+
p.description = "A password generation gem for Ruby and Rails applications."
|
8
|
+
p.url = "http://github.com/cryptice/passgen"
|
9
|
+
p.author = "Erik Lindblad"
|
10
|
+
p.email = "erik@l2c.se"
|
11
|
+
p.ignore_pattern = ["tmp/*", "script/*", "nbproject/*"]
|
12
|
+
p.development_dependencies = []
|
13
|
+
end
|
14
|
+
|
15
|
+
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
|
data/lib/passgen.rb
ADDED
@@ -0,0 +1,328 @@
|
|
1
|
+
#= Passgen
|
2
|
+
#
|
3
|
+
#Ruby gem for generating passwords quickly and easily. Although it is
|
4
|
+
#suitable for use within Rails it has no Rails dependencies and can be used in
|
5
|
+
#non-Rails applications as well.
|
6
|
+
#
|
7
|
+
#== Install
|
8
|
+
#
|
9
|
+
# gem install cryptice-passgen --source http://gems.github.com
|
10
|
+
#
|
11
|
+
#== Usage
|
12
|
+
#
|
13
|
+
#The usage could not be easier. Just require and call the generate method:
|
14
|
+
#
|
15
|
+
# >> require 'rubygems'
|
16
|
+
# >> require 'passgen'
|
17
|
+
# >> Passgen::generate
|
18
|
+
# => "zLWCeS3xC9"
|
19
|
+
#
|
20
|
+
#== Examples
|
21
|
+
#
|
22
|
+
# >> Passgen::generate
|
23
|
+
# => "zLWCeS3xC9"
|
24
|
+
#
|
25
|
+
# >> Passgen::generate(:length => 20)
|
26
|
+
# => "6lCcHvkuEW6OuzAtkoAs"
|
27
|
+
#
|
28
|
+
# >> Passgen::generate(:symbols => true)
|
29
|
+
# => "gr)$6bIym1"
|
30
|
+
#
|
31
|
+
# >> Passgen::generate(:lowercase => :only)
|
32
|
+
# => "ysbwuxbcea"
|
33
|
+
#
|
34
|
+
# >> Passgen::generate(:number => 3)
|
35
|
+
# => ["REdOigTkdI", "PQu8DsV9WZ", "qptKLbw8YQ"]
|
36
|
+
#
|
37
|
+
# >> Passgen::generate(:seed => 5)
|
38
|
+
# => "JoV9M2qjiK"
|
39
|
+
# >> Passgen::generate(:seed => 5) # Will generate same password again
|
40
|
+
# => "JoV9M2qjiK"
|
41
|
+
#
|
42
|
+
# >> Passgen::generate(:seed => :default) # Will set random seed...
|
43
|
+
# => "SI8QDBdV98"
|
44
|
+
# >> Passgen::generate(:seed => :default) # and hence give different password
|
45
|
+
# => "tHHU5HLBAn"
|
46
|
+
#
|
47
|
+
#== Options:
|
48
|
+
#
|
49
|
+
#=== :lowercase => true/false/:only
|
50
|
+
#* true - Use lowercase letters in the generated password.
|
51
|
+
#* false - Do not use lowercase letters in the generated password.
|
52
|
+
#* :only - Only use lowercase letters in the generated password.
|
53
|
+
#
|
54
|
+
#=== :uppercase => true/false/:only
|
55
|
+
#* true - Use uppercase letters in the generated password.
|
56
|
+
#* false - Do not use uppercase letters in the generated password.
|
57
|
+
#* :only - Only use uppercase letters in the generated password.
|
58
|
+
#
|
59
|
+
#=== :digits => true/false/:only
|
60
|
+
#* true - Use digits in the generated password.
|
61
|
+
#* false - Do not use digits in the generated password.
|
62
|
+
#* :only - Only use digits in the generated password.
|
63
|
+
#
|
64
|
+
#=== :symbols => true/false/:only/:list
|
65
|
+
#* true - Use symbols in the generated password.
|
66
|
+
#* false - Do not use symbols in the generated password.
|
67
|
+
#* :only - Only use symbols in the generated password.
|
68
|
+
#* :list - A string with the symbols to use. Not implemented yet.
|
69
|
+
#
|
70
|
+
#=== :pronounceable => true/false
|
71
|
+
#Not implmented yet.
|
72
|
+
#
|
73
|
+
#=== :number => integer
|
74
|
+
#Number of passwords to generate. If >1 the result is an Array.
|
75
|
+
#
|
76
|
+
#=== :length => integer/range
|
77
|
+
#The number of characters in the generated passwords. A range results in passwords
|
78
|
+
#lengths within the given range.
|
79
|
+
#
|
80
|
+
#=== :seed => integer/:default
|
81
|
+
#Set the srand seed to the given integer prior to generating the passwords.
|
82
|
+
#
|
83
|
+
#=== Default values:
|
84
|
+
#
|
85
|
+
#:lowercase => true
|
86
|
+
#
|
87
|
+
#:uppercase => true
|
88
|
+
#
|
89
|
+
#:digits => true
|
90
|
+
#
|
91
|
+
#:symbols => false
|
92
|
+
#
|
93
|
+
#:pronounceable => Not implemented yet.
|
94
|
+
#
|
95
|
+
#:number => 1
|
96
|
+
#
|
97
|
+
#:length => 10
|
98
|
+
#
|
99
|
+
#:seed => nil
|
100
|
+
#
|
101
|
+
#== Copyright and license
|
102
|
+
#
|
103
|
+
#Copyright (c) 2009 Erik Lindblad
|
104
|
+
#
|
105
|
+
#Permission is hereby granted, free of charge, to any person obtaining
|
106
|
+
#a copy of this software and associated documentation files (the
|
107
|
+
#"Software"), to deal in the Software without restriction, including
|
108
|
+
#without limitation the rights to use, copy, modify, merge, publish,
|
109
|
+
#distribute, sublicense, and/or sell copies of the Software, and to
|
110
|
+
#permit persons to whom the Software is furnished to do so, subject to
|
111
|
+
#the following conditions:
|
112
|
+
#
|
113
|
+
#The above copyright notice and this permission notice shall be
|
114
|
+
#included in all copies or substantial portions of the Software.
|
115
|
+
#
|
116
|
+
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
117
|
+
#EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
118
|
+
#MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
119
|
+
#NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
120
|
+
#LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
121
|
+
#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
122
|
+
#WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
123
|
+
|
124
|
+
require 'digest'
|
125
|
+
require 'passgen/probabilities'
|
126
|
+
require 'passgen/strength_analyzer'
|
127
|
+
|
128
|
+
module Passgen
|
129
|
+
|
130
|
+
VERSION = "1.0.0"
|
131
|
+
|
132
|
+
DEFAULT_PARAMS = {
|
133
|
+
:number => 1,
|
134
|
+
:length => 10,
|
135
|
+
:lowercase => true,
|
136
|
+
:uppercase => true,
|
137
|
+
:digits => true,
|
138
|
+
:symbols => false,
|
139
|
+
:pronounceable => false
|
140
|
+
}
|
141
|
+
|
142
|
+
def self.default_seed
|
143
|
+
Digest::MD5.hexdigest("#{rand}#{Time.now}#{Process.object_id}").to_i(16)
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.generate(params={})
|
147
|
+
set_options(params)
|
148
|
+
tokens = valid_tokens
|
149
|
+
set_seed
|
150
|
+
|
151
|
+
if n == 1
|
152
|
+
generate_one(tokens)
|
153
|
+
else
|
154
|
+
Array.new(n) {|i| generate_one(tokens) }
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def self.analyze(pw)
|
159
|
+
Passgen::StrengthAnalyzer.analyze(pw)
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
def self.alphabet(index)
|
164
|
+
if use_lowercase? && !use_uppercase?
|
165
|
+
LOWERCASE_TOKENS[index]
|
166
|
+
elsif use_uppercase? && !use_lowercase?
|
167
|
+
UPPERCASE_TOKENS[index]
|
168
|
+
else
|
169
|
+
tmp = LOWERCASE_TOKENS[index]
|
170
|
+
tmp.upcase! if rand > 0.5
|
171
|
+
tmp
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def self.generate_one(tokens)
|
176
|
+
if @options[:pronounceable]
|
177
|
+
generate_pronounceable
|
178
|
+
else
|
179
|
+
Array.new(password_length) {tokens[rand(tokens.size)]}.join
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def self.generate_pronounceable
|
184
|
+
password = ""
|
185
|
+
|
186
|
+
# Append digits in front
|
187
|
+
digits_prefix = if @options[:digits_before]
|
188
|
+
@options[:length] -= @options[:digits_before]
|
189
|
+
Array.new(@options[:digits_before]) {DIGIT_TOKENS[rand(DIGIT_TOKENS.size)]}.join
|
190
|
+
else
|
191
|
+
""
|
192
|
+
end
|
193
|
+
|
194
|
+
# Append digits at the end
|
195
|
+
digits_suffix = if @options[:digits_after]
|
196
|
+
@options[:length] -= @options[:digits_after]
|
197
|
+
Array.new(@options[:digits_after]) {DIGIT_TOKENS[rand(DIGIT_TOKENS.size)]}.join
|
198
|
+
else
|
199
|
+
""
|
200
|
+
end
|
201
|
+
|
202
|
+
# Find a random starting point.
|
203
|
+
found_start = false
|
204
|
+
ranno = rand * SIGMA # random number [0,1[ weighed by sum of frequencies
|
205
|
+
sum = 0;
|
206
|
+
N_LETTERS.times do |c1|
|
207
|
+
N_LETTERS.times do |c2|
|
208
|
+
N_LETTERS.times do |c3|
|
209
|
+
sum += P[c1][c2][c3]
|
210
|
+
if sum > ranno
|
211
|
+
password << alphabet(c1)
|
212
|
+
password << alphabet(c2)
|
213
|
+
password << alphabet(c3)
|
214
|
+
found_start = true
|
215
|
+
break
|
216
|
+
end
|
217
|
+
end
|
218
|
+
break if found_start
|
219
|
+
end
|
220
|
+
break if found_start
|
221
|
+
end
|
222
|
+
|
223
|
+
# Do a random walk.
|
224
|
+
(3...@options[:length]).each do |nchar|
|
225
|
+
c1 = LETTER_INDEXES[password[nchar-2..nchar-2]]
|
226
|
+
c2 = LETTER_INDEXES[password[nchar-1..nchar-1]]
|
227
|
+
sum = 0
|
228
|
+
N_LETTERS.times {|c3| sum += P[c1][c2][c3]}
|
229
|
+
break if sum == 0
|
230
|
+
ranno = rand * sum
|
231
|
+
sum = 0;
|
232
|
+
N_LETTERS.times do |c3|
|
233
|
+
sum += P[c1][c2][c3]
|
234
|
+
if sum > ranno
|
235
|
+
password << alphabet(c3)
|
236
|
+
break
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
digits_prefix + password + digits_suffix
|
241
|
+
end
|
242
|
+
|
243
|
+
def self.password_length
|
244
|
+
if @options[:length].is_a?(Range)
|
245
|
+
tmp = @options[:length].to_a
|
246
|
+
tmp[rand(tmp.size)]
|
247
|
+
else
|
248
|
+
@options[:length].to_i
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def self.n
|
253
|
+
@options[:number]
|
254
|
+
end
|
255
|
+
|
256
|
+
def self.set_options(params)
|
257
|
+
if params[:lowercase] == :only
|
258
|
+
params[:uppercase] = false
|
259
|
+
params[:digits] = false
|
260
|
+
end
|
261
|
+
|
262
|
+
if params[:uppercase] == :only
|
263
|
+
params[:lowercase] = false
|
264
|
+
params[:digits] = false
|
265
|
+
end
|
266
|
+
|
267
|
+
if params[:digits] == :only
|
268
|
+
params[:lowercase] = false
|
269
|
+
params[:uppercase] = false
|
270
|
+
end
|
271
|
+
|
272
|
+
if params[:symbols] == :only
|
273
|
+
params[:lowercase] = false
|
274
|
+
params[:uppercase] = false
|
275
|
+
params[:digits] = false
|
276
|
+
params[:symbols] = true
|
277
|
+
end
|
278
|
+
|
279
|
+
if params[:digits_before] == true
|
280
|
+
params[:digits_before] = 2
|
281
|
+
end
|
282
|
+
|
283
|
+
if params[:digits_after] == true
|
284
|
+
params[:digits_after] = 2
|
285
|
+
end
|
286
|
+
|
287
|
+
@options = DEFAULT_PARAMS.merge(params)
|
288
|
+
end
|
289
|
+
|
290
|
+
def self.set_seed
|
291
|
+
if @options[:seed]
|
292
|
+
if @options[:seed] == :default
|
293
|
+
srand(default_seed)
|
294
|
+
else
|
295
|
+
srand(@options[:seed])
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def self.symbol_tokens
|
301
|
+
%w{! @ # $ % & / ( ) + ? *}
|
302
|
+
end
|
303
|
+
|
304
|
+
def self.use_lowercase?
|
305
|
+
@options[:lowercase]
|
306
|
+
end
|
307
|
+
|
308
|
+
def self.use_uppercase?
|
309
|
+
@options[:uppercase]
|
310
|
+
end
|
311
|
+
|
312
|
+
def self.use_digits?
|
313
|
+
@options[:digits]
|
314
|
+
end
|
315
|
+
|
316
|
+
def self.use_symbols?
|
317
|
+
@options[:symbols]
|
318
|
+
end
|
319
|
+
|
320
|
+
def self.valid_tokens
|
321
|
+
tmp = []
|
322
|
+
tmp += LOWERCASE_TOKENS if use_lowercase?
|
323
|
+
tmp += UPPERCASE_TOKENS if use_uppercase?
|
324
|
+
tmp += DIGIT_TOKENS if use_digits?
|
325
|
+
tmp += symbol_tokens if use_symbols?
|
326
|
+
tmp
|
327
|
+
end
|
328
|
+
end
|