Class: CryptCheckpass::PBKDF2

Inherits:
CryptCheckpass show all
Extended by:
PHCStringFormat
Defined in:
lib/crypt_checkpass/pbkdf2.rb

Overview

PBKDF2 is the most beloved algorithm by security professionals.

Newhash:

You can use crypto_newhash to create a new password hash using PBKDF2:

crypt_newhash(password, id: 'pbkdf2-sha1', rounds: 1024)

where:

  • password is the raw binary password that you want to digest.

  • id is "pbkdf2-digest". You can specify sha1 / sha256 / sha512. Unlike plain SHA1, PBKDF2 + SHA1 combination still has no known weakness as of writing so specifying pbkdf-sha1 should just suffice normally.

  • rounds is for iteration rounds.

The generated password hash has following format.

Format:

This algorithm does not have a standard hash format. Here we follow npm's @phc/pbkdf2.

%r{
  (?<id>   pbkdf2-[\w\d]+ ){0}
  (?<i>    i=[1-9][0-9]*  ){0}
  (?<salt> [a-zA-Z0-9+/]* ){0}
  (?<csum> [a-zA-Z0-9+/]* ){0}

  \A [$] \g<id>
     [$] \g<i>
     [$] \g<salt>
     [$] \g<csum>
  \z
}x
  • This is a strict PHC string format. See also PHCStringFormat

  • The id can either be "pbkdf2-sha1", "pbkdf2-sha256", or "pbkdf2-sha512".

  • The only parameter i is the iteration (rounds) of the calculation.

Other formats:

Python Passlib generates something different, in the same $pbkdf2-{digest}$ id. Passlib's and @phc/pbkdf2's are distinguishable because Passlib does not follow PHC String Format.

Examples:

crypt_newhash 'password', id: 'pbkdf2-sha1'
# => "$pbkdf2-sha1$i=1024$a9b0ggwILmLgiAwV34bpzA$nJ+GYjlNDao8BJedGVc8UROXpcU"
crypt_checkpass? 'password', '$pbkdf2-sha1$i=1024$a9b0ggwILmLgiAwV34bpzA$nJ+GYjlNDao8BJedGVc8UROXpcU'
# => true

See Also:

Class Method Summary collapse

Methods inherited from CryptCheckpass

crypt_checkpass?, crypt_newhash

Class Method Details

.checkpass?(pass, hash) ⇒ true, false

Checks if the given password matches the hash.

Parameters:

  • pass (String)

    a password to test.

  • hash (String)

    a good hash digest string.

Returns:

  • (true)

    they are identical.

  • (false)

    they are distinct.

Raises:

  • (NotImplementedError)

    don't know how to parse hash.



110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/crypt_checkpass/pbkdf2.rb', line 110

def self.checkpass? pass, hash
  require 'consttime_memequal'

  json     = phcdecode hash
  id       = json[:id]
  i        = json[:params][:i]
  salt     = json[:salt]
  expected = json[:csum]
  dklen    = expected.bytesize
  actual   = __derive_key id, i, salt, pass, dklen

  return consttime_memequal? expected, actual
end

.newhash(pass, id: 'pbkdf2-sha1', rounds: 1024) ⇒ String

Note:

There is no way to specify salt. That's a bad idea.

Generate a new password hash string.

Parameters:

  • pass (String)

    raw binary password string.

  • id (String) (defaults to: 'pbkdf2-sha1')

    name of the algorithm

  • rounds (Integer) (defaults to: 1024)

    iteration rounds

Returns:

  • (String)

    hashed digest string of password.



138
139
140
141
142
# File 'lib/crypt_checkpass/pbkdf2.rb', line 138

def self.newhash pass, id: 'pbkdf2-sha1', rounds: 1024
  salt = SecureRandom.random_bytes 16
  csum = __derive_key id, rounds, salt, pass
  return phcencode id, { i: rounds }, salt, csum
end

.provide?(id) ⇒ true, false

Checks if the given ID can be handled by this class. A class is free to handle several IDs, like 'argon2i', 'argon2d', ...

Parameters:

  • id (String)

    hash function ID.

Returns:

  • (true)

    it does.

  • (false)

    it desn't.



125
126
127
128
129
130
131
# File 'lib/crypt_checkpass/pbkdf2.rb', line 125

def self.provide? id
  case id when 'pbkdf2-sha1', 'pbkdf2-sha256', 'pbkdf2-sha512' then
    return true
  else
    return false
  end
end

.understand?(str) ⇒ true, false

Checks if the given hash string can be handled by this class.

Parameters:

  • str (String)

    a good hashed string.

Returns:

  • (true)

    it does.

  • (false)

    it desn't.



94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/crypt_checkpass/pbkdf2.rb', line 94

def self.understand? str
  return match? str, %r{
    (?<id>   pbkdf2-sha(1|256|512) ){0}
    (?<i>    i=[1-9][0-9]*         ){0}
    (?<salt> [a-zA-Z0-9+/]*        ){0}
    (?<csum> [a-zA-Z0-9+/]*        ){0}

    \A [$] \g<id>
       [$] \g<i>
       [$] \g<salt>
       [$] \g<csum>
    \z
  }x
end