passwors 0.1.0

Passwors is a simple password handling library that utilises Rust's type system to enfore better password handling. Use it as a basic building block for more complex authentication systems. let pw = ClearTextPassword::from_string(some_password_source).unwrap(); let salt = HashSalt::new().unwrap(); // You should grab this from your database. let a2hash = Argon2PasswordHasher::default(); let pw_hash = pw.hash(&a2hash, &salt); assert_eq!(pw_hash, stored_hash, "Login failed!");

extern crate passwors;

use passwors::*;
use std::collections::HashMap;



// This is just a simplified version of what
// your database entries might look like.
// You don't need much more than that.
pub struct User {
    pub name:     String,
    pub password: HashedPassword,
    pub salt:     HashSalt,
    pub hasher:   Argon2Settings,
}



// You might actually want to use proper databases
// like SQLite instead, but this is more than enough
// to show the basic concepts.
pub struct UserDB {
    all_users: HashMap<String, User>,
}

impl UserDB {
    pub fn new() -> UserDB {
        UserDB { all_users: HashMap::new() }
    }

    // Usually, you would use a numerical ID as keys
    // to the user entries, but this makes the example
    // way simpler.
    pub fn add_user(&mut self, user: User) {
        let name = user.name.clone();
        self.all_users.insert(name, user);
    }

    // Now, to upgrade hasher settings in case we
    // changed them, we'd obviously need a mutable
    // reference. However, this is way beyond this
    // example.
    pub fn find_user(&self, name: &str) -> Option<&User> {
        self.all_users.get(name)
    }
}



fn main() {
    let mut db = UserDB::new();

    // Let's first add a new user with really
    // shitty login credentials.
    {   let hasher  = Argon2Settings::new().try_create().unwrap();
        let salt    = HashSalt::new().unwrap();
        let pw_hash = {
            // Every time you use such passwords, a kitten dies.
            // ... I'm sorry, poor kitten. :,(
            let pw = ClearTextPassword::from_string("1234qwerasdfzxcv".to_owned()).unwrap();
            pw.hash(&hasher, &salt)
        };
        db.add_user(User {
            name:     "admin".to_owned(),
            password: pw_hash,
            salt:     salt,
            hasher:   hasher.settings(),
        });
    }

    // Now, a user wants to log in using these credentials:
    //   - name:     `admin`
    //   - password: `1234qwerasdfzxcv`
    // This is, how we try to log the user in:

    // First, we actually have to look up whether a user
    // with that name really exists. If he doesn't, you
    // might want to return a banned dummy user instead
    // to complicate timing attacks.
    let user     = db.find_user("admin").unwrap();
    let password = "1234qwerasdfzxcv".to_owned();

    // Now that we have our user, we have to re-create his
    // login settings to hash the password.
    let hasher  = user.hasher.try_create().unwrap();
    let pw_hash = {
        // This takes and destroys the string. You
        // can also create clear text passwords from
        // byte vectors, byte slices, or from readers.
        let pw = ClearTextPassword::from_string(password).unwrap();
        pw.hash(&hasher, &user.salt)
    };

    assert_eq!(user.password, pw_hash);

    // Now, if your application detects that the user's
    // hasher settings are different from your current
    // default, you would actually hash the password twice,
    // the second time using the new settings. You would
    // then simply replace the old password hash with the
    // new one, et voilá, you have successfully upgraded
    // user passwords without them even noticing.
}