Skip to content

Commit

Permalink
Add test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
stevenmaguire committed Feb 18, 2020
1 parent 4f1b4c2 commit 787fa80
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 6 deletions.
65 changes: 59 additions & 6 deletions src/PasswordHasher.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,16 @@ class PasswordHasher implements IPasswordHasher
* (All UInt32s are stored big-endian.)
*/

/**
* Compatibility mode
* @var integer
*/
private $compatibilityMode;

/**
* Iteration count
* @var integer
*/
private $iterCount;

/**
Expand Down Expand Up @@ -64,7 +73,7 @@ public function __construct(
*/
public function hashPassword($password)
{
if ($password == null) {
if (is_null($password)) {
throw new InvalidArgumentException('Password cannot be null');
}

Expand All @@ -75,6 +84,12 @@ public function hashPassword($password)
}
}

/**
* Creates a hashed password using version 2 hashing algorithm.
*
* @param string $password
* @return string
*/
private static function hashPasswordV2($password)
{
$pbkdf2Prf = KeyDerivationPrf::HMACSHA1; // default for Rfc2898DeriveBytes
Expand All @@ -98,6 +113,12 @@ private static function hashPasswordV2($password)
return $outputBytes;
}

/**
* Creates a hashed password using version 3 hashing algorithm.
*
* @param string $password
* @return string
*/
private function hashPasswordV3($password)
{
$prf = KeyDerivationPrf::HMACSHA256;
Expand Down Expand Up @@ -140,17 +161,17 @@ private function hashPasswordV3($password)
*/
public function verifyHashedPassword($hashedPassword, $providedPassword)
{
if ($hashedPassword == null) {
if (is_null($hashedPassword)) {
throw new InvalidArgumentException('hashedPassword is null');
}

if ($providedPassword == null) {
if (is_null($providedPassword)) {
throw new InvalidArgumentException('providedPassword is null');
}

$decodedHashedPassword = base64_decode($hashedPassword);

// read the format marker from the hashed password
// Read the format marker from the hashed password
if (strlen($decodedHashedPassword) == 0) {
return PasswordVerificationResult::FAILED;
}
Expand All @@ -166,7 +187,7 @@ public function verifyHashedPassword($hashedPassword, $providedPassword)
}

/**
* Performs verification using strategy version 2.
* Performs verification using version 2 password hashing scheme.
*
* @param string $decodedHashedPassword
* @param string $providedPassword
Expand All @@ -186,7 +207,7 @@ private function verifyWithV2($decodedHashedPassword, $providedPassword)
}

/**
* Performs verification using strategy version 3.
* Performs verification using version 3 password hashing scheme.
*
* @param string $decodedHashedPassword
* @param string $providedPassword
Expand All @@ -206,6 +227,14 @@ private function verifyWithV3($decodedHashedPassword, $providedPassword)
}
}

/**
* Attempts to verify the given password against the given hashed password
* using version 2 password hashing scheme.
*
* @param string $hashedPassword
* @param string $password
* @return boolean
*/
private static function verifyHashedPasswordV2($hashedPassword, $password)
{
$pbkdf2Prf = KeyDerivationPrf::HMACSHA1; // default for Rfc2898DeriveBytes
Expand Down Expand Up @@ -235,6 +264,15 @@ private static function verifyHashedPasswordV2($hashedPassword, $password)
return $actualSubkey === $expectedSubkey;
}

/**
* Attempts to verify the given password against the given hashed password
* using version 3 password hashing scheme.
*
* @param string $hashedPassword
* @param string $password
* @param integer &$iterCount
* @return boolean
*/
private static function verifyHashedPasswordV3($hashedPassword, $password, &$iterCount)
{
$iterCount = 0;
Expand Down Expand Up @@ -272,6 +310,14 @@ private static function verifyHashedPasswordV3($hashedPassword, $password, &$ite
return $actualSubkey === $expectedSubkey;
}

/**
* Updates the given buffer to include network byte order.
*
* @param string &$buffer
* @param integer $offset
* @param integer $value
* @return void
*/
private static function writeNetworkByteOrder(&$buffer, $offset, $value)
{
$buffer[$offset] = chr($value >> 24);
Expand All @@ -280,6 +326,13 @@ private static function writeNetworkByteOrder(&$buffer, $offset, $value)
$buffer[$offset + 3] = chr($value & 0xFF);
}

/**
* Reads and returns the network byte order stored in the given buffer.
*
* @param string $buffer
* @param integer $offset
* @return integer
*/
private static function readNetworkByteOrder($buffer, $offset)
{
return ord($buffer[$offset]) << 24
Expand Down
57 changes: 57 additions & 0 deletions tests/PasswordHasherTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ public function testInvalidPasswordVerification()
$result = $hasher->verifyHashedPassword('very strong hash', null);
}

/**
* Test invalid password verification.
*/
public function testEmptyPasswordHashVerification()
{
$hasher = new PasswordHasher();
$hashedPassword = base64_encode('');
$result = $hasher->verifyHashedPassword($hashedPassword, 'very strong password');
$this->assertEquals(PasswordVerificationResult::FAILED, $result);
}

/**
* Test invalid password verification.
*/
Expand All @@ -69,6 +80,40 @@ public function testHashV2Passwords()
$this->assertEquals(PasswordVerificationResult::SUCCESS, $result);
}

/**
* Test some basic v2 password hashing.
*/
public function testHashV2PasswordMismatch()
{
$hasher = new PasswordHasher(PasswordHasherCompatibilityMode::IDENTITY_V2);
$hashedPassword = $hasher->hashPassword('very strong password');
$result = $hasher->verifyHashedPassword($hashedPassword, 'very strong password!');
$this->assertEquals(PasswordVerificationResult::FAILED, $result);
}

/**
* Test some basic v2 password hashing.
*/
public function testHashV2PasswordLength()
{
$hasher = new PasswordHasher(PasswordHasherCompatibilityMode::IDENTITY_V2);
$hashedPassword = $hasher->hashPassword('very strong password');
$result = $hasher->verifyHashedPassword(substr($hashedPassword, 0, 20), 'very strong password!!');
$this->assertEquals(PasswordVerificationResult::FAILED, $result);
}

/**
* Test upgrading v2 password to v3.
*/
public function testHashV2PasswordRehash()
{
$v2Hasher = new PasswordHasher(PasswordHasherCompatibilityMode::IDENTITY_V2);
$v3Hasher = new PasswordHasher(PasswordHasherCompatibilityMode::IDENTITY_V3);
$v2HashedPassword = $v2Hasher->hashPassword('very strong password');
$result = $v3Hasher->verifyHashedPassword($v2HashedPassword, 'very strong password');
$this->assertEquals(PasswordVerificationResult::SUCCESS_REHASH_NEEDED, $result);
}

/**
* Test insufficient v3 iteration count.
*/
Expand All @@ -89,6 +134,18 @@ public function testHashV3Passwords()
$this->assertEquals(PasswordVerificationResult::SUCCESS, $result);
}

/**
* Test upgrading v3 password to more secure v3.
*/
public function testHashV3PasswordRehash()
{
$lowIterHasher = new PasswordHasher(PasswordHasherCompatibilityMode::IDENTITY_V3, 1000);
$highIterHasher = new PasswordHasher(PasswordHasherCompatibilityMode::IDENTITY_V3);
$lowIterHashedPassword = $lowIterHasher->hashPassword('very strong password');
$result = $highIterHasher->verifyHashedPassword($lowIterHashedPassword, 'very strong password');
$this->assertEquals(PasswordVerificationResult::SUCCESS_REHASH_NEEDED, $result);
}

/**
* Test some basic v3 hashed passwords.
*/
Expand Down

0 comments on commit 787fa80

Please sign in to comment.