easy-password 0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 22d9fd1676c83cd88d002fbc80e9f99727e524c704027a2d4d37078c17c5234d
4
+ data.tar.gz: 325356f44444034a31212ee79b0e2e92bb7392314351df8e4b78985baa09acb9
5
+ SHA512:
6
+ metadata.gz: 67ffa6ec83d80b16bbcbddb2f649fe183b97d2753e018c35deb3a2fd27f6652af2cb191fa26282fde1c1020a97cdf5a6f995c7d1d70323ecbb9d7c8ead00b21c
7
+ data.tar.gz: 5763ce1f321402431fea4169d1bbb351adc6e33223eb1d2744970fd94aa73762b8dcd0e0b78114a939958847c80d5f8895ea775efb85d0c1f6d613102bbf8031
data/README.md ADDED
@@ -0,0 +1,52 @@
1
+ easy-password
2
+ =============
3
+ Password generator, checker, hasher
4
+
5
+
6
+ Examples
7
+ ========
8
+
9
+ Adding a simple password generator and using it by default:
10
+
11
+ ~~~ruby
12
+ # Generate 10 random alphanumeric characters
13
+ EasyPassword.generator :random10 do
14
+ SecureRandom.alphanumeric(10)
15
+ end
16
+
17
+ # Define the default generator
18
+ EasyPassword.default_generator = :random10
19
+ ~~~
20
+
21
+
22
+ Adding a checker
23
+
24
+ ~~~ruby
25
+ # Implementing classic at least 1 lowercase, 1 upercase, 1 digit
26
+ EasyPassword.checker :aA1 do |password, all|
27
+ list = { /\d/ => :digit_needed,
28
+ /[A-Z]/ => :upercase_needed,
29
+ /[a-z]/ => :lowercase_needed,
30
+ }.lazy.map {|regex, failure| failure if password !~ regex }
31
+ .reject(&:nil?)
32
+ all ? list.to_a : list.first
33
+ end
34
+
35
+ # Looking for known bad passwords in a database (using Sequel)
36
+ Password.checker :hack_dictionary do |password, all|
37
+ ! DB[:bad_passwords].first(:password => password).nil?
38
+ end
39
+ ~~~
40
+
41
+ Creating password
42
+
43
+ ~~~ruby
44
+ password = EasyPassword.new
45
+ password = EasyPassword.new('foobar')
46
+ ~~~
47
+
48
+ Checking for weakness
49
+
50
+ ~~~ruby
51
+ password.weakness
52
+ ~~~
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require_relative 'lib/easy-password/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'easy-password'
7
+ s.version = EasyPassword::VERSION
8
+ s.summary = "Password generator, checker, hasher"
9
+ s.description = <<~EOF
10
+ Ease password creation by allowing:
11
+ * password generation
12
+ * password weakness checking
13
+ * hashing password to sha256, md5, sha, ntlm, lmhash
14
+ EOF
15
+
16
+ s.homepage = 'https://gitlab.com/sdalu/easy-password'
17
+ s.license = 'MIT'
18
+
19
+ s.authors = [ "Stéphane D'Alu" ]
20
+ s.email = [ 'stephane.dalu@insa-lyon.fr' ]
21
+
22
+ s.files = %w[ README.md easy-password.gemspec ] +
23
+ Dir['lib/**/*.rb']
24
+
25
+ s.add_development_dependency 'yard', '~>0'
26
+ s.add_development_dependency 'redcarpet', '~>3'
27
+ s.add_development_dependency 'rake', '~>13'
28
+ end
@@ -0,0 +1,316 @@
1
+ require 'openssl'
2
+
3
+ #
4
+ # Adding a simple password generator and using it by default:
5
+ #
6
+ # # Generate 10 random alphanumeric characters
7
+ # EasyPassword.generator :random10 do
8
+ # SecureRandom.alphanumeric(10)
9
+ # end
10
+ #
11
+ # # Define the default generator
12
+ # EasyPassword.default_generator = :random10
13
+ #
14
+ #
15
+ # Adding a checker
16
+ #
17
+ # # Implementing classic at least 1 lowercase, 1 upercase, 1 digit
18
+ # EasyPassword.checker :aA1 do |password, all|
19
+ # list = { /\d/ => :digit_needed,
20
+ # /[A-Z]/ => :upercase_needed,
21
+ # /[a-z]/ => :lowercase_needed,
22
+ # }.lazy.map {|regex, failure| failure if password !~ regex }
23
+ # .reject(&:nil?)
24
+ # all ? list.to_a : list.first
25
+ # end
26
+ #
27
+ # # Looking for known bad passwords in a database (using Sequel)
28
+ # Password.checker :hack_dictionary do |password, all|
29
+ # ! DB[:bad_passwords].first(:password => password).nil?
30
+ # end
31
+ #
32
+ # Creating password
33
+ #
34
+ # password = EasyPassword.new
35
+ # password = EasyPassword.new('foobar')
36
+ #
37
+ # Checking for weakness
38
+ #
39
+ # password.weakness
40
+ #
41
+
42
+ class EasyPassword
43
+ Digest = OpenSSL::Digest
44
+
45
+ @hide = true
46
+ @checkers = {}
47
+ @generators = {}
48
+ @default_generator = nil
49
+ @default_checkers = nil
50
+
51
+
52
+ # Is password value hidden when calling #to_s
53
+ def self.hide
54
+ @hide
55
+ end
56
+
57
+
58
+ # Control if password value is hidden when calling #to_s
59
+ def self.hide=(hide)
60
+ @hide = hide
61
+ end
62
+
63
+
64
+ # Define the default generator to use
65
+ #
66
+ # @param [Symbol] type Generator nickname
67
+ #
68
+ def self.default_generator=(type)
69
+ @default_generator = type
70
+ end
71
+
72
+
73
+ # Default generator
74
+ def self.default_generator
75
+ @default_generator
76
+ end
77
+
78
+
79
+ # Define the list of default checkers to use
80
+ #
81
+ # @param [Array<Symbol>,nil] checkers list of checkers nickname, if nil
82
+ # all available checkers will be used
83
+ #
84
+ def self.default_checkers=(checkers)
85
+ @default_checkers = checkers
86
+ end
87
+
88
+
89
+ # List of default checkers to use
90
+ def self.default_checkers
91
+ @default_checkers
92
+ end
93
+
94
+
95
+ # DSL to add a new password generator
96
+ #
97
+ # @yieldparam [void]
98
+ # @yieldreturn [String] plain text password
99
+ #
100
+ def self.generator(name, &block)
101
+ @generators[name] = block
102
+ end
103
+
104
+
105
+ # DSL to ass a new password checker
106
+ #
107
+ # @yieldparam [String] password plain text password
108
+ # @yieldparam [Boolean] all should all weakness be listed
109
+ # @yieldreturn [false, nil, []] if no weakness have been discovered
110
+ # @yieldreturn [true, Symbol, Array<Symbol>] name of weakness
111
+ #
112
+ def self.checker(name, &block)
113
+ @checkers[name] ||= block
114
+ end
115
+
116
+
117
+ # Check for weakness
118
+ #
119
+ # @param [String, EasyPassword] password
120
+ # @param [Symbol] checkers
121
+ # @param [Boolean] all
122
+ #
123
+ # @raise [KeyError] if a requested checker is not defined
124
+ #
125
+ # @return [Hash{Symbol=>Array<Symbol>}]
126
+ #
127
+ def self.weakness(password, *checkers, all: true)
128
+ return nil if @checkers.empty?
129
+ password = password.raw if password.kind_of?(EasyPassword)
130
+
131
+ checkers = self.default_checkers if checkers.empty?
132
+ list = if checkers.nil? || checkers.empty?
133
+ then @checkers.lazy
134
+ else checkers.lazy.map {|n| [n, @checkers.fetch(n)] }
135
+ end
136
+ list = list.map {|name, checker|
137
+ case r = checker.call(password, all: all)
138
+ when Array then [ name, r ] unless r.empty?
139
+ when Symbol then [ name, [ r ] ]
140
+ when true then [ name, [ name ] ]
141
+ when nil, false
142
+ else raise ArgumentError, 'unsupported checker return value'
143
+ end
144
+ }.reject(&:nil?)
145
+
146
+ list = if all
147
+ then Hash[list.to_a]
148
+ else Hash[*list.first].transform_values {|v| v[0,1] }
149
+ end
150
+ list unless list.empty?
151
+ end
152
+
153
+
154
+ # Generate a plain text password string.
155
+ #
156
+ # @param [Symbol] type Generator nickname
157
+ # @return [String]
158
+ #
159
+ def self.generate(type = self.default_generator)
160
+ if type.nil?
161
+ raise ArgumentError, 'invalid generator type'
162
+ end
163
+
164
+ @generators[type]&.call() ||
165
+ raise("requested generator '#{type}' doesn't exist")
166
+ end
167
+
168
+
169
+ # Create a MD5-hashed password
170
+ #
171
+ # @param [String] password plain text password
172
+ #
173
+ # @return [String] hashed password
174
+ #
175
+ def self.md5(password)
176
+ "{MD5}" + [Digest::MD5.digest(password) ].pack('m0')
177
+ end
178
+
179
+
180
+ # Create a SHA-hashed password
181
+ #
182
+ # @param [String] password plain text password
183
+ #
184
+ # @return [String] hashed password
185
+ #
186
+ def self.sha(password)
187
+ "{SHA}" + [Digest::SHA1.digest(password) ].pack('m0')
188
+ end
189
+
190
+
191
+ # Create a SHA256-hashed password
192
+ #
193
+ # @param [String] password plain text password
194
+ #
195
+ # @return [String] hashed password
196
+ #
197
+ def self.sha256(password)
198
+ "{sha256}" + [Digest::SHA256.digest(password)].pack('m0')
199
+ end
200
+
201
+
202
+ # Create an NTML-hashed password
203
+ #
204
+ # @param [String] password plain text password
205
+ #
206
+ # @return [String] hashed password
207
+ #
208
+ def self.ntlm(password)
209
+ Digest::MD4.hexdigest(password.encode("utf-16le"))
210
+ end
211
+
212
+
213
+ # Create a LMHASH-hashed password
214
+ #
215
+ # @param [String] password plain text password
216
+ #
217
+ # @return [String] hashed password
218
+ #
219
+ def self.lmhash(password)
220
+ passwd = password[0..13].upcase
221
+ passwd = passwd + "\000" * (14 - passwd.length)
222
+ des = OpenSSL::Cipher::Cipher.new('des-ecb')
223
+ des.encrypt
224
+ [passwd[0..6], passwd[7..13]].collect { |key56|
225
+ keybin = key56.unpack('B*')[0].scan(/.{7}/).collect {|k|
226
+ k + (k.count('1') % 2 == 0 ? '1' : '0') }
227
+ des.key = keybin.pack('B8' * 8)
228
+ des.update('KGS!@#$%')
229
+ }.join.unpack('C*').map { |b| '%02x' % b }.join
230
+ end
231
+
232
+
233
+ # Create a new EasyPassword
234
+ def initialize(password = EasyPassword::generate)
235
+ @passwd = password.clone.freeze
236
+ end
237
+
238
+
239
+ # Get the plain text password
240
+ #
241
+ # @return [String] plain text password
242
+ def raw
243
+ @passwd
244
+ end
245
+
246
+
247
+ # Get the SHA256-hashed password
248
+ #
249
+ # @return [String] hashed password
250
+ #
251
+ def sha
252
+ self.class.sha(@passwd)
253
+ end
254
+
255
+
256
+ # Get the SHA256-hashed password
257
+ #
258
+ # @return [String] hashed password
259
+ #
260
+ def sha256
261
+ self.class.sha256(@passwd)
262
+ end
263
+
264
+
265
+ # Get the MD5-hashed password
266
+ #
267
+ # @return [String] hashed password
268
+ #
269
+ def md5
270
+ self.class.md5(@passwd)
271
+ end
272
+
273
+
274
+ # Get the NTLM-hashed password
275
+ #
276
+ # @return [String] hashed password
277
+ #
278
+ def ntlm
279
+ self.class.ntlm(@passwd)
280
+ end
281
+
282
+
283
+ # Get the LMHASH-hashed password
284
+ #
285
+ # @return [String] hashed password
286
+ #
287
+ def lmhash
288
+ self.class.lmhash(@passwd)
289
+ end
290
+
291
+
292
+ # Display password.
293
+ # The behavior is controlled by Password.hide, so either
294
+ # the plain text password will be displayed or ********
295
+ #
296
+ # @return [String]
297
+ #
298
+ def to_s
299
+ self.class.hide != true ? "********" : self.raw
300
+ end
301
+
302
+
303
+ # Check for weakness
304
+ #
305
+ # @param [Symbol] checkers
306
+ # @param [Boolean] all
307
+ #
308
+ # @raise [KeyError] if a requested checker is not defined
309
+ #
310
+ # @return [Hash{Symbol=>Array<Symbol>}]
311
+ #
312
+ def weakness(*checkers, all: true)
313
+ self.class.weakness(@passwd, *checkers, all: all)
314
+ end
315
+
316
+ end
@@ -0,0 +1,5 @@
1
+ class EasyPassword
2
+ # Version
3
+ VERSION = '0.1'
4
+ end
5
+
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: easy-password
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Stéphane D'Alu
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-05-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: yard
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: redcarpet
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13'
55
+ description: |
56
+ Ease password creation by allowing:
57
+ * password generation
58
+ * password weakness checking
59
+ * hashing password to sha256, md5, sha, ntlm, lmhash
60
+ email:
61
+ - stephane.dalu@insa-lyon.fr
62
+ executables: []
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - README.md
67
+ - easy-password.gemspec
68
+ - lib/easy-password.rb
69
+ - lib/easy-password/version.rb
70
+ homepage: https://gitlab.com/sdalu/easy-password
71
+ licenses:
72
+ - MIT
73
+ metadata: {}
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubygems_version: 3.0.8
90
+ signing_key:
91
+ specification_version: 4
92
+ summary: Password generator, checker, hasher
93
+ test_files: []