Announcing argonautica, a crate for idiomatic argon2 password hashing in Rust


#1

I just published a new crate: argonautica, which makes argon2 password hashing simple and easy in Rust.

Argon2 won the Password Hashing Competition in 2015, a several year project to identify a successor to bcrypt, scrypt, and other common cryptographically-secure hashing algorithms.

The main focus of the crate is a simple and easy-to-use API, but…

argonautica also provides three things that other argon2 hashing crates currently lack:

  • The ability to hash with a secret key (which not even the C implementation exposes publicly - it’s in the code; just not in the public API)
  • Use of SIMD on stable (I know we’re getting SIMD in 1.27, but this crate uses SIMD in it’s C code; so it will run on 1.26 as well)
  • The latest argon2 variant: argon2id

Hashing example:

extern crate argonautica;

use argonautica::Hasher;

fn main() {
    let mut hasher = Hasher::default();
    let hash = hasher
        .with_password("P@ssw0rd")
        .with_secret_key("\
            secret key that you should really store in a .env file \
            instead of in code, but this is just an example\
        ")
        .hash()
        .unwrap();

    println!("{}", &hash);
    // 👆 prints a hash, which will be random since the default Hasher uses a random salt
}

Verifying example:

extern crate argonautica;

use argonautica::Verifier;

fn main() {
    let mut verifier = Verifier::default();
    let is_valid = verifier
        .with_hash("
            $argon2id$v=19$m=4096,t=192,p=4$\
            o2y5PU86Vt+sr93N7YUGgC7AMpTKpTQCk4tNGUPZMY4$\
            yzP/ukZRPIbZg6PvgnUUobUMbApfF9RH6NagL9L4Xr4\
        ")
        .with_password("P@ssw0rd")
        .with_secret_key("\
            secret key that you should really store in a .env file \
            instead of in code, but this is just an example\
        ")
        .verify()
        .unwrap();
    assert!(is_valid);
}

#2

Nice! Bringing an entirely different sort of safety to the Rust ecosystem!

I saw in your documentation (after quite a lot of scrolling, there was a lot! :heart:) that you wrap the reference C implementation.

Out of curiosity: how do you manage to expose something that is not public in the inner C API, as public in Rust? (The hashing-with-secret-key you repeatedly trumpet)


#3

So there are essentially two ways to hash (publicly) with the C API.

The “high level” API give you a string-encoded hash, which includes a bunch of stuff: the hash bytes encoded a certain way, the salt bytes encoded a certain way, and information about some of the parameters you used (again encoded a certain way). You need all this information to later verify. That API doesn’t let you use a secret key.

The “low level” API lets you use a secret key, but just gives you raw hash bytes back. So, ultimately, by “expose something the C API doesn’t expose publicly,” all I really did is look at how the encoding in the C code (not exposed publicly) worked (how it packaged up the raw hash bytes, raw salt bytes, and those various parameters into a string) and rewrote that in Rust.

Said more simply (maybe), the only missing piece in the C API that was preventing folks from getting (publicly) a string-encoded hash while using a secret key was the (private) encoding function, which was easy to re-write in Rust.