MDL-71712 lib: Upgrade PHP-JWT to 6.0.0

This commit is contained in:
Amaia Anabitarte 2022-02-01 12:50:16 +01:00
parent 4f9a539600
commit af1401bc76
9 changed files with 341 additions and 84 deletions

View file

@ -13,7 +13,7 @@ modification, are permitted provided that the following conditions are met:
disclaimer in the documentation and/or other materials provided disclaimer in the documentation and/or other materials provided
with the distribution. with the distribution.
* Neither the name of Neuman Vong nor the names of other * Neither the name of the copyright holder nor the names of other
contributors may be used to endorse or promote products derived contributors may be used to endorse or promote products derived
from this software without specific prior written permission. from this software without specific prior written permission.

View file

@ -1,4 +1,4 @@
[![Build Status](https://travis-ci.org/firebase/php-jwt.png?branch=master)](https://travis-ci.org/firebase/php-jwt) ![Build Status](https://github.com/firebase/php-jwt/actions/workflows/tests.yml/badge.svg)
[![Latest Stable Version](https://poser.pugx.org/firebase/php-jwt/v/stable)](https://packagist.org/packages/firebase/php-jwt) [![Latest Stable Version](https://poser.pugx.org/firebase/php-jwt/v/stable)](https://packagist.org/packages/firebase/php-jwt)
[![Total Downloads](https://poser.pugx.org/firebase/php-jwt/downloads)](https://packagist.org/packages/firebase/php-jwt) [![Total Downloads](https://poser.pugx.org/firebase/php-jwt/downloads)](https://packagist.org/packages/firebase/php-jwt)
[![License](https://poser.pugx.org/firebase/php-jwt/license)](https://packagist.org/packages/firebase/php-jwt) [![License](https://poser.pugx.org/firebase/php-jwt/license)](https://packagist.org/packages/firebase/php-jwt)
@ -16,11 +16,18 @@ Use composer to manage your dependencies and download PHP-JWT:
composer require firebase/php-jwt composer require firebase/php-jwt
``` ```
Optionally, install the `paragonie/sodium_compat` package from composer if your
php is < 7.2 or does not have libsodium installed:
```bash
composer require paragonie/sodium_compat
```
Example Example
------- -------
```php ```php
<?php use Firebase\JWT\JWT;
use \Firebase\JWT\JWT; use Firebase\JWT\Key;
$key = "example_key"; $key = "example_key";
$payload = array( $payload = array(
@ -36,8 +43,8 @@ $payload = array(
* https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40 * https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40
* for a list of spec-compliant algorithms. * for a list of spec-compliant algorithms.
*/ */
$jwt = JWT::encode($payload, $key); $jwt = JWT::encode($payload, $key, 'HS256');
$decoded = JWT::decode($jwt, $key, array('HS256')); $decoded = JWT::decode($jwt, new Key($key, 'HS256'));
print_r($decoded); print_r($decoded);
@ -56,15 +63,13 @@ $decoded_array = (array) $decoded;
* Source: http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#nbfDef * Source: http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#nbfDef
*/ */
JWT::$leeway = 60; // $leeway in seconds JWT::$leeway = 60; // $leeway in seconds
$decoded = JWT::decode($jwt, $key, array('HS256')); $decoded = JWT::decode($jwt, new Key($key, 'HS256'));
?>
``` ```
Example with RS256 (openssl) Example with RS256 (openssl)
---------------------------- ----------------------------
```php ```php
<?php use Firebase\JWT\JWT;
use \Firebase\JWT\JWT; use Firebase\JWT\Key;
$privateKey = <<<EOD $privateKey = <<<EOD
-----BEGIN RSA PRIVATE KEY----- -----BEGIN RSA PRIVATE KEY-----
@ -103,7 +108,7 @@ $payload = array(
$jwt = JWT::encode($payload, $privateKey, 'RS256'); $jwt = JWT::encode($payload, $privateKey, 'RS256');
echo "Encode:\n" . print_r($jwt, true) . "\n"; echo "Encode:\n" . print_r($jwt, true) . "\n";
$decoded = JWT::decode($jwt, $publicKey, array('RS256')); $decoded = JWT::decode($jwt, new Key($publicKey, 'RS256'));
/* /*
NOTE: This will now be an object instead of an associative array. To get NOTE: This will now be an object instead of an associative array. To get
@ -112,12 +117,104 @@ $decoded = JWT::decode($jwt, $publicKey, array('RS256'));
$decoded_array = (array) $decoded; $decoded_array = (array) $decoded;
echo "Decode:\n" . print_r($decoded_array, true) . "\n"; echo "Decode:\n" . print_r($decoded_array, true) . "\n";
?> ```
Example with a passphrase
-------------------------
```php
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
// Your passphrase
$passphrase = '[YOUR_PASSPHRASE]';
// Your private key file with passphrase
// Can be generated with "ssh-keygen -t rsa -m pem"
$privateKeyFile = '/path/to/key-with-passphrase.pem';
// Create a private key of type "resource"
$privateKey = openssl_pkey_get_private(
file_get_contents($privateKeyFile),
$passphrase
);
$payload = array(
"iss" => "example.org",
"aud" => "example.com",
"iat" => 1356999524,
"nbf" => 1357000000
);
$jwt = JWT::encode($payload, $privateKey, 'RS256');
echo "Encode:\n" . print_r($jwt, true) . "\n";
// Get public key from the private key, or pull from from a file.
$publicKey = openssl_pkey_get_details($privateKey)['key'];
$decoded = JWT::decode($jwt, new Key($publicKey, 'RS256'));
echo "Decode:\n" . print_r((array) $decoded, true) . "\n";
```
Example with EdDSA (libsodium and Ed25519 signature)
----------------------------
```php
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
// Public and private keys are expected to be Base64 encoded. The last
// non-empty line is used so that keys can be generated with
// sodium_crypto_sign_keypair(). The secret keys generated by other tools may
// need to be adjusted to match the input expected by libsodium.
$keyPair = sodium_crypto_sign_keypair();
$privateKey = base64_encode(sodium_crypto_sign_secretkey($keyPair));
$publicKey = base64_encode(sodium_crypto_sign_publickey($keyPair));
$payload = array(
"iss" => "example.org",
"aud" => "example.com",
"iat" => 1356999524,
"nbf" => 1357000000
);
$jwt = JWT::encode($payload, $privateKey, 'EdDSA');
echo "Encode:\n" . print_r($jwt, true) . "\n";
$decoded = JWT::decode($jwt, new Key($publicKey, 'EdDSA'));
echo "Decode:\n" . print_r((array) $decoded, true) . "\n";
````
Using JWKs
----------
```php
use Firebase\JWT\JWK;
use Firebase\JWT\JWT;
// Set of keys. The "keys" key is required. For example, the JSON response to
// this endpoint: https://www.gstatic.com/iap/verify/public_key-jwk
$jwks = ['keys' => []];
// JWK::parseKeySet($jwks) returns an associative array of **kid** to Firebase\JWT\Key
// objects. Pass this as the second parameter to JWT::decode.
JWT::decode($payload, JWK::parseKeySet($jwks));
``` ```
Changelog Changelog
--------- ---------
#### 6.0.0 / 2022-01-24
- **Backwards-Compatibility Breaking Changes**: See the [Release Notes](https://github.com/firebase/php-jwt/releases/tag/v5.5.1) for more information.
- New Key object to prevent key/algorithm type confusion (#365)
- Add JWK support (#273)
- Add ES256 support (#256)
- Add ES384 support (#324)
- Add Ed25519 support (#343)
#### 5.0.0 / 2017-06-26 #### 5.0.0 / 2017-06-26
- Support RS384 and RS512. - Support RS384 and RS512.
See [#117](https://github.com/firebase/php-jwt/pull/117). Thanks [@joostfaassen](https://github.com/joostfaassen)! See [#117](https://github.com/firebase/php-jwt/pull/117). Thanks [@joostfaassen](https://github.com/joostfaassen)!

View file

@ -22,6 +22,9 @@
"require": { "require": {
"php": ">=5.3.0" "php": ">=5.3.0"
}, },
"suggest": {
"paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
},
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Firebase\\JWT\\": "src" "Firebase\\JWT\\": "src"

View file

@ -1,4 +1,5 @@
<?php <?php
namespace Firebase\JWT; namespace Firebase\JWT;
class BeforeValidException extends \UnexpectedValueException class BeforeValidException extends \UnexpectedValueException

View file

@ -1,4 +1,5 @@
<?php <?php
namespace Firebase\JWT; namespace Firebase\JWT;
class ExpiredException extends \UnexpectedValueException class ExpiredException extends \UnexpectedValueException

View file

@ -3,6 +3,7 @@
namespace Firebase\JWT; namespace Firebase\JWT;
use DomainException; use DomainException;
use InvalidArgumentException;
use UnexpectedValueException; use UnexpectedValueException;
/** /**
@ -24,7 +25,7 @@ class JWK
* *
* @param array $jwks The JSON Web Key Set as an associative array * @param array $jwks The JSON Web Key Set as an associative array
* *
* @return array An associative array that represents the set of keys * @return array<string, Key> An associative array of key IDs (kid) to Key objects
* *
* @throws InvalidArgumentException Provided JWK Set is empty * @throws InvalidArgumentException Provided JWK Set is empty
* @throws UnexpectedValueException Provided JWK Set was invalid * @throws UnexpectedValueException Provided JWK Set was invalid
@ -62,7 +63,7 @@ class JWK
* *
* @param array $jwk An individual JWK * @param array $jwk An individual JWK
* *
* @return resource|array An associative array that represents the key * @return Key The key object for the JWK
* *
* @throws InvalidArgumentException Provided JWK is empty * @throws InvalidArgumentException Provided JWK is empty
* @throws UnexpectedValueException Provided JWK was invalid * @throws UnexpectedValueException Provided JWK was invalid
@ -70,7 +71,7 @@ class JWK
* *
* @uses createPemFromModulusAndExponent * @uses createPemFromModulusAndExponent
*/ */
private static function parseKey(array $jwk) public static function parseKey(array $jwk)
{ {
if (empty($jwk)) { if (empty($jwk)) {
throw new InvalidArgumentException('JWK must not be empty'); throw new InvalidArgumentException('JWK must not be empty');
@ -78,10 +79,16 @@ class JWK
if (!isset($jwk['kty'])) { if (!isset($jwk['kty'])) {
throw new UnexpectedValueException('JWK must contain a "kty" parameter'); throw new UnexpectedValueException('JWK must contain a "kty" parameter');
} }
if (!isset($jwk['alg'])) {
// The "alg" parameter is optional in a KTY, but is required for parsing in
// this library. Add it manually to your JWK array if it doesn't already exist.
// @see https://datatracker.ietf.org/doc/html/rfc7517#section-4.4
throw new UnexpectedValueException('JWK must contain an "alg" parameter');
}
switch ($jwk['kty']) { switch ($jwk['kty']) {
case 'RSA': case 'RSA':
if (\array_key_exists('d', $jwk)) { if (!empty($jwk['d'])) {
throw new UnexpectedValueException('RSA private keys are not supported'); throw new UnexpectedValueException('RSA private keys are not supported');
} }
if (!isset($jwk['n']) || !isset($jwk['e'])) { if (!isset($jwk['n']) || !isset($jwk['e'])) {
@ -95,7 +102,7 @@ class JWK
'OpenSSL error: ' . \openssl_error_string() 'OpenSSL error: ' . \openssl_error_string()
); );
} }
return $publicKey; return new Key($publicKey, $jwk['alg']);
default: default:
// Currently only RSA is supported // Currently only RSA is supported
break; break;

View file

@ -2,10 +2,13 @@
namespace Firebase\JWT; namespace Firebase\JWT;
use \DomainException; use ArrayAccess;
use \InvalidArgumentException; use DomainException;
use \UnexpectedValueException; use Exception;
use \DateTime; use InvalidArgumentException;
use OpenSSLAsymmetricKey;
use UnexpectedValueException;
use DateTime;
/** /**
* JSON Web Token implementation, based on this spec: * JSON Web Token implementation, based on this spec:
@ -22,16 +25,19 @@ use \DateTime;
*/ */
class JWT class JWT
{ {
const ASN1_INTEGER = 0x02; // const ASN1_INTEGER = 0x02;
const ASN1_SEQUENCE = 0x10; // const ASN1_SEQUENCE = 0x10;
const ASN1_BIT_STRING = 0x03; // const ASN1_BIT_STRING = 0x03;
private static $asn1Integer = 0x02;
private static $asn1Sequence = 0x10;
private static $asn1BitString = 0x03;
/** /**
* When checking nbf, iat or expiration times, * When checking nbf, iat or expiration times,
* we want to provide some extra leeway time to * we want to provide some extra leeway time to
* account for clock skew. * account for clock skew.
*/ */
public static $leeway = 180; public static $leeway = 0;
/** /**
* Allow the current timestamp to be specified. * Allow the current timestamp to be specified.
@ -42,6 +48,7 @@ class JWT
public static $timestamp = null; public static $timestamp = null;
public static $supported_algs = array( public static $supported_algs = array(
'ES384' => array('openssl', 'SHA384'),
'ES256' => array('openssl', 'SHA256'), 'ES256' => array('openssl', 'SHA256'),
'HS256' => array('hash_hmac', 'SHA256'), 'HS256' => array('hash_hmac', 'SHA256'),
'HS384' => array('hash_hmac', 'SHA384'), 'HS384' => array('hash_hmac', 'SHA384'),
@ -49,19 +56,23 @@ class JWT
'RS256' => array('openssl', 'SHA256'), 'RS256' => array('openssl', 'SHA256'),
'RS384' => array('openssl', 'SHA384'), 'RS384' => array('openssl', 'SHA384'),
'RS512' => array('openssl', 'SHA512'), 'RS512' => array('openssl', 'SHA512'),
'EdDSA' => array('sodium_crypto', 'EdDSA'),
); );
/** /**
* Decodes a JWT string into a PHP object. * Decodes a JWT string into a PHP object.
* *
* @param string $jwt The JWT * @param string $jwt The JWT
* @param string|array|resource $key The key, or map of keys. * @param Key|array<string, Key> $keyOrKeyArray The Key or associative array of key IDs (kid) to Key objects.
* If the algorithm used is asymmetric, this is the public key * If the algorithm used is asymmetric, this is the public key
* @param array $allowed_algs List of supported verification algorithms * Each Key object contains an algorithm and matching key.
* Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512' * Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
* 'HS512', 'RS256', 'RS384', and 'RS512'
* *
* @return object The JWT's payload as a PHP object * @return object The JWT's payload as a PHP object
* *
* @throws InvalidArgumentException Provided key/key-array was empty
* @throws DomainException Provided JWT is malformed
* @throws UnexpectedValueException Provided JWT was invalid * @throws UnexpectedValueException Provided JWT was invalid
* @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed * @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed
* @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf' * @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf'
@ -71,11 +82,12 @@ class JWT
* @uses jsonDecode * @uses jsonDecode
* @uses urlsafeB64Decode * @uses urlsafeB64Decode
*/ */
public static function decode($jwt, $key, array $allowed_algs = array()) public static function decode($jwt, $keyOrKeyArray)
{ {
// Validate JWT
$timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp; $timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp;
if (empty($key)) { if (empty($keyOrKeyArray)) {
throw new InvalidArgumentException('Key may not be empty'); throw new InvalidArgumentException('Key may not be empty');
} }
$tks = \explode('.', $jwt); $tks = \explode('.', $jwt);
@ -98,27 +110,19 @@ class JWT
if (empty(static::$supported_algs[$header->alg])) { if (empty(static::$supported_algs[$header->alg])) {
throw new UnexpectedValueException('Algorithm not supported'); throw new UnexpectedValueException('Algorithm not supported');
} }
if (!\in_array($header->alg, $allowed_algs)) {
throw new UnexpectedValueException('Algorithm not allowed'); $key = self::getKey($keyOrKeyArray, empty($header->kid) ? null : $header->kid);
// Check the algorithm
if (!self::constantTimeEquals($key->getAlgorithm(), $header->alg)) {
// See issue #351
throw new UnexpectedValueException('Incorrect key for this algorithm');
} }
if ($header->alg === 'ES256') { if ($header->alg === 'ES256' || $header->alg === 'ES384') {
// OpenSSL expects an ASN.1 DER sequence for ES256 signatures // OpenSSL expects an ASN.1 DER sequence for ES256/ES384 signatures
$sig = self::signatureToDER($sig); $sig = self::signatureToDER($sig);
} }
if (!static::verify("$headb64.$bodyb64", $sig, $key->getKeyMaterial(), $header->alg)) {
if (\is_array($key) || $key instanceof \ArrayAccess) {
if (isset($header->kid)) {
if (!isset($key[$header->kid])) {
throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
}
$key = $key[$header->kid];
} else {
throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
}
}
// Check the signature
if (!static::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) {
throw new SignatureInvalidException('Signature verification failed'); throw new SignatureInvalidException('Signature verification failed');
} }
@ -150,20 +154,21 @@ class JWT
/** /**
* Converts and signs a PHP object or array into a JWT string. * Converts and signs a PHP object or array into a JWT string.
* *
* @param object|array $payload PHP object or array * @param object|array $payload PHP object or array
* @param string $key The secret key. * @param string|resource $key The secret key.
* If the algorithm used is asymmetric, this is the private key * If the algorithm used is asymmetric, this is the private key
* @param string $alg The signing algorithm. * @param string $alg The signing algorithm.
* Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512' * Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
* @param mixed $keyId * 'HS512', 'RS256', 'RS384', and 'RS512'
* @param array $head An array with header elements to attach * @param mixed $keyId
* @param array $head An array with header elements to attach
* *
* @return string A signed JWT * @return string A signed JWT
* *
* @uses jsonEncode * @uses jsonEncode
* @uses urlsafeB64Encode * @uses urlsafeB64Encode
*/ */
public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null) public static function encode($payload, $key, $alg, $keyId = null, $head = null)
{ {
$header = array('typ' => 'JWT', 'alg' => $alg); $header = array('typ' => 'JWT', 'alg' => $alg);
if ($keyId !== null) { if ($keyId !== null) {
@ -189,13 +194,14 @@ class JWT
* @param string $msg The message to sign * @param string $msg The message to sign
* @param string|resource $key The secret key * @param string|resource $key The secret key
* @param string $alg The signing algorithm. * @param string $alg The signing algorithm.
* Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512' * Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
* 'HS512', 'RS256', 'RS384', and 'RS512'
* *
* @return string An encrypted message * @return string An encrypted message
* *
* @throws DomainException Unsupported algorithm was specified * @throws DomainException Unsupported algorithm or bad key was specified
*/ */
public static function sign($msg, $key, $alg = 'HS256') public static function sign($msg, $key, $alg)
{ {
if (empty(static::$supported_algs[$alg])) { if (empty(static::$supported_algs[$alg])) {
throw new DomainException('Algorithm not supported'); throw new DomainException('Algorithm not supported');
@ -209,11 +215,24 @@ class JWT
$success = \openssl_sign($msg, $signature, $key, $algorithm); $success = \openssl_sign($msg, $signature, $key, $algorithm);
if (!$success) { if (!$success) {
throw new DomainException("OpenSSL unable to sign data"); throw new DomainException("OpenSSL unable to sign data");
} else { }
if ($alg === 'ES256') { if ($alg === 'ES256') {
$signature = self::signatureFromDER($signature, 256); $signature = self::signatureFromDER($signature, 256);
} } elseif ($alg === 'ES384') {
return $signature; $signature = self::signatureFromDER($signature, 384);
}
return $signature;
case 'sodium_crypto':
if (!function_exists('sodium_crypto_sign_detached')) {
throw new DomainException('libsodium is not available');
}
try {
// The last non-empty line is used as the key.
$lines = array_filter(explode("\n", $key));
$key = base64_decode(end($lines));
return sodium_crypto_sign_detached($msg, $key);
} catch (Exception $e) {
throw new DomainException($e->getMessage(), 0, $e);
} }
} }
} }
@ -229,7 +248,7 @@ class JWT
* *
* @return bool * @return bool
* *
* @throws DomainException Invalid Algorithm or OpenSSL failure * @throws DomainException Invalid Algorithm, bad key, or OpenSSL failure
*/ */
private static function verify($msg, $signature, $key, $alg) private static function verify($msg, $signature, $key, $alg)
{ {
@ -250,21 +269,22 @@ class JWT
throw new DomainException( throw new DomainException(
'OpenSSL error: ' . \openssl_error_string() 'OpenSSL error: ' . \openssl_error_string()
); );
case 'sodium_crypto':
if (!function_exists('sodium_crypto_sign_verify_detached')) {
throw new DomainException('libsodium is not available');
}
try {
// The last non-empty line is used as the key.
$lines = array_filter(explode("\n", $key));
$key = base64_decode(end($lines));
return sodium_crypto_sign_verify_detached($signature, $msg, $key);
} catch (Exception $e) {
throw new DomainException($e->getMessage(), 0, $e);
}
case 'hash_hmac': case 'hash_hmac':
default: default:
$hash = \hash_hmac($algorithm, $msg, $key, true); $hash = \hash_hmac($algorithm, $msg, $key, true);
if (\function_exists('hash_equals')) { return self::constantTimeEquals($signature, $hash);
return \hash_equals($signature, $hash);
}
$len = \min(static::safeStrlen($signature), static::safeStrlen($hash));
$status = 0;
for ($i = 0; $i < $len; $i++) {
$status |= (\ord($signature[$i]) ^ \ord($hash[$i]));
}
$status |= (static::safeStrlen($signature) ^ static::safeStrlen($hash));
return ($status === 0);
} }
} }
@ -314,7 +334,12 @@ class JWT
*/ */
public static function jsonEncode($input) public static function jsonEncode($input)
{ {
$json = \json_encode($input); if (PHP_VERSION_ID >= 50400) {
$json = \json_encode($input, \JSON_UNESCAPED_SLASHES);
} else {
// PHP 5.3 only
$json = \json_encode($input);
}
if ($errno = \json_last_error()) { if ($errno = \json_last_error()) {
static::handleJsonError($errno); static::handleJsonError($errno);
} elseif ($json === 'null' && $input !== null) { } elseif ($json === 'null' && $input !== null) {
@ -352,6 +377,69 @@ class JWT
return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_')); return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_'));
} }
/**
* Determine if an algorithm has been provided for each Key
*
* @param Key|array<string, Key> $keyOrKeyArray
* @param string|null $kid
*
* @throws UnexpectedValueException
*
* @return array containing the keyMaterial and algorithm
*/
private static function getKey($keyOrKeyArray, $kid = null)
{
if ($keyOrKeyArray instanceof Key) {
return $keyOrKeyArray;
}
if (is_array($keyOrKeyArray) || $keyOrKeyArray instanceof ArrayAccess) {
foreach ($keyOrKeyArray as $keyId => $key) {
if (!$key instanceof Key) {
throw new UnexpectedValueException(
'$keyOrKeyArray must be an instance of Firebase\JWT\Key key or an '
. 'array of Firebase\JWT\Key keys'
);
}
}
if (!isset($kid)) {
throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
}
if (!isset($keyOrKeyArray[$kid])) {
throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
}
return $keyOrKeyArray[$kid];
}
throw new UnexpectedValueException(
'$keyOrKeyArray must be an instance of Firebase\JWT\Key key or an '
. 'array of Firebase\JWT\Key keys'
);
}
/**
* @param string $left
* @param string $right
* @return bool
*/
public static function constantTimeEquals($left, $right)
{
if (\function_exists('hash_equals')) {
return \hash_equals($left, $right);
}
$len = \min(static::safeStrlen($left), static::safeStrlen($right));
$status = 0;
for ($i = 0; $i < $len; $i++) {
$status |= (\ord($left[$i]) ^ \ord($right[$i]));
}
$status |= (static::safeStrlen($left) ^ static::safeStrlen($right));
return ($status === 0);
}
/** /**
* Helper method to create a JSON error. * Helper method to create a JSON error.
* *
@ -415,9 +503,9 @@ class JWT
} }
return self::encodeDER( return self::encodeDER(
self::ASN1_SEQUENCE, self::$asn1Sequence,
self::encodeDER(self::ASN1_INTEGER, $r) . self::encodeDER(self::$asn1Integer, $r) .
self::encodeDER(self::ASN1_INTEGER, $s) self::encodeDER(self::$asn1Integer, $s)
); );
} }
@ -431,7 +519,7 @@ class JWT
private static function encodeDER($type, $value) private static function encodeDER($type, $value)
{ {
$tag_header = 0; $tag_header = 0;
if ($type === self::ASN1_SEQUENCE) { if ($type === self::$asn1Sequence) {
$tag_header |= 0x20; $tag_header |= 0x20;
} }
@ -496,7 +584,7 @@ class JWT
} }
// Value // Value
if ($type == self::ASN1_BIT_STRING) { if ($type == self::$asn1BitString) {
$pos++; // Skip the first contents octet (padding indicator) $pos++; // Skip the first contents octet (padding indicator)
$data = \substr($der, $pos, $len - 1); $data = \substr($der, $pos, $len - 1);
$pos += $len - 1; $pos += $len - 1;

59
lib/php-jwt/src/Key.php Normal file
View file

@ -0,0 +1,59 @@
<?php
namespace Firebase\JWT;
use InvalidArgumentException;
use OpenSSLAsymmetricKey;
class Key
{
/** @var string $algorithm */
private $algorithm;
/** @var string|resource|OpenSSLAsymmetricKey $keyMaterial */
private $keyMaterial;
/**
* @param string|resource|OpenSSLAsymmetricKey $keyMaterial
* @param string $algorithm
*/
public function __construct($keyMaterial, $algorithm)
{
if (
!is_string($keyMaterial)
&& !is_resource($keyMaterial)
&& !$keyMaterial instanceof OpenSSLAsymmetricKey
) {
throw new InvalidArgumentException('Type error: $keyMaterial must be a string, resource, or OpenSSLAsymmetricKey');
}
if (empty($keyMaterial)) {
throw new InvalidArgumentException('Type error: $keyMaterial must not be empty');
}
if (!is_string($algorithm)|| empty($keyMaterial)) {
throw new InvalidArgumentException('Type error: $algorithm must be a string');
}
$this->keyMaterial = $keyMaterial;
$this->algorithm = $algorithm;
}
/**
* Return the algorithm valid for this key
*
* @return string
*/
public function getAlgorithm()
{
return $this->algorithm;
}
/**
* @return string|resource|OpenSSLAsymmetricKey
*/
public function getKeyMaterial()
{
return $this->keyMaterial;
}
}

View file

@ -1,4 +1,5 @@
<?php <?php
namespace Firebase\JWT; namespace Firebase\JWT;
class SignatureInvalidException extends \UnexpectedValueException class SignatureInvalidException extends \UnexpectedValueException