Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions src/Auth/Hash.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,36 @@ abstract class Hash
* @param mixed $value The value to set for the option
* @return self
*/
protected function setOption(string $key, mixed $value): self
public function setOption(string $key, mixed $value): self
{
$this->options[$key] = $value;

return $this;
}

/**
* Set multiple hashing options at once
*
* @param array<string, mixed> $options Array of options to set
* @return self
*/
public function setOptions(array $options): self
{
foreach ($options as $key => $value) {
$this->setOption($key, $value);
}

return $this;
}

/**
* Get a specific option value
*
* @param string $key The option key to retrieve
* @param mixed $default Default value if option doesn't exist
* @return mixed The option value or default if not found
*/
protected function getOption(string $key, mixed $default = null): mixed
public function getOption(string $key, mixed $default = null): mixed
{
return $this->options[$key] ?? $default;
}
Expand Down Expand Up @@ -61,4 +76,11 @@ abstract public function hash(string $value): string;
* @return bool
*/
abstract public function verify(string $value, string $hash): bool;

/**
* Get the name of the hash algorithm
*
* @return string
*/
abstract public function getName(): string;
}
29 changes: 13 additions & 16 deletions src/Auth/Hashes/Argon2.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ class Argon2 extends Hash
*/
public function __construct()
{
$this->setOption('memory_cost', 65536);
$this->setOption('time_cost', 4);
$this->setOption('type', $this->getName());
$this->setOption('memoryCost', 65536);
$this->setOption('timeCost', 4);
$this->setOption('threads', 3);
}

Expand Down Expand Up @@ -42,11 +43,7 @@ public function verify(string $value, string $hash): bool
*/
public function setMemoryCost(int $cost): self
{
if ($cost < PASSWORD_ARGON2_DEFAULT_MEMORY_COST) {
throw new \InvalidArgumentException('Memory cost must be >= '.PASSWORD_ARGON2_DEFAULT_MEMORY_COST.' KiB');
}

$this->setOption('memory_cost', $cost);
$this->setOption('memoryCost', $cost);

return $this;
}
Expand All @@ -61,11 +58,7 @@ public function setMemoryCost(int $cost): self
*/
public function setTimeCost(int $cost): self
{
if ($cost < PASSWORD_ARGON2_DEFAULT_TIME_COST) {
throw new \InvalidArgumentException('Time cost must be >= '.PASSWORD_ARGON2_DEFAULT_TIME_COST);
}

$this->setOption('time_cost', $cost);
$this->setOption('timeCost', $cost);

return $this;
}
Expand All @@ -80,12 +73,16 @@ public function setTimeCost(int $cost): self
*/
public function setThreads(int $threads): self
{
if ($threads < PASSWORD_ARGON2_DEFAULT_THREADS) {
throw new \InvalidArgumentException('Threads must be >= '.PASSWORD_ARGON2_DEFAULT_THREADS);
}

$this->setOption('threads', $threads);

return $this;
}

/**
* {@inheritdoc}
*/
public function getName(): string
{
return 'argon2';
}
}
9 changes: 9 additions & 0 deletions src/Auth/Hashes/Bcrypt.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class Bcrypt extends Hash
*/
public function __construct()
{
$this->setOption('type', $this->getName());
$this->setOption('cost', 8);
}

Expand Down Expand Up @@ -48,4 +49,12 @@ public function setCost(int $cost): self

return $this;
}

/**
* {@inheritdoc}
*/
public function getName(): string
{
return 'bcrypt';
}
}
16 changes: 16 additions & 0 deletions src/Auth/Hashes/MD5.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@

class MD5 extends Hash
{
/**
* Constructor
*/
public function __construct()
{
$this->setOption('type', $this->getName());
}

/**
* {@inheritdoc}
*/
Expand All @@ -21,4 +29,12 @@ public function verify(string $value, string $hash): bool
{
return $this->hash($value) === $hash;
}

/**
* {@inheritdoc}
*/
public function getName(): string
{
return 'md5';
}
}
9 changes: 9 additions & 0 deletions src/Auth/Hashes/PHPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public function __construct()
$randomState .= getmypid();
}

$this->setOption('type', $this->getName());
$this->setOption('iteration_count_log2', 8);
$this->setOption('portable_hashes', false);
$this->setOption('random_state', $randomState);
Expand Down Expand Up @@ -253,4 +254,12 @@ public function setPortableHashes(bool $portable): PHPass

return $this;
}

/**
* {@inheritdoc}
*/
public function getName(): string
{
return 'phpass';
}
}
16 changes: 16 additions & 0 deletions src/Auth/Hashes/Plaintext.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@

