easy-password 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +52 -0
- data/easy-password.gemspec +28 -0
- data/lib/easy-password.rb +316 -0
- data/lib/easy-password/version.rb +5 -0
- metadata +93 -0
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
|
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: []
|