easy-password 0.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.
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: []