Subversion Repositories oidplus

Rev

Rev 637 | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 637 Rev 679
Line 1... Line 1...
1
<?php
1
<?php
2
 
2
 
3
namespace Firebase\JWT;
3
namespace Firebase\JWT;
4
 
4
 
-
 
5
use ArrayAccess;
5
use DomainException;
6
use DomainException;
6
use Exception;
7
use Exception;
7
use InvalidArgumentException;
8
use InvalidArgumentException;
-
 
9
use OpenSSLAsymmetricKey;
8
use UnexpectedValueException;
10
use UnexpectedValueException;
9
use DateTime;
11
use DateTime;
10
 
12
 
11
/**
13
/**
12
 * JSON Web Token implementation, based on this spec:
14
 * JSON Web Token implementation, based on this spec:
Line 56... Line 58...
56
 
58
 
57
    /**
59
    /**
58
     * Decodes a JWT string into a PHP object.
60
     * Decodes a JWT string into a PHP object.
59
     *
61
     *
60
     * @param string                    $jwt            The JWT
62
     * @param string                    $jwt            The JWT
61
     * @param string|array|resource     $key            The key, or map of keys.
63
     * @param Key|array<Key>|mixed      $keyOrKeyArray  The Key or array of Key objects.
62
     *                                                  If the algorithm used is asymmetric, this is the public key
64
     *                                                  If the algorithm used is asymmetric, this is the public key
63
     * @param array                     $allowed_algs   List of supported verification algorithms
65
     *                                                  Each Key object contains an algorithm and matching key.
64
     *                                                  Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
66
     *                                                  Supported algorithms are 'ES384','ES256', 'HS256', 'HS384',
65
     *                                                  'HS512', 'RS256', 'RS384', and 'RS512'
67
     *                                                  'HS512', 'RS256', 'RS384', and 'RS512'
-
 
68
     * @param array                     $allowed_algs   [DEPRECATED] List of supported verification algorithms. Only
-
 
69
     *                                                  should be used for backwards  compatibility.
66
     *
70
     *
67
     * @return object The JWT's payload as a PHP object
71
     * @return object The JWT's payload as a PHP object
68
     *
72
     *
69
     * @throws InvalidArgumentException     Provided JWT was empty
73
     * @throws InvalidArgumentException     Provided JWT was empty
70
     * @throws UnexpectedValueException     Provided JWT was invalid
74
     * @throws UnexpectedValueException     Provided JWT was invalid
Line 74... Line 78...
74
     * @throws ExpiredException             Provided JWT has since expired, as defined by the 'exp' claim
78
     * @throws ExpiredException             Provided JWT has since expired, as defined by the 'exp' claim
75
     *
79
     *
76
     * @uses jsonDecode
80
     * @uses jsonDecode
77
     * @uses urlsafeB64Decode
81
     * @uses urlsafeB64Decode
78
     */
82
     */
79
    public static function decode($jwt, $key, array $allowed_algs = array())
83
    public static function decode($jwt, $keyOrKeyArray, array $allowed_algs = array())
