}END_TEST // we test in the core functionality to avoid a threshold of 0, which // is the only non possible value (everyting bigger would roll over) START_TEST(test_pph_init_context_wrong_threshold) { // a placeholder for the result. pph_context *context; uint8 threshold = 0; // this is, obviously, the only wrong threshold value uint8 partial_bytes = 0;// this function is part of the non-partial bytes context = pph_init_context(threshold, partial_bytes); ck_assert_msg(context == NULL, "the context returned upon a wrong threshold value should be NULL"); // test for over-extension of the threshold value threshold =MAX_NUMBER_OF_SHARES+1; context = pph_init_context(threshold, partial_bytes); ck_assert_msg(context == NULL); }
END_TEST // Test an initialization with proper values, asking for 0 partial bytes. START_TEST(test_pph_init_context_no_partial_bytes) { pph_context *context; PPH_ERROR error; // set the correct threshold and partial bytes this time uint8 threshold = 2; uint8 partial_bytes = 0; //test for a over-extended value for partial bytes first context = pph_init_context(threshold,DIGEST_LENGTH+1); ck_assert_msg(context == NULL); context = pph_init_context(threshold,partial_bytes); ck_assert_msg(context != NULL, "this was a good initialization"); error = pph_destroy_context(context); ck_assert_msg(error == PPH_ERROR_OK, "the free function didn't work properly"); }
END_TEST // This checks for a proper behavior when providing an existing username, // first, as the first and only username, then after having many on the list START_TEST(test_check_login_proper_data) { PPH_ERROR error; pph_context *context; uint8 threshold = 2; uint8 partial_bytes = 0; unsigned char password[] = "i'mnothere"; unsigned char username[] = "nonexistentpassword"; unsigned char anotheruser[] = "0anotheruser"; unsigned int i; // setup the context context = pph_init_context(threshold,partial_bytes); ck_assert_msg(context != NULL, "this was a good initialization, go tell someone"); // add a single user and see how it behaves: // 1) add a user error = pph_create_account(context, username, strlen(username), password, strlen(password), 1); ck_assert_msg(error == PPH_ERROR_OK, " this shouldn't have broken the test"); // 2) ask for it, providing correct credentials error = pph_check_login(context, username, strlen(username), password, strlen(password)); ck_assert_msg(error == PPH_ERROR_OK, "expected OK"); // lets add a whole bunch of users and check for an existing one again // 1) add a whole new bunch of users: for(i=1;i<9;i++) { anotheruser[0] = i+48; error = pph_create_account(context, anotheruser, strlen(anotheruser), "anotherpassword", strlen("anotherpassword"), 1); ck_assert_msg(error == PPH_ERROR_OK, " this shouldn't have broken the test"); } // 2) ask again, with the correct password error = pph_check_login(context, username, strlen(username), password, strlen(password)); ck_assert_msg(error == PPH_ERROR_OK, "expected ERROR_OK"); pph_destroy_context(context); }
END_TEST // test input sanity on the store function START_TEST(test_pph_store_context_input_sanity) { PPH_ERROR error; pph_context *context; uint8 threshold = 2; uint8 partial_bytes = 0; unsigned int i; // wrong context pointer test error = pph_store_context(NULL,"pph.db"); ck_assert_msg(error == PPH_BAD_PTR, " expected BAD_PTR"); // initialize a context because we are going to give a valid pointer from now // on context = pph_init_context(threshold, partial_bytes); // wrong filename error = pph_store_context(context, NULL); ck_assert_msg(error == PPH_BAD_PTR, " expected BAD_PTR"); // correct data error = pph_store_context(context, "pph.db"); ck_assert_msg(error == PPH_ERROR_OK," expected ERROR_OK"); pph_destroy_context(context); }END_TEST
END_TEST // Test create accounts with thresholdless accounts and partial bytes. START_TEST(test_pph_create_accounts) { PPH_ERROR error; pph_context *context; uint8 threshold = 2; uint8 partial_bytes = 2; unsigned char password[] = "verysecure"; unsigned char username[] = "atleastitry"; uint8 password_digest[DIGEST_LENGTH]; unsigned int i; context = pph_init_context(threshold, partial_bytes); ck_assert_msg(context != NULL, "this was a good initialization, go tell someone"); // create a thresholdless account with partial bytes. error = pph_create_account(context, username, strlen(username), password, strlen(password), 0); ck_assert_msg(error == PPH_ERROR_OK, "We should have gotten PPH_ERROR_OK in the return value"); // check the username matches. ck_assert_str_eq(username,context->account_data->account.username); // now lets check there are collisions between threshold and thresholdless // accounts error = pph_create_account(context, username, strlen(username), password, strlen(password), 1); ck_assert_msg(error == PPH_ACCOUNT_EXISTS, "We should have gotten an error since this account repeats"); // finally, check it returns the proper error code if the vault is locked // still, we will set the key to null and the locked flag to 0 to simulate // this. context->is_unlocked = false; context->AES_key = NULL; // we will check for the existing account error handler now... error = pph_create_account(context, "someotherguy", strlen("someotherguy"), "came-here-asking-the-same-thing",strlen("came-here-asking-the-same-thing"), 0); ck_assert_msg(error == PPH_CONTEXT_IS_LOCKED, "We should have gotten an error now that the vault is locked"); error = pph_destroy_context(context); ck_assert_msg(error == PPH_ERROR_OK, "the free function didn't work properly"); }
END_TEST // this test attempts to create full ranged usernames and passwords and check // the login procedures at the same time. START_TEST(test_pph_create_and_check_login_full_range) { pph_context *context; uint8 username_buffer[MAX_USERNAME_LENGTH]; uint8 password_buffer[MAX_PASSWORD_LENGTH]; unsigned int i; uint8 threshold = 2; uint8 partial_bytes = 0; PPH_ERROR error; context = pph_init_context( threshold, partial_bytes); ck_assert(context != NULL); // we will iterate all of the lengths of the username fields and generate // random string users. for( i = 1; i < MAX_USERNAME_LENGTH; i++){ // generate a username and a password of length i get_random_bytes(i, username_buffer); get_random_bytes(i, password_buffer); // create an account with those credentials error = pph_create_account( context, username_buffer, i, password_buffer, i, 1); ck_assert( error == PPH_ERROR_OK ); // check the login of the newly created account error = pph_check_login( context, username_buffer, i, password_buffer, i); ck_assert( error == PPH_ERROR_OK); // now invert the first byte of the password so we can't login password_buffer[0] = ~password_buffer[0]; error = pph_check_login( context, username_buffer, i, password_buffer, i); ck_assert( error != PPH_ERROR_OK); } pph_destroy_context(context); }END_TEST
END_TEST // We check for both, thresholdless accounts and threshold accounts under // partial bytes setup. START_TEST(test_create_account_mixed_accounts) { PPH_ERROR error; pph_context *context; uint8 threshold = 2; uint8 partial_bytes = 2; unsigned char password[] = "verysecure"; unsigned char username[] = "atleastitry"; context = pph_init_context(threshold, partial_bytes); ck_assert_msg(context != NULL, "this was a good initialization, go tell someone"); // create a thresholdless account. error = pph_create_account(context, username, strlen(username), password, strlen(password), 0); // THL account. ck_assert_msg(error == PPH_ERROR_OK, "We should've gotten PPH_ERROR_OK in the return value"); // check that we get a valid username for this. ck_assert_str_eq(username,context->account_data->account.username); // now let's create a bunch of accounts with thresholds this time error = pph_create_account(context, "johhnyjoe", strlen("johhnyjoe"), "passwording", strlen("passwording"),1); ck_assert_msg(error == PPH_ERROR_OK, "We should have gotten PPH_ERROR_OK in the return value"); error = pph_create_account(context, "richardWalkins", strlen("richardWalkins"), "i'm-unreliable",strlen("i'm-unreliable"),5); ck_assert_msg(error == PPH_ERROR_OK, "We should have gotten PPH_ERROR_OK in the return value"); pph_destroy_context(context); }
END_TEST // this test is intended to check correct sanity check on the username field START_TEST(test_create_account_usernames) { pph_context *context; PPH_ERROR error; uint8 threshold = 2; uint8 partial_bytes = 0; unsigned char username[MAX_USERNAME_LENGTH+1]; unsigned int i; // we will simulate a really big username, terminated with a null character // to get an incorrect "length" parameter for(i=0;i<MAX_USERNAME_LENGTH;i++) { username[i] = 'k'; } username[MAX_USERNAME_LENGTH] = '\0'; // initialize a correct context from scratch context = pph_init_context(threshold,partial_bytes); // sending bogus information to the create user function. error = pph_create_account(context, username, strlen(username), "yessir,verysecure", strlen("yessir,verysecure"), 1); ck_assert_msg(error == PPH_USERNAME_IS_TOO_LONG, "We should've gotten USERNAME_IS_TOO_LONG in the return value"); error = pph_destroy_context(context); ck_assert_msg(error == PPH_ERROR_OK, "the free function didn't work properly after failing to add a user"); }
END_TEST // we check that destroy context is working too. This shouldn't be a problem. START_TEST(test_pph_destroy_context_partial_bytes) { pph_context *context; PPH_ERROR error; uint8 threshold = 2; uint8 partial_bytes = 2; context = pph_init_context(threshold, partial_bytes); ck_assert_msg(context != NULL, " shouldn't break here"); error = pph_destroy_context(context); ck_assert(error == PPH_ERROR_OK); }
END_TEST // this test is intended to check correct sanity checks on the password fields START_TEST(test_create_account_passwords) { PPH_ERROR error; pph_context *context; uint8 threshold = 2; uint8 partial_bytes = 0; unsigned char password[MAX_PASSWORD_LENGTH+1]; unsigned int i; context = pph_init_context(threshold, partial_bytes); ck_assert_msg(context != NULL, "this was a good initialization, go tell someone"); for(i=0;i<MAX_PASSWORD_LENGTH;i++) { password[i] = 'k'; // endless string of k's } password[MAX_PASSWORD_LENGTH] = '\0'; // sending bogus information to the create user function. error = pph_create_account(context, "ichooseverylongpasswords", strlen("ichooseverylongpasswords"),password,strlen(password),1); ck_assert_msg(error == PPH_PASSWORD_IS_TOO_LONG, "We should've gotten PPH_PASSWORD_IS_TOO_LONG in the return value"); error = pph_destroy_context(context); ck_assert_msg(error == PPH_ERROR_OK, "the free function didn't work properly"); }
}END_TEST // do a full lifecycle test, in other words, create a context with accounts, // store it, reload it, unlock it and provide login and creation service. START_TEST(test_pph_store_and_reload_with_users) { PPH_ERROR error; pph_context *context; uint8 threshold = 2; uint8 partial_bytes = 0; uint8 secret[DIGEST_LENGTH]; unsigned int i; unsigned int username_count=5; const uint8 *usernames[] = {"username1", "username12", "username1231", "username26", "username5", }; const uint8 *passwords[] = {"password1", "password12", "password1231", "password26", "password5" }; unsigned int username_lengths[] = { strlen("username1"), strlen("username12"), strlen("username1231"), strlen("username26"), strlen("username5"), }; const uint8 *usernames_subset[] = { "username12", "username26"}; unsigned int username_lengths_subset[] = { strlen("username12"), strlen("username26"), }; const uint8 *password_subset[] = {"password12", "password26"}; // setup the context context = pph_init_context(threshold, partial_bytes); ck_assert_msg(context != NULL, "this was a good initialization, go tell someone"); for(i=0;i<username_count;i++) { pph_create_account(context,usernames[i], strlen(usernames[i]),passwords[i], strlen(passwords[i]),1); } // backup our secret memcpy(secret,context->secret,DIGEST_LENGTH); // let's store our data! error = pph_store_context(context,"pph.db"); ck_assert_msg(error == PPH_ERROR_OK, " couldn't store a context with users"); // destroy the existing context in memory pph_destroy_context(context); // reload the one in disk. context = pph_reload_context("pph.db"); ck_assert_msg(context != NULL, " Didn't get a valid structure from disk"); // now give a correct full account information, we expect to have our secret // back. pph_account_node *user_nodes; user_nodes = context->account_data; error = pph_unlock_password_data(context, username_count, usernames, username_lengths, passwords); for(i=0;i<DIGEST_LENGTH;i++) { ck_assert(secret[i]==context->secret[i]); } // clean up, we will reload the context. pph_destroy_context(context); context = pph_reload_context("pph.db"); ck_assert_msg(context != NULL, " didn't get a valid structure from disk"); // now give a correct full account information, we expect to have our secret // back. error = pph_unlock_password_data(context, 2, usernames_subset, username_lengths_subset, password_subset); for(i=0;i<DIGEST_LENGTH;i++) { ck_assert(secret[i]==context->secret[i]); } pph_destroy_context(context); }
END_TEST // we test partial verification, we use a seemingly locked context and try to // login. We don't care if the account is thresholdless or threshold, since // we only check for the leaked partial bytes. START_TEST(test_pph_partial_verification_and_unlock) { PPH_ERROR error; pph_context *context; uint8 threshold = 2; uint8 partial_bytes = 2; unsigned int i; unsigned int username_count=5; const uint8 *usernames[] = {"username1", "username12", "username1231", "username26", "username5", }; const uint8 *passwords[] = {"password1", "password12", "password1231", "password26", "password5" }; unsigned int username_lengths[] = { strlen("username1"), strlen("username12"), strlen("username1231"), strlen("username26"), strlen("username5"), }; const uint8 *usernames_subset[] = { "username12", "username26"}; unsigned int username_lengths_subset[] = { strlen("username12"), strlen("username26"), }; const uint8 *password_subset[] = { "password12", "password26"}; // check for bad pointers at first error = pph_unlock_password_data(NULL, username_count, usernames, username_lengths, passwords); ck_assert_msg(error == PPH_BAD_PTR," EXPECTED BAD_PTR"); // setup the context context = pph_init_context(threshold, partial_bytes); ck_assert_msg(context != NULL, "this was a good initialization, go tell someone"); // store the accounts for(i=0;i<username_count;i++) { error = pph_create_account(context, usernames[i], strlen(usernames[i]), passwords[i], strlen(passwords[i]),1); ck_assert(error == PPH_ERROR_OK); } // let's pretend all is broken context->is_unlocked = false; context->AES_key = NULL; context->secret = NULL; context->share_context= NULL; // now try to login properly with partial verification error = pph_check_login(context, usernames[0], strlen(usernames[0]), passwords[0], strlen(passwords[0])); ck_assert(error == PPH_ERROR_OK); // now let's see if we can try to login with a wrong password, we shouldn't error = pph_check_login(context, usernames[0], strlen(usernames[0]), "wrongpass", strlen("wrongpass")); ck_assert(error == PPH_ACCOUNT_IS_INVALID); // now give a wrong username count, i.e. below the threshold. error = pph_unlock_password_data(context, 0, usernames, username_lengths, passwords); ck_assert_msg(error == PPH_ACCOUNT_IS_INVALID, " Expected ACCOUNT_IS_INVALID"); // do it again, more graphical... error = pph_unlock_password_data(context, threshold -1, usernames, username_lengths, passwords); ck_assert_msg(error == PPH_ACCOUNT_IS_INVALID, " Expected ACCOUNT_IS_INVALID"); // let's check for NULL pointers on the username and password fields error = pph_unlock_password_data(context, username_count, NULL, username_lengths, passwords); ck_assert_msg(error == PPH_BAD_PTR," EXPECTED BAD_PTR"); // let's check for NULL pointers on the username and password fields error = pph_unlock_password_data(context, username_count, usernames, username_lengths, NULL); ck_assert_msg(error == PPH_BAD_PTR," EXPECTED BAD_PTR"); // now give a correct full account information, we expect to have our secret // back. error = pph_unlock_password_data(context, username_count, usernames, username_lengths, passwords); ck_assert(error == PPH_ERROR_OK); ck_assert_msg(context->secret !=NULL, " didnt allocate the secret!"); ck_assert(context->AES_key != NULL); // let's imagine it's all broken (Again). context->is_unlocked = false; context->AES_key = NULL; context->secret = NULL; context->share_context = NULL; // now give correct account information, we expect to have our secret // back. error = pph_unlock_password_data(context, 2, usernames_subset, username_lengths_subset, password_subset); ck_assert(error == PPH_ERROR_OK); ck_assert(context->AES_key != NULL); // for the sake of it, let's login with a correct account after the // secret was recombined. error = pph_check_login(context, usernames_subset[0], strlen(usernames_subset[0]),password_subset[0], strlen(password_subset[0])); ck_assert(error == PPH_ERROR_OK); pph_destroy_context(context); }
END_TEST // we check that the unlock password data cannot unlock the valut provided w // wrong information. START_TEST(test_pph_unlock_password_data_correct_thresholds) { PPH_ERROR error; pph_context *context; uint8 threshold = 2; uint8 partial_bytes = 0; unsigned int i; unsigned int username_count=5; uint8 secret[DIGEST_LENGTH]; const uint8 *usernames[] = {"username1", "username12", "username1231", "username26", "username5", }; const uint8 *passwords[] = {"password1", "password12", "password1231", "password26", "password5" }; unsigned int username_lengths[] = { strlen("username1"), strlen("username12"), strlen("username1231"), strlen("username26"), strlen("username5"), }; const uint8 *usernames_subset[] = { "username12", "username26"}; unsigned int username_lengths_subset[] = { strlen("username12"), strlen("username26"), }; const uint8 *password_subset[] = {"password12", "password26"}; const uint8 *bad_passwords[] = { "whoisthisguy?", "notauser"}; // setup the context context = pph_init_context(threshold,partial_bytes); ck_assert_msg(context != NULL, "this was a good initialization, go tell someone"); //backup the secret memcpy(secret,context->secret,DIGEST_LENGTH); // create some usernames so we can unlock the context. for(i=0;i<username_count;i++) { pph_create_account(context,usernames[i], strlen(usernames[i]), passwords[i], strlen(passwords[i]),1); } // let's imagine it's all broken context->is_unlocked = false; strcpy(context->secret,"thiswasnotthesecretstring"); // now give a correct full account information, we expect to have our secret // back. error = pph_unlock_password_data(context, username_count, usernames, username_lengths, passwords); for(i=0;i<DIGEST_LENGTH;i++) { ck_assert(secret[i] == context->secret[i]); } // let's imagine it's all broken (Again) context->is_unlocked = false; strcpy(context->secret,"thiswasnotthesecretstring"); // now give a correct full account information, we expect to have our secret // back. error = pph_unlock_password_data(context, 2, usernames_subset, username_lengths_subset, password_subset); for(i=0;i<DIGEST_LENGTH;i++) { ck_assert(secret[i] == context->secret[i]); } // and last but not least, create a superuser account and unlock it // by himself, note how he's got three shares assigned to his account. pph_create_account(context,"ipicklocks", strlen("ipicklocks"),"ipickpockets", strlen("ipickpockets"), 3); error = pph_unlock_password_data(context, 1 ,strdup("ipicklocks"), 1, strdup("ipickpockets")); for(i=0;i<DIGEST_LENGTH;i++) { ck_assert(secret[i] == context->secret[i]); } // attempt to unlock the vault with wrong passwords free(context->secret); context->is_unlocked = false; error = pph_unlock_password_data(context, 2, usernames_subset, username_lengths_subset, bad_passwords); ck_assert(error == PPH_ACCOUNT_IS_INVALID); // clean up our mess pph_destroy_context(context); }
}END_TEST ////////// shamir recombination and persistent storage test cases. ////////// // this checks that the unlock password data correctly parses input. START_TEST(test_pph_unlock_password_data_input_sanity) { PPH_ERROR error; pph_context *context; uint8 threshold = 2; uint8 partial_bytes = 0; unsigned int i; unsigned int username_count=5; const uint8 *usernames[] = {"username1", "username12", "username1231", "username26", "username5", }; unsigned int username_lengths[] = { strlen("username1"), strlen("username12"), strlen("username1231"), strlen("username26"), strlen("username5"), }; const uint8 *passwords[] = {"password1", "password12", "password1231", "password26", "password5" }; // check for bad pointers at first error = pph_unlock_password_data(NULL, username_count, usernames, username_lengths, passwords); ck_assert_msg(error == PPH_BAD_PTR," EXPECTED BAD_PTR"); // setup the context context = pph_init_context(threshold, partial_bytes); ck_assert_msg(context != NULL, "this was a good initialization, go tell someone"); // let's imagine it's all broken context->is_unlocked = false; // now give a wrong username count, below the threshold. error = pph_unlock_password_data(context, 0, usernames, username_lengths, passwords); ck_assert_msg(error == PPH_ACCOUNT_IS_INVALID, " Expected ACCOUNT_IS_INVALID"); // do it again, more graphical... error = pph_unlock_password_data(context, threshold -1, usernames, username_lengths, passwords); ck_assert_msg(error == PPH_ACCOUNT_IS_INVALID, " Expected ACCOUNT_IS_INVALID"); // let's check for NULL pointers on the username and password fields error = pph_unlock_password_data(context, username_count, NULL, username_lengths, passwords); ck_assert_msg(error == PPH_BAD_PTR," EXPECTED BAD_PTR"); // check for wrong values in the username_lengths field error = pph_unlock_password_data(context, username_count, usernames, NULL, passwords); ck_assert( error == PPH_BAD_PTR); // let's check for NULL pointers on the username and password fields error = pph_unlock_password_data(context, username_count, usernames, username_lengths, NULL); ck_assert_msg(error == PPH_BAD_PTR," EXPECTED BAD_PTR"); pph_destroy_context(context); }
int PHS(void *out, size_t outlen, const void *in, size_t inlen, const void* salt, size_t saltlen, int tcost, int mcost){ static pph_context *context = NULL; pph_entry *generated_entry; uint8 share[DIGEST_LENGTH]; // check we are given proper pointers if(out == NULL || in == NULL || salt == NULL){ return -1; } // we only support 32 byte digests at the moment. if(outlen != DIGEST_LENGTH){ return -1; } // check the input length if(inlen < 1 || inlen > MAX_PASSWORD_LENGTH){ return -1; } // check the salt length if(saltlen < 1 || saltlen > MAX_SALT_LENGTH){ return -1; } if(tcost < 1 || tcost > MAX_NUMBER_OF_SHARES){ return -1; } // remember, in our case, tcost maps directly to the threshold value, we also // decided to leave no partial bytes to have the whole hash protected by the // shares context = pph_init_context(tcost,0); // get a share to xor it with the password gfshare_ctx_enc_getshare(context->share_context, context->next_entry, share); context->next_entry++; if(context->next_entry > MAX_NUMBER_OF_SHARES){ context->next_entry = 0; } // generate an entry. generated_entry = create_polyhashed_entry( in, inlen, salt, saltlen, share, DIGEST_LENGTH, context->partial_bytes); // copy the resulting polyhash to the output memcpy(out, generated_entry->polyhashed_value, outlen); // free the generated entry free(generated_entry); pph_destroy_context(context); return 0; }
END_TEST // This checks for a proper return code when asking for the wrong username START_TEST(test_check_login_wrong_username) { PPH_ERROR error; pph_context *context; uint8 threshold = 2; uint8 partial_bytes = 0; unsigned char password[] = "i'mnothere"; unsigned char username[] = "nonexistentpassword"; unsigned char anotheruser[] = "0anotheruser"; unsigned int i; // setup the context context = pph_init_context(threshold,partial_bytes); ck_assert_msg(context != NULL, "this was a good initialization, go tell someone"); // check with an uninitialized userlist first error = pph_check_login(context, username, strlen(username), password, strlen(password)); ck_assert_msg(error == PPH_ACCOUNT_IS_INVALID, "expected ACCOUNT_IS_INVALID"); // add a single user and see how it behaves: // 1) add a user error = pph_create_account(context, anotheruser, strlen(anotheruser), "anotherpassword", strlen("anotherpassword"), 1); ck_assert_msg(error == PPH_ERROR_OK, " this shouldn't have broken the test"); // 2) ask for a user that's not here error = pph_check_login(context, username, strlen(username), password, strlen(password)); ck_assert_msg(error == PPH_ACCOUNT_IS_INVALID, "expected ACCOUNT_IS_INVALID"); // lets add a whole bunch of users and check for an existing one again // 1) add a whole new bunch of users: for(i=1;i<9;i++) { anotheruser[0] = i+48; error = pph_create_account(context, anotheruser, strlen(anotheruser), "anotherpassword",strlen("anotherpassword"), 1); ck_assert_msg(error == PPH_ERROR_OK, " this shouldn't have broken the test"); } // 2) ask for a user that's not here error = pph_check_login(context, username, strlen(username), password, strlen(password)); ck_assert_msg(error == PPH_ACCOUNT_IS_INVALID, "expected ACCOUNT_IS_INVALID"); pph_destroy_context(context); }
END_TEST // This test checks for the input of the check_login function, proper error // codes should be returned. START_TEST(test_check_login_input_sanity) { PPH_ERROR error; pph_context *context; uint8 threshold = 2; uint8 partial_bytes = 0; unsigned char password[] = "i'mnothere"; unsigned char username[] = "nonexistentpassword"; unsigned char too_big_username[MAX_USERNAME_LENGTH+2]; unsigned char too_big_password[MAX_PASSWORD_LENGTH+2]; unsigned int i; // lets send a null context pointer first context=NULL; error = pph_check_login(context, username,strlen(username), password, strlen(password)); ck_assert_msg(error == PPH_BAD_PTR, "expected PPH_BAD_PTR"); // we will send a wrong username pointer now context = pph_init_context(threshold, partial_bytes); ck_assert_msg(context != NULL, "this was a good initialization, go tell someone"); error = pph_check_login(context, NULL, 0, password, 0); ck_assert_msg(error == PPH_BAD_PTR, "expected PPH_BAD_PTR"); // do the same for the password error = pph_check_login(context, username, 0, NULL, 0); ck_assert_msg(error == PPH_BAD_PTR, "expected PPH_BAD_PTR"); // now lets create some big usernames and passwords for(i=0;i<MAX_USERNAME_LENGTH+1;i++) { too_big_username[i]='j'; } too_big_username[i]='\0'; // null terminate our string // and query for a login error = pph_check_login(context, too_big_username, strlen(too_big_username), password, strlen(password)); ck_assert_msg(error == PPH_USERNAME_IS_TOO_LONG, "expected USERNAME_IS_TOO_LONG"); // let's do the same with the password for(i=0;i<MAX_PASSWORD_LENGTH+1;i++) { too_big_password[i]='j'; } too_big_password[i]='\0'; error=pph_check_login(context, username, strlen(username), too_big_password, strlen(too_big_password)); ck_assert_msg(error == PPH_PASSWORD_IS_TOO_LONG, "expected PASSWORD_IS_TOO_LONG"); // finally, check it returns the proper error code if the vault is locked // still. We set the unlocked flag to false to lock the context, and we also // know that partial bytes is 0 and won't provide any login functionality. context->is_unlocked = false; error=pph_check_login(context,username,strlen(username), password, strlen(password)); ck_assert_msg(error == PPH_CONTEXT_IS_LOCKED, "expected CONTEXT_IS_LOCKED"); pph_destroy_context(context); }
END_TEST // this test is intended to check that a correct account structure is // producted, we check the expected hash matches and that we have a two-way // flow. In other words, that we can derive the hash from the xored hash and // vice versa START_TEST(test_create_account_entry_consistency) { PPH_ERROR error; pph_context *context; uint8 threshold = 2; uint8 partial_bytes = 0; unsigned char password[] = "verysecure"; unsigned char username[] = "atleastitry"; // We don't know the salt yet, but we know the password value, upon creating // the account, we will replace those x's with the salt values. unsigned char salted_password[] = {'x','x','x','x','x','x','x','x','x', 'x','x','x','x','x','x','x','v','e','r', 'y','s','e','c','u','r','e','\0'}; uint8 password_digest[DIGEST_LENGTH]; unsigned int i; uint8 *digest_result; uint8 share_result[SHARE_LENGTH]; context = pph_init_context(threshold, partial_bytes); ck_assert_msg(context != NULL, "this was a good initialization, go tell someone"); // create the username. error = pph_create_account(context, username,strlen(username), password,strlen(password),1); ck_assert_msg(error == PPH_ERROR_OK, "We should've gotten PPH_ERROR_OK in the return value"); // we do this because we assume the username here is a normal string, but // under normal circumstances, we can't assume this. context->account_data->account.username[strlen(username)]='\0'; ck_assert_str_eq(username,context->account_data->account.username); // now lets check we can take the digest back from the share memcpy(salted_password,context->account_data->account.entries->salt, MAX_SALT_LENGTH); _calculate_digest(password_digest, salted_password, MAX_SALT_LENGTH + strlen(password)); digest_result=context->account_data->account.entries->polyhashed_value; gfshare_ctx_enc_getshare(context->share_context, 1, share_result); _xor_share_with_digest(digest_result, share_result, digest_result, DIGEST_LENGTH); // compare the resulting digests to prove they match. for(i=0;i<DIGEST_LENGTH;i++) { ck_assert(password_digest[i]==digest_result[i]); } // we will check for the existing account error handler now... error = pph_create_account(context, username, strlen(username), password, strlen(password),1); ck_assert_msg(error == PPH_ACCOUNT_EXISTS, "We should've gotten an error since this account repeats"); // finally, check it returns the proper error code if the vault is locked // still, we will simulate account locking by unsetting the flag. context->is_unlocked = false; // we will check for the locked context error now... error = pph_create_account(context, "someotherguy", strlen("someotherguy"), "came-here-asking-the-same-thing",strlen("came-here-asking-the-same-thing") ,1); ck_assert_msg(error == PPH_CONTEXT_IS_LOCKED, "We should've gotten an error now that the vault is locked"); error = pph_destroy_context(context); ck_assert_msg(error == PPH_ERROR_OK, "the free function didn't work properly"); }
END_TEST // This checks for a proper behavior when providing an existing username, // first, as the first and only username, then after having many on the list START_TEST(test_check_login_thresholdless) { PPH_ERROR error; pph_context *context; uint8 threshold = 2; uint8 partial_bytes = 2; unsigned char password[] = "i'mnothere"; unsigned char username[] = "nonexistentpassword"; unsigned char anotheruser[] = "0anotheruser"; unsigned int i; // setup the context context = pph_init_context(threshold, partial_bytes); ck_assert_msg(context != NULL, "this was a good initialization, go tell someone"); // add a single user and see how it behaves: // 1) add a user error = pph_create_account(context, username, strlen(username), password, strlen(password), 0); ck_assert_msg(error == PPH_ERROR_OK, " this shouldn't have broken the test"); // 2) ask for it, providing correct credentials error = pph_check_login(context, username, strlen(username), password, strlen(password)); ck_assert_msg(error == PPH_ERROR_OK, "expected OK"); // lets add a whole bunch of users and check for an existing one again // 1) add a whole new bunch of users: for(i=1;i<9;i++) { error = pph_create_account(context, anotheruser, strlen(anotheruser), "anotherpassword", strlen("anotherpassword"), 1); ck_assert_msg(error == PPH_ERROR_OK, " this shouldn't have broken the test"); anotheruser[0] = i+48; } // 2) ask again error = pph_check_login(context, username, strlen(username), password, strlen(password)); ck_assert_msg(error == PPH_ERROR_OK, "expected ERROR_OK"); // 3) ask one more time, mistyping our passwords error = pph_check_login(context, username, strlen(username), "i'mnotthere", strlen("i'mnotthere")); ck_assert_msg(error == PPH_ACCOUNT_IS_INVALID, " how did we get in!?"); // 4) check if threshold accounts can login (they should) error = pph_check_login(context, "0anotheruser", strlen("0anotheruser"), "anotherpassword", strlen("anotherpassword")); ck_assert_msg(error == PPH_ERROR_OK, " we should've been able to login as admin"); // clean up our mess. pph_destroy_context(context); }
int main(void) { // 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 // recover shares and thus cannot help to transition to normal operation. pph_create_account(context,"Eve", strlen("Eve"), "i'm.all.ears", strlen("i'm.all.ears"), 0); // to fully check a login we must have a 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 // validation 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 accounts 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"); } // In order to be able to create protector accounts, we must bootstrap the. // for this, we setup an array of username strings and an array of password // strings. const uint8 **usernames = malloc(sizeof(*usernames)*2); usernames[0] = strdup("Alice"); usernames[1] = strdup("Bob"); const uint8 **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, bootstraps the vault and recovers the shares. pph_unlock_password_data(context, 2, usernames, username_lengths, passwords, password_lengths); // now the data is available. 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"); } // we should now store the context and free the data before leaving pph_store_context(context,"securepasswords"); pph_destroy_context(context); return 0; }
END_TEST // we will check for a full input range unlocking procedure using random // username-password combinations of various lengths, the procedure should // yield a correct secret and an incorrect secret when prompted with one bad // password START_TEST(test_pph_unlock_password_data_full_range) { uint8 *usernames[MAX_USERNAME_LENGTH]; uint8 *passwords[MAX_PASSWORD_LENGTH]; unsigned int *username_lengths; unsigned int i; PPH_ERROR error; uint8 threshold = 2; uint8 partial_bytes = 0; pph_context *context; // initialize the buffers username_lengths = malloc(sizeof(*username_lengths)*MAX_USERNAME_LENGTH); context = pph_init_context( threshold, partial_bytes); ck_assert(context != NULL); // initialize a username password pair of each length of a random value for( i = 0; i < MAX_USERNAME_LENGTH-1; i++){ usernames[i] = malloc(sizeof(*usernames[i])*MAX_USERNAME_LENGTH); passwords[i] = malloc(sizeof(*passwords[i])*MAX_PASSWORD_LENGTH); ck_assert( usernames[i] != NULL); ck_assert( passwords[i] != NULL); get_random_bytes( i+1, usernames[i]); get_random_bytes( i+1, passwords[i]); username_lengths[i]= i + 1; error = pph_create_account( context, usernames[i], username_lengths[i], passwords[i], username_lengths[i], 1); ck_assert( error == PPH_ERROR_OK); error = pph_check_login( context, usernames[i], username_lengths[i], passwords[i], username_lengths[i]); ck_assert( error == PPH_ERROR_OK); } // lock the context context->is_unlocked = false; // unlock the context error = pph_unlock_password_data( context, MAX_USERNAME_LENGTH -1, usernames, username_lengths, passwords); ck_assert( error == PPH_ERROR_OK ); // check we can login after unlocking for(i = 0; i < MAX_USERNAME_LENGTH-1; i++){ error = pph_check_login( context, usernames[i], username_lengths[i], passwords[i], username_lengths[i]); ck_assert(error == PPH_ERROR_OK); } // now, fail to unlock the context, context->is_unlocked = false; passwords[0][0] = ~passwords[0][0]; error = pph_unlock_password_data( context, MAX_USERNAME_LENGTH -1, usernames, username_lengths, passwords); ck_assert( error != PPH_ERROR_OK); // free everything for(i = 0; i < MAX_USERNAME_LENGTH-1; i++) { free(usernames[i]); free(passwords[i]); } free(username_lengths); pph_destroy_context(context); }END_TEST;