diff --git a/src/Features/VerifyTrait.php b/src/Features/VerifyTrait.php index f1ca2eb..b0e7c66 100644 --- a/src/Features/VerifyTrait.php +++ b/src/Features/VerifyTrait.php @@ -60,7 +60,7 @@ public function verifyInclusionProof( int $treeSize ): bool { $this->assertValidHashFunction($hashFunction); - $rootBytes = $this->decodeMerkleRoot($merkleRoot); + $rootBytes = $this->decodeMerkleRoot($merkleRoot, $hashFunction); // Verify manually following RFC 9162 ยง2.1.3 since Tree::verifyInclusionProof // requires the full tree which we don't have on the client side. @@ -386,8 +386,9 @@ protected function parseInclusionProof(array $row): InclusionProof * Decode a Merkle root from its prefixed format. * * @throws ClientException If the format is invalid + * Supported $hashFunction 'sha256', 'sha384', 'sha512', 'blake2b' */ - protected function decodeMerkleRoot(string $merkleRoot): string + protected function decodeMerkleRoot(string $merkleRoot, string $hashFunction): string { $prefix = 'pkd-mr-v1:'; if (!str_starts_with($merkleRoot, $prefix)) { @@ -397,8 +398,16 @@ protected function decodeMerkleRoot(string $merkleRoot): string $encoded = substr($merkleRoot, strlen($prefix)); $decoded = Base64UrlSafe::decodeNoPadding($encoded); - if (strlen($decoded) < 32) { - throw new ClientException('Invalid Merkle root format: expected 32+ bytes'); + $expectedByteLen = match ($hashFunction) { + 'sha256' => 32, + 'sha384' => 48, + 'sha512' => 64, + 'blake2b' => 32, // variable-length 8 to 512 bits (1 to 64 bytes) but 32 minimum for safety. + default => 32, // Fallback to 32 bytes as a minimum but $hashFunc should never be null. + }; + + if (strlen($decoded) < $expectedByteLen) { + throw new ClientException("decodeMerkleRoot: Invalid Merkle root format. Expected a minimum of {$expectedByteLen} bytes for {$hashFunction} - Got {$decoded}"); } return $decoded; diff --git a/tests/unit/Features/VerifyTraitTest.php b/tests/unit/Features/VerifyTraitTest.php index e5d105a..880fcc6 100644 --- a/tests/unit/Features/VerifyTraitTest.php +++ b/tests/unit/Features/VerifyTraitTest.php @@ -378,7 +378,7 @@ public function testDecodeMerkleRootThrowsOnInvalidLength(): void $invalidRoot = 'pkd-mr-v1:' . Base64UrlSafe::encodeUnpadded(str_repeat("\x00", 16)); $this->expectException(ClientException::class); - $this->expectExceptionMessage('Invalid Merkle root format: expected 32+ bytes'); + $this->expectExceptionMessage('decodeMerkleRoot: Invalid Merkle root format. Expected a minimum of 32 bytes for sha256'); $client->verifyInclusionProof('sha256', $invalidRoot, 'leaf1', $proof, $tree->getSize()); }