| 
<?phpdeclare(strict_types=1);
 namespace ParagonIE\Paserk\Types;
 
 use ParagonIE\HiddenString\HiddenString;
 use ParagonIE\Paserk\{
 ConstraintTrait,
 Operations\PBKW,
 PaserkException,
 PaserkTypeInterface,
 Util
 };
 use ParagonIE\Paseto\{
 Exception\InvalidVersionException,
 KeyInterface,
 Keys\AsymmetricSecretKey,
 ProtocolCollection,
 ProtocolInterface
 };
 use SodiumException;
 use function
 array_key_exists,
 explode;
 
 /**
 * Class SecretPW
 * @package ParagonIE\Paserk\Types
 */
 class SecretPW implements PaserkTypeInterface
 {
 use ConstraintTrait;
 
 /** @var array<string, string> */
 protected array $localCache = [];
 
 /** @var array $options */
 protected array $options = [];
 
 /** @var HiddenString $password */
 protected HiddenString $password;
 
 /**
 * SecretPW constructor.
 * @param HiddenString $password
 * @param array $options
 *
 * @param ProtocolInterface ...$version
 * @throws InvalidVersionException
 */
 public function __construct(
 HiddenString $password,
 array $options = [],
 ProtocolInterface ...$version
 ) {
 $this->password = $password;
 $this->options = $options;
 $this->localCache = [];
 if (count($version) > 0) {
 $this->collection = new ProtocolCollection(...$version);
 } else {
 $this->collection = ProtocolCollection::v4();
 }
 }
 
 /**
 * Decode a PASERK string into a PASETO secret key
 *
 * @param string $paserk
 * @return KeyInterface
 *
 * @throws PaserkException
 */
 public function decode(string $paserk): KeyInterface
 {
 $pieces = explode('.', $paserk);
 $header = $pieces[0];
 $version = Util::getPasetoVersion($header);
 $this->throwIfInvalidProtocol($version);
 /// @SPEC DETAIL: Algorithm Lucidity
 
 $pbkw = PBKW::forVersion($version);
 
 return $pbkw->secretPwUnwrap($paserk, $this->password);
 }
 
 /**
 * Encode a PASETO secret key into a PASERK string
 *
 * @param KeyInterface $key
 * @return string
 *
 * @throws InvalidVersionException
 * @throws PaserkException
 */
 public function encode(KeyInterface $key): string
 {
 if (!($key instanceof AsymmetricSecretKey)) {
 throw new PaserkException('Only asymmetric secret keys are allowed here');
 }
 $this->throwIfInvalidProtocol($key->getProtocol());
 /// @SPEC DETAIL: Algorithm Lucidity
 
 $secretId = (new SecretType($key->getProtocol()))->encode($key);
 if (!array_key_exists($secretId, $this->localCache)) {
 $this->localCache[$secretId] = PBKW::forVersion($key->getProtocol())
 ->secretPwWrap($key, $this->password, $this->options);
 }
 return $this->localCache[$secretId];
 }
 
 /**
 * @return string
 */
 public static function getTypeLabel(): string
 {
 return 'secret-pw';
 }
 
 /**
 * Get the sid PASERK for the PASERK representation of this secret key.
 *
 * @param KeyInterface $key
 * @return string
 *
 * @throws InvalidVersionException
 * @throws PaserkException
 * @throws SodiumException
 */
 public function id(KeyInterface $key): string
 {
 return Sid::encode(
 $key->getProtocol(),
 $this->encode($key)
 );
 }
 }
 
 |