forked from laravel/framework
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
145 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
<?php | ||
|
||
namespace Illuminate\Hashing; | ||
|
||
use RuntimeException; | ||
use Illuminate\Contracts\Hashing\Hasher as HasherContract; | ||
|
||
class Argon2Hasher implements HasherContract | ||
{ | ||
/** | ||
* Hash the given value. | ||
* | ||
* @param string $value | ||
* @param array $options | ||
* @return string | ||
* | ||
* @throws \RuntimeException | ||
*/ | ||
public function make($value, array $options = []): string | ||
{ | ||
if (extension_loaded('sodium')) { | ||
return sodium_crypto_pwhash_str( | ||
$value, | ||
$options['time_cost'] ?? SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE, | ||
$options['memory_cost'] ?? SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE | ||
); | ||
} | ||
|
||
throw new RuntimeException('Argon2i hashing not supported.'); | ||
} | ||
|
||
/** | ||
* Check a plain text value against a hashed value. | ||
* | ||
* @param string $value | ||
* @param string $hashedValue | ||
* @param array $options | ||
* @return bool | ||
* | ||
* @throws \RuntimeException | ||
*/ | ||
public function check($value, $hashedValue, array $options = []): bool | ||
{ | ||
if (extension_loaded('sodium')) { | ||
$valid = sodium_crypto_pwhash_str_verify($hashedValue, $value); | ||
sodium_memzero($value); | ||
return $valid; | ||
} | ||
|
||
throw new RuntimeException('Argon2i hashing not supported.'); | ||
} | ||
|
||
/** | ||
* Check if the given hash has been hashed using the given options. | ||
* | ||
* @param string $hashedValue | ||
* @param array $options | ||
* @return bool | ||
* | ||
* @throws \RuntimeException | ||
*/ | ||
public function needsRehash($hashedValue, array $options = []): bool | ||
{ | ||
// Extract options from the hashed value | ||
list($memoryCost, $timeCost) = sscanf($hashedValue, '$%*[argon2id]$v=%*ld$m=%d,t=%d'); | ||
$hashOptions = ['memory_cost' => $memoryCost, 'time_cost' => $timeCost]; | ||
|
||
// Filter unknown options from the options array | ||
$options = array_filter($options, function ($key) use ($hashOptions) { | ||
return isset($hashOptions[$key]); | ||
}, ARRAY_FILTER_USE_KEY); | ||
|
||
return ! empty(array_diff_assoc($options, $hashOptions)); | ||
} | ||
|
||
/** | ||
* Determine if the system supports Argon2i hashing. | ||
* | ||
* @return bool | ||
*/ | ||
public function isSupported(): bool | ||
{ | ||
return extension_loaded('sodium'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
<?php | ||
|
||
namespace Illuminate\Tests\Hashing; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
use Illuminate\Hashing\Argon2Hasher; | ||
|
||
class Argon2HasherTest extends TestCase | ||
{ | ||
const PLAINTEXT_PASSWORD = 'password'; | ||
|
||
public function setUp() | ||
{ | ||
if (! (new Argon2Hasher)->isSupported()) { | ||
$this->markTestSkipped('Argon2i hashing not supported.'); | ||
} | ||
} | ||
|
||
public function testHashPassword() | ||
{ | ||
$hasher = new Argon2Hasher; | ||
$hashedPassword = $hasher->make(self::PLAINTEXT_PASSWORD); | ||
|
||
$this->assertNotSame(self::PLAINTEXT_PASSWORD, $hashedPassword); | ||
$this->assertStringStartsWith(SODIUM_CRYPTO_PWHASH_STRPREFIX, $hashedPassword); | ||
} | ||
|
||
public function testVerifyPassword() | ||
{ | ||
$hasher = new Argon2Hasher; | ||
$hashedPassword = $hasher->make(self::PLAINTEXT_PASSWORD); | ||
|
||
$this->assertTrue($hasher->check(self::PLAINTEXT_PASSWORD, $hashedPassword)); | ||
$this->assertFalse($hasher->check(strrev(self::PLAINTEXT_PASSWORD), $hashedPassword)); | ||
} | ||
|
||
public function testNeedsRehash() | ||
{ | ||
$hasher = new Argon2Hasher; | ||
$hashedPassword = $hasher->make(self::PLAINTEXT_PASSWORD); | ||
|
||
$this->assertFalse($hasher->needsRehash($hashedPassword)); | ||
$this->assertTrue($hasher->needsRehash($hashedPassword, ['time_cost' => 1])); | ||
$this->assertTrue($hasher->needsRehash($hashedPassword, ['memory_cost' => 1])); | ||
} | ||
} |