The C implementation for the PolyPasswordHasher password storage scheme. This repository provides a C library to manage a polypasswordhasher store.
A polypasswordhasher store uses Shamir shares to obscure the password's hashes in order to make them unfeasible to crack. More details about this scheme can be seen here
Included, you will find an automake-autoconf-libtool project to build the library, Instructions of how to build, and use.
A set of tests is also included. Tests are made for check. If you have check installed, running make check will run all of the test suites.
Building the library =====================In order to build this project, you will need to meet the following requirements:
-
The open ssl development libraries (libssl-dev)
-
The autoconf/automake/libtool binaries
-
If you are running the tests, you need the check framework.
In order to build, we have to setup the project by running:
autoreconf --install
./configure
make
After compiling, the unit tests can be run by using:
make check
After compiling, the library is easily installed by running:
make install
We do, however, recommend running the tests before installing.
The installation script will copy the header files to /usr/local/include and the shared library into /usr/local/lib.
Compiling a program with libpolypasswordhasher ======================================== After the installation has been done successfully, you can complile programs by including the header inside your C implementation (#include )Compilation is done the following way:
gcc -o polypasswordhasher_example.out polypasswordhasher_example.c -lcrypto -lpolypasswordhasher
Since the shared object is not installed in the standard library path, in order to run such programs you will need to export LD_LIBRARY_PATH before running, or copy the shared object into /usr/lib. The first alternative is suggested if you are not familiar with these types of installations:
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
./polypasswordhasher_example.out
In order to keep things simple, we are going to produce a vault with some accounts, store it, reload it and unlock it.
In order to explain some of the features of libpolypasswordhasher we will add both, threshold and shielded accounts. The library supports shielded accounts, which are, in essence, user accounts that cannot bootstrap the store (imagine normal users vs super users).
We will also configure the context for two isolated-check-bits. Isolated-check bits aid us in providing a login capability even if the store is locked. The store is usually locked upon reboot, since the shares are not stored anywhere in disk.
...
#include<libpolypasswordhasher.h>
...
// a context is the data structure that holds the information about the
// whole pph store
pph_context *context;
// Setting a theshold of two means that we are going to need two accounts
// to attempt bootstrapping.
uint8 threshold = 2;
// isolated-check-bits will be set to two, so users can login after any reboot
// event.
uint8 isolated_check_bits = 2;
// setup the context, this will generate us the shares, setup information
// needed to operate and initialize all of the data structures.
context = pph_init_context(threshold, isolated_check_bits);
// add some users, we send the context, a username, a password and a number
// of shares to assign to the user. The a user can have many shares, and count
// more towards the threshold.
pph_create_account(context, "Alice", strlen("Alice"),
"I.love.bob", strlen("I.love.bob"), 1);
pph_create_account(context, "Bob", strlen("Bob"),
"i.secretly.love.eve",strlen("i.secretly.love.eve"),1);
// when creating a user with no shares, we get a *shielded* account.
// Shielded accounts have their hash encrypted and are unable to
// bootstrap a context
pph_create_account(context,"Eve", strlen("Eve"),
"i'm.all.ears", strlen("i'm.all.ears"), 0);
// to check a login we must have an bootstrapped context, we send the credentials and
// receive an error in return
if(pph_check_login(context, "Alice", strlen("Alice"), "I.love.bob",
strlen("I.love.bob")) == PPH_ERROR_OK){
printf("welcome alice\n");
}else{
printf("generic error message\n");
}
// We can, then store a context to work with it later, have in mind the
// context will be stored in a locked state and alice and bob will have
// to bootstrap it.
pph_store_context(context,"securepasswords");
// We should destroy a context when we finish to free sensible data, such as
// the share information. The pph_destroy_context function ensures that all
// of the data structures associated with the context are properly freed.
pph_destroy_context(context);
// time goes by and we want to start working again, with the same information
// about alice, bob and eve...
// We reload our context, we reload a context from disk using
// pph_reload_context, providing a filename, remember that the obtained
// context is locked after loading from disk.
context = pph_reload_context("securepasswords");
// at this point we can still provide a login service, thanks to the isolated
// bytes extension. But in order to create accounts and to provide full login
// functionality, we should bootstrap the store.
if(pph_check_login(context, "Alice",strlen("alice"), "i'm.trudy",
strlen("i'm.trudy")) == PPH_ERROR_OK){
printf("welcome alice!\n"); // this won't happen
}else{
printf("go away trudy!\n");
}
// during the locked phase, we are unable to create **protector** account
if(pph_create_account(context, "trudy", strlen("trudy"), "I'm.trudy",
strlen("I'm.trudy"), 1) == PPH_CONTEXT_IS_LOCKED){
printf("Sorry, we cannot create accounts at this time\n");
}else{
printf("This shouldn't happen\n");
}
// But Nevertheless we can create *shielded* account
if(pph_create_account(context, "trudy", strlen("trudy"), "I'm.trudy",
strlen("I'm.trudy"), 0) == PPH_ERROR_OK){
printf("Trudy shielded account created\n");
}else{
printf("This shouldn't happen\n");
}
// In order to be able to create accounts, we must unlock the vault.
// for this, we setup an array of username strings and an array of password
// strings.
char **usernames = malloc(sizeof(*usernames)*2);
usernames[0] = strdup("Alice");
usernames[1] = strdup("Bob");
char **passwords = malloc(sizeof(*passwords)*2);
passwords[0] = strdup("I.love.bob");
passwords[1] = strdup("i.secretly.love.eve");
unsigned int *username_lengths = malloc(sizeof(*username_lengths)*2);
username_lengths[0] = strlen("Alice");
username_lengths[1] = strlen("bob");
unsigned int *password_lengths = malloc(sizeof(*password_lengths)*2);
password_lengths[0] = strlen("I.love.bob");
password_lengths[1] = strlen("i.secretly.love.eve");
// if the information provided was correct, the pph_unlock_password_data
// returns PPH_ERROR_OK, transitions to normal operation and recovers the shares.
pph_unlock_password_data(context, 2, usernames, username_lengths, passwords, password_lengths);
// now the data is unlocked. We can create accounts now.
pph_create_account(context, "carl", strlen("carl"), "verysafe",
strlen("verysafe"),0);
// we can now check accounts using the full feature also (non-isolated-check-bits)
if(pph_check_login(context, "carl", strlen("carl"), "verysafe",
strlen("verysafe")) == PPH_ERROR_OK){
printf("welcome back carl\n");
}else{
printf("you are not carl\n");
}
// we should now store the context and free the data before leaving
pph_store_context(context,"securepasswords");
pph_destroy_context(context);
free(password_lengths);
free(usernames);
free(passwords);
free(username_lengths);
====
### Context management functions.====
#### pph\_init\_context Initializes a polypasswordhasher context structure with everything needed in order to work. This is a one-time only initialization, pph_store_context and pph_reload_context will provide a persistent context after initialization. ##### parameters:-
Threshold : the minimum number of shares (or username accounts) to provide in order for it to transition from bootstrapping to normal operation.
-
isolated_check_bits: how many bytes are non-obscured by either the AES key or the shamir secret in order to provide isolated verification.
An initialized pph_context
====
#### pph\_destroy\_contextSafely destroy all of the references in an initialized pph_context.
- pph_context: the context to destroy.
An error code indicating whether the operation was successful or not.
====
#### pph\_store\_context Persist the non-sensitive information about a context to disk. Have in mind that certain parameters (such as the secret) are not written to disk. In other words, the context written to disk is stored in a locked state.-
context : the context to persist
-
filename : the name of the file to persist
An error code indicating whether the operation was successful or what was the reason for failure.
====
#### pph\_reload\_context After successfull context-storage, you can reload the context into memory by using this function. A reloaded context is locked until the pph_unlock_password_data function is called. A locked context may not operate for creating accounts, and can only verify logins if the isolated-check-bits argument provided was non-zero.- filename : the filename of the context to reload
A locked, but initialized, pph_context.
====
#### pph\_unlock\_password\_data. Provided a sufficient accounts (above the threshold), attempt to unlock the context data structure.-
context : the context to attempt transition with.
-
username_count : the number of accounts provided
-
usernames : an array of usernames to attempt secret-recombination
-
username_lengths: an array containing the length of each specific username
-
passwords : an array of passwords correspoding to each username in the same index
An error indicating if the attempt was successful or not.
====
### User Management Functions====
#### pph\_create\_account Given some credentials and an normal-operating context, store the user data inside a context.-
context : the context in which the user will be added
-
username : the username field, if it already exists, the system will throw an error
-
username_length : the length of the username field.
-
password : the password for that secific user.
-
password_length : the length of the password field
An error indicating whether the account could be added, or the cause of failure. Too long usernames and passwords will return an error, as well as an already existing account.
====
#### pph\_check\_login Provided a username and password pair, check if such pair exists within th context.-
context : the context that stores the account information.
-
username : the username to look for.
-
username_length : the length of the username provided
-
password : the password attempt for such username
-
password_length : the length of the password field provided.
An error code indicating if the login attempt was successful.
====
### Other functions====
#### PHS Hash an input string with the given arguments.This function is a mere demonstration of the resulting hashes inside a polypasswordhasher store. The motivation behind this function is to showcase the safety of the protector passwords. This function is aimed for the Password Hashing Competition.
-
void * out: The output hash
-
size_t outlen: the length of the produced string
-
void * in: The input password
-
size_t inlen: the length of the input password
-
void * salt: A salt input
-
size_t saltlen: The length of the input salt
-
int tcost: a time cost, in this case, this parameter modifies the threshold of the produced context directly.
-
int mcost: a memory/ other cost parameter
An error code indicating if the hash procedure was successful. If the function is successful, the resulting hash will be placed in the out buffer.
====