enzoic 1.1.3 → 1.2.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.
- checksums.yaml +5 -5
- data/README.md +11 -1
- data/Rakefile +4 -4
- data/enzoic.gemspec +1 -1
- data/ext/argon2-wrapper/libargon2-wrapper.bundle +0 -0
- data/ext/phc-winner-argon2/argon2 +0 -0
- data/ext/phc-winner-argon2/argon2.dSYM/Contents/Resources/DWARF/argon2 +0 -0
- data/ext/phc-winner-argon2/libargon2.0.dylib +0 -0
- data/ext/phc-winner-argon2/libargon2.0.dylib.dSYM/Contents/Resources/DWARF/libargon2.0.dylib +0 -0
- data/ext/phc-winner-argon2/libargon2.a +0 -0
- data/ext/phc-winner-argon2/src/argon2.o +0 -0
- data/ext/phc-winner-argon2/src/blake2/blake2b.o +0 -0
- data/ext/phc-winner-argon2/src/core.o +0 -0
- data/ext/phc-winner-argon2/src/encoding.o +0 -0
- data/ext/phc-winner-argon2/src/ref.o +0 -0
- data/ext/phc-winner-argon2/src/thread.o +0 -0
- data/lib/enzoic/hashing.rb +148 -7
- data/lib/enzoic/password_type.rb +26 -0
- data/lib/enzoic/version.rb +1 -1
- data/lib/enzoic.rb +218 -98
- metadata +12 -13
- data/ext/phc-winner-argon2/src/opt.o +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 2af7917c4ee90414cdffeda3948ce2a0642f523f6c44fe07db64deab4f5b6e25
|
|
4
|
+
data.tar.gz: 4cdfeb61e75c47f95132e9a0c644f89a987597a48685038ac7308dba66e008ee
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 557fadc6dab20d5d76d2098466c31728ecba0a1fcd16ce9cd13a798817239bb791012691b37130b7e64dd7275728d83c9f89bdfeda10dbde0b369faf705f8ecf
|
|
7
|
+
data.tar.gz: 420af16ed93a7696969a74fbfcd13e491e23f934a9f15712fe42fc3eb777e6cdcf51751ac4ebc2bfa28b250fc7d17c3107f10989a524c4f1779811254423cc50
|
data/README.md
CHANGED
|
@@ -35,6 +35,7 @@ require 'enzoic'
|
|
|
35
35
|
enzoic = Enzoic::Enzoic.new(apiKey: YOUR_API_KEY, secret: YOUR_API_SECRET)
|
|
36
36
|
|
|
37
37
|
# Check whether a password has been compromised
|
|
38
|
+
# see https://www.enzoic.com/docs-passwords-api/ for more information
|
|
38
39
|
if enzoic.check_password("password-to-test")
|
|
39
40
|
puts("Password is compromised")
|
|
40
41
|
else
|
|
@@ -42,6 +43,7 @@ else
|
|
|
42
43
|
end
|
|
43
44
|
|
|
44
45
|
# Check whether a specific set of credentials are compromised
|
|
46
|
+
# see https://www.enzoic.com/docs-credentials-api/ for more information
|
|
45
47
|
if enzoic.check_credentials("test@enzoic.com", "password-to-test")
|
|
46
48
|
puts("Credentials are compromised")
|
|
47
49
|
else
|
|
@@ -53,7 +55,7 @@ end
|
|
|
53
55
|
# lastCheckDate is the timestamp for the last check you performed for this user.
|
|
54
56
|
# If the DateTime you provide for the last check is greater than the timestamp Enzoic has
|
|
55
57
|
# for the last breach affecting this user, the check will not be performed.
|
|
56
|
-
# This can be used to substantially increase performance.
|
|
58
|
+
# This can be used to substantially increase performance.
|
|
57
59
|
if enzoic.check_credentials("test@enzoic.com", "password-to-test", DateTime.parse("2019-07-15T19:57:43.000Z"))
|
|
58
60
|
puts("Credentials are compromised")
|
|
59
61
|
else
|
|
@@ -61,12 +63,20 @@ else
|
|
|
61
63
|
end
|
|
62
64
|
|
|
63
65
|
# get all exposures for a given user
|
|
66
|
+
# see https://www.enzoic.com/docs-exposures-api/#get-exposures for more information
|
|
64
67
|
exposures = enzoic.get_exposures_for_user("test@enzoic.com")
|
|
65
68
|
puts(exposures.count.to_s + " exposures found for test@enzoic.com")
|
|
66
69
|
|
|
67
70
|
# now get the full details for the first exposure found
|
|
71
|
+
# see https://www.enzoic.com/docs-exposures-api/#get-exposure-details for more information
|
|
68
72
|
details = enzoic.get_exposure_details(exposures.exposures[0])
|
|
69
73
|
puts("First exposure for test@enzoic.com was " + details.title)
|
|
74
|
+
|
|
75
|
+
# get all passwords for a given user - requires special approval, contact Enzoic sales
|
|
76
|
+
# see https://www.enzoic.com/docs-raw-passwords-api/ for more information
|
|
77
|
+
user_passwords = enzoic.get_passwords_for_user("eicar_0@enzoic.com")
|
|
78
|
+
puts("First password for eicar_0@enzoic.com was " + user_passwords.passwords[0].password)
|
|
79
|
+
|
|
70
80
|
```
|
|
71
81
|
|
|
72
82
|
More information in reference format can be found below.
|
data/Rakefile
CHANGED
|
@@ -20,9 +20,9 @@ Rake::ExtensionTask.new('whirlpool', gemspec) do |ext|
|
|
|
20
20
|
ext.lib_dir = 'lib/digest'
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
Rake::ExtensionTask.new('argon2-wrapper', gemspec) do |ext|
|
|
24
|
+
ext.ext_dir = 'ext/argon2-wrapper'
|
|
25
|
+
ext.lib_dir = 'lib/enzoic'
|
|
26
|
+
end
|
|
27
27
|
|
|
28
28
|
task :default => :test
|
data/enzoic.gemspec
CHANGED
|
@@ -27,7 +27,7 @@ Gem::Specification.new do |spec|
|
|
|
27
27
|
spec.add_dependency 'unix-crypt', '~> 1.3'
|
|
28
28
|
spec.add_dependency 'base64url', '~> 1.0', '>= 1.0.1'
|
|
29
29
|
|
|
30
|
-
spec.add_development_dependency "bundler", '~> 2.
|
|
30
|
+
spec.add_development_dependency "bundler", '~> 2.2.11', '>= 2.2.11'
|
|
31
31
|
spec.add_development_dependency "rake", '~> 10.4', '>= 10.4.2'
|
|
32
32
|
spec.add_development_dependency "test-unit", '~> 3.2', '>= 3.2.4'
|
|
33
33
|
spec.add_development_dependency "rake-compiler", '~> 1.0', '>= 1.0.4'
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/ext/phc-winner-argon2/libargon2.0.dylib.dSYM/Contents/Resources/DWARF/libargon2.0.dylib
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/lib/enzoic/hashing.rb
CHANGED
|
@@ -24,6 +24,14 @@ module Enzoic
|
|
|
24
24
|
return Digest::SHA1.hexdigest to_hash
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
def self.sha1_binary(to_hash)
|
|
28
|
+
return Digest::SHA1.digest(to_hash).bytes
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.sha1_binary_array(to_hash_bytes)
|
|
32
|
+
return Digest::SHA1.digest(to_hash_bytes.pack('c*')).bytes
|
|
33
|
+
end
|
|
34
|
+
|
|
27
35
|
def self.sha256(to_hash)
|
|
28
36
|
return Digest::SHA256.hexdigest to_hash
|
|
29
37
|
end
|
|
@@ -91,7 +99,7 @@ module Enzoic
|
|
|
91
99
|
|
|
92
100
|
itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
|
93
101
|
to_hash_bytes = to_hash.bytes
|
|
94
|
-
count = 2**itoa64.index(salt[3])
|
|
102
|
+
count = 2 ** itoa64.index(salt[3])
|
|
95
103
|
justsalt = salt[4..12]
|
|
96
104
|
|
|
97
105
|
hash = self.md5_binary(justsalt + to_hash)
|
|
@@ -148,13 +156,146 @@ module Enzoic
|
|
|
148
156
|
end
|
|
149
157
|
|
|
150
158
|
def self.md5crypt(to_hash, salt)
|
|
151
|
-
return UnixCrypt::MD5.build(to_hash, salt.start_with?("$1$") ? salt[3..salt.length] : salt)
|
|
159
|
+
return UnixCrypt::MD5.build(to_hash, salt.start_with?("$1$") ? salt[3..salt.length] : salt)
|
|
152
160
|
end
|
|
153
161
|
|
|
154
162
|
def self.custom_algorithm4(to_hash, salt)
|
|
155
163
|
return self.bcrypt(self.md5(to_hash), salt)
|
|
156
164
|
end
|
|
157
165
|
|
|
166
|
+
def self.custom_algorithm5(to_hash, salt)
|
|
167
|
+
return self.sha256(self.md5(to_hash + salt))
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def self.osCommerce_AEF(to_hash, salt)
|
|
171
|
+
return self.md5(salt + to_hash)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def self.desCrypt(to_hash, salt)
|
|
175
|
+
return UnixCrypt::DES.build(to_hash, salt)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def self.convertToUnsigned(val)
|
|
179
|
+
return [val].pack('L').unpack('L').first
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def self.mySQLPre4_1(to_hash)
|
|
183
|
+
nr = 1345345333
|
|
184
|
+
add = 7
|
|
185
|
+
nr2 = 0x12345671
|
|
186
|
+
|
|
187
|
+
for i in 0..to_hash.length - 1 do
|
|
188
|
+
c = to_hash[i]
|
|
189
|
+
|
|
190
|
+
if c == " " || c == "\t"
|
|
191
|
+
next
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
tmp = c.ord
|
|
195
|
+
nr = nr ^ ((((nr & 63) + add) * tmp) + (self.convertToUnsigned(nr << 8)))
|
|
196
|
+
nr2 += (self.convertToUnsigned(nr2 << 8)) ^ nr
|
|
197
|
+
add += tmp
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
result1 = nr & ((self.convertToUnsigned(1 << 31)) - 1)
|
|
201
|
+
result2 = nr2 & ((self.convertToUnsigned(1 << 31)) - 1)
|
|
202
|
+
|
|
203
|
+
return result1.to_s(16) + result2.to_s(16)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def self.mySQLPost4_1(to_hash)
|
|
207
|
+
return "*" + self.bytes_to_hex(self.sha1_binary_array(self.sha1_binary(to_hash)));
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def self.punBB(to_hash, salt)
|
|
211
|
+
return self.sha1(salt + self.sha1(to_hash))
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def self.custom_algorithm6(to_hash, salt)
|
|
215
|
+
return self.sha1(to_hash + salt)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def self.partial_md5_20(to_hash)
|
|
219
|
+
return self.md5(to_hash)[0..19]
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def self.partial_md5_29(to_hash)
|
|
223
|
+
return self.md5(to_hash)[0..28]
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def self.ave_datalife_diferior(to_hash)
|
|
227
|
+
return self.md5(self.md5(to_hash))
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def self.django_md5(to_hash, salt)
|
|
231
|
+
return "md5$" + salt + "$" + self.md5(salt + to_hash)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def self.django_sha1(to_hash, salt)
|
|
235
|
+
return "sha1$" + salt + "$" + self.sha1(salt + to_hash)
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def self.pligg_cms(to_hash, salt)
|
|
239
|
+
return salt + self.sha1(salt + to_hash)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def self.runcms_smf1_1(to_hash, salt)
|
|
243
|
+
return self.sha1(salt + to_hash)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def self.ntlm(to_hash)
|
|
247
|
+
pwd = to_hash.dup
|
|
248
|
+
pwd = pwd.dup.force_encoding('UTF-8').encode(Encoding::UTF_16LE, Encoding::UTF_8).force_encoding('UTF-8')
|
|
249
|
+
OpenSSL::Digest::MD4.hexdigest pwd
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def self.sha1dash(to_hash, salt)
|
|
253
|
+
return self.sha1("--" + salt + "--" + to_hash + "--")
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def self.sha384(to_hash)
|
|
257
|
+
return Digest::SHA384.hexdigest to_hash
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def self.custom_algorithm7(to_hash, salt)
|
|
261
|
+
derived_salt = self.sha1(salt)
|
|
262
|
+
return OpenSSL::HMAC.hexdigest("SHA256",
|
|
263
|
+
"d2e1a4c569e7018cc142e9cce755a964bd9b193d2d31f02d80bb589c959afd7e",
|
|
264
|
+
derived_salt + to_hash)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def self.custom_algorithm9(to_hash, salt)
|
|
268
|
+
result = self.sha512(to_hash + salt)
|
|
269
|
+
for i in 0..10 do
|
|
270
|
+
result = self.sha512(result)
|
|
271
|
+
end
|
|
272
|
+
return result
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def self.sha512crypt(to_hash, salt)
|
|
276
|
+
return UnixCrypt::SHA512.build(to_hash, salt.start_with?("$6$") ? salt[3..salt.length] : salt)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def self.custom_algorithm10(to_hash, salt)
|
|
280
|
+
return self.sha512(to_hash + ":" + salt)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def self.sha256crypt(to_hash, salt)
|
|
284
|
+
salt_to_use = salt
|
|
285
|
+
if salt_to_use.start_with?("$5$")
|
|
286
|
+
salt_to_use = salt_to_use[3..salt.length];
|
|
287
|
+
end
|
|
288
|
+
if salt_to_use.start_with?("rounds=")
|
|
289
|
+
salt_to_use = salt_to_use[salt_to_use.index("$") + 1..salt_to_use.length]
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
return UnixCrypt::SHA256.build(to_hash, salt_to_use)
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def self.authMeSHA256(to_hash, salt)
|
|
296
|
+
return "$SHA$" + salt + "$" + self.sha256(self.sha256(to_hash) + salt);
|
|
297
|
+
end
|
|
298
|
+
|
|
158
299
|
def self.argon2_raw(to_hash, salt)
|
|
159
300
|
time_cost = 3
|
|
160
301
|
mem_cost = 10
|
|
@@ -238,13 +379,13 @@ module Enzoic
|
|
|
238
379
|
end
|
|
239
380
|
|
|
240
381
|
def self.xor(byte_array1, byte_array2)
|
|
241
|
-
|
|
382
|
+
result = Array.new(byte_array1.length);
|
|
242
383
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
384
|
+
for i in 0..byte_array1.length - 1 do
|
|
385
|
+
result[i] = byte_array1[i] ^ byte_array2[i];
|
|
386
|
+
end
|
|
246
387
|
|
|
247
|
-
|
|
388
|
+
return result;
|
|
248
389
|
end
|
|
249
390
|
|
|
250
391
|
def self.bytes_to_hex(bytes)
|
data/lib/enzoic/password_type.rb
CHANGED
|
@@ -18,6 +18,32 @@ module Enzoic
|
|
|
18
18
|
SHA512 = 14
|
|
19
19
|
MD5Crypt = 16
|
|
20
20
|
CustomAlgorithm4 = 17
|
|
21
|
+
CustomAlgorithm5 = 18
|
|
22
|
+
OsCommerce_AEF = 19
|
|
23
|
+
DESCrypt = 20
|
|
24
|
+
MySQLPre4_1 = 21
|
|
25
|
+
MySQLPost4_1 = 22
|
|
26
|
+
PeopleSoft = 23
|
|
27
|
+
PunBB = 24
|
|
28
|
+
CustomAlgorithm6 = 25
|
|
29
|
+
PartialMD5_20 = 26
|
|
30
|
+
AVE_DataLife_Diferior = 27
|
|
31
|
+
DjangoMD5 = 28
|
|
32
|
+
DjangoSHA1 = 29
|
|
33
|
+
PartialMD5_29 = 30
|
|
34
|
+
PliggCMS = 31
|
|
35
|
+
RunCMS_SMF1_1 = 32
|
|
36
|
+
NTLM = 33
|
|
37
|
+
SHA1Dash = 34
|
|
38
|
+
SHA384 = 35
|
|
39
|
+
CustomAlgorithm7 = 36
|
|
40
|
+
CustomAlgorithm8 = 37
|
|
41
|
+
CustomAlgorithm9 = 38
|
|
42
|
+
SHA512Crypt = 39
|
|
43
|
+
CustomAlgorithm10 = 40
|
|
44
|
+
SHA256Crypt = 41
|
|
45
|
+
AuthMeSHA256 = 42
|
|
46
|
+
|
|
21
47
|
Unknown = 97
|
|
22
48
|
UnusablePassword = 98
|
|
23
49
|
None = 99
|
data/lib/enzoic/version.rb
CHANGED
data/lib/enzoic.rb
CHANGED
|
@@ -15,61 +15,72 @@ module Enzoic
|
|
|
15
15
|
# to access the Enzoic API.
|
|
16
16
|
class Enzoic
|
|
17
17
|
def initialize(options = {})
|
|
18
|
-
@apiKey = options[:apiKey] || ''
|
|
18
|
+
@apiKey = options[:apiKey] || ''
|
|
19
19
|
raise EnzoicFail, "No API key provided" if @apiKey == ''
|
|
20
|
-
@secret = options[:secret] || ''
|
|
20
|
+
@secret = options[:secret] || ''
|
|
21
21
|
raise EnzoicFail, "No Secret provided" if @secret == ''
|
|
22
22
|
@baseURL = options[:baseURL] || "https://api.enzoic.com/v1"
|
|
23
23
|
@authString = calc_auth_string(@apiKey, @secret)
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
def check_credentials(username, password,
|
|
26
|
+
def check_credentials(username, password, last_check_timestamp = Date.new(1980, 1, 1))
|
|
27
27
|
raise EnzoicFail, "API key/Secret not set" if !@authString || @authString == ''
|
|
28
28
|
|
|
29
|
-
response = make_rest_call(@baseURL + Constants::ACCOUNTS_API_PATH +
|
|
29
|
+
response = make_rest_call(@baseURL + Constants::ACCOUNTS_API_PATH +
|
|
30
|
+
"?username=" + Hashing.sha256(username.downcase),
|
|
31
|
+
"GET", nil)
|
|
30
32
|
|
|
31
|
-
if
|
|
33
|
+
if response == "404"
|
|
32
34
|
return false
|
|
33
35
|
end
|
|
34
36
|
|
|
35
37
|
account_response = JSON.parse(response)
|
|
36
38
|
|
|
37
39
|
# if lastCheckTimestamp was provided, see if we need to go any further
|
|
38
|
-
if
|
|
40
|
+
if Date.parse(account_response["lastBreachDate"]) > last_check_timestamp
|
|
39
41
|
hashes_required = account_response["passwordHashesRequired"]
|
|
40
42
|
|
|
41
43
|
bcrypt_count = 0
|
|
42
44
|
query_string = ""
|
|
45
|
+
credential_hashes = []
|
|
43
46
|
|
|
44
|
-
|
|
45
|
-
hash_spec = hashes_required[i]
|
|
46
|
-
|
|
47
|
+
hashes_required.each do |hash_spec|
|
|
47
48
|
# bcrypt gets far too expensive for good response time if there are many of them to calculate.
|
|
48
49
|
# some mostly garbage accounts have accumulated a number of them in our DB and if we happen to hit one it
|
|
49
50
|
# kills performance, so short circuit out after at most 2 BCrypt hashes
|
|
50
|
-
if
|
|
51
|
-
if
|
|
51
|
+
if hash_spec["hashType"] != PasswordType::BCrypt || bcrypt_count <= 2
|
|
52
|
+
if hash_spec["hashType"] == PasswordType::BCrypt
|
|
52
53
|
bcrypt_count = bcrypt_count + 1
|
|
53
54
|
end
|
|
54
55
|
|
|
55
|
-
if
|
|
56
|
-
credential_hash = calc_credential_hash(username, password, account_response["salt"], hash_spec)
|
|
56
|
+
if hash_spec["hashType"] != nil
|
|
57
|
+
credential_hash = calc_credential_hash(username.downcase, password, account_response["salt"], hash_spec)
|
|
58
|
+
|
|
59
|
+
if credential_hash != nil
|
|
60
|
+
credential_hashes << credential_hash
|
|
57
61
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
query_string = query_string + "?hashes=" + CGI.escape(credential_hash);
|
|
62
|
+
if query_string.length == 0
|
|
63
|
+
query_string = query_string + "?partialHashes=" + CGI.escape(credential_hash[0..6])
|
|
61
64
|
else
|
|
62
|
-
query_string = query_string + "&
|
|
65
|
+
query_string = query_string + "&partialHashes=" + CGI.escape(credential_hash[0..6])
|
|
63
66
|
end
|
|
64
67
|
end
|
|
65
68
|
end
|
|
66
69
|
end
|
|
67
70
|
end
|
|
68
71
|
|
|
69
|
-
if
|
|
72
|
+
if query_string.length > 0
|
|
70
73
|
creds_response = make_rest_call(
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
@baseURL + Constants::CREDENTIALS_API_PATH + query_string, "GET", nil)
|
|
75
|
+
|
|
76
|
+
if creds_response != "404"
|
|
77
|
+
creds_result = JSON.parse(creds_response, object_class: OpenStruct)
|
|
78
|
+
creds_result.candidateHashes.each do |candidateHash|
|
|
79
|
+
if credential_hashes.include? candidateHash
|
|
80
|
+
return true
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
73
84
|
end
|
|
74
85
|
end
|
|
75
86
|
|
|
@@ -77,21 +88,36 @@ module Enzoic
|
|
|
77
88
|
end
|
|
78
89
|
|
|
79
90
|
def check_password(password)
|
|
91
|
+
md5 = Hashing.md5(password)
|
|
92
|
+
sha1 = Hashing.sha1(password)
|
|
93
|
+
sha256 = Hashing.sha256(password)
|
|
94
|
+
|
|
80
95
|
response = make_rest_call(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
96
|
+
@baseURL + Constants::PASSWORDS_API_PATH, "POST",
|
|
97
|
+
'{' +
|
|
98
|
+
'"partialMD5":"' + md5[0..6] + '",' +
|
|
99
|
+
'"partialSHA1":"' + sha1[0..6] + '",' +
|
|
100
|
+
'"partialSHA256":"' + sha256[0..6] + '"' +
|
|
101
|
+
'}')
|
|
86
102
|
|
|
87
|
-
|
|
103
|
+
if response != "404"
|
|
104
|
+
result = JSON.parse(response, object_class: OpenStruct)
|
|
105
|
+
|
|
106
|
+
result.candidates.each do |candidate|
|
|
107
|
+
if candidate.md5 == md5 || candidate.sha1 == sha1 || candidate.sha256 == sha256
|
|
108
|
+
return true
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
return false
|
|
88
114
|
end
|
|
89
115
|
|
|
90
116
|
def get_exposures_for_user(username)
|
|
91
|
-
response = make_rest_call(@baseURL + Constants::EXPOSURES_API_PATH + "?username=" + Hashing.sha256(username),
|
|
92
|
-
|
|
117
|
+
response = make_rest_call(@baseURL + Constants::EXPOSURES_API_PATH + "?username=" + Hashing.sha256(username.downcase),
|
|
118
|
+
"GET", nil)
|
|
93
119
|
|
|
94
|
-
if
|
|
120
|
+
if response == "404"
|
|
95
121
|
# don't have this email in the DB - return empty response
|
|
96
122
|
return JSON.parse('{ "count": 0, "exposures": [] }', object_class: OpenStruct)
|
|
97
123
|
else
|
|
@@ -102,9 +128,9 @@ module Enzoic
|
|
|
102
128
|
|
|
103
129
|
def get_exposure_details(exposure_id)
|
|
104
130
|
response = make_rest_call(@baseURL + Constants::EXPOSURES_API_PATH + "?id=" + CGI.escape(exposure_id),
|
|
105
|
-
|
|
131
|
+
"GET", nil)
|
|
106
132
|
|
|
107
|
-
if
|
|
133
|
+
if response != "404"
|
|
108
134
|
# deserialize response
|
|
109
135
|
return JSON.parse(response, object_class: OpenStruct)
|
|
110
136
|
else
|
|
@@ -112,82 +138,176 @@ module Enzoic
|
|
|
112
138
|
end
|
|
113
139
|
end
|
|
114
140
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
141
|
+
def get_passwords_for_user(username)
|
|
142
|
+
response = make_rest_call(@baseURL + Constants::ACCOUNTS_API_PATH + "?username=" +
|
|
143
|
+
Hashing.sha256(username.downcase) + "&includePasswords=1",
|
|
144
|
+
"GET", nil)
|
|
145
|
+
|
|
146
|
+
if response == "404"
|
|
147
|
+
# don't have this email in the DB - return empty response
|
|
148
|
+
return JSON.parse('{ "lastBreachDate": null, "passwords": [] }', object_class: OpenStruct)
|
|
149
|
+
else
|
|
150
|
+
# deserialize response
|
|
151
|
+
return JSON.parse(response, object_class: OpenStruct)
|
|
124
152
|
end
|
|
153
|
+
end
|
|
125
154
|
|
|
126
|
-
|
|
127
|
-
password_hash = calc_password_hash(hash_spec["hashType"], password, hash_spec["salt"])
|
|
155
|
+
private
|
|
128
156
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
157
|
+
def make_rest_call(rest_url, method, body)
|
|
158
|
+
begin
|
|
159
|
+
response = RestClient::Request.execute(method: method, url: rest_url,
|
|
160
|
+
payload: body,
|
|
161
|
+
headers: { content_type: :json, accept: :json, authorization: @authString })
|
|
162
|
+
return response.body
|
|
163
|
+
rescue RestClient::NotFound
|
|
164
|
+
return "404"
|
|
134
165
|
end
|
|
166
|
+
end
|
|
135
167
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
when PasswordType::MD5
|
|
139
|
-
return Hashing.md5(password)
|
|
140
|
-
when PasswordType::SHA1
|
|
141
|
-
return Hashing.sha1(password)
|
|
142
|
-
when PasswordType::SHA256
|
|
143
|
-
return Hashing.sha256(password)
|
|
144
|
-
when PasswordType::SHA512
|
|
145
|
-
return Hashing.sha512(password)
|
|
146
|
-
when PasswordType::IPBoard_MyBB
|
|
147
|
-
if (salt != nil && salt.length > 0)
|
|
148
|
-
return Hashing.mybb(password, salt)
|
|
149
|
-
end
|
|
150
|
-
when PasswordType::VBulletinPre3_8_5
|
|
151
|
-
if (salt != nil && salt.length > 0)
|
|
152
|
-
return Hashing.vbulletin(password, salt)
|
|
153
|
-
end
|
|
154
|
-
when PasswordType::VBulletinPost3_8_5
|
|
155
|
-
if (salt != nil && salt.length > 0)
|
|
156
|
-
return Hashing.vbulletin(password, salt)
|
|
157
|
-
end
|
|
158
|
-
when PasswordType::BCrypt
|
|
159
|
-
if (salt != nil && salt.length > 0)
|
|
160
|
-
return Hashing.bcrypt(password, salt)
|
|
161
|
-
end
|
|
162
|
-
when PasswordType::CRC32
|
|
163
|
-
return Hashing.crc32(password)
|
|
164
|
-
when PasswordType::PHPBB3
|
|
165
|
-
if (salt != nil && salt.length > 0)
|
|
166
|
-
return Hashing.phpbb3(password, salt)
|
|
167
|
-
end
|
|
168
|
-
when PasswordType::CustomAlgorithm1
|
|
169
|
-
if (salt != nil && salt.length > 0)
|
|
170
|
-
return Hashing.custom_algorithm1(password, salt)
|
|
171
|
-
end
|
|
172
|
-
when PasswordType::CustomAlgorithm2
|
|
173
|
-
if (salt != nil && salt.length > 0)
|
|
174
|
-
return Hashing.custom_algorithm2(password, salt)
|
|
175
|
-
end
|
|
176
|
-
when PasswordType::MD5Crypt
|
|
177
|
-
if (salt != nil && salt.length > 0)
|
|
178
|
-
return Hashing.md5crypt(password, salt)
|
|
179
|
-
end
|
|
180
|
-
when PasswordType::CustomAlgorithm4
|
|
181
|
-
if (salt != nil && salt.length > 0)
|
|
182
|
-
return Hashing.custom_algorithm4(password, salt)
|
|
183
|
-
end
|
|
184
|
-
end
|
|
168
|
+
def calc_credential_hash(username, password, salt, hash_spec)
|
|
169
|
+
password_hash = calc_password_hash(hash_spec["hashType"], password, hash_spec["salt"])
|
|
185
170
|
|
|
171
|
+
if password_hash != nil
|
|
172
|
+
return Hashing.argon2_raw(username.downcase + "$" + password_hash, salt)
|
|
173
|
+
else
|
|
186
174
|
return nil
|
|
187
175
|
end
|
|
176
|
+
end
|
|
188
177
|
|
|
189
|
-
|
|
190
|
-
|
|
178
|
+
def calc_password_hash(password_type, password, salt)
|
|
179
|
+
case password_type
|
|
180
|
+
when PasswordType::MD5
|
|
181
|
+
return Hashing.md5(password)
|
|
182
|
+
when PasswordType::SHA1
|
|
183
|
+
return Hashing.sha1(password)
|
|
184
|
+
when PasswordType::SHA256
|
|
185
|
+
return Hashing.sha256(password)
|
|
186
|
+
when PasswordType::SHA512
|
|
187
|
+
return Hashing.sha512(password)
|
|
188
|
+
when PasswordType::IPBoard_MyBB
|
|
189
|
+
if salt != nil && salt.length > 0
|
|
190
|
+
return Hashing.mybb(password, salt)
|
|
191
|
+
end
|
|
192
|
+
when PasswordType::VBulletinPre3_8_5
|
|
193
|
+
if salt != nil && salt.length > 0
|
|
194
|
+
return Hashing.vbulletin(password, salt)
|
|
195
|
+
end
|
|
196
|
+
when PasswordType::VBulletinPost3_8_5
|
|
197
|
+
if salt != nil && salt.length > 0
|
|
198
|
+
return Hashing.vbulletin(password, salt)
|
|
199
|
+
end
|
|
200
|
+
when PasswordType::BCrypt
|
|
201
|
+
if salt != nil && salt.length > 0
|
|
202
|
+
return Hashing.bcrypt(password, salt)
|
|
203
|
+
end
|
|
204
|
+
when PasswordType::CRC32
|
|
205
|
+
return Hashing.crc32(password)
|
|
206
|
+
when PasswordType::PHPBB3
|
|
207
|
+
if salt != nil && salt.length > 0
|
|
208
|
+
return Hashing.phpbb3(password, salt)
|
|
209
|
+
end
|
|
210
|
+
when PasswordType::CustomAlgorithm1
|
|
211
|
+
if salt != nil && salt.length > 0
|
|
212
|
+
return Hashing.custom_algorithm1(password, salt)
|
|
213
|
+
end
|
|
214
|
+
when PasswordType::CustomAlgorithm2
|
|
215
|
+
if salt != nil && salt.length > 0
|
|
216
|
+
return Hashing.custom_algorithm2(password, salt)
|
|
217
|
+
end
|
|
218
|
+
when PasswordType::MD5Crypt
|
|
219
|
+
if salt != nil && salt.length > 0
|
|
220
|
+
return Hashing.md5crypt(password, salt)
|
|
221
|
+
end
|
|
222
|
+
when PasswordType::CustomAlgorithm4
|
|
223
|
+
if salt != nil && salt.length > 0
|
|
224
|
+
return Hashing.custom_algorithm4(password, salt)
|
|
225
|
+
end
|
|
226
|
+
when PasswordType::CustomAlgorithm5
|
|
227
|
+
if salt != nil && salt.length > 0
|
|
228
|
+
return Hashing.custom_algorithm5(password, salt)
|
|
229
|
+
end
|
|
230
|
+
when PasswordType::OsCommerce_AEF
|
|
231
|
+
if salt != nil && salt.length > 0
|
|
232
|
+
return Hashing.osCommerce_AEF(password, salt)
|
|
233
|
+
end
|
|
234
|
+
when PasswordType::DESCrypt
|
|
235
|
+
if salt != nil && salt.length > 0
|
|
236
|
+
return Hashing.desCrypt(password, salt)
|
|
237
|
+
end
|
|
238
|
+
when PasswordType::MySQLPre4_1
|
|
239
|
+
return Hashing.mySQLPre4_1(password)
|
|
240
|
+
when PasswordType::MySQLPost4_1
|
|
241
|
+
return Hashing.mySQLPost4_1(password)
|
|
242
|
+
when PasswordType::PunBB
|
|
243
|
+
if salt != nil && salt.length > 0
|
|
244
|
+
return Hashing.punBB(password, salt)
|
|
245
|
+
end
|
|
246
|
+
when PasswordType::CustomAlgorithm6
|
|
247
|
+
if salt != nil && salt.length > 0
|
|
248
|
+
return Hashing.custom_algorithm6(password, salt)
|
|
249
|
+
end
|
|
250
|
+
when PasswordType::PartialMD5_20
|
|
251
|
+
return Hashing.partial_md5_20(password)
|
|
252
|
+
when PasswordType::AVE_DataLife_Diferior
|
|
253
|
+
return Hashing.ave_datalife_diferior(password)
|
|
254
|
+
when PasswordType::DjangoMD5
|
|
255
|
+
if salt != nil && salt.length > 0
|
|
256
|
+
return Hashing.django_md5(password, salt)
|
|
257
|
+
end
|
|
258
|
+
when PasswordType::DjangoSHA1
|
|
259
|
+
if salt != nil && salt.length > 0
|
|
260
|
+
return Hashing.django_sha1(password, salt)
|
|
261
|
+
end
|
|
262
|
+
when PasswordType::PartialMD5_29
|
|
263
|
+
return Hashing.partial_md5_29(password)
|
|
264
|
+
when PasswordType::PliggCMS
|
|
265
|
+
if salt != nil && salt.length > 0
|
|
266
|
+
return Hashing.pligg_cms(password, salt)
|
|
267
|
+
end
|
|
268
|
+
when PasswordType::RunCMS_SMF1_1
|
|
269
|
+
if salt != nil && salt.length > 0
|
|
270
|
+
return Hashing.runcms_smf1_1(password, salt)
|
|
271
|
+
end
|
|
272
|
+
when PasswordType::NTLM
|
|
273
|
+
return Hashing.ntlm(password)
|
|
274
|
+
when PasswordType::SHA1Dash
|
|
275
|
+
if salt != nil && salt.length > 0
|
|
276
|
+
return Hashing.sha1dash(password, salt)
|
|
277
|
+
end
|
|
278
|
+
when PasswordType::SHA384
|
|
279
|
+
return Hashing.sha384(password)
|
|
280
|
+
when PasswordType::CustomAlgorithm7
|
|
281
|
+
if salt != nil && salt.length > 0
|
|
282
|
+
return Hashing.custom_algorithm7(password, salt)
|
|
283
|
+
end
|
|
284
|
+
when PasswordType::CustomAlgorithm9
|
|
285
|
+
if salt != nil && salt.length > 0
|
|
286
|
+
return Hashing.custom_algorithm9(password, salt)
|
|
287
|
+
end
|
|
288
|
+
when PasswordType::SHA512Crypt
|
|
289
|
+
if salt != nil && salt.length > 0
|
|
290
|
+
return Hashing.sha512crypt(password, salt)
|
|
291
|
+
end
|
|
292
|
+
when PasswordType::CustomAlgorithm10
|
|
293
|
+
if salt != nil && salt.length > 0
|
|
294
|
+
return Hashing.custom_algorithm10(password, salt)
|
|
295
|
+
end
|
|
296
|
+
when PasswordType::SHA256Crypt
|
|
297
|
+
if salt != nil && salt.length > 0
|
|
298
|
+
return Hashing.sha256crypt(password, salt)
|
|
299
|
+
end
|
|
300
|
+
when PasswordType::AuthMeSHA256
|
|
301
|
+
if salt != nil && salt.length > 0
|
|
302
|
+
return Hashing.authMeSHA256(password, salt)
|
|
303
|
+
end
|
|
304
|
+
else
|
|
305
|
+
return nil
|
|
191
306
|
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def calc_auth_string(api_key, secret)
|
|
310
|
+
return "basic " + Base64.strict_encode64(api_key + ":" + secret)
|
|
311
|
+
end
|
|
192
312
|
end
|
|
193
313
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: enzoic
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Enzoic
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2022-10-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ffi
|
|
@@ -116,22 +116,22 @@ dependencies:
|
|
|
116
116
|
name: bundler
|
|
117
117
|
requirement: !ruby/object:Gem::Requirement
|
|
118
118
|
requirements:
|
|
119
|
-
- - "~>"
|
|
120
|
-
- !ruby/object:Gem::Version
|
|
121
|
-
version: 2.0.2
|
|
122
119
|
- - ">="
|
|
123
120
|
- !ruby/object:Gem::Version
|
|
124
|
-
version: 2.
|
|
121
|
+
version: 2.2.11
|
|
122
|
+
- - "~>"
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: 2.2.11
|
|
125
125
|
type: :development
|
|
126
126
|
prerelease: false
|
|
127
127
|
version_requirements: !ruby/object:Gem::Requirement
|
|
128
128
|
requirements:
|
|
129
|
-
- - "~>"
|
|
130
|
-
- !ruby/object:Gem::Version
|
|
131
|
-
version: 2.0.2
|
|
132
129
|
- - ">="
|
|
133
130
|
- !ruby/object:Gem::Version
|
|
134
|
-
version: 2.
|
|
131
|
+
version: 2.2.11
|
|
132
|
+
- - "~>"
|
|
133
|
+
- !ruby/object:Gem::Version
|
|
134
|
+
version: 2.2.11
|
|
135
135
|
- !ruby/object:Gem::Dependency
|
|
136
136
|
name: rake
|
|
137
137
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -307,8 +307,8 @@ files:
|
|
|
307
307
|
- ext/phc-winner-argon2/src/genkat.c
|
|
308
308
|
- ext/phc-winner-argon2/src/genkat.h
|
|
309
309
|
- ext/phc-winner-argon2/src/opt.c
|
|
310
|
-
- ext/phc-winner-argon2/src/opt.o
|
|
311
310
|
- ext/phc-winner-argon2/src/ref.c
|
|
311
|
+
- ext/phc-winner-argon2/src/ref.o
|
|
312
312
|
- ext/phc-winner-argon2/src/run.c
|
|
313
313
|
- ext/phc-winner-argon2/src/test.c
|
|
314
314
|
- ext/phc-winner-argon2/src/thread.c
|
|
@@ -361,8 +361,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
361
361
|
- !ruby/object:Gem::Version
|
|
362
362
|
version: '0'
|
|
363
363
|
requirements: []
|
|
364
|
-
|
|
365
|
-
rubygems_version: 2.5.2.3
|
|
364
|
+
rubygems_version: 3.0.3.1
|
|
366
365
|
signing_key:
|
|
367
366
|
specification_version: 4
|
|
368
367
|
summary: Ruby library for Enzoic API
|
|
Binary file
|