80
    {
84
    {
81
        $timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp;
85
        $timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp;
82
 
86
 
83
        if (empty($key)) {
87
        if (empty($keyOrKeyArray)) {
84
            throw new InvalidArgumentException('Key may not be empty');
88
            throw new InvalidArgumentException('Key may not be empty');
85
        }
89
        }
86
        $tks = \explode('.', $jwt);
90
        $tks = \explode('.', $jwt);
87
        if (\count($tks) != 3) {
91
        if (\count($tks) != 3) {
88
            throw new UnexpectedValueException('Wrong number of segments');
92
            throw new UnexpectedValueException('Wrong number of segments');
Line 101... Line 105...
101
            throw new UnexpectedValueException('Empty algorithm');
105
            throw new UnexpectedValueException('Empty algorithm');
102
        }
106
        }
103
        if (empty(static::$supported_algs[$header->alg])) {
107
        if (empty(static::$supported_algs[$header->alg])) {
104
            throw new UnexpectedValueException('Algorithm not supported');
108
            throw new UnexpectedValueException('Algorithm not supported');
105
        }
109
        }
-
 
110
 
-
 
111
        list($keyMaterial, $algorithm) = self::getKeyMaterialAndAlgorithm(
-
 
112
            $keyOrKeyArray,
-
 
113
            empty($header->kid) ? null : $header->kid
-
 
114
        );
-
 
115
 
-
 
116
        if (empty($algorithm)) {
-
 
117
            // Use deprecated "allowed_algs" to determine if the algorithm is supported.
-
 
118
            // This opens up the possibility of an attack in some implementations.
-
 
119
            // @see https://github.com/firebase/php-jwt/issues/351
106
        if (!\in_array($header->alg, $allowed_algs)) {
120
            if (!\in_array($header->alg, $allowed_algs)) {
107
            throw new UnexpectedValueException('Algorithm not allowed');
121
                throw new UnexpectedValueException('Algorithm not allowed');
108
        }
122
            }
-
 
123
        } else {
-
 
124
            // Check the algorithm
-
 
125
            if (!self::constantTimeEquals($algorithm, $header->alg)) {
-
 
126
                // See issue #351
-
 
127
                throw new UnexpectedValueException('Incorrect key for this algorithm');
-
 
128
            }
-
 
129
        }
109
        if ($header->alg === 'ES256' || $header->alg === 'ES384') {
130
        if ($header->alg === 'ES256' || $header->alg === 'ES384') {
110
            // OpenSSL expects an ASN.1 DER sequence for ES256/ES384 signatures
131
            // OpenSSL expects an ASN.1 DER sequence for ES256/ES384 signatures
111
            $sig = self::signatureToDER($sig);
132
            $sig = self::signatureToDER($sig);
112
        }
133
        }
113
 
134
 
114
        if (\is_array($key) || $key instanceof \ArrayAccess) {
-
 
115
            if (isset($header->kid)) {
-
 
116
                if (!isset($key[$header->kid])) {
-
 
117
                    throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
-
 
118
                }
-
 
119
                $key = $key[$header->kid];
-
 
120
            } else {
-
 
121
                throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
-
 
122
            }
-
 
123
        }
-
 
124
 
-
 
125
        // Check the signature
-
 
126
        if (!static::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) {
135
        if (!static::verify("$headb64.$bodyb64", $sig, $keyMaterial, $header->alg)) {
127
            throw new SignatureInvalidException('Signature verification failed');
136
            throw new SignatureInvalidException('Signature verification failed');
128
        }
137
        }
129
 
138
 
130
        // Check the nbf if it is defined. This is the time that the
139
        // Check the nbf if it is defined. This is the time that the
131
        // token can actually be used. If it's not yet that time, abort.
140
        // token can actually be used. If it's not yet that time, abort.
Line 283... Line 292...
283
                  throw new DomainException($e->getMessage(), 0, $e);
292
                  throw new DomainException($e->getMessage(), 0, $e);
284
              }
293
              }
285
            case 'hash_hmac':
294
            case 'hash_hmac':
286
            default:
295
            default:
287
                $hash = \hash_hmac($algorithm, $msg, $key, true);
296
                $hash = \hash_hmac($algorithm, $msg, $key, true);
288
                if (\function_exists('hash_equals')) {
-
 
289
                    return \hash_equals($signature, $hash);
297
                return self::constantTimeEquals($signature, $hash);
290
                }
-
 
291
                $len = \min(static::safeStrlen($signature), static::safeStrlen($hash));
-
 
292
 
-
 
293
                $status = 0;
-
 
294
                for ($i = 0; $i < $len; $i++) {
-
 
295
                    $status |= (\ord($signature[$i]) ^ \ord($hash[$i]));
-
 
296
                }
-
 
297
                $status |= (static::safeStrlen($signature) ^ static::safeStrlen($hash));
