<?php
declare(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)
);
}
}
|