Class: SaltyDog::PBKDF2
- Inherits:
-
Object
- Object
- SaltyDog::PBKDF2
- Defined in:
- lib/salty_dog/salty_dog.rb
Overview
PBKDF2 encapsulates the identically-named password-based key derivation function outlined in PKCS #5: Password-Based Cryptography Standard. PBKDF1, as set forth in the same document, has been recommended for removal from use, and thus is not implemented in SaltyDog. If you just need to generate keys, skip down to ::digest:
Constant Summary collapse
- ALLOWED_DIGESTS =
According to the recommendation, the hash functions that are supported for HMAC (pseudorandom number generation) are SHA1, SHA224, SHA256, SHA384, AND SHA512. These are provided here.
[:sha1, :sha224, :sha256, :sha384, :sha512]
Class Method Summary collapse
-
.build_digest(digest) ⇒ Object
Build the derived key.
-
.calculate_key(digest, password, salt, l, r, iterations) ⇒ Object
The workhorse of SaltyDog::PBKDF2.
-
.check_key_length_requirements(length) ⇒ Object
Check desired key length requirements.
-
.digest(options = {}) ⇒ Object
The primary point of entry for SaltyDog::PBKDF2.
-
.prf(digest, password, seed) ⇒ Object
Uses a pseudorandom function based on the digest function provided to SaltyDog::PBKDF2.digest to generate input for each iteration round.
-
.xor(x, y) ⇒ Object
XOR two strings
x
andy
. -
.xor_sum(digest, password, salt, iterations, block_number) ⇒ Object
Within each iteration, SaltyDog::PBKDF2.xor_sum XORs each block of output from SaltyDog::PBKDF2.prf.
Class Method Details
.build_digest(digest) ⇒ Object
Build the derived key. Called directly by SaltyDog::PBKDF2.digest.
53 54 55 56 57 58 59 60 |
# File 'lib/salty_dog/salty_dog.rb', line 53 def self.build_digest(digest) if !ALLOWED_DIGESTS.include?(digest) raise PBKDF2Error, 'Invalid digest' end klass = "OpenSSL::Digest::#{digest.to_s.upcase}" @digest = Object::const_get(klass).new end |
.calculate_key(digest, password, salt, l, r, iterations) ⇒ Object
The workhorse of SaltyDog::PBKDF2. ::calculate_key initiates the specified number of iterations of hashing in calculating each block of the derived key. All blocks are then concatenated together in computing the final derived key.
127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/salty_dog/salty_dog.rb', line 127 def self.calculate_key(digest, password, salt, l, r, iterations) t = "" for i in 1..l+1 do t << self.xor_sum(digest, password, salt, iterations, i) end total_length = digest.length * (l-1) + r sliced = t.slice(0..total_length - 1) sliced end |
.check_key_length_requirements(length) ⇒ Object
Check desired key length requirements. These are:
-
Must be present
-
Must be strictly positive
-
Must be no larger than (2^32 - 1) * digest length of the chosen hash
function
Raises a PBKDF2Error if any of these requirements are not met.
72 73 74 75 76 |
# File 'lib/salty_dog/salty_dog.rb', line 72 def self.check_key_length_requirements(length) raise PBKDF2Error, 'A key length must be provided' if !length raise PBKDF2Error, 'Desired key is too long' if ((2**32 - 1) * @digest.length) < length raise PBKDF2Error, 'Desired key length must be positive' if length < 0 end |
.digest(options = {}) ⇒ Object
The primary point of entry for SaltyDog::PBKDF2. The available options are:
-
:digest - One of
:sha1
,:sha224
,:sha256
,:sha384
, or
:sha512
. Defaults to :sha512
.
-
:password - A password for use in deriving the key. Required, and must be a string.
-
:salt - A salt that is concatenated to the password in key derivation.
Required, and must ba a string.
-
:length - The desired length, in bytes, of the derived key. Required.
-
:iterations - The number of iterations to be used in key derivation.
Defaults to 10000.
Returns a hex-string representing the derived key.
36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/salty_dog/salty_dog.rb', line 36 def self.digest( = {}) digest = [:digest] || :sha512 self.build_digest(digest) check_key_length_requirements([:length]) @length = [:length] @iterations = [:iterations] || 10000 @l = (@length / @digest.length).ceil @r = @length - (@l - 1) * @digest.length self.calculate_key(@digest, [:password].to_s, [:salt].to_s, @l, @r, @iterations).unpack('H*')[0] end |
.prf(digest, password, seed) ⇒ Object
Uses a pseudorandom function based on the digest function provided to SaltyDog::PBKDF2.digest to generate input for each iteration round.
97 98 99 100 |
# File 'lib/salty_dog/salty_dog.rb', line 97 def self.prf(digest, password, seed) raise PBKDF2Error if !password || !seed OpenSSL::HMAC.digest(digest, password, seed) end |
.xor(x, y) ⇒ Object
XOR two strings x
and y
.
Raises a PBKDF2Error if a
and b
are not the same length.
Returns a string of bytes representing the XORed value.
85 86 87 88 89 90 91 |
# File 'lib/salty_dog/salty_dog.rb', line 85 def self.xor(x, y) raise PBKDF2Error, 'XOR arguments are not the same length' if x.length - y.length != 0 output = "".encode('ASCII-8BIT') x.bytes.zip(y.bytes) { |x,y| output << (x^y) } output end |
.xor_sum(digest, password, salt, iterations, block_number) ⇒ Object
Within each iteration, SaltyDog::PBKDF2.xor_sum XORs each block of output from SaltyDog::PBKDF2.prf. The result of this chain of XORs is provided to ::calculate_key to be used as a block of the final derived key.
107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/salty_dog/salty_dog.rb', line 107 def self.xor_sum(digest, password, salt, iterations, block_number) packed_index = [block_number].pack("N") seed = salt + packed_index final = self.prf(digest, password, seed) u = final for i in 2..iterations do u = self.prf(digest, password, u) final = self.xor(final, u) end final end |