From 2a31df04128d8262e8916e7fb0b4b7359b768e77 Mon Sep 17 00:00:00 2001 From: Jacob Thomason Date: Sun, 8 Dec 2024 02:43:18 -0500 Subject: [PATCH 01/10] Resolved PHP 8.4 deprecations --- src/Builder.php | 36 +++++++++++------------ src/Exception/PasetoException.php | 4 +-- src/JsonToken.php | 14 ++++----- src/Keys/AsymmetricSecretKey.php | 2 +- src/Keys/Base/AsymmetricPublicKey.php | 12 ++++++-- src/Keys/Base/AsymmetricSecretKey.php | 8 ++--- src/Keys/Base/SymmetricKey.php | 9 ++++-- src/Keys/Version3/AsymmetricPublicKey.php | 7 +++-- src/Keys/Version3/AsymmetricSecretKey.php | 6 ++-- src/Keys/Version4/AsymmetricPublicKey.php | 7 +++-- src/Keys/Version4/AsymmetricSecretKey.php | 9 ++++-- src/Parser.php | 14 ++++----- src/Protocol/Version3.php | 4 +-- src/Protocol/Version4.php | 4 +-- src/ProtocolInterface.php | 4 +-- src/Rules/NotExpired.php | 4 ++- src/Rules/ValidAt.php | 3 +- 17 files changed, 84 insertions(+), 63 deletions(-) diff --git a/src/Builder.php b/src/Builder.php index e4b0e24..f7ad446 100644 --- a/src/Builder.php +++ b/src/Builder.php @@ -50,9 +50,9 @@ class Builder extends PasetoBase * @throws PasetoException */ public function __construct( - JsonToken $baseToken = null, - ProtocolInterface $protocol = null, - SendingKey $key = null + ?JsonToken $baseToken = null, + ?ProtocolInterface $protocol = null, + ?SendingKey $key = null ) { if (!$baseToken) { $baseToken = new JsonToken(); @@ -176,8 +176,8 @@ public function getImplicitAssertions(): array */ public static function getLocal( SymmetricKey $key, - ProtocolInterface $version = null, - JsonToken $baseToken = null + ?ProtocolInterface $version = null, + ?JsonToken $baseToken = null ): self { if (!$version) { $version = $key->getProtocol(); @@ -202,8 +202,8 @@ public static function getLocal( */ public static function getLocalWithKeyRing( SendingKeyRing $key, - ProtocolInterface $version = null, - JsonToken $baseToken = null + ?ProtocolInterface $version = null, + ?JsonToken $baseToken = null ): self { if (!$version) { $version = $key->getProtocol(); @@ -228,8 +228,8 @@ public static function getLocalWithKeyRing( */ public static function getPublic( AsymmetricSecretKey $key, - ProtocolInterface $version = null, - JsonToken $baseToken = null + ?ProtocolInterface $version = null, + ?JsonToken $baseToken = null ): self { if (!$version) { $version = $key->getProtocol(); @@ -254,8 +254,8 @@ public static function getPublic( */ public static function getPublicWithKeyRing( SendingKeyRing $key, - ProtocolInterface $version = null, - JsonToken $baseToken = null + ?ProtocolInterface $version = null, + ?JsonToken $baseToken = null ): self { if (!$version) { $version = $key->getProtocol(); @@ -308,7 +308,7 @@ public function setAudience(string $aud): self * @param DateTimeInterface|null $time * @return self */ - public function setExpiration(DateTimeInterface $time = null): self + public function setExpiration(?DateTimeInterface $time = null): self { if (!$time) { $time = new DateTime('NOW'); @@ -348,7 +348,7 @@ public function setImplicitAssertions(array $assertions): self * @param DateTimeInterface|null $time * @return self */ - public function setIssuedAt(DateTimeInterface $time = null): self + public function setIssuedAt(?DateTimeInterface $time = null): self { if (!$time) { $time = new DateTime('NOW'); @@ -384,7 +384,7 @@ public function setJti(string $id): self * @param DateTimeInterface|null $time * @return self */ - public function setNotBefore(DateTimeInterface $time = null): self + public function setNotBefore(?DateTimeInterface $time = null): self { if (!$time) { $time = new DateTime('NOW'); @@ -569,7 +569,7 @@ public function setJsonToken(JsonToken $token): self * * @return self */ - public function setVersion(ProtocolInterface $version = null): self + public function setVersion(?ProtocolInterface $version = null): self { if (!$version) { $version = new Version4(); @@ -716,7 +716,7 @@ public function withClaims(array $claims): self * @param DateTimeInterface|null $time * @return self */ - public function withExpiration(DateTimeInterface $time = null): self + public function withExpiration(?DateTimeInterface $time = null): self { return (clone $this)->setExpiration($time); } @@ -765,7 +765,7 @@ public function withImplicitAssertions(array $implicit): self * @param DateTimeInterface|null $time * @return self */ - public function withIssuedAt(DateTimeInterface $time = null): self + public function withIssuedAt(?DateTimeInterface $time = null): self { return (clone $this)->setIssuedAt($time); } @@ -798,7 +798,7 @@ public function withJti(string $id): self * @param DateTimeInterface|null $time * @return self */ - public function withNotBefore(DateTimeInterface $time = null): self + public function withNotBefore(?DateTimeInterface $time = null): self { return (clone $this)->setNotBefore($time); } diff --git a/src/Exception/PasetoException.php b/src/Exception/PasetoException.php index e048be7..3b910cf 100644 --- a/src/Exception/PasetoException.php +++ b/src/Exception/PasetoException.php @@ -9,14 +9,14 @@ * Class PasetoException * @package ParagonIE\Paseto\Exception */ -class PasetoException extends Exception +class PasetoException extends \Exception { /** * @param string $message * @param int $code * @param Throwable|null $previous */ - public function __construct($message = "", $code = 0, Throwable $previous = null) + public function __construct($message = "", $code = 0, ?Throwable $previous = null) { parent::__construct($message, $code, $previous); $this->setHelpfulMessage(ExceptionCode::explainErrorCode($code)); diff --git a/src/JsonToken.php b/src/JsonToken.php index 6e5433e..6e429b1 100644 --- a/src/JsonToken.php +++ b/src/JsonToken.php @@ -197,7 +197,7 @@ public function has(string $claim): bool { return array_key_exists($claim, $this->claims); } - + /** * Set a claim to an arbitrary value. * @@ -241,7 +241,7 @@ public function setClaims(array $claims): self * @param DateTimeInterface|null $time * @return self */ - public function setExpiration(DateTimeInterface $time = null): self + public function setExpiration(?DateTimeInterface $time = null): self { if (!$time) { $time = new DateTime('NOW'); @@ -287,7 +287,7 @@ public function setFooterArray(array $footer = []): self * @param DateTimeInterface|null $time * @return self */ - public function setIssuedAt(DateTimeInterface $time = null): self + public function setIssuedAt(?DateTimeInterface $time = null): self { if (!$time) { $time = new DateTime('NOW'); @@ -326,7 +326,7 @@ public function setJti(string $id): self * @param DateTimeInterface|null $time * @return self */ - public function setNotBefore(DateTimeInterface $time = null): self + public function setNotBefore(?DateTimeInterface $time = null): self { if (!$time) { $time = new DateTime('NOW'); @@ -387,7 +387,7 @@ public function withClaims(array $claims): self * @param DateTimeInterface|null $time * @return self */ - public function withExpiration(DateTimeInterface $time = null): self + public function withExpiration(?DateTimeInterface $time = null): self { return (clone $this)->setExpiration($time); } @@ -422,7 +422,7 @@ public function withFooterArray(array $footer = []): self * @param DateTimeInterface|null $time * @return self */ - public function withIssuedAt(DateTimeInterface $time = null): self + public function withIssuedAt(?DateTimeInterface $time = null): self { return (clone $this)->setIssuedAt($time); } @@ -455,7 +455,7 @@ public function withJti(string $id): self * @param DateTimeInterface|null $time * @return self */ - public function withNotBefore(DateTimeInterface $time = null): self + public function withNotBefore(?DateTimeInterface $time = null): self { return (clone $this)->setNotBefore($time); } diff --git a/src/Keys/AsymmetricSecretKey.php b/src/Keys/AsymmetricSecretKey.php index eb101cf..a51cd34 100644 --- a/src/Keys/AsymmetricSecretKey.php +++ b/src/Keys/AsymmetricSecretKey.php @@ -23,7 +23,7 @@ class AsymmetricSecretKey extends BaseAsymmetricSecretKey { public function __construct( string $keyData, - ProtocolInterface $protocol = null + ?ProtocolInterface $protocol = null ) { if (is_null($protocol)) { $protocol = new Version4(); diff --git a/src/Keys/Base/AsymmetricPublicKey.php b/src/Keys/Base/AsymmetricPublicKey.php index e65c30d..6839b09 100644 --- a/src/Keys/Base/AsymmetricPublicKey.php +++ b/src/Keys/Base/AsymmetricPublicKey.php @@ -117,7 +117,10 @@ public static function v4(string $keyMaterial): V4AsymmetricPublicKey * * @throws Exception */ - public static function newVersionKey(string $keyMaterial, ProtocolInterface $protocol = null): self + public static function newVersionKey( + string $keyMaterial, + ?ProtocolInterface $protocol = null, + ): self { $protocol = $protocol ?? new Version4(); @@ -155,7 +158,10 @@ abstract public function encodePem(): string; * @throws Exception * @throws TypeError */ - public static function fromEncodedString(string $encoded, ProtocolInterface $version = null): self + public static function fromEncodedString( + string $encoded, + ?ProtocolInterface $version = null, + ): self { if (!$version) { $version = new Version4(); @@ -175,7 +181,7 @@ public static function fromEncodedString(string $encoded, ProtocolInterface $ver * * @throws Exception */ - public static function importPem(string $pem, ProtocolInterface $protocol = null): self + public static function importPem(string $pem, ?ProtocolInterface $protocol = null): self { $protocol = $protocol ?? new Version4(); diff --git a/src/Keys/Base/AsymmetricSecretKey.php b/src/Keys/Base/AsymmetricSecretKey.php index 6f02495..7157875 100644 --- a/src/Keys/Base/AsymmetricSecretKey.php +++ b/src/Keys/Base/AsymmetricSecretKey.php @@ -159,7 +159,7 @@ public static function v4(string $keyMaterial): V4AsymmetricSecretKey * @throws Exception * @throws TypeError */ - public static function generate(ProtocolInterface $protocol = null): self + public static function generate(?ProtocolInterface $protocol = null): self { $protocol = $protocol ?? new Version4; if (hash_equals($protocol::header(), Version3::HEADER)) { @@ -178,7 +178,7 @@ public static function generate(ProtocolInterface $protocol = null): self * * @throws Exception */ - public static function newVersionKey(string $keyMaterial, ProtocolInterface $protocol = null): self + public static function newVersionKey(string $keyMaterial, ?ProtocolInterface $protocol = null): self { $protocol = $protocol ?? new Version4(); @@ -216,7 +216,7 @@ abstract public function encodePem(): string; * @throws Exception * @throws TypeError */ - public static function fromEncodedString(string $encoded, ProtocolInterface $version = null): self + public static function fromEncodedString(string $encoded, ?ProtocolInterface $version = null): self { if ($version && hash_equals($version::header(), Version3::HEADER)) { return V3AsymmetricSecretKey::fromEncodedString($encoded); @@ -232,7 +232,7 @@ public static function fromEncodedString(string $encoded, ProtocolInterface $ver * * @throws Exception */ - public static function importPem(string $pem, ProtocolInterface $protocol = null): self + public static function importPem(string $pem, ?ProtocolInterface $protocol = null): self { $protocol = $protocol ?? new Version4(); diff --git a/src/Keys/Base/SymmetricKey.php b/src/Keys/Base/SymmetricKey.php index 5acd873..2ab770f 100644 --- a/src/Keys/Base/SymmetricKey.php +++ b/src/Keys/Base/SymmetricKey.php @@ -45,7 +45,7 @@ class SymmetricKey implements ReceivingKey, SendingKey */ public function __construct( string $keyMaterial, - ProtocolInterface $protocol = null + ?ProtocolInterface $protocol = null ) { $this->protocol = $protocol ?? new Version4; @@ -81,7 +81,7 @@ public function __destruct() * * @throws Exception */ - public static function generate(ProtocolInterface $protocol = null): self + public static function generate(?ProtocolInterface $protocol = null): self { $protocol = $protocol ?? new Version4; $length = $protocol::getSymmetricKeyByteLength(); @@ -172,7 +172,10 @@ public function encode(): string * @throws TypeError * @throws PasetoException */ - public static function fromEncodedString(string $encoded, ProtocolInterface $version = null): self + public static function fromEncodedString( + string $encoded, + ?ProtocolInterface $version = null, + ): self { $decoded = Base64UrlSafe::decodeNoPadding($encoded); return new self($decoded, $version); diff --git a/src/Keys/Version3/AsymmetricPublicKey.php b/src/Keys/Version3/AsymmetricPublicKey.php index bdbdd10..d3fdb67 100644 --- a/src/Keys/Version3/AsymmetricPublicKey.php +++ b/src/Keys/Version3/AsymmetricPublicKey.php @@ -70,7 +70,10 @@ public function encodePem(): string ); } - public static function fromEncodedString(string $encoded, ProtocolInterface $version = null): self + public static function fromEncodedString( + string $encoded, + ?ProtocolInterface $version = null, + ): self { $decodeString = Base64UrlSafe::decode($encoded); $length = Binary::safeStrlen($encoded); @@ -105,7 +108,7 @@ public function toHexString(): string * * @throws Exception */ - public static function importPem(string $pem, ProtocolInterface $protocol = null): self + public static function importPem(string $pem, ?ProtocolInterface $protocol = null): self { return new self($pem); } diff --git a/src/Keys/Version3/AsymmetricSecretKey.php b/src/Keys/Version3/AsymmetricSecretKey.php index 6a9bad5..e9e1015 100644 --- a/src/Keys/Version3/AsymmetricSecretKey.php +++ b/src/Keys/Version3/AsymmetricSecretKey.php @@ -36,7 +36,7 @@ public function __construct( parent::__construct($keyData, new Version3()); } - public static function generate(ProtocolInterface $protocol = null): self + public static function generate(?ProtocolInterface $protocol = null): self { return new self( Util::dos2unix(SecretKey::generate(Version3::CURVE)->exportPem()) @@ -64,7 +64,7 @@ public function encodePem(): string return Util::dos2unix($this->key); } - public static function fromEncodedString(string $encoded, ProtocolInterface $version = null): self + public static function fromEncodedString(string $encoded, ?ProtocolInterface $version = null): self { $decoded = Base64UrlSafe::decodeNoPadding($encoded); @@ -101,7 +101,7 @@ public function getPublicKey(): AsymmetricPublicKey ); } - public static function importPem(string $pem, ProtocolInterface $protocol = null): self + public static function importPem(string $pem, ?ProtocolInterface $protocol = null): self { return new self($pem); } diff --git a/src/Keys/Version4/AsymmetricPublicKey.php b/src/Keys/Version4/AsymmetricPublicKey.php index d5501fc..d991afa 100644 --- a/src/Keys/Version4/AsymmetricPublicKey.php +++ b/src/Keys/Version4/AsymmetricPublicKey.php @@ -63,7 +63,10 @@ public function encodePem(): string "-----END PUBLIC KEY-----"; } - public static function fromEncodedString(string $encoded, ProtocolInterface $version = null): self + public static function fromEncodedString( + string $encoded, + ?ProtocolInterface $version = null, + ): self { $decoded = Base64UrlSafe::decode($encoded); return new self($decoded); @@ -81,7 +84,7 @@ public function toHexString(): string * * @throws Exception */ - public static function importPem(string $pem, ProtocolInterface $protocol = null): self + public static function importPem(string $pem, ?ProtocolInterface $protocol = null): self { $formattedKey = str_replace('-----BEGIN PUBLIC KEY-----', '', $pem); $formattedKey = str_replace('-----END PUBLIC KEY-----', '', $formattedKey); diff --git a/src/Keys/Version4/AsymmetricSecretKey.php b/src/Keys/Version4/AsymmetricSecretKey.php index 2747d8e..f51ec87 100644 --- a/src/Keys/Version4/AsymmetricSecretKey.php +++ b/src/Keys/Version4/AsymmetricSecretKey.php @@ -51,7 +51,7 @@ public function __construct(string $keyData) parent::__construct($keyData, new Version4()); } - public static function generate(ProtocolInterface $protocol = null): self + public static function generate(?ProtocolInterface $protocol = null): self { return new self( sodium_crypto_sign_secretkey( @@ -75,7 +75,10 @@ public function encodePem(): string "-----END EC PRIVATE KEY-----"; } - public static function fromEncodedString(string $encoded, ProtocolInterface $version = null): self + public static function fromEncodedString( + string $encoded, + ?ProtocolInterface $version = null, + ): self { $decoded = Base64UrlSafe::decodeNoPadding($encoded); return new self($decoded); @@ -94,7 +97,7 @@ public function getPublicKey(): AsymmetricPublicKey * * @throws Exception */ - public static function importPem(string $pem, ProtocolInterface $protocol = null): self + public static function importPem(string $pem, ?ProtocolInterface $protocol = null): self { $formattedKey = str_replace('-----BEGIN EC PRIVATE KEY-----', '', $pem); $formattedKey = str_replace('-----END EC PRIVATE KEY-----', '', $formattedKey); diff --git a/src/Parser.php b/src/Parser.php index e9fa64b..ceca7d0 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -70,9 +70,9 @@ class Parser extends PasetoBase * @throws PasetoException */ public function __construct( - ProtocolCollection $allowedVersions = null, - Purpose $purpose = null, - ReceivingKey $key = null, + ?ProtocolCollection $allowedVersions = null, + ?Purpose $purpose = null, + ?ReceivingKey $key = null, array $parserRules = [] ) { $this->allowedVersions = $allowedVersions ?? ProtocolCollection::default(); @@ -138,7 +138,7 @@ public static function extractFooter(string $tainted): string */ public static function getLocal( SymmetricKey $key, - ProtocolCollection $allowedVersions = null + ?ProtocolCollection $allowedVersions = null ): self { return new self( $allowedVersions ?? ProtocolCollection::default(), @@ -159,7 +159,7 @@ public static function getLocal( */ public static function getLocalWithKeyRing( ReceivingKeyRing $key, - ProtocolCollection $allowedVersions = null + ?ProtocolCollection $allowedVersions = null ): self { return new self( $allowedVersions ?? ProtocolCollection::default(), @@ -180,7 +180,7 @@ public static function getLocalWithKeyRing( */ public static function getPublic( AsymmetricPublicKey $key, - ProtocolCollection $allowedVersions = null + ?ProtocolCollection $allowedVersions = null ): self { return new self( $allowedVersions ?? ProtocolCollection::default(), @@ -201,7 +201,7 @@ public static function getPublic( */ public static function getPublicWithKeyRing( ReceivingKeyRing $key, - ProtocolCollection $allowedVersions = null + ?ProtocolCollection $allowedVersions = null ): self { return new self( $allowedVersions ?? ProtocolCollection::default(), diff --git a/src/Protocol/Version3.php b/src/Protocol/Version3.php index 9f594ce..367b5be 100644 --- a/src/Protocol/Version3.php +++ b/src/Protocol/Version3.php @@ -179,7 +179,7 @@ protected static function __encrypt( public static function decrypt( string $data, SymmetricKey $key, - string $footer = null, + ?string $footer = null, string $implicit = '' ): string { /* @@ -292,7 +292,7 @@ public static function sign( public static function verify( string $signMsg, AsymmetricPublicKey $key, - string $footer = null, + ?string $footer = null, string $implicit = '' ): string { /* diff --git a/src/Protocol/Version4.php b/src/Protocol/Version4.php index 116bd2d..f4befb5 100644 --- a/src/Protocol/Version4.php +++ b/src/Protocol/Version4.php @@ -173,7 +173,7 @@ protected static function __encrypt( public static function decrypt( string $data, SymmetricKey $key, - string $footer = null, + ?string $footer = null, string $implicit = '' ): string { /* @@ -274,7 +274,7 @@ public static function sign( public static function verify( string $signMsg, AsymmetricPublicKey $key, - string $footer = null, + ?string $footer = null, string $implicit = '' ): string { /* diff --git a/src/ProtocolInterface.php b/src/ProtocolInterface.php index 4886fad..5a29b42 100644 --- a/src/ProtocolInterface.php +++ b/src/ProtocolInterface.php @@ -73,7 +73,7 @@ public static function encrypt( public static function decrypt( string $data, SymmetricKey $key, - string $footer = null, + ?string $footer = null, string $implicit = '' ): string; @@ -105,7 +105,7 @@ public static function sign( public static function verify( string $signMsg, AsymmetricPublicKey $key, - string $footer = null, + ?string $footer = null, string $implicit = '' ): string; } diff --git a/src/Rules/NotExpired.php b/src/Rules/NotExpired.php index 86dd4e3..2f354c0 100644 --- a/src/Rules/NotExpired.php +++ b/src/Rules/NotExpired.php @@ -22,15 +22,17 @@ class NotExpired implements ValidationRuleInterface /** * NotExpired constructor. + * * @param DateTimeInterface|null $now Allows "now" to be overwritten for unit testing */ - public function __construct(DateTimeInterface $now = null) + public function __construct(?DateTimeInterface $now = null) { if (!$now) { $now = new DateTime(); } $this->now = $now; } + /** * @return string */ diff --git a/src/Rules/ValidAt.php b/src/Rules/ValidAt.php index 5d7cf60..9bc4111 100644 --- a/src/Rules/ValidAt.php +++ b/src/Rules/ValidAt.php @@ -22,9 +22,10 @@ class ValidAt implements ValidationRuleInterface /** * ValidAt constructor. + * * @param DateTimeInterface|null $now */ - public function __construct(DateTimeInterface $now = null) + public function __construct(?DateTimeInterface $now = null) { if (!$now) { $now = new DateTime(); From 0e06fff38000fa043f11773a7fb46326386e346f Mon Sep 17 00:00:00 2001 From: Jacob Thomason Date: Sun, 8 Dec 2024 02:45:05 -0500 Subject: [PATCH 02/10] Run workflows for PHP 8.4 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5cd5e5b..9323f14 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: operating-system: ['ubuntu-latest'] - php-versions: ['8.1', '8.2', '8.3'] + php-versions: ['8.1', '8.2', '8.3', '8.4'] phpunit-versions: ['latest'] steps: - name: Checkout From b97975b0364c64013236469f5e4f1ec2b7de112e Mon Sep 17 00:00:00 2001 From: Jacob Thomason Date: Sun, 8 Dec 2024 02:49:48 -0500 Subject: [PATCH 03/10] Update psalm for PHP 8.4 support --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 7d570b5..52483a9 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ }, "require-dev": { "phpunit/phpunit": "^9", - "vimeo/psalm": "^4|^5" + "vimeo/psalm": "dev-master" }, "scripts": { "full-test": [ From e13ac67c4187a7bf5c2dd029ac75700632613f6f Mon Sep 17 00:00:00 2001 From: Jacob Thomason Date: Sun, 8 Dec 2024 03:05:59 -0500 Subject: [PATCH 04/10] Resolved psalm errors --- src/Builder.php | 1 + src/Exception/PasetoException.php | 3 ++- src/Keys/Version4/AsymmetricPublicKey.php | 1 + src/Keys/Version4/AsymmetricSecretKey.php | 1 + src/Util.php | 13 ++++++++++++- 5 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Builder.php b/src/Builder.php index f7ad446..613d6d1 100644 --- a/src/Builder.php +++ b/src/Builder.php @@ -615,6 +615,7 @@ public function toString(): string ->format(DateTime::ATOM); } $claims = json_encode($claimsArray, JSON_FORCE_OBJECT); + assert(is_string($claims)); $protocol = $this->version; ProtocolCollection::throwIfUnsupported($protocol); diff --git a/src/Exception/PasetoException.php b/src/Exception/PasetoException.php index 3b910cf..d305443 100644 --- a/src/Exception/PasetoException.php +++ b/src/Exception/PasetoException.php @@ -7,9 +7,10 @@ /** * Class PasetoException + * * @package ParagonIE\Paseto\Exception */ -class PasetoException extends \Exception +class PasetoException extends Exception { /** * @param string $message diff --git a/src/Keys/Version4/AsymmetricPublicKey.php b/src/Keys/Version4/AsymmetricPublicKey.php index d991afa..1123867 100644 --- a/src/Keys/Version4/AsymmetricPublicKey.php +++ b/src/Keys/Version4/AsymmetricPublicKey.php @@ -88,6 +88,7 @@ public static function importPem(string $pem, ?ProtocolInterface $protocol = nul { $formattedKey = str_replace('-----BEGIN PUBLIC KEY-----', '', $pem); $formattedKey = str_replace('-----END PUBLIC KEY-----', '', $formattedKey); + assert(is_string($formattedKey)); $key = Base64::decode(strtok($formattedKey, "\n")); $prefix = Hex::decode(self::PEM_ENCODE_PREFIX); diff --git a/src/Keys/Version4/AsymmetricSecretKey.php b/src/Keys/Version4/AsymmetricSecretKey.php index f51ec87..348a43d 100644 --- a/src/Keys/Version4/AsymmetricSecretKey.php +++ b/src/Keys/Version4/AsymmetricSecretKey.php @@ -101,6 +101,7 @@ public static function importPem(string $pem, ?ProtocolInterface $protocol = nul { $formattedKey = str_replace('-----BEGIN EC PRIVATE KEY-----', '', $pem); $formattedKey = str_replace('-----END EC PRIVATE KEY-----', '', $formattedKey); + assert(is_string($formattedKey)); $key = Base64::decode(strtok($formattedKey, "\n")); $prefix = Hex::decode(self::PEM_ENCODE_PREFIX); diff --git a/src/Util.php b/src/Util.php index 4c58cea..88a7473 100644 --- a/src/Util.php +++ b/src/Util.php @@ -46,9 +46,15 @@ public static function calculateJsonDepth(string $json): int // Remove whitespace: $stripped = preg_replace('/\s+/', '', $stripped); + if ($stripped === null) { + throw new EncodingException('Invalid JSON string provided', ExceptionCode::INVALID_JSON); + } // Strip everything out of quotes: $stripped = preg_replace('#"[^"]+"([:,}\]])#', '$1', $stripped); + if ($stripped === null) { + throw new EncodingException('Invalid JSON string provided', ExceptionCode::INVALID_JSON); + } // Remove everything that isn't a map or list definition $stripped = preg_replace('#[^\[\]{}]#', '', $stripped); @@ -80,7 +86,12 @@ public static function calculateJsonDepth(string $json): int */ public static function countJsonKeys(string $json): int { - return preg_match_all('#[^\\\]":#', $json); + $keyCount = preg_match_all('#[^\\\]":#', $json); + if ($keyCount === false) { + throw new EncodingException('Invalid JSON string provided', ExceptionCode::INVALID_JSON); + } + + return $keyCount; } /** From 38db5cc69db6d79342f5a89795af03ae8d1293cd Mon Sep 17 00:00:00 2001 From: Jacob Thomason Date: Sun, 8 Dec 2024 03:16:44 -0500 Subject: [PATCH 05/10] Resolved additional psalm errors --- src/Keys/Version4/AsymmetricPublicKey.php | 12 ++++++++++-- src/Keys/Version4/AsymmetricSecretKey.php | 12 ++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/Keys/Version4/AsymmetricPublicKey.php b/src/Keys/Version4/AsymmetricPublicKey.php index 1123867..a18d4c2 100644 --- a/src/Keys/Version4/AsymmetricPublicKey.php +++ b/src/Keys/Version4/AsymmetricPublicKey.php @@ -88,8 +88,16 @@ public static function importPem(string $pem, ?ProtocolInterface $protocol = nul { $formattedKey = str_replace('-----BEGIN PUBLIC KEY-----', '', $pem); $formattedKey = str_replace('-----END PUBLIC KEY-----', '', $formattedKey); - assert(is_string($formattedKey)); - $key = Base64::decode(strtok($formattedKey, "\n")); + if (!is_string($formattedKey)) { + throw new PasetoException('Invalid PEM format', ExceptionCode::UNSPECIFIED_CRYPTOGRAPHIC_ERROR); + } + + $tokenizedKey = strtok($formattedKey, "\n") ?: throw new PasetoException( + 'Invalid PEM format', + ExceptionCode::UNSPECIFIED_CRYPTOGRAPHIC_ERROR, + ); + + $key = Base64::decode($tokenizedKey); $prefix = Hex::decode(self::PEM_ENCODE_PREFIX); return new self(substr($key, strlen($prefix))); diff --git a/src/Keys/Version4/AsymmetricSecretKey.php b/src/Keys/Version4/AsymmetricSecretKey.php index 348a43d..ff9d1cc 100644 --- a/src/Keys/Version4/AsymmetricSecretKey.php +++ b/src/Keys/Version4/AsymmetricSecretKey.php @@ -101,8 +101,16 @@ public static function importPem(string $pem, ?ProtocolInterface $protocol = nul { $formattedKey = str_replace('-----BEGIN EC PRIVATE KEY-----', '', $pem); $formattedKey = str_replace('-----END EC PRIVATE KEY-----', '', $formattedKey); - assert(is_string($formattedKey)); - $key = Base64::decode(strtok($formattedKey, "\n")); + if (!is_string($formattedKey)) { + throw new PasetoException('Invalid PEM format', ExceptionCode::UNSPECIFIED_CRYPTOGRAPHIC_ERROR); + } + + $tokenizedKey = strtok($formattedKey, "\n") ?: throw new PasetoException( + 'Invalid PEM format', + ExceptionCode::UNSPECIFIED_CRYPTOGRAPHIC_ERROR, + ); + + $key = Base64::decode($tokenizedKey); $prefix = Hex::decode(self::PEM_ENCODE_PREFIX); return new self(substr($key, strlen($prefix))); From dd04f42b0dbf0b989609609ca599ba1b3f4e60f8 Mon Sep 17 00:00:00 2001 From: Jacob Thomason Date: Sun, 8 Dec 2024 03:23:42 -0500 Subject: [PATCH 06/10] Only check for PHP 8.4 --- src/Keys/Version4/AsymmetricPublicKey.php | 14 ++++++++------ src/Keys/Version4/AsymmetricSecretKey.php | 14 ++++++++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Keys/Version4/AsymmetricPublicKey.php b/src/Keys/Version4/AsymmetricPublicKey.php index a18d4c2..a6785a5 100644 --- a/src/Keys/Version4/AsymmetricPublicKey.php +++ b/src/Keys/Version4/AsymmetricPublicKey.php @@ -88,14 +88,16 @@ public static function importPem(string $pem, ?ProtocolInterface $protocol = nul { $formattedKey = str_replace('-----BEGIN PUBLIC KEY-----', '', $pem); $formattedKey = str_replace('-----END PUBLIC KEY-----', '', $formattedKey); - if (!is_string($formattedKey)) { - throw new PasetoException('Invalid PEM format', ExceptionCode::UNSPECIFIED_CRYPTOGRAPHIC_ERROR); + if (PHP_VERSION_ID >= 80400) { + if (!is_string($formattedKey)) { + throw new PasetoException('Invalid PEM format', ExceptionCode::UNSPECIFIED_CRYPTOGRAPHIC_ERROR); + } } - $tokenizedKey = strtok($formattedKey, "\n") ?: throw new PasetoException( - 'Invalid PEM format', - ExceptionCode::UNSPECIFIED_CRYPTOGRAPHIC_ERROR, - ); + $tokenizedKey = strtok($formattedKey, "\n"); + if ($tokenizedKey === false) { + throw new PasetoException('Invalid PEM format', ExceptionCode::UNSPECIFIED_CRYPTOGRAPHIC_ERROR); + } $key = Base64::decode($tokenizedKey); $prefix = Hex::decode(self::PEM_ENCODE_PREFIX); diff --git a/src/Keys/Version4/AsymmetricSecretKey.php b/src/Keys/Version4/AsymmetricSecretKey.php index ff9d1cc..5776314 100644 --- a/src/Keys/Version4/AsymmetricSecretKey.php +++ b/src/Keys/Version4/AsymmetricSecretKey.php @@ -101,14 +101,16 @@ public static function importPem(string $pem, ?ProtocolInterface $protocol = nul { $formattedKey = str_replace('-----BEGIN EC PRIVATE KEY-----', '', $pem); $formattedKey = str_replace('-----END EC PRIVATE KEY-----', '', $formattedKey); - if (!is_string($formattedKey)) { - throw new PasetoException('Invalid PEM format', ExceptionCode::UNSPECIFIED_CRYPTOGRAPHIC_ERROR); + if (PHP_VERSION_ID >= 80400) { + if (!is_string($formattedKey)) { + throw new PasetoException('Invalid PEM format', ExceptionCode::UNSPECIFIED_CRYPTOGRAPHIC_ERROR); + } } - $tokenizedKey = strtok($formattedKey, "\n") ?: throw new PasetoException( - 'Invalid PEM format', - ExceptionCode::UNSPECIFIED_CRYPTOGRAPHIC_ERROR, - ); + $tokenizedKey = strtok($formattedKey, "\n"); + if ($tokenizedKey === false) { + throw new PasetoException('Invalid PEM format', ExceptionCode::UNSPECIFIED_CRYPTOGRAPHIC_ERROR); + } $key = Base64::decode($tokenizedKey); $prefix = Hex::decode(self::PEM_ENCODE_PREFIX); From 1a7af97d35c6065e7c93f9319a90e1f431b201bc Mon Sep 17 00:00:00 2001 From: Jacob Thomason Date: Sun, 8 Dec 2024 03:30:27 -0500 Subject: [PATCH 07/10] Suppress psalm error as conflict between PHP versions --- src/Keys/Version4/AsymmetricPublicKey.php | 11 +++++++---- src/Keys/Version4/AsymmetricSecretKey.php | 11 +++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/Keys/Version4/AsymmetricPublicKey.php b/src/Keys/Version4/AsymmetricPublicKey.php index a6785a5..93cc6f1 100644 --- a/src/Keys/Version4/AsymmetricPublicKey.php +++ b/src/Keys/Version4/AsymmetricPublicKey.php @@ -88,10 +88,13 @@ public static function importPem(string $pem, ?ProtocolInterface $protocol = nul { $formattedKey = str_replace('-----BEGIN PUBLIC KEY-----', '', $pem); $formattedKey = str_replace('-----END PUBLIC KEY-----', '', $formattedKey); - if (PHP_VERSION_ID >= 80400) { - if (!is_string($formattedKey)) { - throw new PasetoException('Invalid PEM format', ExceptionCode::UNSPECIFIED_CRYPTOGRAPHIC_ERROR); - } + + /** + * @psalm-suppress DocblockTypeContradiction + * PHP 8.4 updated the docblock return for str_replace, which makes this check required + */ + if (!is_string($formattedKey)) { + throw new PasetoException('Invalid PEM format', ExceptionCode::UNSPECIFIED_CRYPTOGRAPHIC_ERROR); } $tokenizedKey = strtok($formattedKey, "\n"); diff --git a/src/Keys/Version4/AsymmetricSecretKey.php b/src/Keys/Version4/AsymmetricSecretKey.php index 5776314..6ff2994 100644 --- a/src/Keys/Version4/AsymmetricSecretKey.php +++ b/src/Keys/Version4/AsymmetricSecretKey.php @@ -101,10 +101,13 @@ public static function importPem(string $pem, ?ProtocolInterface $protocol = nul { $formattedKey = str_replace('-----BEGIN EC PRIVATE KEY-----', '', $pem); $formattedKey = str_replace('-----END EC PRIVATE KEY-----', '', $formattedKey); - if (PHP_VERSION_ID >= 80400) { - if (!is_string($formattedKey)) { - throw new PasetoException('Invalid PEM format', ExceptionCode::UNSPECIFIED_CRYPTOGRAPHIC_ERROR); - } + + /** + * @psalm-suppress DocblockTypeContradiction + * PHP 8.4 updated the docblock return for str_replace, which makes this check required + */ + if (!is_string($formattedKey)) { + throw new PasetoException('Invalid PEM format', ExceptionCode::UNSPECIFIED_CRYPTOGRAPHIC_ERROR); } $tokenizedKey = strtok($formattedKey, "\n"); From 8fd43664b682ffdfcc9d18ed84b447da302d2bcf Mon Sep 17 00:00:00 2001 From: Jacob Thomason Date: Sun, 8 Dec 2024 03:45:46 -0500 Subject: [PATCH 08/10] Upgraded PHPUnit and resolved test issues --- .gitignore | 1 + composer.json | 2 +- tests/KeyTest.php | 10 +++++----- tests/LucidityTest.php | 5 +++-- tests/MultiKeyTest.php | 6 +++--- tests/PurposeTest.php | 12 +++++------- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 74fe76f..76fc95e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /.idea +/.phpunit.result.cache /composer.lock /vendor diff --git a/composer.json b/composer.json index 52483a9..c5f9644 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,7 @@ "paragonie/sodium_compat": "^1|^2" }, "require-dev": { - "phpunit/phpunit": "^9", + "phpunit/phpunit": "^11", "vimeo/psalm": "dev-master" }, "scripts": { diff --git a/tests/KeyTest.php b/tests/KeyTest.php index 113758f..99fde26 100644 --- a/tests/KeyTest.php +++ b/tests/KeyTest.php @@ -5,16 +5,18 @@ use ParagonIE\ConstantTime\Binary; use ParagonIE\Paseto\Builder; use ParagonIE\Paseto\Exception\{PasetoException, SecurityException,}; -use ParagonIE\Paseto\Keys\{Base\AsymmetricPublicKey, Base\AsymmetricSecretKey}; use ParagonIE\Paseto\Keys\Version4\SymmetricKey; +use ParagonIE\Paseto\Keys\{Base\AsymmetricPublicKey, Base\AsymmetricSecretKey}; use ParagonIE\Paseto\Protocol\{Version3, Version4}; use ParagonIE\Paseto\Purpose; use ParagonIE\Paseto\Util; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class KeyTest extends TestCase { - public function pemProvider(): array + /** @return array> */ + public static function pemProvider(): array { return [ [ @@ -54,9 +56,7 @@ public function pemProvider(): array ]; } - /** - * @dataProvider pemProvider - */ + #[DataProvider('pemProvider')] public function testExportImportPem(AsymmetricSecretKey $sk, string $skPem, string $pkPem): void { $this->assertSame($skPem, $sk->encodePem()); diff --git a/tests/LucidityTest.php b/tests/LucidityTest.php index 9bd04e1..672ccbc 100644 --- a/tests/LucidityTest.php +++ b/tests/LucidityTest.php @@ -7,6 +7,7 @@ use ParagonIE\Paseto\KeyInterface; use ParagonIE\Paseto\Keys\Base\SymmetricKey; use ParagonIE\Paseto\Protocol\{Version3, Version4}; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use TypeError; @@ -16,7 +17,7 @@ class LucidityTest extends TestCase * @return array[] * @throws Exception */ - public function luciditySymmetric(): array + public static function luciditySymmetric(): array { $v4_lk = Version4::generateSymmetricKey(); $v4_sk = Version4::generateAsymmetricSecretKey(); @@ -45,10 +46,10 @@ public function luciditySymmetric(): array * @param KeyInterface $validKey * @param KeyInterface $invalidKey * - * @dataProvider luciditySymmetric * @throws Exception * @throws PasetoException */ + #[DataProvider('luciditySymmetric')] public function testLocalLucidity( $protocol, KeyInterface $validKey, diff --git a/tests/MultiKeyTest.php b/tests/MultiKeyTest.php index bba4ba8..86d52b5 100644 --- a/tests/MultiKeyTest.php +++ b/tests/MultiKeyTest.php @@ -17,6 +17,7 @@ SendingKeyRing}; use ParagonIE\Paseto\Keys\{Base\AsymmetricSecretKey, Base\SymmetricKey}; use ParagonIE\Paseto\Protocol\{Version3, Version4}; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use TypeError; @@ -233,7 +234,7 @@ protected function doSendingTest(ProtocolInterface $v): void * @return array[] * @throws Exception */ - public function typeCheckData(): array + public static function typeCheckData(): array { $v3_lk = Version3::generateSymmetricKey(); $v3_sk = Version3::generateAsymmetricSecretKey(); @@ -472,8 +473,6 @@ public function typeCheckData(): array } /** - * @dataProvider typeCheckData - * * @param SendingKeyRing|ReceivingKeyRing $keyring * @param SendingKey|ReceivingKey $key * @param bool $expectFail @@ -481,6 +480,7 @@ public function typeCheckData(): array * @psalm-suppress PossiblyInvalidArgument * @throws PasetoException */ + #[DataProvider('typeCheckData')] public function testTypeChecks( SendingKeyRing|ReceivingKeyRing$keyring, SendingKey|ReceivingKey $key, diff --git a/tests/PurposeTest.php b/tests/PurposeTest.php index 5bfd862..cd1ee22 100644 --- a/tests/PurposeTest.php +++ b/tests/PurposeTest.php @@ -62,13 +62,13 @@ public function setUp(): void $this->sk3 = V3AsymmetricSecretKey::generate(); $this->pk3 = $this->sk3->getPublicKey(); $this->k3 = V3SymmetricKey::generate(); - + $this->sk4 = V4AsymmetricSecretKey::generate(); $this->pk4 = $this->sk4->getPublicKey(); $this->k4 = V4SymmetricKey::generate(); } - public function receivingKeyProvider(): array + public static function receivingKeyProvider(): array { if (!$this->setUpAtRuntime) { $this->setUp(); @@ -82,7 +82,7 @@ public function receivingKeyProvider(): array ]; } - public function sendingKeyProvider(): array + public static function sendingKeyProvider(): array { if (!$this->setUpAtRuntime) { $this->setUp(); @@ -97,13 +97,12 @@ public function sendingKeyProvider(): array } /** - * @dataProvider receivingKeyProvider - * * @param ReceivingKey $key * @param string $expected * @return void * @throws InvalidPurposeException */ + #[DataProvider('receivingKeyProvider')] public function testReceivingMapping(ReceivingKey $key, string $expected): void { $purpose = Purpose::fromReceivingKey($key); @@ -111,13 +110,12 @@ public function testReceivingMapping(ReceivingKey $key, string $expected): void } /** - * @dataProvider sendingKeyProvider - * * @param SendingKey $key * @param string $expected * @return void * @throws InvalidPurposeException */ + #[DataProvider('sendingKeyProvider')] public function testSendingMapping(SendingKey $key, string $expected): void { $purpose = Purpose::fromSendingKey($key); From 285d1880b32b0e59ec3f77125d08c596e3aff6b2 Mon Sep 17 00:00:00 2001 From: Jacob Thomason Date: Sun, 8 Dec 2024 03:48:48 -0500 Subject: [PATCH 09/10] Downgrade PHPUnit for 8.1 support --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index c5f9644..b8c2df8 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,7 @@ "paragonie/sodium_compat": "^1|^2" }, "require-dev": { - "phpunit/phpunit": "^11", + "phpunit/phpunit": "^10", "vimeo/psalm": "dev-master" }, "scripts": { From e9dfe4cf2ec53602241f52a965366998d580be29 Mon Sep 17 00:00:00 2001 From: "P.I.E. Security Team" Date: Mon, 6 Jan 2025 20:30:40 -0500 Subject: [PATCH 10/10] Add missing import --- tests/PurposeTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/PurposeTest.php b/tests/PurposeTest.php index cd1ee22..462b500 100644 --- a/tests/PurposeTest.php +++ b/tests/PurposeTest.php @@ -26,6 +26,7 @@ SendingKey }; use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\Attributes\DataProvider; use Exception; class PurposeTest extends TestCase