Class: BCrypt::Password
- Inherits:
-
String
- Object
- String
- BCrypt::Password
- Defined in:
- lib/bcrypt/password.rb
Overview
A password management class which allows you to safely store users’ passwords and compare them.
Example usage:
include BCrypt
# hash a user's password
@password = Password.create("my grand secret")
@password #=> "$2a$12$C5.FIvVDS9W4AYZ/Ib37YuWd/7ozp1UaMhU28UKrfSxp2oDchbi3K"
# store it safely
@user.update_attribute(:password, @password)
# read it back
@user.reload!
@db_password = Password.new(@user.password)
# compare it after retrieval
@db_password == "my grand secret" #=> true
@db_password == "a paltry guess" #=> false
Instance Attribute Summary collapse
-
#checksum ⇒ Object
readonly
The hash portion of the stored password hash.
-
#cost ⇒ Object
readonly
The cost factor used to create the hash.
-
#salt ⇒ Object
readonly
The salt of the store password hash (including version and cost).
-
#version ⇒ Object
readonly
The version of the bcrypt() algorithm used to create the hash.
Class Method Summary collapse
-
.create(secret, options = {}) ⇒ Object
Hashes a secret, returning a BCrypt::Password instance.
- .valid_hash?(h) ⇒ Boolean
Instance Method Summary collapse
-
#==(secret) ⇒ Object
(also: #is_password?)
Compares a potential secret against the hash.
-
#initialize(raw_hash) ⇒ Password
constructor
Initializes a BCrypt::Password instance with the data from a stored hash.
Constructor Details
#initialize(raw_hash) ⇒ Password
Initializes a BCrypt::Password instance with the data from a stored hash.
55 56 57 58 59 60 61 62 |
# File 'lib/bcrypt/password.rb', line 55 def initialize(raw_hash) if valid_hash?(raw_hash) self.replace(raw_hash) @version, @cost, @salt, @checksum = split_hash(self) else raise Errors::InvalidHash.new("invalid hash") end end |
Instance Attribute Details
#checksum ⇒ Object (readonly)
The hash portion of the stored password hash.
25 26 27 |
# File 'lib/bcrypt/password.rb', line 25 def checksum @checksum end |
#cost ⇒ Object (readonly)
The cost factor used to create the hash.
31 32 33 |
# File 'lib/bcrypt/password.rb', line 31 def cost @cost end |
#salt ⇒ Object (readonly)
The salt of the store password hash (including version and cost).
27 28 29 |
# File 'lib/bcrypt/password.rb', line 27 def salt @salt end |
#version ⇒ Object (readonly)
The version of the bcrypt() algorithm used to create the hash.
29 30 31 |
# File 'lib/bcrypt/password.rb', line 29 def version @version end |
Class Method Details
.create(secret, options = {}) ⇒ Object
Hashes a secret, returning a BCrypt::Password instance. Takes an optional :cost
option, which is a logarithmic variable which determines how computational expensive the hash is to calculate (a :cost
of 4 is twice as much work as a :cost
of 3). The higher the :cost
the harder it becomes for attackers to try to guess passwords (even if a copy of your database is stolen), but the slower it is to check users’ passwords.
Example:
@password = BCrypt::Password.create("my secret", :cost => 13)
43 44 45 46 47 |
# File 'lib/bcrypt/password.rb', line 43 def create(secret, = {}) cost = [:cost] || BCrypt::Engine.cost raise ArgumentError if cost > BCrypt::Engine::MAX_COST Password.new(BCrypt::Engine.hash_secret(secret, BCrypt::Engine.generate_salt(cost))) end |
.valid_hash?(h) ⇒ Boolean
49 50 51 |
# File 'lib/bcrypt/password.rb', line 49 def valid_hash?(h) /\A\$[0-9a-z]{2}\$[0-9]{2}\$[A-Za-z0-9\.\/]{53}\z/ === h end |
Instance Method Details
#==(secret) ⇒ Object Also known as: is_password?
Compares a potential secret against the hash. Returns true if the secret is the original secret, false otherwise.
Comparison edge case/gotcha:
secret = "my secret"
@password = BCrypt::Password.create(secret)
@password == secret # => True
@password == @password # => False
@password == @password.to_s # => False
@password.to_s == @password # => True
@password.to_s == @password.to_s # => True
secret == @password # => probably False, because the secret is not a BCrypt::Password instance.
78 79 80 81 82 83 84 85 86 87 |
# File 'lib/bcrypt/password.rb', line 78 def ==(secret) hash = BCrypt::Engine.hash_secret(secret, @salt) return false if hash.strip.empty? || strip.empty? || hash.bytesize != bytesize # Constant time comparison so they can't tell the length. res = 0 bytesize.times { |i| res |= getbyte(i) ^ hash.getbyte(i) } res == 0 end |