class BCrypt::Engine
A Ruby wrapper for the bcrypt() C extension calls and the Java calls.
Constants
- DEFAULT_COST
The default computational expense parameter.
- MAX_COST
The maximum cost supported by the algorithm.
- MAX_SALT_LENGTH
Maximum possible size of bcrypt() salts.
- MAX_SECRET_BYTESIZE
Maximum possible size of bcrypt() secrets. Older versions of the bcrypt library would truncate passwords longer than 72 bytes, but newer ones do not. We truncate like the old library for forward compatibility. This way users upgrading from Ubuntu 18.04 to 20.04 will not have their user passwords invalidated, for example. A max secret length greater than 255 leads to bcrypt returning nil. github.com/bcrypt-ruby/bcrypt-ruby/issues/225#issuecomment-875908425
- MIN_COST
The minimum cost supported by the algorithm.
Public Class Methods
Given a secret and a salt, generates a salted hash (which you can then store safely).
static VALUE bc_crypt(VALUE self, VALUE key, VALUE setting) { char * value; void * data; int size; VALUE out; data = NULL; size = 0xDEADBEEF; if(NIL_P(key) || NIL_P(setting)) return Qnil; value = crypt_ra( NIL_P(key) ? NULL : StringValuePtr(key), NIL_P(setting) ? NULL : StringValuePtr(setting), &data, &size); if(!value || !data) return Qnil; out = rb_str_new2(value); xfree(data); return out; }
Given a logarithmic cost parameter, generates a salt for use with bc_crypt
.
static VALUE bc_salt(VALUE self, VALUE prefix, VALUE count, VALUE input) { char * salt; VALUE str_salt; salt = crypt_gensalt_ra( StringValuePtr(prefix), NUM2ULONG(count), NIL_P(input) ? NULL : StringValuePtr(input), NIL_P(input) ? 0 : RSTRING_LEN(input)); if(!salt) return Qnil; str_salt = rb_str_new2(salt); free(salt); return str_salt; }
Autodetects the cost from the salt string.
# File lib/bcrypt/engine.rb 122 def self.autodetect_cost(salt) 123 salt[4..5].to_i 124 end
Returns the cost factor which will result in computation times less than upper_time_limit_in_ms
.
Example:
BCrypt::Engine.calibrate(200) #=> 10 BCrypt::Engine.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/engine.rb 112 def self.calibrate(upper_time_limit_in_ms) 113 (BCrypt::Engine::MIN_COST..BCrypt::Engine::MAX_COST-1).each do |i| 114 start_time = Time.now 115 Password.create("testing testing", :cost => i+1) 116 end_time = Time.now - start_time 117 return i if end_time * 1_000 > upper_time_limit_in_ms 118 end 119 end
Returns the cost factor that will be used if one is not specified when creating a password hash. Defaults to DEFAULT_COST
if not set.
# File lib/bcrypt/engine.rb 32 def self.cost 33 @cost || DEFAULT_COST 34 end
Set a default cost factor that will be used if one is not specified when creating a password hash.
Example:
BCrypt::Engine::DEFAULT_COST #=> 12 BCrypt::Password.create('secret').cost #=> 12 BCrypt::Engine.cost = 8 BCrypt::Password.create('secret').cost #=> 8 # cost can still be overridden as needed BCrypt::Password.create('secret', :cost => 6).cost #=> 6
# File lib/bcrypt/engine.rb 49 def self.cost=(cost) 50 @cost = cost 51 end
Generates a random salt with a given computational cost.
# File lib/bcrypt/engine.rb 74 def self.generate_salt(cost = self.cost) 75 cost = cost.to_i 76 if cost > 0 77 if cost < MIN_COST 78 cost = MIN_COST 79 end 80 if RUBY_PLATFORM == "java" 81 Java.bcrypt_jruby.BCrypt.gensalt(cost) 82 else 83 __bc_salt("$2a$", cost, OpenSSL::Random.random_bytes(MAX_SALT_LENGTH)) 84 end 85 else 86 raise Errors::InvalidCost.new("cost must be numeric and > 0") 87 end 88 end
Given a secret and a valid salt (see BCrypt::Engine.generate_salt
) calculates a bcrypt() password hash. Secrets longer than 72 bytes are truncated.
# File lib/bcrypt/engine.rb 55 def self.hash_secret(secret, salt, _ = nil) 56 if valid_secret?(secret) 57 if valid_salt?(salt) 58 if RUBY_PLATFORM == "java" 59 Java.bcrypt_jruby.BCrypt.hashpw(secret.to_s.to_java_bytes, salt.to_s) 60 else 61 secret = secret.to_s 62 secret = secret.byteslice(0, MAX_SECRET_BYTESIZE) if secret && secret.bytesize > MAX_SECRET_BYTESIZE 63 __bc_crypt(secret, salt) 64 end 65 else 66 raise Errors::InvalidSalt.new("invalid salt") 67 end 68 else 69 raise Errors::InvalidSecret.new("invalid secret") 70 end 71 end
Returns true if salt
is a valid bcrypt() salt, false if not.
# File lib/bcrypt/engine.rb 91 def self.valid_salt?(salt) 92 !!(salt =~ /\A\$[0-9a-z]{2,}\$[0-9]{2,}\$[A-Za-z0-9\.\/]{22,}\z/) 93 end
Returns true if secret
is a valid bcrypt() secret, false if not.
# File lib/bcrypt/engine.rb 96 def self.valid_secret?(secret) 97 secret.respond_to?(:to_s) 98 end