class RbPass::PasswordHasher
Public Class Methods
new(iteration_count_log2, portable_hashes)
click to toggle source
# File lib/rbpass/password_hasher.rb, line 5 def initialize(iteration_count_log2, portable_hashes) @itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' @iteration_count_log2 = iteration_count_log2.between?(4, 31) ? iteration_count_log2 : 8 @portable_hashes = portable_hashes @random_state = Time.now.usec.to_s + Process.pid.to_s end
Public Instance Methods
check(password, stored_hash)
click to toggle source
# File lib/rbpass/password_hasher.rb, line 171 def check(password, stored_hash) hash = crypt_private(password, stored_hash) if hash[0] == '*' hash = password.crypt(stored_hash) end hash == stored_hash end
crypt_private(password, setting)
click to toggle source
# File lib/rbpass/password_hasher.rb, line 83 def crypt_private(password, setting) output = '*0' if setting[0,2] == output output = '*1' end return output if setting[0,3] != '$P$' and setting[0,3] != '$H$' count_log2 = @itoa64.index(setting[3]) return output if !count_log2.between?(7, 30) count = 1 << count_log2 salt = setting[4,8] return output if salt.length != 8 hash = Digest::MD5.digest(salt + password) while count > 0 do hash = Digest::MD5.digest(hash + password) count -= 1 end output = setting[0, 12] output << encode64(hash, 16) output end
encode64(input, count)
click to toggle source
# File lib/rbpass/password_hasher.rb, line 35 def encode64(input, count) output = '' i = 0 while i < count value = (input[i]).ord i += 1 output << @itoa64[value & 0x3f] if i < count value |= (input[i]).ord << 8 end output << @itoa64[(value >> 6) & 0x3f] if i >= count break end i += 1 if i < count value |= (input[i]).ord << 16 end output << @itoa64[(value >> 12) & 0x3f] if i >= count break end i += 1 output << @itoa64[(value >> 18) & 0x3f] end output end
gensalt_blowfish(input)
click to toggle source
# File lib/rbpass/password_hasher.rb, line 114 def gensalt_blowfish(input) itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' output = '$2a$' output << ('0'.ord + @iteration_count_log2 / 10).chr output << ('0'.ord + @iteration_count_log2 % 10).chr output << '$' i = 0 while true c1 = (input[i]).ord i += 1 output << itoa64[c1 >> 2] c1 = (c1 & 0x03) << 4 if i >= 16 output << itoa64[c1] break end c2 = (input[i]).ord i += 1 c1 |= c2 >> 4 output << itoa64[c1] c1 = (c2 & 0x0f) << 2 c2 = (input[i]).ord i += 1 c1 |= c2 >> 6 output << itoa64[c1] output << itoa64[c2 & 0x3f] end output end
gensalt_private(input)
click to toggle source
# File lib/rbpass/password_hasher.rb, line 74 def gensalt_private(input) output = '$P$' output << @itoa64[[@iteration_count_log2 + 5, 30].min] output << encode64(input, 6) output end
get_random_bytes(count)
click to toggle source
# File lib/rbpass/password_hasher.rb, line 12 def get_random_bytes(count) output = '' if File.readable?('/dev/urandom') output = File.read('/dev/urandom', count) end if output.length < count output = '' i = 0 while i < count do @random_state = Digest::MD5.hexdigest(Time.now.usec.to_s + @random_state) output << Digest::MD5.hexdigest(@random_state).split(//).pack('H*') i += 16 end output = output[0..count] end output end
hash(password)
click to toggle source
# File lib/rbpass/password_hasher.rb, line 151 def hash(password) random = '' if !@portable_hashes random = get_random_bytes(16) hash = password.crypt(gensalt_blowfish(random)) return hash if hash.length == 60 end if random.length < 6 random = get_random_bytes(6) end hash = crypt_private(password, gensalt_private(random)) return hash if hash.length == 34 '*' end