Ruby bcrypt non-salt password comparison

347 Views Asked by At

I'm confused about bcrypt password hash retrieval and comparison. They are indeed equal, yet the comparison returns false. BTW I am using Sinatra, not RAILS.

My code has salt in it, but I can't even get non-salted to work. I can't see what's wrong here as it all outputs as being equivalent.

    require 'pg'
    require 'bcrypt'

    pw = 'trump_hairs'
    # salt = 'grains'           # not used for this trial

    # salty = pw + salt
    # salted = BCrypt::Password.create(salty)   
    hashed = BCrypt::Password.create(pw)    
    # p salted
    conn = PG.connect( dbname: 'alphaDB' )

    if true
        @res = conn.exec_params( 
            %q{ INSERT INTO USERS ( username, password, email, status) VALUES($1, $2, $3, $4) },
            ['peter', hashed, '[email protected]', 'on'] )
    end

    ######## this record works out just fine.  pw is a text field

    @res = conn.exec_params(
        %q{ SELECT password FROM users WHERE username = $1 },
        ['peter']   )
    r = @res.getvalue(0,0)

    puts BCrypt::Password.new(r)
    held = BCrypt::Password.new(r)
    p held
    p held.equal? hashed
    puts (hashed == held ? "success" : hashed)

I create the password and enter a record with hard-coded fields and the hashed password. I then perform a SELECT for the same username (no duplicates, so it's unique)(the boolean on the INSERT allows me to turn off the INSERT for repetitive SELECT trials), and I get the identical password hash.

Then it all falls apart. It won't compare to true. I have no idea why. I expected it to be true. The part I'm not getting past is from the bcrypt documentation:

my_password = BCrypt::Password.new("$2a$10$vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa")
my_password == "trump_hairs"     #=> true (my password inserted here)

I'm missing something, and would like to implement a salt as well. Cheers

2

There are 2 best solutions below

3
On

Upon further investigation, the BCrypt::Password methods and parent methods return different results:

    require 'bcrypt'

    @res = conn.exec_params(
        %q{ SELECT password FROM users WHERE username = $1 },
        ['peter']   )
    r = @res.getvalue(0,0)
    puts "r:" + r

    held = BCrypt::Password.new(r)      # Password
    puts "held: " + held
    puts held == pw                     # => true
    puts pw == held                     # => false
    puts held.eql?(pw)              # => false
    puts pw.eql?(held)              # => false
    puts held.equal?(pw)            # => false
    puts pw.equal?(held)            # => false
    puts held != pw                     # => false
    puts held === pw                    # => false
    puts
    puts held.inspect                   # => "$2a$10$mmTIpDHa9VZDrYZpRj9.x.R1/ihCEcjqwQZQbHsFwnn/tzDHtd9x6"

Results:

    r:$2a$10$mmTIpDHa9VZDrYZpRj9.x.R1/ihCEcjqwQZQbHsFwnn/tzDHtd9x6
    held: $2a$10$mmTIpDHa9VZDrYZpRj9.x.R1/ihCEcjqwQZQbHsFwnn/tzDHtd9x6
    true
    false
    false
    false
    false
    false
    false
    false

    "$2a$10$mmTIpDHa9VZDrYZpRj9.x.R1/ihCEcjqwQZQbHsFwnn/tzDHtd9x6"

So I've posted this to Github and noted that the first comparator, in that order, must be observed.

1
On

Actually BCrypt::Password overrides the == method. So in your code held == pw line returns true because it calls BCrypt's == method. When you use pw == held Ruby uses String's == method which returns false.

In that order if you want to check your hashes validness. You must use the BCyrpt::Password object on the left side.