-
 
298
 
-
 
299
                return ($status === 0);
-
 
300
        }
298
        }
301
    }
299
    }
302
 
300
 
303
    /**
301
    /**
304
     * Decode a JSON string into a PHP object.
302
     * Decode a JSON string into a PHP object.
Line 382... Line 380...
382
    public static function urlsafeB64Encode($input)
380
    public static function urlsafeB64Encode($input)
383
    {
381
    {
384
        return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_'));
382
        return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_'));
385
    }
383
    }
386
 
384
 
-
 
385
 
-
 
386
    /**
-
 
387
     * Determine if an algorithm has been provided for each Key
-
 
388
     *
-
 
389
     * @param Key|array<Key>|mixed $keyOrKeyArray
-
 
390
     * @param string|null $kid
-
 
391
     *
-
 
392
     * @throws UnexpectedValueException
-
 
393
     *
-
 
394
     * @return array containing the keyMaterial and algorithm
-
 
395
     */
-
 
396
    private static function getKeyMaterialAndAlgorithm($keyOrKeyArray, $kid = null)
-
 
397
    {
-
 
398
        if (
-
 
399
            is_string($keyOrKeyArray)
-
 
400
            || is_resource($keyOrKeyArray)
-
 
401
            || $keyOrKeyArray instanceof OpenSSLAsymmetricKey
-
 
402
        ) {
-
 
403
            return array($keyOrKeyArray, null);
-
 
404
        }
-
 
405
 
-
 
406
        if ($keyOrKeyArray instanceof Key) {
-
 
407
            return array($keyOrKeyArray->getKeyMaterial(), $keyOrKeyArray->getAlgorithm());
-
 
408
        }
-
 
409
 
-
 
410
        if (is_array($keyOrKeyArray) || $keyOrKeyArray instanceof ArrayAccess) {
-
 
411
            if (!isset($kid)) {
-
 
412
                throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
-
 
413
            }
-
 
414
            if (!isset($keyOrKeyArray[$kid])) {
-
 
415
                throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
-
 
416
            }
-
 
417
 
-
 
418
            $key = $keyOrKeyArray[$kid];
-
 
419
 
-
 
420
            if ($key instanceof Key) {
-
 
421
                return array($key->getKeyMaterial(), $key->getAlgorithm());
-
 
422
            }
-
 
423
 
-
 
424
            return array($key, null);
-
 
425
        }
-
 
426
 
-
 
427
        throw new UnexpectedValueException(
-
 
428
            '$keyOrKeyArray must be a string|resource key, an array of string|resource keys, '
-
 
429
            . 'an instance of Firebase\JWT\Key key or an array of Firebase\JWT\Key keys'
-
 
430
        );
-
 
431
    }
-
 
432
 
-
 
433
    /**
-
 
434
     * @param string $left
-
 
435
     * @param string $right
-
 
436
     * @return bool
-
 
437
     */
-
 
438
    public static function constantTimeEquals($left, $right)
-
 
439
    {
-
 
440
        if (\function_exists('hash_equals')) {
-
 
441
            return \hash_equals($left, $right);
-
 
442
        }
-
 
443
        $len = \min(static::safeStrlen($left), static::safeStrlen($right));
-
 
444
 
-
 
445
        $status = 0;
-
 
446
        for ($i = 0; $i < $len; $i++) {
-
 
447
            $status |= (\ord($left[$i]) ^ \ord($right[$i]));
-
 
448
        }
-
 
449
        $status |= (static::safeStrlen($left) ^ static::safeStrlen($right));
-
 
450
 
-
 
451
        return ($status === 0);
-
 
452
    }
-
 
453
 
387
    /**
454
    /**
388
     * Helper method to create a JSON error.
455
     * Helper method to create a JSON error.
389
     *
456
     *
390
     * @param int $errno An error number from json_last_error()
457
     * @param int $errno An error number from json_last_error()
391
     *
458
     *