| 
<?phpdeclare(strict_types=1);
 namespace ParagonIE\Paserk\Operations;
 
 use ParagonIE\Paserk\Operations\PKE\{
 PKEv3,
 PKEv4
 };
 use ParagonIE\Paserk\Operations\Key\{
 SealingPublicKey,
 SealingSecretKey
 };
 use ParagonIE\Paserk\PaserkException;
 use ParagonIE\Paseto\Keys\SymmetricKey;
 use ParagonIE\Paseto\ProtocolInterface;
 use function
 array_pop,
 count,
 explode,
 implode;
 
 /**
 * Class PKE
 * @package ParagonIE\Paserk\Operations
 */
 class PKE
 {
 const DOMAIN_SEPARATION_ENCRYPT = "\x01";
 const DOMAIN_SEPARATION_AUTH = "\x02";
 
 protected ProtocolInterface $version;
 
 public function __construct(ProtocolInterface $version)
 {
 $this->version = $version;
 }
 
 /**
 * @return PKEInterface
 * @throws PaserkException
 */
 public function getSealer(): PKEInterface
 {
 return match ($this->version::header()) {
 'v3' => new PKEv3(),
 'v4' => new PKEv4(),
 default => throw new PaserkException(
 'Unknown version: ' . $this->version::header()
 ),
 };
 }
 
 /**
 * @param SymmetricKey $ptk     Symmetric key to seal with this public key
 * @param SealingPublicKey $pk  Wrapping key
 * @return string
 *
 * @throws PaserkException
 */
 public function seal(SymmetricKey $ptk, SealingPublicKey $pk): string
 {
 if (!hash_equals($ptk->getProtocol()::header(), $this->version::header())) {
 throw new PaserkException('Plaintext key is not intended for this version');
 }
 if (!hash_equals($pk->getProtocol()::header(), $this->version::header())) {
 throw new PaserkException('Wrapping key is not intended for this version');
 }
 /// @SPEC DETAIL: Algorithm Lucidity enforcement.
 
 $sealer = $this->getSealer();
 return $sealer::header() . $sealer->seal($ptk, $pk);
 }
 
 /**
 * @param string $paserk
 * @param SealingSecretKey $sk  Unwrapping key
 * @return SymmetricKey
 *
 * @throws PaserkException
 */
 public function unseal(string $paserk, SealingSecretKey $sk): SymmetricKey
 {
 // Step 1:
 if (!hash_equals($sk->getProtocol()::header(), $this->version::header())) {
 throw new PaserkException('Unwrapping key is not intended for this version');
 }
 $pieces = explode('.', $paserk);
 if (count($pieces) !== 3) {
 throw new PaserkException('Invalid PASERK');
 }
 $payload = array_pop($pieces);
 $header = implode('.', $pieces) . '.'; // Recreate header
 $sealer = $this->getSealer();
 return $sealer->unseal($header, $payload, $sk);
 }
 }
 
 |