Basic Usage

Here we give an overview of the core functionality of libpasta. Examples can be viewed in different languages, with the full list of language support found in other languages.

The full Rust API documentation can be found here.

Password Hashes

A common scenario is that a particular user has password, which a service will check on each login to authenticate the user.

extern crate libpasta;

// We re-export the rpassword crate for CLI password input.
use libpasta::rpassword::*;

fn main() {
    let password = prompt_password_stdout("Please enter your password:").unwrap();
    let password_hash = libpasta::hash_password(&password);
    println!("The hashed password is: '{}'", password_hash);
}

The above code randomly generates a salt, and outputs the hash in the following format: $$scrypt-mcf$log_n=14,r=8,p=1$pfJFg/hVSthuA5l....

Details for how this is serialized can be found in the technical details chapter. This adheres to libpasta’s strong defaults principle.

However, for using libpasta one only needs to know that hash_password outputs a variable-length string.

Verifying passwords

Now that you have the hashed output, verifying that an inputted password is correct can be done as follows:

extern crate libpasta;
use libpasta::rpassword::*;

struct User {
    // ...
    password_hash: String,
}

fn auth_user(user: &User) {
    let password = prompt_password_stdout("Enter password:").unwrap();
    if libpasta::verify_password(&user.password_hash, &password) {
        println!("The password is correct!");
        // ~> Handle correct password
    } else {
        println!("Incorrect password.");
        // ~> Handle incorrect password
    }
}

Password migration

One of the key features of libpasta is the ability to easily migrate passwords to new algorithms.

Suppose we previously have bcrypt hashes in the following form: $2a$10$175ikf/E6E.73e83..... This a bcrypt hash, structured as $<bcrypt identifier>$<cost>$<salthash>.

libpasta includes a simple work flow for migrating passwords to a new algorithm (or new parameterization of an existing algorithm).
First, wrap existing hashes in the new algorithm to ensure their security immediately. Second, as users log in, update the wrapped hashes to just use the new algorithm. Wrapping simply takes an existing hash and re-hashes it with the new algorithm.

The following code first wraps an existing hash, and then a move to just using the new algorithm:

extern crate libpasta;
use libpasta::rpassword::*;
use libpasta::{migrate_hash, HashUpdate};

struct User {
    // ...
    password_hash: String,
}

fn migrate_users(users: Vec<&mut User>) {
    // Step 1: Wrap old hash
    for user in users {
        let hash = user.password_hash;
        if let HashUpdate::Verified(Some(new_hash)) = migrate_hash(hash) {
            user.password_hash = new_hash;
        }
    }
}

fn auth_user(user: &mut User) {
    // Step 2: Update algorithm during log in
    let password = prompt_password_stdout("Enter password:").unwrap();

    match libpasta::verify_password_update_hash(&user.password_hash, password) {
        HashUpdate::Verified(res) => {
            if let Some(new_hash) = res {
                user.password_hash = new_hash;
            }
            println!("Password correct, new hash: \n{}", user.password_hash);
        },
        HashUpdate::Failed => {
            println!("Password incorrect, hash unchanged: \n{}", user.password_hash);
        }
    }
}

In the first step, we do not need the user’s password (and can therefore apply this to all user passwords when desired). However, the password hash is now comprised of both a bcrypt computation AND an argon2 computation.

In the second step, if the user correctly enters their password, then a new hash is computed from scratch with a fresh salt using the new algorithm. This requires updating the stored version of the hash.

More detailed information of password migration can be found here.

Basic configuration

libpasta is designed to work out-of-the box with strong defaults. However, other configurations are supported through use of the Config object. In particular, this is necessary to use keyed functions.

This comes with the additional overheard that the config must be explicitly used.

For example, suppose we wish to use bcrypt with cost=15 as the default algorithm.

extern crate libpasta;

use libpasta::primitives::Bcrypt;
use libpasta::config::Config;

fn main() {
    let config = Config::with_primitive(Bcrypt::new(15));
    let password_hash = config.hash_password("hunter2");
    println!("The hashed password is: '{}'", password_hash);
    // Prints bcrypt hash
}

Additionally, values may be set using a configuration file. Written in YAML, these look as follows:

default: Custom
keyed: ~
primitive: 
  id: "scrypt"
  params: 
    ln: 11
    r: 8
    p: 1

This specifies the algorithm to use, in this case, scrypt. Similar to the above, to use this config use the Config::from_file method.

libpasta also has a parameter selection tool which can optionally output configuration values.