-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: password hash * formatting (fix phpstan) * feat: hash driver register
- Loading branch information
1 parent
ea2d45d
commit e6782f2
Showing
8 changed files
with
323 additions
and
0 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace System\Security\Hashing; | ||
|
||
class Argon2IdHasher extends ArgonHasher implements HashInterface | ||
{ | ||
public function make(string $value, array $options = []): string | ||
{ | ||
$hash = @password_hash($value, PASSWORD_ARGON2ID, [ | ||
'memory_cost' => $options['memory'] ?? $this->memory, | ||
'time_cost' => $options['time'] ?? $this->time, | ||
'threads' => $options['threads'] ?? $this->threads, | ||
]); | ||
|
||
if (!is_string($hash)) { | ||
throw new \RuntimeException(PASSWORD_ARGON2ID . ' hashing not supported.'); | ||
} | ||
|
||
return $hash; | ||
} | ||
|
||
public function isValidAlgorithm(string $hash): bool | ||
{ | ||
return 'argon2id' === $this->info($hash)['algoName']; | ||
} | ||
} |
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,55 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace System\Security\Hashing; | ||
|
||
class ArgonHasher extends DefaultHasher implements HashInterface | ||
{ | ||
protected int $memory = 1024; | ||
|
||
protected int $time = 2; | ||
|
||
protected int $threads = 2; | ||
|
||
public function setMemory(int $memory): self | ||
{ | ||
$this->memory = $memory; | ||
|
||
return $this; | ||
} | ||
|
||
public function setTime(int $time): self | ||
{ | ||
$this->time = $time; | ||
|
||
return $this; | ||
} | ||
|
||
public function setThreads(int $threads): self | ||
{ | ||
$this->threads = $threads; | ||
|
||
return $this; | ||
} | ||
|
||
public function make(string $value, array $options = []): string | ||
{ | ||
$hash = @password_hash($value, PASSWORD_ARGON2I, [ | ||
'memory_cost' => $options['memory'] ?? $this->memory, | ||
'time_cost' => $options['time'] ?? $this->time, | ||
'threads' => $options['threads'] ?? $this->threads, | ||
]); | ||
|
||
if (!is_string($hash)) { | ||
throw new \RuntimeException(PASSWORD_ARGON2I . ' hashing not supported.'); | ||
} | ||
|
||
return $hash; | ||
} | ||
|
||
public function isValidAlgorithm(string $hash): bool | ||
{ | ||
return 'argon2i' === $this->info($hash)['algoName']; | ||
} | ||
} |
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,31 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace System\Security\Hashing; | ||
|
||
class BcryptHasher extends DefaultHasher implements HashInterface | ||
{ | ||
protected int $rounds = 12; | ||
|
||
public function setRounds(int $rounds): self | ||
{ | ||
$this->rounds = $rounds; | ||
|
||
return $this; | ||
} | ||
|
||
public function make(string $value, array $options = []): string | ||
{ | ||
$hash = password_hash($value, PASSWORD_BCRYPT, [ | ||
'cost' => $options['rounds'] ?? $this->rounds, | ||
]); | ||
|
||
return $hash; | ||
} | ||
|
||
public function isValidAlgorithm(string $hash): bool | ||
{ | ||
return 'bcrypt' === $this->info($hash)['algoName']; | ||
} | ||
} |
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,28 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace System\Security\Hashing; | ||
|
||
class DefaultHasher implements HashInterface | ||
{ | ||
public function info(string $hash): array | ||
{ | ||
return password_get_info($hash); | ||
} | ||
|
||
public function verify(string $value, string $hashed_value, array $options = []): bool | ||
{ | ||
return password_verify($value, $hashed_value); | ||
} | ||
|
||
public function make(string $value, array $options = []): string | ||
{ | ||
return password_hash($value, PASSWORD_DEFAULT); | ||
} | ||
|
||
public function isValidAlgorithm(string $hash): bool | ||
{ | ||
return 'bcrypt' === $this->info($hash)['algoName']; | ||
} | ||
} |
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,33 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace System\Security\Hashing; | ||
|
||
interface HashInterface | ||
{ | ||
/** | ||
* Get information about hash. | ||
* | ||
* @return array<string, int|string|bool> | ||
*/ | ||
public function info(string $hash): array; | ||
|
||
/** | ||
* Verify hash and hashed. | ||
* | ||
* @param array<string, int|string|bool> $options | ||
*/ | ||
public function verify(string $value, string $hashed_value, array $options = []): bool; | ||
|
||
/** | ||
* Hash given string. | ||
* | ||
* @param array<string, int|string|bool> $options | ||
* | ||
* @throws \RuntimeException | ||
*/ | ||
public function make(string $value, array $options = []): string; | ||
|
||
public function isValidAlgorithm(string $hash): bool; | ||
} |
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,61 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace System\Security\Hashing; | ||
|
||
class HashManager implements HashInterface | ||
{ | ||
/** @var array<string, HashInterface> */ | ||
private $driver = []; | ||
|
||
private HashInterface $default_driver; | ||
|
||
public function __construct() | ||
{ | ||
$this->setDefaultDriver(new DefaultHasher()); | ||
} | ||
|
||
public function setDefaultDriver(HashInterface $driver): self | ||
{ | ||
$this->default_driver = $driver; | ||
|
||
return $this; | ||
} | ||
|
||
public function setDriver(string $driver_name, HashInterface $driver): self | ||
{ | ||
$this->driver[$driver_name] = $driver; | ||
|
||
return $this; | ||
} | ||
|
||
public function driver(?string $driver = null): HashInterface | ||
{ | ||
if (array_key_exists($driver, $this->driver)) { | ||
return $this->driver[$driver]; | ||
} | ||
|
||
return $this->default_driver; | ||
} | ||
|
||
public function info(string $hashed_value): array | ||
{ | ||
return $this->driver()->info($hashed_value); | ||
} | ||
|
||
public function make(string $value, array $options = []): string | ||
{ | ||
return $this->driver()->make($value, $options); | ||
} | ||
|
||
public function verify(string $value, string $hashed_value, array $options = []): bool | ||
{ | ||
return $this->driver()->verify($value, $hashed_value, $options); | ||
} | ||
|
||
public function isValidAlgorithm(string $hash): bool | ||
{ | ||
return $this->driver()->isValidAlgorithm($hash); | ||
} | ||
} |
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,33 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace System\Test\Security\Hashing; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
use System\Security\Hashing\BcryptHasher; | ||
use System\Security\Hashing\HashManager; | ||
|
||
class HasherMangerTest extends TestCase | ||
{ | ||
/** @test */ | ||
public function itCanHashDefaultHasher() | ||
{ | ||
$hasher = new HashManager(); | ||
$hash = $hasher->make('password'); | ||
$this->assertNotSame('password', $hash); | ||
$this->assertTrue($hasher->verify('password', $hash)); | ||
$this->assertTrue($hasher->isValidAlgorithm($hash)); | ||
} | ||
|
||
/** @test */ | ||
public function itCanUsingDriver() | ||
{ | ||
$hasher = new HashManager(); | ||
$hasher->setDriver('bcrypt', new BcryptHasher()); | ||
$hash = $hasher->driver('bcrypt')->make('password'); | ||
$this->assertNotSame('password', $hash); | ||
$this->assertTrue($hasher->driver('bcrypt')->verify('password', $hash)); | ||
$this->assertTrue($hasher->driver('bcrypt')->isValidAlgorithm($hash)); | ||
} | ||
} |
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,54 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace System\Test\Security\Hashing; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
use System\Security\Hashing\Argon2IdHasher; | ||
use System\Security\Hashing\ArgonHasher; | ||
use System\Security\Hashing\BcryptHasher; | ||
use System\Security\Hashing\DefaultHasher; | ||
|
||
class HasherTest extends TestCase | ||
{ | ||
/** @test */ | ||
public function itCanHashDefaultHasher() | ||
{ | ||
$hasher = new DefaultHasher(); | ||
$hash = $hasher->make('password'); | ||
$this->assertNotSame('password', $hash); | ||
$this->assertTrue($hasher->verify('password', $hash)); | ||
$this->assertTrue($hasher->isValidAlgorithm($hash)); | ||
} | ||
|
||
/** @test */ | ||
public function itCanHashBryptHasher() | ||
{ | ||
$hasher = new BcryptHasher(); | ||
$hash = $hasher->make('password'); | ||
$this->assertNotSame('password', $hash); | ||
$this->assertTrue($hasher->verify('password', $hash)); | ||
$this->assertTrue($hasher->isValidAlgorithm($hash)); | ||
} | ||
|
||
/** @test */ | ||
public function itCanHashArgonHasher() | ||
{ | ||
$hasher = new ArgonHasher(); | ||
$hash = $hasher->make('password'); | ||
$this->assertNotSame('password', $hash); | ||
$this->assertTrue($hasher->verify('password', $hash)); | ||
$this->assertTrue($hasher->isValidAlgorithm($hash)); | ||
} | ||
|
||
/** @test */ | ||
public function itCanHashArgon2IdHasher() | ||
{ | ||
$hasher = new Argon2IdHasher(); | ||
$hash = $hasher->make('password'); | ||
$this->assertNotSame('password', $hash); | ||
$this->assertTrue($hasher->verify('password', $hash)); | ||
$this->assertTrue($hasher->isValidAlgorithm($hash)); | ||
} | ||
} |