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.8.1'

Class Method Summary collapse

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.

Parameters:

  • passwords (Enumerable<String>)

    passwords to grade

Returns:

  • (Array<Hash>)

    strength hashes (one per input, same order)

Raises:

  • (ArgumentError)

    if ‘passwords` is not enumerable



38
39
40
41
42
# File 'lib/philiprehberger/password.rb', line 38

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.

Parameters:

  • password (String)

    the password to check

Returns:

  • (Boolean)

    true if the password is common



20
21
22
# File 'lib/philiprehberger/password.rb', line 20

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.

Parameters:

  • password (String)

    the password to evaluate

Returns:

  • (Float)

    estimated entropy in bits (0.0 for empty passwords)



49
50
51
# File 'lib/philiprehberger/password.rb', line 49

def self.entropy(password)
  Strength.entropy(password)
end

.generate(**options) ⇒ String

Generate a secure random password, passphrase, or PIN.

Parameters:

  • length (Integer)

    password length (default 16; ignored for passphrase style)

  • uppercase (Boolean)

    include uppercase letters (default true)

  • lowercase (Boolean)

    include lowercase letters (default true)

  • digits (Boolean)

    include digits (default true)

  • symbols (Boolean)

    include symbols (default true)

  • style (Symbol, nil)

    ‘:passphrase` or `:pin` for alternative styles

  • words (Integer)

    word count for passphrase style (default 4)

  • separator (String)

    separator for passphrase style (default “-”)

Returns:

  • (String)

    the generated password



86
87
88
# File 'lib/philiprehberger/password.rb', line 86

def self.generate(**options)
  Generator.generate(**options)
end

.hash(password, cost: 12) ⇒ String

Hash a password using bcrypt. Requires the bcrypt gem to be installed.

Parameters:

  • password (String)

    the plaintext password to hash

  • cost (Integer) (defaults to: 12)

    bcrypt cost factor (4-31, default 12)

Returns:

  • (String)

    the bcrypt hash



104
105
106
# File 'lib/philiprehberger/password.rb', line 104

def self.hash(password, cost: 12)
  Hashing.hash(password, cost: cost)
end

.keyboard_patterns(password) ⇒ Array<Hash>

Detect keyboard patterns, sequences, and repeated characters.

Parameters:

  • password (String)

    the password to inspect

Returns:

  • (Array<Hash>)

    detected pattern hashes (keyboard rows, sequences, repeats)



94
95
96
# File 'lib/philiprehberger/password.rb', line 94

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.

Parameters:

  • password (String)

    the password to mask

  • visible (Integer) (defaults to: 0)

    number of trailing characters to expose (>= 0)

  • mask (String) (defaults to: '*')

    single-character replacement for masked positions

Returns:

  • (String)

    the masked password

Raises:

  • (ArgumentError)

    if visible is negative or mask is not one character



136
137
138
139
140
141
142
143
144
145
146
# File 'lib/philiprehberger/password.rb', line 136

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.

Parameters:

  • password (String)

    the password to evaluate

Returns:

  • (Integer)

    strength score (0 = very weak, 4 = very strong)



58
59
60
# File 'lib/philiprehberger/password.rb', line 58

def self.score(password)
  Strength.compute(password)[:score]
end

.strength(password) ⇒ Hash

Compute strength of a single password.

Parameters:

  • password (String)

    the password to evaluate

Returns:

  • (Hash)

    strength hash with ‘:score` (0-4), `:label`, `:entropy`, and `:feedback`



28
29
30
# File 'lib/philiprehberger/password.rb', line 28

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.

Parameters:

  • password (String)

    the password to evaluate

  • threshold (Integer) (defaults to: 3)

    minimum acceptable score (0-4)

Returns:

  • (Boolean)


71
72
73
# File 'lib/philiprehberger/password.rb', line 71

def self.strong?(password, threshold: 3)
  score(password) >= threshold
end

.verify(password, hash) ⇒ Boolean

Verify a password against a bcrypt hash. Requires the bcrypt gem to be installed.

Parameters:

  • password (String)

    the plaintext password to verify

  • hash (String)

    the bcrypt hash to verify against

Returns:

  • (Boolean)

    true when the password matches the hash



114
115
116
# File 'lib/philiprehberger/password.rb', line 114

def self.verify(password, hash)
  Hashing.verify(password, hash)
end

.zxcvbn(password) ⇒ Hash

Perform zxcvbn-style strength estimation.

Parameters:

  • password (String)

    the password to evaluate

Returns:

  • (Hash)

    zxcvbn-style report with ‘:score`, `:patterns`, and `:crack_time_display`



122
123
124
# File 'lib/philiprehberger/password.rb', line 122

def self.zxcvbn(password)
  Zxcvbn.estimate(password)
end