enzoic 1.1.3 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|