From d9bf6c432e2c28600ee5aa562f3f36f48cb7b7cc Mon Sep 17 00:00:00 2001 From: Christopher Ng Date: Thu, 27 Jun 2024 16:56:35 -0700 Subject: [PATCH 1/3] feat: Add method to validate an IHasher hash Signed-off-by: Christopher Ng --- lib/private/Security/Hasher.php | 14 ++++++++++++++ lib/public/Security/IHasher.php | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/lib/private/Security/Hasher.php b/lib/private/Security/Hasher.php index 22e850157fc0b..081cac5116167 100644 --- a/lib/private/Security/Hasher.php +++ b/lib/private/Security/Hasher.php @@ -190,4 +190,18 @@ private function getPrefferedAlgorithm(): string { return $default; } + + public function validate(string $prefixedHash): bool { + $splitHash = $this->splitHash($prefixedHash); + if (empty($splitHash)) { + return false; + } + $validVersions = [3, 2, 1]; + $version = $splitHash['version']; + if (!in_array($version, $validVersions, true)) { + return false; + } + $algoName = password_get_info($splitHash['hash'])['algoName']; + return $algoName !== 'unknown'; + } } diff --git a/lib/public/Security/IHasher.php b/lib/public/Security/IHasher.php index 378c2cf3f51ef..d985ffe48ab91 100644 --- a/lib/public/Security/IHasher.php +++ b/lib/public/Security/IHasher.php @@ -47,4 +47,11 @@ public function hash(string $message): string; * @since 8.0.0 */ public function verify(string $message, string $hash, &$newHash = null): bool ; + + /** + * Check if the prefixed hash is valid + * + * @since 30.0.0 + */ + public function validate(string $prefixedHash): bool; } From 415edcac9b48983b2266576080bdf4c2fd23482a Mon Sep 17 00:00:00 2001 From: Christopher Ng Date: Thu, 27 Jun 2024 16:56:35 -0700 Subject: [PATCH 2/3] chore: More explicit splitHash typing Signed-off-by: Christopher Ng --- lib/private/Security/Hasher.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/private/Security/Hasher.php b/lib/private/Security/Hasher.php index 081cac5116167..3ab0e1bbcac1f 100644 --- a/lib/private/Security/Hasher.php +++ b/lib/private/Security/Hasher.php @@ -79,7 +79,7 @@ public function hash(string $message): string { /** * Get the version and hash from a prefixedHash * @param string $prefixedHash - * @return null|array Null if the hash is not prefixed, otherwise array('version' => 1, 'hash' => 'foo') + * @return null|array{version: int, hash: string} Null if the hash is not prefixed, otherwise array('version' => 1, 'hash' => 'foo') */ protected function splitHash(string $prefixedHash): ?array { $explodedString = explode('|', $prefixedHash, 2); From 48b69c53dca7d1c7889deea9e82ca25decadb39e Mon Sep 17 00:00:00 2001 From: Christopher Ng Date: Thu, 4 Jul 2024 16:57:09 -0700 Subject: [PATCH 3/3] test: Test hash validation Signed-off-by: Christopher Ng --- tests/lib/Security/HasherTest.php | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/lib/Security/HasherTest.php b/tests/lib/Security/HasherTest.php index 0eb233de485df..f51b4ee39db02 100644 --- a/tests/lib/Security/HasherTest.php +++ b/tests/lib/Security/HasherTest.php @@ -264,4 +264,29 @@ public function testHashUsePasswordDefault() { $info = password_get_info($relativePath['hash']); $this->assertEquals(PASSWORD_BCRYPT, $info['algo']); } + + public function testValidHash() { + $hash = '3|$argon2id$v=19$m=65536,t=4,p=1$czFCSjk3LklVdXppZ2VCWA$li0NgdXe2/jwSRxgteGQPWlzJU0E0xdtfHbCbrpych0'; + + $isValid = $this->hasher->validate($hash); + + $this->assertTrue($isValid); + } + + public function testValidGeneratedHash() { + $message = 'secret'; + $hash = $this->hasher->hash($message); + + $isValid = $this->hasher->validate($hash); + + $this->assertTrue($isValid); + } + + public function testInvalidHash() { + $invalidHash = 'someInvalidHash'; + + $isValid = $this->hasher->validate($invalidHash); + + $this->assertFalse($isValid); + } }