gen-password 0.16.0

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.
data/README ADDED
@@ -0,0 +1,60 @@
1
+ WHAT IS IT?
2
+ -----------
3
+
4
+ Ruby/Password is a suite of password handling methods for Ruby. It supports
5
+ the manual entry of passwords from the keyboard in both buffered and
6
+ unbuffered modes, password strength checking, random password generation,
7
+ phonemic password generation (for easy memorisation by human-beings) and the
8
+ encryption of passwords.
9
+
10
+ The common CrackLib library is used to perform password strength checking.
11
+
12
+ From the CrackLib README:
13
+
14
+ CrackLib makes literally hundreds of tests to determine whether you've
15
+ chosen a bad password.
16
+
17
+ * It tries to generate words from your username and GECOS entry and tries
18
+ to match them against the password you've chosen.
19
+
20
+ * It checks for simplistic patterns.
21
+
22
+ * It then tries to reverse-engineer your password into a dictionary
23
+ word, and searches for it in your dictionary.
24
+
25
+ - after all that, it's PROBABLY a safe(-ish) password. 8-)
26
+
27
+
28
+ The target audience for this library is system administrators who need
29
+ to write Ruby programs that prompt for, generate, verify and encrypt
30
+ passwords.
31
+
32
+
33
+ THIS FORK
34
+ ---------
35
+
36
+ This is a fork from the original software written by Ian Macdonald. I've (Albert Lash of Savonix / Docunext) forked it to try and package it as a Ruby 1.9.1 gem
37
+ for my own convenience. I made the version 0.15.4 to try and differentiate from Ian's tree.
38
+
39
+ INSTALLATION
40
+ ------------
41
+
42
+ Please see the INSTALL file for details of how to install Ruby/Password.
43
+
44
+
45
+ USAGE
46
+ -----
47
+
48
+ Please see the RDoc documentation for details of how to use Ruby/Password.
49
+
50
+
51
+ LICENCE
52
+ -------
53
+
54
+ This program is free software; you can redistribute it and/or modify
55
+ it under the terms of the GNU General Public License as published by
56
+ the Free Software Foundation; either version 2, or (at your option)
57
+ any later version.
58
+
59
+ Please see the file COPYING for the terms of the licence.
60
+
@@ -0,0 +1,23 @@
1
+ require 'rake'
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |gem|
6
+ gem.name = "gen-password"
7
+ gem.summary = %Q{A password handling library for Ruby}
8
+ gem.description = %Q{Ruby/GenPassword is a suite of password handling methods for Ruby. It supports
9
+ the manual entry of passwords from the keyboard in both buffered and
10
+ unbuffered modes, random password generation,
11
+ phonemic password generation (for easy memorisation by human-beings) and the
12
+ encryption of passwords. It is a fork of Ruby/Password without password checking using cracklib}
13
+ gem.email = "itsakshaymankar@gmail.com"
14
+ gem.homepage = "http://www.docunext.com/"
15
+ gem.authors = ["Albert Lash", "Ian Macdonald", "Akshay Mankar", "Vini Gupta"]
16
+ gem.add_dependency "ruby-termios"
17
+ gem.add_development_dependency "shoulda"
18
+ end
19
+ Jeweler::GemcutterTasks.new
20
+ rescue LoadError
21
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
22
+ end
23
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.16.0
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/ruby1.9.1
2
+ require 'password'
3
+
4
+ # This example should work with Ruby 1.9.1
5
+
6
+ ctpw = ARGV[0]
7
+
8
+ #ctpw = `xxd -l 3 -p /dev/random`
9
+
10
+ puts ctpw
11
+
12
+ cleartext = ctpw
13
+
14
+ password = Password.new(cleartext)
15
+ crypted = password.crypt()
16
+
17
+ puts crypted
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/ruby -w
2
+ #
3
+ # $Id: example.rb,v 1.7 2004/04/07 09:49:06 ianmacd Exp $
4
+ #
5
+ # Copyright (C) 2002-2004 Ian Macdonald
6
+ #
7
+ # This program is free software; you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation; either version 2, or (at your option)
10
+ # any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program; if not, write to the Free Software Foundation,
19
+ # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
+
21
+ require 'password'
22
+
23
+ def handle_password( pw )
24
+ pw.check
25
+ puts pw.crypt( `uname` == "Linux\n" ? Password::MD5 : Password::DES )
26
+ end
27
+
28
+ begin
29
+ my_string = Password.get( "Password with get: " )
30
+ handle_password( my_string )
31
+ rescue Password::WeakPassword => reason
32
+ puts reason
33
+ retry
34
+ end
35
+
36
+ begin
37
+ my_string = Password.getc( "Password with getc: ", 'X' )
38
+ handle_password( my_string )
39
+ rescue Password::WeakPassword => reason
40
+ puts reason
41
+ retry
42
+ end
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/ruby1.9.1
2
+ require 'password'
3
+
4
+ # This example should work with 1.8 and 1.9
5
+ ctpw = ARGV[0]
6
+
7
+ #ctpw = `xxd -l 3 -p /dev/random`
8
+
9
+ unless ctpw.nil?
10
+ puts ctpw
11
+ password = Password.new(ctpw)
12
+ crypted = password.crypt(Password::MD5)
13
+ puts crypted
14
+ else
15
+ puts "No password was provided."
16
+ end
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/ruby -w
2
+ #
3
+ # $Id: pwgen,v 1.3 2004/09/04 22:20:27 ianmacd Exp $
4
+ #
5
+ # Copyright (C) 2004 Ian Macdonald
6
+ #
7
+ # This program is free software; you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation; either version 2, or (at your option)
10
+ # any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program; if not, write to the Free Software Foundation,
19
+ # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
+
21
+
22
+ require 'optparse'
23
+ require 'ostruct'
24
+ require 'password'
25
+
26
+
27
+ class Optparse
28
+
29
+ USAGE_BANNER = "Usage: pwgen [ OPTIONS ] [ password_length ] [ number_passwords ]"
30
+
31
+ def self.parse(args)
32
+ options = OpenStruct.new
33
+ options.columns = $stdout.tty?
34
+ options.one_case = $stdout.tty? ? Password::ONE_CASE : 0
35
+ options.one_digit = $stdout.tty? ? Password::ONE_DIGIT : 0
36
+ options.secure = false
37
+
38
+ opts = OptionParser.new do |opts|
39
+ opts.banner = USAGE_BANNER
40
+
41
+ opts.on( "-C", "Print the generated passwords in columns") do
42
+ options.columns = true
43
+ end
44
+
45
+ opts.on( "-1", "Don't print the generated passwords",
46
+ " in columns") do
47
+ options.columns = false
48
+ end
49
+
50
+ opts.on( "-c", "--[no-]capitalise", "--[no-]capitalize",
51
+ "Include at least one capital letter in",
52
+ " the password" ) do |opt|
53
+ options.one_case = opt ? Password::ONE_CASE : 0
54
+ end
55
+
56
+ opts.on( "-n", "--[no-]numerals",
57
+ "Include at least one digit in the password" ) do |opt|
58
+ options.one_digit = opt ? Password::ONE_DIGIT : 0
59
+ end
60
+
61
+ opts.on( "-s", "--secure", "Generate completely random passwords" ) do
62
+ options.secure = true
63
+ end
64
+
65
+ opts.on( "-v", "--version", "Display version and exit" ) do
66
+ puts PWGEN_VERSION
67
+ exit
68
+ end
69
+
70
+ opts.on_tail( "-h", "--help", "Display this usage message and exit" ) do
71
+ puts opts
72
+ exit
73
+ end
74
+
75
+ end
76
+
77
+ opts.parse!(args)
78
+ options
79
+ end
80
+ end
81
+
82
+ PWGEN_VERSION = '0.5.2'
83
+ TERM_WIDTH = 80
84
+
85
+ options = Optparse.parse(ARGV)
86
+
87
+ unless [ 0, 2 ].include? ARGV.size
88
+ puts Optparse::USAGE_BANNER
89
+ exit 1
90
+ end
91
+
92
+ length, number = *ARGV.map { |arg| arg.to_i }
93
+ length ||= 8
94
+
95
+ generator = if length < 5 || options.secure
96
+ Proc.new { Password.random( length ) }
97
+ else
98
+ Proc.new { Password.phonemic( length, options.one_case |
99
+ options.one_digit ) }
100
+ end
101
+
102
+ columns = options.columns ? TERM_WIDTH / ( length + 1 ) : 1
103
+ columns = 1 if columns == 0
104
+ number ||= options.columns ? columns * 20 : 1
105
+
106
+ need_new_line = false
107
+
108
+ 0.upto number - 1 do |n|
109
+ if ! options.columns || n % columns == columns - 1
110
+ puts generator.call
111
+ need_new_line = false
112
+ else
113
+ print generator.call, ' '
114
+ need_new_line = true
115
+ end
116
+ end
117
+
118
+ puts if need_new_line
@@ -0,0 +1,54 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+ # stub: gen-password 0.16.0 ruby lib
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "gen-password"
9
+ s.version = "0.16.0"
10
+
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
+ s.authors = ["Albert Lash", "Ian Macdonald", "Akshay Mankar", "Vini Gupta"]
13
+ s.date = "2014-01-13"
14
+ s.description = "Ruby/GenPassword is a suite of password handling methods for Ruby. It supports\nthe manual entry of passwords from the keyboard in both buffered and\nunbuffered modes, random password generation,\nphonemic password generation (for easy memorisation by human-beings) and the\nencryption of passwords. It is a fork of Ruby/Password without password checking using cracklib"
15
+ s.email = "itsakshaymankar@gmail.com"
16
+ s.extra_rdoc_files = [
17
+ "README"
18
+ ]
19
+ s.files = [
20
+ "CHANGES",
21
+ "COPYING",
22
+ "Changelog",
23
+ "README",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "example/crypt.rb",
27
+ "example/example.rb",
28
+ "example/pw.rb",
29
+ "example/pwgen",
30
+ "gen-password.gemspec",
31
+ "lib/password.rb",
32
+ "test/tc_password.rb"
33
+ ]
34
+ s.homepage = "http://www.docunext.com/"
35
+ s.require_paths = ["lib"]
36
+ s.rubygems_version = "2.1.11"
37
+ s.summary = "A password handling library for Ruby"
38
+
39
+ if s.respond_to? :specification_version then
40
+ s.specification_version = 4
41
+
42
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
43
+ s.add_runtime_dependency(%q<ruby-termios>, [">= 0"])
44
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
45
+ else
46
+ s.add_dependency(%q<ruby-termios>, [">= 0"])
47
+ s.add_dependency(%q<shoulda>, [">= 0"])
48
+ end
49
+ else
50
+ s.add_dependency(%q<ruby-termios>, [">= 0"])
51
+ s.add_dependency(%q<shoulda>, [">= 0"])
52
+ end
53
+ end
54
+
@@ -0,0 +1,431 @@
1
+ # $Id: password.rb,v 1.24 2006/03/02 19:42:33 ianmacd Exp $
2
+ #
3
+ # Version : 0.5.3
4
+ # Author : Ian Macdonald <ian@caliban.org>
5
+ #
6
+ # Copyright (C) 2002-2006 Ian Macdonald
7
+ #
8
+ # This program is free software; you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation; either version 2, or (at your option)
11
+ # any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program; if not, write to the Free Software Foundation,
20
+ # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21
+
22
+
23
+ require 'crack'
24
+ require 'termios'
25
+
26
+
27
+ # Ruby/Password is a collection of password handling routines for Ruby,
28
+ # including an interface to CrackLib for the purposes of testing password
29
+ # strength.
30
+ #
31
+ # require 'password'
32
+ #
33
+ # # Define and check a password in code
34
+ # pw = Password.new( "bigblackcat" )
35
+ # pw.check
36
+ #
37
+ # # Get and check a password from the keyboard
38
+ # begin
39
+ # password = Password.get( "New password: " )
40
+ # password.check
41
+ # rescue Password::WeakPassword => reason
42
+ # puts reason
43
+ # retry
44
+ # end
45
+ #
46
+ # # Automatically generate and encrypt a password
47
+ # password = Password.phonemic( 12, Password:ONE_CASE | Password::ONE_DIGIT )
48
+ # crypted = password.crypt
49
+ #
50
+ #
51
+ class Password < String
52
+
53
+ # This exception class is raised if an error occurs during password
54
+ # encryption when calling Password#crypt.
55
+ #
56
+ class CryptError < StandardError; end
57
+
58
+ # This exception class is raised if a bad dictionary path is detected by
59
+ # Password#check.
60
+ #
61
+ class DictionaryError < StandardError; end
62
+
63
+ # This exception class is raised if a weak password is detected by
64
+ # Password#check.
65
+ #
66
+ class WeakPassword < StandardError; end
67
+
68
+ VERSION = '0.5.3'
69
+
70
+ # DES algorithm
71
+ #
72
+ DES = true
73
+
74
+ # MD5 algorithm (see <em>crypt(3)</em> for more information)
75
+ #
76
+ MD5 = false
77
+
78
+ # This flag is used in conjunction with Password.phonemic and states that a
79
+ # password must include a digit.
80
+ #
81
+ ONE_DIGIT = 1
82
+
83
+ # This flag is used in conjunction with Password.phonemic and states that a
84
+ # password must include a capital letter.
85
+ #
86
+ ONE_CASE = 1 << 1
87
+
88
+ # Characters that may appear in generated passwords. Password.urandom may
89
+ # also use the characters + and /.
90
+ #
91
+ PASSWD_CHARS = '0123456789' +
92
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
93
+ 'abcdefghijklmnopqrstuvwxyz'
94
+
95
+ # Valid salt characters for use by Password#crypt.
96
+ #
97
+ SALT_CHARS = '0123456789' +
98
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
99
+ 'abcdefghijklmnopqrstuvwxyz' +
100
+ './'
101
+
102
+ # :stopdoc:
103
+
104
+ # phoneme flags
105
+ #
106
+ CONSONANT = 1
107
+ VOWEL = 1 << 1
108
+ DIPHTHONG = 1 << 2
109
+ NOT_FIRST = 1 << 3 # indicates that a given phoneme may not occur first
110
+
111
+ PHONEMES = {
112
+ :a => VOWEL,
113
+ :ae => VOWEL | DIPHTHONG,
114
+ :ah => VOWEL | DIPHTHONG,
115
+ :ai => VOWEL | DIPHTHONG,
116
+ :b => CONSONANT,
117
+ :c => CONSONANT,
118
+ :ch => CONSONANT | DIPHTHONG,
119
+ :d => CONSONANT,
120
+ :e => VOWEL,
121
+ :ee => VOWEL | DIPHTHONG,
122
+ :ei => VOWEL | DIPHTHONG,
123
+ :f => CONSONANT,
124
+ :g => CONSONANT,
125
+ :gh => CONSONANT | DIPHTHONG | NOT_FIRST,
126
+ :h => CONSONANT,
127
+ :i => VOWEL,
128
+ :ie => VOWEL | DIPHTHONG,
129
+ :j => CONSONANT,
130
+ :k => CONSONANT,
131
+ :l => CONSONANT,
132
+ :m => CONSONANT,
133
+ :n => CONSONANT,
134
+ :ng => CONSONANT | DIPHTHONG | NOT_FIRST,
135
+ :o => VOWEL,
136
+ :oh => VOWEL | DIPHTHONG,
137
+ :oo => VOWEL | DIPHTHONG,
138
+ :p => CONSONANT,
139
+ :ph => CONSONANT | DIPHTHONG,
140
+ :qu => CONSONANT | DIPHTHONG,
141
+ :r => CONSONANT,
142
+ :s => CONSONANT,
143
+ :sh => CONSONANT | DIPHTHONG,
144
+ :t => CONSONANT,
145
+ :th => CONSONANT | DIPHTHONG,
146
+ :u => VOWEL,
147
+ :v => CONSONANT,
148
+ :w => CONSONANT,
149
+ :x => CONSONANT,
150
+ :y => CONSONANT,
151
+ :z => CONSONANT
152
+ }
153
+
154
+ # :startdoc:
155
+
156
+
157
+ # Turn local terminal echo on or off. This method is used for securing the
158
+ # display, so that a soon to be entered password will not be echoed to the
159
+ # screen. It is also used for restoring the display afterwards.
160
+ #
161
+ # If _masked_ is +true+, the keyboard is put into unbuffered mode, allowing
162
+ # the retrieval of characters one at a time. _masked_ has no effect when
163
+ # _on_ is +false+. You are unlikely to need this method in the course of
164
+ # normal operations.
165
+ #
166
+ def Password.echo(on=true, masked=false)
167
+ term = Termios::getattr( $stdin )
168
+
169
+ if on
170
+ term.c_lflag |= ( Termios::ECHO | Termios::ICANON )
171
+ else # off
172
+ term.c_lflag &= ~Termios::ECHO
173
+ term.c_lflag &= ~Termios::ICANON if masked
174
+ end
175
+
176
+ Termios::setattr( $stdin, Termios::TCSANOW, term )
177
+ end
178
+
179
+
180
+ # Get a password from _STDIN_, using buffered line input and displaying
181
+ # _message_ as the prompt. No output will appear while the password is being
182
+ # typed. Hitting <b>[Enter]</b> completes password entry. If _STDIN_ is not
183
+ # connected to a tty, no prompt will be displayed.
184
+ #
185
+ def Password.get(message="Password: ")
186
+ begin
187
+ if $stdin.tty?
188
+ Password.echo false
189
+ print message if message
190
+ end
191
+
192
+ pw = Password.new( $stdin.gets || "" )
193
+ pw.chomp!
194
+
195
+ ensure
196
+ if $stdin.tty?
197
+ Password.echo true
198
+ print "\n"
199
+ end
200
+ end
201
+ end
202
+
203
+
204
+ # Get a password from _STDIN_ in unbuffered mode, i.e. one key at a time.
205
+ # _message_ will be displayed as the prompt and each key press with echo
206
+ # _mask_ to the terminal. There is no need to hit <b>[Enter]</b> at the end.
207
+ #
208
+ def Password.getc(message="Password: ", mask='*')
209
+ # Save current buffering mode
210
+ buffering = $stdout.sync
211
+
212
+ # Turn off buffering
213
+ $stdout.sync = true
214
+
215
+ begin
216
+ Password.echo(false, true)
217
+ print message if message
218
+ pw = ""
219
+
220
+ while ( char = $stdin.getc ) != 10 # break after [Enter]
221
+ putc mask
222
+ pw << char
223
+ end
224
+
225
+ ensure
226
+ Password.echo true
227
+ print "\n"
228
+ end
229
+
230
+ # Restore original buffering mode
231
+ $stdout.sync = buffering
232
+
233
+ Password.new( pw )
234
+ end
235
+
236
+
237
+ # :stopdoc:
238
+
239
+ # Determine whether next character should be a vowel or consonant.
240
+ #
241
+ def Password.get_vowel_or_consonant
242
+ rand( 2 ) == 1 ? VOWEL : CONSONANT
243
+ end
244
+
245
+ # :startdoc:
246
+
247
+
248
+ # Generate a memorable password of _length_ characters, using phonemes that
249
+ # a human-being can easily remember. _flags_ is one or more of
250
+ # <em>Password::ONE_DIGIT</em> and <em>Password::ONE_CASE</em>, logically
251
+ # OR'ed together. For example:
252
+ #
253
+ # pw = Password.phonemic( 8, Password::ONE_DIGIT | Password::ONE_CASE )
254
+ #
255
+ # This would generate an eight character password, containing a digit and an
256
+ # upper-case letter, such as <b>Ug2shoth</b>.
257
+ #
258
+ # This method was inspired by the
259
+ # pwgen[http://sourceforge.net/projects/pwgen/] tool, written by Theodore
260
+ # Ts'o.
261
+ #
262
+ # Generated passwords may contain any of the characters in
263
+ # <em>Password::PASSWD_CHARS</em>.
264
+ #
265
+ def Password.phonemic(length=8, flags=nil)
266
+
267
+ pw = nil
268
+ ph_flags = flags
269
+
270
+ loop do
271
+
272
+ pw = ""
273
+
274
+ # Separate the flags integer into an array of individual flags
275
+ feature_flags = [ flags & ONE_DIGIT, flags & ONE_CASE ]
276
+
277
+ prev = []
278
+ first = true
279
+ desired = Password.get_vowel_or_consonant
280
+
281
+ # Get an Array of all of the phonemes
282
+ phonemes = PHONEMES.keys.map { |ph| ph.to_s }
283
+ nr_phonemes = phonemes.size
284
+
285
+ while pw.length < length do
286
+
287
+ # Get a random phoneme and its length
288
+ phoneme = phonemes[ rand( nr_phonemes ) ]
289
+ ph_len = phoneme.length
290
+
291
+ # Get its flags as an Array
292
+ ph_flags = PHONEMES[ phoneme.to_sym ]
293
+ ph_flags = [ ph_flags & CONSONANT, ph_flags & VOWEL,
294
+ ph_flags & DIPHTHONG, ph_flags & NOT_FIRST ]
295
+
296
+ # Filter on the basic type of the next phoneme
297
+ next if ph_flags.include? desired
298
+
299
+ # Handle the NOT_FIRST flag
300
+ next if first and ph_flags.include? NOT_FIRST
301
+
302
+ # Don't allow a VOWEL followed a vowel/diphthong pair
303
+ next if prev.include? VOWEL and ph_flags.include? VOWEL and
304
+ ph_flags.include? DIPHTHONG
305
+
306
+ # Don't allow us to go longer than the desired length
307
+ next if ph_len > length - pw.length
308
+
309
+ # We've found a phoneme that meets our criteria
310
+ pw << phoneme
311
+
312
+ # Handle ONE_CASE
313
+ if feature_flags.include? ONE_CASE
314
+
315
+ if (first or ph_flags.include? CONSONANT) and rand( 10 ) < 3
316
+ pw[-ph_len, 1] = pw[-ph_len, 1].upcase
317
+ feature_flags.delete ONE_CASE
318
+ end
319
+
320
+ end
321
+
322
+ # Is password already long enough?
323
+ break if pw.length >= length
324
+
325
+ # Handle ONE_DIGIT
326
+ if feature_flags.include? ONE_DIGIT
327
+ if ! first and rand( 10 ) < 3
328
+ pw << ( rand( 10 ) + ?0.ord ).chr
329
+ feature_flags.delete ONE_DIGIT
330
+
331
+ first = true
332
+ prev = []
333
+ desired = Password.get_vowel_or_consonant
334
+ next
335
+ end
336
+
337
+ end
338
+
339
+ if desired == CONSONANT
340
+ desired = VOWEL
341
+ elsif prev.include? VOWEL or ph_flags.include? DIPHTHONG or
342
+ rand(10) > 3
343
+ desired = CONSONANT
344
+ else
345
+ desired = VOWEL
346
+ end
347
+
348
+ prev = ph_flags
349
+ first = false
350
+ end
351
+
352
+ # Try again
353
+ break unless feature_flags.include? ONE_CASE or
354
+ feature_flags.include? ONE_DIGIT
355
+
356
+ end
357
+
358
+ Password.new( pw )
359
+
360
+ end
361
+
362
+
363
+ # Generate a random password of _length_ characters. Unlike the
364
+ # Password.phonemic method, no attempt will be made to generate a memorable
365
+ # password. Generated passwords may contain any of the characters in
366
+ # <em>Password::PASSWD_CHARS</em>.
367
+ #
368
+ #
369
+ def Password.random(length=8)
370
+ pw = ""
371
+ nr_chars = PASSWD_CHARS.size
372
+
373
+ length.times { pw << PASSWD_CHARS[ rand( nr_chars ) ] }
374
+
375
+ Password.new( pw )
376
+ end
377
+
378
+
379
+ # An alternative to Password.random. It uses the <tt>/dev/urandom</tt>
380
+ # device to generate passwords, returning +nil+ on systems that do not
381
+ # implement the device. The passwords it generates may contain any of the
382
+ # characters in <em>Password::PASSWD_CHARS</em>, plus the additional
383
+ # characters + and /.
384
+ #
385
+ def Password.urandom(length=8)
386
+ return nil unless File.chardev? '/dev/urandom'
387
+
388
+ rand_data = nil
389
+ File.open( "/dev/urandom" ) { |f| rand_data = f.read( length ) }
390
+
391
+ # Base64 encode it
392
+ Password.new( [ rand_data ].pack( 'm' )[ 0 .. length - 1 ] )
393
+ end
394
+
395
+
396
+ # Encrypt a password using _type_ encryption. _salt_, if supplied, will be
397
+ # used to perturb the encryption algorithm and should be chosen from the
398
+ # <em>Password::SALT_CHARS</em>. If no salt is given, a randomly generated
399
+ # salt will be used.
400
+ #
401
+ def crypt(type=DES, salt='')
402
+
403
+ unless ( salt.split( // ) - SALT_CHARS.split( // ) ).empty?
404
+ raise CryptError, 'bad salt'
405
+ end
406
+
407
+ salt = Password.random( type ? 2 : 8 ) if salt.empty?
408
+
409
+ # (Linux glibc2 interprets a salt prefix of '$1$' as a call to use MD5
410
+ # instead of DES when calling crypt(3))
411
+ salt = '$1$' + salt if type == MD5
412
+
413
+ # Pass to crypt in class String (our parent class)
414
+ crypt = super( salt )
415
+
416
+ # Raise an exception if MD5 was wanted, but result is not recognisable
417
+ if type == MD5 && crypt !~ /^\$1\$/
418
+ raise CryptError, 'MD5 not implemented'
419
+ end
420
+
421
+ crypt
422
+ end
423
+
424
+ end
425
+
426
+
427
+ # Display a phonemic password, if run directly.
428
+ #
429
+ if $0 == __FILE__
430
+ puts Password.phonemic
431
+ end