|
| 1 | +<?php |
| 2 | + |
| 3 | +declare(strict_types=1); |
| 4 | + |
| 5 | +namespace Webauthn\Tests\Unit; |
| 6 | + |
| 7 | +use PHPUnit\Framework\Attributes\Test; |
| 8 | +use PHPUnit\Framework\TestCase; |
| 9 | +use Symfony\Component\HttpClient\MockHttpClient; |
| 10 | +use Symfony\Component\HttpClient\Response\MockResponse; |
| 11 | +use Webauthn\Exception\CertificateChainException; |
| 12 | +use Webauthn\MetadataService\CertificateChain\PhpCertificateChainValidator; |
| 13 | + |
| 14 | +/** |
| 15 | + * @internal |
| 16 | + */ |
| 17 | +final class PhpCertificateChainValidatorTest extends TestCase |
| 18 | +{ |
| 19 | + #[Test] |
| 20 | + public function itValidatesCertificateChainWithSelfSignedRootCA(): void |
| 21 | + { |
| 22 | + $leafCert = file_get_contents(__DIR__ . '/../certificates/intermediate-ca/leaf.pem'); |
| 23 | + $intermediateCert = file_get_contents(__DIR__ . '/../certificates/intermediate-ca/intermediate-ca.pem'); |
| 24 | + $rootCert = file_get_contents(__DIR__ . '/../certificates/intermediate-ca/root-ca.pem'); |
| 25 | + |
| 26 | + $httpClient = new MockHttpClient([new MockResponse('')]); |
| 27 | + |
| 28 | + $validator = PhpCertificateChainValidator::create($httpClient); |
| 29 | + |
| 30 | + // Test with root CA as trust anchor (self-signed certificate) |
| 31 | + // The untrusted chain includes leaf and intermediate |
| 32 | + // This should work with both old and new implementation |
| 33 | + $validator->check([$leafCert, $intermediateCert], [$rootCert]); |
| 34 | + |
| 35 | + // If we reach here, validation succeeded |
| 36 | + static::assertTrue(true); |
| 37 | + } |
| 38 | + |
| 39 | + #[Test] |
| 40 | + public function itAcceptsWhenLeafCertificateMatchesTrustAnchor(): void |
| 41 | + { |
| 42 | + $leafCert = file_get_contents(__DIR__ . '/../certificates/intermediate-ca/leaf.pem'); |
| 43 | + |
| 44 | + $httpClient = new MockHttpClient([new MockResponse('')]); |
| 45 | + |
| 46 | + $validator = PhpCertificateChainValidator::create($httpClient); |
| 47 | + |
| 48 | + // When the leaf certificate is the same as the trust anchor, it should be accepted |
| 49 | + // This is a valid case mentioned in FIDO MDS spec: |
| 50 | + // "A trust anchor can be [...] even the attestation certificate itself" |
| 51 | + $validator->check([$leafCert], [$leafCert]); |
| 52 | + |
| 53 | + // If we reach here, validation succeeded |
| 54 | + static::assertTrue(true); |
| 55 | + } |
| 56 | + |
| 57 | + #[Test] |
| 58 | + public function itRejectsCertificateChainWithInvalidTrustAnchor(): void |
| 59 | + { |
| 60 | + $leafCert = file_get_contents(__DIR__ . '/../certificates/intermediate-ca/leaf.pem'); |
| 61 | + $rootCert = file_get_contents(__DIR__ . '/../certificates/intermediate-ca/root-ca.pem'); |
| 62 | + |
| 63 | + $httpClient = new MockHttpClient([new MockResponse('')]); |
| 64 | + |
| 65 | + $validator = PhpCertificateChainValidator::create($httpClient); |
| 66 | + |
| 67 | + // Leaf certificate cannot be verified with root CA directly (missing intermediate) |
| 68 | + $this->expectException(CertificateChainException::class); |
| 69 | + $validator->check([$leafCert], [$rootCert]); |
| 70 | + } |
| 71 | +} |
0 commit comments