Class: EasyPassword

Inherits:
Object
  • Object
show all
Defined in:
lib/easy-password.rb,
lib/easy-password/version.rb

Overview

Adding a simple password generator and using it by default:

# Generate 10 random alphanumeric characters
EasyPassword.generator :random10 do
  SecureRandom.alphanumeric(10)
end

# Define the default generator
EasyPassword.default_generator = :random10

Adding a checker

# Implementing classic at least 1 lowercase, 1 upercase, 1 digit
EasyPassword.checker :aA1 do |password, all|
  list = { /\d/    => :digit_needed,
           /[A-Z]/ => :upercase_needed,
           /[a-z]/ => :lowercase_needed,
         }.lazy.map {|regex, failure| failure if password !~ regex }
               .reject(&:nil?)
  all ? list.to_a : list.first
end

# Looking for known bad passwords in a database (using Sequel)
Password.checker :hack_dictionary do |password, all|
  ! DB[:bad_passwords].first(:password => password).nil?
end

Creating password

password = EasyPassword.new
password = EasyPassword.new('foobar')

Checking for weakness

password.weakness

Constant Summary collapse

Digest =
OpenSSL::Digest
VERSION =

Version

'0.1'

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(password = EasyPassword::generate) ⇒ EasyPassword

Create a new EasyPassword



234
235
236
# File 'lib/easy-password.rb', line 234

def initialize(password = EasyPassword::generate)
    @passwd = password.clone.freeze
end

Class Method Details

.checker(name) {|password, all| ... } ⇒ Object

DSL to ass a new password checker

Yield Parameters:

  • password (String)

    plain text password

  • all (Boolean)

    should all weakness be listed

Yield Returns:

  • (false, nil, [])

    if no weakness have been discovered

  • (true, Symbol, Array<Symbol>)

    name of weakness



112
113
114
# File 'lib/easy-password.rb', line 112

def self.checker(name, &block)
    @checkers[name] ||= block
end

.default_checkersObject

List of default checkers to use



90
91
92
# File 'lib/easy-password.rb', line 90

def self.default_checkers
    @default_checkers
end

.default_checkers=(checkers) ⇒ Object

Define the list of default checkers to use

Parameters:

  • checkers (Array<Symbol>, nil)

    list of checkers nickname, if nil all available checkers will be used



84
85
86
# File 'lib/easy-password.rb', line 84

def self.default_checkers=(checkers)
    @default_checkers = checkers
end

.default_generatorObject

Default generator



74
75
76
# File 'lib/easy-password.rb', line 74

def self.default_generator
    @default_generator
end

.default_generator=(type) ⇒ Object

Define the default generator to use

Parameters:

  • type (Symbol)

    Generator nickname



68
69
70
# File 'lib/easy-password.rb', line 68

def self.default_generator=(type)
    @default_generator = type
end

.generate(type = self.default_generator) ⇒ String

Generate a plain text password string.

Parameters:

  • type (Symbol) (defaults to: self.default_generator)

    Generator nickname

Returns:

  • (String)


159
160
161
162
163
164
165
166
# File 'lib/easy-password.rb', line 159

def self.generate(type = self.default_generator)
    if type.nil?
        raise ArgumentError, 'invalid generator type'
    end
        
    @generators[type]&.call() ||
        raise("requested generator '#{type}' doesn't exist")
end

.generator(name) {|| ... } ⇒ Object

DSL to add a new password generator

Yield Parameters:

  • (void)

Yield Returns:

  • (String)

    plain text password



100
101
102
# File 'lib/easy-password.rb', line 100

def self.generator(name, &block)
    @generators[name] = block
end

.hideObject

Is password value hidden when calling #to_s



53
54
55
# File 'lib/easy-password.rb', line 53

def self.hide
    @hide
end

.hide=(hide) ⇒ Object

Control if password value is hidden when calling #to_s



59
60
61
# File 'lib/easy-password.rb', line 59

def self.hide=(hide)
    @hide = hide
end

.lmhash(password) ⇒ String

Create a LMHASH-hashed password

Parameters:

  • password (String)

    plain text password

Returns:

  • (String)

    hashed password



219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/easy-password.rb', line 219

def self.lmhash(password)
    passwd = password[0..13].upcase
    passwd = passwd + "\000" * (14 - passwd.length)
    des = OpenSSL::Cipher::Cipher.new('des-ecb')
    des.encrypt
    [passwd[0..6], passwd[7..13]].collect { |key56|
        keybin = key56.unpack('B*')[0].scan(/.{7}/).collect {|k|
            k + (k.count('1') % 2 == 0 ? '1' : '0') }
        des.key = keybin.pack('B8' * 8)
        des.update('KGS!@#$%')
    }.join.unpack('C*').map { |b| '%02x' % b }.join
