Hashing sensitive data in Rust with argon2rs

Argon2 is the password hashing algorithm that won the Password Hashing Competition in 2015. It is comprised of 2 main versions: Argon2i, which is optimised to resist side-channel attacks; and the version we’re interested in, Argon2d, which is optimised to resist GPU cracking attacks.

The argon2rs crate is a pure rust implementation of Argon2, which supports both of the above versions.

We’ll be using argon2rs’ default values for the number of passes etc., so the only things we need to supply are the data to hash and salt. If you’re not familiar, “salt” refers to random data used in one-way hashing functions — i.e. hashing functions that are too expensive to invert to be practical for attackers — to defend against dictionary attacks and pre-computed rainbow table attacks.

A common mistake when hashing data is using the same salt every time. By doing so, all same-value data that is input yields the same hashed value in the database, effectively negating the work the salt was there to do in the first place. For example, if 2 users use rustacean as their password then the value of password_hash in the database will be the same for both users in the database. Not good.

Generating a unique salt each time is fine and dandy, but at some point we’re going to need to verify the hashed data, say a password, against data the user has input into the system. When that time comes, we’re going to need access to the random salt that was used for the original hash, meaning we need to store it in the database too.

I’m sure you can see the problem with this. By storing salt in the database, we’re back in a situation where using salt in the first place becomes pointless should the database become compromised.

The solution is to not actually hash the data with the random salt directly, but to first hash the random salt with static salt stored locally, then hash the data with the hashed value of the random salt.

In doing this, both the database and the file system would need to be compromised for the aforementioned kinds of attack to be practical.

Enough theory then, let’s take a look at how to use the argon2rs crate to achieve this.

We’re going to have 3 dependencies: argon2rs, of course, for hashing; dotenv for storing a local salt; and rand for generating random salt. Add them to your Cargo.toml:

Let’s start by setting up a local salt. At the highest level in your project’s directory, create a file named .env and add a LOCAL_SALT environment variable. Make sure you add .env to your .gitignore too.

At this point, we’ve finished preparing the data for the database. I’m just printing the values, but you’ll want to take the values of data_hash_storable_encoding and random_salt and add them to whatever it concerns in whichever database system you’re using.

Now to add a way to compare user-inputted data to the hash in the database:

And there we have it. You should now be able to pass the same data into this system twice and wind up with 2 different hash values in your database.

I’m no security expert, but from what I’ve gleaned in my research this approach to hashing is a good one. If you are an expert and find something wrong with it, please do reach out. I’ll update this post accordingly.

Happy Rusting.

Thanks for reading! If you liked this post, you may like others archived in: Rust, Security. You can keep up to date with new posts by subscribing to the RSS Feed or by following me on Micro.blog.