-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathruby-entropy.rb
109 lines (89 loc) · 2.88 KB
/
ruby-entropy.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
class RubyEntropy
attr_reader :passphrase
#lowercase only
COMMON_PASSPHRASES = ["admin", "administrator", "jesus", "letmein", "master", "open sesame", "opensesame", "password", "passphrase", "sunshine", "trustnoi", "trustnol", "welcome"]
KEY_PATTERNS = ["zxc", "cxz", "bnm", "mnb", "jkl", "lkj", "asd", "dsa", "qwe", "ewq", "iop", "poi"]
def initialize(passphrase)
@passphrase = passphrase
end
def strength
(31 * bad_passphrase_multiplier * Math.log(entropy / 13.62)).round(2)
end
def self.blacklist_passphrase(phrase)
if phrase.class != String
return 'you must enter a string'
end
COMMON_PASSPHRASES.push(phrase.downcase)
return 'success'
end
private
def entropy
Math.log2(count ** length)
end
def length
@passphrase.length
end
def letters
26 if @passphrase.match(/[a-z]|[A-Z]/)
end
def multiple_cases
26 if @passphrase.match(/[a-z]/) && @passphrase.match(/[A-Z]/)
end
def digits
10 if @passphrase.match(/\d/)
end
def symbols
33 if @passphrase.match(/\W/)
end
def count
letters.to_i + multiple_cases.to_i + digits.to_i + symbols.to_i
end
def key_pattern?
KEY_PATTERNS.each { |pattern| return true if @passphrase.downcase.include?(pattern) }
false
end
def numerical_pattern?
pattern = @passphrase.split('').map(&:to_i)
pattern.each_with_index do |num, index|
return true if pattern[index + 1] == num + 1 && pattern[index + 2] == num + 2 && pattern[index + 3] == num + 3
return true if pattern[index + 1] == num - 1 && pattern[index + 2] == num - 2 && pattern[index + 3] == num - 3
end
false
end
def repetitious?
characters = @passphrase.split('')
characters.each_with_index do |character, index|
return true if characters[index + 1] == character && characters[index + 2] == character
end
false
end
def common?
@passphrases = []
@passphrases << @passphrase
if @passphrase.match(/[@0|1$5]/)
@passphrases << @passphrase.gsub('@', 'a').gsub('0', 'o').gsub(/[|1!]/, 'l').gsub(/[$5]/, 's')
@passphrases << @passphrase.gsub('@', 'a').gsub('0', 'o').gsub(/[|1!]/, 'i').gsub(/[$5]/, 's')
end
COMMON_PASSPHRASES.each do |commoner|
@passphrases.each { |passphrase| return true if passphrase.downcase.include?(commoner.downcase) }
end
false
end
def uniqueness
(@passphrase.downcase.split('').uniq.length/length.to_f) < 0.4
end
def repeaters
mode = []
@passphrase.downcase.split('').uniq.each do |character|
mode << @passphrase.split('').count(character)
end
mode.max.downto(2) do |num|
return true if (mode.count(num)/mode.length.to_f) > 0.75
end
false
end
def bad_passphrase_multiplier
repeaters || uniqueness ? (return 0.1) : 1
key_pattern? || numerical_pattern? || repetitious? || common? ? @passphrase.length < 12 ? 0.5 : 0.75 : 1
end
end