class Plaintext extends Hash
{
/**
* Constructor
*/
public function __construct()
{
$this->setOption('type', $this->getName());
}

/**
* {@inheritdoc}
*/
Expand All @@ -21,4 +29,12 @@ public function verify(string $value, string $hash): bool
{
return $this->hash($value) === $hash;
}

/**
* {@inheritdoc}
*/
public function getName(): string
{
return 'plaintext';
}
}
9 changes: 9 additions & 0 deletions src/Auth/Hashes/Scrypt.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class Scrypt extends Hash
*/
public function __construct()
{
$this->setOption('type', $this->getName());
$this->setOption('costCpu', 8);
$this->setOption('costMemory', 14);
$this->setOption('costParallel', 1);
Expand Down Expand Up @@ -139,4 +140,12 @@ public function setSalt(string $salt): self

return $this;
}

/**
* {@inheritdoc}
*/
public function getName(): string
{
return 'scrypt';
}
}
10 changes: 10 additions & 0 deletions src/Auth/Hashes/ScryptModified.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public function __construct()
$saltSeparator = random_bytes(16);
$signerKey = random_bytes(32);

$this->setOption('type', $this->getName());

// Set default options with secure random values
$this->setOption('salt', base64_encode($salt));
$this->setOption('saltSeparator', base64_encode($saltSeparator));
Expand Down Expand Up @@ -164,4 +166,12 @@ public function setSignerKey(string $key): self

return $this;
}

/**
* {@inheritdoc}
*/
public function getName(): string
{
return 'scryptMod';
}
}
10 changes: 9 additions & 1 deletion src/Auth/Hashes/Sha.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class Sha extends Hash
*/
public function __construct()
{
$this->setOption('version', 'sha3-512');
$this->setOption('version', 'sha256');
}

/**
Expand Down Expand Up @@ -86,4 +86,12 @@ public function verify(string $value, string $hash): bool
{
return $this->hash($value) === $hash;
}

/**
* {@inheritdoc}
*/
public function getName(): string
{
return 'sha';
}
}
31 changes: 29 additions & 2 deletions src/Auth/Proofs/Password.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class Password extends Proof

public const SCRYPT = 'scrypt';

public const SCRYPT_MODIFIED = 'scrypt-modified';
public const SCRYPT_MODIFIED = 'scryptMod';

public const SHA = 'sha';

Expand Down Expand Up @@ -60,7 +60,7 @@ public function __construct(array $hashes = [])
}

$this->hashes = $hashes;
$this->hash = reset($hashes); // Set the first hash as the default one
$this->hash = new Argon2(); // Set the first hash as the default one
}

/**
Expand Down Expand Up @@ -171,4 +171,31 @@ public function generate(): string

return $password;
}

/**
* Create a hash instance by type
*
* @param string $type One of the supported hash types (ARGON2, BCRYPT, SCRYPT, SCRYPT_MODIFIED, SHA, MD5, PHPASS)
* @param array<string, mixed> $options Optional parameters for hash configuration
* @return Hash
*
* @throws \Exception
*/
public static function createHash(string $type, array $options = []): Hash
{
$hash = match ($type) {
self::ARGON2 => new Argon2(),
self::BCRYPT => new Bcrypt(),
self::SCRYPT => new Scrypt(),
self::SCRYPT_MODIFIED => new ScryptModified(),
self::SHA => new Sha(),
self::MD5 => new MD5(),
self::PHPASS => new PHPass(),
default => throw new \Exception("Unsupported hash type: {$type}")
};

$hash->setOptions($options);

return $hash;
}
}
38 changes: 33 additions & 5 deletions src/Auth/Store.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,59 @@ class Store
protected array $data = [];

/**
* Get a value from the store
* @var string|null
*/
protected ?string $key = null;

/**
* Get a property from the store
*
* @param string $key
* @param mixed $default
* @return mixed
*/
public function get(string $key, mixed $default = null): mixed
public function getProperty(string $key, mixed $default = null): mixed
{
return $this->data[$key] ?? $default;
}

/**
* Set a value in the store
* Set a property in the store
*
* @param string $key
* @param mixed $value
* @return self
*/
public function set(string $key, mixed $value): self
public function setProperty(string $key, mixed $value): self
{
$this->data[$key] = $value;

return $this;
}

/**
* Get the store key
*
* @return string|null
*/
public function getKey(): ?string
{
return $this->key;
}

/**
* Set the store key
*
* @param string|null $key
* @return self
*/
public function setKey(?string $key): self
{
$this->key = $key;

return $this;
}

/**
* Encode store data to base64 string
*
Expand Down Expand Up @@ -66,7 +94,7 @@ public function decode(string $data): self
$json = json_decode($decoded, true, 512, JSON_THROW_ON_ERROR);
if (is_array($json)) {
foreach ($json as $key => $value) {
$this->set($key, $value);
$this->setProperty($key, $value);
}
}
} catch (\JsonException $e) {
Expand Down
Loading