Class | BCrypt::Engine |
In: |
lib/bcrypt.rb
lib/bcrypt_engine.rb lib/bcrypt.rb lib/bcrypt_engine.rb |
Parent: | Object |
A Ruby wrapper for the bcrypt() C extension calls and the Java calls.
DEFAULT_COST | = | 10 | The default computational expense parameter. | |
MIN_COST | = | 4 | The minimum cost supported by the algorithm. | |
MAX_SALT_LENGTH | = | 16 | Maximum possible size of bcrypt() salts. | |
BCRYPT_MAXSALT | = | 16 | ||
BCRYPT_SALT_OUTPUT_SIZE | = | 7 + (BCRYPT_MAXSALT * 4 + 2) / 3 + 1 | ||
BCRYPT_OUTPUT_SIZE | = | 128 | ||
DEFAULT_COST | = | 10 | The default computational expense parameter. | |
MIN_COST | = | 4 | The minimum cost supported by the algorithm. | |
MAX_SALT_LENGTH | = | 16 | Maximum possible size of bcrypt() salts. | |
BCRYPT_MAXSALT | = | 16 | ||
BCRYPT_SALT_OUTPUT_SIZE | = | 7 + (BCRYPT_MAXSALT * 4 + 2) / 3 + 1 | ||
BCRYPT_OUTPUT_SIZE | = | 128 |
# File lib/bcrypt_engine.rb, line 26 26: def self.__bc_crypt(key, salt, cost) 27: buffer_out = FFI::Buffer.alloc_out(BCRYPT_OUTPUT_SIZE, 1) 28: out = ruby_bcrypt(buffer_out, key || "", salt) 29: buffer_out.free 30: out && out.any? ? out : nil 31: end
# File lib/bcrypt_engine.rb, line 26 26: def self.__bc_crypt(key, salt, cost) 27: buffer_out = FFI::Buffer.alloc_out(BCRYPT_OUTPUT_SIZE, 1) 28: out = ruby_bcrypt(buffer_out, key || "", salt) 29: buffer_out.free 30: out && out.any? ? out : nil 31: end
# File lib/bcrypt_engine.rb, line 16 16: def self.__bc_salt(cost, seed) 17: buffer_out = FFI::Buffer.alloc_out(BCRYPT_SALT_OUTPUT_SIZE, 1) 18: seed_ptr = FFI::MemoryPointer.new(:uint8, BCRYPT_MAXSALT) 19: seed.bytes.to_a.each_with_index { |b, i| seed_ptr.int8_put(i, b) } 20: out = ruby_bcrypt_gensalt(buffer_out, cost, seed_ptr) 21: seed_ptr.free 22: buffer_out.free 23: out || "" 24: end
# File lib/bcrypt_engine.rb, line 16 16: def self.__bc_salt(cost, seed) 17: buffer_out = FFI::Buffer.alloc_out(BCRYPT_SALT_OUTPUT_SIZE, 1) 18: seed_ptr = FFI::MemoryPointer.new(:uint8, BCRYPT_MAXSALT) 19: seed.bytes.to_a.each_with_index { |b, i| seed_ptr.int8_put(i, b) } 20: out = ruby_bcrypt_gensalt(buffer_out, cost, seed_ptr) 21: seed_ptr.free 22: buffer_out.free 23: out || "" 24: end
Autodetects the cost from the salt string.
# File lib/bcrypt.rb, line 113 113: def self.autodetect_cost(salt) 114: salt[4..5].to_i 115: end
Autodetects the cost from the salt string.
# File lib/bcrypt.rb, line 113 113: def self.autodetect_cost(salt) 114: salt[4..5].to_i 115: end
Returns the cost factor which will result in computation times less than upper_time_limit_in_ms.
Example:
BCrypt.calibrate(200) #=> 10 BCrypt.calibrate(1000) #=> 12 # should take less than 200ms BCrypt::Password.create("woo", :cost => 10) # should take less than 1000ms BCrypt::Password.create("woo", :cost => 12)
# File lib/bcrypt.rb, line 103 103: def self.calibrate(upper_time_limit_in_ms) 104: 40.times do |i| 105: start_time = Time.now 106: Password.create("testing testing", :cost => i+1) 107: end_time = Time.now - start_time 108: return i if end_time * 1_000 > upper_time_limit_in_ms 109: end 110: end
Returns the cost factor which will result in computation times less than upper_time_limit_in_ms.
Example:
BCrypt.calibrate(200) #=> 10 BCrypt.calibrate(1000) #=> 12 # should take less than 200ms BCrypt::Password.create("woo", :cost => 10) # should take less than 1000ms BCrypt::Password.create("woo", :cost => 12)
# File lib/bcrypt.rb, line 103 103: def self.calibrate(upper_time_limit_in_ms) 104: 40.times do |i| 105: start_time = Time.now 106: Password.create("testing testing", :cost => i+1) 107: end_time = Time.now - start_time 108: return i if end_time * 1_000 > upper_time_limit_in_ms 109: end 110: end
Generates a random salt with a given computational cost.
# File lib/bcrypt.rb, line 64 64: def self.generate_salt(cost = DEFAULT_COST) 65: cost = cost.to_i 66: if cost > 0 67: if cost < MIN_COST 68: cost = MIN_COST 69: end 70: if RUBY_PLATFORM == "java" 71: Java.bcrypt_jruby.BCrypt.gensalt(cost) 72: else 73: prefix = "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW" 74: __bc_salt(prefix, cost, OpenSSL::Random.random_bytes(MAX_SALT_LENGTH)) 75: end 76: else 77: raise Errors::InvalidCost.new("cost must be numeric and > 0") 78: end 79: end
Generates a random salt with a given computational cost.
# File lib/bcrypt.rb, line 64 64: def self.generate_salt(cost = DEFAULT_COST) 65: cost = cost.to_i 66: if cost > 0 67: if cost < MIN_COST 68: cost = MIN_COST 69: end 70: if RUBY_PLATFORM == "java" 71: Java.bcrypt_jruby.BCrypt.gensalt(cost) 72: else 73: prefix = "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW" 74: __bc_salt(prefix, cost, OpenSSL::Random.random_bytes(MAX_SALT_LENGTH)) 75: end 76: else 77: raise Errors::InvalidCost.new("cost must be numeric and > 0") 78: end 79: end
Given a secret and a valid salt (see BCrypt::Engine.generate_salt) calculates a bcrypt() password hash.
# File lib/bcrypt.rb, line 43 43: def self.hash_secret(secret, salt, cost = nil) 44: if valid_secret?(secret) 45: if valid_salt?(salt) 46: if cost.nil? 47: cost = autodetect_cost(salt) 48: end 49: 50: if RUBY_PLATFORM == "java" 51: Java.bcrypt_jruby.BCrypt.hashpw(secret.to_s, salt.to_s) 52: else 53: __bc_crypt(secret.to_s, salt) 54: end 55: else 56: raise Errors::InvalidSalt.new("invalid salt") 57: end 58: else 59: raise Errors::InvalidSecret.new("invalid secret") 60: end 61: end
Given a secret and a valid salt (see BCrypt::Engine.generate_salt) calculates a bcrypt() password hash.
# File lib/bcrypt.rb, line 43 43: def self.hash_secret(secret, salt, cost = nil) 44: if valid_secret?(secret) 45: if valid_salt?(salt) 46: if cost.nil? 47: cost = autodetect_cost(salt) 48: end 49: 50: if RUBY_PLATFORM == "java" 51: Java.bcrypt_jruby.BCrypt.hashpw(secret.to_s, salt.to_s) 52: else 53: __bc_crypt(secret.to_s, salt) 54: end 55: else 56: raise Errors::InvalidSalt.new("invalid salt") 57: end 58: else 59: raise Errors::InvalidSecret.new("invalid secret") 60: end 61: end
Returns true if salt is a valid bcrypt() salt, false if not.
# File lib/bcrypt.rb, line 82 82: def self.valid_salt?(salt) 83: !!(salt =~ /^\$[0-9a-z]{2,}\$[0-9]{2,}\$[A-Za-z0-9\.\/]{22,}$/) 84: end
Returns true if salt is a valid bcrypt() salt, false if not.
# File lib/bcrypt.rb, line 82 82: def self.valid_salt?(salt) 83: !!(salt =~ /^\$[0-9a-z]{2,}\$[0-9]{2,}\$[A-Za-z0-9\.\/]{22,}$/) 84: end
Returns true if secret is a valid bcrypt() secret, false if not.
# File lib/bcrypt.rb, line 87 87: def self.valid_secret?(secret) 88: secret.respond_to?(:to_s) 89: end