headers->getFirst('Signature'); if ($signatureHeader === null) { return VerificationResult::failure('Missing Signature header'); } $signature = RequestSignature::fromHttpSignatureHeader($signatureHeader); // Check if signature is expired if ($signature->isExpired()) { return VerificationResult::failure('Signature has expired'); } // Get signing key $key = $this->keyRepository->findByKeyId($signature->keyId); if ($key === null) { return VerificationResult::failure('Unknown key ID: ' . $signature->keyId); } if (! $key->isValid()) { return VerificationResult::failure('Signing key is not valid'); } // Verify algorithm matches if ($key->algorithm !== $signature->algorithm) { return VerificationResult::failure('Algorithm mismatch'); } // Build signing string $signingString = new SigningString($request); $stringToSign = $signingString->build($signature->headers); // Verify signature $isValid = $this->verifySignature($stringToSign, $signature->signature, $key); if ($isValid) { return VerificationResult::success($signature, $key); } else { return VerificationResult::failure('Invalid signature'); } } catch (\Exception $e) { return VerificationResult::failure('Signature verification error: ' . $e->getMessage()); } } /** * Verify signature using the appropriate algorithm */ private function verifySignature(string $data, string $signature, SigningKey $key): bool { return match ($key->algorithm) { SigningAlgorithm::HMAC_SHA256 => $this->verifyHmacSignature($data, $signature, $key, 'sha256'), SigningAlgorithm::HMAC_SHA512 => $this->verifyHmacSignature($data, $signature, $key, 'sha512'), SigningAlgorithm::RSA_SHA256 => $this->verifyRsaSignature($data, $signature, $key), SigningAlgorithm::ED25519 => throw new \InvalidArgumentException('ED25519 not yet implemented'), }; } /** * Verify HMAC signature */ private function verifyHmacSignature(string $data, string $signature, SigningKey $key, string $algorithm): bool { $expectedSignature = hash_hmac($algorithm, $data, $key->keyMaterial, true); $expectedSignatureBase64 = base64_encode($expectedSignature); return hash_equals($expectedSignatureBase64, $signature); } /** * Verify RSA signature */ private function verifyRsaSignature(string $data, string $signature, SigningKey $key): bool { // Extract public key from private key $privateKey = openssl_pkey_get_private($key->keyMaterial); if ($privateKey === false) { return false; } $keyDetails = openssl_pkey_get_details($privateKey); if ($keyDetails === false || ! isset($keyDetails['key'])) { return false; } $publicKey = openssl_pkey_get_public($keyDetails['key']); if ($publicKey === false) { return false; } $signatureBinary = base64_decode($signature); if ($signatureBinary === false) { return false; } $result = openssl_verify($data, $signatureBinary, $publicKey, $key->algorithm->getOpenSSLSignatureAlgorithm()); return $result === 1; } /** * Verify digest header if present */ public function verifyDigest(Request $request): bool { $digestHeader = $request->headers->getFirst('Digest'); if ($digestHeader === null) { return true; // No digest to verify } $body = $request->body; // Parse digest header: "SHA256=base64hash" if (! preg_match('/^([A-Z0-9]+)=(.+)$/', $digestHeader, $matches)) { return false; } $algorithm = strtolower($matches[1]); $expectedHash = $matches[2]; $actualHash = base64_encode(hash($algorithm, $body, true)); return hash_equals($expectedHash, $actualHash); } }