diff --git a/src/Methods/HmacSignature.php b/src/Methods/HmacSignature.php index d5d0b9d..cd72779 100644 --- a/src/Methods/HmacSignature.php +++ b/src/Methods/HmacSignature.php @@ -37,7 +37,7 @@ public function __construct(HmacSignatureKeyRepositoryInterface $signatureKeyRep */ public function sign(RequestInterface $request, string $keyId, string $algorithm): RequestInterface { - $signatureData = $this->generateSignature((string) $request->getBody(), $keyId, $algorithm); + $signatureData = $this->generateSignature((string)$request->getBody(), $keyId, $algorithm); return $request ->withHeader(RequestSigningMethodInterface::SIGNATURE_HEADER, $signatureData->getSignature()) @@ -90,10 +90,10 @@ public function verify(RequestInterface $request): bool $algorithm = $request->getHeaderLine(RequestSigningMethodInterface::SIGNATURE_ALGORITHM_HEADER); $generatedSignature = $this - ->generateSignature((string) $request->getBody(), $keyId, $algorithm) + ->generateSignature((string)$request->getBody(), $keyId, $algorithm) ->getSignature(); - return hash_equals($signature, $generatedSignature); + return hash_equals($signature, $generatedSignature) || $this->doFallbackVerification($request); } catch (Throwable $throwable) { // We do nothing with the exception. The request stays marked as "invalid". } @@ -105,4 +105,40 @@ public function supports(string $method): bool { return strtolower(self::METHOD_NAME) === strtolower($method); } + + /** + * Since we've moved away from hash() signing in favor of hash_hmac we should (for now) support the + * hash verification method to keep older implementations valid + * + * @throws UnsupportedHashingAlgorithmException + * @throws SignatureKeyNotFoundException + */ + private function doFallbackVerification(RequestInterface $request): bool + { + $keyId = $request->getHeaderLine(RequestSigningMethodInterface::SIGNATURE_KEY_ID_HEADER); + $signature = $request->getHeaderLine(RequestSigningMethodInterface::SIGNATURE_HEADER); + $algorithm = $request->getHeaderLine(RequestSigningMethodInterface::SIGNATURE_ALGORITHM_HEADER); + + $key = $this->signatureKeyRepository->findOneById($keyId); + + $algorithm = strtolower($algorithm); + + if (in_array($algorithm, hash_algos()) === false) { + throw UnsupportedHashingAlgorithmException::forAlgorithm($algorithm); + } + + $generatedSignature = hash( + $algorithm, + implode( + ':', + [ + $key->getId(), + $key->getSecret(), + (string)$request->getBody(), + ] + ) + ); + + return hash_equals($signature, $generatedSignature); + } } diff --git a/tests/Unit/Methods/HmacSignatureTest.php b/tests/Unit/Methods/HmacSignatureTest.php index 08d76ee..f3297b8 100644 --- a/tests/Unit/Methods/HmacSignatureTest.php +++ b/tests/Unit/Methods/HmacSignatureTest.php @@ -122,6 +122,21 @@ public function testItReturnsFalseForAnHashingAlgorithm(): void $this->assertFalse($hmacSignature->verify($request)); } + public function testItCanVerifyUsingTheFallbackMethod(): void + { + // First, we'll create a dummy request + $request = $this->getSignedDummyRequest()->withHeader( + RequestSigningMethodInterface::SIGNATURE_HEADER, + '6086a395c7fe9b47f47cee4623b76cc4cc79cb3f44e968091edc447cfe7e7533dc2564f49b22bc1f225c24f172be0d2a5b5893e0825c6fde782a63f3e43ce7d1' + ); + + // Next, we'll instantiate the HmacSignature class + $hmacSignature = new HmacSignature($this->getKeyRepository($this->getDummySignatureKey())); + + // Next, we'll verify that this request passes its verification + $this->assertTrue($hmacSignature->verify($request)); + } + private function getDummyRequest(): RequestInterface { return new Request('POST', 'https://pay.nl');