| 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