Module: Philiprehberger::Password
- Defined in:
- lib/philiprehberger/password.rb,
lib/philiprehberger/password/policy.rb,
lib/philiprehberger/password/zxcvbn.rb,
lib/philiprehberger/password/hashing.rb,
lib/philiprehberger/password/version.rb,
lib/philiprehberger/password/patterns.rb,
lib/philiprehberger/password/strength.rb,
lib/philiprehberger/password/generator.rb,
lib/philiprehberger/password/common_passwords.rb
Defined Under Namespace
Modules: CommonPasswords, Generator, Hashing, Patterns, Strength, Zxcvbn Classes: Policy
Constant Summary collapse
- VERSION =
'0.9.0'
Class Method Summary collapse
-
.batch_strength(passwords) ⇒ Array<Hash>
Compute strength for a list of passwords in input order.
-
.common?(password) ⇒ Boolean
Check if a password appears in the common password dictionary.
-
.entropy(password) ⇒ Float
Estimated entropy of the password in bits (log2(pool_size ^ length)).
-
.generate(**options) ⇒ String
Generate a secure random password, passphrase, PIN, or pronounceable password.
-
.hash(password, cost: 12) ⇒ String
Hash a password using bcrypt.
-
.keyboard_patterns(password) ⇒ Array<Hash>
Detect keyboard patterns, sequences, and repeated characters.
-
.mask(password, visible: 0, mask: '*') ⇒ String
Mask a password for safe display in logs, diagnostics, or UI surfaces.
-
.score(password) ⇒ Integer
Strength score as a 0-4 integer.
-
.secure_compare(left, right) ⇒ Boolean
Timing-safe string comparison.
-
.strength(password) ⇒ Hash
Compute strength of a single password.
-
.strong?(password, threshold: 3) ⇒ Boolean
Predicate: is the password "strong enough"?.
-
.verify(password, hash) ⇒ Boolean
Verify a password against a bcrypt hash.
-
.zxcvbn(password) ⇒ Hash
Perform zxcvbn-style strength estimation.
Class Method Details
.batch_strength(passwords) ⇒ Array<Hash>
Compute strength for a list of passwords in input order.
Each element is coerced via to_s so non-string entries don't raise.
39 40 41 42 43 |
# File 'lib/philiprehberger/password.rb', line 39 def self.batch_strength(passwords) raise ArgumentError, 'passwords must be enumerable' unless passwords.respond_to?(:each) passwords.map { |p| Strength.compute(p.to_s) } end |
.common?(password) ⇒ Boolean
Check if a password appears in the common password dictionary.
21 22 23 |
# File 'lib/philiprehberger/password.rb', line 21 def self.common?(password) CommonPasswords.include?(password.to_s.downcase) end |
.entropy(password) ⇒ Float
Estimated entropy of the password in bits (log2(pool_size ^ length)). Pool size is inferred from the character classes present.
50 51 52 |
# File 'lib/philiprehberger/password.rb', line 50 def self.entropy(password) Strength.entropy(password) end |
.generate(**options) ⇒ String
Generate a secure random password, passphrase, PIN, or pronounceable password.
89 90 91 |
# File 'lib/philiprehberger/password.rb', line 89 def self.generate(**) Generator.generate(**) end |
.hash(password, cost: 12) ⇒ String
Hash a password using bcrypt. Requires the bcrypt gem to be installed.
130 131 132 |
# File 'lib/philiprehberger/password.rb', line 130 def self.hash(password, cost: 12) Hashing.hash(password, cost: cost) end |
.keyboard_patterns(password) ⇒ Array<Hash>
Detect keyboard patterns, sequences, and repeated characters.
120 121 122 |
# File 'lib/philiprehberger/password.rb', line 120 def self.keyboard_patterns(password) Patterns.detect(password) end |
.mask(password, visible: 0, mask: '*') ⇒ String
Mask a password for safe display in logs, diagnostics, or UI surfaces.
Reveals the trailing visible characters and replaces the rest with
mask so that the full length of the password is still preserved.
When visible is 0 (default) the entire password is masked.
162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/philiprehberger/password.rb', line 162 def self.mask(password, visible: 0, mask: '*') raise ArgumentError, 'visible must be >= 0' if visible.negative? raise ArgumentError, 'mask must be a single character' unless mask.is_a?(String) && mask.length == 1 str = password.to_s return '' if str.empty? reveal = [visible, str.length].min masked_length = str.length - reveal (mask * masked_length) + str[-reveal, reveal].to_s end |
.score(password) ⇒ Integer
Strength score as a 0-4 integer. Convenience accessor that returns
only the :score from strength.
59 60 61 |
# File 'lib/philiprehberger/password.rb', line 59 def self.score(password) Strength.compute(password)[:score] end |
.secure_compare(left, right) ⇒ Boolean
Timing-safe string comparison. Uses the constant-time
OpenSSL.fixed_length_secure_compare when both inputs are the same byte
length, and a length-masked constant-time fallback (returning false
without early-exiting on the length difference) otherwise.
101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/philiprehberger/password.rb', line 101 def self.secure_compare(left, right) a = left.to_s.b b = right.to_s.b return OpenSSL.fixed_length_secure_compare(a, b) if a.bytesize == b.bytesize # Compare equal-length digests so we never branch purely on the byte # size, then force false since differing lengths cannot be equal. OpenSSL.fixed_length_secure_compare( OpenSSL::Digest::SHA256.digest(a), OpenSSL::Digest::SHA256.digest(b) ) false end |
.strength(password) ⇒ Hash
Compute strength of a single password.
29 30 31 |
# File 'lib/philiprehberger/password.rb', line 29 def self.strength(password) Strength.compute(password) end |
.strong?(password, threshold: 3) ⇒ Boolean
Predicate: is the password "strong enough"?
Returns true when score is at least threshold (default 3, the
"strong" tier on the 0-4 scale: terrible/weak/fair/strong/excellent).
Use a higher threshold (e.g. 4) for stricter gating.
72 73 74 |
# File 'lib/philiprehberger/password.rb', line 72 def self.strong?(password, threshold: 3) score(password) >= threshold end |