}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);
}
Example #12
0
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");

}
Example #19
0
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;