Ruby cryptographic gems

Published on

I'm not an expert in cryptography — I'm just a developer, and most developers are in this same boat of not being experts in cryptography. This doesn't mean we're negligent by not studying this field, this just means it isn't our area of expertise. What is negligent, however, is for someone like us to roll out their own cryptographic algorithms or libraries without knowing what they're doing.

Thus, never roll your own cryptography unless you absolutely know what you're doing. Because of that, I want to explore some cryptography gems that awesome people brought to the Ruby ecosystem.

bcrypt

The first one is bcrypt. It dals with a very common use case: storing user passwords in databases and checking them when signing a user in.

Here is some code demonstrating its usage. Explanation comes below.

The user_input variable is some password the user has input. Perhaps through a sign in form or through a account creation form. Let's assume the latter scenario for now.

So, the user is creating an account and they submitted the password they wish to use later to sign in. How to store such password in the database?

pwd = BCrypt::Password.create(user_input)

It is this simple. Now, just store pwd.to_s in a text field for the user password. If you check the contents of pwd.to_s, you'll notice it looks something like $2a$12$ccaJDXyKniehBeYgZM4wDOl91.zctTI03qPhOlDGVk5KZ1qcC9Hge. The value for you will likely be different, even though the password is the same, because there is a salt in the mix. Just to be clear: that funny string is the hashed version of the password.

Now, what to do with that funny string? You use it when the user is signing in, to check whether they typed the correct password. Remember that what will be stored in the "password" field in the database will be that funny value. So let's say a user is trying to sign in. They provide you their email address and the password. With the email address, you can fetch the entity in the database that has their hashed password (the funny string).

It's then just a matter of comparing the hashed password to what the user provided in the sign in form:

# User provided this in the sign in form
user_input = '9fn837nf'

# The hashed password as stored in your database
pwd_in_database = '$2a$12$ccaJDXyKniehBeYgZM4wDOl91.zctTI03qPhOlDGVk5KZ1qcC9Hge'

pwd = BCrypt::Password.new(pwd_in_database)
if pwd == user_input
  puts('user sign in successfully')
else
  puts('wrong password')
end

rbnacl

The other gem I want to explore is rbnacl. This gem provides general purpose cryptography for many different scenarios and algorithms. They do so in a simplified way so that mortals like us don't have to become cryptography experts. Check out these docs to see what I'm talking about!

Symmetric encryption

Say you want to have a key that allows to encrypt some data and later to decrypt that data. It's easy as:

# Generates a random key with the correct length
key = RbNaCl::Random.random_bytes(RbNaCl::SecretBox.key_bytes)

sbox = RbNaCl::SimpleBox.from_secret_key(key)
encrypted_data = sbox.encrypt('hello world!')

You'll have to store the key somewhere safe. Note that it is a binary string, so you might want to base64-encode it to send it around. I generated the key that when base64-encoded is: nveFTikebVaqd4SMzCZ5P7BAKv6BeNwAqowTFkJbHjY=.

The encrypted_data is also binary data. Encoding it in base64, it turns into:

LbmIxONTUZXQGzGQwK1gB29H0OxoS8bn0GE/QAzJXt/8K9WNFnaz6XX3F0ecFwZRb9mm9Q==

Now, to decrypt the data, one only needs that key and the encrypted payload. Let's take a look:

require 'rbnacl'
require 'base64'

encrypted_payload = Base64.decode64(
  'LbmIxONTUZXQGzGQwK1gB29H0OxoS8bn0GE/QAzJXt/8K9WNFnaz6XX3F0ecFwZRb9mm9Q==')
key = Base64.decode64('nveFTikebVaqd4SMzCZ5P7BAKv6BeNwAqowTFkJbHjY=')

sbox = RbNaCl::SimpleBox.from_secret_key(key)
data = sbox.decrypt(encrypted_payload)

puts(data)

This script will print hello world!, which was the original message.

I didn't use replit here because rbnacl requires libsodium to be installed in the OS and I didn't manage to get that installed in replit. The folks at rbnacl provide some help on how to install that.

That's a very simple way of encrypting data without having much knowledge about cryptography. The gem also provides ways to have more control on the encryption, like choosing the encryption algorithm. But then you'll have to dig by yourself in the documentation — which by the way are just great.