<?php
 declare(strict_types=1); namespace SpomkyLabs\Pki\X509\CertificationPath; use ArrayIterator; use Countable; use IteratorAggregate; use LogicException; use SpomkyLabs\Pki\CryptoBridge\Crypto; use SpomkyLabs\Pki\X509\Certificate\Certificate; use SpomkyLabs\Pki\X509\Certificate\CertificateBundle; use SpomkyLabs\Pki\X509\Certificate\CertificateChain; use SpomkyLabs\Pki\X509\CertificationPath\PathBuilding\CertificationPathBuilder; use SpomkyLabs\Pki\X509\CertificationPath\PathValidation\PathValidationConfig; use SpomkyLabs\Pki\X509\CertificationPath\PathValidation\PathValidationResult; use SpomkyLabs\Pki\X509\CertificationPath\PathValidation\PathValidator; use function count; final class CertificationPath implements Countable, IteratorAggregate { private readonly array $certificates; private function __construct(Certificate ...$certificates) { $this->certificates = $certificates; } public static function create(Certificate ...$certificates): self { return new self(...$certificates); } public static function fromCertificateChain(CertificateChain $chain): self { return self::create(...array_reverse($chain->certificates(), false)); } public static function toTarget( Certificate $target, CertificateBundle $trust_anchors, ?CertificateBundle $intermediate = null ): self { return CertificationPathBuilder::create($trust_anchors)->shortestPathToTarget($target, $intermediate); } public static function fromTrustAnchorToTarget( Certificate $trust_anchor, Certificate $target, ?CertificateBundle $intermediate = null ): self { return self::toTarget($target, CertificateBundle::create($trust_anchor), $intermediate); } public function certificates(): array { return $this->certificates; } public function trustAnchorCertificate(): Certificate { if (count($this->certificates) === 0) { throw new LogicException('No certificates.'); } return $this->certificates[0]; } public function endEntityCertificate(): Certificate { if (count($this->certificates) === 0) { throw new LogicException('No certificates.'); } return $this->certificates[count($this->certificates) - 1]; } public function certificateChain(): CertificateChain { return CertificateChain::create(...array_reverse($this->certificates, false)); } public function startsWith(Certificate ...$certs): bool { $n = count($certs); if ($n > count($this->certificates)) { return false; } for ($i = 0; $i < $n; ++$i) { if (! $certs[$i]->equals($this->certificates[$i])) { return false; } } return true; } public function validate(PathValidationConfig $config, ?Crypto $crypto = null): PathValidationResult { $crypto ??= Crypto::getDefault(); return PathValidator::create($crypto, $config, ...$this->certificates)->validate(); } public function count(): int { return count($this->certificates); } public function getIterator(): ArrayIterator { return new ArrayIterator($this->certificates); } } 