Skip to content

Commit

Permalink
Modify vpuh function to take &str and return enum. (#5)
Browse files Browse the repository at this point in the history
* Modify `vpuh` function to take `&str` and return enum.

Options are either Verified/Failed, and Verified has an optional
String return value.

In the C-API code, we expand this to a enum of 3 values.

* Fix migrate_hash and tweak migration logic and testing.

* Increase test coverage.
  • Loading branch information
samscott89 committed Feb 17, 2018
1 parent 9c09a51 commit 410be82
Show file tree
Hide file tree
Showing 13 changed files with 236 additions and 107 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "libpasta"
version = "0.0.6"
version = "0.1.0-rc0"
authors = ["Sam Scott <[email protected]>"]
categories = ["authentication", "cryptography"]
description = "All-inclusive password hashing library"
Expand All @@ -18,7 +18,7 @@ fastpbkdf2 = "0.1"
itertools = "0.7"
lazy_static = "1.0"
log = "0.4"
num-traits = "0.1"
num-traits = "0.2"
ring-pwhash = ">= 0.11, < 0.13"
rpassword = "2.0"
rust-crypto = "0.2"
Expand All @@ -33,7 +33,7 @@ default-features = false
features = []

[dev-dependencies]
env_logger = "0.4"
env_logger = "0.5"
time = "0.1.36"

[features]
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION = 0.0.6
VERSION = 0.1.0-rc0

all: libpasta.so libpasta.a

Expand Down
31 changes: 19 additions & 12 deletions examples/migrate_password.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
extern crate libpasta;
use libpasta::rpassword::*;
use libpasta::HashUpdate;

#[derive(Debug)]
struct User {
Expand All @@ -10,18 +11,27 @@ struct User {
fn migrate_users(users: &mut [User]) {
// Step 1: Wrap old hash
for user in users {
libpasta::migrate_hash(&mut user.password_hash);
if let Some(new_hash) = libpasta::migrate_hash(&user.password_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();
if libpasta::verify_password_update_hash(&mut user.password_hash, &password) {
println!("Password correct, new hash: \n{}", user.password_hash);
} else {
println!("Password incorrect, hash unchanged: \n{}",
user.password_hash);

match libpasta::verify_password_update_hash(&user.password_hash, &password) {
HashUpdate::Verified(output) => {
if let Some(new_hash) = output {
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);
}
}
}

Expand All @@ -36,12 +46,9 @@ fn main() {
auth_user(&mut users[0]);
}

// Do not use this code as a good example of how to do hashing.
// This is intentionally awkward
use libpasta::{hashing, primitives};
extern crate serde_mcf;
use libpasta::{config, primitives};

fn deprected_hash(password: &str) -> String {
let alg = hashing::Algorithm::Single(primitives::Bcrypt::default());
serde_mcf::to_string(&alg.hash(password)).unwrap()
let config = config::Config::with_primitive(primitives::Bcrypt::default());
config.hash_password(password)
}
6 changes: 3 additions & 3 deletions libpasta-capi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
[package]
name = "pasta"
version = "0.0.6"
version = "0.1.0-rc0"
authors = ["Sam Scott <[email protected]>"]
build = "build.rs"


[dependencies]
libc = "0.2"
libpasta = { version = "0.0.6", path = ".." }
libpasta = { version = "0.1.0-rc0", path = ".." }
rpassword = "2.0"

[build-dependencies]
cbindgen = "0.3"
cbindgen = "0.4"

[lib]
crate-type = ["cdylib", "staticlib"]
4 changes: 2 additions & 2 deletions libpasta-capi/ctest/compile.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

set -ex

cargo build --release --manifest-path ../Cargo.toml
g++ -DDEBUG -std=c++11 -g -o test test.cpp -Wall -I../include -L../target/release -lpasta
cargo build --release --manifest-path ../Cargo.toml
g++ -DDEBUG -std=c++11 -ggdb -o test test.cpp -Wall -I../include -L../target/release/ -lpasta
27 changes: 18 additions & 9 deletions libpasta-capi/ctest/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,33 @@ void test_hash_and_verify() {
}

void test_migrate() {
char *hash = (char *)"$2a$10$vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa";
hash = migrate_hash(hash);
// printf("New hash: %s\n", hash);
char *old_hash = (char *)"$2a$10$vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa";
char *hash;
HashUpdateFfi *res = migrate_hash(old_hash);
switch(res->tag) {
case HashUpdateFfi::Tag::Updated: hash = res->updated._0; break;
case HashUpdateFfi::Tag::Ok: assert (false && "Expected a password migration");
case HashUpdateFfi::Tag::Failed: assert (false && "Problem migrating password");
}
assert (strcmp(old_hash, hash) != 0);
printf("New hash: %s\n", hash);
free_string(hash);

hash = (char *)"$2a$10$vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa";
char *newhash;
bool res = verify_password_update_hash_in_place(hash, "my password", &newhash);
assert (res);
// printf("New hash: %s\n", newhash);
res = verify_password_update_hash(hash, "my password");
switch(res->tag) {
case HashUpdateFfi::Tag::Updated: newhash = res->updated._0;
case HashUpdateFfi::Tag::Ok: printf("Password verified\n"); break;
case HashUpdateFfi::Tag::Failed: assert (false && "Password failed");
}
printf("New hash: %s\n", newhash);
assert (strcmp(newhash, hash) != 0);
assert (verify_password(newhash, "my password"));
// free_string(hash) // dont need to free this since it's static
free_string(newhash);

assert (!verify_password_update_hash_in_place(hash, "not my password", &newhash));
// printf("New hash: %s\n", newhash);
free_string(newhash);
assert (verify_password_update_hash(hash, "not my password")->tag == HashUpdateFfi::Tag::Failed);
}

void test_config() {
Expand Down
30 changes: 23 additions & 7 deletions libpasta-capi/include/pasta-bindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,34 @@ struct Config;
// arbitrary parameter sets is essential.
struct Primitive;

struct HashUpdateFfi {
enum class Tag {
Updated,
Ok,
Failed,
};

struct Updated_Body {
char *_0;
};

Tag tag;
union {
Updated_Body updated;
};
};

extern "C" {

char *config_hash_password(const Config *config, const char *password);

char *config_migrate_hash(const Config *config, const char *hash);
HashUpdateFfi *config_migrate_hash(const Config *config, const char *hash);

bool config_verify_password(const Config *config, const char *hash, const char *password);

bool config_verify_password_update_hash(const Config *config,
const char *hash,
const char *password,
char **new_hash);
HashUpdateFfi *config_verify_password_update_hash(const Config *config,
const char *hash,
const char *password);

Config *config_with_primitive(const Primitive *prim);

Expand All @@ -48,7 +64,7 @@ void free_string(char *s);

char *hash_password(const char *password);

char *migrate_hash(const char *hash);
HashUpdateFfi *migrate_hash(const char *hash);

Primitive *new_argon2i(unsigned int passes, unsigned int lanes, unsigned int kib);

Expand All @@ -60,6 +76,6 @@ char *read_password(const char *prompt);

bool verify_password(const char *hash, const char *password);

bool verify_password_update_hash_in_place(const char *hash, const char *password, char **new_hash);
HashUpdateFfi *verify_password_update_hash(const char *hash, const char *password);

} // extern "C"
107 changes: 72 additions & 35 deletions libpasta-capi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ extern crate libc;
extern crate libpasta;
extern crate rpassword;

use libpasta::HashUpdate;
use libpasta::primitives::*;
use libpasta::config::Config;

use libc::{c_char, c_uchar, c_uint};
use rpassword::prompt_password_stdout;

Expand All @@ -25,6 +29,29 @@ macro_rules! ffi_string {
)
}

macro_rules! box_ptr {
($x:expr) => (
Box::into_raw(Box::new($x))
)
}

#[repr(C)]
pub enum HashUpdateFfi {
Updated(*mut c_char),
Ok,
Failed,
}

impl From<HashUpdate> for HashUpdateFfi {
fn from(other: HashUpdate) -> Self {
match other {
HashUpdate::Verified(Some(x)) => HashUpdateFfi::Updated(CString::new(x).unwrap().into_raw()),
HashUpdate::Verified(None) => HashUpdateFfi::Ok,
HashUpdate::Failed => HashUpdateFfi::Failed,
}
}
}

#[no_mangle]
pub extern fn read_password(prompt: *const c_char) -> *mut c_char {
let prompt = unsafe { ffi_string!(prompt) };
Expand Down Expand Up @@ -74,87 +101,81 @@ pub extern "C" fn config_verify_password(config: *const Config, hash: *const c_c
}

#[no_mangle]
pub extern "C" fn verify_password_update_hash_in_place(hash: *const c_char, password: *const c_char, new_hash: *mut *mut c_char) -> bool {
let mut hash = unsafe { ffi_string!(hash).to_owned() };
pub extern "C" fn verify_password_update_hash(hash: *const c_char, password: *const c_char) -> *mut HashUpdateFfi {
let hash = unsafe { ffi_string!(hash) };
let password = unsafe { ffi_string!(password) };
let res = libpasta::verify_password_update_hash(&mut hash, password);
unsafe {
*new_hash = CString::new(hash).unwrap().into_raw();
}
res
box_ptr!(libpasta::verify_password_update_hash(hash, password).into())
}

#[no_mangle]
pub extern "C" fn config_verify_password_update_hash(config: *const Config, hash: *const c_char, password: *const c_char, new_hash: *mut *mut c_char) -> bool {
let config = unsafe { ffi_ref!(config) };
let mut hash = unsafe { ffi_string!(hash).to_owned() };
pub extern "C" fn config_verify_password_update_hash(config: *const Config, hash: *const c_char, password: *const c_char) -> *mut HashUpdateFfi {
let config = unsafe { ffi_ref!(config) };
let hash = unsafe { ffi_string!(hash) };
let password = unsafe { ffi_string!(password) };
let res = config.verify_password_update_hash(&mut hash, password);
unsafe {
*new_hash = CString::new(hash).unwrap().into_raw();
}
res
box_ptr!(config.verify_password_update_hash(hash, password).into())
}

// use libpasta::primitives::Primitive;
use libpasta::primitives::*;
use libpasta::config::Config;

#[no_mangle]
pub extern "C" fn migrate_hash(hash: *const c_char) -> *mut c_char {
let mut hash = unsafe { ffi_string!(hash).to_owned() };
libpasta::migrate_hash(&mut hash);
CString::new(hash).unwrap().into_raw()
pub extern "C" fn migrate_hash(hash: *const c_char) -> *mut HashUpdateFfi {
let hash = unsafe { ffi_string!(hash).to_owned() };
if let Some(new_hash) = libpasta::migrate_hash(&hash) {
box_ptr!(HashUpdateFfi::Updated(CString::new(new_hash).unwrap().into_raw()))
} else {
box_ptr!(HashUpdateFfi::Ok)
}
}

#[no_mangle]
pub extern "C" fn config_migrate_hash(config: *const Config, hash: *const c_char) -> *mut c_char {
pub extern "C" fn config_migrate_hash(config: *const Config, hash: *const c_char) -> *mut HashUpdateFfi {
let config = unsafe { ffi_ref!(config) };
let mut hash = unsafe { ffi_string!(hash).to_owned() };
config.migrate_hash(&mut hash);
CString::new(hash).unwrap().into_raw()
let hash = unsafe { ffi_string!(hash).to_owned() };
if let Some(new_hash) = config.migrate_hash(&hash) {
box_ptr!(HashUpdateFfi::Updated(CString::new(new_hash).unwrap().into_raw()))
} else {
box_ptr!(HashUpdateFfi::Ok)
}
}


#[no_mangle]
pub extern "C" fn config_with_primitive(prim: *const Primitive) -> *mut Config {
let prim = unsafe { ffi_ref!(prim) };
Box::into_raw(Box::new(Config::with_primitive(prim.clone())))
box_ptr!(Config::with_primitive(prim.clone()))
}

#[no_mangle]
pub extern "C" fn default_argon2i() -> *mut Primitive {
Box::into_raw(Box::new(Argon2::default()))
box_ptr!(Argon2::default())
}

#[no_mangle]
pub extern "C" fn default_bcrypt() -> *mut Primitive {
Box::into_raw(Box::new(Bcrypt::default()))
box_ptr!(Bcrypt::default())
}

#[no_mangle]
pub extern "C" fn default_pbkdf2i() -> *mut Primitive {
Box::into_raw(Box::new(Pbkdf2::default()))
box_ptr!(Pbkdf2::default())
}

#[no_mangle]
pub extern "C" fn default_scrypt() -> *mut Primitive {
Box::into_raw(Box::new(Scrypt::default()))
box_ptr!(Scrypt::default())
}

#[no_mangle]
pub extern "C" fn new_argon2i(passes: c_uint, lanes: c_uint, kib: c_uint) -> *mut Primitive {
Box::into_raw(Box::new(Argon2::new(passes, lanes, kib)))
box_ptr!(Argon2::new(passes, lanes, kib))
}

#[no_mangle]
pub extern "C" fn new_bcrypt(cost: c_uint) -> *mut Primitive {
Box::into_raw(Box::new(Bcrypt::new(cost)))
box_ptr!(Bcrypt::new(cost))
}

#[no_mangle]
pub extern "C" fn new_scrypt(log_n: c_uchar, r: c_uint, p: c_uint) -> *mut Primitive {
Box::into_raw(Box::new(Scrypt::new(log_n, r, p)))
box_ptr!(Scrypt::new(log_n, r, p))
}

#[no_mangle]
Expand All @@ -168,3 +189,19 @@ pub extern "C" fn free_Config(config: *mut Config) {

}

#[cfg(test)]
mod test {
use std::ffi::CString;

#[test]
fn test_migrate() {
unsafe {
let hash = "$2a$10$vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa";
let hash = CString::new(hash).unwrap().into_raw();
let password = "my password";
let password = CString::new(password).unwrap().into_raw();
let res = super::verify_password_update_hash(hash, password);
}

}
}
Loading

0 comments on commit 410be82

Please sign in to comment.