RubyPlus Unplugged : BCrypt Gem

Introduction

I was getting the BCrypt::Errors::InvalidHash: invalid hash error when I forgot to declare the has_secure_password in the model for a Rails project. This is covered in the episode on Authentication from Scratch. The error message did not make any sense to me. The questions are:

  1. How can I reproduce this problem?
  2. What error message would be meaningful and helpful for the developers using the library?

Reproducing the Problem

After reading the source code for bcrypt gem I found a way to reproduce the problem.

require 'bcrypt'
 => true
> BCrypt::Password.new(nil)
BCrypt::Errors::InvalidHash: invalid hash
    from /Users/bparanj/.rvm/gems/ruby-2.3.1@rails5/gems/bcrypt-3.1.11/lib/bcrypt/password.rb:60:in `initialize'

What is hash? Is it the data structure? It is confusing error message because a developer looking at this error message on the browser might thing it is the data structure. Looking at the source code, we can see what a hash means:

def valid_hash?(h)
  h =~ /^\$[0-9a-z]{2}\$[0-9]{2}\$[A-Za-z0-9\.\/]{53}$/
end

This tells us that the word hash has a different meaning is in the context of security. According to the Wikipedia:

A cryptographic hash function is a hash function which takes an input (or 'message') and returns a fixed-size alphanumeric string. The string is called the 'hash value', 'message digest', 'digital fingerprint', 'digest' or 'checksum').

Creating a Hash

We can create a hash for a password:

 h = BCrypt::Password.create("test")
 => "$2a$10$3pQfd7SpGKlOE7mfotr5POa5iiF4gFHaUya1FunlWkEOj5z23lydC"

We can now compare it with the stored hash to see if the user entered the correct password:

 BCrypt::Password.new(h) == 'test'
 => true

The nil is not a valid hash because it is not a valid alphanumeric string. In the bcrypt gem implementation, you can see the constructor checks for a valid hash:

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

We must raise an exception saying 'Password digest cannot be nil'. This message makes it clear to the developer why the gem is crashing. In a Rails project, if you check the user record, you will find that the password_digest is nil. If you are interested in contributing to open source. Write a failing test and fix this issue and send a pull request to the author of the gem. Contributing to open source can be as simple as knowing the best practices and making them better by applying them.


Related Articles


Ace the Technical Interview

  • Easily find the gaps in your knowledge
  • Get customized lessons based on where you are
  • Take consistent action everyday
  • Builtin accountability to keep you on track
  • You will solve bigger problems over time
  • Get the job of your dreams

Take the 30 Day Coding Skills Challenge

Gain confidence to attend the interview

No spam ever. Unsubscribe anytime.