Skip to content

Commit

Permalink
Implement PS256/384/512
Browse files Browse the repository at this point in the history
The heavy work is offloaded to phpseclib/phpseclib v3, which is added as a dependency.
  • Loading branch information
SvenRtbg committed Nov 22, 2024
1 parent 02ca42b commit 7bdc992
Show file tree
Hide file tree
Showing 11 changed files with 602 additions and 1 deletion.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"php": "~8.2.0 || ~8.3.0 || ~8.4.0",
"ext-openssl": "*",
"ext-sodium": "*",
"phpseclib/phpseclib": "^3.0.36",
"psr/clock": "^1.0"
},
"require-dev": {
Expand Down
229 changes: 228 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

72 changes: 72 additions & 0 deletions src/Signer/RsaPss.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);

namespace Lcobucci\JWT\Signer;

use Lcobucci\JWT\Signer;
use phpseclib3\Crypt\PublicKeyLoader;
use phpseclib3\Crypt\RSA;
use phpseclib3\Crypt\RSA\PrivateKey;
use phpseclib3\Crypt\RSA\PublicKey;
use phpseclib3\Exception\NoKeyLoadedException;

use function assert;
use function is_string;

abstract class RsaPss implements Signer
{
private const MINIMUM_KEY_LENGTH = 2048;

final public function sign(string $payload, Key $key): string
{
try {
$private = PublicKeyLoader::loadPrivateKey($key->contents(), $key->passphrase());
} catch (NoKeyLoadedException $e) {
throw new InvalidKeyProvided('It was not possible to parse your key, reason: ' . $e->getMessage());

Check warning on line 25 in src/Signer/RsaPss.php

View workflow job for this annotation

GitHub Actions / Mutation tests (locked, 8.2, ubuntu-latest)

Escaped Mutant for Mutator "Concat": @@ @@ try { $private = PublicKeyLoader::loadPrivateKey($key->contents(), $key->passphrase()); } catch (NoKeyLoadedException $e) { - throw new InvalidKeyProvided('It was not possible to parse your key, reason: ' . $e->getMessage()); + throw new InvalidKeyProvided($e->getMessage() . 'It was not possible to parse your key, reason: '); } if (!$private instanceof PrivateKey) { throw InvalidKeyProvided::incompatibleKeyType('RSA', $private::class);

Check warning on line 25 in src/Signer/RsaPss.php

View workflow job for this annotation

GitHub Actions / Mutation tests (locked, 8.2, ubuntu-latest)

Escaped Mutant for Mutator "ConcatOperandRemoval": @@ @@ try { $private = PublicKeyLoader::loadPrivateKey($key->contents(), $key->passphrase()); } catch (NoKeyLoadedException $e) { - throw new InvalidKeyProvided('It was not possible to parse your key, reason: ' . $e->getMessage()); + throw new InvalidKeyProvided('It was not possible to parse your key, reason: '); } if (!$private instanceof PrivateKey) { throw InvalidKeyProvided::incompatibleKeyType('RSA', $private::class);
}

if (! $private instanceof PrivateKey) {
throw InvalidKeyProvided::incompatibleKeyType('RSA', $private::class);
}

if ($private->getLength() < self::MINIMUM_KEY_LENGTH) {
throw InvalidKeyProvided::tooShort(self::MINIMUM_KEY_LENGTH, $private->getLength());
}

$signature = $private
->withPadding(RSA::SIGNATURE_PSS)
->withHash($this->algorithm())
->withMGFHash($this->algorithm())
->sign($payload);

assert(is_string($signature) && $signature !== '');

Check warning on line 42 in src/Signer/RsaPss.php

View workflow job for this annotation

GitHub Actions / Mutation tests (locked, 8.2, ubuntu-latest)

Escaped Mutant for Mutator "LogicalAnd": @@ @@ throw InvalidKeyProvided::tooShort(self::MINIMUM_KEY_LENGTH, $private->getLength()); } $signature = $private->withPadding(RSA::SIGNATURE_PSS)->withHash($this->algorithm())->withMGFHash($this->algorithm())->sign($payload); - assert(is_string($signature) && $signature !== ''); + assert(is_string($signature) || $signature !== ''); return $signature; } final public function verify(string $expected, string $payload, Key $key): bool

return $signature;
}

final public function verify(string $expected, string $payload, Key $key): bool
{
try {
$public = PublicKeyLoader::loadPublicKey($key->contents());
} catch (NoKeyLoadedException $e) {
throw new InvalidKeyProvided('It was not possible to parse your key, reason: ' . $e->getMessage());

Check warning on line 52 in src/Signer/RsaPss.php

View workflow job for this annotation

GitHub Actions / Mutation tests (locked, 8.2, ubuntu-latest)

Escaped Mutant for Mutator "Concat": @@ @@ try { $public = PublicKeyLoader::loadPublicKey($key->contents()); } catch (NoKeyLoadedException $e) { - throw new InvalidKeyProvided('It was not possible to parse your key, reason: ' . $e->getMessage()); + throw new InvalidKeyProvided($e->getMessage() . 'It was not possible to parse your key, reason: '); } if (!$public instanceof PublicKey) { throw InvalidKeyProvided::incompatibleKeyType('RSA', $public::class);

Check warning on line 52 in src/Signer/RsaPss.php

View workflow job for this annotation

GitHub Actions / Mutation tests (locked, 8.2, ubuntu-latest)

Escaped Mutant for Mutator "ConcatOperandRemoval": @@ @@ try { $public = PublicKeyLoader::loadPublicKey($key->contents()); } catch (NoKeyLoadedException $e) { - throw new InvalidKeyProvided('It was not possible to parse your key, reason: ' . $e->getMessage()); + throw new InvalidKeyProvided('It was not possible to parse your key, reason: '); } if (!$public instanceof PublicKey) { throw InvalidKeyProvided::incompatibleKeyType('RSA', $public::class);
}

if (! $public instanceof PublicKey) {
throw InvalidKeyProvided::incompatibleKeyType('RSA', $public::class);
}

return $public
->withPadding(RSA::SIGNATURE_PSS)
->withHash($this->algorithm())
->withMGFHash($this->algorithm())
->verify($payload, $expected);
}

/**
* Returns which algorithm to be used to create/verify the signature (using phpseclib hash identifiers)
*
* @internal
*/
abstract public function algorithm(): string;
}
19 changes: 19 additions & 0 deletions src/Signer/RsaPss/Sha256.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);

namespace Lcobucci\JWT\Signer\RsaPss;

use Lcobucci\JWT\Signer\RsaPss;

final class Sha256 extends RsaPss
{
public function algorithmId(): string
{
return 'PS256';
}

public function algorithm(): string
{
return 'sha256';
}
}
19 changes: 19 additions & 0 deletions src/Signer/RsaPss/Sha384.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);

namespace Lcobucci\JWT\Signer\RsaPss;

use Lcobucci\JWT\Signer\RsaPss;

final class Sha384 extends RsaPss
{
public function algorithmId(): string
{
return 'PS384';
}

public function algorithm(): string
{
return 'sha384';
}
}
19 changes: 19 additions & 0 deletions src/Signer/RsaPss/Sha512.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);

namespace Lcobucci\JWT\Signer\RsaPss;

use Lcobucci\JWT\Signer\RsaPss;

final class Sha512 extends RsaPss
{
public function algorithmId(): string
{
return 'PS512';
}

public function algorithm(): string
{
return 'sha512';
}
}
Loading

0 comments on commit 7bdc992

Please sign in to comment.