Skip to content

Commit

Permalink
Convert Algorithm to native enum (#22)
Browse files Browse the repository at this point in the history
As the title indicates, this moves the Algorithm to a native `Enum`, and
reworks the existing constants to be aliases to the enumerations rather
than strings.

The other enum-like interfaces (Claim and Header) are left alone since
they're typically used as array _keys_, and will produce an error.
  • Loading branch information
Firehed authored Jul 19, 2024
1 parent a74cca2 commit a07020e
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 42 deletions.
45 changes: 31 additions & 14 deletions src/Algorithm.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,36 @@
/**
* Constants for algorithm header parameter values in RFC7518 Section 3.1
*/
interface Algorithm
enum Algorithm: string
{
const NONE = 'none';
const HMAC_SHA_256 = 'HS256';
const HMAC_SHA_384 = 'HS384';
const HMAC_SHA_512 = 'HS512';
const ECDSA_256 = 'ES256';
const ECDSA_384 = 'ES384';
const ECDSA_512 = 'ES512';
const PKCS_256 = 'RS256';
const PKCS_384 = 'RS384';
const PKCS_512 = 'RS512';
const PSS_256 = 'PS256';
const PSS_384 = 'PS384';
const PSS_512 = 'PS512';
case None = 'none';
case HmacSha256 = 'HS256';
case HmacSha384 = 'HS384';
case HmacSha512 = 'HS512';
case Ecdsa256 = 'ES256';
case Ecdsa384 = 'ES384';
case Ecdsa512 = 'ES512';
case Pkcs256 = 'RS256';
case Pkcs384 = 'RS384';
case Pkcs512 = 'RS512';
case Pss256 = 'PS256';
case Pss384 = 'PS384';
case Pss512 = 'PS512';

/**
* Constants for backwards compatibility
*/
const NONE = Algorithm::None;
const HMAC_SHA_256 = Algorithm::HmacSha256;
const HMAC_SHA_384 = Algorithm::HmacSha384;
const HMAC_SHA_512 = Algorithm::HmacSha512;
const ECDSA_256 = Algorithm::Ecdsa256;
const ECDSA_384 = Algorithm::Ecdsa384;
const ECDSA_512 = Algorithm::Ecdsa512;
const PKCS_256 = Algorithm::Pkcs256;
const PKCS_384 = Algorithm::Pkcs384;
const PKCS_512 = Algorithm::Pkcs512;
const PSS_256 = Algorithm::Pss256;
const PSS_384 = Algorithm::Pss384;
const PSS_512 = Algorithm::Pss512;
}
31 changes: 11 additions & 20 deletions src/JWT.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class JWT
// Actual JWT components
/**
* @var array{
* alg: Algorithm::* | null,
* alg: Algorithm | null,
* typ: 'JWT',
* kid?: array-key,
* }
Expand Down Expand Up @@ -66,7 +66,7 @@ public function getClaims(): array
if ($this->is_verified) {
return $this->claims;
}
if ($this->headers[Header::ALGORITHM] === Algorithm::NONE) {
if ($this->headers[Header::ALGORITHM] === Algorithm::None) {
throw new BadMethodCallException(
'This token is not verified! Either call `verify` first, or '.
'access the unverified claims with `getUnverifiedClaims`.'
Expand Down Expand Up @@ -126,7 +126,7 @@ private function authenticate(): void
// If the algorithm that came out of the application-provided key
// container is *still* Algorithm::NONE, skip verification.
$this->headers[Header::ALGORITHM] = $alg;
if ($alg === Algorithm::NONE) {
if ($alg === Algorithm::None) {
return;
}
$sig = $this->sign($secret);
Expand All @@ -148,23 +148,14 @@ private function sign(Secret $key): string
'.'.
self::b64encode($this->claims);

switch ($alg) {
case Algorithm::NONE:
$data = '';
break;
case Algorithm::HMAC_SHA_256:
$data = hash_hmac('SHA256', $payload, $key->reveal(), true);
break;
case Algorithm::HMAC_SHA_384:
$data = hash_hmac('SHA384', $payload, $key->reveal(), true);
break;
case Algorithm::HMAC_SHA_512:
$data = hash_hmac('SHA512', $payload, $key->reveal(), true);
break;
default:
throw new Exception("Unsupported algorithm");
// use openssl_sign and friends to do the signing
}
$data = match ($alg) {
Algorithm::None => '',
Algorithm::HmacSha256 => hash_hmac('SHA256', $payload, $key->reveal(), true),
Algorithm::HmacSha384 => hash_hmac('SHA384', $payload, $key->reveal(), true),
Algorithm::HmacSha512 => hash_hmac('SHA512', $payload, $key->reveal(), true),
default => throw new Exception('Unsupported algorithm'),
};
// use openssl_sign and friends to do the signing
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
} // sign

Expand Down
9 changes: 3 additions & 6 deletions src/KeyContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,14 @@
class KeyContainer
{

/** @var array{Algorithm::*, Secret}[] */
/** @var array{Algorithm, Secret}[] */
private array $keys = [];

private int|string|null $default = null;

private int|string|null $last = null;

/**
* @param Algorithm::* $alg
*/
public function addKey(int|string $id, string $alg, Secret $secret): self
public function addKey(int|string $id, Algorithm $alg, Secret $secret): self
{
$this->keys[$id] = [$alg, $secret];
$this->last = $id;
Expand All @@ -31,7 +28,7 @@ public function setDefaultKey(int|string $id): self
}

/**
* @return array{Algorithm::*, Secret, string|int}
* @return array{Algorithm, Secret, string|int}
*/
public function getKey(int|string|null $id = null): array
{
Expand Down
3 changes: 1 addition & 2 deletions tests/JWTTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,7 @@ public static function vectors(): array
// KeyContainer,
// should be signed
// ]
$kc = function (string $alg, Secret $s): KeyContainer {
/** @var Algorithm::* $alg */
$kc = function (Algorithm $alg, Secret $s): KeyContainer {
return (new KeyContainer())
->addKey(1, $alg, $s);
};
Expand Down

0 comments on commit a07020e

Please sign in to comment.