Class: BCrypt::Engine

Inherits:
Object
  • Object
show all
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.

10
MIN_COST =

The minimum cost supported by the algorithm.

4
MAX_SALT_LENGTH =

Maximum possible size of bcrypt() salts.

16

Class Method Summary collapse

Class Method Details

.__bc_crypt(key, setting) ⇒ Object

Given a secret and a salt, generates a salted hash (which you can then store safely).



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
# File 'ext/mri/bcrypt_ext.c', line 29

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) return Qnil;

    out = rb_str_new(data, size - 1);

    xfree(data);

    return out;
}

.__bc_salt(prefix, count, input) ⇒ Object

Given a logarithmic cost parameter, generates a salt for use with bc_crypt.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'ext/mri/bcrypt_ext.c', line 9

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);
    xfree(salt);

    return str_salt;
}

.autodetect_cost(salt) ⇒ Object

Autodetects the cost from the salt string.



118
119
120
# File 'lib/bcrypt/engine.rb', line 118

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)


108
109
110
111
112
113
114
115
# File 'lib/bcrypt/engine.rb', line 108

def self.calibrate(upper_time_limit_in_ms)
  40.times 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

.costObject

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.



22
23
24
# File 'lib/bcrypt/engine.rb', line 22

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            #=> 10
BCrypt::Password.create('secret').cost  #=> 10

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


39
40
41
# File 'lib/bcrypt/engine.rb', line 39

def self.cost=(cost)
  @cost = cost
end

.generate_salt(cost = self.cost) ⇒ Object

Generates a random salt with a given computational cost.



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/bcrypt/engine.rb', line 69

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
      prefix = "$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW"
      __bc_salt(prefix, 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.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/bcrypt/engine.rb', line 45

def self.hash_secret(secret, salt, _ = nil)
  if valid_secret?(secret)
    if valid_salt?(salt)
      if RUBY_PLATFORM == "java"
        # the native C bcrypt implementation used by the MRI version of this gem
        # interprets secret as a null-terminated string; to ensure consistency,
        # we drop after any null byte
        trimmed = secret.to_s.split("\0").first # drop after null byte to emulate C

        # the java hashpw() method taking byte[] expects the null terminator
        bytes = (trimmed.to_s + "\0").to_java_bytes # add null byte for byte[] API
        Java.bcrypt_jruby.BCrypt.hashpw(bytes, salt.to_s)
      else
        __bc_crypt(secret.to_s, 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.

Returns:

  • (Boolean)


87
88
89
# File 'lib/bcrypt/engine.rb', line 87

def self.valid_salt?(salt)
  !!(salt =~ /^\$[0-9a-z]{2,}\$[0-9]{2,}\$[A-Za-z0-9\.\/]{22,}$/)
end

.valid_secret?(secret) ⇒ Boolean

Returns true if secret is a valid bcrypt() secret, false if not.

Returns:

  • (Boolean)


92
93
94
# File 'lib/bcrypt/engine.rb', line 92

def self.valid_secret?(secret)
  secret.respond_to?(:to_s)
end