| 
<?phpdeclare(strict_types=1);
 namespace ParagonIE\Paserk\Types;
 
 use ParagonIE\Paserk\{
 ConstraintTrait,
 PaserkException,
 PaserkTypeInterface
 };
 use ParagonIE\Paserk\Operations\{
 PKE,
 Key\SealingPublicKey,
 Key\SealingSecretKey
 };
 use ParagonIE\Paseto\{
 Exception\InvalidVersionException,
 KeyInterface,
 Keys\SymmetricKey
 };
 use SodiumException;
 use Throwable;
 use function
 array_key_exists,
 is_null;
 
 /**
 * Class Seal
 * @package ParagonIE\Paserk\Types
 */
 class Seal implements PaserkTypeInterface
 {
 use ConstraintTrait;
 
 /** @var SealingPublicKey $pk */
 protected SealingPublicKey $pk;
 
 /** @var SealingSecretKey|null $sk */
 protected ?SealingSecretKey $sk = null;
 
 /** @var array<string, string> */
 protected array $localCache = [];
 
 /**
 * Seal constructor.
 *
 * @param SealingPublicKey $pk
 * @param SealingSecretKey|null $sk
 */
 public function __construct(SealingPublicKey $pk, ?SealingSecretKey $sk = null)
 {
 $this->pk = $pk;
 if ($sk) {
 $this->sk = $sk;
 }
 $this->localCache = [];
 }
 
 /**
 * @param SealingSecretKey $sk
 * @return self
 *
 * @throws PaserkException
 */
 public static function fromSecretKey(SealingSecretKey $sk): self
 {
 try {
 $pk = new SealingPublicKey($sk->getPublicKey()->raw(), $sk->getProtocol());
 return new self($pk, $sk);
 } catch (Throwable $ex) {
 throw new PaserkException("Could not load public key", 0, $ex);
 }
 }
 
 /**
 * @param string $paserk
 * @return KeyInterface
 *
 * @throws PaserkException
 */
 public function decode(string $paserk): KeyInterface
 {
 if (is_null($this->sk)) {
 throw new PaserkException('No secret key was provided; cannot unseal');
 }
 $unsealed = (new PKE($this->sk->getProtocol()))
 ->unseal($paserk, $this->sk);
 $this->throwIfInvalidProtocol($unsealed->getProtocol());
 /// @SPEC DETAIL: Algorithm Lucidity
 
 return $unsealed;
 }
 
 /**
 * @param KeyInterface $key
 * @return string
 *
 * @throws PaserkException
 * @throws InvalidVersionException
 */
 public function encode(KeyInterface $key): string
 {
 if (!($key instanceof SymmetricKey)) {
 throw new PaserkException('Only symmetric keys are allowed here');
 }
 $this->throwIfInvalidProtocol($key->getProtocol());
 /// @SPEC DETAIL: Algorithm Lucidity
 
 $localId = (new Local($this->pk->getProtocol()))->encode($key);
 if (!array_key_exists($localId, $this->localCache)) {
 $pke = new PKE($this->pk->getProtocol());
 $this->localCache[$localId] = $pke->seal($key, $this->pk);
 }
 return $this->localCache[$localId];
 }
 
 /**
 * @return string
 */
 public static function getTypeLabel(): string
 {
 return 'seal';
 }
 
 /**
 * @param KeyInterface $key
 * @return string
 *
 * @throws InvalidVersionException
 * @throws PaserkException
 * @throws SodiumException
 */
 public function id(KeyInterface $key): string
 {
 return Lid::encode(
 $key->getProtocol(),
 $this->encode($key)
 );
 }
 }
 
 |