Class: BCrypt::Engine
- Inherits:
-
Object
- Object
- BCrypt::Engine
- Defined in:
- lib/bcrypt/engine.rb,
ext/mri/bcrypt_ext.c
Overview
A Ruby wrapper for the bcrypt() C extension calls and the Java calls.
Constant Summary collapse
- DEFAULT_COST =
The default computational expense parameter.
12
- MIN_COST =
The minimum cost supported by the algorithm.
4
- MAX_COST =
The maximum cost supported by the algorithm.
31
- 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
72
- MAX_SALT_LENGTH =
Maximum possible size of bcrypt() salts.
16
Class Method Summary collapse
-
.__bc_crypt(key, setting) ⇒ Object
Given a secret and a salt, generates a salted hash (which you can then store safely).
-
.__bc_salt(prefix, count, input) ⇒ Object
Given a logarithmic cost parameter, generates a salt for use with
bc_crypt
. -
.autodetect_cost(salt) ⇒ Object
Autodetects the cost from the salt string.
-
.calibrate(upper_time_limit_in_ms) ⇒ Object
Returns the cost factor which will result in computation times less than
upper_time_limit_in_ms
. -
.cost ⇒ Object
Returns the cost factor that will be used if one is not specified when creating a password hash.
-
.cost=(cost) ⇒ Object
Set a default cost factor that will be used if one is not specified when creating a password hash.
-
.generate_salt(cost = self.cost) ⇒ Object
Generates a random salt with a given computational cost.
-
.hash_secret(secret, salt, _ = nil) ⇒ Object
Given a secret and a valid salt (see BCrypt::Engine.generate_salt) calculates a bcrypt() password hash.
-
.valid_salt?(salt) ⇒ Boolean
Returns true if
salt
is a valid bcrypt() salt, false if not. -
.valid_secret?(secret) ⇒ Boolean
Returns true if
secret
is a valid bcrypt() secret, false if not.
Class Method Details
.__bc_crypt(key, setting) ⇒ Object
Given a secret and a salt, generates a salted hash (which you can then store safely).
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'ext/mri/bcrypt_ext.c', line 75
static VALUE bc_crypt(VALUE self, VALUE key, VALUE setting) {
char * value;
VALUE out;
struct bc_crypt_args args;
if(NIL_P(key) || NIL_P(setting)) return Qnil;
/* duplicate the parameters for thread safety. If another thread has a
* reference to the parameters and mutates them while we are working,
* that would be very bad. Duping the strings means that the reference
* isn't shared. */
key = rb_str_new_frozen(key);
setting = rb_str_new_frozen(setting);
args.data = NULL;
args.size = 0xDEADBEEF;
args.key = NIL_P(key) ? NULL : StringValueCStr(key);
args.setting = NIL_P(setting) ? NULL : StringValueCStr(setting);
#ifdef HAVE_RUBY_THREAD_H
value = rb_thread_call_without_gvl(bc_crypt_nogvl, &args, NULL, NULL);
#else
value = bc_crypt_nogvl((void *)&args);
#endif
if(!value || !args.data) return Qnil;
out = rb_str_new2(value);
RB_GC_GUARD(key);
RB_GC_GUARD(setting);
free(args.data);
return out;
}
|
.__bc_salt(prefix, count, input) ⇒ Object
Given a logarithmic cost parameter, generates a salt for use with bc_crypt
.
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'ext/mri/bcrypt_ext.c', line 26
static VALUE bc_salt(VALUE self, VALUE prefix, VALUE count, VALUE input) {
char * salt;
VALUE str_salt;
struct bc_salt_args args;
/* duplicate the parameters for thread safety. If another thread has a
* reference to the parameters and mutates them while we are working,
* that would be very bad. Duping the strings means that the reference
* isn't shared. */
prefix = rb_str_new_frozen(prefix);
input = rb_str_new_frozen(input);
args.prefix = StringValueCStr(prefix);
args.count = NUM2ULONG(count);
args.input = NIL_P(input) ? NULL : StringValuePtr(input);
args.size = NIL_P(input) ? 0 : RSTRING_LEN(input);
#ifdef HAVE_RUBY_THREAD_H
salt = rb_thread_call_without_gvl(bc_salt_nogvl, &args, NULL, NULL);
#else
salt = bc_salt_nogvl((void *)&args);
#endif
if(!salt) return Qnil;
str_salt = rb_str_new2(salt);
RB_GC_GUARD(prefix);
RB_GC_GUARD(input);
free(salt);
return str_salt;
}
|
.autodetect_cost(salt) ⇒ Object
Autodetects the cost from the salt string.
129 130 131 |
# File 'lib/bcrypt/engine.rb', line 129 def self.autodetect_cost(salt) salt[4..5].to_i end |
.calibrate(upper_time_limit_in_ms) ⇒ Object
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)
119 120 121 122 123 124 125 126 |
# File 'lib/bcrypt/engine.rb', line 119 def self.calibrate(upper_time_limit_in_ms) (BCrypt::Engine::MIN_COST..BCrypt::Engine::MAX_COST-1).each do |i| start_time = Time.now Password.create("testing testing", :cost => i+1) end_time = Time.now - start_time return i if end_time * 1_000 > upper_time_limit_in_ms end end |
.cost ⇒ Object
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.
32 33 34 |
# File 'lib/bcrypt/engine.rb', line 32 def self.cost @cost || DEFAULT_COST end |
.cost=(cost) ⇒ Object
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
49 50 51 |
# File 'lib/bcrypt/engine.rb', line 49 def self.cost=(cost) @cost = cost end |
.generate_salt(cost = self.cost) ⇒ Object
Generates a random salt with a given computational cost.
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/bcrypt/engine.rb', line 81 def self.generate_salt(cost = self.cost) cost = cost.to_i if cost > 0 if cost < MIN_COST cost = MIN_COST end if RUBY_PLATFORM == "java" Java.bcrypt_jruby.BCrypt.gensalt(cost) else __bc_salt("$2a$", cost, OpenSSL::Random.random_bytes(MAX_SALT_LENGTH)) end else raise Errors::InvalidCost.new("cost must be numeric and > 0") end end |
.hash_secret(secret, salt, _ = nil) ⇒ Object
Given a secret and a valid salt (see BCrypt::Engine.generate_salt) calculates a bcrypt() password hash. Secrets longer than 72 bytes are truncated.
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/bcrypt/engine.rb', line 55 def self.hash_secret(secret, salt, _ = nil) unless _.nil? warn "[DEPRECATION] Passing the third argument to " \ "`BCrypt::Engine.hash_secret` is deprecated. " \ "Please do not pass the third argument which " \ "is currently not used." end if valid_secret?(secret) if valid_salt?(salt) if RUBY_PLATFORM == "java" Java.bcrypt_jruby.BCrypt.hashpw(secret.to_s.to_java_bytes, salt.to_s) else secret = secret.to_s secret = secret.byteslice(0, MAX_SECRET_BYTESIZE) if secret && secret.bytesize > MAX_SECRET_BYTESIZE __bc_crypt(secret, salt) end else raise Errors::InvalidSalt.new("invalid salt") end else raise Errors::InvalidSecret.new("invalid secret") end end |
.valid_salt?(salt) ⇒ Boolean
Returns true if salt
is a valid bcrypt() salt, false if not.
98 99 100 |
# File 'lib/bcrypt/engine.rb', line 98 def self.valid_salt?(salt) !!(salt =~ /\A\$[0-9a-z]{2,}\$[0-9]{2,}\$[A-Za-z0-9\.\/]{22,}\z/) end |
.valid_secret?(secret) ⇒ Boolean
Returns true if secret
is a valid bcrypt() secret, false if not.
103 104 105 |
# File 'lib/bcrypt/engine.rb', line 103 def self.valid_secret?(secret) secret.respond_to?(:to_s) end |