end

.md5(password) ⇒ String

Create a MD5-hashed password

Parameters:

  • password (String)

    plain text password

Returns:

  • (String)

    hashed password



175
176
177
# File 'lib/easy-password.rb', line 175

def self.md5(password)
    "{MD5}"    + [Digest::MD5.digest(password)   ].pack('m0')
end

.ntlm(password) ⇒ String

Create an NTML-hashed password

Parameters:

  • password (String)

    plain text password

Returns:

  • (String)

    hashed password



208
209
210
# File 'lib/easy-password.rb', line 208

def self.ntlm(password)
    Digest::MD4.hexdigest(password.encode("utf-16le"))
end

.sha(password) ⇒ String

Create a SHA-hashed password

Parameters:

  • password (String)

    plain text password

Returns:

  • (String)

    hashed password



186
187
188
# File 'lib/easy-password.rb', line 186

def self.sha(password)
    "{SHA}"    + [Digest::SHA1.digest(password)  ].pack('m0')
end

.sha256(password) ⇒ String

Create a SHA256-hashed password

Parameters:

  • password (String)

    plain text password

Returns:

  • (String)

    hashed password



197
198
199
# File 'lib/easy-password.rb', line 197

def self.sha256(password)
    "{sha256}" + [Digest::SHA256.digest(password)].pack('m0')
end

.weakness(password, *checkers, all: true) ⇒ Hash{Symbol=>Array<Symbol>}

Check for weakness

Parameters:

  • password (String, EasyPassword)
  • checkers (Symbol)
  • all (Boolean) (defaults to: true)

Returns:

  • (Hash{Symbol=>Array<Symbol>})

Raises:

  • (KeyError)

    if a requested checker is not defined



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/easy-password.rb', line 127

def self.weakness(password, *checkers, all: true)
    return nil              if @checkers.empty?       
    password = password.raw if password.kind_of?(EasyPassword)

    checkers = self.default_checkers if checkers.empty?
    list     = if checkers.nil? || checkers.empty?
               then @checkers.lazy
               else checkers.lazy.map {|n| [n, @checkers.fetch(n)] }
               end
    list     = list.map {|name, checker|
        case r = checker.call(password, all: all)
        when Array      then [ name, r        ] unless r.empty?
        when Symbol     then [ name, [ r ]    ]
        when true       then [ name, [ name ] ]
        when nil, false
        else raise ArgumentError, 'unsupported checker return value'
        end
    }.reject(&:nil?)

    list = if all
           then Hash[list.to_a]
           else Hash[*list.first].transform_values {|v| v[0,1] }
           end
    list unless list.empty?
end

Instance Method Details

#lmhashString

Get the LMHASH-hashed password

Returns:

  • (String)

    hashed password



287
288
289
# File 'lib/easy-password.rb', line 287

def lmhash
    self.class.lmhash(@passwd)
end

#md5String

Get the MD5-hashed password

Returns:

  • (String)

    hashed password



269
270
271
# File 'lib/easy-password.rb', line 269

def md5
    self.class.md5(@passwd)
end

#ntlmString

Get the NTLM-hashed password

Returns:

  • (String)

    hashed password



278
279
280
# File 'lib/easy-password.rb', line 278

def ntlm
    self.class.ntlm(@passwd)
end

#rawString

Get the plain text password

Returns:

  • (String)

    plain text password



242
243
244
# File 'lib/easy-password.rb', line 242

def raw
    @passwd
end

#shaString

Get the SHA256-hashed password

Returns:

  • (String)

    hashed password



251
252
253
# File 'lib/easy-password.rb', line 251

def sha
    self.class.sha(@passwd)
end

#sha256String

Get the SHA256-hashed password

Returns:

  • (String)

    hashed password



260
261
262
# File 'lib/easy-password.rb', line 260

def sha256
    self.class.sha256(@passwd)
end

#to_sString

Display password. The behavior is controlled by Password.hide, so either the plain text password will be displayed or ********

Returns:

  • (String)


298
299
300
# File 'lib/easy-password.rb', line 298

def to_s
    self.class.hide != true ? "********" : self.raw
end

#weakness(*checkers, all: true) ⇒ Hash{Symbol=>Array<Symbol>}

Check for weakness

Parameters:

  • checkers (Symbol)
  • all (Boolean) (defaults to: true)

Returns:

  • (Hash{Symbol=>Array<Symbol>})

Raises:

  • (KeyError)

    if a requested checker is not defined



312
313
314
# File 'lib/easy-password.rb', line 312

def weakness(*checkers, all: true)
    self.class.weakness(@passwd, *checkers, all: all)
end