passgen 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
@@ -0,0 +1,11 @@
1
+ CHANGELOG
2
+ Manifest
3
+ README.rdoc
4
+ Rakefile
5
+ rails/init.rb
6
+ lib/passgen.rb
7
+ lib/passgen/probabilities.rb
8
+ lib/passgen/strength_analyzer.rb
9
+ passgen.gemspec
10
+ spec/passgen/strength_analyzer_spec.rb
11
+ spec/passgen_spec.rb
@@ -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.
@@ -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 }
@@ -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